aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile16
-rw-r--r--tools/arch/arm/include/uapi/asm/kvm.h12
-rw-r--r--tools/arch/arm64/include/uapi/asm/kvm.h60
-rw-r--r--tools/arch/powerpc/include/uapi/asm/kvm.h46
-rw-r--r--tools/arch/powerpc/include/uapi/asm/mman.h4
-rw-r--r--tools/arch/riscv/include/uapi/asm/bitsperlong.h13
-rw-r--r--tools/arch/riscv/include/uapi/asm/perf_regs.h42
-rw-r--r--tools/arch/s390/include/uapi/asm/kvm.h4
-rw-r--r--tools/arch/sparc/include/uapi/asm/mman.h4
-rw-r--r--tools/arch/x86/include/asm/cpufeatures.h28
-rw-r--r--tools/arch/x86/include/asm/inat.h (renamed from tools/perf/util/intel-pt-decoder/inat.h)16
-rw-r--r--tools/arch/x86/include/asm/inat_types.h15
-rw-r--r--tools/arch/x86/include/asm/insn.h (renamed from tools/perf/util/intel-pt-decoder/insn.h)15
-rw-r--r--tools/arch/x86/include/asm/orc_types.h (renamed from tools/objtool/arch/x86/include/asm/orc_types.h)14
-rw-r--r--tools/arch/x86/include/uapi/asm/kvm.h55
-rw-r--r--tools/arch/x86/include/uapi/asm/perf_regs.h3
-rw-r--r--tools/arch/x86/include/uapi/asm/vmx.h1
-rw-r--r--tools/arch/x86/lib/inat.c (renamed from tools/perf/util/intel-pt-decoder/inat.c)19
-rw-r--r--tools/arch/x86/lib/insn.c (renamed from tools/perf/util/intel-pt-decoder/insn.c)19
-rw-r--r--tools/arch/x86/lib/memcpy_64.S1
-rw-r--r--tools/arch/x86/lib/x86-opcode-map.txt (renamed from tools/objtool/arch/x86/lib/x86-opcode-map.txt)0
-rw-r--r--tools/arch/x86/tools/gen-insn-attr-x86.awk (renamed from tools/objtool/arch/x86/tools/gen-insn-attr-x86.awk)0
-rw-r--r--tools/bpf/.gitignore1
-rw-r--r--tools/bpf/Makefile5
-rw-r--r--tools/bpf/Makefile.helpers1
-rw-r--r--tools/bpf/bpf_asm.c2
-rw-r--r--tools/bpf/bpf_dbg.c2
-rw-r--r--tools/bpf/bpf_jit_disasm.c2
-rw-r--r--tools/bpf/bpftool/.gitignore2
-rw-r--r--tools/bpf/bpftool/Documentation/Makefile1
-rw-r--r--tools/bpf/bpftool/Documentation/bpftool-btf.rst46
-rw-r--r--tools/bpf/bpftool/Documentation/bpftool-cgroup.rst31
-rw-r--r--tools/bpf/bpftool/Documentation/bpftool-feature.rst4
-rw-r--r--tools/bpf/bpftool/Documentation/bpftool-map.rst15
-rw-r--r--tools/bpf/bpftool/Documentation/bpftool-net.rst61
-rw-r--r--tools/bpf/bpftool/Documentation/bpftool-perf.rst4
-rw-r--r--tools/bpf/bpftool/Documentation/bpftool-prog.rst42
-rw-r--r--tools/bpf/bpftool/Documentation/bpftool.rst4
-rw-r--r--tools/bpf/bpftool/Makefile43
-rw-r--r--tools/bpf/bpftool/bash-completion/bpftool179
-rw-r--r--tools/bpf/bpftool/btf.c508
-rw-r--r--tools/bpf/bpftool/btf_dumper.c8
-rw-r--r--tools/bpf/bpftool/cgroup.c97
-rw-r--r--tools/bpf/bpftool/common.c65
-rw-r--r--tools/bpf/bpftool/feature.c105
-rw-r--r--tools/bpf/bpftool/jit_disasm.c11
-rw-r--r--tools/bpf/bpftool/json_writer.c6
-rw-r--r--tools/bpf/bpftool/json_writer.h6
-rw-r--r--tools/bpf/bpftool/main.c45
-rw-r--r--tools/bpf/bpftool/main.h8
-rw-r--r--tools/bpf/bpftool/map.c69
-rw-r--r--tools/bpf/bpftool/map_perf_ring.c205
-rw-r--r--tools/bpf/bpftool/net.c178
-rw-r--r--tools/bpf/bpftool/perf.c4
-rw-r--r--tools/bpf/bpftool/prog.c385
-rw-r--r--tools/bpf/bpftool/xlated_dumper.c4
-rw-r--r--tools/build/Makefile.feature6
-rw-r--r--tools/build/Makefile.include1
-rw-r--r--tools/build/feature/Makefile14
-rw-r--r--tools/build/feature/test-all.c7
-rw-r--r--tools/build/feature/test-fortify-source.c1
-rw-r--r--tools/build/feature/test-gettid.c11
-rw-r--r--tools/build/feature/test-hello.c1
-rw-r--r--tools/build/feature/test-libcap.c20
-rw-r--r--tools/build/feature/test-libslang-include-subdir.c7
-rw-r--r--tools/build/feature/test-setns.c1
-rw-r--r--tools/cgroup/iocost_coef_gen.py178
-rw-r--r--tools/cgroup/iocost_monitor.py277
-rw-r--r--tools/crypto/getstat.c294
-rw-r--r--tools/firewire/nosy-dump.c15
-rw-r--r--tools/firmware/Makefile2
-rw-r--r--tools/firmware/ihex2fw.c5
-rw-r--r--tools/gpio/.gitignore2
-rw-r--r--tools/gpio/gpio-event-mon.c5
-rw-r--r--tools/gpio/gpio-hammer.c5
-rw-r--r--tools/gpio/gpio-utils.c5
-rw-r--r--tools/gpio/gpio-utils.h4
-rw-r--r--tools/gpio/lsgpio.c5
-rw-r--r--tools/hv/hv_fcopy_daemon.c11
-rwxr-xr-xtools/hv/hv_get_dhcp_info.sh2
-rw-r--r--tools/hv/hv_kvp_daemon.c10
-rwxr-xr-xtools/hv/hv_set_ifconfig.sh2
-rw-r--r--tools/hv/hv_vss_daemon.c17
-rw-r--r--tools/hv/lsvmbus75
-rw-r--r--tools/iio/.gitignore4
-rw-r--r--tools/iio/iio_event_monitor.c5
-rw-r--r--tools/iio/iio_generic_buffer.c6
-rw-r--r--tools/iio/iio_utils.c9
-rw-r--r--tools/iio/iio_utils.h5
-rw-r--r--tools/iio/lsiio.c5
-rw-r--r--tools/include/asm-generic/barrier.h6
-rw-r--r--tools/include/linux/bitops.h1
-rw-r--r--tools/include/linux/bits.h17
-rw-r--r--tools/include/linux/compiler-gcc.h2
-rw-r--r--tools/include/linux/const.h9
-rw-r--r--tools/include/linux/ctype.h75
-rw-r--r--tools/include/linux/err.h2
-rw-r--r--tools/include/linux/kernel.h1
-rw-r--r--tools/include/linux/log2.h6
-rw-r--r--tools/include/linux/rbtree.h14
-rw-r--r--tools/include/linux/rbtree_augmented.h14
-rw-r--r--tools/include/linux/rcu.h4
-rw-r--r--tools/include/linux/ring_buffer.h1
-rw-r--r--tools/include/linux/sizes.h48
-rw-r--r--tools/include/linux/string.h11
-rw-r--r--tools/include/linux/zalloc.h12
-rw-r--r--tools/include/uapi/asm-generic/mman-common.h15
-rw-r--r--tools/include/uapi/asm-generic/mman.h10
-rw-r--r--tools/include/uapi/asm-generic/socket.h147
-rw-r--r--tools/include/uapi/asm-generic/unistd.h20
-rw-r--r--tools/include/uapi/asm/bitsperlong.h18
-rw-r--r--tools/include/uapi/drm/drm.h38
-rw-r--r--tools/include/uapi/drm/i915_drm.h461
-rw-r--r--tools/include/uapi/linux/bpf.h143
-rw-r--r--tools/include/uapi/linux/btf.h2
-rw-r--r--tools/include/uapi/linux/const.h31
-rw-r--r--tools/include/uapi/linux/fcntl.h2
-rw-r--r--tools/include/uapi/linux/fs.h4
-rw-r--r--tools/include/uapi/linux/if_link.h6
-rw-r--r--tools/include/uapi/linux/if_tun.h114
-rw-r--r--tools/include/uapi/linux/if_xdp.h30
-rw-r--r--tools/include/uapi/linux/kvm.h26
-rw-r--r--tools/include/uapi/linux/mount.h62
-rw-r--r--tools/include/uapi/linux/perf_event.h3
-rw-r--r--tools/include/uapi/linux/pkt_cls.h2
-rw-r--r--tools/include/uapi/linux/sched.h31
-rw-r--r--tools/include/uapi/linux/usbdevice_fs.h26
-rw-r--r--tools/io_uring/Makefile2
-rw-r--r--tools/io_uring/io_uring-cp.c21
-rw-r--r--tools/io_uring/liburing.h64
-rw-r--r--tools/io_uring/queue.c36
-rw-r--r--tools/io_uring/setup.c10
-rw-r--r--tools/io_uring/syscall.c48
-rwxr-xr-xtools/kvm/kvm_stat/kvm_stat19
-rw-r--r--tools/kvm/kvm_stat/kvm_stat.txt2
-rw-r--r--tools/laptop/freefall/freefall.c3
-rwxr-xr-xtools/leds/get_led_device_info.sh201
-rw-r--r--tools/lib/api/fd/array.c3
-rw-r--r--tools/lib/argv_split.c100
-rw-r--r--tools/lib/bitmap.c4
-rw-r--r--tools/lib/bpf/Build4
-rw-r--r--tools/lib/bpf/Makefile36
-rw-r--r--tools/lib/bpf/README.rst3
-rw-r--r--tools/lib/bpf/bpf.c32
-rw-r--r--tools/lib/bpf/bpf.h2
-rw-r--r--tools/lib/bpf/bpf_prog_linfo.c5
-rw-r--r--tools/lib/bpf/btf.c585
-rw-r--r--tools/lib/bpf/btf.h202
-rw-r--r--tools/lib/bpf/btf_dump.c1287
-rw-r--r--tools/lib/bpf/hashmap.c229
-rw-r--r--tools/lib/bpf/hashmap.h178
-rw-r--r--tools/lib/bpf/libbpf.c2860
-rw-r--r--tools/lib/bpf/libbpf.h158
-rw-r--r--tools/lib/bpf/libbpf.map26
-rw-r--r--tools/lib/bpf/libbpf_internal.h131
-rw-r--r--tools/lib/bpf/libbpf_probes.c15
-rw-r--r--tools/lib/bpf/libbpf_util.h13
-rw-r--r--tools/lib/bpf/str_error.c2
-rw-r--r--tools/lib/bpf/xsk.c214
-rw-r--r--tools/lib/bpf/xsk.h35
-rw-r--r--tools/lib/ctype.c35
-rw-r--r--tools/lib/find_bit.c6
-rw-r--r--tools/lib/rbtree.c14
-rw-r--r--tools/lib/string.c55
-rw-r--r--tools/lib/symbol/kallsyms.c14
-rw-r--r--tools/lib/symbol/kallsyms.h2
-rw-r--r--tools/lib/traceevent/Makefile10
-rw-r--r--tools/lib/traceevent/event-parse-api.c40
-rw-r--r--tools/lib/traceevent/event-parse-local.h6
-rw-r--r--tools/lib/traceevent/event-parse.c391
-rw-r--r--tools/lib/traceevent/event-parse.h30
-rw-r--r--tools/lib/traceevent/event-plugin.c2
-rw-r--r--tools/lib/vsprintf.c19
-rw-r--r--tools/lib/zalloc.c15
-rw-r--r--tools/memory-model/Documentation/explanation.txt53
-rw-r--r--tools/memory-model/README18
-rw-r--r--tools/memory-model/linux-kernel.bell6
-rw-r--r--tools/memory-model/linux-kernel.cat102
-rw-r--r--tools/memory-model/linux-kernel.def1
-rw-r--r--tools/memory-model/litmus-tests/MP+poonceonces.litmus2
-rw-r--r--tools/memory-model/litmus-tests/README2
-rw-r--r--tools/memory-model/lock.cat2
-rw-r--r--tools/memory-model/scripts/README4
-rwxr-xr-xtools/memory-model/scripts/checkalllitmus.sh2
-rwxr-xr-x[-rw-r--r--]tools/memory-model/scripts/checkghlitmus.sh0
-rwxr-xr-xtools/memory-model/scripts/checklitmus.sh2
-rwxr-xr-x[-rw-r--r--]tools/memory-model/scripts/checklitmushist.sh0
-rwxr-xr-x[-rw-r--r--]tools/memory-model/scripts/cmplitmushist.sh0
-rwxr-xr-x[-rw-r--r--]tools/memory-model/scripts/initlitmushist.sh0
-rwxr-xr-x[-rw-r--r--]tools/memory-model/scripts/judgelitmus.sh0
-rwxr-xr-x[-rw-r--r--]tools/memory-model/scripts/newlitmushist.sh0
-rwxr-xr-x[-rw-r--r--]tools/memory-model/scripts/parseargs.sh2
-rwxr-xr-x[-rw-r--r--]tools/memory-model/scripts/runlitmushist.sh2
-rw-r--r--tools/objtool/Build5
-rw-r--r--tools/objtool/Documentation/stack-validation.txt4
-rw-r--r--tools/objtool/Makefile6
-rw-r--r--tools/objtool/arch.h50
-rw-r--r--tools/objtool/arch/x86/Build4
-rw-r--r--tools/objtool/arch/x86/decode.c20
-rw-r--r--tools/objtool/arch/x86/include/asm/inat.h244
-rw-r--r--tools/objtool/arch/x86/include/asm/inat_types.h29
-rw-r--r--tools/objtool/arch/x86/include/asm/insn.h229
-rw-r--r--tools/objtool/arch/x86/lib/inat.c97
-rw-r--r--tools/objtool/arch/x86/lib/insn.c606
-rw-r--r--tools/objtool/builtin-check.c14
-rw-r--r--tools/objtool/builtin-orc.c14
-rw-r--r--tools/objtool/builtin.h14
-rw-r--r--tools/objtool/cfi.h14
-rw-r--r--tools/objtool/check.c354
-rw-r--r--tools/objtool/check.h20
-rw-r--r--tools/objtool/elf.c22
-rw-r--r--tools/objtool/elf.h19
-rw-r--r--tools/objtool/objtool.c14
-rw-r--r--tools/objtool/orc.h14
-rw-r--r--tools/objtool/orc_dump.c14
-rw-r--r--tools/objtool/orc_gen.c14
-rw-r--r--tools/objtool/special.c14
-rw-r--r--tools/objtool/special.h14
-rwxr-xr-xtools/objtool/sync-check.sh44
-rw-r--r--tools/objtool/warn.h14
-rw-r--r--tools/pci/Makefile5
-rw-r--r--tools/pci/pcitest.c21
-rw-r--r--tools/pcmcia/crc32hash.c1
-rw-r--r--tools/perf/.gitignore3
-rw-r--r--tools/perf/Documentation/Makefile3
-rw-r--r--tools/perf/Documentation/db-export.txt41
-rw-r--r--tools/perf/Documentation/intel-pt.txt55
-rw-r--r--tools/perf/Documentation/itrace.txt2
-rw-r--r--tools/perf/Documentation/perf-config.txt13
-rw-r--r--tools/perf/Documentation/perf-diff.txt31
-rw-r--r--tools/perf/Documentation/perf-probe.txt3
-rw-r--r--tools/perf/Documentation/perf-record.txt24
-rw-r--r--tools/perf/Documentation/perf-report.txt28
-rw-r--r--tools/perf/Documentation/perf-script.txt34
-rw-r--r--tools/perf/Documentation/perf-stat.txt10
-rw-r--r--tools/perf/Documentation/perf-top.txt43
-rw-r--r--tools/perf/Documentation/perf-trace.txt9
-rw-r--r--tools/perf/Documentation/perf.data-file-format.txt104
-rw-r--r--tools/perf/Documentation/tips.txt2
-rw-r--r--tools/perf/MANIFEST3
-rw-r--r--tools/perf/Makefile.config40
-rw-r--r--tools/perf/Makefile.perf78
-rw-r--r--tools/perf/arch/arm/Makefile1
-rw-r--r--tools/perf/arch/arm/annotate/instructions.c2
-rw-r--r--tools/perf/arch/arm/util/auxtrace.c10
-rw-r--r--tools/perf/arch/arm/util/cs-etm.c398
-rw-r--r--tools/perf/arch/arm/util/dwarf-regs.c5
-rw-r--r--tools/perf/arch/arm64/Build2
-rw-r--r--tools/perf/arch/arm64/annotate/instructions.c1
-rwxr-xr-xtools/perf/arch/arm64/entry/syscalls/mksyscalltbl2
-rw-r--r--tools/perf/arch/arm64/tests/Build2
-rw-r--r--tools/perf/arch/arm64/util/arm-spe.c31
-rw-r--r--tools/perf/arch/arm64/util/dwarf-regs.c5
-rw-r--r--tools/perf/arch/arm64/util/header.c7
-rw-r--r--tools/perf/arch/arm64/util/sym-handling.c12
-rw-r--r--tools/perf/arch/common.c6
-rw-r--r--tools/perf/arch/common.h4
-rw-r--r--tools/perf/arch/csky/Makefile1
-rw-r--r--tools/perf/arch/csky/annotate/instructions.c48
-rw-r--r--tools/perf/arch/powerpc/entry/syscalls/syscall.tbl146
-rw-r--r--tools/perf/arch/powerpc/util/dwarf-regs.c6
-rw-r--r--tools/perf/arch/powerpc/util/kvm-stat.c12
-rw-r--r--tools/perf/arch/powerpc/util/mem-events.c1
-rw-r--r--tools/perf/arch/powerpc/util/perf_regs.c5
-rw-r--r--tools/perf/arch/powerpc/util/skip-callchain-idx.c6
-rw-r--r--tools/perf/arch/powerpc/util/sym-handling.c5
-rw-r--r--tools/perf/arch/powerpc/util/unwind-libdw.c1
-rw-r--r--tools/perf/arch/powerpc/util/unwind-libunwind.c6
-rw-r--r--tools/perf/arch/riscv/Build1
-rw-r--r--tools/perf/arch/riscv/Makefile4
-rw-r--r--tools/perf/arch/riscv/include/perf_regs.h96
-rw-r--r--tools/perf/arch/riscv/util/Build2
-rw-r--r--tools/perf/arch/riscv/util/dwarf-regs.c72
-rw-r--r--tools/perf/arch/riscv/util/unwind-libdw.c57
-rw-r--r--tools/perf/arch/s390/Makefile1
-rw-r--r--tools/perf/arch/s390/util/auxtrace.c16
-rw-r--r--tools/perf/arch/s390/util/header.c10
-rw-r--r--tools/perf/arch/s390/util/kvm-stat.c14
-rw-r--r--tools/perf/arch/s390/util/machine.c40
-rw-r--r--tools/perf/arch/sh/Makefile1
-rw-r--r--tools/perf/arch/sh/util/dwarf-regs.c16
-rw-r--r--tools/perf/arch/sparc/Makefile1
-rw-r--r--tools/perf/arch/sparc/util/dwarf-regs.c6
-rw-r--r--tools/perf/arch/x86/entry/syscalls/syscall_64.tbl8
-rw-r--r--tools/perf/arch/x86/include/arch-tests.h1
-rw-r--r--tools/perf/arch/x86/include/perf_regs.h1
-rw-r--r--tools/perf/arch/x86/tests/Build2
-rw-r--r--tools/perf/arch/x86/tests/arch-tests.c4
-rw-r--r--tools/perf/arch/x86/tests/bp-modify.c1
-rw-r--r--tools/perf/arch/x86/tests/gen-insn-x86-dat.awk9
-rwxr-xr-xtools/perf/arch/x86/tests/gen-insn-x86-dat.sh9
-rw-r--r--tools/perf/arch/x86/tests/insn-x86.c3
-rw-r--r--tools/perf/arch/x86/tests/intel-cqm.c10
-rw-r--r--tools/perf/arch/x86/tests/intel-pt-pkt-decoder-test.c304
-rw-r--r--tools/perf/arch/x86/tests/perf-time-to-tsc.c34
-rw-r--r--tools/perf/arch/x86/tests/rdpmc.c4
-rw-r--r--tools/perf/arch/x86/util/archinsn.c3
-rw-r--r--tools/perf/arch/x86/util/auxtrace.c21
-rw-r--r--tools/perf/arch/x86/util/dwarf-regs.c16
-rw-r--r--tools/perf/arch/x86/util/event.c2
-rw-r--r--tools/perf/arch/x86/util/header.c1
-rw-r--r--tools/perf/arch/x86/util/intel-bts.c62
-rw-r--r--tools/perf/arch/x86/util/intel-pt.c135
-rw-r--r--tools/perf/arch/x86/util/kvm-stat.c17
-rw-r--r--tools/perf/arch/x86/util/machine.c3
-rw-r--r--tools/perf/arch/x86/util/perf_regs.c10
-rw-r--r--tools/perf/arch/x86/util/tsc.c8
-rw-r--r--tools/perf/arch/xtensa/Makefile1
-rw-r--r--tools/perf/arch/xtensa/util/dwarf-regs.c6
-rw-r--r--tools/perf/bench/epoll-ctl.c8
-rw-r--r--tools/perf/bench/epoll-wait.c8
-rw-r--r--tools/perf/bench/futex-hash.c8
-rw-r--r--tools/perf/bench/futex-lock-pi.c10
-rw-r--r--tools/perf/bench/futex-requeue.c7
-rw-r--r--tools/perf/bench/futex-wake-parallel.c6
-rw-r--r--tools/perf/bench/futex-wake.c7
-rw-r--r--tools/perf/bench/mem-functions.c5
-rw-r--r--tools/perf/bench/numa.c9
-rw-r--r--tools/perf/bench/sched-messaging.c1
-rw-r--r--tools/perf/bench/sched-pipe.c1
-rw-r--r--tools/perf/builtin-annotate.c22
-rw-r--r--tools/perf/builtin-bench.c3
-rw-r--r--tools/perf/builtin-buildid-cache.c6
-rw-r--r--tools/perf/builtin-buildid-list.c4
-rw-r--r--tools/perf/builtin-c2c.c24
-rw-r--r--tools/perf/builtin-config.c4
-rw-r--r--tools/perf/builtin-data.c2
-rw-r--r--tools/perf/builtin-diff.c408
-rw-r--r--tools/perf/builtin-evlist.c4
-rw-r--r--tools/perf/builtin-ftrace.c42
-rw-r--r--tools/perf/builtin-help.c7
-rw-r--r--tools/perf/builtin-inject.c64
-rw-r--r--tools/perf/builtin-kallsyms.c4
-rw-r--r--tools/perf/builtin-kmem.c34
-rw-r--r--tools/perf/builtin-kvm.c53
-rw-r--r--tools/perf/builtin-list.c5
-rw-r--r--tools/perf/builtin-lock.c44
-rw-r--r--tools/perf/builtin-mem.c4
-rw-r--r--tools/perf/builtin-probe.c33
-rw-r--r--tools/perf/builtin-record.c105
-rw-r--r--tools/perf/builtin-report.c72
-rw-r--r--tools/perf/builtin-sched.c106
-rw-r--r--tools/perf/builtin-script.c306
-rw-r--r--tools/perf/builtin-stat.c235
-rw-r--r--tools/perf/builtin-timechart.c66
-rw-r--r--tools/perf/builtin-top.c176
-rw-r--r--tools/perf/builtin-trace.c792
-rw-r--r--tools/perf/builtin-version.c3
-rw-r--r--tools/perf/builtin.h2
-rwxr-xr-xtools/perf/check-headers.sh15
-rw-r--r--tools/perf/examples/bpf/augmented_raw_syscalls.c448
-rw-r--r--tools/perf/include/bpf/bpf.h2
-rw-r--r--tools/perf/jvmti/jvmti_agent.c2
-rw-r--r--tools/perf/jvmti/libjvmti.c4
-rw-r--r--tools/perf/lib/Build12
-rw-r--r--tools/perf/lib/Documentation/Makefile7
-rw-r--r--tools/perf/lib/Documentation/man/libperf.rst100
-rw-r--r--tools/perf/lib/Documentation/tutorial/tutorial.rst123
-rw-r--r--tools/perf/lib/Makefile158
-rw-r--r--tools/perf/lib/core.c34
-rw-r--r--tools/perf/lib/cpumap.c262
-rw-r--r--tools/perf/lib/evlist.c159
-rw-r--r--tools/perf/lib/evsel.c232
-rw-r--r--tools/perf/lib/include/internal/cpumap.h19
-rw-r--r--tools/perf/lib/include/internal/evlist.h50
-rw-r--r--tools/perf/lib/include/internal/evsel.h29
-rw-r--r--tools/perf/lib/include/internal/lib.h10
-rw-r--r--tools/perf/lib/include/internal/tests.h19
-rw-r--r--tools/perf/lib/include/internal/threadmap.h23
-rw-r--r--tools/perf/lib/include/internal/xyarray.h (renamed from tools/perf/util/xyarray.h)9
-rw-r--r--tools/perf/lib/include/perf/core.h22
-rw-r--r--tools/perf/lib/include/perf/cpumap.h25
-rw-r--r--tools/perf/lib/include/perf/event.h385
-rw-r--r--tools/perf/lib/include/perf/evlist.h35
-rw-r--r--tools/perf/lib/include/perf/evsel.h39
-rw-r--r--tools/perf/lib/include/perf/threadmap.h20
-rw-r--r--tools/perf/lib/internal.h18
-rw-r--r--tools/perf/lib/lib.c46
-rw-r--r--tools/perf/lib/libperf.map43
-rw-r--r--tools/perf/lib/libperf.pc.template11
-rw-r--r--tools/perf/lib/tests/Makefile38
-rw-r--r--tools/perf/lib/tests/test-cpumap.c21
-rw-r--r--tools/perf/lib/tests/test-evlist.c186
-rw-r--r--tools/perf/lib/tests/test-evsel.c125
-rw-r--r--tools/perf/lib/tests/test-threadmap.c21
-rw-r--r--tools/perf/lib/threadmap.c91
-rw-r--r--tools/perf/lib/xyarray.c33
-rw-r--r--tools/perf/perf-sys.h51
-rw-r--r--tools/perf/perf-with-kcore.sh14
-rw-r--r--tools/perf/perf.c12
-rw-r--r--tools/perf/perf.h82
-rw-r--r--tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-ddrc.json44
-rw-r--r--tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-hha.json51
-rw-r--r--tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-l3c.json37
-rw-r--r--tools/perf/pmu-events/arch/powerpc/power9/memory.json2
-rw-r--r--tools/perf/pmu-events/arch/powerpc/power9/other.json8
-rw-r--r--tools/perf/pmu-events/arch/s390/cf_m8561/basic.json58
-rw-r--r--tools/perf/pmu-events/arch/s390/cf_m8561/crypto.json114
-rw-r--r--tools/perf/pmu-events/arch/s390/cf_m8561/crypto6.json30
-rw-r--r--tools/perf/pmu-events/arch/s390/cf_m8561/extended.json373
-rw-r--r--tools/perf/pmu-events/arch/s390/mapfile.csv1
-rw-r--r--tools/perf/pmu-events/arch/x86/cascadelakex/clx-metrics.json4
-rw-r--r--tools/perf/pmu-events/arch/x86/icelake/cache.json552
-rw-r--r--tools/perf/pmu-events/arch/x86/icelake/floating-point.json102
-rw-r--r--tools/perf/pmu-events/arch/x86/icelake/frontend.json424
-rw-r--r--tools/perf/pmu-events/arch/x86/icelake/memory.json410
-rw-r--r--tools/perf/pmu-events/arch/x86/icelake/other.json121
-rw-r--r--tools/perf/pmu-events/arch/x86/icelake/pipeline.json892
-rw-r--r--tools/perf/pmu-events/arch/x86/icelake/virtual-memory.json236
-rw-r--r--tools/perf/pmu-events/arch/x86/mapfile.csv3
-rw-r--r--tools/perf/pmu-events/arch/x86/skylakex/skx-metrics.json22
-rw-r--r--tools/perf/pmu-events/arch/x86/tremontx/cache.json111
-rw-r--r--tools/perf/pmu-events/arch/x86/tremontx/frontend.json26
-rw-r--r--tools/perf/pmu-events/arch/x86/tremontx/memory.json26
-rw-r--r--tools/perf/pmu-events/arch/x86/tremontx/other.json26
-rw-r--r--tools/perf/pmu-events/arch/x86/tremontx/pipeline.json111
-rw-r--r--tools/perf/pmu-events/arch/x86/tremontx/uncore-memory.json73
-rw-r--r--tools/perf/pmu-events/arch/x86/tremontx/uncore-other.json431
-rw-r--r--tools/perf/pmu-events/arch/x86/tremontx/uncore-power.json11
-rw-r--r--tools/perf/pmu-events/arch/x86/tremontx/virtual-memory.json86
-rw-r--r--tools/perf/pmu-events/jevents.c10
-rwxr-xr-xtools/perf/python/twatch.py9
-rw-r--r--tools/perf/scripts/perl/Perf-Trace-Util/Context.c18
-rw-r--r--tools/perf/scripts/perl/rw-by-file.pl2
-rw-r--r--tools/perf/scripts/perl/rw-by-pid.pl2
-rw-r--r--tools/perf/scripts/perl/rwtop.pl2
-rw-r--r--tools/perf/scripts/perl/wakeup-latency.pl2
-rw-r--r--tools/perf/scripts/python/Perf-Trace-Util/Context.c17
-rw-r--r--tools/perf/scripts/python/export-to-postgresql.py398
-rw-r--r--tools/perf/scripts/python/export-to-sqlite.py373
-rwxr-xr-xtools/perf/scripts/python/exported-sql-viewer.py379
-rw-r--r--tools/perf/tests/Build4
-rw-r--r--tools/perf/tests/attr.c3
-rw-r--r--tools/perf/tests/backward-ring-buffer.c22
-rw-r--r--tools/perf/tests/bitmap.c5
-rw-r--r--tools/perf/tests/bp_account.c4
-rw-r--r--tools/perf/tests/bp_signal.c3
-rw-r--r--tools/perf/tests/bp_signal_overflow.c3
-rw-r--r--tools/perf/tests/bpf-script-example.c1
-rw-r--r--tools/perf/tests/bpf-script-test-kbuild.c1
-rw-r--r--tools/perf/tests/bpf-script-test-prologue.c1
-rw-r--r--tools/perf/tests/bpf-script-test-relocation.c1
-rw-r--r--tools/perf/tests/bpf.c16
-rw-r--r--tools/perf/tests/builtin-test.c18
-rw-r--r--tools/perf/tests/code-reading.c61
-rw-r--r--tools/perf/tests/cpumap.c33
-rw-r--r--tools/perf/tests/dso-data.c1
-rw-r--r--tools/perf/tests/dwarf-unwind.c6
-rw-r--r--tools/perf/tests/event-times.c83
-rw-r--r--tools/perf/tests/event_update.c32
-rw-r--r--tools/perf/tests/evsel-roundtrip-name.c12
-rw-r--r--tools/perf/tests/evsel-tp-sched.c8
-rw-r--r--tools/perf/tests/expr.c4
-rw-r--r--tools/perf/tests/hists_common.c3
-rw-r--r--tools/perf/tests/hists_cumulate.c20
-rw-r--r--tools/perf/tests/hists_filter.c12
-rw-r--r--tools/perf/tests/hists_link.c12
-rw-r--r--tools/perf/tests/hists_output.c22
-rw-r--r--tools/perf/tests/keep-tracking.c47
-rw-r--r--tools/perf/tests/kmod-path.c2
-rw-r--r--tools/perf/tests/llvm.c3
-rw-r--r--tools/perf/tests/make1
-rw-r--r--tools/perf/tests/map_groups.c121
-rw-r--r--tools/perf/tests/mem.c2
-rw-r--r--tools/perf/tests/mem2node.c12
-rw-r--r--tools/perf/tests/mmap-basic.c31
-rw-r--r--tools/perf/tests/mmap-thread-lookup.c6
-rw-r--r--tools/perf/tests/openat-syscall-all-cpus.c20
-rw-r--r--tools/perf/tests/openat-syscall-tp-fields.c18
-rw-r--r--tools/perf/tests/openat-syscall.c12
-rw-r--r--tools/perf/tests/parse-events.c1248
-rw-r--r--tools/perf/tests/parse-no-sample-id-all.c10
-rw-r--r--tools/perf/tests/perf-record.c13
-rw-r--r--tools/perf/tests/sample-parsing.c17
-rw-r--r--tools/perf/tests/sdt.c4
-rw-r--r--tools/perf/tests/shell/lib/probe.sh1
-rwxr-xr-xtools/perf/tests/shell/probe_vfs_getname.sh3
-rwxr-xr-xtools/perf/tests/shell/record+probe_libc_inet_pton.sh3
-rwxr-xr-xtools/perf/tests/shell/record+script_probe_vfs_getname.sh1
-rwxr-xr-xtools/perf/tests/shell/record+zstd_comp_decomp.sh4
-rwxr-xr-xtools/perf/tests/shell/trace+probe_vfs_getname.sh5
-rw-r--r--tools/perf/tests/stat.c8
-rw-r--r--tools/perf/tests/sw-clock.c35
-rw-r--r--tools/perf/tests/switch-tracking.c70
-rw-r--r--tools/perf/tests/task-exit.c38
-rw-r--r--tools/perf/tests/tests.h2
-rw-r--r--tools/perf/tests/thread-map.c46
-rw-r--r--tools/perf/tests/thread-mg-share.c1
-rw-r--r--tools/perf/tests/time-utils-test.c251
-rw-r--r--tools/perf/tests/topology.c9
-rw-r--r--tools/perf/tests/unit_number__scnprintf.c1
-rw-r--r--tools/perf/tests/vmlinux-kallsyms.c11
-rw-r--r--tools/perf/tests/wp.c5
-rw-r--r--tools/perf/trace/beauty/Build4
-rw-r--r--tools/perf/trace/beauty/beauty.h15
-rw-r--r--tools/perf/trace/beauty/clone.c1
-rwxr-xr-xtools/perf/trace/beauty/fsconfig.sh17
-rw-r--r--tools/perf/trace/beauty/fsmount.c34
-rwxr-xr-xtools/perf/trace/beauty/fsmount.sh22
-rw-r--r--tools/perf/trace/beauty/fspick.c24
-rwxr-xr-xtools/perf/trace/beauty/fspick.sh17
-rw-r--r--tools/perf/trace/beauty/ioctl.c2
-rw-r--r--tools/perf/trace/beauty/move_mount.c24
-rwxr-xr-xtools/perf/trace/beauty/move_mount_flags.sh17
-rw-r--r--tools/perf/trace/beauty/sync_file_range.c31
-rwxr-xr-xtools/perf/trace/beauty/sync_file_range.sh17
-rwxr-xr-xtools/perf/trace/beauty/usbdevfs_ioctl.sh9
-rw-r--r--tools/perf/ui/browser.c16
-rw-r--r--tools/perf/ui/browser.h2
-rw-r--r--tools/perf/ui/browsers/annotate.c25
-rw-r--r--tools/perf/ui/browsers/header.c1
-rw-r--r--tools/perf/ui/browsers/hists.c90
-rw-r--r--tools/perf/ui/browsers/map.c4
-rw-r--r--tools/perf/ui/browsers/res_sample.c15
-rw-r--r--tools/perf/ui/browsers/scripts.c20
-rw-r--r--tools/perf/ui/gtk/annotate.c11
-rw-r--r--tools/perf/ui/gtk/browser.c2
-rw-r--r--tools/perf/ui/gtk/gtk.h8
-rw-r--r--tools/perf/ui/gtk/helpline.c1
-rw-r--r--tools/perf/ui/gtk/hists.c12
-rw-r--r--tools/perf/ui/gtk/setup.c1
-rw-r--r--tools/perf/ui/gtk/util.c4
-rw-r--r--tools/perf/ui/helpline.c4
-rw-r--r--tools/perf/ui/helpline.h2
-rw-r--r--tools/perf/ui/hist.c20
-rw-r--r--tools/perf/ui/libslang.h5
-rw-r--r--tools/perf/ui/progress.c1
-rw-r--r--tools/perf/ui/setup.c5
-rw-r--r--tools/perf/ui/stdio/hist.c47
-rw-r--r--tools/perf/ui/tui/helpline.c2
-rw-r--r--tools/perf/ui/tui/progress.c1
-rw-r--r--tools/perf/ui/tui/setup.c4
-rw-r--r--tools/perf/ui/tui/util.c40
-rw-r--r--tools/perf/ui/util.c4
-rw-r--r--tools/perf/ui/util.h2
-rw-r--r--tools/perf/util/Build16
-rwxr-xr-xtools/perf/util/PERF-VERSION-GEN2
-rw-r--r--tools/perf/util/annotate.c90
-rw-r--r--tools/perf/util/annotate.h28
-rw-r--r--tools/perf/util/arm-spe.c12
-rw-r--r--tools/perf/util/auxtrace.c129
-rw-r--r--tools/perf/util/auxtrace.h134
-rw-r--r--tools/perf/util/bpf-event.c39
-rw-r--r--tools/perf/util/bpf-event.h15
-rw-r--r--tools/perf/util/bpf-loader.c46
-rw-r--r--tools/perf/util/bpf-loader.h30
-rw-r--r--tools/perf/util/bpf-prologue.c2
-rw-r--r--tools/perf/util/branch.c3
-rw-r--r--tools/perf/util/branch.h8
-rw-r--r--tools/perf/util/build-id.c8
-rw-r--r--tools/perf/util/build-id.h2
-rw-r--r--tools/perf/util/c++/clang-c.h2
-rw-r--r--tools/perf/util/c++/clang-test.cpp4
-rw-r--r--tools/perf/util/cacheline.c25
-rw-r--r--tools/perf/util/cacheline.h21
-rw-r--r--tools/perf/util/call-path.c16
-rw-r--r--tools/perf/util/call-path.h11
-rw-r--r--tools/perf/util/callchain.c18
-rw-r--r--tools/perf/util/callchain.h3
-rw-r--r--tools/perf/util/cap.c29
-rw-r--r--tools/perf/util/cap.h32
-rw-r--r--tools/perf/util/cgroup.c29
-rw-r--r--tools/perf/util/cgroup.h6
-rw-r--r--tools/perf/util/cloexec.c4
-rw-r--r--tools/perf/util/color.c3
-rw-r--r--tools/perf/util/color_config.c3
-rw-r--r--tools/perf/util/comm.c2
-rw-r--r--tools/perf/util/config.c17
-rw-r--r--tools/perf/util/counts.c19
-rw-r--r--tools/perf/util/counts.h38
-rw-r--r--tools/perf/util/cpumap.c354
-rw-r--r--tools/perf/util/cpumap.h67
-rw-r--r--tools/perf/util/cputopo.c104
-rw-r--r--tools/perf/util/cputopo.h3
-rw-r--r--tools/perf/util/cs-etm-decoder/cs-etm-decoder.c269
-rw-r--r--tools/perf/util/cs-etm-decoder/cs-etm-decoder.h39
-rw-r--r--tools/perf/util/cs-etm.c1113
-rw-r--r--tools/perf/util/cs-etm.h97
-rw-r--r--tools/perf/util/ctype.c49
-rw-r--r--tools/perf/util/data-convert-bt.c49
-rw-r--r--tools/perf/util/data.c5
-rw-r--r--tools/perf/util/db-export.c316
-rw-r--r--tools/perf/util/db-export.h40
-rw-r--r--tools/perf/util/debug.c9
-rw-r--r--tools/perf/util/debug.h6
-rw-r--r--tools/perf/util/demangle-java.c5
-rw-r--r--tools/perf/util/dso.c370
-rw-r--r--tools/perf/util/dso.h28
-rw-r--r--tools/perf/util/dsos.c232
-rw-r--r--tools/perf/util/dsos.h44
-rw-r--r--tools/perf/util/dwarf-aux.c19
-rw-r--r--tools/perf/util/dwarf-aux.h18
-rw-r--r--tools/perf/util/env.c18
-rw-r--r--tools/perf/util/env.h5
-rw-r--r--tools/perf/util/event.c132
-rw-r--r--tools/perf/util/event.h471
-rw-r--r--tools/perf/util/events_stats.h51
-rw-r--r--tools/perf/util/evlist.c633
-rw-r--r--tools/perf/util/evlist.h219
-rw-r--r--tools/perf/util/evsel.c585
-rw-r--r--tools/perf/util/evsel.h222
-rw-r--r--tools/perf/util/evsel_fprintf.c16
-rw-r--r--tools/perf/util/evswitch.c61
-rw-r--r--tools/perf/util/evswitch.h31
-rw-r--r--tools/perf/util/expr.y2
-rw-r--r--tools/perf/util/genelf.c6
-rw-r--r--tools/perf/util/genelf_debug.c4
-rw-r--r--tools/perf/util/get_current_dir_name.c7
-rw-r--r--tools/perf/util/get_current_dir_name.h8
-rw-r--r--tools/perf/util/header.c444
-rw-r--r--tools/perf/util/header.h24
-rw-r--r--tools/perf/util/help-unknown-cmd.c2
-rw-r--r--tools/perf/util/hist.c126
-rw-r--r--tools/perf/util/hist.h53
-rw-r--r--tools/perf/util/include/linux/ctype.h1
-rw-r--r--tools/perf/util/intel-bts.c50
-rw-r--r--tools/perf/util/intel-bts.h11
-rw-r--r--tools/perf/util/intel-pt-decoder/Build22
-rw-r--r--tools/perf/util/intel-pt-decoder/gen-insn-attr-x86.awk392
-rw-r--r--tools/perf/util/intel-pt-decoder/inat_types.h29
-rw-r--r--tools/perf/util/intel-pt-decoder/intel-pt-decoder.c482
-rw-r--r--tools/perf/util/intel-pt-decoder/intel-pt-decoder.h155
-rw-r--r--tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c21
-rw-r--r--tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h11
-rw-r--r--tools/perf/util/intel-pt-decoder/intel-pt-log.c11
-rw-r--r--tools/perf/util/intel-pt-decoder/intel-pt-log.h11
-rw-r--r--tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c151
-rw-r--r--tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.h32
-rw-r--r--tools/perf/util/intel-pt-decoder/x86-opcode-map.txt1072
-rw-r--r--tools/perf/util/intel-pt.c892
-rw-r--r--tools/perf/util/intel-pt.h11
-rw-r--r--tools/perf/util/intlist.c3
-rw-r--r--tools/perf/util/jitdump.c18
-rw-r--r--tools/perf/util/jitdump.h1
-rw-r--r--tools/perf/util/kvm-stat.h24
-rw-r--r--tools/perf/util/llvm-utils.c5
-rw-r--r--tools/perf/util/llvm-utils.h2
-rw-r--r--tools/perf/util/lzma.c1
-rw-r--r--tools/perf/util/machine.c123
-rw-r--r--tools/perf/util/machine.h14
-rw-r--r--tools/perf/util/map.c23
-rw-r--r--tools/perf/util/map.h2
-rw-r--r--tools/perf/util/map_groups.h6
-rw-r--r--tools/perf/util/mem-events.c2
-rw-r--r--tools/perf/util/mem-events.h9
-rw-r--r--tools/perf/util/mem2node.c5
-rw-r--r--tools/perf/util/mem2node.h3
-rw-r--r--tools/perf/util/metricgroup.c140
-rw-r--r--tools/perf/util/metricgroup.h20
-rw-r--r--tools/perf/util/mmap.c14
-rw-r--r--tools/perf/util/mmap.h1
-rw-r--r--tools/perf/util/namespaces.c9
-rw-r--r--tools/perf/util/namespaces.h12
-rw-r--r--tools/perf/util/ordered-events.c7
-rw-r--r--tools/perf/util/parse-branch-options.c6
-rw-r--r--tools/perf/util/parse-events.c168
-rw-r--r--tools/perf/util/parse-events.h9
-rw-r--r--tools/perf/util/parse-events.l1
-rw-r--r--tools/perf/util/parse-events.y2
-rw-r--r--tools/perf/util/parse-regs-options.c8
-rw-r--r--tools/perf/util/path.c3
-rw-r--r--tools/perf/util/path.h3
-rw-r--r--tools/perf/util/perf-hooks.c1
-rw-r--r--tools/perf/util/perf_regs.h4
-rw-r--r--tools/perf/util/pmu.c98
-rw-r--r--tools/perf/util/pmu.h4
-rw-r--r--tools/perf/util/print_binary.c2
-rw-r--r--tools/perf/util/probe-event.c91
-rw-r--r--tools/perf/util/probe-event.h2
-rw-r--r--tools/perf/util/probe-file.c25
-rw-r--r--tools/perf/util/probe-file.h1
-rw-r--r--tools/perf/util/probe-finder.c38
-rw-r--r--tools/perf/util/probe-finder.h2
-rw-r--r--tools/perf/util/pstack.c3
-rw-r--r--tools/perf/util/python-ext-sources5
-rw-r--r--tools/perf/util/python.c140
-rw-r--r--tools/perf/util/rblist.c3
-rw-r--r--tools/perf/util/record.c78
-rw-r--r--tools/perf/util/record.h74
-rw-r--r--tools/perf/util/rlimit.c29
-rw-r--r--tools/perf/util/rlimit.h6
-rw-r--r--tools/perf/util/s390-cpumsf.c117
-rw-r--r--tools/perf/util/s390-sample-raw.c8
-rw-r--r--tools/perf/util/sample-raw.c2
-rw-r--r--tools/perf/util/sample-raw.h6
-rw-r--r--tools/perf/util/sane_ctype.h52
-rw-r--r--tools/perf/util/scripting-engines/trace-event-perl.c16
-rw-r--r--tools/perf/util/scripting-engines/trace-event-python.c149
-rw-r--r--tools/perf/util/session.c228
-rw-r--r--tools/perf/util/session.h15
-rw-r--r--tools/perf/util/setns.c4
-rw-r--r--tools/perf/util/setup.py7
-rw-r--r--tools/perf/util/smt.c8
-rw-r--r--tools/perf/util/sort.c81
-rw-r--r--tools/perf/util/sort.h47
-rw-r--r--tools/perf/util/srccode.c21
-rw-r--r--tools/perf/util/srcline.c9
-rw-r--r--tools/perf/util/stat-display.c158
-rw-r--r--tools/perf/util/stat-shadow.c161
-rw-r--r--tools/perf/util/stat.c92
-rw-r--r--tools/perf/util/stat.h43
-rw-r--r--tools/perf/util/strbuf.c8
-rw-r--r--tools/perf/util/strfilter.c9
-rw-r--r--tools/perf/util/string.c169
-rw-r--r--tools/perf/util/string2.h15
-rw-r--r--tools/perf/util/strlist.c5
-rw-r--r--tools/perf/util/svghelper.c70
-rw-r--r--tools/perf/util/svghelper.h4
-rw-r--r--tools/perf/util/symbol-elf.c31
-rw-r--r--tools/perf/util/symbol-minimal.c5
-rw-r--r--tools/perf/util/symbol.c149
-rw-r--r--tools/perf/util/symbol.h81
-rw-r--r--tools/perf/util/symbol_conf.h5
-rw-r--r--tools/perf/util/symbol_fprintf.c1
-rw-r--r--tools/perf/util/symsrc.h46
-rw-r--r--tools/perf/util/syscalltbl.c13
-rw-r--r--tools/perf/util/syscalltbl.h1
-rw-r--r--tools/perf/util/target.c8
-rw-r--r--tools/perf/util/thread-stack.c77
-rw-r--r--tools/perf/util/thread-stack.h15
-rw-r--r--tools/perf/util/thread.c75
-rw-r--r--tools/perf/util/thread.h12
-rw-r--r--tools/perf/util/thread_map.c144
-rw-r--r--tools/perf/util/thread_map.h66
-rw-r--r--tools/perf/util/time-utils.c131
-rw-r--r--tools/perf/util/time-utils.h9
-rw-r--r--tools/perf/util/tool.h10
-rw-r--r--tools/perf/util/top.c16
-rw-r--r--tools/perf/util/top.h12
-rw-r--r--tools/perf/util/trace-event-info.c34
-rw-r--r--tools/perf/util/trace-event-parse.c23
-rw-r--r--tools/perf/util/trace-event-read.c19
-rw-r--r--tools/perf/util/trace-event-scripting.c21
-rw-r--r--tools/perf/util/trace-event.h8
-rw-r--r--tools/perf/util/trigger.h1
-rw-r--r--tools/perf/util/unwind-libdw.c2
-rw-r--r--tools/perf/util/unwind-libunwind-local.c21
-rw-r--r--tools/perf/util/unwind-libunwind.c41
-rw-r--r--tools/perf/util/unwind.h25
-rw-r--r--tools/perf/util/usage.c3
-rw-r--r--tools/perf/util/util-cxx.h27
-rw-r--r--tools/perf/util/util.c84
-rw-r--r--tools/perf/util/util.h23
-rw-r--r--tools/perf/util/values.c3
-rw-r--r--tools/perf/util/vdso.c2
-rw-r--r--tools/perf/util/xyarray.c2
-rw-r--r--tools/perf/util/zlib.c1
-rw-r--r--tools/perf/util/zstd.c4
-rw-r--r--tools/power/acpi/.gitignore8
-rw-r--r--tools/power/acpi/Makefile5
-rw-r--r--tools/power/acpi/Makefile.config5
-rw-r--r--tools/power/acpi/Makefile.rules5
-rw-r--r--tools/power/acpi/tools/acpidbg/Makefile5
-rw-r--r--tools/power/acpi/tools/acpidbg/acpidbg.c5
-rw-r--r--tools/power/acpi/tools/acpidump/Makefile5
-rw-r--r--tools/power/acpi/tools/acpidump/apfiles.c10
-rw-r--r--tools/power/acpi/tools/ec/Makefile5
-rw-r--r--tools/power/acpi/tools/ec/ec_access.c3
-rw-r--r--tools/power/cpupower/Makefile28
-rw-r--r--tools/power/cpupower/bench/benchmark.c15
-rw-r--r--tools/power/cpupower/bench/benchmark.h15
-rw-r--r--tools/power/cpupower/bench/config.h15
-rw-r--r--tools/power/cpupower/bench/cpufreq-bench_plot.sh17
-rw-r--r--tools/power/cpupower/bench/cpufreq-bench_script.sh17
-rw-r--r--tools/power/cpupower/bench/main.c15
-rw-r--r--tools/power/cpupower/bench/parse.c15
-rw-r--r--tools/power/cpupower/bench/parse.h15
-rw-r--r--tools/power/cpupower/bench/system.c15
-rw-r--r--tools/power/cpupower/bench/system.h15
-rw-r--r--tools/power/cpupower/debug/i386/centrino-decode.c3
-rw-r--r--tools/power/cpupower/debug/i386/intel_gsic.c3
-rw-r--r--tools/power/cpupower/debug/i386/powernow-k8-decode.c3
-rw-r--r--tools/power/cpupower/debug/kernel/Makefile4
-rw-r--r--tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c1
-rw-r--r--tools/power/cpupower/lib/cpufreq.c3
-rw-r--r--tools/power/cpupower/lib/cpufreq.h10
-rw-r--r--tools/power/cpupower/lib/cpuidle.c3
-rw-r--r--tools/power/cpupower/lib/cpupower.c3
-rw-r--r--tools/power/cpupower/man/cpupower-monitor.12
-rw-r--r--tools/power/cpupower/po/cs.po2
-rw-r--r--tools/power/cpupower/po/de.po346
-rw-r--r--tools/power/cpupower/po/fr.po2
-rw-r--r--tools/power/cpupower/po/it.po2
-rw-r--r--tools/power/cpupower/po/pt.po2
-rw-r--r--tools/power/cpupower/utils/cpufreq-info.c3
-rw-r--r--tools/power/cpupower/utils/cpufreq-set.c5
-rw-r--r--tools/power/cpupower/utils/cpuidle-info.c3
-rw-r--r--tools/power/cpupower/utils/cpupower-info.c3
-rw-r--r--tools/power/cpupower/utils/cpupower-set.c3
-rw-r--r--tools/power/cpupower/utils/cpupower.c3
-rw-r--r--tools/power/cpupower/utils/helpers/helpers.h3
-rw-r--r--tools/power/cpupower/utils/helpers/sysfs.c3
-rw-r--r--tools/power/cpupower/utils/helpers/topology.c3
-rw-r--r--tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c3
-rw-r--r--tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c4
-rw-r--r--tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c4
-rw-r--r--tools/power/cpupower/utils/idle_monitor/cpupower-monitor.h4
-rw-r--r--tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c3
-rw-r--r--tools/power/cpupower/utils/idle_monitor/idle_monitors.h4
-rw-r--r--tools/power/cpupower/utils/idle_monitor/mperf_monitor.c3
-rw-r--r--tools/power/cpupower/utils/idle_monitor/nhm_idle.c3
-rw-r--r--tools/power/cpupower/utils/idle_monitor/snb_idle.c3
-rw-r--r--tools/power/pm-graph/README556
-rwxr-xr-xtools/power/pm-graph/bootgraph.py59
-rw-r--r--tools/power/pm-graph/config/example.cfg26
-rw-r--r--tools/power/pm-graph/sleepgraph.816
-rwxr-xr-xtools/power/pm-graph/sleepgraph.py1375
-rw-r--r--tools/power/x86/intel-speed-select/.gitignore2
-rw-r--r--tools/power/x86/intel-speed-select/Build1
-rw-r--r--tools/power/x86/intel-speed-select/Makefile56
-rw-r--r--tools/power/x86/intel-speed-select/isst-config.c1612
-rw-r--r--tools/power/x86/intel-speed-select/isst-core.c743
-rw-r--r--tools/power/x86/intel-speed-select/isst-display.c529
-rw-r--r--tools/power/x86/intel-speed-select/isst.h232
-rwxr-xr-xtools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py1
-rw-r--r--tools/power/x86/turbostat/Makefile3
-rw-r--r--tools/power/x86/turbostat/turbostat.c229
-rw-r--r--tools/power/x86/x86_energy_perf_policy/Makefile3
-rw-r--r--tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.82
-rw-r--r--tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c31
-rw-r--r--tools/scripts/Makefile.include9
-rw-r--r--tools/spi/Makefile1
-rw-r--r--tools/spi/spidev_test.c5
-rw-r--r--tools/testing/fault-injection/failcmd.sh2
-rwxr-xr-xtools/testing/ktest/config-bisect.pl7
-rwxr-xr-xtools/testing/ktest/ktest.pl2
-rw-r--r--tools/testing/nvdimm/dax-dev.c10
-rw-r--r--tools/testing/nvdimm/dimm_devs.c19
-rw-r--r--tools/testing/nvdimm/pmem-dax.c10
-rw-r--r--tools/testing/nvdimm/test/iomap.c64
-rw-r--r--tools/testing/nvdimm/test/nfit.c13
-rw-r--r--tools/testing/nvdimm/test/nfit_test.h10
-rw-r--r--tools/testing/radix-tree/benchmark.c10
-rw-r--r--tools/testing/radix-tree/idr-test.c56
-rw-r--r--tools/testing/radix-tree/iteration_check.c10
-rw-r--r--tools/testing/radix-tree/linux/rcupdate.h2
-rw-r--r--tools/testing/radix-tree/multiorder.c10
-rw-r--r--tools/testing/scatterlist/Makefile1
-rw-r--r--tools/testing/selftests/Makefile2
-rw-r--r--tools/testing/selftests/android/Makefile1
-rw-r--r--tools/testing/selftests/android/ion/Makefile1
-rw-r--r--tools/testing/selftests/android/ion/ion.h11
-rw-r--r--tools/testing/selftests/android/ion/ionapp_export.c11
-rw-r--r--tools/testing/selftests/android/ion/ionapp_import.c11
-rw-r--r--tools/testing/selftests/arm64/.gitignore1
-rw-r--r--tools/testing/selftests/arm64/Makefile12
-rwxr-xr-xtools/testing/selftests/arm64/run_tags_test.sh12
-rw-r--r--tools/testing/selftests/arm64/tags_test.c31
-rw-r--r--tools/testing/selftests/bpf/.gitignore5
-rw-r--r--tools/testing/selftests/bpf/Makefile159
-rw-r--r--tools/testing/selftests/bpf/bpf_endian.h17
-rw-r--r--tools/testing/selftests/bpf/bpf_helpers.h144
-rw-r--r--tools/testing/selftests/bpf/bpf_util.h37
-rw-r--r--tools/testing/selftests/bpf/cgroup_helpers.c57
-rw-r--r--tools/testing/selftests/bpf/config1
-rw-r--r--tools/testing/selftests/bpf/map_tests/.gitignore1
-rw-r--r--tools/testing/selftests/bpf/prog_tests/attach_probe.c160
-rw-r--r--tools/testing/selftests/bpf/prog_tests/bpf_obj_id.c26
-rw-r--r--tools/testing/selftests/bpf/prog_tests/bpf_verif_scale.c104
-rw-r--r--tools/testing/selftests/bpf/prog_tests/core_reloc.c385
-rw-r--r--tools/testing/selftests/bpf/prog_tests/flow_dissector.c278
-rw-r--r--tools/testing/selftests/bpf/prog_tests/get_stack_raw_tp.c85
-rw-r--r--tools/testing/selftests/bpf/prog_tests/global_data.c20
-rw-r--r--tools/testing/selftests/bpf/prog_tests/l4lb_all.c9
-rw-r--r--tools/testing/selftests/bpf/prog_tests/map_lock.c38
-rw-r--r--tools/testing/selftests/bpf/prog_tests/perf_buffer.c94
-rw-r--r--tools/testing/selftests/bpf/prog_tests/pkt_access.c4
-rw-r--r--tools/testing/selftests/bpf/prog_tests/pkt_md_access.c4
-rw-r--r--tools/testing/selftests/bpf/prog_tests/queue_stack_map.c8
-rw-r--r--tools/testing/selftests/bpf/prog_tests/reference_tracking.c19
-rw-r--r--tools/testing/selftests/bpf/prog_tests/send_signal.c225
-rw-r--r--tools/testing/selftests/bpf/prog_tests/sockopt.c985
-rw-r--r--tools/testing/selftests/bpf/prog_tests/sockopt_inherit.c235
-rw-r--r--tools/testing/selftests/bpf/prog_tests/sockopt_multi.c332
-rw-r--r--tools/testing/selftests/bpf/prog_tests/sockopt_sk.c200
-rw-r--r--tools/testing/selftests/bpf/prog_tests/spinlock.c16
-rw-r--r--tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c62
-rw-r--r--tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c38
-rw-r--r--tools/testing/selftests/bpf/prog_tests/stacktrace_map.c60
-rw-r--r--tools/testing/selftests/bpf/prog_tests/stacktrace_map_raw_tp.c24
-rw-r--r--tools/testing/selftests/bpf/prog_tests/task_fd_query_rawtp.c3
-rw-r--r--tools/testing/selftests/bpf/prog_tests/task_fd_query_tp.c5
-rw-r--r--tools/testing/selftests/bpf/prog_tests/tcp_estats.c4
-rw-r--r--tools/testing/selftests/bpf/prog_tests/tcp_rtt.c256
-rw-r--r--tools/testing/selftests/bpf/prog_tests/xdp.c4
-rw-r--r--tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c4
-rw-r--r--tools/testing/selftests/bpf/prog_tests/xdp_noinline.c11
-rw-r--r--tools/testing/selftests/bpf/progs/bpf_flow.c82
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_arrays.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___diff_arr_dim.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___diff_arr_val_sz.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_non_array.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_too_shallow.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_too_small.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_wrong_val_type1.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_wrong_val_type2.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_flavors.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_flavors__err_wrong_name.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_ints.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_ints___bool.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_bitfield.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_16.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_32.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_64.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_8.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_ints___reverse_sign.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_misc.c5
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_mods.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_mods___mod_swap.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_mods___typedefs.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_nesting.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___anon_embed.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___dup_compat_types.c5
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_container.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_field.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_dup_incompat_types.c4
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_container.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_field.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_nonstruct_container.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_partial_match_dups.c4
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_too_deep.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___extra_nesting.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___struct_union_mixup.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_primitives.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_enum_def.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_func_proto.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_ptr_type.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_enum.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_int.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_ptr.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_ptr_as_arr.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf__core_reloc_ptr_as_arr___diff_sz.c3
-rw-r--r--tools/testing/selftests/bpf/progs/btf_dump_test_case_bitfields.c92
-rw-r--r--tools/testing/selftests/bpf/progs/btf_dump_test_case_multidim.c35
-rw-r--r--tools/testing/selftests/bpf/progs/btf_dump_test_case_namespacing.c73
-rw-r--r--tools/testing/selftests/bpf/progs/btf_dump_test_case_ordering.c63
-rw-r--r--tools/testing/selftests/bpf/progs/btf_dump_test_case_packing.c75
-rw-r--r--tools/testing/selftests/bpf/progs/btf_dump_test_case_padding.c111
-rw-r--r--tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c229
-rw-r--r--tools/testing/selftests/bpf/progs/core_reloc_types.h667
-rw-r--r--tools/testing/selftests/bpf/progs/get_cgroup_id_kern.c26
-rw-r--r--tools/testing/selftests/bpf/progs/loop1.c28
-rw-r--r--tools/testing/selftests/bpf/progs/loop2.c28
-rw-r--r--tools/testing/selftests/bpf/progs/loop3.c22
-rw-r--r--tools/testing/selftests/bpf/progs/loop4.c18
-rw-r--r--tools/testing/selftests/bpf/progs/loop5.c32
-rw-r--r--tools/testing/selftests/bpf/progs/netcnt_prog.c28
-rw-r--r--tools/testing/selftests/bpf/progs/pyperf.h263
-rw-r--r--tools/testing/selftests/bpf/progs/pyperf100.c4
-rw-r--r--tools/testing/selftests/bpf/progs/pyperf180.c4
-rw-r--r--tools/testing/selftests/bpf/progs/pyperf50.c4
-rw-r--r--tools/testing/selftests/bpf/progs/pyperf600.c9
-rw-r--r--tools/testing/selftests/bpf/progs/pyperf600_nounroll.c8
-rw-r--r--tools/testing/selftests/bpf/progs/sendmsg6_prog.c3
-rw-r--r--tools/testing/selftests/bpf/progs/socket_cookie_prog.c46
-rw-r--r--tools/testing/selftests/bpf/progs/sockmap_parse_prog.c8
-rw-r--r--tools/testing/selftests/bpf/progs/sockmap_tcp_msg_prog.c9
-rw-r--r--tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c56
-rw-r--r--tools/testing/selftests/bpf/progs/sockopt_inherit.c97
-rw-r--r--tools/testing/selftests/bpf/progs/sockopt_multi.c71
-rw-r--r--tools/testing/selftests/bpf/progs/sockopt_sk.c133
-rw-r--r--tools/testing/selftests/bpf/progs/strobemeta.c10
-rw-r--r--tools/testing/selftests/bpf/progs/strobemeta.h530
-rw-r--r--tools/testing/selftests/bpf/progs/strobemeta_nounroll1.c9
-rw-r--r--tools/testing/selftests/bpf/progs/strobemeta_nounroll2.c9
-rw-r--r--tools/testing/selftests/bpf/progs/tcp_rtt.c61
-rw-r--r--tools/testing/selftests/bpf/progs/test_attach_probe.c52
-rw-r--r--tools/testing/selftests/bpf/progs/test_btf_newkv.c70
-rw-r--r--tools/testing/selftests/bpf/progs/test_core_reloc_arrays.c55
-rw-r--r--tools/testing/selftests/bpf/progs/test_core_reloc_flavors.c62
-rw-r--r--tools/testing/selftests/bpf/progs/test_core_reloc_ints.c44
-rw-r--r--tools/testing/selftests/bpf/progs/test_core_reloc_kernel.c36
-rw-r--r--tools/testing/selftests/bpf/progs/test_core_reloc_misc.c57
-rw-r--r--tools/testing/selftests/bpf/progs/test_core_reloc_mods.c62
-rw-r--r--tools/testing/selftests/bpf/progs/test_core_reloc_nesting.c46
-rw-r--r--tools/testing/selftests/bpf/progs/test_core_reloc_primitives.c43
-rw-r--r--tools/testing/selftests/bpf/progs/test_core_reloc_ptr_as_arr.c30
-rw-r--r--tools/testing/selftests/bpf/progs/test_get_stack_rawtp.c39
-rw-r--r--tools/testing/selftests/bpf/progs/test_global_data.c38
-rw-r--r--tools/testing/selftests/bpf/progs/test_jhash.h3
-rw-r--r--tools/testing/selftests/bpf/progs/test_l4lb.c68
-rw-r--r--tools/testing/selftests/bpf/progs/test_l4lb_noinline.c68
-rw-r--r--tools/testing/selftests/bpf/progs/test_lwt_seg6local.c35
-rw-r--r--tools/testing/selftests/bpf/progs/test_map_in_map.c30
-rw-r--r--tools/testing/selftests/bpf/progs/test_map_lock.c28
-rw-r--r--tools/testing/selftests/bpf/progs/test_obj_id.c17
-rw-r--r--tools/testing/selftests/bpf/progs/test_perf_buffer.c25
-rw-r--r--tools/testing/selftests/bpf/progs/test_pkt_access.c5
-rw-r--r--tools/testing/selftests/bpf/progs/test_pkt_md_access.c5
-rw-r--r--tools/testing/selftests/bpf/progs/test_seg6_loop.c258
-rw-r--r--tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c68
-rw-r--r--tools/testing/selftests/bpf/progs/test_send_signal_kern.c47
-rw-r--r--tools/testing/selftests/bpf/progs/test_sock_fields_kern.c86
-rw-r--r--tools/testing/selftests/bpf/progs/test_spin_lock.c41
-rw-r--r--tools/testing/selftests/bpf/progs/test_stacktrace_build_id.c54
-rw-r--r--tools/testing/selftests/bpf/progs/test_stacktrace_map.c50
-rw-r--r--tools/testing/selftests/bpf/progs/test_sysctl_loop1.c71
-rw-r--r--tools/testing/selftests/bpf/progs/test_sysctl_loop2.c72
-rw-r--r--tools/testing/selftests/bpf/progs/test_sysctl_prog.c5
-rw-r--r--tools/testing/selftests/bpf/progs/test_tc_edt.c1
-rw-r--r--tools/testing/selftests/bpf/progs/test_tcp_check_syncookie_kern.c48
-rw-r--r--tools/testing/selftests/bpf/progs/test_tcp_estats.c12
-rw-r--r--tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c24
-rw-r--r--tools/testing/selftests/bpf/progs/test_tcpnotify_kern.c24
-rw-r--r--tools/testing/selftests/bpf/progs/test_verif_scale2.c2
-rw-r--r--tools/testing/selftests/bpf/progs/test_xdp.c26
-rw-r--r--tools/testing/selftests/bpf/progs/test_xdp_loop.c231
-rw-r--r--tools/testing/selftests/bpf/progs/test_xdp_noinline.c113
-rw-r--r--tools/testing/selftests/bpf/progs/xdp_redirect_map.c31
-rw-r--r--tools/testing/selftests/bpf/progs/xdp_tx.c12
-rw-r--r--tools/testing/selftests/bpf/progs/xdping_kern.c184
-rw-r--r--tools/testing/selftests/bpf/test_align.c16
-rwxr-xr-xtools/testing/selftests/bpf/test_bpftool_build.sh143
-rw-r--r--tools/testing/selftests/bpf/test_btf.c169
-rw-r--r--tools/testing/selftests/bpf/test_btf_dump.c150
-rw-r--r--tools/testing/selftests/bpf/test_cgroup_attach.c571
-rw-r--r--tools/testing/selftests/bpf/test_cgroup_storage.c6
-rw-r--r--tools/testing/selftests/bpf/test_dev_cgroup.c5
-rw-r--r--tools/testing/selftests/bpf/test_hashmap.c382
-rw-r--r--tools/testing/selftests/bpf/test_iptunnel_common.h5
-rw-r--r--tools/testing/selftests/bpf/test_lpm_map.c41
-rw-r--r--tools/testing/selftests/bpf/test_lru_map.c293
-rw-r--r--tools/testing/selftests/bpf/test_maps.c42
-rwxr-xr-xtools/testing/selftests/bpf/test_offload.py2
-rw-r--r--tools/testing/selftests/bpf/test_progs.c437
-rw-r--r--tools/testing/selftests/bpf/test_progs.h63
-rw-r--r--tools/testing/selftests/bpf/test_queue_stack_map.h30
-rw-r--r--tools/testing/selftests/bpf/test_section_names.c20
-rw-r--r--tools/testing/selftests/bpf/test_select_reuseport.c54
-rw-r--r--tools/testing/selftests/bpf/test_sock.c7
-rw-r--r--tools/testing/selftests/bpf/test_sock_addr.c214
-rw-r--r--tools/testing/selftests/bpf/test_sock_fields.c1
-rw-r--r--tools/testing/selftests/bpf/test_socket_cookie.c25
-rw-r--r--tools/testing/selftests/bpf/test_sockmap_kern.h117
-rw-r--r--tools/testing/selftests/bpf/test_stub.c40
-rw-r--r--tools/testing/selftests/bpf/test_sysctl.c139
-rwxr-xr-xtools/testing/selftests/bpf/test_tcp_check_syncookie.sh3
-rw-r--r--tools/testing/selftests/bpf/test_tcp_check_syncookie_user.c61
-rw-r--r--tools/testing/selftests/bpf/test_tcpnotify_user.c90
-rwxr-xr-xtools/testing/selftests/bpf/test_tunnel.sh32
-rw-r--r--tools/testing/selftests/bpf/test_verifier.c196
-rwxr-xr-xtools/testing/selftests/bpf/test_xdp_veth.sh118
-rwxr-xr-xtools/testing/selftests/bpf/test_xdp_vlan.sh57
-rwxr-xr-xtools/testing/selftests/bpf/test_xdp_vlan_mode_generic.sh9
-rwxr-xr-xtools/testing/selftests/bpf/test_xdp_vlan_mode_native.sh9
-rwxr-xr-xtools/testing/selftests/bpf/test_xdping.sh99
-rw-r--r--tools/testing/selftests/bpf/trace_helpers.c129
-rw-r--r--tools/testing/selftests/bpf/trace_helpers.h9
-rw-r--r--tools/testing/selftests/bpf/verifier/array_access.c2
-rw-r--r--tools/testing/selftests/bpf/verifier/basic_instr.c85
-rw-r--r--tools/testing/selftests/bpf/verifier/calls.c22
-rw-r--r--tools/testing/selftests/bpf/verifier/cfg.c11
-rw-r--r--tools/testing/selftests/bpf/verifier/ctx_skb.c11
-rw-r--r--tools/testing/selftests/bpf/verifier/direct_packet_access.c3
-rw-r--r--tools/testing/selftests/bpf/verifier/div_overflow.c14
-rw-r--r--tools/testing/selftests/bpf/verifier/event_output.c94
-rw-r--r--tools/testing/selftests/bpf/verifier/helper_access_var_len.c28
-rw-r--r--tools/testing/selftests/bpf/verifier/loops1.c189
-rw-r--r--tools/testing/selftests/bpf/verifier/precise.c194
-rw-r--r--tools/testing/selftests/bpf/verifier/prevent_map_lookup.c15
-rw-r--r--tools/testing/selftests/bpf/verifier/sock.c18
-rw-r--r--tools/testing/selftests/bpf/verifier/subreg.c533
-rw-r--r--tools/testing/selftests/bpf/verifier/value_ptr_arith.c2
-rw-r--r--tools/testing/selftests/bpf/verifier/wide_access.c73
-rw-r--r--tools/testing/selftests/bpf/xdping.c258
-rw-r--r--tools/testing/selftests/bpf/xdping.h13
-rw-r--r--tools/testing/selftests/breakpoints/breakpoint_test.c3
-rw-r--r--tools/testing/selftests/breakpoints/breakpoint_test_arm64.c11
-rw-r--r--tools/testing/selftests/breakpoints/step_after_suspend_test.c11
-rw-r--r--tools/testing/selftests/cgroup/cgroup_util.c3
-rw-r--r--tools/testing/selftests/cgroup/test_core.c7
-rw-r--r--tools/testing/selftests/cgroup/test_freezer.c55
-rw-r--r--tools/testing/selftests/cgroup/test_memcontrol.c4
-rw-r--r--tools/testing/selftests/drivers/dma-buf/Makefile1
-rw-r--r--tools/testing/selftests/drivers/dma-buf/config1
-rwxr-xr-xtools/testing/selftests/drivers/net/mlxsw/devlink_trap.sh129
-rwxr-xr-xtools/testing/selftests/drivers/net/mlxsw/devlink_trap_l2_drops.sh484
-rwxr-xr-xtools/testing/selftests/drivers/net/mlxsw/fib_offload.sh349
-rwxr-xr-xtools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh6
-rwxr-xr-xtools/testing/selftests/drivers/net/mlxsw/qos_dscp_router.sh24
-rwxr-xr-xtools/testing/selftests/drivers/net/mlxsw/qos_mc_aware.sh4
-rwxr-xr-xtools/testing/selftests/drivers/net/mlxsw/rtnetlink.sh47
-rwxr-xr-xtools/testing/selftests/drivers/net/netdevsim/devlink.sh165
-rwxr-xr-xtools/testing/selftests/drivers/net/netdevsim/devlink_trap.sh364
-rw-r--r--tools/testing/selftests/efivarfs/Makefile1
-rw-r--r--tools/testing/selftests/exec/execveat.c3
-rw-r--r--tools/testing/selftests/firmware/Makefile1
-rwxr-xr-xtools/testing/selftests/firmware/fw_filesystem.sh126
-rwxr-xr-xtools/testing/selftests/firmware/fw_lib.sh18
-rwxr-xr-xtools/testing/selftests/firmware/fw_run_tests.sh1
-rwxr-xr-xtools/testing/selftests/ftrace/ftracetest41
-rw-r--r--tools/testing/selftests/ftrace/test.d/functions6
-rw-r--r--tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_user.tc32
-rw-r--r--tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc16
-rw-r--r--tools/testing/selftests/ftrace/test.d/kprobe/kprobe_ftrace.tc2
-rw-r--r--tools/testing/selftests/ftrace/test.d/kprobe/kprobe_multiprobe.tc35
-rw-r--r--tools/testing/selftests/ftrace/test.d/kprobe/kprobe_syntax_errors.tc16
-rw-r--r--tools/testing/selftests/ftrace/test.d/selftest/bashisms.tc21
-rw-r--r--tools/testing/selftests/futex/functional/futex_requeue_pi.c6
-rw-r--r--tools/testing/selftests/futex/functional/futex_requeue_pi_mismatched_ops.c6
-rw-r--r--tools/testing/selftests/futex/functional/futex_requeue_pi_signal_restart.c6
-rw-r--r--tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c6
-rw-r--r--tools/testing/selftests/futex/functional/futex_wait_timeout.c6
-rw-r--r--tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c6
-rw-r--r--tools/testing/selftests/futex/functional/futex_wait_wouldblock.c6
-rwxr-xr-xtools/testing/selftests/futex/functional/run.sh6
-rw-r--r--tools/testing/selftests/futex/include/atomic.h6
-rw-r--r--tools/testing/selftests/futex/include/futextest.h6
-rw-r--r--tools/testing/selftests/futex/include/logging.h6
-rwxr-xr-xtools/testing/selftests/futex/run.sh6
-rw-r--r--tools/testing/selftests/gpio/gpio-mockup-chardev.c5
-rw-r--r--tools/testing/selftests/ia64/aliasing-test.c5
-rw-r--r--tools/testing/selftests/ir/ir_loopback.c6
-rw-r--r--tools/testing/selftests/kcmp/Makefile1
-rw-r--r--tools/testing/selftests/kexec/Makefile1
-rw-r--r--tools/testing/selftests/kmod/Makefile1
-rwxr-xr-xtools/testing/selftests/kmod/kmod.sh6
-rw-r--r--tools/testing/selftests/kselftest.h15
-rw-r--r--tools/testing/selftests/kselftest/runner.sh12
-rw-r--r--tools/testing/selftests/kselftest_harness.h19
-rw-r--r--tools/testing/selftests/kvm/.gitignore4
-rw-r--r--tools/testing/selftests/kvm/Makefile46
-rw-r--r--tools/testing/selftests/kvm/config3
-rw-r--r--tools/testing/selftests/kvm/dirty_log_test.c72
-rw-r--r--tools/testing/selftests/kvm/include/aarch64/processor.h4
-rw-r--r--tools/testing/selftests/kvm/include/evmcs.h2
-rw-r--r--tools/testing/selftests/kvm/include/kvm_util.h27
-rw-r--r--tools/testing/selftests/kvm/include/s390x/processor.h22
-rw-r--r--tools/testing/selftests/kvm/include/sparsebit.h4
-rw-r--r--tools/testing/selftests/kvm/include/test_util.h4
-rw-r--r--tools/testing/selftests/kvm/include/x86_64/processor.h6
-rw-r--r--tools/testing/selftests/kvm/include/x86_64/vmx.h4
-rw-r--r--tools/testing/selftests/kvm/kvm_create_max_vcpus.c (renamed from tools/testing/selftests/kvm/x86_64/kvm_create_max_vcpus.c)7
-rw-r--r--tools/testing/selftests/kvm/lib/aarch64/processor.c61
-rw-r--r--tools/testing/selftests/kvm/lib/aarch64/ucall.c112
-rw-r--r--tools/testing/selftests/kvm/lib/assert.c3
-rw-r--r--tools/testing/selftests/kvm/lib/elf.c3
-rw-r--r--tools/testing/selftests/kvm/lib/io.c3
-rw-r--r--tools/testing/selftests/kvm/lib/kvm_util.c76
-rw-r--r--tools/testing/selftests/kvm/lib/kvm_util_internal.h5
-rw-r--r--tools/testing/selftests/kvm/lib/s390x/processor.c278
-rw-r--r--tools/testing/selftests/kvm/lib/s390x/ucall.c56
-rw-r--r--tools/testing/selftests/kvm/lib/sparsebit.c3
-rw-r--r--tools/testing/selftests/kvm/lib/ucall.c150
-rw-r--r--tools/testing/selftests/kvm/lib/x86_64/processor.c46
-rw-r--r--tools/testing/selftests/kvm/lib/x86_64/ucall.c56
-rw-r--r--tools/testing/selftests/kvm/lib/x86_64/vmx.c25
-rw-r--r--tools/testing/selftests/kvm/s390x/memop.c166
-rw-r--r--tools/testing/selftests/kvm/s390x/sync_regs_test.c183
-rw-r--r--tools/testing/selftests/kvm/x86_64/cr4_cpuid_sync_test.c1
-rw-r--r--tools/testing/selftests/kvm/x86_64/evmcs_test.c23
-rw-r--r--tools/testing/selftests/kvm/x86_64/hyperv_cpuid.c21
-rw-r--r--tools/testing/selftests/kvm/x86_64/mmio_warning_test.c126
-rw-r--r--tools/testing/selftests/kvm/x86_64/platform_info_test.c3
-rw-r--r--tools/testing/selftests/kvm/x86_64/set_sregs_test.c4
-rw-r--r--tools/testing/selftests/kvm/x86_64/smm_test.c5
-rw-r--r--tools/testing/selftests/kvm/x86_64/state_test.c12
-rw-r--r--tools/testing/selftests/kvm/x86_64/sync_regs_test.c57
-rw-r--r--tools/testing/selftests/kvm/x86_64/vmx_close_while_nested_test.c8
-rw-r--r--tools/testing/selftests/kvm/x86_64/vmx_set_nested_state_test.c101
-rw-r--r--tools/testing/selftests/kvm/x86_64/vmx_tsc_adjust_test.c9
-rw-r--r--tools/testing/selftests/lib.mk4
-rw-r--r--tools/testing/selftests/lib/Makefile1
-rw-r--r--tools/testing/selftests/livepatch/functions.sh46
-rw-r--r--tools/testing/selftests/membarrier/Makefile1
-rw-r--r--tools/testing/selftests/net/.gitignore4
-rw-r--r--tools/testing/selftests/net/Makefile9
-rw-r--r--tools/testing/selftests/net/config4
-rwxr-xr-xtools/testing/selftests/net/fcnal-test.sh3432
-rwxr-xr-xtools/testing/selftests/net/fib-onlink-tests.sh48
-rwxr-xr-xtools/testing/selftests/net/fib_nexthop_multiprefix.sh290
-rwxr-xr-xtools/testing/selftests/net/fib_nexthops.sh1028
-rwxr-xr-xtools/testing/selftests/net/fib_rule_tests.sh10
-rwxr-xr-xtools/testing/selftests/net/fib_tests.sh35
-rw-r--r--tools/testing/selftests/net/forwarding/devlink_lib.sh189
-rwxr-xr-xtools/testing/selftests/net/forwarding/gre_inner_v4_multipath.sh305
-rwxr-xr-xtools/testing/selftests/net/forwarding/gre_inner_v6_multipath.sh306
-rwxr-xr-xtools/testing/selftests/net/forwarding/gre_multipath.sh28
-rwxr-xr-xtools/testing/selftests/net/forwarding/ip6gre_inner_v4_multipath.sh304
-rwxr-xr-xtools/testing/selftests/net/forwarding/ip6gre_inner_v6_multipath.sh305
-rw-r--r--tools/testing/selftests/net/forwarding/lib.sh19
-rwxr-xr-xtools/testing/selftests/net/forwarding/router_broadcast.sh5
-rwxr-xr-xtools/testing/selftests/net/forwarding/router_mpath_nh.sh359
-rw-r--r--tools/testing/selftests/net/forwarding/tc_common.sh17
-rwxr-xr-xtools/testing/selftests/net/forwarding/tc_flower.sh60
-rwxr-xr-xtools/testing/selftests/net/forwarding/tc_flower_router.sh172
-rwxr-xr-xtools/testing/selftests/net/forwarding/tc_shblocks.sh29
-rwxr-xr-xtools/testing/selftests/net/icmp_redirect.sh534
-rw-r--r--tools/testing/selftests/net/ipv6_flowlabel.c229
-rwxr-xr-xtools/testing/selftests/net/ipv6_flowlabel.sh21
-rw-r--r--tools/testing/selftests/net/ipv6_flowlabel_mgr.c199
-rw-r--r--tools/testing/selftests/net/l2tp.sh382
-rw-r--r--tools/testing/selftests/net/nettest.c1757
-rwxr-xr-xtools/testing/selftests/net/pmtu.sh401
-rw-r--r--tools/testing/selftests/net/psock_fanout.c16
-rw-r--r--tools/testing/selftests/net/psock_lib.h16
-rw-r--r--tools/testing/selftests/net/psock_tpacket.c16
-rwxr-xr-xtools/testing/selftests/net/route_localnet.sh74
-rwxr-xr-xtools/testing/selftests/net/rtnetlink.sh57
-rwxr-xr-xtools/testing/selftests/net/run_afpackettests14
-rw-r--r--tools/testing/selftests/net/so_txtime.c296
-rwxr-xr-xtools/testing/selftests/net/so_txtime.sh31
-rw-r--r--tools/testing/selftests/net/tcp_fastopen_backup_key.c335
-rwxr-xr-xtools/testing/selftests/net/tcp_fastopen_backup_key.sh55
-rw-r--r--tools/testing/selftests/net/tcp_inq.c12
-rw-r--r--tools/testing/selftests/net/tcp_mmap.c16
-rwxr-xr-xtools/testing/selftests/net/test_blackhole_dev.sh11
-rw-r--r--tools/testing/selftests/net/tls.c283
-rw-r--r--tools/testing/selftests/net/txring_overwrite.c2
-rwxr-xr-xtools/testing/selftests/net/udpgso_bench.sh63
-rw-r--r--tools/testing/selftests/net/udpgso_bench_tx.c309
-rwxr-xr-xtools/testing/selftests/net/xfrm_policy.sh34
-rw-r--r--tools/testing/selftests/netfilter/Makefile2
-rwxr-xr-xtools/testing/selftests/netfilter/nft_flowtable.sh372
-rwxr-xr-xtools/testing/selftests/netfilter/nft_nat.sh83
-rw-r--r--tools/testing/selftests/networking/timestamping/timestamping.c23
-rw-r--r--tools/testing/selftests/networking/timestamping/txtimestamp.c15
-rw-r--r--tools/testing/selftests/nsfs/Makefile1
-rwxr-xr-xtools/testing/selftests/ntb/ntb_test.sh65
-rw-r--r--tools/testing/selftests/pidfd/.gitignore3
-rw-r--r--tools/testing/selftests/pidfd/Makefile5
-rw-r--r--tools/testing/selftests/pidfd/pidfd.h87
-rw-r--r--tools/testing/selftests/pidfd/pidfd_open_test.c164
-rw-r--r--tools/testing/selftests/pidfd/pidfd_poll_test.c117
-rw-r--r--tools/testing/selftests/pidfd/pidfd_test.c250
-rw-r--r--tools/testing/selftests/pidfd/pidfd_wait.c271
-rw-r--r--tools/testing/selftests/powerpc/Makefile1
-rw-r--r--tools/testing/selftests/powerpc/alignment/Makefile1
-rw-r--r--tools/testing/selftests/powerpc/alignment/alignment_handler.c6
-rw-r--r--tools/testing/selftests/powerpc/alignment/copy_first_unaligned.c7
-rw-r--r--tools/testing/selftests/powerpc/benchmarks/context_switch.c6
-rw-r--r--tools/testing/selftests/powerpc/benchmarks/futex_bench.c2
-rw-r--r--tools/testing/selftests/powerpc/benchmarks/gettimeofday.c2
-rw-r--r--tools/testing/selftests/powerpc/benchmarks/mmap_bench.c2
-rw-r--r--tools/testing/selftests/powerpc/benchmarks/null_syscall.c6
-rw-r--r--tools/testing/selftests/powerpc/cache_shape/cache_shape.c6
-rw-r--r--tools/testing/selftests/powerpc/copyloops/.gitignore9
-rw-r--r--tools/testing/selftests/powerpc/copyloops/Makefile7
-rw-r--r--tools/testing/selftests/powerpc/copyloops/asm/export.h1
l---------tools/testing/selftests/powerpc/copyloops/memcpy_mcsafe_64.S1
-rw-r--r--tools/testing/selftests/powerpc/dscr/dscr.h5
-rw-r--r--tools/testing/selftests/powerpc/dscr/dscr_default_test.c5
-rw-r--r--tools/testing/selftests/powerpc/dscr/dscr_explicit_test.c5
-rw-r--r--tools/testing/selftests/powerpc/dscr/dscr_inherit_exec_test.c5
-rw-r--r--tools/testing/selftests/powerpc/dscr/dscr_inherit_test.c5
-rw-r--r--tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c5
-rw-r--r--tools/testing/selftests/powerpc/dscr/dscr_sysfs_thread_test.c5
-rw-r--r--tools/testing/selftests/powerpc/dscr/dscr_user_test.c5
-rw-r--r--tools/testing/selftests/powerpc/eeh/Makefile9
-rwxr-xr-xtools/testing/selftests/powerpc/eeh/eeh-basic.sh82
-rwxr-xr-xtools/testing/selftests/powerpc/eeh/eeh-functions.sh76
-rw-r--r--tools/testing/selftests/powerpc/harness.c2
-rw-r--r--tools/testing/selftests/powerpc/include/fpu_asm.h6
-rw-r--r--tools/testing/selftests/powerpc/include/gpr_asm.h6
-rw-r--r--tools/testing/selftests/powerpc/include/reg.h2
-rw-r--r--tools/testing/selftests/powerpc/include/subunit.h2
-rw-r--r--tools/testing/selftests/powerpc/include/utils.h2
-rw-r--r--tools/testing/selftests/powerpc/include/vmx_asm.h6
-rw-r--r--tools/testing/selftests/powerpc/include/vsx_asm.h6
-rw-r--r--tools/testing/selftests/powerpc/lib/reg.S6
-rw-r--r--tools/testing/selftests/powerpc/math/fpu_asm.S6
-rw-r--r--tools/testing/selftests/powerpc/math/fpu_preempt.c6
-rw-r--r--tools/testing/selftests/powerpc/math/fpu_signal.c6
-rw-r--r--tools/testing/selftests/powerpc/math/fpu_syscall.c6
-rw-r--r--tools/testing/selftests/powerpc/math/vmx_asm.S6
-rw-r--r--tools/testing/selftests/powerpc/math/vmx_preempt.c6
-rw-r--r--tools/testing/selftests/powerpc/math/vmx_signal.c6
-rw-r--r--tools/testing/selftests/powerpc/math/vmx_syscall.c6
-rw-r--r--tools/testing/selftests/powerpc/math/vsx_asm.S6
-rw-r--r--tools/testing/selftests/powerpc/math/vsx_preempt.c6
-rw-r--r--tools/testing/selftests/powerpc/mm/.gitignore3
-rw-r--r--tools/testing/selftests/powerpc/mm/Makefile4
-rw-r--r--tools/testing/selftests/powerpc/mm/large_vm_fork_separation.c87
-rw-r--r--tools/testing/selftests/powerpc/mm/prot_sao.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/count_instructions.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/busy_loop.S2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/cycles_with_mmcr2_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/ebb.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/ebb.h2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/fixed_instruction_loop.S2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/no_handler_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/reg_access_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/task_event_pinned_vs_ebb_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/task_event_vs_ebb_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/trace.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/trace.h2
-rw-r--r--tools/testing/selftests/powerpc/pmu/event.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/event.h2
-rw-r--r--tools/testing/selftests/powerpc/pmu/l3_bank_test.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/lib.c2
-rw-r--r--tools/testing/selftests/powerpc/pmu/lib.h2
-rw-r--r--tools/testing/selftests/powerpc/pmu/loop.S2
-rw-r--r--tools/testing/selftests/powerpc/pmu/per_event_excludes.c2
-rw-r--r--tools/testing/selftests/powerpc/primitives/Makefile1
-rw-r--r--tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c6
-rw-r--r--tools/testing/selftests/powerpc/ptrace/.gitignore3
-rw-r--r--tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c6
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c6
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-gpr.h6
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-tar.c6
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-tar.h6
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c6
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-gpr.c6
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c6
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c6
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c6
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c6
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c6
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c6
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-vsx.h6
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace.h6
-rwxr-xr-xtools/testing/selftests/powerpc/scripts/hmi.sh9
-rw-r--r--tools/testing/selftests/powerpc/security/.gitignore1
-rw-r--r--tools/testing/selftests/powerpc/signal/signal.S6
-rw-r--r--tools/testing/selftests/powerpc/signal/signal.c6
-rw-r--r--tools/testing/selftests/powerpc/signal/signal_tm.c6
-rw-r--r--tools/testing/selftests/powerpc/stringloops/.gitignore5
-rw-r--r--tools/testing/selftests/powerpc/stringloops/asm/ppc-opcode.h6
-rw-r--r--tools/testing/selftests/powerpc/stringloops/asm/ppc_asm.h2
-rw-r--r--tools/testing/selftests/powerpc/syscalls/Makefile1
-rw-r--r--tools/testing/selftests/powerpc/syscalls/ipc_unmuxed.c6
-rw-r--r--tools/testing/selftests/powerpc/tm/tm-exec.c6
-rw-r--r--tools/testing/selftests/powerpc/tm/tm-fork.c2
-rw-r--r--tools/testing/selftests/powerpc/tm/tm-signal-context-chk-fpu.c56
-rw-r--r--tools/testing/selftests/powerpc/tm/tm-signal-context-chk-gpr.c66
-rw-r--r--tools/testing/selftests/powerpc/tm/tm-signal-context-chk-vmx.c81
-rw-r--r--tools/testing/selftests/powerpc/tm/tm-signal-context-chk-vsx.c137
-rw-r--r--tools/testing/selftests/powerpc/tm/tm-signal-msr-resv.c2
-rw-r--r--tools/testing/selftests/powerpc/tm/tm-signal-stack.c2
-rw-r--r--tools/testing/selftests/powerpc/tm/tm-signal.S6
-rw-r--r--tools/testing/selftests/powerpc/tm/tm-syscall.c2
-rw-r--r--tools/testing/selftests/powerpc/tm/tm-tar.c2
-rw-r--r--tools/testing/selftests/powerpc/tm/tm-tmspr.c3
-rw-r--r--tools/testing/selftests/powerpc/tm/tm-trap.c2
-rw-r--r--tools/testing/selftests/powerpc/tm/tm-unavailable.c2
-rw-r--r--tools/testing/selftests/powerpc/tm/tm-vmx-unavail.c2
-rw-r--r--tools/testing/selftests/powerpc/tm/tm-vmxcopy.c4
-rw-r--r--tools/testing/selftests/powerpc/tm/tm.h5
-rw-r--r--tools/testing/selftests/powerpc/utils.c2
-rw-r--r--tools/testing/selftests/powerpc/vphn/Makefile3
l---------tools/testing/selftests/powerpc/vphn/asm/lppaca.h1
l---------tools/testing/selftests/powerpc/vphn/vphn.c2
l---------tools/testing/selftests/powerpc/vphn/vphn.h1
-rw-r--r--tools/testing/selftests/proc/.gitignore1
-rw-r--r--tools/testing/selftests/proc/Makefile2
-rw-r--r--tools/testing/selftests/proc/proc-pid-vm.c17
-rw-r--r--tools/testing/selftests/proc/setns-sysvipc.c133
-rwxr-xr-xtools/testing/selftests/pstore/common_tests2
-rwxr-xr-xtools/testing/selftests/pstore/pstore_crash_test2
-rwxr-xr-xtools/testing/selftests/pstore/pstore_post_reboot_tests2
-rwxr-xr-xtools/testing/selftests/pstore/pstore_tests2
-rwxr-xr-xtools/testing/selftests/ptp/phc.sh166
-rw-r--r--tools/testing/selftests/ptp/testptp.c100
-rw-r--r--tools/testing/selftests/ptrace/.gitignore1
-rw-r--r--tools/testing/selftests/ptrace/Makefile3
-rw-r--r--tools/testing/selftests/ptrace/get_syscall_info.c271
-rw-r--r--tools/testing/selftests/rcutorture/Makefile3
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/configinit.sh39
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/cpus2use.sh5
-rw-r--r--tools/testing/selftests/rcutorture/bin/functions.sh13
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/jitter.sh13
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm-build.sh9
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm-find-errors.sh3
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm-recheck.sh13
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh25
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm.sh14
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/parse-build.sh2
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/parse-console.sh1
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/CFcommon3
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot1
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE03.boot1
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TRIVIAL14
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TRIVIAL.boot3
-rw-r--r--tools/testing/selftests/rseq/rseq-arm.h61
-rw-r--r--tools/testing/selftests/rtc/rtctest.c6
-rw-r--r--tools/testing/selftests/rtc/setdate.c11
-rw-r--r--tools/testing/selftests/safesetid/safesetid-test.c18
-rw-r--r--tools/testing/selftests/seccomp/seccomp_bpf.c15
-rw-r--r--tools/testing/selftests/sigaltstack/Makefile1
-rw-r--r--tools/testing/selftests/size/Makefile1
-rw-r--r--tools/testing/selftests/size/get_size.c3
-rw-r--r--tools/testing/selftests/static_keys/Makefile1
-rw-r--r--tools/testing/selftests/sysctl/Makefile1
-rw-r--r--tools/testing/selftests/tc-testing/README26
-rw-r--r--tools/testing/selftests/tc-testing/TdcPlugin.py5
-rw-r--r--tools/testing/selftests/tc-testing/config3
-rw-r--r--tools/testing/selftests/tc-testing/creating-testcases/scapy-example.json98
-rw-r--r--tools/testing/selftests/tc-testing/plugin-lib/buildebpfPlugin.py5
-rw-r--r--tools/testing/selftests/tc-testing/plugin-lib/nsPlugin.py40
-rw-r--r--tools/testing/selftests/tc-testing/plugin-lib/scapyPlugin.py50
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/actions/bpf.json6
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/actions/ct.json314
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json94
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/actions/mpls.json1088
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json220
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/actions/vlan.json94
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/filters/concurrency.json18
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/filters/fw.json306
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/filters/matchall.json391
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/filters/tests.json31
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/qdiscs/fifo.json304
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/qdiscs/ingress.json102
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/qdiscs/prio.json276
-rwxr-xr-xtools/testing/selftests/tc-testing/tdc.py100
-rw-r--r--tools/testing/selftests/tc-testing/tdc_config.py3
-rw-r--r--tools/testing/selftests/tc-testing/tdc_helper.py5
-rw-r--r--tools/testing/selftests/timers/adjtick.c1
-rw-r--r--tools/testing/selftests/timers/freq-step.c16
-rw-r--r--tools/testing/selftests/timers/leapcrash.c1
-rw-r--r--tools/testing/selftests/timers/mqueue-lat.c1
-rw-r--r--tools/testing/selftests/timers/nanosleep.c1
-rw-r--r--tools/testing/selftests/timers/nsleep-lat.c1
-rw-r--r--tools/testing/selftests/timers/posix_timers.c3
-rw-r--r--tools/testing/selftests/timers/raw_skew.c1
-rw-r--r--tools/testing/selftests/timers/set-tai.c1
-rw-r--r--tools/testing/selftests/timers/set-tz.c2
-rw-r--r--tools/testing/selftests/timers/threadtest.c1
-rw-r--r--tools/testing/selftests/timers/valid-adjtimex.c2
-rw-r--r--tools/testing/selftests/tmpfs/Makefile1
-rw-r--r--tools/testing/selftests/user/Makefile1
-rw-r--r--tools/testing/selftests/vDSO/vdso_standalone_test_x86.c2
-rw-r--r--tools/testing/selftests/vDSO/vdso_test.c2
-rw-r--r--tools/testing/selftests/vm/Makefile6
-rw-r--r--tools/testing/selftests/vm/userfaultfd.c6
-rw-r--r--tools/testing/selftests/vm/va_128TBswitch.c10
-rw-r--r--tools/testing/selftests/vm/virtual_address_range.c2
-rw-r--r--tools/testing/selftests/x86/Makefile9
-rwxr-xr-xtools/testing/selftests/x86/check_cc.sh2
-rw-r--r--tools/testing/selftests/x86/check_initial_reg_state.c10
-rw-r--r--tools/testing/selftests/x86/entry_from_vm86.c3
-rw-r--r--tools/testing/selftests/x86/fsgsbase.c211
-rw-r--r--tools/testing/selftests/x86/mpx-debug.h15
-rw-r--r--tools/testing/selftests/x86/mpx-dig.c497
-rw-r--r--tools/testing/selftests/x86/mpx-hw.h124
-rw-r--r--tools/testing/selftests/x86/mpx-mini-test.c1616
-rw-r--r--tools/testing/selftests/x86/mpx-mm.h10
-rw-r--r--tools/testing/selftests/x86/protection_keys.c2
-rw-r--r--tools/testing/selftests/x86/sigreturn.c10
-rw-r--r--tools/testing/selftests/x86/single_step_syscall.c10
-rw-r--r--tools/testing/selftests/x86/syscall_arg_fault.c132
-rw-r--r--tools/testing/selftests/x86/syscall_nt.c10
-rw-r--r--tools/testing/selftests/x86/syscall_numbering.c89
-rw-r--r--tools/testing/selftests/x86/sysret_rip.c10
-rw-r--r--tools/testing/selftests/x86/sysret_ss_attrs.c10
-rw-r--r--tools/testing/selftests/x86/test_mremap_vdso.c10
-rw-r--r--tools/testing/selftests/x86/test_syscall_vdso.c10
-rw-r--r--tools/testing/selftests/x86/test_vsyscall.c120
-rw-r--r--tools/testing/selftests/x86/thunks.S10
-rw-r--r--tools/testing/selftests/x86/thunks_32.S10
-rw-r--r--tools/testing/selftests/x86/trivial_32bit_program.c2
-rw-r--r--tools/testing/selftests/x86/trivial_64bit_program.c2
-rw-r--r--tools/testing/selftests/x86/unwind_vdso.c10
-rw-r--r--tools/testing/selftests/x86/vdso_restorer.c10
-rw-r--r--tools/testing/selftests/zram/README2
-rwxr-xr-xtools/testing/selftests/zram/zram01.sh11
-rwxr-xr-xtools/testing/selftests/zram/zram02.sh11
-rwxr-xr-xtools/testing/selftests/zram/zram_lib.sh11
-rw-r--r--tools/testing/vsock/Makefile1
-rw-r--r--tools/testing/vsock/control.c6
-rw-r--r--tools/testing/vsock/timeout.c6
-rw-r--r--tools/testing/vsock/vsock_diag_test.c6
-rw-r--r--tools/thermal/tmon/pid.c13
-rw-r--r--tools/thermal/tmon/sysfs.c11
-rw-r--r--tools/thermal/tmon/tmon.c11
-rw-r--r--tools/thermal/tmon/tmon.h11
-rw-r--r--tools/thermal/tmon/tui.c11
-rwxr-xr-xtools/time/udelay_test.sh9
-rw-r--r--tools/usb/ffs-aio-example/simple/host_app/Makefile1
-rw-r--r--tools/usb/ffs-test.c15
-rw-r--r--tools/usb/testusb.c15
-rw-r--r--tools/usb/usbip/libsrc/names.c20
-rw-r--r--tools/usb/usbip/libsrc/names.h17
-rw-r--r--tools/usb/usbip/libsrc/usbip_common.c6
-rw-r--r--tools/usb/usbip/libsrc/usbip_device_driver.c20
-rw-r--r--tools/usb/usbip/libsrc/usbip_device_driver.h14
-rw-r--r--tools/usb/usbip/libsrc/usbip_host_common.c14
-rw-r--r--tools/usb/usbip/libsrc/usbip_host_common.h14
-rw-r--r--tools/usb/usbip/libsrc/usbip_host_driver.c14
-rw-r--r--tools/usb/usbip/libsrc/usbip_host_driver.h14
-rw-r--r--tools/usb/usbip/src/usbip.c14
-rw-r--r--tools/usb/usbip/src/usbip.h14
-rw-r--r--tools/usb/usbip/src/usbip_attach.c14
-rw-r--r--tools/usb/usbip/src/usbip_bind.c14
-rw-r--r--tools/usb/usbip/src/usbip_detach.c14
-rw-r--r--tools/usb/usbip/src/usbip_list.c14
-rw-r--r--tools/usb/usbip/src/usbip_network.c14
-rw-r--r--tools/usb/usbip/src/usbip_port.c11
-rw-r--r--tools/usb/usbip/src/usbip_unbind.c14
-rw-r--r--tools/usb/usbip/src/usbipd.c14
-rw-r--r--tools/usb/usbip/src/utils.c14
-rw-r--r--tools/usb/usbip/src/utils.h14
-rw-r--r--tools/virtio/linux/kernel.h2
-rw-r--r--tools/virtio/ringtest/main.c2
-rw-r--r--tools/virtio/ringtest/main.h2
-rw-r--r--tools/virtio/ringtest/ring.c2
-rw-r--r--tools/virtio/ringtest/virtio_ring_0_9.c2
-rw-r--r--tools/virtio/vhost_test/Makefile1
-rw-r--r--tools/virtio/virtio-trace/trace-agent-ctl.c4
-rw-r--r--tools/virtio/virtio-trace/trace-agent-rw.c4
-rw-r--r--tools/virtio/virtio-trace/trace-agent.c4
-rw-r--r--tools/vm/page-types.c14
-rw-r--r--tools/vm/slabinfo-gnuplot.sh9
-rw-r--r--tools/vm/slabinfo.c118
-rw-r--r--tools/wmi/Makefile1
-rw-r--r--tools/wmi/dell-smbios-example.c5
1523 files changed, 72454 insertions, 21255 deletions
diff --git a/tools/Makefile b/tools/Makefile
index 3dfd72ae6c1a..7e42f7b8bfa7 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -10,6 +10,7 @@ help:
@echo 'Possible targets:'
@echo ''
@echo ' acpi - ACPI tools'
+ @echo ' bpf - misc BPF tools'
@echo ' cgroup - cgroup tools'
@echo ' cpupower - a tool for all things x86 CPU power'
@echo ' debugging - tools for debugging'
@@ -19,15 +20,15 @@ help:
@echo ' gpio - GPIO tools'
@echo ' hv - tools used when in Hyper-V clients'
@echo ' iio - IIO tools'
+ @echo ' intel-speed-select - Intel Speed Select tool'
@echo ' kvm_stat - top-like utility for displaying kvm statistics'
@echo ' leds - LEDs tools'
@echo ' liblockdep - user-space wrapper for kernel locking-validator'
- @echo ' bpf - misc BPF tools'
+ @echo ' objtool - an ELF object analysis tool'
@echo ' pci - PCI tools'
@echo ' perf - Linux performance measurement and analysis tool'
@echo ' selftests - various kernel selftests'
@echo ' spi - spi tools'
- @echo ' objtool - an ELF object analysis tool'
@echo ' tmon - thermal monitoring and tuning tool'
@echo ' turbostat - Intel CPU idle stats and freq reporting tool'
@echo ' usb - USB testing tools'
@@ -82,7 +83,7 @@ perf: FORCE
selftests: FORCE
$(call descend,testing/$@)
-turbostat x86_energy_perf_policy: FORCE
+turbostat x86_energy_perf_policy intel-speed-select: FORCE
$(call descend,power/x86/$@)
tmon: FORCE
@@ -115,7 +116,7 @@ liblockdep_install:
selftests_install:
$(call descend,testing/$(@:_install=),install)
-turbostat_install x86_energy_perf_policy_install:
+turbostat_install x86_energy_perf_policy_install intel-speed-select_install:
$(call descend,power/x86/$(@:_install=),install)
tmon_install:
@@ -132,7 +133,7 @@ install: acpi_install cgroup_install cpupower_install gpio_install \
perf_install selftests_install turbostat_install usb_install \
virtio_install vm_install bpf_install x86_energy_perf_policy_install \
tmon_install freefall_install objtool_install kvm_stat_install \
- wmi_install pci_install debugging_install
+ wmi_install pci_install debugging_install intel-speed-select_install
acpi_clean:
$(call descend,power/acpi,clean)
@@ -162,7 +163,7 @@ perf_clean:
selftests_clean:
$(call descend,testing/$(@:_clean=),clean)
-turbostat_clean x86_energy_perf_policy_clean:
+turbostat_clean x86_energy_perf_policy_clean intel-speed-select_clean:
$(call descend,power/x86/$(@:_clean=),clean)
tmon_clean:
@@ -178,6 +179,7 @@ clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \
perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
- gpio_clean objtool_clean leds_clean wmi_clean pci_clean firmware_clean debugging_clean
+ gpio_clean objtool_clean leds_clean wmi_clean pci_clean firmware_clean debugging_clean \
+ intel-speed-select_clean
.PHONY: FORCE
diff --git a/tools/arch/arm/include/uapi/asm/kvm.h b/tools/arch/arm/include/uapi/asm/kvm.h
index 4602464ebdfb..a4217c1a5d01 100644
--- a/tools/arch/arm/include/uapi/asm/kvm.h
+++ b/tools/arch/arm/include/uapi/asm/kvm.h
@@ -214,6 +214,18 @@ struct kvm_vcpu_events {
#define KVM_REG_ARM_FW_REG(r) (KVM_REG_ARM | KVM_REG_SIZE_U64 | \
KVM_REG_ARM_FW | ((r) & 0xffff))
#define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0)
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1 KVM_REG_ARM_FW_REG(1)
+ /* Higher values mean better protection. */
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL 0
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL 1
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED 2
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2 KVM_REG_ARM_FW_REG(2)
+ /* Higher values mean better protection. */
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL 0
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN 1
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL 2
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED 3
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED (1U << 4)
/* Device Control API: ARM VGIC */
#define KVM_DEV_ARM_VGIC_GRP_ADDR 0
diff --git a/tools/arch/arm64/include/uapi/asm/kvm.h b/tools/arch/arm64/include/uapi/asm/kvm.h
index 97c3478ee6e7..9a507716ae2f 100644
--- a/tools/arch/arm64/include/uapi/asm/kvm.h
+++ b/tools/arch/arm64/include/uapi/asm/kvm.h
@@ -35,6 +35,7 @@
#include <linux/psci.h>
#include <linux/types.h>
#include <asm/ptrace.h>
+#include <asm/sve_context.h>
#define __KVM_HAVE_GUEST_DEBUG
#define __KVM_HAVE_IRQ_LINE
@@ -102,6 +103,9 @@ struct kvm_regs {
#define KVM_ARM_VCPU_EL1_32BIT 1 /* CPU running a 32bit VM */
#define KVM_ARM_VCPU_PSCI_0_2 2 /* CPU uses PSCI v0.2 */
#define KVM_ARM_VCPU_PMU_V3 3 /* Support guest PMUv3 */
+#define KVM_ARM_VCPU_SVE 4 /* enable SVE for this CPU */
+#define KVM_ARM_VCPU_PTRAUTH_ADDRESS 5 /* VCPU uses address authentication */
+#define KVM_ARM_VCPU_PTRAUTH_GENERIC 6 /* VCPU uses generic authentication */
struct kvm_vcpu_init {
__u32 target;
@@ -225,6 +229,62 @@ struct kvm_vcpu_events {
#define KVM_REG_ARM_FW_REG(r) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
KVM_REG_ARM_FW | ((r) & 0xffff))
#define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0)
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1 KVM_REG_ARM_FW_REG(1)
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL 0
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL 1
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED 2
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2 KVM_REG_ARM_FW_REG(2)
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL 0
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN 1
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL 2
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED 3
+#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED (1U << 4)
+
+/* SVE registers */
+#define KVM_REG_ARM64_SVE (0x15 << KVM_REG_ARM_COPROC_SHIFT)
+
+/* Z- and P-regs occupy blocks at the following offsets within this range: */
+#define KVM_REG_ARM64_SVE_ZREG_BASE 0
+#define KVM_REG_ARM64_SVE_PREG_BASE 0x400
+#define KVM_REG_ARM64_SVE_FFR_BASE 0x600
+
+#define KVM_ARM64_SVE_NUM_ZREGS __SVE_NUM_ZREGS
+#define KVM_ARM64_SVE_NUM_PREGS __SVE_NUM_PREGS
+
+#define KVM_ARM64_SVE_MAX_SLICES 32
+
+#define KVM_REG_ARM64_SVE_ZREG(n, i) \
+ (KVM_REG_ARM64 | KVM_REG_ARM64_SVE | KVM_REG_ARM64_SVE_ZREG_BASE | \
+ KVM_REG_SIZE_U2048 | \
+ (((n) & (KVM_ARM64_SVE_NUM_ZREGS - 1)) << 5) | \
+ ((i) & (KVM_ARM64_SVE_MAX_SLICES - 1)))
+
+#define KVM_REG_ARM64_SVE_PREG(n, i) \
+ (KVM_REG_ARM64 | KVM_REG_ARM64_SVE | KVM_REG_ARM64_SVE_PREG_BASE | \
+ KVM_REG_SIZE_U256 | \
+ (((n) & (KVM_ARM64_SVE_NUM_PREGS - 1)) << 5) | \
+ ((i) & (KVM_ARM64_SVE_MAX_SLICES - 1)))
+
+#define KVM_REG_ARM64_SVE_FFR(i) \
+ (KVM_REG_ARM64 | KVM_REG_ARM64_SVE | KVM_REG_ARM64_SVE_FFR_BASE | \
+ KVM_REG_SIZE_U256 | \
+ ((i) & (KVM_ARM64_SVE_MAX_SLICES - 1)))
+
+/*
+ * Register values for KVM_REG_ARM64_SVE_ZREG(), KVM_REG_ARM64_SVE_PREG() and
+ * KVM_REG_ARM64_SVE_FFR() are represented in memory in an endianness-
+ * invariant layout which differs from the layout used for the FPSIMD
+ * V-registers on big-endian systems: see sigcontext.h for more explanation.
+ */
+
+#define KVM_ARM64_SVE_VQ_MIN __SVE_VQ_MIN
+#define KVM_ARM64_SVE_VQ_MAX __SVE_VQ_MAX
+
+/* Vector lengths pseudo-register: */
+#define KVM_REG_ARM64_SVE_VLS (KVM_REG_ARM64 | KVM_REG_ARM64_SVE | \
+ KVM_REG_SIZE_U512 | 0xffff)
+#define KVM_ARM64_SVE_VLS_WORDS \
+ ((KVM_ARM64_SVE_VQ_MAX - KVM_ARM64_SVE_VQ_MIN) / 64 + 1)
/* Device Control API: ARM VGIC */
#define KVM_DEV_ARM_VGIC_GRP_ADDR 0
diff --git a/tools/arch/powerpc/include/uapi/asm/kvm.h b/tools/arch/powerpc/include/uapi/asm/kvm.h
index 26ca425f4c2c..b0f72dea8b11 100644
--- a/tools/arch/powerpc/include/uapi/asm/kvm.h
+++ b/tools/arch/powerpc/include/uapi/asm/kvm.h
@@ -482,6 +482,8 @@ struct kvm_ppc_cpu_char {
#define KVM_REG_PPC_ICP_PPRI_SHIFT 16 /* pending irq priority */
#define KVM_REG_PPC_ICP_PPRI_MASK 0xff
+#define KVM_REG_PPC_VP_STATE (KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x8d)
+
/* Device control API: PPC-specific devices */
#define KVM_DEV_MPIC_GRP_MISC 1
#define KVM_DEV_MPIC_BASE_ADDR 0 /* 64-bit */
@@ -677,4 +679,48 @@ struct kvm_ppc_cpu_char {
#define KVM_XICS_PRESENTED (1ULL << 43)
#define KVM_XICS_QUEUED (1ULL << 44)
+/* POWER9 XIVE Native Interrupt Controller */
+#define KVM_DEV_XIVE_GRP_CTRL 1
+#define KVM_DEV_XIVE_RESET 1
+#define KVM_DEV_XIVE_EQ_SYNC 2
+#define KVM_DEV_XIVE_GRP_SOURCE 2 /* 64-bit source identifier */
+#define KVM_DEV_XIVE_GRP_SOURCE_CONFIG 3 /* 64-bit source identifier */
+#define KVM_DEV_XIVE_GRP_EQ_CONFIG 4 /* 64-bit EQ identifier */
+#define KVM_DEV_XIVE_GRP_SOURCE_SYNC 5 /* 64-bit source identifier */
+
+/* Layout of 64-bit XIVE source attribute values */
+#define KVM_XIVE_LEVEL_SENSITIVE (1ULL << 0)
+#define KVM_XIVE_LEVEL_ASSERTED (1ULL << 1)
+
+/* Layout of 64-bit XIVE source configuration attribute values */
+#define KVM_XIVE_SOURCE_PRIORITY_SHIFT 0
+#define KVM_XIVE_SOURCE_PRIORITY_MASK 0x7
+#define KVM_XIVE_SOURCE_SERVER_SHIFT 3
+#define KVM_XIVE_SOURCE_SERVER_MASK 0xfffffff8ULL
+#define KVM_XIVE_SOURCE_MASKED_SHIFT 32
+#define KVM_XIVE_SOURCE_MASKED_MASK 0x100000000ULL
+#define KVM_XIVE_SOURCE_EISN_SHIFT 33
+#define KVM_XIVE_SOURCE_EISN_MASK 0xfffffffe00000000ULL
+
+/* Layout of 64-bit EQ identifier */
+#define KVM_XIVE_EQ_PRIORITY_SHIFT 0
+#define KVM_XIVE_EQ_PRIORITY_MASK 0x7
+#define KVM_XIVE_EQ_SERVER_SHIFT 3
+#define KVM_XIVE_EQ_SERVER_MASK 0xfffffff8ULL
+
+/* Layout of EQ configuration values (64 bytes) */
+struct kvm_ppc_xive_eq {
+ __u32 flags;
+ __u32 qshift;
+ __u64 qaddr;
+ __u32 qtoggle;
+ __u32 qindex;
+ __u8 pad[40];
+};
+
+#define KVM_XIVE_EQ_ALWAYS_NOTIFY 0x00000001
+
+#define KVM_XIVE_TIMA_PAGE_OFFSET 0
+#define KVM_XIVE_ESB_PAGE_OFFSET 4
+
#endif /* __LINUX_KVM_POWERPC_H */
diff --git a/tools/arch/powerpc/include/uapi/asm/mman.h b/tools/arch/powerpc/include/uapi/asm/mman.h
index f33105bc5ca6..8601d824a9c6 100644
--- a/tools/arch/powerpc/include/uapi/asm/mman.h
+++ b/tools/arch/powerpc/include/uapi/asm/mman.h
@@ -4,12 +4,8 @@
#define MAP_DENYWRITE 0x0800
#define MAP_EXECUTABLE 0x1000
#define MAP_GROWSDOWN 0x0100
-#define MAP_HUGETLB 0x40000
#define MAP_LOCKED 0x80
-#define MAP_NONBLOCK 0x10000
#define MAP_NORESERVE 0x40
-#define MAP_POPULATE 0x8000
-#define MAP_STACK 0x20000
#include <uapi/asm-generic/mman-common.h>
/* MAP_32BIT is undefined on powerpc, fix it for perf */
#define MAP_32BIT 0
diff --git a/tools/arch/riscv/include/uapi/asm/bitsperlong.h b/tools/arch/riscv/include/uapi/asm/bitsperlong.h
index 0b3cb52fd29d..0b9b58b57ff6 100644
--- a/tools/arch/riscv/include/uapi/asm/bitsperlong.h
+++ b/tools/arch/riscv/include/uapi/asm/bitsperlong.h
@@ -1,18 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2012 ARM Ltd.
* Copyright (C) 2015 Regents of the University of California
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _UAPI_ASM_RISCV_BITSPERLONG_H
diff --git a/tools/arch/riscv/include/uapi/asm/perf_regs.h b/tools/arch/riscv/include/uapi/asm/perf_regs.h
new file mode 100644
index 000000000000..196f964bfcb4
--- /dev/null
+++ b/tools/arch/riscv/include/uapi/asm/perf_regs.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. */
+
+#ifndef _ASM_RISCV_PERF_REGS_H
+#define _ASM_RISCV_PERF_REGS_H
+
+enum perf_event_riscv_regs {
+ PERF_REG_RISCV_PC,
+ PERF_REG_RISCV_RA,
+ PERF_REG_RISCV_SP,
+ PERF_REG_RISCV_GP,
+ PERF_REG_RISCV_TP,
+ PERF_REG_RISCV_T0,
+ PERF_REG_RISCV_T1,
+ PERF_REG_RISCV_T2,
+ PERF_REG_RISCV_S0,
+ PERF_REG_RISCV_S1,
+ PERF_REG_RISCV_A0,
+ PERF_REG_RISCV_A1,
+ PERF_REG_RISCV_A2,
+ PERF_REG_RISCV_A3,
+ PERF_REG_RISCV_A4,
+ PERF_REG_RISCV_A5,
+ PERF_REG_RISCV_A6,
+ PERF_REG_RISCV_A7,
+ PERF_REG_RISCV_S2,
+ PERF_REG_RISCV_S3,
+ PERF_REG_RISCV_S4,
+ PERF_REG_RISCV_S5,
+ PERF_REG_RISCV_S6,
+ PERF_REG_RISCV_S7,
+ PERF_REG_RISCV_S8,
+ PERF_REG_RISCV_S9,
+ PERF_REG_RISCV_S10,
+ PERF_REG_RISCV_S11,
+ PERF_REG_RISCV_T3,
+ PERF_REG_RISCV_T4,
+ PERF_REG_RISCV_T5,
+ PERF_REG_RISCV_T6,
+ PERF_REG_RISCV_MAX,
+};
+#endif /* _ASM_RISCV_PERF_REGS_H */
diff --git a/tools/arch/s390/include/uapi/asm/kvm.h b/tools/arch/s390/include/uapi/asm/kvm.h
index 09652eabe769..47104e5b47fd 100644
--- a/tools/arch/s390/include/uapi/asm/kvm.h
+++ b/tools/arch/s390/include/uapi/asm/kvm.h
@@ -153,7 +153,9 @@ struct kvm_s390_vm_cpu_subfunc {
__u8 ppno[16]; /* with MSA5 */
__u8 kma[16]; /* with MSA8 */
__u8 kdsa[16]; /* with MSA9 */
- __u8 reserved[1792];
+ __u8 sortl[32]; /* with STFLE.150 */
+ __u8 dfltcc[32]; /* with STFLE.151 */
+ __u8 reserved[1728];
};
/* kvm attributes for crypto */
diff --git a/tools/arch/sparc/include/uapi/asm/mman.h b/tools/arch/sparc/include/uapi/asm/mman.h
index 38920eed8cbf..7b94dccc843d 100644
--- a/tools/arch/sparc/include/uapi/asm/mman.h
+++ b/tools/arch/sparc/include/uapi/asm/mman.h
@@ -4,12 +4,8 @@
#define MAP_DENYWRITE 0x0800
#define MAP_EXECUTABLE 0x1000
#define MAP_GROWSDOWN 0x0200
-#define MAP_HUGETLB 0x40000
#define MAP_LOCKED 0x100
-#define MAP_NONBLOCK 0x10000
#define MAP_NORESERVE 0x40
-#define MAP_POPULATE 0x8000
-#define MAP_STACK 0x20000
#include <uapi/asm-generic/mman-common.h>
/* MAP_32BIT is undefined on sparc, fix it for perf */
#define MAP_32BIT 0
diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h
index 981ff9479648..5171b9c7ca3e 100644
--- a/tools/arch/x86/include/asm/cpufeatures.h
+++ b/tools/arch/x86/include/asm/cpufeatures.h
@@ -96,7 +96,6 @@
#define X86_FEATURE_SYSCALL32 ( 3*32+14) /* "" syscall in IA32 userspace */
#define X86_FEATURE_SYSENTER32 ( 3*32+15) /* "" sysenter in IA32 userspace */
#define X86_FEATURE_REP_GOOD ( 3*32+16) /* REP microcode works well */
-#define X86_FEATURE_MFENCE_RDTSC ( 3*32+17) /* "" MFENCE synchronizes RDTSC */
#define X86_FEATURE_LFENCE_RDTSC ( 3*32+18) /* "" LFENCE synchronizes RDTSC */
#define X86_FEATURE_ACC_POWER ( 3*32+19) /* AMD Accumulated Power Mechanism */
#define X86_FEATURE_NOPL ( 3*32+20) /* The NOPL (0F 1F) instructions */
@@ -239,12 +238,14 @@
#define X86_FEATURE_BMI1 ( 9*32+ 3) /* 1st group bit manipulation extensions */
#define X86_FEATURE_HLE ( 9*32+ 4) /* Hardware Lock Elision */
#define X86_FEATURE_AVX2 ( 9*32+ 5) /* AVX2 instructions */
+#define X86_FEATURE_FDP_EXCPTN_ONLY ( 9*32+ 6) /* "" FPU data pointer updated only on x87 exceptions */
#define X86_FEATURE_SMEP ( 9*32+ 7) /* Supervisor Mode Execution Protection */
#define X86_FEATURE_BMI2 ( 9*32+ 8) /* 2nd group bit manipulation extensions */
#define X86_FEATURE_ERMS ( 9*32+ 9) /* Enhanced REP MOVSB/STOSB instructions */
#define X86_FEATURE_INVPCID ( 9*32+10) /* Invalidate Processor Context ID */
#define X86_FEATURE_RTM ( 9*32+11) /* Restricted Transactional Memory */
#define X86_FEATURE_CQM ( 9*32+12) /* Cache QoS Monitoring */
+#define X86_FEATURE_ZERO_FCS_FDS ( 9*32+13) /* "" Zero out FPU CS and FPU DS */
#define X86_FEATURE_MPX ( 9*32+14) /* Memory Protection Extension */
#define X86_FEATURE_RDT_A ( 9*32+15) /* Resource Director Technology Allocation */
#define X86_FEATURE_AVX512F ( 9*32+16) /* AVX-512 Foundation */
@@ -269,13 +270,21 @@
#define X86_FEATURE_XGETBV1 (10*32+ 2) /* XGETBV with ECX = 1 instruction */
#define X86_FEATURE_XSAVES (10*32+ 3) /* XSAVES/XRSTORS instructions */
-/* Intel-defined CPU QoS Sub-leaf, CPUID level 0x0000000F:0 (EDX), word 11 */
-#define X86_FEATURE_CQM_LLC (11*32+ 1) /* LLC QoS if 1 */
+/*
+ * Extended auxiliary flags: Linux defined - for features scattered in various
+ * CPUID levels like 0xf, etc.
+ *
+ * Reuse free bits when adding new feature flags!
+ */
+#define X86_FEATURE_CQM_LLC (11*32+ 0) /* LLC QoS if 1 */
+#define X86_FEATURE_CQM_OCCUP_LLC (11*32+ 1) /* LLC occupancy monitoring */
+#define X86_FEATURE_CQM_MBM_TOTAL (11*32+ 2) /* LLC Total MBM monitoring */
+#define X86_FEATURE_CQM_MBM_LOCAL (11*32+ 3) /* LLC Local MBM monitoring */
+#define X86_FEATURE_FENCE_SWAPGS_USER (11*32+ 4) /* "" LFENCE in user entry SWAPGS path */
+#define X86_FEATURE_FENCE_SWAPGS_KERNEL (11*32+ 5) /* "" LFENCE in kernel entry SWAPGS path */
-/* Intel-defined CPU QoS Sub-leaf, CPUID level 0x0000000F:1 (EDX), word 12 */
-#define X86_FEATURE_CQM_OCCUP_LLC (12*32+ 0) /* LLC occupancy monitoring */
-#define X86_FEATURE_CQM_MBM_TOTAL (12*32+ 1) /* LLC Total MBM monitoring */
-#define X86_FEATURE_CQM_MBM_LOCAL (12*32+ 2) /* LLC Local MBM monitoring */
+/* Intel-defined CPU features, CPUID level 0x00000007:1 (EAX), word 12 */
+#define X86_FEATURE_AVX512_BF16 (12*32+ 5) /* AVX512 BFLOAT16 instructions */
/* AMD-defined CPU features, CPUID level 0x80000008 (EBX), word 13 */
#define X86_FEATURE_CLZERO (13*32+ 0) /* CLZERO instruction */
@@ -322,6 +331,7 @@
#define X86_FEATURE_UMIP (16*32+ 2) /* User Mode Instruction Protection */
#define X86_FEATURE_PKU (16*32+ 3) /* Protection Keys for Userspace */
#define X86_FEATURE_OSPKE (16*32+ 4) /* OS Protection Keys Enable */
+#define X86_FEATURE_WAITPKG (16*32+ 5) /* UMONITOR/UMWAIT/TPAUSE Instructions */
#define X86_FEATURE_AVX512_VBMI2 (16*32+ 6) /* Additional AVX512 Vector Bit Manipulation Instructions */
#define X86_FEATURE_GFNI (16*32+ 8) /* Galois Field New Instructions */
#define X86_FEATURE_VAES (16*32+ 9) /* Vector AES */
@@ -344,6 +354,7 @@
/* Intel-defined CPU features, CPUID level 0x00000007:0 (EDX), word 18 */
#define X86_FEATURE_AVX512_4VNNIW (18*32+ 2) /* AVX-512 Neural Network Instructions */
#define X86_FEATURE_AVX512_4FMAPS (18*32+ 3) /* AVX-512 Multiply Accumulation Single precision */
+#define X86_FEATURE_MD_CLEAR (18*32+10) /* VERW clears CPU buffers */
#define X86_FEATURE_TSX_FORCE_ABORT (18*32+13) /* "" TSX_FORCE_ABORT */
#define X86_FEATURE_PCONFIG (18*32+18) /* Intel PCONFIG */
#define X86_FEATURE_SPEC_CTRL (18*32+26) /* "" Speculation Control (IBRS + IBPB) */
@@ -382,5 +393,8 @@
#define X86_BUG_SPECTRE_V2 X86_BUG(16) /* CPU is affected by Spectre variant 2 attack with indirect branches */
#define X86_BUG_SPEC_STORE_BYPASS X86_BUG(17) /* CPU is affected by speculative store bypass attack */
#define X86_BUG_L1TF X86_BUG(18) /* CPU is affected by L1 Terminal Fault */
+#define X86_BUG_MDS X86_BUG(19) /* CPU is affected by Microarchitectural data sampling */
+#define X86_BUG_MSBDS_ONLY X86_BUG(20) /* CPU is only affected by the MSDBS variant of BUG_MDS */
+#define X86_BUG_SWAPGS X86_BUG(21) /* CPU is affected by speculation through SWAPGS */
#endif /* _ASM_X86_CPUFEATURES_H */
diff --git a/tools/perf/util/intel-pt-decoder/inat.h b/tools/arch/x86/include/asm/inat.h
index 52dc8d911173..877827b7c2c3 100644
--- a/tools/perf/util/intel-pt-decoder/inat.h
+++ b/tools/arch/x86/include/asm/inat.h
@@ -1,24 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _ASM_X86_INAT_H
#define _ASM_X86_INAT_H
/*
* x86 instruction attributes
*
* Written by Masami Hiramatsu <mhiramat@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
*/
#include "inat_types.h"
diff --git a/tools/arch/x86/include/asm/inat_types.h b/tools/arch/x86/include/asm/inat_types.h
new file mode 100644
index 000000000000..b047efa9ddc2
--- /dev/null
+++ b/tools/arch/x86/include/asm/inat_types.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _ASM_X86_INAT_TYPES_H
+#define _ASM_X86_INAT_TYPES_H
+/*
+ * x86 instruction attributes
+ *
+ * Written by Masami Hiramatsu <mhiramat@redhat.com>
+ */
+
+/* Instruction attributes */
+typedef unsigned int insn_attr_t;
+typedef unsigned char insn_byte_t;
+typedef signed int insn_value_t;
+
+#endif
diff --git a/tools/perf/util/intel-pt-decoder/insn.h b/tools/arch/x86/include/asm/insn.h
index 2669c9f748e4..37a4c390750b 100644
--- a/tools/perf/util/intel-pt-decoder/insn.h
+++ b/tools/arch/x86/include/asm/insn.h
@@ -1,22 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _ASM_X86_INSN_H
#define _ASM_X86_INSN_H
/*
* x86 instruction analysis
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
* Copyright (C) IBM Corporation, 2009
*/
diff --git a/tools/objtool/arch/x86/include/asm/orc_types.h b/tools/arch/x86/include/asm/orc_types.h
index 46f516dd80ce..6e060907c163 100644
--- a/tools/objtool/arch/x86/include/asm/orc_types.h
+++ b/tools/arch/x86/include/asm/orc_types.h
@@ -1,18 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _ORC_TYPES_H
diff --git a/tools/arch/x86/include/uapi/asm/kvm.h b/tools/arch/x86/include/uapi/asm/kvm.h
index 7a0e64ccd6ff..503d3f42da16 100644
--- a/tools/arch/x86/include/uapi/asm/kvm.h
+++ b/tools/arch/x86/include/uapi/asm/kvm.h
@@ -378,10 +378,14 @@ struct kvm_sync_regs {
struct kvm_vcpu_events events;
};
-#define KVM_X86_QUIRK_LINT0_REENABLED (1 << 0)
-#define KVM_X86_QUIRK_CD_NW_CLEARED (1 << 1)
-#define KVM_X86_QUIRK_LAPIC_MMIO_HOLE (1 << 2)
-#define KVM_X86_QUIRK_OUT_7E_INC_RIP (1 << 3)
+#define KVM_X86_QUIRK_LINT0_REENABLED (1 << 0)
+#define KVM_X86_QUIRK_CD_NW_CLEARED (1 << 1)
+#define KVM_X86_QUIRK_LAPIC_MMIO_HOLE (1 << 2)
+#define KVM_X86_QUIRK_OUT_7E_INC_RIP (1 << 3)
+#define KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT (1 << 4)
+
+#define KVM_STATE_NESTED_FORMAT_VMX 0
+#define KVM_STATE_NESTED_FORMAT_SVM 1 /* unused */
#define KVM_STATE_NESTED_GUEST_MODE 0x00000001
#define KVM_STATE_NESTED_RUN_PENDING 0x00000002
@@ -390,9 +394,16 @@ struct kvm_sync_regs {
#define KVM_STATE_NESTED_SMM_GUEST_MODE 0x00000001
#define KVM_STATE_NESTED_SMM_VMXON 0x00000002
-struct kvm_vmx_nested_state {
+#define KVM_STATE_NESTED_VMX_VMCS_SIZE 0x1000
+
+struct kvm_vmx_nested_state_data {
+ __u8 vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE];
+ __u8 shadow_vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE];
+};
+
+struct kvm_vmx_nested_state_hdr {
__u64 vmxon_pa;
- __u64 vmcs_pa;
+ __u64 vmcs12_pa;
struct {
__u16 flags;
@@ -401,24 +412,38 @@ struct kvm_vmx_nested_state {
/* for KVM_CAP_NESTED_STATE */
struct kvm_nested_state {
- /* KVM_STATE_* flags */
__u16 flags;
-
- /* 0 for VMX, 1 for SVM. */
__u16 format;
-
- /* 128 for SVM, 128 + VMCS size for VMX. */
__u32 size;
union {
- /* VMXON, VMCS */
- struct kvm_vmx_nested_state vmx;
+ struct kvm_vmx_nested_state_hdr vmx;
/* Pad the header to 128 bytes. */
__u8 pad[120];
- };
+ } hdr;
- __u8 data[0];
+ /*
+ * Define data region as 0 bytes to preserve backwards-compatability
+ * to old definition of kvm_nested_state in order to avoid changing
+ * KVM_{GET,PUT}_NESTED_STATE ioctl values.
+ */
+ union {
+ struct kvm_vmx_nested_state_data vmx[0];
+ } data;
+};
+
+/* for KVM_CAP_PMU_EVENT_FILTER */
+struct kvm_pmu_event_filter {
+ __u32 action;
+ __u32 nevents;
+ __u32 fixed_counter_bitmap;
+ __u32 flags;
+ __u32 pad[4];
+ __u64 events[0];
};
+#define KVM_PMU_EVENT_ALLOW 0
+#define KVM_PMU_EVENT_DENY 1
+
#endif /* _ASM_X86_KVM_H */
diff --git a/tools/arch/x86/include/uapi/asm/perf_regs.h b/tools/arch/x86/include/uapi/asm/perf_regs.h
index ac67bbea10ca..7c9d2bb3833b 100644
--- a/tools/arch/x86/include/uapi/asm/perf_regs.h
+++ b/tools/arch/x86/include/uapi/asm/perf_regs.h
@@ -52,4 +52,7 @@ enum perf_event_x86_regs {
/* These include both GPRs and XMMX registers */
PERF_REG_X86_XMM_MAX = PERF_REG_X86_XMM15 + 2,
};
+
+#define PERF_REG_EXTENDED_MASK (~((1ULL << PERF_REG_X86_XMM0) - 1))
+
#endif /* _ASM_X86_PERF_REGS_H */
diff --git a/tools/arch/x86/include/uapi/asm/vmx.h b/tools/arch/x86/include/uapi/asm/vmx.h
index d213ec5c3766..f0b0c90dd398 100644
--- a/tools/arch/x86/include/uapi/asm/vmx.h
+++ b/tools/arch/x86/include/uapi/asm/vmx.h
@@ -146,7 +146,6 @@
#define VMX_ABORT_SAVE_GUEST_MSR_FAIL 1
#define VMX_ABORT_LOAD_HOST_PDPTE_FAIL 2
-#define VMX_ABORT_VMCS_CORRUPTED 3
#define VMX_ABORT_LOAD_HOST_MSR_FAIL 4
#endif /* _UAPIVMX_H */
diff --git a/tools/perf/util/intel-pt-decoder/inat.c b/tools/arch/x86/lib/inat.c
index 906d94aa0a24..4f5ed49e1b4e 100644
--- a/tools/perf/util/intel-pt-decoder/inat.c
+++ b/tools/arch/x86/lib/inat.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* x86 instruction attribute tables
*
* Written by Masami Hiramatsu <mhiramat@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
*/
-#include "insn.h"
+#include "../include/asm/insn.h"
/* Attribute tables are generated from opcode map */
#include "inat-tables.c"
@@ -94,3 +80,4 @@ insn_attr_t inat_get_avx_attribute(insn_byte_t opcode, insn_byte_t vex_m,
}
return table[opcode];
}
+
diff --git a/tools/perf/util/intel-pt-decoder/insn.c b/tools/arch/x86/lib/insn.c
index ca983e2bea8b..79e048f1d902 100644
--- a/tools/perf/util/intel-pt-decoder/insn.c
+++ b/tools/arch/x86/lib/insn.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* x86 instruction analysis
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
* Copyright (C) IBM Corporation, 2002, 2004, 2009
*/
@@ -23,8 +10,8 @@
#else
#include <string.h>
#endif
-#include "inat.h"
-#include "insn.h"
+#include "../include/asm/inat.h"
+#include "../include/asm/insn.h"
/* Verify next sizeof(t) bytes can be on the same instruction */
#define validate_next(t, insn, n) \
diff --git a/tools/arch/x86/lib/memcpy_64.S b/tools/arch/x86/lib/memcpy_64.S
index 9d05572370ed..92748660ba51 100644
--- a/tools/arch/x86/lib/memcpy_64.S
+++ b/tools/arch/x86/lib/memcpy_64.S
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright 2002 Andi Kleen */
#include <linux/linkage.h>
diff --git a/tools/objtool/arch/x86/lib/x86-opcode-map.txt b/tools/arch/x86/lib/x86-opcode-map.txt
index e0b85930dd77..e0b85930dd77 100644
--- a/tools/objtool/arch/x86/lib/x86-opcode-map.txt
+++ b/tools/arch/x86/lib/x86-opcode-map.txt
diff --git a/tools/objtool/arch/x86/tools/gen-insn-attr-x86.awk b/tools/arch/x86/tools/gen-insn-attr-x86.awk
index b02a36b2c14f..b02a36b2c14f 100644
--- a/tools/objtool/arch/x86/tools/gen-insn-attr-x86.awk
+++ b/tools/arch/x86/tools/gen-insn-attr-x86.awk
diff --git a/tools/bpf/.gitignore b/tools/bpf/.gitignore
index dfe2bd5a4b95..59024197e71d 100644
--- a/tools/bpf/.gitignore
+++ b/tools/bpf/.gitignore
@@ -1,4 +1,5 @@
FEATURE-DUMP.bpf
+feature
bpf_asm
bpf_dbg
bpf_exp.yacc.*
diff --git a/tools/bpf/Makefile b/tools/bpf/Makefile
index 53b60ad452f5..fbf5e4a0cb9c 100644
--- a/tools/bpf/Makefile
+++ b/tools/bpf/Makefile
@@ -81,10 +81,11 @@ $(OUTPUT)bpf_exp.lex.o: $(OUTPUT)bpf_exp.lex.c
clean: bpftool_clean
$(call QUIET_CLEAN, bpf-progs)
- $(Q)rm -rf $(OUTPUT)*.o $(OUTPUT)bpf_jit_disasm $(OUTPUT)bpf_dbg \
+ $(Q)$(RM) -r -- $(OUTPUT)*.o $(OUTPUT)bpf_jit_disasm $(OUTPUT)bpf_dbg \
$(OUTPUT)bpf_asm $(OUTPUT)bpf_exp.yacc.* $(OUTPUT)bpf_exp.lex.*
$(call QUIET_CLEAN, core-gen)
- $(Q)rm -f $(OUTPUT)FEATURE-DUMP.bpf
+ $(Q)$(RM) -- $(OUTPUT)FEATURE-DUMP.bpf
+ $(Q)$(RM) -r -- $(OUTPUT)feature
install: $(PROGS) bpftool_install
$(call QUIET_INSTALL, bpf_jit_disasm)
diff --git a/tools/bpf/Makefile.helpers b/tools/bpf/Makefile.helpers
index c34fea77f39f..854d084026dd 100644
--- a/tools/bpf/Makefile.helpers
+++ b/tools/bpf/Makefile.helpers
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
ifndef allow-override
include ../scripts/Makefile.include
include ../scripts/utilities.mak
diff --git a/tools/bpf/bpf_asm.c b/tools/bpf/bpf_asm.c
index c15aef097b04..e5f95e3eede3 100644
--- a/tools/bpf/bpf_asm.c
+++ b/tools/bpf/bpf_asm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Minimal BPF assembler
*
@@ -16,7 +17,6 @@
* pretty print a C-like construct.
*
* Copyright 2013 Daniel Borkmann <borkmann@redhat.com>
- * Licensed under the GNU General Public License, version 2.0 (GPLv2)
*/
#include <stdbool.h>
diff --git a/tools/bpf/bpf_dbg.c b/tools/bpf/bpf_dbg.c
index 61b9aa5d6415..9d3766e653a9 100644
--- a/tools/bpf/bpf_dbg.c
+++ b/tools/bpf/bpf_dbg.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Minimal BPF debugger
*
@@ -28,7 +29,6 @@
* 7) > step [-<n>, +<n>] (performs single stepping through the BPF)
*
* Copyright 2013 Daniel Borkmann <borkmann@redhat.com>
- * Licensed under the GNU General Public License, version 2.0 (GPLv2)
*/
#include <stdio.h>
diff --git a/tools/bpf/bpf_jit_disasm.c b/tools/bpf/bpf_jit_disasm.c
index 58c2bab4ef6e..c8ae95804728 100644
--- a/tools/bpf/bpf_jit_disasm.c
+++ b/tools/bpf/bpf_jit_disasm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Minimal BPF JIT image disassembler
*
@@ -11,7 +12,6 @@
* 3) Run e.g. `bpf_jit_disasm -o` to read out the last JIT code
*
* Copyright 2013 Daniel Borkmann <borkmann@redhat.com>
- * Licensed under the GNU General Public License, version 2.0 (GPLv2)
*/
#include <stdint.h>
diff --git a/tools/bpf/bpftool/.gitignore b/tools/bpf/bpftool/.gitignore
index 8248b8dd89d4..b13926432b84 100644
--- a/tools/bpf/bpftool/.gitignore
+++ b/tools/bpf/bpftool/.gitignore
@@ -3,3 +3,5 @@
bpftool*.8
bpf-helpers.*
FEATURE-DUMP.bpftool
+feature
+libbpf
diff --git a/tools/bpf/bpftool/Documentation/Makefile b/tools/bpf/bpftool/Documentation/Makefile
index f7663a3e60c9..815ac9804aee 100644
--- a/tools/bpf/bpftool/Documentation/Makefile
+++ b/tools/bpf/bpftool/Documentation/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
include ../../../scripts/Makefile.include
include ../../../scripts/utilities.mak
diff --git a/tools/bpf/bpftool/Documentation/bpftool-btf.rst b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
index 2dbc1413fabd..39615f8e145b 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-btf.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
@@ -19,35 +19,47 @@ SYNOPSIS
BTF COMMANDS
=============
-| **bpftool** **btf dump** *BTF_SRC*
+| **bpftool** **btf** { **show** | **list** } [**id** *BTF_ID*]
+| **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*]
| **bpftool** **btf help**
|
| *BTF_SRC* := { **id** *BTF_ID* | **prog** *PROG* | **map** *MAP* [{**key** | **value** | **kv** | **all**}] | **file** *FILE* }
+| *FORMAT* := { **raw** | **c** }
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
DESCRIPTION
===========
+ **bpftool btf { show | list }** [**id** *BTF_ID*]
+ Show information about loaded BTF objects. If a BTF ID is
+ specified, show information only about given BTF object,
+ otherwise list all BTF objects currently loaded on the
+ system.
+
**bpftool btf dump** *BTF_SRC*
Dump BTF entries from a given *BTF_SRC*.
- When **id** is specified, BTF object with that ID will be
- loaded and all its BTF types emitted.
+ When **id** is specified, BTF object with that ID will be
+ loaded and all its BTF types emitted.
+
+ When **map** is provided, it's expected that map has
+ associated BTF object with BTF types describing key and
+ value. It's possible to select whether to dump only BTF
+ type(s) associated with key (**key**), value (**value**),
+ both key and value (**kv**), or all BTF types present in
+ associated BTF object (**all**). If not specified, **kv**
+ is assumed.
- When **map** is provided, it's expected that map has
- associated BTF object with BTF types describing key and
- value. It's possible to select whether to dump only BTF
- type(s) associated with key (**key**), value (**value**),
- both key and value (**kv**), or all BTF types present in
- associated BTF object (**all**). If not specified, **kv**
- is assumed.
+ When **prog** is provided, it's expected that program has
+ associated BTF object with BTF types.
- When **prog** is provided, it's expected that program has
- associated BTF object with BTF types.
+ When specifying *FILE*, an ELF file is expected, containing
+ .BTF section with well-defined BTF binary format data,
+ typically produced by clang or pahole.
- When specifying *FILE*, an ELF file is expected, containing
- .BTF section with well-defined BTF binary format data,
- typically produced by clang or pahole.
+ **format** option can be used to override default (raw)
+ output format. Raw (**raw**) or C-syntax (**c**) output
+ formats are supported.
**bpftool btf help**
Print short help message.
@@ -67,6 +79,10 @@ OPTIONS
-p, --pretty
Generate human-readable JSON output. Implies **-j**.
+ -d, --debug
+ Print all logs available from libbpf, including debug-level
+ information.
+
EXAMPLES
========
**# bpftool btf dump id 1226**
diff --git a/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst b/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst
index ac26876389c2..06a28b07787d 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst
@@ -20,8 +20,8 @@ SYNOPSIS
CGROUP COMMANDS
===============
-| **bpftool** **cgroup { show | list }** *CGROUP*
-| **bpftool** **cgroup tree** [*CGROUP_ROOT*]
+| **bpftool** **cgroup { show | list }** *CGROUP* [**effective**]
+| **bpftool** **cgroup tree** [*CGROUP_ROOT*] [**effective**]
| **bpftool** **cgroup attach** *CGROUP* *ATTACH_TYPE* *PROG* [*ATTACH_FLAGS*]
| **bpftool** **cgroup detach** *CGROUP* *ATTACH_TYPE* *PROG*
| **bpftool** **cgroup help**
@@ -29,18 +29,23 @@ CGROUP COMMANDS
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
| *ATTACH_TYPE* := { **ingress** | **egress** | **sock_create** | **sock_ops** | **device** |
| **bind4** | **bind6** | **post_bind4** | **post_bind6** | **connect4** | **connect6** |
-| **sendmsg4** | **sendmsg6** | **sysctl** }
+| **sendmsg4** | **sendmsg6** | **recvmsg4** | **recvmsg6** | **sysctl** |
+| **getsockopt** | **setsockopt** }
| *ATTACH_FLAGS* := { **multi** | **override** }
DESCRIPTION
===========
- **bpftool cgroup { show | list }** *CGROUP*
+ **bpftool cgroup { show | list }** *CGROUP* [**effective**]
List all programs attached to the cgroup *CGROUP*.
Output will start with program ID followed by attach type,
attach flags and program name.
- **bpftool cgroup tree** [*CGROUP_ROOT*]
+ If **effective** is specified retrieve effective programs that
+ will execute for events within a cgroup. This includes
+ inherited along with attached ones.
+
+ **bpftool cgroup tree** [*CGROUP_ROOT*] [**effective**]
Iterate over all cgroups in *CGROUP_ROOT* and list all
attached programs. If *CGROUP_ROOT* is not specified,
bpftool uses cgroup v2 mountpoint.
@@ -49,6 +54,10 @@ DESCRIPTION
commands: it starts with absolute cgroup path, followed by
program ID, attach type, attach flags and program name.
+ If **effective** is specified retrieve effective programs that
+ will execute for events within a cgroup. This includes
+ inherited along with attached ones.
+
**bpftool cgroup attach** *CGROUP* *ATTACH_TYPE* *PROG* [*ATTACH_FLAGS*]
Attach program *PROG* to the cgroup *CGROUP* with attach type
*ATTACH_TYPE* and optional *ATTACH_FLAGS*.
@@ -86,7 +95,13 @@ DESCRIPTION
unconnected udp4 socket (since 4.18);
**sendmsg6** call to sendto(2), sendmsg(2), sendmmsg(2) for an
unconnected udp6 socket (since 4.18);
- **sysctl** sysctl access (since 5.2).
+ **recvmsg4** call to recvfrom(2), recvmsg(2), recvmmsg(2) for
+ an unconnected udp4 socket (since 5.2);
+ **recvmsg6** call to recvfrom(2), recvmsg(2), recvmmsg(2) for
+ an unconnected udp6 socket (since 5.2);
+ **sysctl** sysctl access (since 5.2);
+ **getsockopt** call to getsockopt (since 5.3);
+ **setsockopt** call to setsockopt (since 5.3).
**bpftool cgroup detach** *CGROUP* *ATTACH_TYPE* *PROG*
Detach *PROG* from the cgroup *CGROUP* and attach type
@@ -113,6 +128,10 @@ OPTIONS
-f, --bpffs
Show file names of pinned programs.
+ -d, --debug
+ Print all logs available from libbpf, including debug-level
+ information.
+
EXAMPLES
========
|
diff --git a/tools/bpf/bpftool/Documentation/bpftool-feature.rst b/tools/bpf/bpftool/Documentation/bpftool-feature.rst
index 14180e887082..4d08f35034a2 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-feature.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-feature.rst
@@ -73,6 +73,10 @@ OPTIONS
-p, --pretty
Generate human-readable JSON output. Implies **-j**.
+ -d, --debug
+ Print all logs available from libbpf, including debug-level
+ information.
+
SEE ALSO
========
**bpf**\ (2),
diff --git a/tools/bpf/bpftool/Documentation/bpftool-map.rst b/tools/bpf/bpftool/Documentation/bpftool-map.rst
index 13ef27b39f20..1c0f7146aab0 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-map.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-map.rst
@@ -36,6 +36,7 @@ MAP COMMANDS
| **bpftool** **map pop** *MAP*
| **bpftool** **map enqueue** *MAP* **value** *VALUE*
| **bpftool** **map dequeue** *MAP*
+| **bpftool** **map freeze** *MAP*
| **bpftool** **map help**
|
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
@@ -46,7 +47,7 @@ MAP COMMANDS
| *TYPE* := { **hash** | **array** | **prog_array** | **perf_event_array** | **percpu_hash**
| | **percpu_array** | **stack_trace** | **cgroup_array** | **lru_hash**
| | **lru_percpu_hash** | **lpm_trie** | **array_of_maps** | **hash_of_maps**
-| | **devmap** | **sockmap** | **cpumap** | **xskmap** | **sockhash**
+| | **devmap** | **devmap_hash** | **sockmap** | **cpumap** | **xskmap** | **sockhash**
| | **cgroup_storage** | **reuseport_sockarray** | **percpu_cgroup_storage**
| | **queue** | **stack** }
@@ -127,6 +128,14 @@ DESCRIPTION
**bpftool map dequeue** *MAP*
Dequeue and print **value** from the queue.
+ **bpftool map freeze** *MAP*
+ Freeze the map as read-only from user space. Entries from a
+ frozen map can not longer be updated or deleted with the
+ **bpf\ ()** system call. This operation is not reversible,
+ and the map remains immutable from user space until its
+ destruction. However, read and write permissions for BPF
+ programs to the map remain unchanged.
+
**bpftool map help**
Print short help message.
@@ -152,6 +161,10 @@ OPTIONS
Do not automatically attempt to mount any virtual file system
(such as tracefs or BPF virtual file system) when necessary.
+ -d, --debug
+ Print all logs available from libbpf, including debug-level
+ information.
+
EXAMPLES
========
**# bpftool map show**
diff --git a/tools/bpf/bpftool/Documentation/bpftool-net.rst b/tools/bpf/bpftool/Documentation/bpftool-net.rst
index 934580850f42..8651b00b81ea 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-net.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-net.rst
@@ -15,17 +15,22 @@ SYNOPSIS
*OPTIONS* := { [{ **-j** | **--json** }] [{ **-p** | **--pretty** }] }
*COMMANDS* :=
- { **show** | **list** } [ **dev** name ] | **help**
+ { **show** | **list** | **attach** | **detach** | **help** }
NET COMMANDS
============
-| **bpftool** **net { show | list } [ dev name ]**
+| **bpftool** **net { show | list }** [ **dev** *NAME* ]
+| **bpftool** **net attach** *ATTACH_TYPE* *PROG* **dev** *NAME* [ **overwrite** ]
+| **bpftool** **net detach** *ATTACH_TYPE* **dev** *NAME*
| **bpftool** **net help**
+|
+| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
+| *ATTACH_TYPE* := { **xdp** | **xdpgeneric** | **xdpdrv** | **xdpoffload** }
DESCRIPTION
===========
- **bpftool net { show | list } [ dev name ]**
+ **bpftool net { show | list }** [ **dev** *NAME* ]
List bpf program attachments in the kernel networking subsystem.
Currently, only device driver xdp attachments and tc filter
@@ -47,6 +52,24 @@ DESCRIPTION
all bpf programs attached to non clsact qdiscs, and finally all
bpf programs attached to root and clsact qdisc.
+ **bpftool** **net attach** *ATTACH_TYPE* *PROG* **dev** *NAME* [ **overwrite** ]
+ Attach bpf program *PROG* to network interface *NAME* with
+ type specified by *ATTACH_TYPE*. Previously attached bpf program
+ can be replaced by the command used with **overwrite** option.
+ Currently, only XDP-related modes are supported for *ATTACH_TYPE*.
+
+ *ATTACH_TYPE* can be of:
+ **xdp** - try native XDP and fallback to generic XDP if NIC driver does not support it;
+ **xdpgeneric** - Generic XDP. runs at generic XDP hook when packet already enters receive path as skb;
+ **xdpdrv** - Native XDP. runs earliest point in driver's receive path;
+ **xdpoffload** - Offload XDP. runs directly on NIC on each packet reception;
+
+ **bpftool** **net detach** *ATTACH_TYPE* **dev** *NAME*
+ Detach bpf program attached to network interface *NAME* with
+ type specified by *ATTACH_TYPE*. To detach bpf program, same
+ *ATTACH_TYPE* previously used for attach must be specified.
+ Currently, only XDP-related modes are supported for *ATTACH_TYPE*.
+
**bpftool net help**
Print short help message.
@@ -65,6 +88,10 @@ OPTIONS
-p, --pretty
Generate human-readable JSON output. Implies **-j**.
+ -d, --debug
+ Print all logs available from libbpf, including debug-level
+ information.
+
EXAMPLES
========
@@ -133,6 +160,34 @@ EXAMPLES
}
]
+|
+| **# bpftool net attach xdpdrv id 16 dev enp6s0np0**
+| **# bpftool net**
+
+::
+
+ xdp:
+ enp6s0np0(4) driver id 16
+
+|
+| **# bpftool net attach xdpdrv id 16 dev enp6s0np0**
+| **# bpftool net attach xdpdrv id 20 dev enp6s0np0 overwrite**
+| **# bpftool net**
+
+::
+
+ xdp:
+ enp6s0np0(4) driver id 20
+
+|
+| **# bpftool net attach xdpdrv id 16 dev enp6s0np0**
+| **# bpftool net detach xdpdrv dev enp6s0np0**
+| **# bpftool net**
+
+::
+
+ xdp:
+
SEE ALSO
========
diff --git a/tools/bpf/bpftool/Documentation/bpftool-perf.rst b/tools/bpf/bpftool/Documentation/bpftool-perf.rst
index 0c7576523a21..e252bd0bc434 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-perf.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-perf.rst
@@ -53,6 +53,10 @@ OPTIONS
-p, --pretty
Generate human-readable JSON output. Implies **-j**.
+ -d, --debug
+ Print all logs available from libbpf, including debug-level
+ information.
+
EXAMPLES
========
diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
index e8118544d118..7a374b3c851d 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
@@ -29,6 +29,7 @@ PROG COMMANDS
| **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
| **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
| **bpftool** **prog tracelog**
+| **bpftool** **prog run** *PROG* **data_in** *FILE* [**data_out** *FILE* [**data_size_out** *L*]] [**ctx_in** *FILE* [**ctx_out** *FILE* [**ctx_size_out** *M*]]] [**repeat** *N*]
| **bpftool** **prog help**
|
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
@@ -40,7 +41,8 @@ PROG COMMANDS
| **lwt_seg6local** | **sockops** | **sk_skb** | **sk_msg** | **lirc_mode2** |
| **cgroup/bind4** | **cgroup/bind6** | **cgroup/post_bind4** | **cgroup/post_bind6** |
| **cgroup/connect4** | **cgroup/connect6** | **cgroup/sendmsg4** | **cgroup/sendmsg6** |
-| **cgroup/sysctl**
+| **cgroup/recvmsg4** | **cgroup/recvmsg6** | **cgroup/sysctl** |
+| **cgroup/getsockopt** | **cgroup/setsockopt**
| }
| *ATTACH_TYPE* := {
| **msg_verdict** | **stream_verdict** | **stream_parser** | **flow_dissector**
@@ -145,6 +147,39 @@ DESCRIPTION
streaming data from BPF programs to user space, one can use
perf events (see also **bpftool-map**\ (8)).
+ **bpftool prog run** *PROG* **data_in** *FILE* [**data_out** *FILE* [**data_size_out** *L*]] [**ctx_in** *FILE* [**ctx_out** *FILE* [**ctx_size_out** *M*]]] [**repeat** *N*]
+ Run BPF program *PROG* in the kernel testing infrastructure
+ for BPF, meaning that the program works on the data and
+ context provided by the user, and not on actual packets or
+ monitored functions etc. Return value and duration for the
+ test run are printed out to the console.
+
+ Input data is read from the *FILE* passed with **data_in**.
+ If this *FILE* is "**-**", input data is read from standard
+ input. Input context, if any, is read from *FILE* passed with
+ **ctx_in**. Again, "**-**" can be used to read from standard
+ input, but only if standard input is not already in use for
+ input data. If a *FILE* is passed with **data_out**, output
+ data is written to that file. Similarly, output context is
+ written to the *FILE* passed with **ctx_out**. For both
+ output flows, "**-**" can be used to print to the standard
+ output (as plain text, or JSON if relevant option was
+ passed). If output keywords are omitted, output data and
+ context are discarded. Keywords **data_size_out** and
+ **ctx_size_out** are used to pass the size (in bytes) for the
+ output buffers to the kernel, although the default of 32 kB
+ should be more than enough for most cases.
+
+ Keyword **repeat** is used to indicate the number of
+ consecutive runs to perform. Note that output data and
+ context printed to files correspond to the last of those
+ runs. The duration printed out at the end of the runs is an
+ average over all runs performed by the command.
+
+ Not all program types support test run. Among those which do,
+ not all of them can take the **ctx_in**/**ctx_out**
+ arguments. bpftool does not perform checks on program types.
+
**bpftool prog help**
Print short help message.
@@ -174,6 +209,11 @@ OPTIONS
Do not automatically attempt to mount any virtual file system
(such as tracefs or BPF virtual file system) when necessary.
+ -d, --debug
+ Print all logs available, even debug-level information. This
+ includes logs from libbpf as well as from the verifier, when
+ attempting to load programs.
+
EXAMPLES
========
**# bpftool prog show**
diff --git a/tools/bpf/bpftool/Documentation/bpftool.rst b/tools/bpf/bpftool/Documentation/bpftool.rst
index 3e562d7fd56f..6a9c52ef84a9 100644
--- a/tools/bpf/bpftool/Documentation/bpftool.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool.rst
@@ -66,6 +66,10 @@ OPTIONS
Do not automatically attempt to mount any virtual file system
(such as tracefs or BPF virtual file system) when necessary.
+ -d, --debug
+ Print all logs available, even debug-level information. This
+ includes logs from libbpf as well as from the verifier, when
+ attempting to load programs.
SEE ALSO
========
diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile
index 4ad1f0894d53..39bc6f0f4f0b 100644
--- a/tools/bpf/bpftool/Makefile
+++ b/tools/bpf/bpftool/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
include ../../scripts/Makefile.include
include ../../scripts/utilities.mak
@@ -16,27 +17,30 @@ endif
BPF_DIR = $(srctree)/tools/lib/bpf/
ifneq ($(OUTPUT),)
- BPF_PATH = $(OUTPUT)
+ LIBBPF_OUTPUT = $(OUTPUT)/libbpf/
+ LIBBPF_PATH = $(LIBBPF_OUTPUT)
else
- BPF_PATH = $(BPF_DIR)
+ LIBBPF_PATH = $(BPF_DIR)
endif
-LIBBPF = $(BPF_PATH)libbpf.a
+LIBBPF = $(LIBBPF_PATH)libbpf.a
-BPFTOOL_VERSION := $(shell make --no-print-directory -sC ../../.. kernelversion)
+BPFTOOL_VERSION := $(shell make -rR --no-print-directory -sC ../../.. kernelversion)
$(LIBBPF): FORCE
- $(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) $(OUTPUT)libbpf.a
+ $(if $(LIBBPF_OUTPUT),@mkdir -p $(LIBBPF_OUTPUT))
+ $(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(LIBBPF_OUTPUT) $(LIBBPF_OUTPUT)libbpf.a
$(LIBBPF)-clean:
$(call QUIET_CLEAN, libbpf)
- $(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) clean >/dev/null
+ $(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(LIBBPF_OUTPUT) clean >/dev/null
prefix ?= /usr/local
bash_compdir ?= /usr/share/bash-completion/completions
CFLAGS += -O2
-CFLAGS += -W -Wall -Wextra -Wno-unused-parameter -Wshadow -Wno-missing-field-initializers
+CFLAGS += -W -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers
+CFLAGS += $(filter-out -Wswitch-enum,$(EXTRA_WARNINGS))
CFLAGS += -DPACKAGE='"bpftool"' -D__EXPORTED_HEADERS__ \
-I$(srctree)/kernel/bpf/ \
-I$(srctree)/tools/include \
@@ -51,14 +55,14 @@ ifneq ($(EXTRA_LDFLAGS),)
LDFLAGS += $(EXTRA_LDFLAGS)
endif
-LIBS = -lelf $(LIBBPF)
+LIBS = $(LIBBPF) -lelf -lz
INSTALL ?= install
RM ?= rm -f
FEATURE_USER = .bpftool
-FEATURE_TESTS = libbfd disassembler-four-args reallocarray
-FEATURE_DISPLAY = libbfd disassembler-four-args
+FEATURE_TESTS = libbfd disassembler-four-args reallocarray zlib
+FEATURE_DISPLAY = libbfd disassembler-four-args zlib
check_feat := 1
NON_CHECK_FEAT_TARGETS := clean uninstall doc doc-clean doc-install doc-uninstall
@@ -110,17 +114,21 @@ OBJS = $(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o
$(OUTPUT)disasm.o: $(srctree)/kernel/bpf/disasm.c
$(QUIET_CC)$(COMPILE.c) -MMD -o $@ $<
+$(OUTPUT)feature.o: | zdep
+
$(OUTPUT)bpftool: $(OBJS) $(LIBBPF)
- $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)
+ $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
$(OUTPUT)%.o: %.c
$(QUIET_CC)$(COMPILE.c) -MMD -o $@ $<
clean: $(LIBBPF)-clean
$(call QUIET_CLEAN, bpftool)
- $(Q)$(RM) $(OUTPUT)bpftool $(OUTPUT)*.o $(OUTPUT)*.d
+ $(Q)$(RM) -- $(OUTPUT)bpftool $(OUTPUT)*.o $(OUTPUT)*.d
+ $(Q)$(RM) -r -- $(OUTPUT)libbpf/
$(call QUIET_CLEAN, core-gen)
- $(Q)$(RM) $(OUTPUT)FEATURE-DUMP.bpftool
+ $(Q)$(RM) -- $(OUTPUT)FEATURE-DUMP.bpftool
+ $(Q)$(RM) -r -- $(OUTPUT)feature/
install: $(OUTPUT)bpftool
$(call QUIET_INSTALL, bpftool)
@@ -131,8 +139,8 @@ install: $(OUTPUT)bpftool
uninstall:
$(call QUIET_UNINST, bpftool)
- $(Q)$(RM) $(DESTDIR)$(prefix)/sbin/bpftool
- $(Q)$(RM) $(DESTDIR)$(bash_compdir)/bpftool
+ $(Q)$(RM) -- $(DESTDIR)$(prefix)/sbin/bpftool
+ $(Q)$(RM) -- $(DESTDIR)$(bash_compdir)/bpftool
doc:
$(call descend,Documentation)
@@ -148,6 +156,9 @@ doc-uninstall:
FORCE:
-.PHONY: all FORCE clean install uninstall
+zdep:
+ @if [ "$(feature-zlib)" != "1" ]; then echo "No zlib found"; exit 1 ; fi
+
+.PHONY: all FORCE clean install uninstall zdep
.PHONY: doc doc-clean doc-install doc-uninstall
.DEFAULT_GOAL := all
diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
index 50e402a5a9c8..70493a6da206 100644
--- a/tools/bpf/bpftool/bash-completion/bpftool
+++ b/tools/bpf/bpftool/bash-completion/bpftool
@@ -71,6 +71,12 @@ _bpftool_get_prog_tags()
command sed -n 's/.*"tag": "\(.*\)",$/\1/p' )" -- "$cur" ) )
}
+_bpftool_get_btf_ids()
+{
+ COMPREPLY+=( $( compgen -W "$( bpftool -jp btf 2>&1 | \
+ command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )
+}
+
_bpftool_get_obj_map_names()
{
local obj
@@ -181,7 +187,7 @@ _bpftool()
# Deal with options
if [[ ${words[cword]} == -* ]]; then
- local c='--version --json --pretty --bpffs --mapcompat'
+ local c='--version --json --pretty --bpffs --mapcompat --debug'
COMPREPLY=( $( compgen -W "$c" -- "$cur" ) )
return 0
fi
@@ -195,6 +201,10 @@ _bpftool()
_bpftool_get_prog_tags
return 0
;;
+ dev)
+ _sysfs_get_netdevs
+ return 0
+ ;;
file|pinned)
_filedir
return 0
@@ -336,6 +346,13 @@ _bpftool()
load|loadall)
local obj
+ # Propose "load/loadall" to complete "bpftool prog load",
+ # or bash tries to complete "load" as a filename below.
+ if [[ ${#words[@]} -eq 3 ]]; then
+ COMPREPLY=( $( compgen -W "load loadall" -- "$cur" ) )
+ return 0
+ fi
+
if [[ ${#words[@]} -lt 6 ]]; then
_filedir
return 0
@@ -371,8 +388,10 @@ _bpftool()
lirc_mode2 cgroup/bind4 cgroup/bind6 \
cgroup/connect4 cgroup/connect6 \
cgroup/sendmsg4 cgroup/sendmsg6 \
+ cgroup/recvmsg4 cgroup/recvmsg6 \
cgroup/post_bind4 cgroup/post_bind6 \
- cgroup/sysctl" -- \
+ cgroup/sysctl cgroup/getsockopt \
+ cgroup/setsockopt" -- \
"$cur" ) )
return 0
;;
@@ -384,10 +403,6 @@ _bpftool()
_filedir
return 0
;;
- dev)
- _sysfs_get_netdevs
- return 0
- ;;
*)
COMPREPLY=( $( compgen -W "map" -- "$cur" ) )
_bpftool_once_attr 'type'
@@ -400,17 +415,41 @@ _bpftool()
tracelog)
return 0
;;
+ run)
+ if [[ ${#words[@]} -lt 5 ]]; then
+ _filedir
+ return 0
+ fi
+ case $prev in
+ id)
+ _bpftool_get_prog_ids
+ return 0
+ ;;
+ data_in|data_out|ctx_in|ctx_out)
+ _filedir
+ return 0
+ ;;
+ repeat|data_size_out|ctx_size_out)
+ return 0
+ ;;
+ *)
+ _bpftool_once_attr 'data_in data_out data_size_out \
+ ctx_in ctx_out ctx_size_out repeat'
+ return 0
+ ;;
+ esac
+ ;;
*)
[[ $prev == $object ]] && \
- COMPREPLY=( $( compgen -W 'dump help pin attach detach load \
- show list tracelog' -- "$cur" ) )
+ COMPREPLY=( $( compgen -W 'dump help pin attach detach \
+ load loadall show list tracelog run' -- "$cur" ) )
;;
esac
;;
map)
local MAP_TYPE='id pinned'
case $command in
- show|list|dump|peek|pop|dequeue)
+ show|list|dump|peek|pop|dequeue|freeze)
case $prev in
$command)
COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
@@ -450,8 +489,8 @@ _bpftool()
perf_event_array percpu_hash percpu_array \
stack_trace cgroup_array lru_hash \
lru_percpu_hash lpm_trie array_of_maps \
- hash_of_maps devmap sockmap cpumap xskmap \
- sockhash cgroup_storage reuseport_sockarray \
+ hash_of_maps devmap devmap_hash sockmap cpumap \
+ xskmap sockhash cgroup_storage reuseport_sockarray \
percpu_cgroup_storage queue stack' -- \
"$cur" ) )
return 0
@@ -459,10 +498,6 @@ _bpftool()
key|value|flags|name|entries)
return 0
;;
- dev)
- _sysfs_get_netdevs
- return 0
- ;;
*)
_bpftool_once_attr 'type'
_bpftool_once_attr 'key'
@@ -603,7 +638,7 @@ _bpftool()
[[ $prev == $object ]] && \
COMPREPLY=( $( compgen -W 'delete dump getnext help \
lookup pin event_pipe show list update create \
- peek push enqueue pop dequeue' -- \
+ peek push enqueue pop dequeue freeze' -- \
"$cur" ) )
;;
esac
@@ -635,38 +670,70 @@ _bpftool()
map)
_bpftool_get_map_ids
;;
+ $command)
+ _bpftool_get_btf_ids
+ ;;
esac
return 0
;;
+ format)
+ COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) )
+ ;;
*)
- if [[ $cword == 6 ]] && [[ ${words[3]} == "map" ]]; then
- COMPREPLY+=( $( compgen -W 'key value kv all' -- \
- "$cur" ) )
- fi
+ # emit extra options
+ case ${words[3]} in
+ id|file)
+ _bpftool_once_attr 'format'
+ ;;
+ map|prog)
+ if [[ ${words[3]} == "map" ]] && [[ $cword == 6 ]]; then
+ COMPREPLY+=( $( compgen -W "key value kv all" -- "$cur" ) )
+ fi
+ _bpftool_once_attr 'format'
+ ;;
+ *)
+ ;;
+ esac
return 0
;;
esac
;;
+ show|list)
+ case $prev in
+ $command)
+ COMPREPLY+=( $( compgen -W "id" -- "$cur" ) )
+ ;;
+ id)
+ _bpftool_get_btf_ids
+ ;;
+ esac
+ return 0
+ ;;
*)
[[ $prev == $object ]] && \
- COMPREPLY=( $( compgen -W 'dump help' -- "$cur" ) )
+ COMPREPLY=( $( compgen -W 'dump help show list' \
+ -- "$cur" ) )
;;
esac
;;
cgroup)
case $command in
- show|list)
- _filedir
- return 0
- ;;
- tree)
- _filedir
+ show|list|tree)
+ case $cword in
+ 3)
+ _filedir
+ ;;
+ 4)
+ COMPREPLY=( $( compgen -W 'effective' -- "$cur" ) )
+ ;;
+ esac
return 0
;;
attach|detach)
local ATTACH_TYPES='ingress egress sock_create sock_ops \
device bind4 bind6 post_bind4 post_bind6 connect4 \
- connect6 sendmsg4 sendmsg6 sysctl'
+ connect6 sendmsg4 sendmsg6 recvmsg4 recvmsg6 sysctl \
+ getsockopt setsockopt'
local ATTACH_FLAGS='multi override'
local PROG_TYPE='id pinned tag'
case $prev in
@@ -676,7 +743,8 @@ _bpftool()
;;
ingress|egress|sock_create|sock_ops|device|bind4|bind6|\
post_bind4|post_bind6|connect4|connect6|sendmsg4|\
- sendmsg6|sysctl)
+ sendmsg6|recvmsg4|recvmsg6|sysctl|getsockopt|\
+ setsockopt)
COMPREPLY=( $( compgen -W "$PROG_TYPE" -- \
"$cur" ) )
return 0
@@ -718,18 +786,67 @@ _bpftool()
esac
;;
net)
+ local PROG_TYPE='id pinned tag'
+ local ATTACH_TYPES='xdp xdpgeneric xdpdrv xdpoffload'
case $command in
+ show|list)
+ [[ $prev != "$command" ]] && return 0
+ COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
+ return 0
+ ;;
+ attach)
+ case $cword in
+ 3)
+ COMPREPLY=( $( compgen -W "$ATTACH_TYPES" -- "$cur" ) )
+ return 0
+ ;;
+ 4)
+ COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
+ return 0
+ ;;
+ 5)
+ case $prev in
+ id)
+ _bpftool_get_prog_ids
+ ;;
+ pinned)
+ _filedir
+ ;;
+ esac
+ return 0
+ ;;
+ 6)
+ COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
+ return 0
+ ;;
+ 8)
+ _bpftool_once_attr 'overwrite'
+ return 0
+ ;;
+ esac
+ ;;
+ detach)
+ case $cword in
+ 3)
+ COMPREPLY=( $( compgen -W "$ATTACH_TYPES" -- "$cur" ) )
+ return 0
+ ;;
+ 4)
+ COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
+ return 0
+ ;;
+ esac
+ ;;
*)
[[ $prev == $object ]] && \
COMPREPLY=( $( compgen -W 'help \
- show list' -- "$cur" ) )
+ show list attach detach' -- "$cur" ) )
;;
esac
;;
feature)
case $command in
probe)
- [[ $prev == "dev" ]] && _sysfs_get_netdevs && return 0
[[ $prev == "prefix" ]] && return 0
if _bpftool_search_list 'macros'; then
COMPREPLY+=( $( compgen -W 'prefix' -- "$cur" ) )
diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index 58a2cd002a4b..9a9376d1d3df 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -8,9 +8,10 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
-#include <gelf.h>
#include <bpf.h>
+#include <libbpf.h>
#include <linux/btf.h>
+#include <linux/hashtable.h>
#include "btf.h"
#include "json_writer.h"
@@ -35,6 +36,16 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
[BTF_KIND_DATASEC] = "DATASEC",
};
+struct btf_attach_table {
+ DECLARE_HASHTABLE(table, 16);
+};
+
+struct btf_attach_point {
+ __u32 obj_id;
+ __u32 btf_id;
+ struct hlist_node hash;
+};
+
static const char *btf_int_enc_str(__u8 encoding)
{
switch (encoding) {
@@ -208,8 +219,8 @@ static int dump_btf_type(const struct btf *btf, __u32 id,
break;
}
case BTF_KIND_FWD: {
- const char *fwd_kind = BTF_INFO_KIND(t->info) ? "union"
- : "struct";
+ const char *fwd_kind = BTF_INFO_KFLAG(t->info) ? "union"
+ : "struct";
if (json_output)
jsonw_string_field(w, "fwd_kind", fwd_kind);
@@ -340,109 +351,40 @@ static int dump_btf_raw(const struct btf *btf,
return 0;
}
-static bool check_btf_endianness(GElf_Ehdr *ehdr)
+static void __printf(2, 0) btf_dump_printf(void *ctx,
+ const char *fmt, va_list args)
{
- static unsigned int const endian = 1;
-
- switch (ehdr->e_ident[EI_DATA]) {
- case ELFDATA2LSB:
- return *(unsigned char const *)&endian == 1;
- case ELFDATA2MSB:
- return *(unsigned char const *)&endian == 0;
- default:
- return 0;
- }
+ vfprintf(stdout, fmt, args);
}
-static int btf_load_from_elf(const char *path, struct btf **btf)
+static int dump_btf_c(const struct btf *btf,
+ __u32 *root_type_ids, int root_type_cnt)
{
- int err = -1, fd = -1, idx = 0;
- Elf_Data *btf_data = NULL;
- Elf_Scn *scn = NULL;
- Elf *elf = NULL;
- GElf_Ehdr ehdr;
-
- if (elf_version(EV_CURRENT) == EV_NONE) {
- p_err("failed to init libelf for %s", path);
- return -1;
- }
+ struct btf_dump *d;
+ int err = 0, i;
- fd = open(path, O_RDONLY);
- if (fd < 0) {
- p_err("failed to open %s: %s", path, strerror(errno));
- return -1;
- }
+ d = btf_dump__new(btf, NULL, NULL, btf_dump_printf);
+ if (IS_ERR(d))
+ return PTR_ERR(d);
- elf = elf_begin(fd, ELF_C_READ, NULL);
- if (!elf) {
- p_err("failed to open %s as ELF file", path);
- goto done;
- }
- if (!gelf_getehdr(elf, &ehdr)) {
- p_err("failed to get EHDR from %s", path);
- goto done;
- }
- if (!check_btf_endianness(&ehdr)) {
- p_err("non-native ELF endianness is not supported");
- goto done;
- }
- if (!elf_rawdata(elf_getscn(elf, ehdr.e_shstrndx), NULL)) {
- p_err("failed to get e_shstrndx from %s\n", path);
- goto done;
- }
-
- while ((scn = elf_nextscn(elf, scn)) != NULL) {
- GElf_Shdr sh;
- char *name;
-
- idx++;
- if (gelf_getshdr(scn, &sh) != &sh) {
- p_err("failed to get section(%d) header from %s",
- idx, path);
- goto done;
- }
- name = elf_strptr(elf, ehdr.e_shstrndx, sh.sh_name);
- if (!name) {
- p_err("failed to get section(%d) name from %s",
- idx, path);
- goto done;
- }
- if (strcmp(name, BTF_ELF_SEC) == 0) {
- btf_data = elf_getdata(scn, 0);
- if (!btf_data) {
- p_err("failed to get section(%d, %s) data from %s",
- idx, name, path);
+ if (root_type_cnt) {
+ for (i = 0; i < root_type_cnt; i++) {
+ err = btf_dump__dump_type(d, root_type_ids[i]);
+ if (err)
goto done;
- }
- break;
}
- }
-
- if (!btf_data) {
- p_err("%s ELF section not found in %s", BTF_ELF_SEC, path);
- goto done;
- }
+ } else {
+ int cnt = btf__get_nr_types(btf);
- *btf = btf__new(btf_data->d_buf, btf_data->d_size);
- if (IS_ERR(*btf)) {
- err = PTR_ERR(*btf);
- *btf = NULL;
- p_err("failed to load BTF data from %s: %s",
- path, strerror(err));
- goto done;
+ for (i = 1; i <= cnt; i++) {
+ err = btf_dump__dump_type(d, i);
+ if (err)
+ goto done;
+ }
}
- err = 0;
done:
- if (err) {
- if (*btf) {
- btf__free(*btf);
- *btf = NULL;
- }
- }
- if (elf)
- elf_end(elf);
- close(fd);
+ btf_dump__free(d);
return err;
}
@@ -451,6 +393,7 @@ static int do_dump(int argc, char **argv)
struct btf *btf = NULL;
__u32 root_type_ids[2];
int root_type_cnt = 0;
+ bool dump_c = false;
__u32 btf_id = -1;
const char *src;
int fd = -1;
@@ -517,14 +460,19 @@ static int do_dump(int argc, char **argv)
btf_id = strtoul(*argv, &endptr, 0);
if (*endptr) {
- p_err("can't parse %s as ID", **argv);
+ p_err("can't parse %s as ID", *argv);
return -1;
}
NEXT_ARG();
} else if (is_prefix(src, "file")) {
- err = btf_load_from_elf(*argv, &btf);
- if (err)
+ btf = btf__parse_elf(*argv, NULL);
+ if (IS_ERR(btf)) {
+ err = PTR_ERR(btf);
+ btf = NULL;
+ p_err("failed to load BTF from %s: %s",
+ *argv, strerror(err));
goto done;
+ }
NEXT_ARG();
} else {
err = -1;
@@ -532,6 +480,29 @@ static int do_dump(int argc, char **argv)
goto done;
}
+ while (argc) {
+ if (is_prefix(*argv, "format")) {
+ NEXT_ARG();
+ if (argc < 1) {
+ p_err("expecting value for 'format' option\n");
+ goto done;
+ }
+ if (strcmp(*argv, "c") == 0) {
+ dump_c = true;
+ } else if (strcmp(*argv, "raw") == 0) {
+ dump_c = false;
+ } else {
+ p_err("unrecognized format specifier: '%s', possible values: raw, c",
+ *argv);
+ goto done;
+ }
+ NEXT_ARG();
+ } else {
+ p_err("unrecognized option: '%s'", *argv);
+ goto done;
+ }
+ }
+
if (!btf) {
err = btf__get_from_id(btf_id, &btf);
if (err) {
@@ -545,7 +516,16 @@ static int do_dump(int argc, char **argv)
}
}
- dump_btf_raw(btf, root_type_ids, root_type_cnt);
+ if (dump_c) {
+ if (json_output) {
+ p_err("JSON output for C-syntax dump is not supported");
+ err = -ENOTSUP;
+ goto done;
+ }
+ err = dump_btf_c(btf, root_type_ids, root_type_cnt);
+ } else {
+ err = dump_btf_raw(btf, root_type_ids, root_type_cnt);
+ }
done:
close(fd);
@@ -553,6 +533,330 @@ done:
return err;
}
+static int btf_parse_fd(int *argc, char ***argv)
+{
+ unsigned int id;
+ char *endptr;
+ int fd;
+
+ if (!is_prefix(*argv[0], "id")) {
+ p_err("expected 'id', got: '%s'?", **argv);
+ return -1;
+ }
+ NEXT_ARGP();
+
+ id = strtoul(**argv, &endptr, 0);
+ if (*endptr) {
+ p_err("can't parse %s as ID", **argv);
+ return -1;
+ }
+ NEXT_ARGP();
+
+ fd = bpf_btf_get_fd_by_id(id);
+ if (fd < 0)
+ p_err("can't get BTF object by id (%u): %s",
+ id, strerror(errno));
+
+ return fd;
+}
+
+static void delete_btf_table(struct btf_attach_table *tab)
+{
+ struct btf_attach_point *obj;
+ struct hlist_node *tmp;
+
+ unsigned int bkt;
+
+ hash_for_each_safe(tab->table, bkt, tmp, obj, hash) {
+ hash_del(&obj->hash);
+ free(obj);
+ }
+}
+
+static int
+build_btf_type_table(struct btf_attach_table *tab, enum bpf_obj_type type,
+ void *info, __u32 *len)
+{
+ static const char * const names[] = {
+ [BPF_OBJ_UNKNOWN] = "unknown",
+ [BPF_OBJ_PROG] = "prog",
+ [BPF_OBJ_MAP] = "map",
+ };
+ struct btf_attach_point *obj_node;
+ __u32 btf_id, id = 0;
+ int err;
+ int fd;
+
+ while (true) {
+ switch (type) {
+ case BPF_OBJ_PROG:
+ err = bpf_prog_get_next_id(id, &id);
+ break;
+ case BPF_OBJ_MAP:
+ err = bpf_map_get_next_id(id, &id);
+ break;
+ default:
+ err = -1;
+ p_err("unexpected object type: %d", type);
+ goto err_free;
+ }
+ if (err) {
+ if (errno == ENOENT) {
+ err = 0;
+ break;
+ }
+ p_err("can't get next %s: %s%s", names[type],
+ strerror(errno),
+ errno == EINVAL ? " -- kernel too old?" : "");
+ goto err_free;
+ }
+
+ switch (type) {
+ case BPF_OBJ_PROG:
+ fd = bpf_prog_get_fd_by_id(id);
+ break;
+ case BPF_OBJ_MAP:
+ fd = bpf_map_get_fd_by_id(id);
+ break;
+ default:
+ err = -1;
+ p_err("unexpected object type: %d", type);
+ goto err_free;
+ }
+ if (fd < 0) {
+ if (errno == ENOENT)
+ continue;
+ p_err("can't get %s by id (%u): %s", names[type], id,
+ strerror(errno));
+ err = -1;
+ goto err_free;
+ }
+
+ memset(info, 0, *len);
+ err = bpf_obj_get_info_by_fd(fd, info, len);
+ close(fd);
+ if (err) {
+ p_err("can't get %s info: %s", names[type],
+ strerror(errno));
+ goto err_free;
+ }
+
+ switch (type) {
+ case BPF_OBJ_PROG:
+ btf_id = ((struct bpf_prog_info *)info)->btf_id;
+ break;
+ case BPF_OBJ_MAP:
+ btf_id = ((struct bpf_map_info *)info)->btf_id;
+ break;
+ default:
+ err = -1;
+ p_err("unexpected object type: %d", type);
+ goto err_free;
+ }
+ if (!btf_id)
+ continue;
+
+ obj_node = calloc(1, sizeof(*obj_node));
+ if (!obj_node) {
+ p_err("failed to allocate memory: %s", strerror(errno));
+ goto err_free;
+ }
+
+ obj_node->obj_id = id;
+ obj_node->btf_id = btf_id;
+ hash_add(tab->table, &obj_node->hash, obj_node->btf_id);
+ }
+
+ return 0;
+
+err_free:
+ delete_btf_table(tab);
+ return err;
+}
+
+static int
+build_btf_tables(struct btf_attach_table *btf_prog_table,
+ struct btf_attach_table *btf_map_table)
+{
+ struct bpf_prog_info prog_info;
+ __u32 prog_len = sizeof(prog_info);
+ struct bpf_map_info map_info;
+ __u32 map_len = sizeof(map_info);
+ int err = 0;
+
+ err = build_btf_type_table(btf_prog_table, BPF_OBJ_PROG, &prog_info,
+ &prog_len);
+ if (err)
+ return err;
+
+ err = build_btf_type_table(btf_map_table, BPF_OBJ_MAP, &map_info,
+ &map_len);
+ if (err) {
+ delete_btf_table(btf_prog_table);
+ return err;
+ }
+
+ return 0;
+}
+
+static void
+show_btf_plain(struct bpf_btf_info *info, int fd,
+ struct btf_attach_table *btf_prog_table,
+ struct btf_attach_table *btf_map_table)
+{
+ struct btf_attach_point *obj;
+ int n;
+
+ printf("%u: ", info->id);
+ printf("size %uB", info->btf_size);
+
+ n = 0;
+ hash_for_each_possible(btf_prog_table->table, obj, hash, info->id) {
+ if (obj->btf_id == info->id)
+ printf("%s%u", n++ == 0 ? " prog_ids " : ",",
+ obj->obj_id);
+ }
+
+ n = 0;
+ hash_for_each_possible(btf_map_table->table, obj, hash, info->id) {
+ if (obj->btf_id == info->id)
+ printf("%s%u", n++ == 0 ? " map_ids " : ",",
+ obj->obj_id);
+ }
+
+ printf("\n");
+}
+
+static void
+show_btf_json(struct bpf_btf_info *info, int fd,
+ struct btf_attach_table *btf_prog_table,
+ struct btf_attach_table *btf_map_table)
+{
+ struct btf_attach_point *obj;
+
+ jsonw_start_object(json_wtr); /* btf object */
+ jsonw_uint_field(json_wtr, "id", info->id);
+ jsonw_uint_field(json_wtr, "size", info->btf_size);
+
+ jsonw_name(json_wtr, "prog_ids");
+ jsonw_start_array(json_wtr); /* prog_ids */
+ hash_for_each_possible(btf_prog_table->table, obj, hash,
+ info->id) {
+ if (obj->btf_id == info->id)
+ jsonw_uint(json_wtr, obj->obj_id);
+ }
+ jsonw_end_array(json_wtr); /* prog_ids */
+
+ jsonw_name(json_wtr, "map_ids");
+ jsonw_start_array(json_wtr); /* map_ids */
+ hash_for_each_possible(btf_map_table->table, obj, hash,
+ info->id) {
+ if (obj->btf_id == info->id)
+ jsonw_uint(json_wtr, obj->obj_id);
+ }
+ jsonw_end_array(json_wtr); /* map_ids */
+ jsonw_end_object(json_wtr); /* btf object */
+}
+
+static int
+show_btf(int fd, struct btf_attach_table *btf_prog_table,
+ struct btf_attach_table *btf_map_table)
+{
+ struct bpf_btf_info info = {};
+ __u32 len = sizeof(info);
+ int err;
+
+ err = bpf_obj_get_info_by_fd(fd, &info, &len);
+ if (err) {
+ p_err("can't get BTF object info: %s", strerror(errno));
+ return -1;
+ }
+
+ if (json_output)
+ show_btf_json(&info, fd, btf_prog_table, btf_map_table);
+ else
+ show_btf_plain(&info, fd, btf_prog_table, btf_map_table);
+
+ return 0;
+}
+
+static int do_show(int argc, char **argv)
+{
+ struct btf_attach_table btf_prog_table;
+ struct btf_attach_table btf_map_table;
+ int err, fd = -1;
+ __u32 id = 0;
+
+ if (argc == 2) {
+ fd = btf_parse_fd(&argc, &argv);
+ if (fd < 0)
+ return -1;
+ }
+
+ if (argc) {
+ if (fd >= 0)
+ close(fd);
+ return BAD_ARG();
+ }
+
+ hash_init(btf_prog_table.table);
+ hash_init(btf_map_table.table);
+ err = build_btf_tables(&btf_prog_table, &btf_map_table);
+ if (err) {
+ if (fd >= 0)
+ close(fd);
+ return err;
+ }
+
+ if (fd >= 0) {
+ err = show_btf(fd, &btf_prog_table, &btf_map_table);
+ close(fd);
+ goto exit_free;
+ }
+
+ if (json_output)
+ jsonw_start_array(json_wtr); /* root array */
+
+ while (true) {
+ err = bpf_btf_get_next_id(id, &id);
+ if (err) {
+ if (errno == ENOENT) {
+ err = 0;
+ break;
+ }
+ p_err("can't get next BTF object: %s%s",
+ strerror(errno),
+ errno == EINVAL ? " -- kernel too old?" : "");
+ err = -1;
+ break;
+ }
+
+ fd = bpf_btf_get_fd_by_id(id);
+ if (fd < 0) {
+ if (errno == ENOENT)
+ continue;
+ p_err("can't get BTF object by id (%u): %s",
+ id, strerror(errno));
+ err = -1;
+ break;
+ }
+
+ err = show_btf(fd, &btf_prog_table, &btf_map_table);
+ close(fd);
+ if (err)
+ break;
+ }
+
+ if (json_output)
+ jsonw_end_array(json_wtr); /* root array */
+
+exit_free:
+ delete_btf_table(&btf_prog_table);
+ delete_btf_table(&btf_map_table);
+
+ return err;
+}
+
static int do_help(int argc, char **argv)
{
if (json_output) {
@@ -561,20 +865,24 @@ static int do_help(int argc, char **argv)
}
fprintf(stderr,
- "Usage: %s btf dump BTF_SRC\n"
+ "Usage: %s btf { show | list } [id BTF_ID]\n"
+ " %s btf dump BTF_SRC [format FORMAT]\n"
" %s btf help\n"
"\n"
" BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"
+ " FORMAT := { raw | c }\n"
" " HELP_SPEC_MAP "\n"
" " HELP_SPEC_PROGRAM "\n"
" " HELP_SPEC_OPTIONS "\n"
"",
- bin_name, bin_name);
+ bin_name, bin_name, bin_name);
return 0;
}
static const struct cmd cmds[] = {
+ { "show", do_show },
+ { "list", do_show },
{ "help", do_help },
{ "dump", do_dump },
{ 0 }
diff --git a/tools/bpf/bpftool/btf_dumper.c b/tools/bpf/bpftool/btf_dumper.c
index 8cafb9b31467..d66131f69689 100644
--- a/tools/bpf/bpftool/btf_dumper.c
+++ b/tools/bpf/bpftool/btf_dumper.c
@@ -26,9 +26,9 @@ static void btf_dumper_ptr(const void *data, json_writer_t *jw,
bool is_plain_text)
{
if (is_plain_text)
- jsonw_printf(jw, "%p", *(unsigned long *)data);
+ jsonw_printf(jw, "%p", data);
else
- jsonw_printf(jw, "%u", *(unsigned long *)data);
+ jsonw_printf(jw, "%lu", *(unsigned long *)data);
}
static int btf_dumper_modifier(const struct btf_dumper *d, __u32 type_id,
@@ -216,7 +216,7 @@ static int btf_dumper_int(const struct btf_type *t, __u8 bit_offset,
switch (BTF_INT_ENCODING(*int_type)) {
case 0:
if (BTF_INT_BITS(*int_type) == 64)
- jsonw_printf(jw, "%lu", *(__u64 *)data);
+ jsonw_printf(jw, "%llu", *(__u64 *)data);
else if (BTF_INT_BITS(*int_type) == 32)
jsonw_printf(jw, "%u", *(__u32 *)data);
else if (BTF_INT_BITS(*int_type) == 16)
@@ -229,7 +229,7 @@ static int btf_dumper_int(const struct btf_type *t, __u8 bit_offset,
break;
case BTF_INT_SIGNED:
if (BTF_INT_BITS(*int_type) == 64)
- jsonw_printf(jw, "%ld", *(long long *)data);
+ jsonw_printf(jw, "%lld", *(long long *)data);
else if (BTF_INT_BITS(*int_type) == 32)
jsonw_printf(jw, "%d", *(int *)data);
else if (BTF_INT_BITS(*int_type) == 16)
diff --git a/tools/bpf/bpftool/cgroup.c b/tools/bpf/bpftool/cgroup.c
index 7e22f115c8c1..1ef45e55039e 100644
--- a/tools/bpf/bpftool/cgroup.c
+++ b/tools/bpf/bpftool/cgroup.c
@@ -25,7 +25,11 @@
" ATTACH_TYPE := { ingress | egress | sock_create |\n" \
" sock_ops | device | bind4 | bind6 |\n" \
" post_bind4 | post_bind6 | connect4 |\n" \
- " connect6 | sendmsg4 | sendmsg6 | sysctl }"
+ " connect6 | sendmsg4 | sendmsg6 |\n" \
+ " recvmsg4 | recvmsg6 | sysctl |\n" \
+ " getsockopt | setsockopt }"
+
+static unsigned int query_flags;
static const char * const attach_type_strings[] = {
[BPF_CGROUP_INET_INGRESS] = "ingress",
@@ -42,6 +46,10 @@ static const char * const attach_type_strings[] = {
[BPF_CGROUP_UDP4_SENDMSG] = "sendmsg4",
[BPF_CGROUP_UDP6_SENDMSG] = "sendmsg6",
[BPF_CGROUP_SYSCTL] = "sysctl",
+ [BPF_CGROUP_UDP4_RECVMSG] = "recvmsg4",
+ [BPF_CGROUP_UDP6_RECVMSG] = "recvmsg6",
+ [BPF_CGROUP_GETSOCKOPT] = "getsockopt",
+ [BPF_CGROUP_SETSOCKOPT] = "setsockopt",
[__MAX_BPF_ATTACH_TYPE] = NULL,
};
@@ -101,7 +109,8 @@ static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
__u32 prog_cnt = 0;
int ret;
- ret = bpf_prog_query(cgroup_fd, type, 0, NULL, NULL, &prog_cnt);
+ ret = bpf_prog_query(cgroup_fd, type, query_flags, NULL,
+ NULL, &prog_cnt);
if (ret)
return -1;
@@ -111,16 +120,16 @@ static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
int level)
{
+ const char *attach_flags_str;
__u32 prog_ids[1024] = {0};
- char *attach_flags_str;
__u32 prog_cnt, iter;
__u32 attach_flags;
char buf[32];
int ret;
prog_cnt = ARRAY_SIZE(prog_ids);
- ret = bpf_prog_query(cgroup_fd, type, 0, &attach_flags, prog_ids,
- &prog_cnt);
+ ret = bpf_prog_query(cgroup_fd, type, query_flags, &attach_flags,
+ prog_ids, &prog_cnt);
if (ret)
return ret;
@@ -152,20 +161,34 @@ static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
static int do_show(int argc, char **argv)
{
enum bpf_attach_type type;
+ const char *path;
int cgroup_fd;
int ret = -1;
- if (argc < 1) {
- p_err("too few parameters for cgroup show");
- goto exit;
- } else if (argc > 1) {
- p_err("too many parameters for cgroup show");
- goto exit;
+ query_flags = 0;
+
+ if (!REQ_ARGS(1))
+ return -1;
+ path = GET_ARG();
+
+ while (argc) {
+ if (is_prefix(*argv, "effective")) {
+ if (query_flags & BPF_F_QUERY_EFFECTIVE) {
+ p_err("duplicated argument: %s", *argv);
+ return -1;
+ }
+ query_flags |= BPF_F_QUERY_EFFECTIVE;
+ NEXT_ARG();
+ } else {
+ p_err("expected no more arguments, 'effective', got: '%s'?",
+ *argv);
+ return -1;
+ }
}
- cgroup_fd = open(argv[0], O_RDONLY);
+ cgroup_fd = open(path, O_RDONLY);
if (cgroup_fd < 0) {
- p_err("can't open cgroup %s", argv[1]);
+ p_err("can't open cgroup %s", path);
goto exit;
}
@@ -288,26 +311,37 @@ static char *find_cgroup_root(void)
static int do_show_tree(int argc, char **argv)
{
- char *cgroup_root;
+ char *cgroup_root, *cgroup_alloced = NULL;
int ret;
- switch (argc) {
- case 0:
- cgroup_root = find_cgroup_root();
- if (!cgroup_root) {
+ query_flags = 0;
+
+ if (!argc) {
+ cgroup_alloced = find_cgroup_root();
+ if (!cgroup_alloced) {
p_err("cgroup v2 isn't mounted");
return -1;
}
- break;
- case 1:
- cgroup_root = argv[0];
- break;
- default:
- p_err("too many parameters for cgroup tree");
- return -1;
+ cgroup_root = cgroup_alloced;
+ } else {
+ cgroup_root = GET_ARG();
+
+ while (argc) {
+ if (is_prefix(*argv, "effective")) {
+ if (query_flags & BPF_F_QUERY_EFFECTIVE) {
+ p_err("duplicated argument: %s", *argv);
+ return -1;
+ }
+ query_flags |= BPF_F_QUERY_EFFECTIVE;
+ NEXT_ARG();
+ } else {
+ p_err("expected no more arguments, 'effective', got: '%s'?",
+ *argv);
+ return -1;
+ }
+ }
}
-
if (json_output)
jsonw_start_array(json_wtr);
else
@@ -332,8 +366,7 @@ static int do_show_tree(int argc, char **argv)
if (json_output)
jsonw_end_array(json_wtr);
- if (argc == 0)
- free(cgroup_root);
+ free(cgroup_alloced);
return ret;
}
@@ -353,7 +386,7 @@ static int do_attach(int argc, char **argv)
cgroup_fd = open(argv[0], O_RDONLY);
if (cgroup_fd < 0) {
- p_err("can't open cgroup %s", argv[1]);
+ p_err("can't open cgroup %s", argv[0]);
goto exit;
}
@@ -411,7 +444,7 @@ static int do_detach(int argc, char **argv)
cgroup_fd = open(argv[0], O_RDONLY);
if (cgroup_fd < 0) {
- p_err("can't open cgroup %s", argv[1]);
+ p_err("can't open cgroup %s", argv[0]);
goto exit;
}
@@ -453,8 +486,8 @@ static int do_help(int argc, char **argv)
}
fprintf(stderr,
- "Usage: %s %s { show | list } CGROUP\n"
- " %s %s tree [CGROUP_ROOT]\n"
+ "Usage: %s %s { show | list } CGROUP [**effective**]\n"
+ " %s %s tree [CGROUP_ROOT] [**effective**]\n"
" %s %s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
" %s %s detach CGROUP ATTACH_TYPE PROG\n"
" %s %s help\n"
diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c
index f7261fad45c1..88264abaa738 100644
--- a/tools/bpf/bpftool/common.c
+++ b/tools/bpf/bpftool/common.c
@@ -21,6 +21,7 @@
#include <sys/vfs.h>
#include <bpf.h>
+#include <libbpf.h> /* libbpf_num_possible_cpus */
#include "main.h"
@@ -28,7 +29,7 @@
#define BPF_FS_MAGIC 0xcafe4a11
#endif
-void __printf(1, 2) p_err(const char *fmt, ...)
+void p_err(const char *fmt, ...)
{
va_list ap;
@@ -46,7 +47,7 @@ void __printf(1, 2) p_err(const char *fmt, ...)
va_end(ap);
}
-void __printf(1, 2) p_info(const char *fmt, ...)
+void p_info(const char *fmt, ...)
{
va_list ap;
@@ -203,7 +204,11 @@ int do_pin_fd(int fd, const char *name)
if (err)
return err;
- return bpf_obj_pin(fd, name);
+ err = bpf_obj_pin(fd, name);
+ if (err)
+ p_err("can't pin the object (%s): %s", name, strerror(errno));
+
+ return err;
}
int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
@@ -236,7 +241,7 @@ int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
fd = get_fd_by_id(id);
if (fd < 0) {
- p_err("can't get prog by id (%u): %s", id, strerror(errno));
+ p_err("can't open object by id (%u): %s", id, strerror(errno));
return -1;
}
@@ -439,57 +444,13 @@ unsigned int get_page_size(void)
unsigned int get_possible_cpus(void)
{
- static unsigned int result;
- char buf[128];
- long int n;
- char *ptr;
- int fd;
-
- if (result)
- return result;
-
- fd = open("/sys/devices/system/cpu/possible", O_RDONLY);
- if (fd < 0) {
- p_err("can't open sysfs possible cpus");
- exit(-1);
- }
-
- n = read(fd, buf, sizeof(buf));
- if (n < 2) {
- p_err("can't read sysfs possible cpus");
- exit(-1);
- }
- close(fd);
+ int cpus = libbpf_num_possible_cpus();
- if (n == sizeof(buf)) {
- p_err("read sysfs possible cpus overflow");
+ if (cpus < 0) {
+ p_err("Can't get # of possible cpus: %s", strerror(-cpus));
exit(-1);
}
-
- ptr = buf;
- n = 0;
- while (*ptr && *ptr != '\n') {
- unsigned int a, b;
-
- if (sscanf(ptr, "%u-%u", &a, &b) == 2) {
- n += b - a + 1;
-
- ptr = strchr(ptr, '-') + 1;
- } else if (sscanf(ptr, "%u", &a) == 1) {
- n++;
- } else {
- assert(0);
- }
-
- while (isdigit(*ptr))
- ptr++;
- if (*ptr == ',')
- ptr++;
- }
-
- result = n;
-
- return result;
+ return cpus;
}
static char *
diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
index d672d9086fff..03bdc5b3ac49 100644
--- a/tools/bpf/bpftool/feature.c
+++ b/tools/bpf/bpftool/feature.c
@@ -14,6 +14,7 @@
#include <bpf.h>
#include <libbpf.h>
+#include <zlib.h>
#include "main.h"
@@ -284,34 +285,32 @@ static void probe_jit_limit(void)
}
}
-static char *get_kernel_config_option(FILE *fd, const char *option)
+static bool read_next_kernel_config_option(gzFile file, char *buf, size_t n,
+ char **value)
{
- size_t line_n = 0, optlen = strlen(option);
- char *res, *strval, *line = NULL;
- ssize_t n;
+ char *sep;
- rewind(fd);
- while ((n = getline(&line, &line_n, fd)) > 0) {
- if (strncmp(line, option, optlen))
+ while (gzgets(file, buf, n)) {
+ if (strncmp(buf, "CONFIG_", 7))
continue;
- /* Check we have at least '=', value, and '\n' */
- if (strlen(line) < optlen + 3)
- continue;
- if (*(line + optlen) != '=')
+
+ sep = strchr(buf, '=');
+ if (!sep)
continue;
/* Trim ending '\n' */
- line[strlen(line) - 1] = '\0';
+ buf[strlen(buf) - 1] = '\0';
+
+ /* Split on '=' and ensure that a value is present. */
+ *sep = '\0';
+ if (!sep[1])
+ continue;
- /* Copy and return config option value */
- strval = line + optlen + 1;
- res = strdup(strval);
- free(line);
- return res;
+ *value = sep + 1;
+ return true;
}
- free(line);
- return NULL;
+ return false;
}
static void probe_kernel_image_config(void)
@@ -386,59 +385,61 @@ static void probe_kernel_image_config(void)
/* test_bpf module for BPF tests */
"CONFIG_TEST_BPF",
};
- char *value, *buf = NULL;
+ char *values[ARRAY_SIZE(options)] = { };
struct utsname utsn;
char path[PATH_MAX];
- size_t i, n;
- ssize_t ret;
- FILE *fd;
+ gzFile file = NULL;
+ char buf[4096];
+ char *value;
+ size_t i;
- if (uname(&utsn))
- goto no_config;
+ if (!uname(&utsn)) {
+ snprintf(path, sizeof(path), "/boot/config-%s", utsn.release);
- snprintf(path, sizeof(path), "/boot/config-%s", utsn.release);
+ /* gzopen also accepts uncompressed files. */
+ file = gzopen(path, "r");
+ }
- fd = fopen(path, "r");
- if (!fd && errno == ENOENT) {
- /* Some distributions put the config file at /proc/config, give
- * it a try.
- * Sometimes it is also at /proc/config.gz but we do not try
- * this one for now, it would require linking against libz.
+ if (!file) {
+ /* Some distributions build with CONFIG_IKCONFIG=y and put the
+ * config file at /proc/config.gz.
*/
- fd = fopen("/proc/config", "r");
+ file = gzopen("/proc/config.gz", "r");
}
- if (!fd) {
+ if (!file) {
p_info("skipping kernel config, can't open file: %s",
strerror(errno));
- goto no_config;
+ goto end_parse;
}
/* Sanity checks */
- ret = getline(&buf, &n, fd);
- ret = getline(&buf, &n, fd);
- if (!buf || !ret) {
+ if (!gzgets(file, buf, sizeof(buf)) ||
+ !gzgets(file, buf, sizeof(buf))) {
p_info("skipping kernel config, can't read from file: %s",
strerror(errno));
- free(buf);
- goto no_config;
+ goto end_parse;
}
if (strcmp(buf, "# Automatically generated file; DO NOT EDIT.\n")) {
p_info("skipping kernel config, can't find correct file");
- free(buf);
- goto no_config;
+ goto end_parse;
}
- free(buf);
- for (i = 0; i < ARRAY_SIZE(options); i++) {
- value = get_kernel_config_option(fd, options[i]);
- print_kernel_option(options[i], value);
- free(value);
+ while (read_next_kernel_config_option(file, buf, sizeof(buf), &value)) {
+ for (i = 0; i < ARRAY_SIZE(options); i++) {
+ if (values[i] || strcmp(buf, options[i]))
+ continue;
+
+ values[i] = strdup(value);
+ }
}
- fclose(fd);
- return;
-no_config:
- for (i = 0; i < ARRAY_SIZE(options); i++)
- print_kernel_option(options[i], NULL);
+end_parse:
+ if (file)
+ gzclose(file);
+
+ for (i = 0; i < ARRAY_SIZE(options); i++) {
+ print_kernel_option(options[i], values[i]);
+ free(values[i]);
+ }
}
static bool probe_bpf_syscall(const char *define_prefix)
diff --git a/tools/bpf/bpftool/jit_disasm.c b/tools/bpf/bpftool/jit_disasm.c
index 3ef3093560ba..bfed711258ce 100644
--- a/tools/bpf/bpftool/jit_disasm.c
+++ b/tools/bpf/bpftool/jit_disasm.c
@@ -11,6 +11,8 @@
* Licensed under the GNU General Public License, version 2.0 (GPLv2)
*/
+#define _GNU_SOURCE
+#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
@@ -44,11 +46,13 @@ static int fprintf_json(void *out, const char *fmt, ...)
char *s;
va_start(ap, fmt);
+ if (vasprintf(&s, fmt, ap) < 0)
+ return -1;
+ va_end(ap);
+
if (!oper_count) {
int i;
- s = va_arg(ap, char *);
-
/* Strip trailing spaces */
i = strlen(s) - 1;
while (s[i] == ' ')
@@ -61,11 +65,10 @@ static int fprintf_json(void *out, const char *fmt, ...)
} else if (!strcmp(fmt, ",")) {
/* Skip */
} else {
- s = va_arg(ap, char *);
jsonw_string(json_wtr, s);
oper_count++;
}
- va_end(ap);
+ free(s);
return 0;
}
diff --git a/tools/bpf/bpftool/json_writer.c b/tools/bpf/bpftool/json_writer.c
index 6046dcab51cc..86501cd3c763 100644
--- a/tools/bpf/bpftool/json_writer.c
+++ b/tools/bpf/bpftool/json_writer.c
@@ -15,7 +15,6 @@
#include <malloc.h>
#include <inttypes.h>
#include <stdint.h>
-#include <linux/compiler.h>
#include "json_writer.h"
@@ -153,8 +152,7 @@ void jsonw_name(json_writer_t *self, const char *name)
putc(' ', self->out);
}
-void __printf(2, 0)
-jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap)
+void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap)
{
jsonw_eor(self);
putc('"', self->out);
@@ -162,7 +160,7 @@ jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap)
putc('"', self->out);
}
-void __printf(2, 3) jsonw_printf(json_writer_t *self, const char *fmt, ...)
+void jsonw_printf(json_writer_t *self, const char *fmt, ...)
{
va_list ap;
diff --git a/tools/bpf/bpftool/json_writer.h b/tools/bpf/bpftool/json_writer.h
index cb9a1993681c..35cf1f00f96c 100644
--- a/tools/bpf/bpftool/json_writer.h
+++ b/tools/bpf/bpftool/json_writer.h
@@ -14,6 +14,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdarg.h>
+#include <linux/compiler.h>
/* Opaque class structure */
typedef struct json_writer json_writer_t;
@@ -30,8 +31,9 @@ void jsonw_pretty(json_writer_t *self, bool on);
void jsonw_name(json_writer_t *self, const char *name);
/* Add value */
-void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap);
-void jsonw_printf(json_writer_t *self, const char *fmt, ...);
+void __printf(2, 0) jsonw_vprintf_enquote(json_writer_t *self, const char *fmt,
+ va_list ap);
+void __printf(2, 3) jsonw_printf(json_writer_t *self, const char *fmt, ...);
void jsonw_string(json_writer_t *self, const char *value);
void jsonw_bool(json_writer_t *self, bool value);
void jsonw_float(json_writer_t *self, double number);
diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c
index 1ac1fc520e6a..93d008687020 100644
--- a/tools/bpf/bpftool/main.c
+++ b/tools/bpf/bpftool/main.c
@@ -10,6 +10,7 @@
#include <string.h>
#include <bpf.h>
+#include <libbpf.h>
#include "main.h"
@@ -25,6 +26,7 @@ bool pretty_output;
bool json_output;
bool show_pinned;
bool block_mount;
+bool verifier_logs;
int bpf_flags;
struct pinned_obj_table prog_table;
struct pinned_obj_table map_table;
@@ -77,6 +79,13 @@ static int do_version(int argc, char **argv)
return 0;
}
+static int __printf(2, 0)
+print_all_levels(__maybe_unused enum libbpf_print_level level,
+ const char *format, va_list args)
+{
+ return vfprintf(stderr, format, args);
+}
+
int cmd_select(const struct cmd *cmds, int argc, char **argv,
int (*help)(int argc, char **argv))
{
@@ -108,6 +117,35 @@ bool is_prefix(const char *pfx, const char *str)
return !memcmp(str, pfx, strlen(pfx));
}
+/* Last argument MUST be NULL pointer */
+int detect_common_prefix(const char *arg, ...)
+{
+ unsigned int count = 0;
+ const char *ref;
+ char msg[256];
+ va_list ap;
+
+ snprintf(msg, sizeof(msg), "ambiguous prefix: '%s' could be '", arg);
+ va_start(ap, arg);
+ while ((ref = va_arg(ap, const char *))) {
+ if (!is_prefix(arg, ref))
+ continue;
+ count++;
+ if (count > 1)
+ strncat(msg, "' or '", sizeof(msg) - strlen(msg) - 1);
+ strncat(msg, ref, sizeof(msg) - strlen(msg) - 1);
+ }
+ va_end(ap);
+ strncat(msg, "'", sizeof(msg) - strlen(msg) - 1);
+
+ if (count >= 2) {
+ p_err("%s", msg);
+ return -1;
+ }
+
+ return 0;
+}
+
void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep)
{
unsigned char *data = arg;
@@ -317,6 +355,7 @@ int main(int argc, char **argv)
{ "bpffs", no_argument, NULL, 'f' },
{ "mapcompat", no_argument, NULL, 'm' },
{ "nomount", no_argument, NULL, 'n' },
+ { "debug", no_argument, NULL, 'd' },
{ 0 }
};
int opt, ret;
@@ -332,7 +371,7 @@ int main(int argc, char **argv)
hash_init(map_table.table);
opterr = 0;
- while ((opt = getopt_long(argc, argv, "Vhpjfmn",
+ while ((opt = getopt_long(argc, argv, "Vhpjfmnd",
options, NULL)) >= 0) {
switch (opt) {
case 'V':
@@ -362,6 +401,10 @@ int main(int argc, char **argv)
case 'n':
block_mount = true;
break;
+ case 'd':
+ libbpf_set_print(print_all_levels);
+ verifier_logs = true;
+ break;
default:
p_err("unrecognized option '%s'", argv[optind - 1]);
if (json_output)
diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
index 3d63feb7f852..af9ad56c303a 100644
--- a/tools/bpf/bpftool/main.h
+++ b/tools/bpf/bpftool/main.h
@@ -74,6 +74,8 @@ static const char * const prog_type_name[] = {
[BPF_PROG_TYPE_SK_REUSEPORT] = "sk_reuseport",
[BPF_PROG_TYPE_FLOW_DISSECTOR] = "flow_dissector",
[BPF_PROG_TYPE_CGROUP_SYSCTL] = "cgroup_sysctl",
+ [BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE] = "raw_tracepoint_writable",
+ [BPF_PROG_TYPE_CGROUP_SOCKOPT] = "cgroup_sockopt",
};
extern const char * const map_type_name[];
@@ -91,14 +93,16 @@ extern json_writer_t *json_wtr;
extern bool json_output;
extern bool show_pinned;
extern bool block_mount;
+extern bool verifier_logs;
extern int bpf_flags;
extern struct pinned_obj_table prog_table;
extern struct pinned_obj_table map_table;
-void p_err(const char *fmt, ...);
-void p_info(const char *fmt, ...);
+void __printf(1, 2) p_err(const char *fmt, ...);
+void __printf(1, 2) p_info(const char *fmt, ...);
bool is_prefix(const char *pfx, const char *str);
+int detect_common_prefix(const char *arg, ...);
void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep);
void usage(void) __noreturn;
diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
index 3ec82904ccec..de61d73b9030 100644
--- a/tools/bpf/bpftool/map.c
+++ b/tools/bpf/bpftool/map.c
@@ -37,6 +37,7 @@ const char * const map_type_name[] = {
[BPF_MAP_TYPE_ARRAY_OF_MAPS] = "array_of_maps",
[BPF_MAP_TYPE_HASH_OF_MAPS] = "hash_of_maps",
[BPF_MAP_TYPE_DEVMAP] = "devmap",
+ [BPF_MAP_TYPE_DEVMAP_HASH] = "devmap_hash",
[BPF_MAP_TYPE_SOCKMAP] = "sockmap",
[BPF_MAP_TYPE_CPUMAP] = "cpumap",
[BPF_MAP_TYPE_XSKMAP] = "xskmap",
@@ -480,9 +481,11 @@ static int parse_elem(char **argv, struct bpf_map_info *info,
static int show_map_close_json(int fd, struct bpf_map_info *info)
{
- char *memlock;
+ char *memlock, *frozen_str;
+ int frozen = 0;
memlock = get_fdinfo(fd, "memlock");
+ frozen_str = get_fdinfo(fd, "frozen");
jsonw_start_object(json_wtr);
@@ -532,6 +535,12 @@ static int show_map_close_json(int fd, struct bpf_map_info *info)
}
close(fd);
+ if (frozen_str) {
+ frozen = atoi(frozen_str);
+ free(frozen_str);
+ }
+ jsonw_int_field(json_wtr, "frozen", frozen);
+
if (info->btf_id)
jsonw_int_field(json_wtr, "btf_id", info->btf_id);
@@ -554,9 +563,11 @@ static int show_map_close_json(int fd, struct bpf_map_info *info)
static int show_map_close_plain(int fd, struct bpf_map_info *info)
{
- char *memlock;
+ char *memlock, *frozen_str;
+ int frozen = 0;
memlock = get_fdinfo(fd, "memlock");
+ frozen_str = get_fdinfo(fd, "frozen");
printf("%u: ", info->id);
if (info->type < ARRAY_SIZE(map_type_name))
@@ -609,9 +620,23 @@ static int show_map_close_plain(int fd, struct bpf_map_info *info)
printf("\n\tpinned %s", obj->path);
}
}
+ printf("\n");
+
+ if (frozen_str) {
+ frozen = atoi(frozen_str);
+ free(frozen_str);
+ }
+
+ if (!info->btf_id && !frozen)
+ return 0;
+
+ printf("\t");
if (info->btf_id)
- printf("\n\tbtf_id %d", info->btf_id);
+ printf("btf_id %d", info->btf_id);
+
+ if (frozen)
+ printf("%sfrozen", info->btf_id ? " " : "");
printf("\n");
return 0;
@@ -716,12 +741,14 @@ static int dump_map_elem(int fd, void *key, void *value,
return 0;
if (json_output) {
+ jsonw_start_object(json_wtr);
jsonw_name(json_wtr, "key");
print_hex_data_json(key, map_info->key_size);
jsonw_name(json_wtr, "value");
jsonw_start_object(json_wtr);
jsonw_string_field(json_wtr, "error", strerror(lookup_errno));
jsonw_end_object(json_wtr);
+ jsonw_end_object(json_wtr);
} else {
const char *msg = NULL;
@@ -1235,6 +1262,35 @@ exit_free:
return err;
}
+static int do_freeze(int argc, char **argv)
+{
+ int err, fd;
+
+ if (!REQ_ARGS(2))
+ return -1;
+
+ fd = map_parse_fd(&argc, &argv);
+ if (fd < 0)
+ return -1;
+
+ if (argc) {
+ close(fd);
+ return BAD_ARG();
+ }
+
+ err = bpf_map_freeze(fd);
+ close(fd);
+ if (err) {
+ p_err("failed to freeze map: %s", strerror(errno));
+ return err;
+ }
+
+ if (json_output)
+ jsonw_null(json_wtr);
+
+ return 0;
+}
+
static int do_help(int argc, char **argv)
{
if (json_output) {
@@ -1259,6 +1315,7 @@ static int do_help(int argc, char **argv)
" %s %s pop MAP\n"
" %s %s enqueue MAP value VALUE\n"
" %s %s dequeue MAP\n"
+ " %s %s freeze MAP\n"
" %s %s help\n"
"\n"
" " HELP_SPEC_MAP "\n"
@@ -1269,7 +1326,7 @@ static int do_help(int argc, char **argv)
" TYPE := { hash | array | prog_array | perf_event_array | percpu_hash |\n"
" percpu_array | stack_trace | cgroup_array | lru_hash |\n"
" lru_percpu_hash | lpm_trie | array_of_maps | hash_of_maps |\n"
- " devmap | sockmap | cpumap | xskmap | sockhash |\n"
+ " devmap | devmap_hash | sockmap | cpumap | xskmap | sockhash |\n"
" cgroup_storage | reuseport_sockarray | percpu_cgroup_storage }\n"
" " HELP_SPEC_OPTIONS "\n"
"",
@@ -1277,7 +1334,8 @@ static int do_help(int argc, char **argv)
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
- bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]);
+ bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
+ bin_name, argv[-2]);
return 0;
}
@@ -1299,6 +1357,7 @@ static const struct cmd cmds[] = {
{ "enqueue", do_update },
{ "pop", do_pop_dequeue },
{ "dequeue", do_pop_dequeue },
+ { "freeze", do_freeze },
{ 0 }
};
diff --git a/tools/bpf/bpftool/map_perf_ring.c b/tools/bpf/bpftool/map_perf_ring.c
index 0507dfaf7a8f..4c5531d1a450 100644
--- a/tools/bpf/bpftool/map_perf_ring.c
+++ b/tools/bpf/bpftool/map_perf_ring.c
@@ -28,7 +28,7 @@
#define MMAP_PAGE_CNT 16
-static bool stop;
+static volatile bool stop;
struct event_ring_info {
int fd;
@@ -44,32 +44,44 @@ struct perf_event_sample {
unsigned char data[];
};
+struct perf_event_lost {
+ struct perf_event_header header;
+ __u64 id;
+ __u64 lost;
+};
+
static void int_exit(int signo)
{
fprintf(stderr, "Stopping...\n");
stop = true;
}
+struct event_pipe_ctx {
+ bool all_cpus;
+ int cpu;
+ int idx;
+};
+
static enum bpf_perf_event_ret
-print_bpf_output(struct perf_event_header *event, void *private_data)
+print_bpf_output(void *private_data, int cpu, struct perf_event_header *event)
{
- struct perf_event_sample *e = container_of(event, struct perf_event_sample,
+ struct perf_event_sample *e = container_of(event,
+ struct perf_event_sample,
header);
- struct event_ring_info *ring = private_data;
- struct {
- struct perf_event_header header;
- __u64 id;
- __u64 lost;
- } *lost = (typeof(lost))event;
+ struct perf_event_lost *lost = container_of(event,
+ struct perf_event_lost,
+ header);
+ struct event_pipe_ctx *ctx = private_data;
+ int idx = ctx->all_cpus ? cpu : ctx->idx;
if (json_output) {
jsonw_start_object(json_wtr);
jsonw_name(json_wtr, "type");
jsonw_uint(json_wtr, e->header.type);
jsonw_name(json_wtr, "cpu");
- jsonw_uint(json_wtr, ring->cpu);
+ jsonw_uint(json_wtr, cpu);
jsonw_name(json_wtr, "index");
- jsonw_uint(json_wtr, ring->key);
+ jsonw_uint(json_wtr, idx);
if (e->header.type == PERF_RECORD_SAMPLE) {
jsonw_name(json_wtr, "timestamp");
jsonw_uint(json_wtr, e->time);
@@ -89,7 +101,7 @@ print_bpf_output(struct perf_event_header *event, void *private_data)
if (e->header.type == PERF_RECORD_SAMPLE) {
printf("== @%lld.%09lld CPU: %d index: %d =====\n",
e->time / 1000000000ULL, e->time % 1000000000ULL,
- ring->cpu, ring->key);
+ cpu, idx);
fprint_hex(stdout, e->data, e->size, " ");
printf("\n");
} else if (e->header.type == PERF_RECORD_LOST) {
@@ -103,87 +115,25 @@ print_bpf_output(struct perf_event_header *event, void *private_data)
return LIBBPF_PERF_EVENT_CONT;
}
-static void
-perf_event_read(struct event_ring_info *ring, void **buf, size_t *buf_len)
-{
- enum bpf_perf_event_ret ret;
-
- ret = bpf_perf_event_read_simple(ring->mem,
- MMAP_PAGE_CNT * get_page_size(),
- get_page_size(), buf, buf_len,
- print_bpf_output, ring);
- if (ret != LIBBPF_PERF_EVENT_CONT) {
- fprintf(stderr, "perf read loop failed with %d\n", ret);
- stop = true;
- }
-}
-
-static int perf_mmap_size(void)
-{
- return get_page_size() * (MMAP_PAGE_CNT + 1);
-}
-
-static void *perf_event_mmap(int fd)
-{
- int mmap_size = perf_mmap_size();
- void *base;
-
- base = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (base == MAP_FAILED) {
- p_err("event mmap failed: %s\n", strerror(errno));
- return NULL;
- }
-
- return base;
-}
-
-static void perf_event_unmap(void *mem)
-{
- if (munmap(mem, perf_mmap_size()))
- fprintf(stderr, "Can't unmap ring memory!\n");
-}
-
-static int bpf_perf_event_open(int map_fd, int key, int cpu)
+int do_event_pipe(int argc, char **argv)
{
- struct perf_event_attr attr = {
+ struct perf_event_attr perf_attr = {
.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME,
.type = PERF_TYPE_SOFTWARE,
.config = PERF_COUNT_SW_BPF_OUTPUT,
+ .sample_period = 1,
+ .wakeup_events = 1,
};
- int pmu_fd;
-
- pmu_fd = sys_perf_event_open(&attr, -1, cpu, -1, 0);
- if (pmu_fd < 0) {
- p_err("failed to open perf event %d for CPU %d", key, cpu);
- return -1;
- }
-
- if (bpf_map_update_elem(map_fd, &key, &pmu_fd, BPF_ANY)) {
- p_err("failed to update map for event %d for CPU %d", key, cpu);
- goto err_close;
- }
- if (ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0)) {
- p_err("failed to enable event %d for CPU %d", key, cpu);
- goto err_close;
- }
-
- return pmu_fd;
-
-err_close:
- close(pmu_fd);
- return -1;
-}
-
-int do_event_pipe(int argc, char **argv)
-{
- int i, nfds, map_fd, index = -1, cpu = -1;
struct bpf_map_info map_info = {};
- struct event_ring_info *rings;
- size_t tmp_buf_sz = 0;
- void *tmp_buf = NULL;
- struct pollfd *pfds;
+ struct perf_buffer_raw_opts opts = {};
+ struct event_pipe_ctx ctx = {
+ .all_cpus = true,
+ .cpu = -1,
+ .idx = -1,
+ };
+ struct perf_buffer *pb;
__u32 map_info_len;
- bool do_all = true;
+ int err, map_fd;
map_info_len = sizeof(map_info);
map_fd = map_parse_fd_and_info(&argc, &argv, &map_info, &map_info_len);
@@ -205,9 +155,9 @@ int do_event_pipe(int argc, char **argv)
char *endptr;
NEXT_ARG();
- cpu = strtoul(*argv, &endptr, 0);
+ ctx.cpu = strtoul(*argv, &endptr, 0);
if (*endptr) {
- p_err("can't parse %s as CPU ID", **argv);
+ p_err("can't parse %s as CPU ID", *argv);
goto err_close_map;
}
@@ -216,9 +166,9 @@ int do_event_pipe(int argc, char **argv)
char *endptr;
NEXT_ARG();
- index = strtoul(*argv, &endptr, 0);
+ ctx.idx = strtoul(*argv, &endptr, 0);
if (*endptr) {
- p_err("can't parse %s as index", **argv);
+ p_err("can't parse %s as index", *argv);
goto err_close_map;
}
@@ -228,45 +178,32 @@ int do_event_pipe(int argc, char **argv)
goto err_close_map;
}
- do_all = false;
+ ctx.all_cpus = false;
}
- if (!do_all) {
- if (index == -1 || cpu == -1) {
+ if (!ctx.all_cpus) {
+ if (ctx.idx == -1 || ctx.cpu == -1) {
p_err("cpu and index must be specified together");
goto err_close_map;
}
-
- nfds = 1;
} else {
- nfds = min(get_possible_cpus(), map_info.max_entries);
- cpu = 0;
- index = 0;
+ ctx.cpu = 0;
+ ctx.idx = 0;
}
- rings = calloc(nfds, sizeof(rings[0]));
- if (!rings)
+ opts.attr = &perf_attr;
+ opts.event_cb = print_bpf_output;
+ opts.ctx = &ctx;
+ opts.cpu_cnt = ctx.all_cpus ? 0 : 1;
+ opts.cpus = &ctx.cpu;
+ opts.map_keys = &ctx.idx;
+
+ pb = perf_buffer__new_raw(map_fd, MMAP_PAGE_CNT, &opts);
+ err = libbpf_get_error(pb);
+ if (err) {
+ p_err("failed to create perf buffer: %s (%d)",
+ strerror(err), err);
goto err_close_map;
-
- pfds = calloc(nfds, sizeof(pfds[0]));
- if (!pfds)
- goto err_free_rings;
-
- for (i = 0; i < nfds; i++) {
- rings[i].cpu = cpu + i;
- rings[i].key = index + i;
-
- rings[i].fd = bpf_perf_event_open(map_fd, rings[i].key,
- rings[i].cpu);
- if (rings[i].fd < 0)
- goto err_close_fds_prev;
-
- rings[i].mem = perf_event_mmap(rings[i].fd);
- if (!rings[i].mem)
- goto err_close_fds_current;
-
- pfds[i].fd = rings[i].fd;
- pfds[i].events = POLLIN;
}
signal(SIGINT, int_exit);
@@ -277,34 +214,24 @@ int do_event_pipe(int argc, char **argv)
jsonw_start_array(json_wtr);
while (!stop) {
- poll(pfds, nfds, 200);
- for (i = 0; i < nfds; i++)
- perf_event_read(&rings[i], &tmp_buf, &tmp_buf_sz);
+ err = perf_buffer__poll(pb, 200);
+ if (err < 0 && err != -EINTR) {
+ p_err("perf buffer polling failed: %s (%d)",
+ strerror(err), err);
+ goto err_close_pb;
+ }
}
- free(tmp_buf);
if (json_output)
jsonw_end_array(json_wtr);
- for (i = 0; i < nfds; i++) {
- perf_event_unmap(rings[i].mem);
- close(rings[i].fd);
- }
- free(pfds);
- free(rings);
+ perf_buffer__free(pb);
close(map_fd);
return 0;
-err_close_fds_prev:
- while (i--) {
- perf_event_unmap(rings[i].mem);
-err_close_fds_current:
- close(rings[i].fd);
- }
- free(pfds);
-err_free_rings:
- free(rings);
+err_close_pb:
+ perf_buffer__free(pb);
err_close_map:
close(map_fd);
return -1;
diff --git a/tools/bpf/bpftool/net.c b/tools/bpf/bpftool/net.c
index 67e99c56bc88..4f52d3151616 100644
--- a/tools/bpf/bpftool/net.c
+++ b/tools/bpf/bpftool/net.c
@@ -55,6 +55,35 @@ struct bpf_attach_info {
__u32 flow_dissector_id;
};
+enum net_attach_type {
+ NET_ATTACH_TYPE_XDP,
+ NET_ATTACH_TYPE_XDP_GENERIC,
+ NET_ATTACH_TYPE_XDP_DRIVER,
+ NET_ATTACH_TYPE_XDP_OFFLOAD,
+};
+
+static const char * const attach_type_strings[] = {
+ [NET_ATTACH_TYPE_XDP] = "xdp",
+ [NET_ATTACH_TYPE_XDP_GENERIC] = "xdpgeneric",
+ [NET_ATTACH_TYPE_XDP_DRIVER] = "xdpdrv",
+ [NET_ATTACH_TYPE_XDP_OFFLOAD] = "xdpoffload",
+};
+
+const size_t net_attach_type_size = ARRAY_SIZE(attach_type_strings);
+
+static enum net_attach_type parse_attach_type(const char *str)
+{
+ enum net_attach_type type;
+
+ for (type = 0; type < net_attach_type_size; type++) {
+ if (attach_type_strings[type] &&
+ is_prefix(str, attach_type_strings[type]))
+ return type;
+ }
+
+ return net_attach_type_size;
+}
+
static int dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb)
{
struct bpf_netdev_t *netinfo = cookie;
@@ -197,7 +226,7 @@ static int query_flow_dissector(struct bpf_attach_info *attach_info)
fd = open("/proc/self/ns/net", O_RDONLY);
if (fd < 0) {
- p_err("can't open /proc/self/ns/net: %d",
+ p_err("can't open /proc/self/ns/net: %s",
strerror(errno));
return -1;
}
@@ -223,6 +252,134 @@ static int query_flow_dissector(struct bpf_attach_info *attach_info)
return 0;
}
+static int net_parse_dev(int *argc, char ***argv)
+{
+ int ifindex;
+
+ if (is_prefix(**argv, "dev")) {
+ NEXT_ARGP();
+
+ ifindex = if_nametoindex(**argv);
+ if (!ifindex)
+ p_err("invalid devname %s", **argv);
+
+ NEXT_ARGP();
+ } else {
+ p_err("expected 'dev', got: '%s'?", **argv);
+ return -1;
+ }
+
+ return ifindex;
+}
+
+static int do_attach_detach_xdp(int progfd, enum net_attach_type attach_type,
+ int ifindex, bool overwrite)
+{
+ __u32 flags = 0;
+
+ if (!overwrite)
+ flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
+ if (attach_type == NET_ATTACH_TYPE_XDP_GENERIC)
+ flags |= XDP_FLAGS_SKB_MODE;
+ if (attach_type == NET_ATTACH_TYPE_XDP_DRIVER)
+ flags |= XDP_FLAGS_DRV_MODE;
+ if (attach_type == NET_ATTACH_TYPE_XDP_OFFLOAD)
+ flags |= XDP_FLAGS_HW_MODE;
+
+ return bpf_set_link_xdp_fd(ifindex, progfd, flags);
+}
+
+static int do_attach(int argc, char **argv)
+{
+ enum net_attach_type attach_type;
+ int progfd, ifindex, err = 0;
+ bool overwrite = false;
+
+ /* parse attach args */
+ if (!REQ_ARGS(5))
+ return -EINVAL;
+
+ attach_type = parse_attach_type(*argv);
+ if (attach_type == net_attach_type_size) {
+ p_err("invalid net attach/detach type: %s", *argv);
+ return -EINVAL;
+ }
+ NEXT_ARG();
+
+ progfd = prog_parse_fd(&argc, &argv);
+ if (progfd < 0)
+ return -EINVAL;
+
+ ifindex = net_parse_dev(&argc, &argv);
+ if (ifindex < 1) {
+ close(progfd);
+ return -EINVAL;
+ }
+
+ if (argc) {
+ if (is_prefix(*argv, "overwrite")) {
+ overwrite = true;
+ } else {
+ p_err("expected 'overwrite', got: '%s'?", *argv);
+ close(progfd);
+ return -EINVAL;
+ }
+ }
+
+ /* attach xdp prog */
+ if (is_prefix("xdp", attach_type_strings[attach_type]))
+ err = do_attach_detach_xdp(progfd, attach_type, ifindex,
+ overwrite);
+
+ if (err < 0) {
+ p_err("interface %s attach failed: %s",
+ attach_type_strings[attach_type], strerror(-err));
+ return err;
+ }
+
+ if (json_output)
+ jsonw_null(json_wtr);
+
+ return 0;
+}
+
+static int do_detach(int argc, char **argv)
+{
+ enum net_attach_type attach_type;
+ int progfd, ifindex, err = 0;
+
+ /* parse detach args */
+ if (!REQ_ARGS(3))
+ return -EINVAL;
+
+ attach_type = parse_attach_type(*argv);
+ if (attach_type == net_attach_type_size) {
+ p_err("invalid net attach/detach type: %s", *argv);
+ return -EINVAL;
+ }
+ NEXT_ARG();
+
+ ifindex = net_parse_dev(&argc, &argv);
+ if (ifindex < 1)
+ return -EINVAL;
+
+ /* detach xdp prog */
+ progfd = -1;
+ if (is_prefix("xdp", attach_type_strings[attach_type]))
+ err = do_attach_detach_xdp(progfd, attach_type, ifindex, NULL);
+
+ if (err < 0) {
+ p_err("interface %s detach failed: %s",
+ attach_type_strings[attach_type], strerror(-err));
+ return err;
+ }
+
+ if (json_output)
+ jsonw_null(json_wtr);
+
+ return 0;
+}
+
static int do_show(int argc, char **argv)
{
struct bpf_attach_info attach_info = {};
@@ -232,13 +389,9 @@ static int do_show(int argc, char **argv)
char err_buf[256];
if (argc == 2) {
- if (strcmp(argv[0], "dev") != 0)
- usage();
- filter_idx = if_nametoindex(argv[1]);
- if (filter_idx == 0) {
- fprintf(stderr, "invalid dev name %s\n", argv[1]);
+ filter_idx = net_parse_dev(&argc, &argv);
+ if (filter_idx < 1)
return -1;
- }
} else if (argc != 0) {
usage();
}
@@ -305,13 +458,20 @@ static int do_help(int argc, char **argv)
fprintf(stderr,
"Usage: %s %s { show | list } [dev <devname>]\n"
+ " %s %s attach ATTACH_TYPE PROG dev <devname> [ overwrite ]\n"
+ " %s %s detach ATTACH_TYPE dev <devname>\n"
" %s %s help\n"
+ "\n"
+ " " HELP_SPEC_PROGRAM "\n"
+ " ATTACH_TYPE := { xdp | xdpgeneric | xdpdrv | xdpoffload }\n"
+ "\n"
"Note: Only xdp and tc attachments are supported now.\n"
" For progs attached to cgroups, use \"bpftool cgroup\"\n"
" to dump program attachments. For program types\n"
" sk_{filter,skb,msg,reuseport} and lwt/seg6, please\n"
" consult iproute2.\n",
- bin_name, argv[-2], bin_name, argv[-2]);
+ bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
+ bin_name, argv[-2]);
return 0;
}
@@ -319,6 +479,8 @@ static int do_help(int argc, char **argv)
static const struct cmd cmds[] = {
{ "show", do_show },
{ "list", do_show },
+ { "attach", do_attach },
+ { "detach", do_detach },
{ "help", do_help },
{ 0 }
};
diff --git a/tools/bpf/bpftool/perf.c b/tools/bpf/bpftool/perf.c
index f2a545e667c4..b2046f33e23f 100644
--- a/tools/bpf/bpftool/perf.c
+++ b/tools/bpf/bpftool/perf.c
@@ -104,6 +104,8 @@ static void print_perf_json(int pid, int fd, __u32 prog_id, __u32 fd_type,
jsonw_string_field(json_wtr, "filename", buf);
jsonw_lluint_field(json_wtr, "offset", probe_offset);
break;
+ default:
+ break;
}
jsonw_end_object(json_wtr);
}
@@ -140,6 +142,8 @@ static void print_perf_plain(int pid, int fd, __u32 prog_id, __u32 fd_type,
printf("uretprobe filename %s offset %llu\n", buf,
probe_offset);
break;
+ default:
+ break;
}
}
diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
index fc495b27f0fc..43fdbbfe41bb 100644
--- a/tools/bpf/bpftool/prog.c
+++ b/tools/bpf/bpftool/prog.c
@@ -15,6 +15,7 @@
#include <sys/stat.h>
#include <linux/err.h>
+#include <linux/sizes.h>
#include <bpf.h>
#include <btf.h>
@@ -362,7 +363,9 @@ static int do_show(int argc, char **argv)
if (fd < 0)
return -1;
- return show_prog(fd);
+ err = show_prog(fd);
+ close(fd);
+ return err;
}
if (argc)
@@ -748,12 +751,351 @@ static int do_detach(int argc, char **argv)
return 0;
}
+static int check_single_stdin(char *file_data_in, char *file_ctx_in)
+{
+ if (file_data_in && file_ctx_in &&
+ !strcmp(file_data_in, "-") && !strcmp(file_ctx_in, "-")) {
+ p_err("cannot use standard input for both data_in and ctx_in");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int get_run_data(const char *fname, void **data_ptr, unsigned int *size)
+{
+ size_t block_size = 256;
+ size_t buf_size = block_size;
+ size_t nb_read = 0;
+ void *tmp;
+ FILE *f;
+
+ if (!fname) {
+ *data_ptr = NULL;
+ *size = 0;
+ return 0;
+ }
+
+ if (!strcmp(fname, "-"))
+ f = stdin;
+ else
+ f = fopen(fname, "r");
+ if (!f) {
+ p_err("failed to open %s: %s", fname, strerror(errno));
+ return -1;
+ }
+
+ *data_ptr = malloc(block_size);
+ if (!*data_ptr) {
+ p_err("failed to allocate memory for data_in/ctx_in: %s",
+ strerror(errno));
+ goto err_fclose;
+ }
+
+ while ((nb_read += fread(*data_ptr + nb_read, 1, block_size, f))) {
+ if (feof(f))
+ break;
+ if (ferror(f)) {
+ p_err("failed to read data_in/ctx_in from %s: %s",
+ fname, strerror(errno));
+ goto err_free;
+ }
+ if (nb_read > buf_size - block_size) {
+ if (buf_size == UINT32_MAX) {
+ p_err("data_in/ctx_in is too long (max: %d)",
+ UINT32_MAX);
+ goto err_free;
+ }
+ /* No space for fread()-ing next chunk; realloc() */
+ buf_size *= 2;
+ tmp = realloc(*data_ptr, buf_size);
+ if (!tmp) {
+ p_err("failed to reallocate data_in/ctx_in: %s",
+ strerror(errno));
+ goto err_free;
+ }
+ *data_ptr = tmp;
+ }
+ }
+ if (f != stdin)
+ fclose(f);
+
+ *size = nb_read;
+ return 0;
+
+err_free:
+ free(*data_ptr);
+ *data_ptr = NULL;
+err_fclose:
+ if (f != stdin)
+ fclose(f);
+ return -1;
+}
+
+static void hex_print(void *data, unsigned int size, FILE *f)
+{
+ size_t i, j;
+ char c;
+
+ for (i = 0; i < size; i += 16) {
+ /* Row offset */
+ fprintf(f, "%07zx\t", i);
+
+ /* Hexadecimal values */
+ for (j = i; j < i + 16 && j < size; j++)
+ fprintf(f, "%02x%s", *(uint8_t *)(data + j),
+ j % 2 ? " " : "");
+ for (; j < i + 16; j++)
+ fprintf(f, " %s", j % 2 ? " " : "");
+
+ /* ASCII values (if relevant), '.' otherwise */
+ fprintf(f, "| ");
+ for (j = i; j < i + 16 && j < size; j++) {
+ c = *(char *)(data + j);
+ if (c < ' ' || c > '~')
+ c = '.';
+ fprintf(f, "%c%s", c, j == i + 7 ? " " : "");
+ }
+
+ fprintf(f, "\n");
+ }
+}
+
+static int
+print_run_output(void *data, unsigned int size, const char *fname,
+ const char *json_key)
+{
+ size_t nb_written;
+ FILE *f;
+
+ if (!fname)
+ return 0;
+
+ if (!strcmp(fname, "-")) {
+ f = stdout;
+ if (json_output) {
+ jsonw_name(json_wtr, json_key);
+ print_data_json(data, size);
+ } else {
+ hex_print(data, size, f);
+ }
+ return 0;
+ }
+
+ f = fopen(fname, "w");
+ if (!f) {
+ p_err("failed to open %s: %s", fname, strerror(errno));
+ return -1;
+ }
+
+ nb_written = fwrite(data, 1, size, f);
+ fclose(f);
+ if (nb_written != size) {
+ p_err("failed to write output data/ctx: %s", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int alloc_run_data(void **data_ptr, unsigned int size_out)
+{
+ *data_ptr = calloc(size_out, 1);
+ if (!*data_ptr) {
+ p_err("failed to allocate memory for output data/ctx: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int do_run(int argc, char **argv)
+{
+ char *data_fname_in = NULL, *data_fname_out = NULL;
+ char *ctx_fname_in = NULL, *ctx_fname_out = NULL;
+ struct bpf_prog_test_run_attr test_attr = {0};
+ const unsigned int default_size = SZ_32K;
+ void *data_in = NULL, *data_out = NULL;
+ void *ctx_in = NULL, *ctx_out = NULL;
+ unsigned int repeat = 1;
+ int fd, err;
+
+ if (!REQ_ARGS(4))
+ return -1;
+
+ fd = prog_parse_fd(&argc, &argv);
+ if (fd < 0)
+ return -1;
+
+ while (argc) {
+ if (detect_common_prefix(*argv, "data_in", "data_out",
+ "data_size_out", NULL))
+ return -1;
+ if (detect_common_prefix(*argv, "ctx_in", "ctx_out",
+ "ctx_size_out", NULL))
+ return -1;
+
+ if (is_prefix(*argv, "data_in")) {
+ NEXT_ARG();
+ if (!REQ_ARGS(1))
+ return -1;
+
+ data_fname_in = GET_ARG();
+ if (check_single_stdin(data_fname_in, ctx_fname_in))
+ return -1;
+ } else if (is_prefix(*argv, "data_out")) {
+ NEXT_ARG();
+ if (!REQ_ARGS(1))
+ return -1;
+
+ data_fname_out = GET_ARG();
+ } else if (is_prefix(*argv, "data_size_out")) {
+ char *endptr;
+
+ NEXT_ARG();
+ if (!REQ_ARGS(1))
+ return -1;
+
+ test_attr.data_size_out = strtoul(*argv, &endptr, 0);
+ if (*endptr) {
+ p_err("can't parse %s as output data size",
+ *argv);
+ return -1;
+ }
+ NEXT_ARG();
+ } else if (is_prefix(*argv, "ctx_in")) {
+ NEXT_ARG();
+ if (!REQ_ARGS(1))
+ return -1;
+
+ ctx_fname_in = GET_ARG();
+ if (check_single_stdin(data_fname_in, ctx_fname_in))
+ return -1;
+ } else if (is_prefix(*argv, "ctx_out")) {
+ NEXT_ARG();
+ if (!REQ_ARGS(1))
+ return -1;
+
+ ctx_fname_out = GET_ARG();
+ } else if (is_prefix(*argv, "ctx_size_out")) {
+ char *endptr;
+
+ NEXT_ARG();
+ if (!REQ_ARGS(1))
+ return -1;
+
+ test_attr.ctx_size_out = strtoul(*argv, &endptr, 0);
+ if (*endptr) {
+ p_err("can't parse %s as output context size",
+ *argv);
+ return -1;
+ }
+ NEXT_ARG();
+ } else if (is_prefix(*argv, "repeat")) {
+ char *endptr;
+
+ NEXT_ARG();
+ if (!REQ_ARGS(1))
+ return -1;
+
+ repeat = strtoul(*argv, &endptr, 0);
+ if (*endptr) {
+ p_err("can't parse %s as repeat number",
+ *argv);
+ return -1;
+ }
+ NEXT_ARG();
+ } else {
+ p_err("expected no more arguments, 'data_in', 'data_out', 'data_size_out', 'ctx_in', 'ctx_out', 'ctx_size_out' or 'repeat', got: '%s'?",
+ *argv);
+ return -1;
+ }
+ }
+
+ err = get_run_data(data_fname_in, &data_in, &test_attr.data_size_in);
+ if (err)
+ return -1;
+
+ if (data_in) {
+ if (!test_attr.data_size_out)
+ test_attr.data_size_out = default_size;
+ err = alloc_run_data(&data_out, test_attr.data_size_out);
+ if (err)
+ goto free_data_in;
+ }
+
+ err = get_run_data(ctx_fname_in, &ctx_in, &test_attr.ctx_size_in);
+ if (err)
+ goto free_data_out;
+
+ if (ctx_in) {
+ if (!test_attr.ctx_size_out)
+ test_attr.ctx_size_out = default_size;
+ err = alloc_run_data(&ctx_out, test_attr.ctx_size_out);
+ if (err)
+ goto free_ctx_in;
+ }
+
+ test_attr.prog_fd = fd;
+ test_attr.repeat = repeat;
+ test_attr.data_in = data_in;
+ test_attr.data_out = data_out;
+ test_attr.ctx_in = ctx_in;
+ test_attr.ctx_out = ctx_out;
+
+ err = bpf_prog_test_run_xattr(&test_attr);
+ if (err) {
+ p_err("failed to run program: %s", strerror(errno));
+ goto free_ctx_out;
+ }
+
+ err = 0;
+
+ if (json_output)
+ jsonw_start_object(json_wtr); /* root */
+
+ /* Do not exit on errors occurring when printing output data/context,
+ * we still want to print return value and duration for program run.
+ */
+ if (test_attr.data_size_out)
+ err += print_run_output(test_attr.data_out,
+ test_attr.data_size_out,
+ data_fname_out, "data_out");
+ if (test_attr.ctx_size_out)
+ err += print_run_output(test_attr.ctx_out,
+ test_attr.ctx_size_out,
+ ctx_fname_out, "ctx_out");
+
+ if (json_output) {
+ jsonw_uint_field(json_wtr, "retval", test_attr.retval);
+ jsonw_uint_field(json_wtr, "duration", test_attr.duration);
+ jsonw_end_object(json_wtr); /* root */
+ } else {
+ fprintf(stdout, "Return value: %u, duration%s: %uns\n",
+ test_attr.retval,
+ repeat > 1 ? " (average)" : "", test_attr.duration);
+ }
+
+free_ctx_out:
+ free(ctx_out);
+free_ctx_in:
+ free(ctx_in);
+free_data_out:
+ free(data_out);
+free_data_in:
+ free(data_in);
+
+ return err;
+}
+
static int load_with_options(int argc, char **argv, bool first_prog_only)
{
- enum bpf_attach_type expected_attach_type;
- struct bpf_object_open_attr attr = {
- .prog_type = BPF_PROG_TYPE_UNSPEC,
+ struct bpf_object_load_attr load_attr = { 0 };
+ struct bpf_object_open_attr open_attr = {
+ .prog_type = BPF_PROG_TYPE_UNSPEC,
};
+ enum bpf_attach_type expected_attach_type;
struct map_replace *map_replace = NULL;
struct bpf_program *prog = NULL, *pos;
unsigned int old_map_fds = 0;
@@ -767,7 +1109,7 @@ static int load_with_options(int argc, char **argv, bool first_prog_only)
if (!REQ_ARGS(2))
return -1;
- attr.file = GET_ARG();
+ open_attr.file = GET_ARG();
pinfile = GET_ARG();
while (argc) {
@@ -776,7 +1118,7 @@ static int load_with_options(int argc, char **argv, bool first_prog_only)
NEXT_ARG();
- if (attr.prog_type != BPF_PROG_TYPE_UNSPEC) {
+ if (open_attr.prog_type != BPF_PROG_TYPE_UNSPEC) {
p_err("program type already specified");
goto err_free_reuse_maps;
}
@@ -793,7 +1135,8 @@ static int load_with_options(int argc, char **argv, bool first_prog_only)
strcat(type, *argv);
strcat(type, "/");
- err = libbpf_prog_type_by_name(type, &attr.prog_type,
+ err = libbpf_prog_type_by_name(type,
+ &open_attr.prog_type,
&expected_attach_type);
free(type);
if (err < 0)
@@ -879,16 +1222,18 @@ static int load_with_options(int argc, char **argv, bool first_prog_only)
}
}
- obj = __bpf_object__open_xattr(&attr, bpf_flags);
+ set_max_rlimit();
+
+ obj = __bpf_object__open_xattr(&open_attr, bpf_flags);
if (IS_ERR_OR_NULL(obj)) {
p_err("failed to open object file");
goto err_free_reuse_maps;
}
bpf_object__for_each_program(pos, obj) {
- enum bpf_prog_type prog_type = attr.prog_type;
+ enum bpf_prog_type prog_type = open_attr.prog_type;
- if (attr.prog_type == BPF_PROG_TYPE_UNSPEC) {
+ if (open_attr.prog_type == BPF_PROG_TYPE_UNSPEC) {
const char *sec_name = bpf_program__title(pos, false);
err = libbpf_prog_type_by_name(sec_name, &prog_type,
@@ -958,9 +1303,12 @@ static int load_with_options(int argc, char **argv, bool first_prog_only)
goto err_close_obj;
}
- set_max_rlimit();
+ load_attr.obj = obj;
+ if (verifier_logs)
+ /* log_level1 + log_level2 + stats, but not stable UAPI */
+ load_attr.log_level = 1 + 2 + 4;
- err = bpf_object__load(obj);
+ err = bpf_object__load_xattr(&load_attr);
if (err) {
p_err("failed to load object file");
goto err_close_obj;
@@ -1051,6 +1399,11 @@ static int do_help(int argc, char **argv)
" [pinmaps MAP_DIR]\n"
" %s %s attach PROG ATTACH_TYPE [MAP]\n"
" %s %s detach PROG ATTACH_TYPE [MAP]\n"
+ " %s %s run PROG \\\n"
+ " data_in FILE \\\n"
+ " [data_out FILE [data_size_out L]] \\\n"
+ " [ctx_in FILE [ctx_out FILE [ctx_size_out M]]] \\\n"
+ " [repeat N]\n"
" %s %s tracelog\n"
" %s %s help\n"
"\n"
@@ -1063,14 +1416,17 @@ static int do_help(int argc, char **argv)
" sk_reuseport | flow_dissector | cgroup/sysctl |\n"
" cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n"
" cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n"
- " cgroup/sendmsg4 | cgroup/sendmsg6 }\n"
+ " cgroup/sendmsg4 | cgroup/sendmsg6 | cgroup/recvmsg4 |\n"
+ " cgroup/recvmsg6 | cgroup/getsockopt |\n"
+ " cgroup/setsockopt }\n"
" ATTACH_TYPE := { msg_verdict | stream_verdict | stream_parser |\n"
" flow_dissector }\n"
" " HELP_SPEC_OPTIONS "\n"
"",
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
- bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]);
+ bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
+ bin_name, argv[-2]);
return 0;
}
@@ -1086,6 +1442,7 @@ static const struct cmd cmds[] = {
{ "attach", do_attach },
{ "detach", do_detach },
{ "tracelog", do_tracelog },
+ { "run", do_run },
{ 0 }
};
diff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/xlated_dumper.c
index 0bb17bf88b18..494d7ae3614d 100644
--- a/tools/bpf/bpftool/xlated_dumper.c
+++ b/tools/bpf/bpftool/xlated_dumper.c
@@ -31,9 +31,7 @@ void kernel_syms_load(struct dump_data *dd)
if (!fp)
return;
- while (!feof(fp)) {
- if (!fgets(buff, sizeof(buff), fp))
- break;
+ while (fgets(buff, sizeof(buff), fp)) {
tmp = reallocarray(dd->sym_mapping, dd->sym_count + 1,
sizeof(*dd->sym_mapping));
if (!tmp) {
diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index 361207387b1b..8a19753cc26a 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
feature_dir := $(srctree)/tools/build/feature
ifneq ($(OUTPUT),)
@@ -35,11 +36,13 @@ FEATURE_TESTS_BASIC := \
fortify-source \
sync-compare-and-swap \
get_current_dir_name \
+ gettid \
glibc \
gtk2 \
gtk2-infobar \
libaudit \
libbfd \
+ libcap \
libelf \
libelf-getphdrnum \
libelf-gelf_getnote \
@@ -51,6 +54,7 @@ FEATURE_TESTS_BASIC := \
libpython \
libpython-version \
libslang \
+ libslang-include-subdir \
libcrypto \
libunwind \
pthread-attr-setaffinity-np \
@@ -107,12 +111,12 @@ FEATURE_DISPLAY ?= \
gtk2 \
libaudit \
libbfd \
+ libcap \
libelf \
libnuma \
numa_num_possible_cpus \
libperl \
libpython \
- libslang \
libcrypto \
libunwind \
libdw-dwarf-unwind \
diff --git a/tools/build/Makefile.include b/tools/build/Makefile.include
index d360f39a445b..8dadaa0fbb43 100644
--- a/tools/build/Makefile.include
+++ b/tools/build/Makefile.include
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
build := -f $(srctree)/tools/build/Makefile.build dir=. obj
fixdep:
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
index 4b8244ee65ce..8499385365c0 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -20,6 +20,7 @@ FILES= \
test-libbfd-liberty.bin \
test-libbfd-liberty-z.bin \
test-cplus-demangle.bin \
+ test-libcap.bin \
test-libelf.bin \
test-libelf-getphdrnum.bin \
test-libelf-gelf_getnote.bin \
@@ -31,6 +32,7 @@ FILES= \
test-libpython.bin \
test-libpython-version.bin \
test-libslang.bin \
+ test-libslang-include-subdir.bin \
test-libcrypto.bin \
test-libunwind.bin \
test-libunwind-debug-frame.bin \
@@ -54,6 +56,7 @@ FILES= \
test-get_cpuid.bin \
test-sdt.bin \
test-cxx.bin \
+ test-gettid.bin \
test-jvmti.bin \
test-jvmti-cmlr.bin \
test-sched_getcpu.bin \
@@ -103,6 +106,9 @@ $(OUTPUT)test-fortify-source.bin:
$(OUTPUT)test-bionic.bin:
$(BUILD)
+$(OUTPUT)test-libcap.bin:
+ $(BUILD) -lcap
+
$(OUTPUT)test-libelf.bin:
$(BUILD) -lelf
@@ -181,7 +187,10 @@ $(OUTPUT)test-libaudit.bin:
$(BUILD) -laudit
$(OUTPUT)test-libslang.bin:
- $(BUILD) -I/usr/include/slang -lslang
+ $(BUILD) -lslang
+
+$(OUTPUT)test-libslang-include-subdir.bin:
+ $(BUILD) -lslang
$(OUTPUT)test-libcrypto.bin:
$(BUILD) -lcrypto
@@ -267,6 +276,9 @@ $(OUTPUT)test-sdt.bin:
$(OUTPUT)test-cxx.bin:
$(BUILDXX) -std=gnu++11
+$(OUTPUT)test-gettid.bin:
+ $(BUILD)
+
$(OUTPUT)test-jvmti.bin:
$(BUILD)
diff --git a/tools/build/feature/test-all.c b/tools/build/feature/test-all.c
index a59c53705093..88145e8cde1a 100644
--- a/tools/build/feature/test-all.c
+++ b/tools/build/feature/test-all.c
@@ -38,6 +38,10 @@
# include "test-get_current_dir_name.c"
#undef main
+#define main main_test_gettid
+# include "test-gettid.c"
+#undef main
+
#define main main_test_glibc
# include "test-glibc.c"
#undef main
@@ -182,7 +186,7 @@
# include "test-disassembler-four-args.c"
#undef main
-#define main main_test_zstd
+#define main main_test_libzstd
# include "test-libzstd.c"
#undef main
@@ -195,6 +199,7 @@ int main(int argc, char *argv[])
main_test_libelf();
main_test_libelf_mmap();
main_test_get_current_dir_name();
+ main_test_gettid();
main_test_glibc();
main_test_dwarf();
main_test_dwarf_getlocations();
diff --git a/tools/build/feature/test-fortify-source.c b/tools/build/feature/test-fortify-source.c
index c9f398d87868..c8a57194f9f2 100644
--- a/tools/build/feature/test-fortify-source.c
+++ b/tools/build/feature/test-fortify-source.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
int main(void)
diff --git a/tools/build/feature/test-gettid.c b/tools/build/feature/test-gettid.c
new file mode 100644
index 000000000000..ef24e42d3f1b
--- /dev/null
+++ b/tools/build/feature/test-gettid.c
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2019, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
+#define _GNU_SOURCE
+#include <unistd.h>
+
+int main(void)
+{
+ return gettid();
+}
+
+#undef _GNU_SOURCE
diff --git a/tools/build/feature/test-hello.c b/tools/build/feature/test-hello.c
index c9f398d87868..c8a57194f9f2 100644
--- a/tools/build/feature/test-hello.c
+++ b/tools/build/feature/test-hello.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
int main(void)
diff --git a/tools/build/feature/test-libcap.c b/tools/build/feature/test-libcap.c
new file mode 100644
index 000000000000..d2a2e152195f
--- /dev/null
+++ b/tools/build/feature/test-libcap.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <sys/capability.h>
+#include <linux/capability.h>
+
+int main(void)
+{
+ cap_flag_value_t val;
+ cap_t caps = cap_get_proc();
+
+ if (!caps)
+ return 1;
+
+ if (cap_get_flag(caps, CAP_SYS_ADMIN, CAP_EFFECTIVE, &val) != 0)
+ return 1;
+
+ if (cap_free(caps) != 0)
+ return 1;
+
+ return 0;
+}
diff --git a/tools/build/feature/test-libslang-include-subdir.c b/tools/build/feature/test-libslang-include-subdir.c
new file mode 100644
index 000000000000..3ea47ec7590e
--- /dev/null
+++ b/tools/build/feature/test-libslang-include-subdir.c
@@ -0,0 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <slang/slang.h>
+
+int main(void)
+{
+ return SLsmg_init_smg();
+}
diff --git a/tools/build/feature/test-setns.c b/tools/build/feature/test-setns.c
index 4a1581ae7a55..2757c201ed50 100644
--- a/tools/build/feature/test-setns.c
+++ b/tools/build/feature/test-setns.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <sched.h>
diff --git a/tools/cgroup/iocost_coef_gen.py b/tools/cgroup/iocost_coef_gen.py
new file mode 100644
index 000000000000..df17a2ae80e5
--- /dev/null
+++ b/tools/cgroup/iocost_coef_gen.py
@@ -0,0 +1,178 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 Tejun Heo <tj@kernel.org>
+# Copyright (C) 2019 Andy Newell <newella@fb.com>
+# Copyright (C) 2019 Facebook
+
+desc = """
+Generate linear IO cost model coefficients used by the blk-iocost
+controller. If the target raw testdev is specified, destructive tests
+are performed against the whole device; otherwise, on
+./iocost-coef-fio.testfile. The result can be written directly to
+/sys/fs/cgroup/io.cost.model.
+
+On high performance devices, --numjobs > 1 is needed to achieve
+saturation.
+
+See Documentation/admin-guide/cgroup-v2.rst and block/blk-iocost.c
+for more details.
+"""
+
+import argparse
+import re
+import json
+import glob
+import os
+import sys
+import atexit
+import shutil
+import tempfile
+import subprocess
+
+parser = argparse.ArgumentParser(description=desc,
+ formatter_class=argparse.RawTextHelpFormatter)
+parser.add_argument('--testdev', metavar='DEV',
+ help='Raw block device to use for testing, ignores --testfile-size')
+parser.add_argument('--testfile-size-gb', type=float, metavar='GIGABYTES', default=16,
+ help='Testfile size in gigabytes (default: %(default)s)')
+parser.add_argument('--duration', type=int, metavar='SECONDS', default=120,
+ help='Individual test run duration in seconds (default: %(default)s)')
+parser.add_argument('--seqio-block-mb', metavar='MEGABYTES', type=int, default=128,
+ help='Sequential test block size in megabytes (default: %(default)s)')
+parser.add_argument('--seq-depth', type=int, metavar='DEPTH', default=64,
+ help='Sequential test queue depth (default: %(default)s)')
+parser.add_argument('--rand-depth', type=int, metavar='DEPTH', default=64,
+ help='Random test queue depth (default: %(default)s)')
+parser.add_argument('--numjobs', type=int, metavar='JOBS', default=1,
+ help='Number of parallel fio jobs to run (default: %(default)s)')
+parser.add_argument('--quiet', action='store_true')
+parser.add_argument('--verbose', action='store_true')
+
+def info(msg):
+ if not args.quiet:
+ print(msg)
+
+def dbg(msg):
+ if args.verbose and not args.quiet:
+ print(msg)
+
+# determine ('DEVNAME', 'MAJ:MIN') for @path
+def dir_to_dev(path):
+ # find the block device the current directory is on
+ devname = subprocess.run(f'findmnt -nvo SOURCE -T{path}',
+ stdout=subprocess.PIPE, shell=True).stdout
+ devname = os.path.basename(devname).decode('utf-8').strip()
+
+ # partition -> whole device
+ parents = glob.glob('/sys/block/*/' + devname)
+ if len(parents):
+ devname = os.path.basename(os.path.dirname(parents[0]))
+ rdev = os.stat(f'/dev/{devname}').st_rdev
+ return (devname, f'{os.major(rdev)}:{os.minor(rdev)}')
+
+def create_testfile(path, size):
+ global args
+
+ if os.path.isfile(path) and os.stat(path).st_size == size:
+ return
+
+ info(f'Creating testfile {path}')
+ subprocess.check_call(f'rm -f {path}', shell=True)
+ subprocess.check_call(f'touch {path}', shell=True)
+ subprocess.call(f'chattr +C {path}', shell=True)
+ subprocess.check_call(
+ f'pv -s {size} -pr /dev/urandom {"-q" if args.quiet else ""} | '
+ f'dd of={path} count={size} '
+ f'iflag=count_bytes,fullblock oflag=direct bs=16M status=none',
+ shell=True)
+
+def run_fio(testfile, duration, iotype, iodepth, blocksize, jobs):
+ global args
+
+ eta = 'never' if args.quiet else 'always'
+ outfile = tempfile.NamedTemporaryFile()
+ cmd = (f'fio --direct=1 --ioengine=libaio --name=coef '
+ f'--filename={testfile} --runtime={round(duration)} '
+ f'--readwrite={iotype} --iodepth={iodepth} --blocksize={blocksize} '
+ f'--eta={eta} --output-format json --output={outfile.name} '
+ f'--time_based --numjobs={jobs}')
+ if args.verbose:
+ dbg(f'Running {cmd}')
+ subprocess.check_call(cmd, shell=True)
+ with open(outfile.name, 'r') as f:
+ d = json.loads(f.read())
+ return sum(j['read']['bw_bytes'] + j['write']['bw_bytes'] for j in d['jobs'])
+
+def restore_elevator_nomerges():
+ global elevator_path, nomerges_path, elevator, nomerges
+
+ info(f'Restoring elevator to {elevator} and nomerges to {nomerges}')
+ with open(elevator_path, 'w') as f:
+ f.write(elevator)
+ with open(nomerges_path, 'w') as f:
+ f.write(nomerges)
+
+
+args = parser.parse_args()
+
+missing = False
+for cmd in [ 'findmnt', 'pv', 'dd', 'fio' ]:
+ if not shutil.which(cmd):
+ print(f'Required command "{cmd}" is missing', file=sys.stderr)
+ missing = True
+if missing:
+ sys.exit(1)
+
+if args.testdev:
+ devname = os.path.basename(args.testdev)
+ rdev = os.stat(f'/dev/{devname}').st_rdev
+ devno = f'{os.major(rdev)}:{os.minor(rdev)}'
+ testfile = f'/dev/{devname}'
+ info(f'Test target: {devname}({devno})')
+else:
+ devname, devno = dir_to_dev('.')
+ testfile = 'iocost-coef-fio.testfile'
+ testfile_size = int(args.testfile_size_gb * 2 ** 30)
+ create_testfile(testfile, testfile_size)
+ info(f'Test target: {testfile} on {devname}({devno})')
+
+elevator_path = f'/sys/block/{devname}/queue/scheduler'
+nomerges_path = f'/sys/block/{devname}/queue/nomerges'
+
+with open(elevator_path, 'r') as f:
+ elevator = re.sub(r'.*\[(.*)\].*', r'\1', f.read().strip())
+with open(nomerges_path, 'r') as f:
+ nomerges = f.read().strip()
+
+info(f'Temporarily disabling elevator and merges')
+atexit.register(restore_elevator_nomerges)
+with open(elevator_path, 'w') as f:
+ f.write('none')
+with open(nomerges_path, 'w') as f:
+ f.write('1')
+
+info('Determining rbps...')
+rbps = run_fio(testfile, args.duration, 'read',
+ 1, args.seqio_block_mb * (2 ** 20), args.numjobs)
+info(f'\nrbps={rbps}, determining rseqiops...')
+rseqiops = round(run_fio(testfile, args.duration, 'read',
+ args.seq_depth, 4096, args.numjobs) / 4096)
+info(f'\nrseqiops={rseqiops}, determining rrandiops...')
+rrandiops = round(run_fio(testfile, args.duration, 'randread',
+ args.rand_depth, 4096, args.numjobs) / 4096)
+info(f'\nrrandiops={rrandiops}, determining wbps...')
+wbps = run_fio(testfile, args.duration, 'write',
+ 1, args.seqio_block_mb * (2 ** 20), args.numjobs)
+info(f'\nwbps={wbps}, determining wseqiops...')
+wseqiops = round(run_fio(testfile, args.duration, 'write',
+ args.seq_depth, 4096, args.numjobs) / 4096)
+info(f'\nwseqiops={wseqiops}, determining wrandiops...')
+wrandiops = round(run_fio(testfile, args.duration, 'randwrite',
+ args.rand_depth, 4096, args.numjobs) / 4096)
+info(f'\nwrandiops={wrandiops}')
+restore_elevator_nomerges()
+atexit.unregister(restore_elevator_nomerges)
+info('')
+
+print(f'{devno} rbps={rbps} rseqiops={rseqiops} rrandiops={rrandiops} '
+ f'wbps={wbps} wseqiops={wseqiops} wrandiops={wrandiops}')
diff --git a/tools/cgroup/iocost_monitor.py b/tools/cgroup/iocost_monitor.py
new file mode 100644
index 000000000000..f79b23582a1d
--- /dev/null
+++ b/tools/cgroup/iocost_monitor.py
@@ -0,0 +1,277 @@
+#!/usr/bin/env drgn
+#
+# Copyright (C) 2019 Tejun Heo <tj@kernel.org>
+# Copyright (C) 2019 Facebook
+
+desc = """
+This is a drgn script to monitor the blk-iocost cgroup controller.
+See the comment at the top of block/blk-iocost.c for more details.
+For drgn, visit https://github.com/osandov/drgn.
+"""
+
+import sys
+import re
+import time
+import json
+import math
+
+import drgn
+from drgn import container_of
+from drgn.helpers.linux.list import list_for_each_entry,list_empty
+from drgn.helpers.linux.radixtree import radix_tree_for_each,radix_tree_lookup
+
+import argparse
+parser = argparse.ArgumentParser(description=desc,
+ formatter_class=argparse.RawTextHelpFormatter)
+parser.add_argument('devname', metavar='DEV',
+ help='Target block device name (e.g. sda)')
+parser.add_argument('--cgroup', action='append', metavar='REGEX',
+ help='Regex for target cgroups, ')
+parser.add_argument('--interval', '-i', metavar='SECONDS', type=float, default=1,
+ help='Monitoring interval in seconds')
+parser.add_argument('--json', action='store_true',
+ help='Output in json')
+args = parser.parse_args()
+
+def err(s):
+ print(s, file=sys.stderr, flush=True)
+ sys.exit(1)
+
+try:
+ blkcg_root = prog['blkcg_root']
+ plid = prog['blkcg_policy_iocost'].plid.value_()
+except:
+ err('The kernel does not have iocost enabled')
+
+IOC_RUNNING = prog['IOC_RUNNING'].value_()
+NR_USAGE_SLOTS = prog['NR_USAGE_SLOTS'].value_()
+HWEIGHT_WHOLE = prog['HWEIGHT_WHOLE'].value_()
+VTIME_PER_SEC = prog['VTIME_PER_SEC'].value_()
+VTIME_PER_USEC = prog['VTIME_PER_USEC'].value_()
+AUTOP_SSD_FAST = prog['AUTOP_SSD_FAST'].value_()
+AUTOP_SSD_DFL = prog['AUTOP_SSD_DFL'].value_()
+AUTOP_SSD_QD1 = prog['AUTOP_SSD_QD1'].value_()
+AUTOP_HDD = prog['AUTOP_HDD'].value_()
+
+autop_names = {
+ AUTOP_SSD_FAST: 'ssd_fast',
+ AUTOP_SSD_DFL: 'ssd_dfl',
+ AUTOP_SSD_QD1: 'ssd_qd1',
+ AUTOP_HDD: 'hdd',
+}
+
+class BlkgIterator:
+ def blkcg_name(blkcg):
+ return blkcg.css.cgroup.kn.name.string_().decode('utf-8')
+
+ def walk(self, blkcg, q_id, parent_path):
+ if not self.include_dying and \
+ not (blkcg.css.flags.value_() & prog['CSS_ONLINE'].value_()):
+ return
+
+ name = BlkgIterator.blkcg_name(blkcg)
+ path = parent_path + '/' + name if parent_path else name
+ blkg = drgn.Object(prog, 'struct blkcg_gq',
+ address=radix_tree_lookup(blkcg.blkg_tree, q_id))
+ if not blkg.address_:
+ return
+
+ self.blkgs.append((path if path else '/', blkg))
+
+ for c in list_for_each_entry('struct blkcg',
+ blkcg.css.children.address_of_(), 'css.sibling'):
+ self.walk(c, q_id, path)
+
+ def __init__(self, root_blkcg, q_id, include_dying=False):
+ self.include_dying = include_dying
+ self.blkgs = []
+ self.walk(root_blkcg, q_id, '')
+
+ def __iter__(self):
+ return iter(self.blkgs)
+
+class IocStat:
+ def __init__(self, ioc):
+ global autop_names
+
+ self.enabled = ioc.enabled.value_()
+ self.running = ioc.running.value_() == IOC_RUNNING
+ self.period_ms = ioc.period_us.value_() / 1_000
+ self.period_at = ioc.period_at.value_() / 1_000_000
+ self.vperiod_at = ioc.period_at_vtime.value_() / VTIME_PER_SEC
+ self.vrate_pct = ioc.vtime_rate.counter.value_() * 100 / VTIME_PER_USEC
+ self.busy_level = ioc.busy_level.value_()
+ self.autop_idx = ioc.autop_idx.value_()
+ self.user_cost_model = ioc.user_cost_model.value_()
+ self.user_qos_params = ioc.user_qos_params.value_()
+
+ if self.autop_idx in autop_names:
+ self.autop_name = autop_names[self.autop_idx]
+ else:
+ self.autop_name = '?'
+
+ def dict(self, now):
+ return { 'device' : devname,
+ 'timestamp' : str(now),
+ 'enabled' : str(int(self.enabled)),
+ 'running' : str(int(self.running)),
+ 'period_ms' : str(self.period_ms),
+ 'period_at' : str(self.period_at),
+ 'period_vtime_at' : str(self.vperiod_at),
+ 'busy_level' : str(self.busy_level),
+ 'vrate_pct' : str(self.vrate_pct), }
+
+ def table_preamble_str(self):
+ state = ('RUN' if self.running else 'IDLE') if self.enabled else 'OFF'
+ output = f'{devname} {state:4} ' \
+ f'per={self.period_ms}ms ' \
+ f'cur_per={self.period_at:.3f}:v{self.vperiod_at:.3f} ' \
+ f'busy={self.busy_level:+3} ' \
+ f'vrate={self.vrate_pct:6.2f}% ' \
+ f'params={self.autop_name}'
+ if self.user_cost_model or self.user_qos_params:
+ output += f'({"C" if self.user_cost_model else ""}{"Q" if self.user_qos_params else ""})'
+ return output
+
+ def table_header_str(self):
+ return f'{"":25} active {"weight":>9} {"hweight%":>13} {"inflt%":>6} ' \
+ f'{"dbt":>3} {"delay":>6} {"usages%"}'
+
+class IocgStat:
+ def __init__(self, iocg):
+ ioc = iocg.ioc
+ blkg = iocg.pd.blkg
+
+ self.is_active = not list_empty(iocg.active_list.address_of_())
+ self.weight = iocg.weight.value_()
+ self.active = iocg.active.value_()
+ self.inuse = iocg.inuse.value_()
+ self.hwa_pct = iocg.hweight_active.value_() * 100 / HWEIGHT_WHOLE
+ self.hwi_pct = iocg.hweight_inuse.value_() * 100 / HWEIGHT_WHOLE
+ self.address = iocg.value_()
+
+ vdone = iocg.done_vtime.counter.value_()
+ vtime = iocg.vtime.counter.value_()
+ vrate = ioc.vtime_rate.counter.value_()
+ period_vtime = ioc.period_us.value_() * vrate
+ if period_vtime:
+ self.inflight_pct = (vtime - vdone) * 100 / period_vtime
+ else:
+ self.inflight_pct = 0
+
+ self.debt_ms = iocg.abs_vdebt.counter.value_() / VTIME_PER_USEC / 1000
+ self.use_delay = blkg.use_delay.counter.value_()
+ self.delay_ms = blkg.delay_nsec.counter.value_() / 1_000_000
+
+ usage_idx = iocg.usage_idx.value_()
+ self.usages = []
+ self.usage = 0
+ for i in range(NR_USAGE_SLOTS):
+ usage = iocg.usages[(usage_idx + i) % NR_USAGE_SLOTS].value_()
+ upct = usage * 100 / HWEIGHT_WHOLE
+ self.usages.append(upct)
+ self.usage = max(self.usage, upct)
+
+ def dict(self, now, path):
+ out = { 'cgroup' : path,
+ 'timestamp' : str(now),
+ 'is_active' : str(int(self.is_active)),
+ 'weight' : str(self.weight),
+ 'weight_active' : str(self.active),
+ 'weight_inuse' : str(self.inuse),
+ 'hweight_active_pct' : str(self.hwa_pct),
+ 'hweight_inuse_pct' : str(self.hwi_pct),
+ 'inflight_pct' : str(self.inflight_pct),
+ 'debt_ms' : str(self.debt_ms),
+ 'use_delay' : str(self.use_delay),
+ 'delay_ms' : str(self.delay_ms),
+ 'usage_pct' : str(self.usage),
+ 'address' : str(hex(self.address)) }
+ for i in range(len(self.usages)):
+ out[f'usage_pct_{i}'] = str(self.usages[i])
+ return out
+
+ def table_row_str(self, path):
+ out = f'{path[-28:]:28} ' \
+ f'{"*" if self.is_active else " "} ' \
+ f'{self.inuse:5}/{self.active:5} ' \
+ f'{self.hwi_pct:6.2f}/{self.hwa_pct:6.2f} ' \
+ f'{self.inflight_pct:6.2f} ' \
+ f'{min(math.ceil(self.debt_ms), 999):3} ' \
+ f'{min(self.use_delay, 99):2}*'\
+ f'{min(math.ceil(self.delay_ms), 999):03} '
+ for u in self.usages:
+ out += f'{min(round(u), 999):03d}:'
+ out = out.rstrip(':')
+ return out
+
+# handle args
+table_fmt = not args.json
+interval = args.interval
+devname = args.devname
+
+if args.json:
+ table_fmt = False
+
+re_str = None
+if args.cgroup:
+ for r in args.cgroup:
+ if re_str is None:
+ re_str = r
+ else:
+ re_str += '|' + r
+
+filter_re = re.compile(re_str) if re_str else None
+
+# Locate the roots
+q_id = None
+root_iocg = None
+ioc = None
+
+for i, ptr in radix_tree_for_each(blkcg_root.blkg_tree):
+ blkg = drgn.Object(prog, 'struct blkcg_gq', address=ptr)
+ try:
+ if devname == blkg.q.kobj.parent.name.string_().decode('utf-8'):
+ q_id = blkg.q.id.value_()
+ if blkg.pd[plid]:
+ root_iocg = container_of(blkg.pd[plid], 'struct ioc_gq', 'pd')
+ ioc = root_iocg.ioc
+ break
+ except:
+ pass
+
+if ioc is None:
+ err(f'Could not find ioc for {devname}');
+
+# Keep printing
+while True:
+ now = time.time()
+ iocstat = IocStat(ioc)
+ output = ''
+
+ if table_fmt:
+ output += '\n' + iocstat.table_preamble_str()
+ output += '\n' + iocstat.table_header_str()
+ else:
+ output += json.dumps(iocstat.dict(now))
+
+ for path, blkg in BlkgIterator(blkcg_root, q_id):
+ if filter_re and not filter_re.match(path):
+ continue
+ if not blkg.pd[plid]:
+ continue
+
+ iocg = container_of(blkg.pd[plid], 'struct ioc_gq', 'pd')
+ iocg_stat = IocgStat(iocg)
+
+ if not filter_re and not iocg_stat.is_active:
+ continue
+
+ if table_fmt:
+ output += '\n' + iocg_stat.table_row_str(path)
+ else:
+ output += '\n' + json.dumps(iocg_stat.dict(now, path))
+
+ print(output)
+ sys.stdout.flush()
+ time.sleep(interval)
diff --git a/tools/crypto/getstat.c b/tools/crypto/getstat.c
deleted file mode 100644
index 9e8ff76420fa..000000000000
--- a/tools/crypto/getstat.c
+++ /dev/null
@@ -1,294 +0,0 @@
-/* Heavily copied from libkcapi 2015 - 2017, Stephan Mueller <smueller@chronox.de> */
-#include <errno.h>
-#include <linux/cryptouser.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#define CR_RTA(x) ((struct rtattr *)(((char *)(x)) + NLMSG_ALIGN(sizeof(struct crypto_user_alg))))
-
-static int get_stat(const char *drivername)
-{
- struct {
- struct nlmsghdr n;
- struct crypto_user_alg cru;
- } req;
- struct sockaddr_nl nl;
- int sd = 0, ret;
- socklen_t addr_len;
- struct iovec iov;
- struct msghdr msg;
- char buf[4096];
- struct nlmsghdr *res_n = (struct nlmsghdr *)buf;
- struct crypto_user_alg *cru_res = NULL;
- int res_len = 0;
- struct rtattr *tb[CRYPTOCFGA_MAX + 1];
- struct rtattr *rta;
- struct nlmsgerr *errmsg;
-
- memset(&req, 0, sizeof(req));
- memset(&buf, 0, sizeof(buf));
- memset(&msg, 0, sizeof(msg));
-
- req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.cru));
- req.n.nlmsg_flags = NLM_F_REQUEST;
- req.n.nlmsg_type = CRYPTO_MSG_GETSTAT;
- req.n.nlmsg_seq = time(NULL);
-
- strncpy(req.cru.cru_driver_name, drivername, strlen(drivername));
-
- sd = socket(AF_NETLINK, SOCK_RAW, NETLINK_CRYPTO);
- if (sd < 0) {
- fprintf(stderr, "Netlink error: cannot open netlink socket");
- return -errno;
- }
- memset(&nl, 0, sizeof(nl));
- nl.nl_family = AF_NETLINK;
- if (bind(sd, (struct sockaddr *)&nl, sizeof(nl)) < 0) {
- ret = -errno;
- fprintf(stderr, "Netlink error: cannot bind netlink socket");
- goto out;
- }
-
- /* sanity check that netlink socket was successfully opened */
- addr_len = sizeof(nl);
- if (getsockname(sd, (struct sockaddr *)&nl, &addr_len) < 0) {
- ret = -errno;
- printf("Netlink error: cannot getsockname");
- goto out;
- }
- if (addr_len != sizeof(nl)) {
- ret = -errno;
- printf("Netlink error: wrong address length %d", addr_len);
- goto out;
- }
- if (nl.nl_family != AF_NETLINK) {
- ret = -errno;
- printf("Netlink error: wrong address family %d",
- nl.nl_family);
- goto out;
- }
-
- memset(&nl, 0, sizeof(nl));
- nl.nl_family = AF_NETLINK;
- iov.iov_base = (void *)&req.n;
- iov.iov_len = req.n.nlmsg_len;
- msg.msg_name = &nl;
- msg.msg_namelen = sizeof(nl);
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- if (sendmsg(sd, &msg, 0) < 0) {
- ret = -errno;
- printf("Netlink error: sendmsg failed");
- goto out;
- }
- memset(buf, 0, sizeof(buf));
- iov.iov_base = buf;
- while (1) {
- iov.iov_len = sizeof(buf);
- ret = recvmsg(sd, &msg, 0);
- if (ret < 0) {
- if (errno == EINTR || errno == EAGAIN)
- continue;
- ret = -errno;
- printf("Netlink error: netlink receive error");
- goto out;
- }
- if (ret == 0) {
- ret = -errno;
- printf("Netlink error: no data");
- goto out;
- }
- if (ret > sizeof(buf)) {
- ret = -errno;
- printf("Netlink error: received too much data");
- goto out;
- }
- break;
- }
-
- ret = -EFAULT;
- res_len = res_n->nlmsg_len;
- if (res_n->nlmsg_type == NLMSG_ERROR) {
- errmsg = NLMSG_DATA(res_n);
- fprintf(stderr, "Fail with %d\n", errmsg->error);
- ret = errmsg->error;
- goto out;
- }
-
- if (res_n->nlmsg_type == CRYPTO_MSG_GETSTAT) {
- cru_res = NLMSG_DATA(res_n);
- res_len -= NLMSG_SPACE(sizeof(*cru_res));
- }
- if (res_len < 0) {
- printf("Netlink error: nlmsg len %d\n", res_len);
- goto out;
- }
-
- if (!cru_res) {
- ret = -EFAULT;
- printf("Netlink error: no cru_res\n");
- goto out;
- }
-
- rta = CR_RTA(cru_res);
- memset(tb, 0, sizeof(struct rtattr *) * (CRYPTOCFGA_MAX + 1));
- while (RTA_OK(rta, res_len)) {
- if ((rta->rta_type <= CRYPTOCFGA_MAX) && (!tb[rta->rta_type]))
- tb[rta->rta_type] = rta;
- rta = RTA_NEXT(rta, res_len);
- }
- if (res_len) {
- printf("Netlink error: unprocessed data %d",
- res_len);
- goto out;
- }
-
- if (tb[CRYPTOCFGA_STAT_HASH]) {
- struct rtattr *rta = tb[CRYPTOCFGA_STAT_HASH];
- struct crypto_stat_hash *rhash =
- (struct crypto_stat_hash *)RTA_DATA(rta);
- printf("%s\tHash\n\tHash: %llu bytes: %llu\n\tErrors: %llu\n",
- drivername,
- rhash->stat_hash_cnt, rhash->stat_hash_tlen,
- rhash->stat_err_cnt);
- } else if (tb[CRYPTOCFGA_STAT_COMPRESS]) {
- struct rtattr *rta = tb[CRYPTOCFGA_STAT_COMPRESS];
- struct crypto_stat_compress *rblk =
- (struct crypto_stat_compress *)RTA_DATA(rta);
- printf("%s\tCompress\n\tCompress: %llu bytes: %llu\n\tDecompress: %llu bytes: %llu\n\tErrors: %llu\n",
- drivername,
- rblk->stat_compress_cnt, rblk->stat_compress_tlen,
- rblk->stat_decompress_cnt, rblk->stat_decompress_tlen,
- rblk->stat_err_cnt);
- } else if (tb[CRYPTOCFGA_STAT_ACOMP]) {
- struct rtattr *rta = tb[CRYPTOCFGA_STAT_ACOMP];
- struct crypto_stat_compress *rcomp =
- (struct crypto_stat_compress *)RTA_DATA(rta);
- printf("%s\tACompress\n\tCompress: %llu bytes: %llu\n\tDecompress: %llu bytes: %llu\n\tErrors: %llu\n",
- drivername,
- rcomp->stat_compress_cnt, rcomp->stat_compress_tlen,
- rcomp->stat_decompress_cnt, rcomp->stat_decompress_tlen,
- rcomp->stat_err_cnt);
- } else if (tb[CRYPTOCFGA_STAT_AEAD]) {
- struct rtattr *rta = tb[CRYPTOCFGA_STAT_AEAD];
- struct crypto_stat_aead *raead =
- (struct crypto_stat_aead *)RTA_DATA(rta);
- printf("%s\tAEAD\n\tEncrypt: %llu bytes: %llu\n\tDecrypt: %llu bytes: %llu\n\tErrors: %llu\n",
- drivername,
- raead->stat_encrypt_cnt, raead->stat_encrypt_tlen,
- raead->stat_decrypt_cnt, raead->stat_decrypt_tlen,
- raead->stat_err_cnt);
- } else if (tb[CRYPTOCFGA_STAT_BLKCIPHER]) {
- struct rtattr *rta = tb[CRYPTOCFGA_STAT_BLKCIPHER];
- struct crypto_stat_cipher *rblk =
- (struct crypto_stat_cipher *)RTA_DATA(rta);
- printf("%s\tCipher\n\tEncrypt: %llu bytes: %llu\n\tDecrypt: %llu bytes: %llu\n\tErrors: %llu\n",
- drivername,
- rblk->stat_encrypt_cnt, rblk->stat_encrypt_tlen,
- rblk->stat_decrypt_cnt, rblk->stat_decrypt_tlen,
- rblk->stat_err_cnt);
- } else if (tb[CRYPTOCFGA_STAT_AKCIPHER]) {
- struct rtattr *rta = tb[CRYPTOCFGA_STAT_AKCIPHER];
- struct crypto_stat_akcipher *rblk =
- (struct crypto_stat_akcipher *)RTA_DATA(rta);
- printf("%s\tAkcipher\n\tEncrypt: %llu bytes: %llu\n\tDecrypt: %llu bytes: %llu\n\tSign: %llu\n\tVerify: %llu\n\tErrors: %llu\n",
- drivername,
- rblk->stat_encrypt_cnt, rblk->stat_encrypt_tlen,
- rblk->stat_decrypt_cnt, rblk->stat_decrypt_tlen,
- rblk->stat_sign_cnt, rblk->stat_verify_cnt,
- rblk->stat_err_cnt);
- } else if (tb[CRYPTOCFGA_STAT_CIPHER]) {
- struct rtattr *rta = tb[CRYPTOCFGA_STAT_CIPHER];
- struct crypto_stat_cipher *rblk =
- (struct crypto_stat_cipher *)RTA_DATA(rta);
- printf("%s\tcipher\n\tEncrypt: %llu bytes: %llu\n\tDecrypt: %llu bytes: %llu\n\tErrors: %llu\n",
- drivername,
- rblk->stat_encrypt_cnt, rblk->stat_encrypt_tlen,
- rblk->stat_decrypt_cnt, rblk->stat_decrypt_tlen,
- rblk->stat_err_cnt);
- } else if (tb[CRYPTOCFGA_STAT_RNG]) {
- struct rtattr *rta = tb[CRYPTOCFGA_STAT_RNG];
- struct crypto_stat_rng *rrng =
- (struct crypto_stat_rng *)RTA_DATA(rta);
- printf("%s\tRNG\n\tSeed: %llu\n\tGenerate: %llu bytes: %llu\n\tErrors: %llu\n",
- drivername,
- rrng->stat_seed_cnt,
- rrng->stat_generate_cnt, rrng->stat_generate_tlen,
- rrng->stat_err_cnt);
- } else if (tb[CRYPTOCFGA_STAT_KPP]) {
- struct rtattr *rta = tb[CRYPTOCFGA_STAT_KPP];
- struct crypto_stat_kpp *rkpp =
- (struct crypto_stat_kpp *)RTA_DATA(rta);
- printf("%s\tKPP\n\tSetsecret: %llu\n\tGenerate public key: %llu\n\tCompute_shared_secret: %llu\n\tErrors: %llu\n",
- drivername,
- rkpp->stat_setsecret_cnt,
- rkpp->stat_generate_public_key_cnt,
- rkpp->stat_compute_shared_secret_cnt,
- rkpp->stat_err_cnt);
- } else {
- fprintf(stderr, "%s is of an unknown algorithm\n", drivername);
- }
- ret = 0;
-out:
- close(sd);
- return ret;
-}
-
-int main(int argc, const char *argv[])
-{
- char buf[4096];
- FILE *procfd;
- int i, lastspace;
- int ret;
-
- procfd = fopen("/proc/crypto", "r");
- if (!procfd) {
- ret = errno;
- fprintf(stderr, "Cannot open /proc/crypto %s\n", strerror(errno));
- return ret;
- }
- if (argc > 1) {
- if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
- printf("Usage: %s [-h|--help] display this help\n", argv[0]);
- printf("Usage: %s display all crypto statistics\n", argv[0]);
- printf("Usage: %s drivername1 drivername2 ... = display crypto statistics about drivername1 ...\n", argv[0]);
- return 0;
- }
- for (i = 1; i < argc; i++) {
- ret = get_stat(argv[i]);
- if (ret) {
- fprintf(stderr, "Failed with %s\n", strerror(-ret));
- return ret;
- }
- }
- return 0;
- }
-
- while (fgets(buf, sizeof(buf), procfd)) {
- if (!strncmp(buf, "driver", 6)) {
- lastspace = 0;
- i = 0;
- while (i < strlen(buf)) {
- i++;
- if (buf[i] == ' ')
- lastspace = i;
- }
- buf[strlen(buf) - 1] = '\0';
- ret = get_stat(buf + lastspace + 1);
- if (ret) {
- fprintf(stderr, "Failed with %s\n", strerror(-ret));
- goto out;
- }
- }
- }
-out:
- fclose(procfd);
- return ret;
-}
diff --git a/tools/firewire/nosy-dump.c b/tools/firewire/nosy-dump.c
index 3179c711bd65..156e0356e814 100644
--- a/tools/firewire/nosy-dump.c
+++ b/tools/firewire/nosy-dump.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* nosy-dump - Interface to snoop mode driver for TI PCILynx 1394 controllers
* Copyright (C) 2002-2006 Kristian Høgsberg
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <byteswap.h>
diff --git a/tools/firmware/Makefile b/tools/firmware/Makefile
index d329825aa31b..cfb297e6ef5a 100644
--- a/tools/firmware/Makefile
+++ b/tools/firmware/Makefile
@@ -10,4 +10,4 @@ all: ihex2fw
clean:
$(RM) ihex2fw
-.PHONY: all clean \ No newline at end of file
+.PHONY: all clean
diff --git a/tools/firmware/ihex2fw.c b/tools/firmware/ihex2fw.c
index 8925b60e51f5..2ebed47680b1 100644
--- a/tools/firmware/ihex2fw.c
+++ b/tools/firmware/ihex2fw.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Parser/loader for IHEX formatted data.
*
* Copyright © 2008 David Woodhouse <dwmw2@infradead.org>
* Copyright © 2005 Jan Harkes <jaharkes@cs.cmu.edu>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <stdint.h>
diff --git a/tools/gpio/.gitignore b/tools/gpio/.gitignore
index 9e9dd4b681b2..a94c0e83b209 100644
--- a/tools/gpio/.gitignore
+++ b/tools/gpio/.gitignore
@@ -1,4 +1,4 @@
gpio-event-mon
gpio-hammer
lsgpio
-
+include/linux/gpio.h
diff --git a/tools/gpio/gpio-event-mon.c b/tools/gpio/gpio-event-mon.c
index c864544efe05..30ed0e06f52a 100644
--- a/tools/gpio/gpio-event-mon.c
+++ b/tools/gpio/gpio-event-mon.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* gpio-event-mon - monitor GPIO line events from userspace
*
* Copyright (C) 2016 Linus Walleij
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
* Usage:
* gpio-event-mon -n <device-name> -o <offset>
*/
diff --git a/tools/gpio/gpio-hammer.c b/tools/gpio/gpio-hammer.c
index 4bcb234c0fca..0e0060a6eb34 100644
--- a/tools/gpio/gpio-hammer.c
+++ b/tools/gpio/gpio-hammer.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* gpio-hammer - example swiss army knife to shake GPIO lines on a system
*
* Copyright (C) 2016 Linus Walleij
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
* Usage:
* gpio-hammer -n <device-name> -o <offset1> -o <offset2>
*/
diff --git a/tools/gpio/gpio-utils.c b/tools/gpio/gpio-utils.c
index cf7e2f3419ee..53470de6a502 100644
--- a/tools/gpio/gpio-utils.c
+++ b/tools/gpio/gpio-utils.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* GPIO tools - helpers library for the GPIO tools
*
* Copyright (C) 2015 Linus Walleij
* Copyright (C) 2016 Bamvor Jian Zhang
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
*/
#include <unistd.h>
diff --git a/tools/gpio/gpio-utils.h b/tools/gpio/gpio-utils.h
index 344ea041f8d4..cf37f13f3dcb 100644
--- a/tools/gpio/gpio-utils.h
+++ b/tools/gpio/gpio-utils.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* GPIO tools - utility helpers library for the GPIO tools
*
@@ -7,9 +8,6 @@
* Copyright (c) 2010 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
* Copyright (c) 2008 Jonathan Cameron
* *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
*/
#ifndef _GPIO_UTILS_H_
#define _GPIO_UTILS_H_
diff --git a/tools/gpio/lsgpio.c b/tools/gpio/lsgpio.c
index eb3f56efd215..e1430f504c13 100644
--- a/tools/gpio/lsgpio.c
+++ b/tools/gpio/lsgpio.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* lsgpio - example on how to list the GPIO lines on a system
*
* Copyright (C) 2015 Linus Walleij
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
* Usage:
* lsgpio <-n device-name>
*/
diff --git a/tools/hv/hv_fcopy_daemon.c b/tools/hv/hv_fcopy_daemon.c
index 8ff8cb1a11f4..aea2d91ab364 100644
--- a/tools/hv/hv_fcopy_daemon.c
+++ b/tools/hv/hv_fcopy_daemon.c
@@ -1,19 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* An implementation of host to guest copy functionality for Linux.
*
* Copyright (C) 2014, Microsoft, Inc.
*
* Author : K. Y. Srinivasan <kys@microsoft.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
- * NON INFRINGEMENT. See the GNU General Public License for more
- * details.
*/
diff --git a/tools/hv/hv_get_dhcp_info.sh b/tools/hv/hv_get_dhcp_info.sh
index c38686c44656..2f2a3c7df3de 100755
--- a/tools/hv/hv_get_dhcp_info.sh
+++ b/tools/hv/hv_get_dhcp_info.sh
@@ -13,7 +13,7 @@
# the script prints the string "Disabled" to stdout.
#
# Each Distro is expected to implement this script in a distro specific
-# fashion. For instance on Distros that ship with Network Manager enabled,
+# fashion. For instance, on Distros that ship with Network Manager enabled,
# this script can be based on the Network Manager APIs for retrieving DHCP
# information.
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index d7e06fe0270e..e9ef4ca6a655 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -700,7 +700,7 @@ static void kvp_get_ipconfig_info(char *if_name,
/*
- * Gather the DNS state.
+ * Gather the DNS state.
* Since there is no standard way to get this information
* across various distributions of interest; we just invoke
* an external script that needs to be ported across distros
@@ -809,7 +809,7 @@ kvp_get_ip_info(int family, char *if_name, int op,
int sn_offset = 0;
int error = 0;
char *buffer;
- struct hv_kvp_ipaddr_value *ip_buffer;
+ struct hv_kvp_ipaddr_value *ip_buffer = NULL;
char cidr_mask[5]; /* /xyz */
int weight;
int i;
@@ -1051,7 +1051,7 @@ static int parse_ip_val_buffer(char *in_buf, int *offset,
char *start;
/*
- * in_buf has sequence of characters that are seperated by
+ * in_buf has sequence of characters that are separated by
* the character ';'. The last sequence does not have the
* terminating ";" character.
*/
@@ -1386,6 +1386,8 @@ int main(int argc, char *argv[])
daemonize = 0;
break;
case 'h':
+ print_usage(argv);
+ exit(0);
default:
print_usage(argv);
exit(EXIT_FAILURE);
@@ -1490,7 +1492,7 @@ int main(int argc, char *argv[])
case KVP_OP_GET_IP_INFO:
kvp_ip_val = &hv_msg->body.kvp_ip_val;
- error = kvp_mac_to_ip(kvp_ip_val);
+ error = kvp_mac_to_ip(kvp_ip_val);
if (error)
hv_msg->error = error;
diff --git a/tools/hv/hv_set_ifconfig.sh b/tools/hv/hv_set_ifconfig.sh
index 7ed9f85ef908..d10fe35b7f25 100755
--- a/tools/hv/hv_set_ifconfig.sh
+++ b/tools/hv/hv_set_ifconfig.sh
@@ -12,7 +12,7 @@
# be used to configure the interface.
#
# Each Distro is expected to implement this script in a distro specific
-# fashion. For instance on Distros that ship with Network Manager enabled,
+# fashion. For instance, on Distros that ship with Network Manager enabled,
# this script can be based on the Network Manager APIs for configuring the
# interface.
#
diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c
index b13300172762..92902a88f671 100644
--- a/tools/hv/hv_vss_daemon.c
+++ b/tools/hv/hv_vss_daemon.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* An implementation of the host initiated guest snapshot for Hyper-V.
*
- *
* Copyright (C) 2013, Microsoft, Inc.
* Author : K. Y. Srinivasan <kys@microsoft.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
- * NON INFRINGEMENT. See the GNU General Public License for more
- * details.
- *
*/
@@ -53,7 +42,7 @@ static int vss_do_freeze(char *dir, unsigned int cmd)
* If a partition is mounted more than once, only the first
* FREEZE/THAW can succeed and the later ones will get
* EBUSY/EINVAL respectively: there could be 2 cases:
- * 1) a user may mount the same partition to differnt directories
+ * 1) a user may mount the same partition to different directories
* by mistake or on purpose;
* 2) The subvolume of btrfs appears to have the same partition
* mounted more than once.
@@ -229,6 +218,8 @@ int main(int argc, char *argv[])
daemonize = 0;
break;
case 'h':
+ print_usage(argv);
+ exit(0);
default:
print_usage(argv);
exit(EXIT_FAILURE);
diff --git a/tools/hv/lsvmbus b/tools/hv/lsvmbus
index 55e7374bade0..099f2c44dbed 100644
--- a/tools/hv/lsvmbus
+++ b/tools/hv/lsvmbus
@@ -4,10 +4,10 @@
import os
from optparse import OptionParser
+help_msg = "print verbose messages. Try -vv, -vvv for more verbose messages"
parser = OptionParser()
-parser.add_option("-v", "--verbose", dest="verbose",
- help="print verbose messages. Try -vv, -vvv for \
- more verbose messages", action="count")
+parser.add_option(
+ "-v", "--verbose", dest="verbose", help=help_msg, action="count")
(options, args) = parser.parse_args()
@@ -21,27 +21,28 @@ if not os.path.isdir(vmbus_sys_path):
exit(-1)
vmbus_dev_dict = {
- '{0e0b6031-5213-4934-818b-38d90ced39db}' : '[Operating system shutdown]',
- '{9527e630-d0ae-497b-adce-e80ab0175caf}' : '[Time Synchronization]',
- '{57164f39-9115-4e78-ab55-382f3bd5422d}' : '[Heartbeat]',
- '{a9a0f4e7-5a45-4d96-b827-8a841e8c03e6}' : '[Data Exchange]',
- '{35fa2e29-ea23-4236-96ae-3a6ebacba440}' : '[Backup (volume checkpoint)]',
- '{34d14be3-dee4-41c8-9ae7-6b174977c192}' : '[Guest services]',
- '{525074dc-8985-46e2-8057-a307dc18a502}' : '[Dynamic Memory]',
- '{cfa8b69e-5b4a-4cc0-b98b-8ba1a1f3f95a}' : 'Synthetic mouse',
- '{f912ad6d-2b17-48ea-bd65-f927a61c7684}' : 'Synthetic keyboard',
- '{da0a7802-e377-4aac-8e77-0558eb1073f8}' : 'Synthetic framebuffer adapter',
- '{f8615163-df3e-46c5-913f-f2d2f965ed0e}' : 'Synthetic network adapter',
- '{32412632-86cb-44a2-9b5c-50d1417354f5}' : 'Synthetic IDE Controller',
- '{ba6163d9-04a1-4d29-b605-72e2ffb1dc7f}' : 'Synthetic SCSI Controller',
- '{2f9bcc4a-0069-4af3-b76b-6fd0be528cda}' : 'Synthetic fiber channel adapter',
- '{8c2eaf3d-32a7-4b09-ab99-bd1f1c86b501}' : 'Synthetic RDMA adapter',
- '{44c4f61d-4444-4400-9d52-802e27ede19f}' : 'PCI Express pass-through',
- '{276aacf4-ac15-426c-98dd-7521ad3f01fe}' : '[Reserved system device]',
- '{f8e65716-3cb3-4a06-9a60-1889c5cccab5}' : '[Reserved system device]',
- '{3375baf4-9e15-4b30-b765-67acb10d607b}' : '[Reserved system device]',
+ '{0e0b6031-5213-4934-818b-38d90ced39db}': '[Operating system shutdown]',
+ '{9527e630-d0ae-497b-adce-e80ab0175caf}': '[Time Synchronization]',
+ '{57164f39-9115-4e78-ab55-382f3bd5422d}': '[Heartbeat]',
+ '{a9a0f4e7-5a45-4d96-b827-8a841e8c03e6}': '[Data Exchange]',
+ '{35fa2e29-ea23-4236-96ae-3a6ebacba440}': '[Backup (volume checkpoint)]',
+ '{34d14be3-dee4-41c8-9ae7-6b174977c192}': '[Guest services]',
+ '{525074dc-8985-46e2-8057-a307dc18a502}': '[Dynamic Memory]',
+ '{cfa8b69e-5b4a-4cc0-b98b-8ba1a1f3f95a}': 'Synthetic mouse',
+ '{f912ad6d-2b17-48ea-bd65-f927a61c7684}': 'Synthetic keyboard',
+ '{da0a7802-e377-4aac-8e77-0558eb1073f8}': 'Synthetic framebuffer adapter',
+ '{f8615163-df3e-46c5-913f-f2d2f965ed0e}': 'Synthetic network adapter',
+ '{32412632-86cb-44a2-9b5c-50d1417354f5}': 'Synthetic IDE Controller',
+ '{ba6163d9-04a1-4d29-b605-72e2ffb1dc7f}': 'Synthetic SCSI Controller',
+ '{2f9bcc4a-0069-4af3-b76b-6fd0be528cda}': 'Synthetic fiber channel adapter',
+ '{8c2eaf3d-32a7-4b09-ab99-bd1f1c86b501}': 'Synthetic RDMA adapter',
+ '{44c4f61d-4444-4400-9d52-802e27ede19f}': 'PCI Express pass-through',
+ '{276aacf4-ac15-426c-98dd-7521ad3f01fe}': '[Reserved system device]',
+ '{f8e65716-3cb3-4a06-9a60-1889c5cccab5}': '[Reserved system device]',
+ '{3375baf4-9e15-4b30-b765-67acb10d607b}': '[Reserved system device]',
}
+
def get_vmbus_dev_attr(dev_name, attr):
try:
f = open('%s/%s/%s' % (vmbus_sys_path, dev_name, attr), 'r')
@@ -52,6 +53,7 @@ def get_vmbus_dev_attr(dev_name, attr):
return lines
+
class VMBus_Dev:
pass
@@ -66,12 +68,13 @@ for f in os.listdir(vmbus_sys_path):
chn_vp_mapping = get_vmbus_dev_attr(f, 'channel_vp_mapping')
chn_vp_mapping = [c.strip() for c in chn_vp_mapping]
- chn_vp_mapping = sorted(chn_vp_mapping,
- key = lambda c : int(c.split(':')[0]))
+ chn_vp_mapping = sorted(
+ chn_vp_mapping, key=lambda c: int(c.split(':')[0]))
- chn_vp_mapping = ['\tRel_ID=%s, target_cpu=%s' %
- (c.split(':')[0], c.split(':')[1])
- for c in chn_vp_mapping]
+ chn_vp_mapping = [
+ '\tRel_ID=%s, target_cpu=%s' %
+ (c.split(':')[0], c.split(':')[1]) for c in chn_vp_mapping
+ ]
d = VMBus_Dev()
d.sysfs_path = '%s/%s' % (vmbus_sys_path, f)
d.vmbus_id = vmbus_id
@@ -85,7 +88,7 @@ for f in os.listdir(vmbus_sys_path):
vmbus_dev_list.append(d)
-vmbus_dev_list = sorted(vmbus_dev_list, key = lambda d : int(d.vmbus_id))
+vmbus_dev_list = sorted(vmbus_dev_list, key=lambda d: int(d.vmbus_id))
format0 = '%2s: %s'
format1 = '%2s: Class_ID = %s - %s\n%s'
@@ -95,9 +98,15 @@ for d in vmbus_dev_list:
if verbose == 0:
print(('VMBUS ID ' + format0) % (d.vmbus_id, d.dev_desc))
elif verbose == 1:
- print (('VMBUS ID ' + format1) % \
- (d.vmbus_id, d.class_id, d.dev_desc, d.chn_vp_mapping))
+ print(
+ ('VMBUS ID ' + format1) %
+ (d.vmbus_id, d.class_id, d.dev_desc, d.chn_vp_mapping)
+ )
else:
- print (('VMBUS ID ' + format2) % \
- (d.vmbus_id, d.class_id, d.dev_desc, \
- d.device_id, d.sysfs_path, d.chn_vp_mapping))
+ print(
+ ('VMBUS ID ' + format2) %
+ (
+ d.vmbus_id, d.class_id, d.dev_desc,
+ d.device_id, d.sysfs_path, d.chn_vp_mapping
+ )
+ )
diff --git a/tools/iio/.gitignore b/tools/iio/.gitignore
new file mode 100644
index 000000000000..3758202618bd
--- /dev/null
+++ b/tools/iio/.gitignore
@@ -0,0 +1,4 @@
+iio_event_monitor
+iio_generic_buffer
+lsiio
+include/
diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c
index 7bf9bde28bcc..f115d166c985 100644
--- a/tools/iio/iio_event_monitor.c
+++ b/tools/iio/iio_event_monitor.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Industrialio event test code.
*
* Copyright (c) 2011-2012 Lars-Peter Clausen <lars@metafoo.de>
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
* This program is primarily intended as an example application.
* Reads the current buffer setup from sysfs and starts a short capture
* from the specified device, pretty printing the result after appropriate
diff --git a/tools/iio/iio_generic_buffer.c b/tools/iio/iio_generic_buffer.c
index 84545666a09c..34d63bcebcd2 100644
--- a/tools/iio/iio_generic_buffer.c
+++ b/tools/iio/iio_generic_buffer.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Industrialio buffer test code.
*
* Copyright (c) 2008 Jonathan Cameron
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
* This program is primarily intended as an example application.
* Reads the current buffer setup from sysfs and starts a short capture
* from the specified device, pretty printing the result after appropriate
@@ -15,7 +12,6 @@
* generic_buffer -n <device_name> -t <trigger_name>
* If trigger name is not specified the program assumes you want a dataready
* trigger associated with the device and goes looking for it.
- *
*/
#include <unistd.h>
diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c
index 7a6d61c6c012..7399eb7f1378 100644
--- a/tools/iio/iio_utils.c
+++ b/tools/iio/iio_utils.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* IIO - useful set of util functionality
*
* Copyright (c) 2008 Jonathan Cameron
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
*/
#include <string.h>
#include <stdlib.h>
@@ -159,9 +156,9 @@ int iioutils_get_type(unsigned *is_signed, unsigned *bytes, unsigned *bits_used,
*be = (endianchar == 'b');
*bytes = padint / 8;
if (*bits_used == 64)
- *mask = ~0;
+ *mask = ~(0ULL);
else
- *mask = (1ULL << *bits_used) - 1;
+ *mask = (1ULL << *bits_used) - 1ULL;
*is_signed = (signchar == 's');
if (fclose(sysfsfp)) {
diff --git a/tools/iio/iio_utils.h b/tools/iio/iio_utils.h
index 8b379da26e35..74bde4fde2c8 100644
--- a/tools/iio/iio_utils.h
+++ b/tools/iio/iio_utils.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _IIO_UTILS_H_
#define _IIO_UTILS_H_
/* IIO - useful set of util functionality
*
* Copyright (c) 2008 Jonathan Cameron
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
*/
#include <stdint.h>
diff --git a/tools/iio/lsiio.c b/tools/iio/lsiio.c
index ab0f5cf16025..2cf56fb2449b 100644
--- a/tools/iio/lsiio.c
+++ b/tools/iio/lsiio.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Industrial I/O utilities - lsiio.c
*
* Copyright (c) 2010 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
*/
#include <string.h>
diff --git a/tools/include/asm-generic/barrier.h b/tools/include/asm-generic/barrier.h
index 52278d880a61..6ef36e920ea8 100644
--- a/tools/include/asm-generic/barrier.h
+++ b/tools/include/asm-generic/barrier.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copied from the kernel sources to tools/perf/:
*
@@ -8,11 +9,6 @@
*
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public Licence
- * as published by the Free Software Foundation; either version
- * 2 of the Licence, or (at your option) any later version.
*/
#ifndef __TOOLS_LINUX_ASM_GENERIC_BARRIER_H
#define __TOOLS_LINUX_ASM_GENERIC_BARRIER_H
diff --git a/tools/include/linux/bitops.h b/tools/include/linux/bitops.h
index 0b0ef3abc966..140c8362f113 100644
--- a/tools/include/linux/bitops.h
+++ b/tools/include/linux/bitops.h
@@ -3,6 +3,7 @@
#define _TOOLS_LINUX_BITOPS_H_
#include <asm/types.h>
+#include <limits.h>
#ifndef __WORDSIZE
#define __WORDSIZE (__SIZEOF_LONG__ * 8)
#endif
diff --git a/tools/include/linux/bits.h b/tools/include/linux/bits.h
index 2b7b532c1d51..669d69441a62 100644
--- a/tools/include/linux/bits.h
+++ b/tools/include/linux/bits.h
@@ -1,13 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __LINUX_BITS_H
#define __LINUX_BITS_H
+
+#include <linux/const.h>
#include <asm/bitsperlong.h>
-#define BIT(nr) (1UL << (nr))
-#define BIT_ULL(nr) (1ULL << (nr))
-#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
+#define BIT(nr) (UL(1) << (nr))
+#define BIT_ULL(nr) (ULL(1) << (nr))
+#define BIT_MASK(nr) (UL(1) << ((nr) % BITS_PER_LONG))
#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
-#define BIT_ULL_MASK(nr) (1ULL << ((nr) % BITS_PER_LONG_LONG))
+#define BIT_ULL_MASK(nr) (ULL(1) << ((nr) % BITS_PER_LONG_LONG))
#define BIT_ULL_WORD(nr) ((nr) / BITS_PER_LONG_LONG)
#define BITS_PER_BYTE 8
@@ -17,10 +19,11 @@
* GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000.
*/
#define GENMASK(h, l) \
- (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
+ (((~UL(0)) - (UL(1) << (l)) + 1) & \
+ (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
#define GENMASK_ULL(h, l) \
- (((~0ULL) - (1ULL << (l)) + 1) & \
- (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h))))
+ (((~ULL(0)) - (ULL(1) << (l)) + 1) & \
+ (~ULL(0) >> (BITS_PER_LONG_LONG - 1 - (h))))
#endif /* __LINUX_BITS_H */
diff --git a/tools/include/linux/compiler-gcc.h b/tools/include/linux/compiler-gcc.h
index 0d35f18006a1..95c072b70d0e 100644
--- a/tools/include/linux/compiler-gcc.h
+++ b/tools/include/linux/compiler-gcc.h
@@ -6,9 +6,11 @@
/*
* Common definitions for all gcc versions go here.
*/
+#ifndef GCC_VERSION
#define GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
+#endif
#if GCC_VERSION >= 70000 && !defined(__CHECKER__)
# define __fallthrough __attribute__ ((fallthrough))
diff --git a/tools/include/linux/const.h b/tools/include/linux/const.h
new file mode 100644
index 000000000000..7b55a55f5911
--- /dev/null
+++ b/tools/include/linux/const.h
@@ -0,0 +1,9 @@
+#ifndef _LINUX_CONST_H
+#define _LINUX_CONST_H
+
+#include <uapi/linux/const.h>
+
+#define UL(x) (_UL(x))
+#define ULL(x) (_ULL(x))
+
+#endif /* _LINUX_CONST_H */
diff --git a/tools/include/linux/ctype.h b/tools/include/linux/ctype.h
new file mode 100644
index 000000000000..310090b4c474
--- /dev/null
+++ b/tools/include/linux/ctype.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_CTYPE_H
+#define _LINUX_CTYPE_H
+
+/*
+ * NOTE! This ctype does not handle EOF like the standard C
+ * library is required to.
+ */
+
+#define _U 0x01 /* upper */
+#define _L 0x02 /* lower */
+#define _D 0x04 /* digit */
+#define _C 0x08 /* cntrl */
+#define _P 0x10 /* punct */
+#define _S 0x20 /* white space (space/lf/tab) */
+#define _X 0x40 /* hex digit */
+#define _SP 0x80 /* hard space (0x20) */
+
+extern const unsigned char _ctype[];
+
+#define __ismask(x) (_ctype[(int)(unsigned char)(x)])
+
+#define isalnum(c) ((__ismask(c)&(_U|_L|_D)) != 0)
+#define isalpha(c) ((__ismask(c)&(_U|_L)) != 0)
+#define iscntrl(c) ((__ismask(c)&(_C)) != 0)
+static inline int __isdigit(int c)
+{
+ return '0' <= c && c <= '9';
+}
+#define isdigit(c) __isdigit(c)
+#define isgraph(c) ((__ismask(c)&(_P|_U|_L|_D)) != 0)
+#define islower(c) ((__ismask(c)&(_L)) != 0)
+#define isprint(c) ((__ismask(c)&(_P|_U|_L|_D|_SP)) != 0)
+#define ispunct(c) ((__ismask(c)&(_P)) != 0)
+/* Note: isspace() must return false for %NUL-terminator */
+#define isspace(c) ((__ismask(c)&(_S)) != 0)
+#define isupper(c) ((__ismask(c)&(_U)) != 0)
+#define isxdigit(c) ((__ismask(c)&(_D|_X)) != 0)
+
+#define isascii(c) (((unsigned char)(c))<=0x7f)
+#define toascii(c) (((unsigned char)(c))&0x7f)
+
+static inline unsigned char __tolower(unsigned char c)
+{
+ if (isupper(c))
+ c -= 'A'-'a';
+ return c;
+}
+
+static inline unsigned char __toupper(unsigned char c)
+{
+ if (islower(c))
+ c -= 'a'-'A';
+ return c;
+}
+
+#define tolower(c) __tolower(c)
+#define toupper(c) __toupper(c)
+
+/*
+ * Fast implementation of tolower() for internal usage. Do not use in your
+ * code.
+ */
+static inline char _tolower(const char c)
+{
+ return c | 0x20;
+}
+
+/* Fast check for octal digit */
+static inline int isodigit(const char c)
+{
+ return c >= '0' && c <= '7';
+}
+
+#endif
diff --git a/tools/include/linux/err.h b/tools/include/linux/err.h
index 2f5a12b88a86..25f2bb3a991d 100644
--- a/tools/include/linux/err.h
+++ b/tools/include/linux/err.h
@@ -20,7 +20,7 @@
* Userspace note:
* The same principle works for userspace, because 'error' pointers
* fall down to the unused hole far from user space, as described
- * in Documentation/x86/x86_64/mm.txt for x86_64 arch:
+ * in Documentation/x86/x86_64/mm.rst for x86_64 arch:
*
* 0000000000000000 - 00007fffffffffff (=47 bits) user space, different per mm hole caused by [48:63] sign extension
* ffffffffffe00000 - ffffffffffffffff (=2 MB) unused hole
diff --git a/tools/include/linux/kernel.h b/tools/include/linux/kernel.h
index 857d9e22826e..cba226948a0c 100644
--- a/tools/include/linux/kernel.h
+++ b/tools/include/linux/kernel.h
@@ -102,6 +102,7 @@
int vscnprintf(char *buf, size_t size, const char *fmt, va_list args);
int scnprintf(char * buf, size_t size, const char * fmt, ...);
+int scnprintf_pad(char * buf, size_t size, const char * fmt, ...);
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
diff --git a/tools/include/linux/log2.h b/tools/include/linux/log2.h
index 0325cefc2220..e20a67d538b8 100644
--- a/tools/include/linux/log2.h
+++ b/tools/include/linux/log2.h
@@ -1,12 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Integer base 2 logarithm calculation
*
* Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#ifndef _TOOLS_LINUX_LOG2_H
diff --git a/tools/include/linux/rbtree.h b/tools/include/linux/rbtree.h
index 8e9ed4786269..d83763a5327c 100644
--- a/tools/include/linux/rbtree.h
+++ b/tools/include/linux/rbtree.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
Red Black Trees
(C) 1999 Andrea Arcangeli <andrea@suse.de>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
linux/include/linux/rbtree.h
diff --git a/tools/include/linux/rbtree_augmented.h b/tools/include/linux/rbtree_augmented.h
index d008e1404580..ddd01006ece5 100644
--- a/tools/include/linux/rbtree_augmented.h
+++ b/tools/include/linux/rbtree_augmented.h
@@ -1,22 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
Red Black Trees
(C) 1999 Andrea Arcangeli <andrea@suse.de>
(C) 2002 David Woodhouse <dwmw2@infradead.org>
(C) 2012 Michel Lespinasse <walken@google.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
tools/linux/include/linux/rbtree_augmented.h
diff --git a/tools/include/linux/rcu.h b/tools/include/linux/rcu.h
index 7d02527e5bce..9554d3fa54f3 100644
--- a/tools/include/linux/rcu.h
+++ b/tools/include/linux/rcu.h
@@ -19,7 +19,7 @@ static inline bool rcu_is_watching(void)
return false;
}
-#define rcu_assign_pointer(p, v) ((p) = (v))
-#define RCU_INIT_POINTER(p, v) p=(v)
+#define rcu_assign_pointer(p, v) do { (p) = (v); } while (0)
+#define RCU_INIT_POINTER(p, v) do { (p) = (v); } while (0)
#endif
diff --git a/tools/include/linux/ring_buffer.h b/tools/include/linux/ring_buffer.h
index 9a083ae60473..6c02617377c2 100644
--- a/tools/include/linux/ring_buffer.h
+++ b/tools/include/linux/ring_buffer.h
@@ -2,6 +2,7 @@
#define _TOOLS_LINUX_RING_BUFFER_H_
#include <asm/barrier.h>
+#include <linux/perf_event.h>
/*
* Contract with kernel for walking the perf ring buffer from
diff --git a/tools/include/linux/sizes.h b/tools/include/linux/sizes.h
new file mode 100644
index 000000000000..1cbb4c4d016e
--- /dev/null
+++ b/tools/include/linux/sizes.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * include/linux/sizes.h
+ */
+#ifndef __LINUX_SIZES_H__
+#define __LINUX_SIZES_H__
+
+#include <linux/const.h>
+
+#define SZ_1 0x00000001
+#define SZ_2 0x00000002
+#define SZ_4 0x00000004
+#define SZ_8 0x00000008
+#define SZ_16 0x00000010
+#define SZ_32 0x00000020
+#define SZ_64 0x00000040
+#define SZ_128 0x00000080
+#define SZ_256 0x00000100
+#define SZ_512 0x00000200
+
+#define SZ_1K 0x00000400
+#define SZ_2K 0x00000800
+#define SZ_4K 0x00001000
+#define SZ_8K 0x00002000
+#define SZ_16K 0x00004000
+#define SZ_32K 0x00008000
+#define SZ_64K 0x00010000
+#define SZ_128K 0x00020000
+#define SZ_256K 0x00040000
+#define SZ_512K 0x00080000
+
+#define SZ_1M 0x00100000
+#define SZ_2M 0x00200000
+#define SZ_4M 0x00400000
+#define SZ_8M 0x00800000
+#define SZ_16M 0x01000000
+#define SZ_32M 0x02000000
+#define SZ_64M 0x04000000
+#define SZ_128M 0x08000000
+#define SZ_256M 0x10000000
+#define SZ_512M 0x20000000
+
+#define SZ_1G 0x40000000
+#define SZ_2G 0x80000000
+
+#define SZ_4G _AC(0x100000000, ULL)
+
+#endif /* __LINUX_SIZES_H__ */
diff --git a/tools/include/linux/string.h b/tools/include/linux/string.h
index 6c3e2cc274c5..980cb9266718 100644
--- a/tools/include/linux/string.h
+++ b/tools/include/linux/string.h
@@ -7,6 +7,9 @@
void *memdup(const void *src, size_t len);
+char **argv_split(const char *str, int *argcp);
+void argv_free(char **argv);
+
int strtobool(const char *s, bool *res);
/*
@@ -19,6 +22,8 @@ extern size_t strlcpy(char *dest, const char *src, size_t size);
char *str_error_r(int errnum, char *buf, size_t buflen);
+char *strreplace(char *s, char old, char new);
+
/**
* strstarts - does @str start with @prefix?
* @str: string to examine
@@ -29,4 +34,8 @@ static inline bool strstarts(const char *str, const char *prefix)
return strncmp(str, prefix, strlen(prefix)) == 0;
}
-#endif /* _LINUX_STRING_H_ */
+extern char * __must_check skip_spaces(const char *);
+
+extern char *strim(char *);
+
+#endif /* _TOOLS_LINUX_STRING_H_ */
diff --git a/tools/include/linux/zalloc.h b/tools/include/linux/zalloc.h
new file mode 100644
index 000000000000..81099c84043f
--- /dev/null
+++ b/tools/include/linux/zalloc.h
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: LGPL-2.1
+#ifndef __TOOLS_LINUX_ZALLOC_H
+#define __TOOLS_LINUX_ZALLOC_H
+
+#include <stddef.h>
+
+void *zalloc(size_t size);
+void __zfree(void **ptr);
+
+#define zfree(ptr) __zfree((void **)(ptr))
+
+#endif // __TOOLS_LINUX_ZALLOC_H
diff --git a/tools/include/uapi/asm-generic/mman-common.h b/tools/include/uapi/asm-generic/mman-common.h
index abd238d0f7a4..63b1f506ea67 100644
--- a/tools/include/uapi/asm-generic/mman-common.h
+++ b/tools/include/uapi/asm-generic/mman-common.h
@@ -19,15 +19,18 @@
#define MAP_TYPE 0x0f /* Mask for type of mapping */
#define MAP_FIXED 0x10 /* Interpret addr exactly */
#define MAP_ANONYMOUS 0x20 /* don't use a file */
-#ifdef CONFIG_MMAP_ALLOW_UNINITIALIZED
-# define MAP_UNINITIALIZED 0x4000000 /* For anonymous mmap, memory could be uninitialized */
-#else
-# define MAP_UNINITIALIZED 0x0 /* Don't support this flag */
-#endif
-/* 0x0100 - 0x80000 flags are defined in asm-generic/mman.h */
+/* 0x0100 - 0x4000 flags are defined in asm-generic/mman.h */
+#define MAP_POPULATE 0x008000 /* populate (prefault) pagetables */
+#define MAP_NONBLOCK 0x010000 /* do not block on IO */
+#define MAP_STACK 0x020000 /* give out an address that is best suited for process/thread stacks */
+#define MAP_HUGETLB 0x040000 /* create a huge page mapping */
+#define MAP_SYNC 0x080000 /* perform synchronous page faults for the mapping */
#define MAP_FIXED_NOREPLACE 0x100000 /* MAP_FIXED which doesn't unmap underlying mapping */
+#define MAP_UNINITIALIZED 0x4000000 /* For anonymous mmap, memory could be
+ * uninitialized */
+
/*
* Flags for mlock
*/
diff --git a/tools/include/uapi/asm-generic/mman.h b/tools/include/uapi/asm-generic/mman.h
index 36c197fc44a0..406f7718f9ad 100644
--- a/tools/include/uapi/asm-generic/mman.h
+++ b/tools/include/uapi/asm-generic/mman.h
@@ -9,13 +9,11 @@
#define MAP_EXECUTABLE 0x1000 /* mark it as an executable */
#define MAP_LOCKED 0x2000 /* pages are locked */
#define MAP_NORESERVE 0x4000 /* don't check for reservations */
-#define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */
-#define MAP_NONBLOCK 0x10000 /* do not block on IO */
-#define MAP_STACK 0x20000 /* give out an address that is best suited for process/thread stacks */
-#define MAP_HUGETLB 0x40000 /* create a huge page mapping */
-#define MAP_SYNC 0x80000 /* perform synchronous page faults for the mapping */
-/* Bits [26:31] are reserved, see mman-common.h for MAP_HUGETLB usage */
+/*
+ * Bits [26:31] are reserved, see asm-generic/hugetlb_encode.h
+ * for MAP_HUGETLB usage
+ */
#define MCL_CURRENT 1 /* lock all current mappings */
#define MCL_FUTURE 2 /* lock all future mappings */
diff --git a/tools/include/uapi/asm-generic/socket.h b/tools/include/uapi/asm-generic/socket.h
new file mode 100644
index 000000000000..77f7c1638eb1
--- /dev/null
+++ b/tools/include/uapi/asm-generic/socket.h
@@ -0,0 +1,147 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __ASM_GENERIC_SOCKET_H
+#define __ASM_GENERIC_SOCKET_H
+
+#include <linux/posix_types.h>
+#include <asm/sockios.h>
+
+/* For setsockopt(2) */
+#define SOL_SOCKET 1
+
+#define SO_DEBUG 1
+#define SO_REUSEADDR 2
+#define SO_TYPE 3
+#define SO_ERROR 4
+#define SO_DONTROUTE 5
+#define SO_BROADCAST 6
+#define SO_SNDBUF 7
+#define SO_RCVBUF 8
+#define SO_SNDBUFFORCE 32
+#define SO_RCVBUFFORCE 33
+#define SO_KEEPALIVE 9
+#define SO_OOBINLINE 10
+#define SO_NO_CHECK 11
+#define SO_PRIORITY 12
+#define SO_LINGER 13
+#define SO_BSDCOMPAT 14
+#define SO_REUSEPORT 15
+#ifndef SO_PASSCRED /* powerpc only differs in these */
+#define SO_PASSCRED 16
+#define SO_PEERCRED 17
+#define SO_RCVLOWAT 18
+#define SO_SNDLOWAT 19
+#define SO_RCVTIMEO_OLD 20
+#define SO_SNDTIMEO_OLD 21
+#endif
+
+/* Security levels - as per NRL IPv6 - don't actually do anything */
+#define SO_SECURITY_AUTHENTICATION 22
+#define SO_SECURITY_ENCRYPTION_TRANSPORT 23
+#define SO_SECURITY_ENCRYPTION_NETWORK 24
+
+#define SO_BINDTODEVICE 25
+
+/* Socket filtering */
+#define SO_ATTACH_FILTER 26
+#define SO_DETACH_FILTER 27
+#define SO_GET_FILTER SO_ATTACH_FILTER
+
+#define SO_PEERNAME 28
+
+#define SO_ACCEPTCONN 30
+
+#define SO_PEERSEC 31
+#define SO_PASSSEC 34
+
+#define SO_MARK 36
+
+#define SO_PROTOCOL 38
+#define SO_DOMAIN 39
+
+#define SO_RXQ_OVFL 40
+
+#define SO_WIFI_STATUS 41
+#define SCM_WIFI_STATUS SO_WIFI_STATUS
+#define SO_PEEK_OFF 42
+
+/* Instruct lower device to use last 4-bytes of skb data as FCS */
+#define SO_NOFCS 43
+
+#define SO_LOCK_FILTER 44
+
+#define SO_SELECT_ERR_QUEUE 45
+
+#define SO_BUSY_POLL 46
+
+#define SO_MAX_PACING_RATE 47
+
+#define SO_BPF_EXTENSIONS 48
+
+#define SO_INCOMING_CPU 49
+
+#define SO_ATTACH_BPF 50
+#define SO_DETACH_BPF SO_DETACH_FILTER
+
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
+#define SO_CNX_ADVICE 53
+
+#define SCM_TIMESTAMPING_OPT_STATS 54
+
+#define SO_MEMINFO 55
+
+#define SO_INCOMING_NAPI_ID 56
+
+#define SO_COOKIE 57
+
+#define SCM_TIMESTAMPING_PKTINFO 58
+
+#define SO_PEERGROUPS 59
+
+#define SO_ZEROCOPY 60
+
+#define SO_TXTIME 61
+#define SCM_TXTIME SO_TXTIME
+
+#define SO_BINDTOIFINDEX 62
+
+#define SO_TIMESTAMP_OLD 29
+#define SO_TIMESTAMPNS_OLD 35
+#define SO_TIMESTAMPING_OLD 37
+
+#define SO_TIMESTAMP_NEW 63
+#define SO_TIMESTAMPNS_NEW 64
+#define SO_TIMESTAMPING_NEW 65
+
+#define SO_RCVTIMEO_NEW 66
+#define SO_SNDTIMEO_NEW 67
+
+#define SO_DETACH_REUSEPORT_BPF 68
+
+#if !defined(__KERNEL__)
+
+#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
+/* on 64-bit and x32, avoid the ?: operator */
+#define SO_TIMESTAMP SO_TIMESTAMP_OLD
+#define SO_TIMESTAMPNS SO_TIMESTAMPNS_OLD
+#define SO_TIMESTAMPING SO_TIMESTAMPING_OLD
+
+#define SO_RCVTIMEO SO_RCVTIMEO_OLD
+#define SO_SNDTIMEO SO_SNDTIMEO_OLD
+#else
+#define SO_TIMESTAMP (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMP_OLD : SO_TIMESTAMP_NEW)
+#define SO_TIMESTAMPNS (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMPNS_OLD : SO_TIMESTAMPNS_NEW)
+#define SO_TIMESTAMPING (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_TIMESTAMPING_OLD : SO_TIMESTAMPING_NEW)
+
+#define SO_RCVTIMEO (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_RCVTIMEO_OLD : SO_RCVTIMEO_NEW)
+#define SO_SNDTIMEO (sizeof(time_t) == sizeof(__kernel_long_t) ? SO_SNDTIMEO_OLD : SO_SNDTIMEO_NEW)
+#endif
+
+#define SCM_TIMESTAMP SO_TIMESTAMP
+#define SCM_TIMESTAMPNS SO_TIMESTAMPNS
+#define SCM_TIMESTAMPING SO_TIMESTAMPING
+
+#endif
+
+#endif /* __ASM_GENERIC_SOCKET_H */
diff --git a/tools/include/uapi/asm-generic/unistd.h b/tools/include/uapi/asm-generic/unistd.h
index dee7292e1df6..1be0e798e362 100644
--- a/tools/include/uapi/asm-generic/unistd.h
+++ b/tools/include/uapi/asm-generic/unistd.h
@@ -832,9 +832,27 @@ __SYSCALL(__NR_io_uring_setup, sys_io_uring_setup)
__SYSCALL(__NR_io_uring_enter, sys_io_uring_enter)
#define __NR_io_uring_register 427
__SYSCALL(__NR_io_uring_register, sys_io_uring_register)
+#define __NR_open_tree 428
+__SYSCALL(__NR_open_tree, sys_open_tree)
+#define __NR_move_mount 429
+__SYSCALL(__NR_move_mount, sys_move_mount)
+#define __NR_fsopen 430
+__SYSCALL(__NR_fsopen, sys_fsopen)
+#define __NR_fsconfig 431
+__SYSCALL(__NR_fsconfig, sys_fsconfig)
+#define __NR_fsmount 432
+__SYSCALL(__NR_fsmount, sys_fsmount)
+#define __NR_fspick 433
+__SYSCALL(__NR_fspick, sys_fspick)
+#define __NR_pidfd_open 434
+__SYSCALL(__NR_pidfd_open, sys_pidfd_open)
+#ifdef __ARCH_WANT_SYS_CLONE3
+#define __NR_clone3 435
+__SYSCALL(__NR_clone3, sys_clone3)
+#endif
#undef __NR_syscalls
-#define __NR_syscalls 428
+#define __NR_syscalls 436
/*
* 32 bit systems traditionally used different
diff --git a/tools/include/uapi/asm/bitsperlong.h b/tools/include/uapi/asm/bitsperlong.h
index 57aaeaf8e192..edba4d93e9e6 100644
--- a/tools/include/uapi/asm/bitsperlong.h
+++ b/tools/include/uapi/asm/bitsperlong.h
@@ -1,22 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0 */
#if defined(__i386__) || defined(__x86_64__)
-#include "../../arch/x86/include/uapi/asm/bitsperlong.h"
+#include "../../../arch/x86/include/uapi/asm/bitsperlong.h"
#elif defined(__aarch64__)
-#include "../../arch/arm64/include/uapi/asm/bitsperlong.h"
+#include "../../../arch/arm64/include/uapi/asm/bitsperlong.h"
#elif defined(__powerpc__)
-#include "../../arch/powerpc/include/uapi/asm/bitsperlong.h"
+#include "../../../arch/powerpc/include/uapi/asm/bitsperlong.h"
#elif defined(__s390__)
-#include "../../arch/s390/include/uapi/asm/bitsperlong.h"
+#include "../../../arch/s390/include/uapi/asm/bitsperlong.h"
#elif defined(__sparc__)
-#include "../../arch/sparc/include/uapi/asm/bitsperlong.h"
+#include "../../../arch/sparc/include/uapi/asm/bitsperlong.h"
#elif defined(__mips__)
-#include "../../arch/mips/include/uapi/asm/bitsperlong.h"
+#include "../../../arch/mips/include/uapi/asm/bitsperlong.h"
#elif defined(__ia64__)
-#include "../../arch/ia64/include/uapi/asm/bitsperlong.h"
+#include "../../../arch/ia64/include/uapi/asm/bitsperlong.h"
#elif defined(__riscv)
-#include "../../arch/riscv/include/uapi/asm/bitsperlong.h"
+#include "../../../arch/riscv/include/uapi/asm/bitsperlong.h"
#elif defined(__alpha__)
-#include "../../arch/alpha/include/uapi/asm/bitsperlong.h"
+#include "../../../arch/alpha/include/uapi/asm/bitsperlong.h"
#else
#include <asm-generic/bitsperlong.h>
#endif
diff --git a/tools/include/uapi/drm/drm.h b/tools/include/uapi/drm/drm.h
index 300f336633f2..8a5b2f8f8eb9 100644
--- a/tools/include/uapi/drm/drm.h
+++ b/tools/include/uapi/drm/drm.h
@@ -50,6 +50,7 @@ typedef unsigned int drm_handle_t;
#else /* One of the BSDs */
+#include <stdint.h>
#include <sys/ioccom.h>
#include <sys/types.h>
typedef int8_t __s8;
@@ -649,6 +650,7 @@ struct drm_gem_open {
#define DRM_CAP_PAGE_FLIP_TARGET 0x11
#define DRM_CAP_CRTC_IN_VBLANK_EVENT 0x12
#define DRM_CAP_SYNCOBJ 0x13
+#define DRM_CAP_SYNCOBJ_TIMELINE 0x14
/** DRM_IOCTL_GET_CAP ioctl argument type */
struct drm_get_cap {
@@ -735,8 +737,18 @@ struct drm_syncobj_handle {
__u32 pad;
};
+struct drm_syncobj_transfer {
+ __u32 src_handle;
+ __u32 dst_handle;
+ __u64 src_point;
+ __u64 dst_point;
+ __u32 flags;
+ __u32 pad;
+};
+
#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL (1 << 0)
#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT (1 << 1)
+#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE (1 << 2) /* wait for time point to become available */
struct drm_syncobj_wait {
__u64 handles;
/* absolute timeout */
@@ -747,12 +759,33 @@ struct drm_syncobj_wait {
__u32 pad;
};
+struct drm_syncobj_timeline_wait {
+ __u64 handles;
+ /* wait on specific timeline point for every handles*/
+ __u64 points;
+ /* absolute timeout */
+ __s64 timeout_nsec;
+ __u32 count_handles;
+ __u32 flags;
+ __u32 first_signaled; /* only valid when not waiting all */
+ __u32 pad;
+};
+
+
struct drm_syncobj_array {
__u64 handles;
__u32 count_handles;
__u32 pad;
};
+struct drm_syncobj_timeline_array {
+ __u64 handles;
+ __u64 points;
+ __u32 count_handles;
+ __u32 pad;
+};
+
+
/* Query current scanout sequence number */
struct drm_crtc_get_sequence {
__u32 crtc_id; /* requested crtc_id */
@@ -909,6 +942,11 @@ extern "C" {
#define DRM_IOCTL_MODE_GET_LEASE DRM_IOWR(0xC8, struct drm_mode_get_lease)
#define DRM_IOCTL_MODE_REVOKE_LEASE DRM_IOWR(0xC9, struct drm_mode_revoke_lease)
+#define DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT DRM_IOWR(0xCA, struct drm_syncobj_timeline_wait)
+#define DRM_IOCTL_SYNCOBJ_QUERY DRM_IOWR(0xCB, struct drm_syncobj_timeline_array)
+#define DRM_IOCTL_SYNCOBJ_TRANSFER DRM_IOWR(0xCC, struct drm_syncobj_transfer)
+#define DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL DRM_IOWR(0xCD, struct drm_syncobj_timeline_array)
+
/**
* Device specific ioctls should only be in their respective headers
* The device specific ioctl range is from 0x40 to 0x9f.
diff --git a/tools/include/uapi/drm/i915_drm.h b/tools/include/uapi/drm/i915_drm.h
index 397810fa2d33..328d05e77d9f 100644
--- a/tools/include/uapi/drm/i915_drm.h
+++ b/tools/include/uapi/drm/i915_drm.h
@@ -63,6 +63,28 @@ extern "C" {
#define I915_RESET_UEVENT "RESET"
/*
+ * i915_user_extension: Base class for defining a chain of extensions
+ *
+ * Many interfaces need to grow over time. In most cases we can simply
+ * extend the struct and have userspace pass in more data. Another option,
+ * as demonstrated by Vulkan's approach to providing extensions for forward
+ * and backward compatibility, is to use a list of optional structs to
+ * provide those extra details.
+ *
+ * The key advantage to using an extension chain is that it allows us to
+ * redefine the interface more easily than an ever growing struct of
+ * increasing complexity, and for large parts of that interface to be
+ * entirely optional. The downside is more pointer chasing; chasing across
+ * the __user boundary with pointers encapsulated inside u64.
+ */
+struct i915_user_extension {
+ __u64 next_extension;
+ __u32 name;
+ __u32 flags; /* All undefined bits must be zero. */
+ __u32 rsvd[4]; /* Reserved for future use; must be zero. */
+};
+
+/*
* MOCS indexes used for GPU surfaces, defining the cacheability of the
* surface data and the coherency for this data wrt. CPU vs. GPU accesses.
*/
@@ -99,9 +121,25 @@ enum drm_i915_gem_engine_class {
I915_ENGINE_CLASS_VIDEO = 2,
I915_ENGINE_CLASS_VIDEO_ENHANCE = 3,
+ /* should be kept compact */
+
I915_ENGINE_CLASS_INVALID = -1
};
+/*
+ * There may be more than one engine fulfilling any role within the system.
+ * Each engine of a class is given a unique instance number and therefore
+ * any engine can be specified by its class:instance tuplet. APIs that allow
+ * access to any engine in the system will use struct i915_engine_class_instance
+ * for this identification.
+ */
+struct i915_engine_class_instance {
+ __u16 engine_class; /* see enum drm_i915_gem_engine_class */
+ __u16 engine_instance;
+#define I915_ENGINE_CLASS_INVALID_NONE -1
+#define I915_ENGINE_CLASS_INVALID_VIRTUAL -2
+};
+
/**
* DOC: perf_events exposed by i915 through /sys/bus/event_sources/drivers/i915
*
@@ -319,6 +357,9 @@ typedef struct _drm_i915_sarea {
#define DRM_I915_PERF_ADD_CONFIG 0x37
#define DRM_I915_PERF_REMOVE_CONFIG 0x38
#define DRM_I915_QUERY 0x39
+#define DRM_I915_GEM_VM_CREATE 0x3a
+#define DRM_I915_GEM_VM_DESTROY 0x3b
+/* Must be kept compact -- no holes */
#define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
#define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
@@ -367,6 +408,7 @@ typedef struct _drm_i915_sarea {
#define DRM_IOCTL_I915_GET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey)
#define DRM_IOCTL_I915_GEM_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_WAIT, struct drm_i915_gem_wait)
#define DRM_IOCTL_I915_GEM_CONTEXT_CREATE DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_CREATE, struct drm_i915_gem_context_create)
+#define DRM_IOCTL_I915_GEM_CONTEXT_CREATE_EXT DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_CREATE, struct drm_i915_gem_context_create_ext)
#define DRM_IOCTL_I915_GEM_CONTEXT_DESTROY DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_DESTROY, struct drm_i915_gem_context_destroy)
#define DRM_IOCTL_I915_REG_READ DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_REG_READ, struct drm_i915_reg_read)
#define DRM_IOCTL_I915_GET_RESET_STATS DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GET_RESET_STATS, struct drm_i915_reset_stats)
@@ -377,6 +419,8 @@ typedef struct _drm_i915_sarea {
#define DRM_IOCTL_I915_PERF_ADD_CONFIG DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_ADD_CONFIG, struct drm_i915_perf_oa_config)
#define DRM_IOCTL_I915_PERF_REMOVE_CONFIG DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_REMOVE_CONFIG, __u64)
#define DRM_IOCTL_I915_QUERY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_QUERY, struct drm_i915_query)
+#define DRM_IOCTL_I915_GEM_VM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_VM_CREATE, struct drm_i915_gem_vm_control)
+#define DRM_IOCTL_I915_GEM_VM_DESTROY DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_VM_DESTROY, struct drm_i915_gem_vm_control)
/* Allow drivers to submit batchbuffers directly to hardware, relying
* on the security mechanisms provided by hardware.
@@ -476,6 +520,7 @@ typedef struct drm_i915_irq_wait {
#define I915_SCHEDULER_CAP_ENABLED (1ul << 0)
#define I915_SCHEDULER_CAP_PRIORITY (1ul << 1)
#define I915_SCHEDULER_CAP_PREEMPTION (1ul << 2)
+#define I915_SCHEDULER_CAP_SEMAPHORES (1ul << 3)
#define I915_PARAM_HUC_STATUS 42
@@ -559,6 +604,14 @@ typedef struct drm_i915_irq_wait {
*/
#define I915_PARAM_MMAP_GTT_COHERENT 52
+/*
+ * Query whether DRM_I915_GEM_EXECBUFFER2 supports coordination of parallel
+ * execution through use of explicit fence support.
+ * See I915_EXEC_FENCE_OUT and I915_EXEC_FENCE_SUBMIT.
+ */
+#define I915_PARAM_HAS_EXEC_SUBMIT_FENCE 53
+/* Must be kept compact -- no holes and well documented */
+
typedef struct drm_i915_getparam {
__s32 param;
/*
@@ -574,6 +627,7 @@ typedef struct drm_i915_getparam {
#define I915_SETPARAM_TEX_LRU_LOG_GRANULARITY 2
#define I915_SETPARAM_ALLOW_BATCHBUFFER 3
#define I915_SETPARAM_NUM_USED_FENCES 4
+/* Must be kept compact -- no holes */
typedef struct drm_i915_setparam {
int param;
@@ -972,7 +1026,7 @@ struct drm_i915_gem_execbuffer2 {
* struct drm_i915_gem_exec_fence *fences.
*/
__u64 cliprects_ptr;
-#define I915_EXEC_RING_MASK (7<<0)
+#define I915_EXEC_RING_MASK (0x3f)
#define I915_EXEC_DEFAULT (0<<0)
#define I915_EXEC_RENDER (1<<0)
#define I915_EXEC_BSD (2<<0)
@@ -1078,7 +1132,16 @@ struct drm_i915_gem_execbuffer2 {
*/
#define I915_EXEC_FENCE_ARRAY (1<<19)
-#define __I915_EXEC_UNKNOWN_FLAGS (-(I915_EXEC_FENCE_ARRAY<<1))
+/*
+ * Setting I915_EXEC_FENCE_SUBMIT implies that lower_32_bits(rsvd2) represent
+ * a sync_file fd to wait upon (in a nonblocking manner) prior to executing
+ * the batch.
+ *
+ * Returns -EINVAL if the sync_file fd cannot be found.
+ */
+#define I915_EXEC_FENCE_SUBMIT (1 << 20)
+
+#define __I915_EXEC_UNKNOWN_FLAGS (-(I915_EXEC_FENCE_SUBMIT << 1))
#define I915_EXEC_CONTEXT_ID_MASK (0xffffffff)
#define i915_execbuffer2_set_context_id(eb2, context) \
@@ -1120,32 +1183,34 @@ struct drm_i915_gem_busy {
* as busy may become idle before the ioctl is completed.
*
* Furthermore, if the object is busy, which engine is busy is only
- * provided as a guide. There are race conditions which prevent the
- * report of which engines are busy from being always accurate.
- * However, the converse is not true. If the object is idle, the
- * result of the ioctl, that all engines are idle, is accurate.
+ * provided as a guide and only indirectly by reporting its class
+ * (there may be more than one engine in each class). There are race
+ * conditions which prevent the report of which engines are busy from
+ * being always accurate. However, the converse is not true. If the
+ * object is idle, the result of the ioctl, that all engines are idle,
+ * is accurate.
*
* The returned dword is split into two fields to indicate both
- * the engines on which the object is being read, and the
- * engine on which it is currently being written (if any).
+ * the engine classess on which the object is being read, and the
+ * engine class on which it is currently being written (if any).
*
* The low word (bits 0:15) indicate if the object is being written
* to by any engine (there can only be one, as the GEM implicit
* synchronisation rules force writes to be serialised). Only the
- * engine for the last write is reported.
+ * engine class (offset by 1, I915_ENGINE_CLASS_RENDER is reported as
+ * 1 not 0 etc) for the last write is reported.
*
- * The high word (bits 16:31) are a bitmask of which engines are
- * currently reading from the object. Multiple engines may be
+ * The high word (bits 16:31) are a bitmask of which engines classes
+ * are currently reading from the object. Multiple engines may be
* reading from the object simultaneously.
*
- * The value of each engine is the same as specified in the
- * EXECBUFFER2 ioctl, i.e. I915_EXEC_RENDER, I915_EXEC_BSD etc.
- * Note I915_EXEC_DEFAULT is a symbolic value and is mapped to
- * the I915_EXEC_RENDER engine for execution, and so it is never
+ * The value of each engine class is the same as specified in the
+ * I915_CONTEXT_SET_ENGINES parameter and via perf, i.e.
+ * I915_ENGINE_CLASS_RENDER, I915_ENGINE_CLASS_COPY, etc.
* reported as active itself. Some hardware may have parallel
* execution engines, e.g. multiple media engines, which are
- * mapped to the same identifier in the EXECBUFFER2 ioctl and
- * so are not separately reported for busyness.
+ * mapped to the same class identifier and so are not separately
+ * reported for busyness.
*
* Caveat emptor:
* Only the boolean result of this query is reliable; that is whether
@@ -1412,65 +1477,18 @@ struct drm_i915_gem_wait {
};
struct drm_i915_gem_context_create {
- /* output: id of new context*/
- __u32 ctx_id;
- __u32 pad;
-};
-
-struct drm_i915_gem_context_destroy {
- __u32 ctx_id;
- __u32 pad;
-};
-
-struct drm_i915_reg_read {
- /*
- * Register offset.
- * For 64bit wide registers where the upper 32bits don't immediately
- * follow the lower 32bits, the offset of the lower 32bits must
- * be specified
- */
- __u64 offset;
-#define I915_REG_READ_8B_WA (1ul << 0)
-
- __u64 val; /* Return value */
-};
-/* Known registers:
- *
- * Render engine timestamp - 0x2358 + 64bit - gen7+
- * - Note this register returns an invalid value if using the default
- * single instruction 8byte read, in order to workaround that pass
- * flag I915_REG_READ_8B_WA in offset field.
- *
- */
-
-struct drm_i915_reset_stats {
- __u32 ctx_id;
- __u32 flags;
-
- /* All resets since boot/module reload, for all contexts */
- __u32 reset_count;
-
- /* Number of batches lost when active in GPU, for this context */
- __u32 batch_active;
-
- /* Number of batches lost pending for execution, for this context */
- __u32 batch_pending;
-
+ __u32 ctx_id; /* output: id of new context*/
__u32 pad;
};
-struct drm_i915_gem_userptr {
- __u64 user_ptr;
- __u64 user_size;
+struct drm_i915_gem_context_create_ext {
+ __u32 ctx_id; /* output: id of new context*/
__u32 flags;
-#define I915_USERPTR_READ_ONLY 0x1
-#define I915_USERPTR_UNSYNCHRONIZED 0x80000000
- /**
- * Returned handle for the object.
- *
- * Object handles are nonzero.
- */
- __u32 handle;
+#define I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS (1u << 0)
+#define I915_CONTEXT_CREATE_FLAGS_SINGLE_TIMELINE (1u << 1)
+#define I915_CONTEXT_CREATE_FLAGS_UNKNOWN \
+ (-(I915_CONTEXT_CREATE_FLAGS_SINGLE_TIMELINE << 1))
+ __u64 extensions;
};
struct drm_i915_gem_context_param {
@@ -1491,6 +1509,63 @@ struct drm_i915_gem_context_param {
* drm_i915_gem_context_param_sseu.
*/
#define I915_CONTEXT_PARAM_SSEU 0x7
+
+/*
+ * Not all clients may want to attempt automatic recover of a context after
+ * a hang (for example, some clients may only submit very small incremental
+ * batches relying on known logical state of previous batches which will never
+ * recover correctly and each attempt will hang), and so would prefer that
+ * the context is forever banned instead.
+ *
+ * If set to false (0), after a reset, subsequent (and in flight) rendering
+ * from this context is discarded, and the client will need to create a new
+ * context to use instead.
+ *
+ * If set to true (1), the kernel will automatically attempt to recover the
+ * context by skipping the hanging batch and executing the next batch starting
+ * from the default context state (discarding the incomplete logical context
+ * state lost due to the reset).
+ *
+ * On creation, all new contexts are marked as recoverable.
+ */
+#define I915_CONTEXT_PARAM_RECOVERABLE 0x8
+
+ /*
+ * The id of the associated virtual memory address space (ppGTT) of
+ * this context. Can be retrieved and passed to another context
+ * (on the same fd) for both to use the same ppGTT and so share
+ * address layouts, and avoid reloading the page tables on context
+ * switches between themselves.
+ *
+ * See DRM_I915_GEM_VM_CREATE and DRM_I915_GEM_VM_DESTROY.
+ */
+#define I915_CONTEXT_PARAM_VM 0x9
+
+/*
+ * I915_CONTEXT_PARAM_ENGINES:
+ *
+ * Bind this context to operate on this subset of available engines. Henceforth,
+ * the I915_EXEC_RING selector for DRM_IOCTL_I915_GEM_EXECBUFFER2 operates as
+ * an index into this array of engines; I915_EXEC_DEFAULT selecting engine[0]
+ * and upwards. Slots 0...N are filled in using the specified (class, instance).
+ * Use
+ * engine_class: I915_ENGINE_CLASS_INVALID,
+ * engine_instance: I915_ENGINE_CLASS_INVALID_NONE
+ * to specify a gap in the array that can be filled in later, e.g. by a
+ * virtual engine used for load balancing.
+ *
+ * Setting the number of engines bound to the context to 0, by passing a zero
+ * sized argument, will revert back to default settings.
+ *
+ * See struct i915_context_param_engines.
+ *
+ * Extensions:
+ * i915_context_engines_load_balance (I915_CONTEXT_ENGINES_EXT_LOAD_BALANCE)
+ * i915_context_engines_bond (I915_CONTEXT_ENGINES_EXT_BOND)
+ */
+#define I915_CONTEXT_PARAM_ENGINES 0xa
+/* Must be kept compact -- no holes and well documented */
+
__u64 value;
};
@@ -1519,13 +1594,13 @@ struct drm_i915_gem_context_param_sseu {
/*
* Engine class & instance to be configured or queried.
*/
- __u16 engine_class;
- __u16 engine_instance;
+ struct i915_engine_class_instance engine;
/*
- * Unused for now. Must be cleared to zero.
+ * Unknown flags must be cleared to zero.
*/
__u32 flags;
+#define I915_CONTEXT_SSEU_FLAG_ENGINE_INDEX (1u << 0)
/*
* Mask of slices to enable for the context. Valid values are a subset
@@ -1553,6 +1628,199 @@ struct drm_i915_gem_context_param_sseu {
__u32 rsvd;
};
+/*
+ * i915_context_engines_load_balance:
+ *
+ * Enable load balancing across this set of engines.
+ *
+ * Into the I915_EXEC_DEFAULT slot [0], a virtual engine is created that when
+ * used will proxy the execbuffer request onto one of the set of engines
+ * in such a way as to distribute the load evenly across the set.
+ *
+ * The set of engines must be compatible (e.g. the same HW class) as they
+ * will share the same logical GPU context and ring.
+ *
+ * To intermix rendering with the virtual engine and direct rendering onto
+ * the backing engines (bypassing the load balancing proxy), the context must
+ * be defined to use a single timeline for all engines.
+ */
+struct i915_context_engines_load_balance {
+ struct i915_user_extension base;
+
+ __u16 engine_index;
+ __u16 num_siblings;
+ __u32 flags; /* all undefined flags must be zero */
+
+ __u64 mbz64; /* reserved for future use; must be zero */
+
+ struct i915_engine_class_instance engines[0];
+} __attribute__((packed));
+
+#define I915_DEFINE_CONTEXT_ENGINES_LOAD_BALANCE(name__, N__) struct { \
+ struct i915_user_extension base; \
+ __u16 engine_index; \
+ __u16 num_siblings; \
+ __u32 flags; \
+ __u64 mbz64; \
+ struct i915_engine_class_instance engines[N__]; \
+} __attribute__((packed)) name__
+
+/*
+ * i915_context_engines_bond:
+ *
+ * Constructed bonded pairs for execution within a virtual engine.
+ *
+ * All engines are equal, but some are more equal than others. Given
+ * the distribution of resources in the HW, it may be preferable to run
+ * a request on a given subset of engines in parallel to a request on a
+ * specific engine. We enable this selection of engines within a virtual
+ * engine by specifying bonding pairs, for any given master engine we will
+ * only execute on one of the corresponding siblings within the virtual engine.
+ *
+ * To execute a request in parallel on the master engine and a sibling requires
+ * coordination with a I915_EXEC_FENCE_SUBMIT.
+ */
+struct i915_context_engines_bond {
+ struct i915_user_extension base;
+
+ struct i915_engine_class_instance master;
+
+ __u16 virtual_index; /* index of virtual engine in ctx->engines[] */
+ __u16 num_bonds;
+
+ __u64 flags; /* all undefined flags must be zero */
+ __u64 mbz64[4]; /* reserved for future use; must be zero */
+
+ struct i915_engine_class_instance engines[0];
+} __attribute__((packed));
+
+#define I915_DEFINE_CONTEXT_ENGINES_BOND(name__, N__) struct { \
+ struct i915_user_extension base; \
+ struct i915_engine_class_instance master; \
+ __u16 virtual_index; \
+ __u16 num_bonds; \
+ __u64 flags; \
+ __u64 mbz64[4]; \
+ struct i915_engine_class_instance engines[N__]; \
+} __attribute__((packed)) name__
+
+struct i915_context_param_engines {
+ __u64 extensions; /* linked chain of extension blocks, 0 terminates */
+#define I915_CONTEXT_ENGINES_EXT_LOAD_BALANCE 0 /* see i915_context_engines_load_balance */
+#define I915_CONTEXT_ENGINES_EXT_BOND 1 /* see i915_context_engines_bond */
+ struct i915_engine_class_instance engines[0];
+} __attribute__((packed));
+
+#define I915_DEFINE_CONTEXT_PARAM_ENGINES(name__, N__) struct { \
+ __u64 extensions; \
+ struct i915_engine_class_instance engines[N__]; \
+} __attribute__((packed)) name__
+
+struct drm_i915_gem_context_create_ext_setparam {
+#define I915_CONTEXT_CREATE_EXT_SETPARAM 0
+ struct i915_user_extension base;
+ struct drm_i915_gem_context_param param;
+};
+
+struct drm_i915_gem_context_create_ext_clone {
+#define I915_CONTEXT_CREATE_EXT_CLONE 1
+ struct i915_user_extension base;
+ __u32 clone_id;
+ __u32 flags;
+#define I915_CONTEXT_CLONE_ENGINES (1u << 0)
+#define I915_CONTEXT_CLONE_FLAGS (1u << 1)
+#define I915_CONTEXT_CLONE_SCHEDATTR (1u << 2)
+#define I915_CONTEXT_CLONE_SSEU (1u << 3)
+#define I915_CONTEXT_CLONE_TIMELINE (1u << 4)
+#define I915_CONTEXT_CLONE_VM (1u << 5)
+#define I915_CONTEXT_CLONE_UNKNOWN -(I915_CONTEXT_CLONE_VM << 1)
+ __u64 rsvd;
+};
+
+struct drm_i915_gem_context_destroy {
+ __u32 ctx_id;
+ __u32 pad;
+};
+
+/*
+ * DRM_I915_GEM_VM_CREATE -
+ *
+ * Create a new virtual memory address space (ppGTT) for use within a context
+ * on the same file. Extensions can be provided to configure exactly how the
+ * address space is setup upon creation.
+ *
+ * The id of new VM (bound to the fd) for use with I915_CONTEXT_PARAM_VM is
+ * returned in the outparam @id.
+ *
+ * No flags are defined, with all bits reserved and must be zero.
+ *
+ * An extension chain maybe provided, starting with @extensions, and terminated
+ * by the @next_extension being 0. Currently, no extensions are defined.
+ *
+ * DRM_I915_GEM_VM_DESTROY -
+ *
+ * Destroys a previously created VM id, specified in @id.
+ *
+ * No extensions or flags are allowed currently, and so must be zero.
+ */
+struct drm_i915_gem_vm_control {
+ __u64 extensions;
+ __u32 flags;
+ __u32 vm_id;
+};
+
+struct drm_i915_reg_read {
+ /*
+ * Register offset.
+ * For 64bit wide registers where the upper 32bits don't immediately
+ * follow the lower 32bits, the offset of the lower 32bits must
+ * be specified
+ */
+ __u64 offset;
+#define I915_REG_READ_8B_WA (1ul << 0)
+
+ __u64 val; /* Return value */
+};
+
+/* Known registers:
+ *
+ * Render engine timestamp - 0x2358 + 64bit - gen7+
+ * - Note this register returns an invalid value if using the default
+ * single instruction 8byte read, in order to workaround that pass
+ * flag I915_REG_READ_8B_WA in offset field.
+ *
+ */
+
+struct drm_i915_reset_stats {
+ __u32 ctx_id;
+ __u32 flags;
+
+ /* All resets since boot/module reload, for all contexts */
+ __u32 reset_count;
+
+ /* Number of batches lost when active in GPU, for this context */
+ __u32 batch_active;
+
+ /* Number of batches lost pending for execution, for this context */
+ __u32 batch_pending;
+
+ __u32 pad;
+};
+
+struct drm_i915_gem_userptr {
+ __u64 user_ptr;
+ __u64 user_size;
+ __u32 flags;
+#define I915_USERPTR_READ_ONLY 0x1
+#define I915_USERPTR_UNSYNCHRONIZED 0x80000000
+ /**
+ * Returned handle for the object.
+ *
+ * Object handles are nonzero.
+ */
+ __u32 handle;
+};
+
enum drm_i915_oa_format {
I915_OA_FORMAT_A13 = 1, /* HSW only */
I915_OA_FORMAT_A29, /* HSW only */
@@ -1714,6 +1982,8 @@ struct drm_i915_perf_oa_config {
struct drm_i915_query_item {
__u64 query_id;
#define DRM_I915_QUERY_TOPOLOGY_INFO 1
+#define DRM_I915_QUERY_ENGINE_INFO 2
+/* Must be kept compact -- no holes and well documented */
/*
* When set to zero by userspace, this is filled with the size of the
@@ -1811,6 +2081,47 @@ struct drm_i915_query_topology_info {
__u8 data[];
};
+/**
+ * struct drm_i915_engine_info
+ *
+ * Describes one engine and it's capabilities as known to the driver.
+ */
+struct drm_i915_engine_info {
+ /** Engine class and instance. */
+ struct i915_engine_class_instance engine;
+
+ /** Reserved field. */
+ __u32 rsvd0;
+
+ /** Engine flags. */
+ __u64 flags;
+
+ /** Capabilities of this engine. */
+ __u64 capabilities;
+#define I915_VIDEO_CLASS_CAPABILITY_HEVC (1 << 0)
+#define I915_VIDEO_AND_ENHANCE_CLASS_CAPABILITY_SFC (1 << 1)
+
+ /** Reserved fields. */
+ __u64 rsvd1[4];
+};
+
+/**
+ * struct drm_i915_query_engine_info
+ *
+ * Engine info query enumerates all engines known to the driver by filling in
+ * an array of struct drm_i915_engine_info structures.
+ */
+struct drm_i915_query_engine_info {
+ /** Number of struct drm_i915_engine_info structs following. */
+ __u32 num_engines;
+
+ /** MBZ */
+ __u32 rsvd[3];
+
+ /** Marker for drm_i915_engine_info structures. */
+ struct drm_i915_engine_info engines[];
+};
+
#if defined(__cplusplus)
}
#endif
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 63e0cf66f01a..77c6be96d676 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -106,6 +106,7 @@ enum bpf_cmd {
BPF_TASK_FD_QUERY,
BPF_MAP_LOOKUP_AND_DELETE_ELEM,
BPF_MAP_FREEZE,
+ BPF_BTF_GET_NEXT_ID,
};
enum bpf_map_type {
@@ -134,6 +135,7 @@ enum bpf_map_type {
BPF_MAP_TYPE_QUEUE,
BPF_MAP_TYPE_STACK,
BPF_MAP_TYPE_SK_STORAGE,
+ BPF_MAP_TYPE_DEVMAP_HASH,
};
/* Note that tracing related programs such as
@@ -170,6 +172,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_FLOW_DISSECTOR,
BPF_PROG_TYPE_CGROUP_SYSCTL,
BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
+ BPF_PROG_TYPE_CGROUP_SOCKOPT,
};
enum bpf_attach_type {
@@ -192,6 +195,10 @@ enum bpf_attach_type {
BPF_LIRC_MODE2,
BPF_FLOW_DISSECTOR,
BPF_CGROUP_SYSCTL,
+ BPF_CGROUP_UDP4_RECVMSG,
+ BPF_CGROUP_UDP6_RECVMSG,
+ BPF_CGROUP_GETSOCKOPT,
+ BPF_CGROUP_SETSOCKOPT,
__MAX_BPF_ATTACH_TYPE
};
@@ -260,6 +267,27 @@ enum bpf_attach_type {
*/
#define BPF_F_ANY_ALIGNMENT (1U << 1)
+/* BPF_F_TEST_RND_HI32 is used in BPF_PROG_LOAD command for testing purpose.
+ * Verifier does sub-register def/use analysis and identifies instructions whose
+ * def only matters for low 32-bit, high 32-bit is never referenced later
+ * through implicit zero extension. Therefore verifier notifies JIT back-ends
+ * that it is safe to ignore clearing high 32-bit for these instructions. This
+ * saves some back-ends a lot of code-gen. However such optimization is not
+ * necessary on some arches, for example x86_64, arm64 etc, whose JIT back-ends
+ * hence hasn't used verifier's analysis result. But, we really want to have a
+ * way to be able to verify the correctness of the described optimization on
+ * x86_64 on which testsuites are frequently exercised.
+ *
+ * So, this flag is introduced. Once it is set, verifier will randomize high
+ * 32-bit for those instructions who has been identified as safe to ignore them.
+ * Then, if verifier is not doing correct analysis, such randomization will
+ * regress tests to expose bugs.
+ */
+#define BPF_F_TEST_RND_HI32 (1U << 2)
+
+/* The verifier internal test flag. Behavior is undefined */
+#define BPF_F_TEST_STATE_FREQ (1U << 3)
+
/* When BPF ldimm64's insn[0].src_reg != 0 then this can have
* two extensions:
*
@@ -313,6 +341,9 @@ enum bpf_attach_type {
#define BPF_F_RDONLY_PROG (1U << 7)
#define BPF_F_WRONLY_PROG (1U << 8)
+/* Clone map from listener for newly accepted socket */
+#define BPF_F_CLONE (1U << 9)
+
/* flags for BPF_PROG_QUERY */
#define BPF_F_QUERY_EFFECTIVE (1U << 0)
@@ -552,6 +583,8 @@ union bpf_attr {
* limited to five).
*
* Each time the helper is called, it appends a line to the trace.
+ * Lines are discarded while *\/sys/kernel/debug/tracing/trace* is
+ * open, use *\/sys/kernel/debug/tracing/trace_pipe* to avoid this.
* The format of the trace is customizable, and the exact output
* one will get depends on the options set in
* *\/sys/kernel/debug/tracing/trace_options* (see also the
@@ -783,7 +816,7 @@ union bpf_attr {
* based on a user-provided identifier for all traffic coming from
* the tasks belonging to the related cgroup. See also the related
* kernel documentation, available from the Linux sources in file
- * *Documentation/cgroup-v1/net_cls.txt*.
+ * *Documentation/admin-guide/cgroup-v1/net_cls.rst*.
*
* The Linux kernel has two versions for cgroups: there are
* cgroups v1 and cgroups v2. Both are available to users, who can
@@ -990,7 +1023,7 @@ union bpf_attr {
* The realm of the route for the packet associated to *skb*, or 0
* if none was found.
*
- * int bpf_perf_event_output(struct pt_reg *ctx, struct bpf_map *map, u64 flags, void *data, u64 size)
+ * int bpf_perf_event_output(struct pt_regs *ctx, struct bpf_map *map, u64 flags, void *data, u64 size)
* Description
* Write raw *data* blob into a special BPF perf event held by
* *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf
@@ -1052,7 +1085,7 @@ union bpf_attr {
* Return
* 0 on success, or a negative error in case of failure.
*
- * int bpf_get_stackid(struct pt_reg *ctx, struct bpf_map *map, u64 flags)
+ * int bpf_get_stackid(struct pt_regs *ctx, struct bpf_map *map, u64 flags)
* Description
* Walk a user or a kernel stack and return its id. To achieve
* this, the helper needs *ctx*, which is a pointer to the context
@@ -1443,8 +1476,8 @@ union bpf_attr {
* If no cookie has been set yet, generate a new cookie. Once
* generated, the socket cookie remains stable for the life of the
* socket. This helper can be useful for monitoring per socket
- * networking traffic statistics as it provides a unique socket
- * identifier per namespace.
+ * networking traffic statistics as it provides a global socket
+ * identifier that can be assumed unique.
* Return
* A 8-byte long non-decreasing number on success, or 0 if the
* socket field is missing inside *skb*.
@@ -1548,8 +1581,11 @@ union bpf_attr {
* but this is only implemented for native XDP (with driver
* support) as of this writing).
*
- * All values for *flags* are reserved for future usage, and must
- * be left at zero.
+ * The lower two bits of *flags* are used as the return code if
+ * the map lookup fails. This is so that the return value can be
+ * one of the XDP program return codes up to XDP_TX, as chosen by
+ * the caller. Any higher bits in the *flags* argument must be
+ * unset.
*
* When used to redirect packets to net devices, this helper
* provides a high performance increase over **bpf_redirect**\ ().
@@ -1698,7 +1734,7 @@ union bpf_attr {
* Return
* 0 on success, or a negative error in case of failure.
*
- * int bpf_override_return(struct pt_reg *regs, u64 rc)
+ * int bpf_override_return(struct pt_regs *regs, u64 rc)
* Description
* Used for error injection, this helper uses kprobes to override
* the return value of the probed function, and to set it to *rc*.
@@ -1744,6 +1780,7 @@ union bpf_attr {
* * **BPF_SOCK_OPS_RTO_CB_FLAG** (retransmission time out)
* * **BPF_SOCK_OPS_RETRANS_CB_FLAG** (retransmission)
* * **BPF_SOCK_OPS_STATE_CB_FLAG** (TCP state change)
+ * * **BPF_SOCK_OPS_RTT_CB_FLAG** (every RTT)
*
* Therefore, this function can be used to clear a callback flag by
* setting the appropriate bit to zero. e.g. to disable the RTO
@@ -2672,6 +2709,47 @@ union bpf_attr {
* 0 on success.
*
* **-ENOENT** if the bpf-local-storage cannot be found.
+ *
+ * int bpf_send_signal(u32 sig)
+ * Description
+ * Send signal *sig* to the current task.
+ * Return
+ * 0 on success or successfully queued.
+ *
+ * **-EBUSY** if work queue under nmi is full.
+ *
+ * **-EINVAL** if *sig* is invalid.
+ *
+ * **-EPERM** if no permission to send the *sig*.
+ *
+ * **-EAGAIN** if bpf program can try again.
+ *
+ * s64 bpf_tcp_gen_syncookie(struct bpf_sock *sk, void *iph, u32 iph_len, struct tcphdr *th, u32 th_len)
+ * Description
+ * Try to issue a SYN cookie for the packet with corresponding
+ * IP/TCP headers, *iph* and *th*, on the listening socket in *sk*.
+ *
+ * *iph* points to the start of the IPv4 or IPv6 header, while
+ * *iph_len* contains **sizeof**\ (**struct iphdr**) or
+ * **sizeof**\ (**struct ip6hdr**).
+ *
+ * *th* points to the start of the TCP header, while *th_len*
+ * contains the length of the TCP header.
+ *
+ * Return
+ * On success, lower 32 bits hold the generated SYN cookie in
+ * followed by 16 bits which hold the MSS value for that cookie,
+ * and the top 16 bits are unused.
+ *
+ * On failure, the returned value is one of the following:
+ *
+ * **-EINVAL** SYN cookie cannot be issued due to error
+ *
+ * **-ENOENT** SYN cookie should not be issued (no SYN flood)
+ *
+ * **-EOPNOTSUPP** kernel configuration does not enable SYN cookies
+ *
+ * **-EPROTONOSUPPORT** IP packet version is not 4 or 6
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
@@ -2782,7 +2860,9 @@ union bpf_attr {
FN(strtol), \
FN(strtoul), \
FN(sk_storage_get), \
- FN(sk_storage_delete),
+ FN(sk_storage_delete), \
+ FN(send_signal), \
+ FN(tcp_gen_syncookie),
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
* function eBPF program intends to call
@@ -3031,6 +3111,12 @@ struct bpf_tcp_sock {
* sum(delta(snd_una)), or how many bytes
* were acked.
*/
+ __u32 dsack_dups; /* RFC4898 tcpEStatsStackDSACKDups
+ * total number of DSACK blocks received
+ */
+ __u32 delivered; /* Total data packets delivered incl. rexmits */
+ __u32 delivered_ce; /* Like the above but only ECE marked packets */
+ __u32 icsk_retransmits; /* Number of unrecovered [RTO] timeouts */
};
struct bpf_sock_tuple {
@@ -3050,6 +3136,10 @@ struct bpf_sock_tuple {
};
};
+struct bpf_xdp_sock {
+ __u32 queue_id;
+};
+
#define XDP_PACKET_HEADROOM 256
/* User return codes for XDP prog type.
@@ -3141,6 +3231,7 @@ struct bpf_prog_info {
char name[BPF_OBJ_NAME_LEN];
__u32 ifindex;
__u32 gpl_compatible:1;
+ __u32 :31; /* alignment pad */
__u64 netns_dev;
__u64 netns_ino;
__u32 nr_jited_ksyms;
@@ -3195,7 +3286,7 @@ struct bpf_sock_addr {
__u32 user_ip4; /* Allows 1,2,4-byte read and 4-byte write.
* Stored in network byte order.
*/
- __u32 user_ip6[4]; /* Allows 1,2,4-byte read an 4-byte write.
+ __u32 user_ip6[4]; /* Allows 1,2,4,8-byte read and 4,8-byte write.
* Stored in network byte order.
*/
__u32 user_port; /* Allows 4-byte read and write.
@@ -3204,12 +3295,13 @@ struct bpf_sock_addr {
__u32 family; /* Allows 4-byte read, but no write */
__u32 type; /* Allows 4-byte read, but no write */
__u32 protocol; /* Allows 4-byte read, but no write */
- __u32 msg_src_ip4; /* Allows 1,2,4-byte read an 4-byte write.
+ __u32 msg_src_ip4; /* Allows 1,2,4-byte read and 4-byte write.
* Stored in network byte order.
*/
- __u32 msg_src_ip6[4]; /* Allows 1,2,4-byte read an 4-byte write.
+ __u32 msg_src_ip6[4]; /* Allows 1,2,4,8-byte read and 4,8-byte write.
* Stored in network byte order.
*/
+ __bpf_md_ptr(struct bpf_sock *, sk);
};
/* User bpf_sock_ops struct to access socket values and specify request ops
@@ -3261,13 +3353,15 @@ struct bpf_sock_ops {
__u32 sk_txhash;
__u64 bytes_received;
__u64 bytes_acked;
+ __bpf_md_ptr(struct bpf_sock *, sk);
};
/* Definitions for bpf_sock_ops_cb_flags */
#define BPF_SOCK_OPS_RTO_CB_FLAG (1<<0)
#define BPF_SOCK_OPS_RETRANS_CB_FLAG (1<<1)
#define BPF_SOCK_OPS_STATE_CB_FLAG (1<<2)
-#define BPF_SOCK_OPS_ALL_CB_FLAGS 0x7 /* Mask of all currently
+#define BPF_SOCK_OPS_RTT_CB_FLAG (1<<3)
+#define BPF_SOCK_OPS_ALL_CB_FLAGS 0xF /* Mask of all currently
* supported cb flags
*/
@@ -3322,6 +3416,8 @@ enum {
BPF_SOCK_OPS_TCP_LISTEN_CB, /* Called on listen(2), right after
* socket transition to LISTEN state.
*/
+ BPF_SOCK_OPS_RTT_CB, /* Called on every RTT.
+ */
};
/* List of TCP states. There is a build check in net/ipv4/tcp.c to detect
@@ -3376,8 +3472,8 @@ struct bpf_raw_tracepoint_args {
/* DIRECT: Skip the FIB rules and go to FIB table associated with device
* OUTPUT: Do lookup from egress perspective; default is ingress
*/
-#define BPF_FIB_LOOKUP_DIRECT BIT(0)
-#define BPF_FIB_LOOKUP_OUTPUT BIT(1)
+#define BPF_FIB_LOOKUP_DIRECT (1U << 0)
+#define BPF_FIB_LOOKUP_OUTPUT (1U << 1)
enum {
BPF_FIB_LKUP_RET_SUCCESS, /* lookup successful */
@@ -3449,6 +3545,10 @@ enum bpf_task_fd_type {
BPF_FD_TYPE_URETPROBE, /* filename + offset */
};
+#define BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG (1U << 0)
+#define BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL (1U << 1)
+#define BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP (1U << 2)
+
struct bpf_flow_keys {
__u16 nhoff;
__u16 thoff;
@@ -3470,6 +3570,8 @@ struct bpf_flow_keys {
__u32 ipv6_dst[4]; /* in6_addr; network order */
};
};
+ __u32 flags;
+ __be32 flow_label;
};
struct bpf_func_info {
@@ -3500,4 +3602,15 @@ struct bpf_sysctl {
*/
};
+struct bpf_sockopt {
+ __bpf_md_ptr(struct bpf_sock *, sk);
+ __bpf_md_ptr(void *, optval);
+ __bpf_md_ptr(void *, optval_end);
+
+ __s32 level;
+ __s32 optname;
+ __s32 optlen;
+ __s32 retval;
+};
+
#endif /* _UAPI__LINUX_BPF_H__ */
diff --git a/tools/include/uapi/linux/btf.h b/tools/include/uapi/linux/btf.h
index 9310652ca4f9..63ae4a39e58b 100644
--- a/tools/include/uapi/linux/btf.h
+++ b/tools/include/uapi/linux/btf.h
@@ -83,7 +83,7 @@ struct btf_type {
* is the 32 bits arrangement:
*/
#define BTF_INT_ENCODING(VAL) (((VAL) & 0x0f000000) >> 24)
-#define BTF_INT_OFFSET(VAL) (((VAL & 0x00ff0000)) >> 16)
+#define BTF_INT_OFFSET(VAL) (((VAL) & 0x00ff0000) >> 16)
#define BTF_INT_BITS(VAL) ((VAL) & 0x000000ff)
/* Attributes stored in the BTF_INT_ENCODING */
diff --git a/tools/include/uapi/linux/const.h b/tools/include/uapi/linux/const.h
new file mode 100644
index 000000000000..5ed721ad5b19
--- /dev/null
+++ b/tools/include/uapi/linux/const.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* const.h: Macros for dealing with constants. */
+
+#ifndef _UAPI_LINUX_CONST_H
+#define _UAPI_LINUX_CONST_H
+
+/* Some constant macros are used in both assembler and
+ * C code. Therefore we cannot annotate them always with
+ * 'UL' and other type specifiers unilaterally. We
+ * use the following macros to deal with this.
+ *
+ * Similarly, _AT() will cast an expression with a type in C, but
+ * leave it unchanged in asm.
+ */
+
+#ifdef __ASSEMBLY__
+#define _AC(X,Y) X
+#define _AT(T,X) X
+#else
+#define __AC(X,Y) (X##Y)
+#define _AC(X,Y) __AC(X,Y)
+#define _AT(T,X) ((T)(X))
+#endif
+
+#define _UL(x) (_AC(x, UL))
+#define _ULL(x) (_AC(x, ULL))
+
+#define _BITUL(x) (_UL(1) << (x))
+#define _BITULL(x) (_ULL(1) << (x))
+
+#endif /* _UAPI_LINUX_CONST_H */
diff --git a/tools/include/uapi/linux/fcntl.h b/tools/include/uapi/linux/fcntl.h
index a2f8658f1c55..1d338357df8a 100644
--- a/tools/include/uapi/linux/fcntl.h
+++ b/tools/include/uapi/linux/fcntl.h
@@ -91,5 +91,7 @@
#define AT_STATX_FORCE_SYNC 0x2000 /* - Force the attributes to be sync'd with the server */
#define AT_STATX_DONT_SYNC 0x4000 /* - Don't sync attributes with the server */
+#define AT_RECURSIVE 0x8000 /* Apply to the entire subtree */
+
#endif /* _UAPI_LINUX_FCNTL_H */
diff --git a/tools/include/uapi/linux/fs.h b/tools/include/uapi/linux/fs.h
index 121e82ce296b..2a616aa3f686 100644
--- a/tools/include/uapi/linux/fs.h
+++ b/tools/include/uapi/linux/fs.h
@@ -311,6 +311,7 @@ struct fscrypt_key {
#define FS_NOCOW_FL 0x00800000 /* Do not cow file */
#define FS_INLINE_DATA_FL 0x10000000 /* Reserved for ext4 */
#define FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */
+#define FS_CASEFOLD_FL 0x40000000 /* Folder is case insensitive */
#define FS_RESERVED_FL 0x80000000 /* reserved for ext2 lib */
#define FS_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */
@@ -320,6 +321,9 @@ struct fscrypt_key {
#define SYNC_FILE_RANGE_WAIT_BEFORE 1
#define SYNC_FILE_RANGE_WRITE 2
#define SYNC_FILE_RANGE_WAIT_AFTER 4
+#define SYNC_FILE_RANGE_WRITE_AND_WAIT (SYNC_FILE_RANGE_WRITE | \
+ SYNC_FILE_RANGE_WAIT_BEFORE | \
+ SYNC_FILE_RANGE_WAIT_AFTER)
/*
* Flags for preadv2/pwritev2:
diff --git a/tools/include/uapi/linux/if_link.h b/tools/include/uapi/linux/if_link.h
index 5b225ff63b48..4a8c02cafa9a 100644
--- a/tools/include/uapi/linux/if_link.h
+++ b/tools/include/uapi/linux/if_link.h
@@ -636,6 +636,7 @@ enum {
IFLA_BOND_AD_USER_PORT_KEY,
IFLA_BOND_AD_ACTOR_SYSTEM,
IFLA_BOND_TLB_DYNAMIC_LB,
+ IFLA_BOND_PEER_NOTIF_DELAY,
__IFLA_BOND_MAX,
};
@@ -694,6 +695,7 @@ enum {
IFLA_VF_IB_NODE_GUID, /* VF Infiniband node GUID */
IFLA_VF_IB_PORT_GUID, /* VF Infiniband port GUID */
IFLA_VF_VLAN_LIST, /* nested list of vlans, option for QinQ */
+ IFLA_VF_BROADCAST, /* VF broadcast */
__IFLA_VF_MAX,
};
@@ -704,6 +706,10 @@ struct ifla_vf_mac {
__u8 mac[32]; /* MAX_ADDR_LEN */
};
+struct ifla_vf_broadcast {
+ __u8 broadcast[32];
+};
+
struct ifla_vf_vlan {
__u32 vf;
__u32 vlan; /* 0 - 4095, 0 disables VLAN filter */
diff --git a/tools/include/uapi/linux/if_tun.h b/tools/include/uapi/linux/if_tun.h
new file mode 100644
index 000000000000..454ae31b93c7
--- /dev/null
+++ b/tools/include/uapi/linux/if_tun.h
@@ -0,0 +1,114 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Universal TUN/TAP device driver.
+ * Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _UAPI__IF_TUN_H
+#define _UAPI__IF_TUN_H
+
+#include <linux/types.h>
+#include <linux/if_ether.h>
+#include <linux/filter.h>
+
+/* Read queue size */
+#define TUN_READQ_SIZE 500
+/* TUN device type flags: deprecated. Use IFF_TUN/IFF_TAP instead. */
+#define TUN_TUN_DEV IFF_TUN
+#define TUN_TAP_DEV IFF_TAP
+#define TUN_TYPE_MASK 0x000f
+
+/* Ioctl defines */
+#define TUNSETNOCSUM _IOW('T', 200, int)
+#define TUNSETDEBUG _IOW('T', 201, int)
+#define TUNSETIFF _IOW('T', 202, int)
+#define TUNSETPERSIST _IOW('T', 203, int)
+#define TUNSETOWNER _IOW('T', 204, int)
+#define TUNSETLINK _IOW('T', 205, int)
+#define TUNSETGROUP _IOW('T', 206, int)
+#define TUNGETFEATURES _IOR('T', 207, unsigned int)
+#define TUNSETOFFLOAD _IOW('T', 208, unsigned int)
+#define TUNSETTXFILTER _IOW('T', 209, unsigned int)
+#define TUNGETIFF _IOR('T', 210, unsigned int)
+#define TUNGETSNDBUF _IOR('T', 211, int)
+#define TUNSETSNDBUF _IOW('T', 212, int)
+#define TUNATTACHFILTER _IOW('T', 213, struct sock_fprog)
+#define TUNDETACHFILTER _IOW('T', 214, struct sock_fprog)
+#define TUNGETVNETHDRSZ _IOR('T', 215, int)
+#define TUNSETVNETHDRSZ _IOW('T', 216, int)
+#define TUNSETQUEUE _IOW('T', 217, int)
+#define TUNSETIFINDEX _IOW('T', 218, unsigned int)
+#define TUNGETFILTER _IOR('T', 219, struct sock_fprog)
+#define TUNSETVNETLE _IOW('T', 220, int)
+#define TUNGETVNETLE _IOR('T', 221, int)
+/* The TUNSETVNETBE and TUNGETVNETBE ioctls are for cross-endian support on
+ * little-endian hosts. Not all kernel configurations support them, but all
+ * configurations that support SET also support GET.
+ */
+#define TUNSETVNETBE _IOW('T', 222, int)
+#define TUNGETVNETBE _IOR('T', 223, int)
+#define TUNSETSTEERINGEBPF _IOR('T', 224, int)
+#define TUNSETFILTEREBPF _IOR('T', 225, int)
+#define TUNSETCARRIER _IOW('T', 226, int)
+#define TUNGETDEVNETNS _IO('T', 227)
+
+/* TUNSETIFF ifr flags */
+#define IFF_TUN 0x0001
+#define IFF_TAP 0x0002
+#define IFF_NAPI 0x0010
+#define IFF_NAPI_FRAGS 0x0020
+#define IFF_NO_PI 0x1000
+/* This flag has no real effect */
+#define IFF_ONE_QUEUE 0x2000
+#define IFF_VNET_HDR 0x4000
+#define IFF_TUN_EXCL 0x8000
+#define IFF_MULTI_QUEUE 0x0100
+#define IFF_ATTACH_QUEUE 0x0200
+#define IFF_DETACH_QUEUE 0x0400
+/* read-only flag */
+#define IFF_PERSIST 0x0800
+#define IFF_NOFILTER 0x1000
+
+/* Socket options */
+#define TUN_TX_TIMESTAMP 1
+
+/* Features for GSO (TUNSETOFFLOAD). */
+#define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */
+#define TUN_F_TSO4 0x02 /* I can handle TSO for IPv4 packets */
+#define TUN_F_TSO6 0x04 /* I can handle TSO for IPv6 packets */
+#define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */
+#define TUN_F_UFO 0x10 /* I can handle UFO packets */
+
+/* Protocol info prepended to the packets (when IFF_NO_PI is not set) */
+#define TUN_PKT_STRIP 0x0001
+struct tun_pi {
+ __u16 flags;
+ __be16 proto;
+};
+
+/*
+ * Filter spec (used for SETXXFILTER ioctls)
+ * This stuff is applicable only to the TAP (Ethernet) devices.
+ * If the count is zero the filter is disabled and the driver accepts
+ * all packets (promisc mode).
+ * If the filter is enabled in order to accept broadcast packets
+ * broadcast addr must be explicitly included in the addr list.
+ */
+#define TUN_FLT_ALLMULTI 0x0001 /* Accept all multicast packets */
+struct tun_filter {
+ __u16 flags; /* TUN_FLT_ flags see above */
+ __u16 count; /* Number of addresses */
+ __u8 addr[0][ETH_ALEN];
+};
+
+#endif /* _UAPI__IF_TUN_H */
diff --git a/tools/include/uapi/linux/if_xdp.h b/tools/include/uapi/linux/if_xdp.h
index caed8b1614ff..be328c59389d 100644
--- a/tools/include/uapi/linux/if_xdp.h
+++ b/tools/include/uapi/linux/if_xdp.h
@@ -16,6 +16,18 @@
#define XDP_SHARED_UMEM (1 << 0)
#define XDP_COPY (1 << 1) /* Force copy-mode */
#define XDP_ZEROCOPY (1 << 2) /* Force zero-copy mode */
+/* If this option is set, the driver might go sleep and in that case
+ * the XDP_RING_NEED_WAKEUP flag in the fill and/or Tx rings will be
+ * set. If it is set, the application need to explicitly wake up the
+ * driver with a poll() (Rx and Tx) or sendto() (Tx only). If you are
+ * running the driver and the application on the same core, you should
+ * use this option so that the kernel will yield to the user space
+ * application.
+ */
+#define XDP_USE_NEED_WAKEUP (1 << 3)
+
+/* Flags for xsk_umem_config flags */
+#define XDP_UMEM_UNALIGNED_CHUNK_FLAG (1 << 0)
struct sockaddr_xdp {
__u16 sxdp_family;
@@ -25,10 +37,14 @@ struct sockaddr_xdp {
__u32 sxdp_shared_umem_fd;
};
+/* XDP_RING flags */
+#define XDP_RING_NEED_WAKEUP (1 << 0)
+
struct xdp_ring_offset {
__u64 producer;
__u64 consumer;
__u64 desc;
+ __u64 flags;
};
struct xdp_mmap_offsets {
@@ -46,12 +62,14 @@ struct xdp_mmap_offsets {
#define XDP_UMEM_FILL_RING 5
#define XDP_UMEM_COMPLETION_RING 6
#define XDP_STATISTICS 7
+#define XDP_OPTIONS 8
struct xdp_umem_reg {
__u64 addr; /* Start of packet data area */
__u64 len; /* Length of packet data area */
__u32 chunk_size;
__u32 headroom;
+ __u32 flags;
};
struct xdp_statistics {
@@ -60,12 +78,24 @@ struct xdp_statistics {
__u64 tx_invalid_descs; /* Dropped due to invalid descriptor */
};
+struct xdp_options {
+ __u32 flags;
+};
+
+/* Flags for the flags field of struct xdp_options */
+#define XDP_OPTIONS_ZEROCOPY (1 << 0)
+
/* Pgoff for mmaping the rings */
#define XDP_PGOFF_RX_RING 0
#define XDP_PGOFF_TX_RING 0x80000000
#define XDP_UMEM_PGOFF_FILL_RING 0x100000000ULL
#define XDP_UMEM_PGOFF_COMPLETION_RING 0x180000000ULL
+/* Masks for unaligned chunks mode */
+#define XSK_UNALIGNED_BUF_OFFSET_SHIFT 48
+#define XSK_UNALIGNED_BUF_ADDR_MASK \
+ ((1ULL << XSK_UNALIGNED_BUF_OFFSET_SHIFT) - 1)
+
/* Rx/Tx descriptor */
struct xdp_desc {
__u64 addr;
diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h
index 6d4ea4b6c922..5e3f12d5359e 100644
--- a/tools/include/uapi/linux/kvm.h
+++ b/tools/include/uapi/linux/kvm.h
@@ -116,7 +116,7 @@ struct kvm_irq_level {
* ACPI gsi notion of irq.
* For IA-64 (APIC model) IOAPIC0: irq 0-23; IOAPIC1: irq 24-47..
* For X86 (standard AT mode) PIC0/1: irq 0-15. IOAPIC0: 0-23..
- * For ARM: See Documentation/virtual/kvm/api.txt
+ * For ARM: See Documentation/virt/kvm/api.txt
*/
union {
__u32 irq;
@@ -696,9 +696,11 @@ struct kvm_ioeventfd {
#define KVM_X86_DISABLE_EXITS_MWAIT (1 << 0)
#define KVM_X86_DISABLE_EXITS_HLT (1 << 1)
#define KVM_X86_DISABLE_EXITS_PAUSE (1 << 2)
+#define KVM_X86_DISABLE_EXITS_CSTATE (1 << 3)
#define KVM_X86_DISABLE_VALID_EXITS (KVM_X86_DISABLE_EXITS_MWAIT | \
KVM_X86_DISABLE_EXITS_HLT | \
- KVM_X86_DISABLE_EXITS_PAUSE)
+ KVM_X86_DISABLE_EXITS_PAUSE | \
+ KVM_X86_DISABLE_EXITS_CSTATE)
/* for KVM_ENABLE_CAP */
struct kvm_enable_cap {
@@ -986,8 +988,14 @@ struct kvm_ppc_resize_hpt {
#define KVM_CAP_HYPERV_ENLIGHTENED_VMCS 163
#define KVM_CAP_EXCEPTION_PAYLOAD 164
#define KVM_CAP_ARM_VM_IPA_SIZE 165
-#define KVM_CAP_MANUAL_DIRTY_LOG_PROTECT 166
+#define KVM_CAP_MANUAL_DIRTY_LOG_PROTECT 166 /* Obsolete */
#define KVM_CAP_HYPERV_CPUID 167
+#define KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 168
+#define KVM_CAP_PPC_IRQ_XIVE 169
+#define KVM_CAP_ARM_SVE 170
+#define KVM_CAP_ARM_PTRAUTH_ADDRESS 171
+#define KVM_CAP_ARM_PTRAUTH_GENERIC 172
+#define KVM_CAP_PMU_EVENT_FILTER 173
#ifdef KVM_CAP_IRQ_ROUTING
@@ -1078,7 +1086,7 @@ struct kvm_xen_hvm_config {
*
* KVM_IRQFD_FLAG_RESAMPLE indicates resamplefd is valid and specifies
* the irqfd to operate in resampling mode for level triggered interrupt
- * emulation. See Documentation/virtual/kvm/api.txt.
+ * emulation. See Documentation/virt/kvm/api.txt.
*/
#define KVM_IRQFD_FLAG_RESAMPLE (1 << 1)
@@ -1145,6 +1153,7 @@ struct kvm_dirty_tlb {
#define KVM_REG_SIZE_U256 0x0050000000000000ULL
#define KVM_REG_SIZE_U512 0x0060000000000000ULL
#define KVM_REG_SIZE_U1024 0x0070000000000000ULL
+#define KVM_REG_SIZE_U2048 0x0080000000000000ULL
struct kvm_reg_list {
__u64 n; /* number of regs */
@@ -1211,6 +1220,8 @@ enum kvm_device_type {
#define KVM_DEV_TYPE_ARM_VGIC_V3 KVM_DEV_TYPE_ARM_VGIC_V3
KVM_DEV_TYPE_ARM_VGIC_ITS,
#define KVM_DEV_TYPE_ARM_VGIC_ITS KVM_DEV_TYPE_ARM_VGIC_ITS
+ KVM_DEV_TYPE_XIVE,
+#define KVM_DEV_TYPE_XIVE KVM_DEV_TYPE_XIVE
KVM_DEV_TYPE_MAX,
};
@@ -1319,6 +1330,8 @@ struct kvm_s390_ucas_mapping {
#define KVM_PPC_GET_RMMU_INFO _IOW(KVMIO, 0xb0, struct kvm_ppc_rmmu_info)
/* Available with KVM_CAP_PPC_GET_CPU_CHAR */
#define KVM_PPC_GET_CPU_CHAR _IOR(KVMIO, 0xb1, struct kvm_ppc_cpu_char)
+/* Available with KVM_CAP_PMU_EVENT_FILTER */
+#define KVM_SET_PMU_EVENT_FILTER _IOW(KVMIO, 0xb2, struct kvm_pmu_event_filter)
/* ioctl for vm fd */
#define KVM_CREATE_DEVICE _IOWR(KVMIO, 0xe0, struct kvm_create_device)
@@ -1434,12 +1447,15 @@ struct kvm_enc_region {
#define KVM_GET_NESTED_STATE _IOWR(KVMIO, 0xbe, struct kvm_nested_state)
#define KVM_SET_NESTED_STATE _IOW(KVMIO, 0xbf, struct kvm_nested_state)
-/* Available with KVM_CAP_MANUAL_DIRTY_LOG_PROTECT */
+/* Available with KVM_CAP_MANUAL_DIRTY_LOG_PROTECT_2 */
#define KVM_CLEAR_DIRTY_LOG _IOWR(KVMIO, 0xc0, struct kvm_clear_dirty_log)
/* Available with KVM_CAP_HYPERV_CPUID */
#define KVM_GET_SUPPORTED_HV_CPUID _IOWR(KVMIO, 0xc1, struct kvm_cpuid2)
+/* Available with KVM_CAP_ARM_SVE */
+#define KVM_ARM_VCPU_FINALIZE _IOW(KVMIO, 0xc2, int)
+
/* Secure Encrypted Virtualization command */
enum sev_cmd_id {
/* Guest initialization commands */
diff --git a/tools/include/uapi/linux/mount.h b/tools/include/uapi/linux/mount.h
index 3f9ec42510b0..96a0240f23fe 100644
--- a/tools/include/uapi/linux/mount.h
+++ b/tools/include/uapi/linux/mount.h
@@ -55,4 +55,66 @@
#define MS_MGC_VAL 0xC0ED0000
#define MS_MGC_MSK 0xffff0000
+/*
+ * open_tree() flags.
+ */
+#define OPEN_TREE_CLONE 1 /* Clone the target tree and attach the clone */
+#define OPEN_TREE_CLOEXEC O_CLOEXEC /* Close the file on execve() */
+
+/*
+ * move_mount() flags.
+ */
+#define MOVE_MOUNT_F_SYMLINKS 0x00000001 /* Follow symlinks on from path */
+#define MOVE_MOUNT_F_AUTOMOUNTS 0x00000002 /* Follow automounts on from path */
+#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */
+#define MOVE_MOUNT_T_SYMLINKS 0x00000010 /* Follow symlinks on to path */
+#define MOVE_MOUNT_T_AUTOMOUNTS 0x00000020 /* Follow automounts on to path */
+#define MOVE_MOUNT_T_EMPTY_PATH 0x00000040 /* Empty to path permitted */
+#define MOVE_MOUNT__MASK 0x00000077
+
+/*
+ * fsopen() flags.
+ */
+#define FSOPEN_CLOEXEC 0x00000001
+
+/*
+ * fspick() flags.
+ */
+#define FSPICK_CLOEXEC 0x00000001
+#define FSPICK_SYMLINK_NOFOLLOW 0x00000002
+#define FSPICK_NO_AUTOMOUNT 0x00000004
+#define FSPICK_EMPTY_PATH 0x00000008
+
+/*
+ * The type of fsconfig() call made.
+ */
+enum fsconfig_command {
+ FSCONFIG_SET_FLAG = 0, /* Set parameter, supplying no value */
+ FSCONFIG_SET_STRING = 1, /* Set parameter, supplying a string value */
+ FSCONFIG_SET_BINARY = 2, /* Set parameter, supplying a binary blob value */
+ FSCONFIG_SET_PATH = 3, /* Set parameter, supplying an object by path */
+ FSCONFIG_SET_PATH_EMPTY = 4, /* Set parameter, supplying an object by (empty) path */
+ FSCONFIG_SET_FD = 5, /* Set parameter, supplying an object by fd */
+ FSCONFIG_CMD_CREATE = 6, /* Invoke superblock creation */
+ FSCONFIG_CMD_RECONFIGURE = 7, /* Invoke superblock reconfiguration */
+};
+
+/*
+ * fsmount() flags.
+ */
+#define FSMOUNT_CLOEXEC 0x00000001
+
+/*
+ * Mount attributes.
+ */
+#define MOUNT_ATTR_RDONLY 0x00000001 /* Mount read-only */
+#define MOUNT_ATTR_NOSUID 0x00000002 /* Ignore suid and sgid bits */
+#define MOUNT_ATTR_NODEV 0x00000004 /* Disallow access to device special files */
+#define MOUNT_ATTR_NOEXEC 0x00000008 /* Disallow program execution */
+#define MOUNT_ATTR__ATIME 0x00000070 /* Setting on how atime should be updated */
+#define MOUNT_ATTR_RELATIME 0x00000000 /* - Update atime relative to mtime/ctime. */
+#define MOUNT_ATTR_NOATIME 0x00000010 /* - Do not update access times. */
+#define MOUNT_ATTR_STRICTATIME 0x00000020 /* - Always perform atime updates */
+#define MOUNT_ATTR_NODIRATIME 0x00000080 /* Do not update directory access times */
+
#endif /* _UAPI_LINUX_MOUNT_H */
diff --git a/tools/include/uapi/linux/perf_event.h b/tools/include/uapi/linux/perf_event.h
index 7198ddd0c6b1..bb7b271397a6 100644
--- a/tools/include/uapi/linux/perf_event.h
+++ b/tools/include/uapi/linux/perf_event.h
@@ -374,7 +374,8 @@ struct perf_event_attr {
namespaces : 1, /* include namespaces data */
ksymbol : 1, /* include ksymbol events */
bpf_event : 1, /* include bpf events */
- __reserved_1 : 33;
+ aux_output : 1, /* generate AUX records instead of events */
+ __reserved_1 : 32;
union {
__u32 wakeup_events; /* wakeup every n events */
diff --git a/tools/include/uapi/linux/pkt_cls.h b/tools/include/uapi/linux/pkt_cls.h
index 401d0c1e612d..12153771396a 100644
--- a/tools/include/uapi/linux/pkt_cls.h
+++ b/tools/include/uapi/linux/pkt_cls.h
@@ -257,7 +257,7 @@ enum {
TCA_FW_UNSPEC,
TCA_FW_CLASSID,
TCA_FW_POLICE,
- TCA_FW_INDEV, /* used by CONFIG_NET_CLS_IND */
+ TCA_FW_INDEV,
TCA_FW_ACT, /* used by CONFIG_NET_CLS_ACT */
TCA_FW_MASK,
__TCA_FW_MAX
diff --git a/tools/include/uapi/linux/sched.h b/tools/include/uapi/linux/sched.h
index 22627f80063e..b3105ac1381a 100644
--- a/tools/include/uapi/linux/sched.h
+++ b/tools/include/uapi/linux/sched.h
@@ -2,6 +2,8 @@
#ifndef _UAPI_LINUX_SCHED_H
#define _UAPI_LINUX_SCHED_H
+#include <linux/types.h>
+
/*
* cloning flags:
*/
@@ -10,6 +12,7 @@
#define CLONE_FS 0x00000200 /* set if fs info shared between processes */
#define CLONE_FILES 0x00000400 /* set if open files shared between processes */
#define CLONE_SIGHAND 0x00000800 /* set if signal handlers and blocked signals shared */
+#define CLONE_PIDFD 0x00001000 /* set if a pidfd should be placed in parent */
#define CLONE_PTRACE 0x00002000 /* set if we want to let tracing continue on the child too */
#define CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release */
#define CLONE_PARENT 0x00008000 /* set if we want to have the same parent as the cloner */
@@ -31,6 +34,20 @@
#define CLONE_IO 0x80000000 /* Clone io context */
/*
+ * Arguments for the clone3 syscall
+ */
+struct clone_args {
+ __aligned_u64 flags;
+ __aligned_u64 pidfd;
+ __aligned_u64 child_tid;
+ __aligned_u64 parent_tid;
+ __aligned_u64 exit_signal;
+ __aligned_u64 stack;
+ __aligned_u64 stack_size;
+ __aligned_u64 tls;
+};
+
+/*
* Scheduling policies
*/
#define SCHED_NORMAL 0
@@ -50,9 +67,21 @@
#define SCHED_FLAG_RESET_ON_FORK 0x01
#define SCHED_FLAG_RECLAIM 0x02
#define SCHED_FLAG_DL_OVERRUN 0x04
+#define SCHED_FLAG_KEEP_POLICY 0x08
+#define SCHED_FLAG_KEEP_PARAMS 0x10
+#define SCHED_FLAG_UTIL_CLAMP_MIN 0x20
+#define SCHED_FLAG_UTIL_CLAMP_MAX 0x40
+
+#define SCHED_FLAG_KEEP_ALL (SCHED_FLAG_KEEP_POLICY | \
+ SCHED_FLAG_KEEP_PARAMS)
+
+#define SCHED_FLAG_UTIL_CLAMP (SCHED_FLAG_UTIL_CLAMP_MIN | \
+ SCHED_FLAG_UTIL_CLAMP_MAX)
#define SCHED_FLAG_ALL (SCHED_FLAG_RESET_ON_FORK | \
SCHED_FLAG_RECLAIM | \
- SCHED_FLAG_DL_OVERRUN)
+ SCHED_FLAG_DL_OVERRUN | \
+ SCHED_FLAG_KEEP_ALL | \
+ SCHED_FLAG_UTIL_CLAMP)
#endif /* _UAPI_LINUX_SCHED_H */
diff --git a/tools/include/uapi/linux/usbdevice_fs.h b/tools/include/uapi/linux/usbdevice_fs.h
index 964e87217be4..78efe870c2b7 100644
--- a/tools/include/uapi/linux/usbdevice_fs.h
+++ b/tools/include/uapi/linux/usbdevice_fs.h
@@ -76,6 +76,26 @@ struct usbdevfs_connectinfo {
unsigned char slow;
};
+struct usbdevfs_conninfo_ex {
+ __u32 size; /* Size of the structure from the kernel's */
+ /* point of view. Can be used by userspace */
+ /* to determine how much data can be */
+ /* used/trusted. */
+ __u32 busnum; /* USB bus number, as enumerated by the */
+ /* kernel, the device is connected to. */
+ __u32 devnum; /* Device address on the bus. */
+ __u32 speed; /* USB_SPEED_* constants from ch9.h */
+ __u8 num_ports; /* Number of ports the device is connected */
+ /* to on the way to the root hub. It may */
+ /* be bigger than size of 'ports' array so */
+ /* userspace can detect overflows. */
+ __u8 ports[7]; /* List of ports on the way from the root */
+ /* hub to the device. Current limit in */
+ /* USB specification is 7 tiers (root hub, */
+ /* 5 intermediate hubs, device), which */
+ /* gives at most 6 port entries. */
+};
+
#define USBDEVFS_URB_SHORT_NOT_OK 0x01
#define USBDEVFS_URB_ISO_ASAP 0x02
#define USBDEVFS_URB_BULK_CONTINUATION 0x04
@@ -137,6 +157,7 @@ struct usbdevfs_hub_portinfo {
#define USBDEVFS_CAP_REAP_AFTER_DISCONNECT 0x10
#define USBDEVFS_CAP_MMAP 0x20
#define USBDEVFS_CAP_DROP_PRIVILEGES 0x40
+#define USBDEVFS_CAP_CONNINFO_EX 0x80
/* USBDEVFS_DISCONNECT_CLAIM flags & struct */
@@ -197,5 +218,10 @@ struct usbdevfs_streams {
#define USBDEVFS_FREE_STREAMS _IOR('U', 29, struct usbdevfs_streams)
#define USBDEVFS_DROP_PRIVILEGES _IOW('U', 30, __u32)
#define USBDEVFS_GET_SPEED _IO('U', 31)
+/*
+ * Returns struct usbdevfs_conninfo_ex; length is variable to allow
+ * extending size of the data returned.
+ */
+#define USBDEVFS_CONNINFO_EX(len) _IOC(_IOC_READ, 'U', 32, len)
#endif /* _UAPI_LINUX_USBDEVICE_FS_H */
diff --git a/tools/io_uring/Makefile b/tools/io_uring/Makefile
index f79522fc37b5..00f146c54c53 100644
--- a/tools/io_uring/Makefile
+++ b/tools/io_uring/Makefile
@@ -8,7 +8,7 @@ all: io_uring-cp io_uring-bench
$(CC) $(CFLAGS) -o $@ $^
io_uring-bench: syscall.o io_uring-bench.o
- $(CC) $(CFLAGS) $(LDLIBS) -o $@ $^
+ $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS)
io_uring-cp: setup.o syscall.o queue.o
diff --git a/tools/io_uring/io_uring-cp.c b/tools/io_uring/io_uring-cp.c
index 633f65bb43a7..81461813ec62 100644
--- a/tools/io_uring/io_uring-cp.c
+++ b/tools/io_uring/io_uring-cp.c
@@ -13,6 +13,7 @@
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
+#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
@@ -85,11 +86,16 @@ static int queue_read(struct io_uring *ring, off_t size, off_t offset)
struct io_uring_sqe *sqe;
struct io_data *data;
+ data = malloc(size + sizeof(*data));
+ if (!data)
+ return 1;
+
sqe = io_uring_get_sqe(ring);
- if (!sqe)
+ if (!sqe) {
+ free(data);
return 1;
+ }
- data = malloc(size + sizeof(*data));
data->read = 1;
data->offset = data->first_offset = offset;
@@ -166,22 +172,23 @@ static int copy_file(struct io_uring *ring, off_t insize)
struct io_data *data;
if (!got_comp) {
- ret = io_uring_wait_completion(ring, &cqe);
+ ret = io_uring_wait_cqe(ring, &cqe);
got_comp = 1;
} else
- ret = io_uring_get_completion(ring, &cqe);
+ ret = io_uring_peek_cqe(ring, &cqe);
if (ret < 0) {
- fprintf(stderr, "io_uring_get_completion: %s\n",
+ fprintf(stderr, "io_uring_peek_cqe: %s\n",
strerror(-ret));
return 1;
}
if (!cqe)
break;
- data = (struct io_data *) (uintptr_t) cqe->user_data;
+ data = io_uring_cqe_get_data(cqe);
if (cqe->res < 0) {
if (cqe->res == -EAGAIN) {
queue_prepped(ring, data);
+ io_uring_cqe_seen(ring, cqe);
continue;
}
fprintf(stderr, "cqe failed: %s\n",
@@ -193,6 +200,7 @@ static int copy_file(struct io_uring *ring, off_t insize)
data->iov.iov_len -= cqe->res;
data->offset += cqe->res;
queue_prepped(ring, data);
+ io_uring_cqe_seen(ring, cqe);
continue;
}
@@ -209,6 +217,7 @@ static int copy_file(struct io_uring *ring, off_t insize)
free(data);
writes--;
}
+ io_uring_cqe_seen(ring, cqe);
}
}
diff --git a/tools/io_uring/liburing.h b/tools/io_uring/liburing.h
index cab0f50257ba..5f305c86b892 100644
--- a/tools/io_uring/liburing.h
+++ b/tools/io_uring/liburing.h
@@ -1,10 +1,16 @@
#ifndef LIB_URING_H
#define LIB_URING_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include <sys/uio.h>
#include <signal.h>
#include <string.h>
#include "../../include/uapi/linux/io_uring.h"
+#include <inttypes.h>
+#include "barrier.h"
/*
* Library interface to io_uring
@@ -46,7 +52,7 @@ struct io_uring {
* System calls
*/
extern int io_uring_setup(unsigned entries, struct io_uring_params *p);
-extern int io_uring_enter(unsigned fd, unsigned to_submit,
+extern int io_uring_enter(int fd, unsigned to_submit,
unsigned min_complete, unsigned flags, sigset_t *sig);
extern int io_uring_register(int fd, unsigned int opcode, void *arg,
unsigned int nr_args);
@@ -59,14 +65,33 @@ extern int io_uring_queue_init(unsigned entries, struct io_uring *ring,
extern int io_uring_queue_mmap(int fd, struct io_uring_params *p,
struct io_uring *ring);
extern void io_uring_queue_exit(struct io_uring *ring);
-extern int io_uring_get_completion(struct io_uring *ring,
+extern int io_uring_peek_cqe(struct io_uring *ring,
struct io_uring_cqe **cqe_ptr);
-extern int io_uring_wait_completion(struct io_uring *ring,
+extern int io_uring_wait_cqe(struct io_uring *ring,
struct io_uring_cqe **cqe_ptr);
extern int io_uring_submit(struct io_uring *ring);
extern struct io_uring_sqe *io_uring_get_sqe(struct io_uring *ring);
/*
+ * Must be called after io_uring_{peek,wait}_cqe() after the cqe has
+ * been processed by the application.
+ */
+static inline void io_uring_cqe_seen(struct io_uring *ring,
+ struct io_uring_cqe *cqe)
+{
+ if (cqe) {
+ struct io_uring_cq *cq = &ring->cq;
+
+ (*cq->khead)++;
+ /*
+ * Ensure that the kernel sees our new head, the kernel has
+ * the matching read barrier.
+ */
+ write_barrier();
+ }
+}
+
+/*
* Command prep helpers
*/
static inline void io_uring_sqe_set_data(struct io_uring_sqe *sqe, void *data)
@@ -74,8 +99,14 @@ static inline void io_uring_sqe_set_data(struct io_uring_sqe *sqe, void *data)
sqe->user_data = (unsigned long) data;
}
+static inline void *io_uring_cqe_get_data(struct io_uring_cqe *cqe)
+{
+ return (void *) (uintptr_t) cqe->user_data;
+}
+
static inline void io_uring_prep_rw(int op, struct io_uring_sqe *sqe, int fd,
- void *addr, unsigned len, off_t offset)
+ const void *addr, unsigned len,
+ off_t offset)
{
memset(sqe, 0, sizeof(*sqe));
sqe->opcode = op;
@@ -86,8 +117,8 @@ static inline void io_uring_prep_rw(int op, struct io_uring_sqe *sqe, int fd,
}
static inline void io_uring_prep_readv(struct io_uring_sqe *sqe, int fd,
- struct iovec *iovecs, unsigned nr_vecs,
- off_t offset)
+ const struct iovec *iovecs,
+ unsigned nr_vecs, off_t offset)
{
io_uring_prep_rw(IORING_OP_READV, sqe, fd, iovecs, nr_vecs, offset);
}
@@ -100,14 +131,14 @@ static inline void io_uring_prep_read_fixed(struct io_uring_sqe *sqe, int fd,
}
static inline void io_uring_prep_writev(struct io_uring_sqe *sqe, int fd,
- struct iovec *iovecs, unsigned nr_vecs,
- off_t offset)
+ const struct iovec *iovecs,
+ unsigned nr_vecs, off_t offset)
{
io_uring_prep_rw(IORING_OP_WRITEV, sqe, fd, iovecs, nr_vecs, offset);
}
static inline void io_uring_prep_write_fixed(struct io_uring_sqe *sqe, int fd,
- void *buf, unsigned nbytes,
+ const void *buf, unsigned nbytes,
off_t offset)
{
io_uring_prep_rw(IORING_OP_WRITE_FIXED, sqe, fd, buf, nbytes, offset);
@@ -131,13 +162,22 @@ static inline void io_uring_prep_poll_remove(struct io_uring_sqe *sqe,
}
static inline void io_uring_prep_fsync(struct io_uring_sqe *sqe, int fd,
- int datasync)
+ unsigned fsync_flags)
{
memset(sqe, 0, sizeof(*sqe));
sqe->opcode = IORING_OP_FSYNC;
sqe->fd = fd;
- if (datasync)
- sqe->fsync_flags = IORING_FSYNC_DATASYNC;
+ sqe->fsync_flags = fsync_flags;
+}
+
+static inline void io_uring_prep_nop(struct io_uring_sqe *sqe)
+{
+ memset(sqe, 0, sizeof(*sqe));
+ sqe->opcode = IORING_OP_NOP;
+}
+
+#ifdef __cplusplus
}
+#endif
#endif
diff --git a/tools/io_uring/queue.c b/tools/io_uring/queue.c
index 88505e873ad9..321819c132c7 100644
--- a/tools/io_uring/queue.c
+++ b/tools/io_uring/queue.c
@@ -8,8 +8,8 @@
#include "liburing.h"
#include "barrier.h"
-static int __io_uring_get_completion(struct io_uring *ring,
- struct io_uring_cqe **cqe_ptr, int wait)
+static int __io_uring_get_cqe(struct io_uring *ring,
+ struct io_uring_cqe **cqe_ptr, int wait)
{
struct io_uring_cq *cq = &ring->cq;
const unsigned mask = *cq->kring_mask;
@@ -39,34 +39,25 @@ static int __io_uring_get_completion(struct io_uring *ring,
return -errno;
} while (1);
- if (*cqe_ptr) {
- *cq->khead = head + 1;
- /*
- * Ensure that the kernel sees our new head, the kernel has
- * the matching read barrier.
- */
- write_barrier();
- }
-
return 0;
}
/*
- * Return an IO completion, if one is readily available
+ * Return an IO completion, if one is readily available. Returns 0 with
+ * cqe_ptr filled in on success, -errno on failure.
*/
-int io_uring_get_completion(struct io_uring *ring,
- struct io_uring_cqe **cqe_ptr)
+int io_uring_peek_cqe(struct io_uring *ring, struct io_uring_cqe **cqe_ptr)
{
- return __io_uring_get_completion(ring, cqe_ptr, 0);
+ return __io_uring_get_cqe(ring, cqe_ptr, 0);
}
/*
- * Return an IO completion, waiting for it if necessary
+ * Return an IO completion, waiting for it if necessary. Returns 0 with
+ * cqe_ptr filled in on success, -errno on failure.
*/
-int io_uring_wait_completion(struct io_uring *ring,
- struct io_uring_cqe **cqe_ptr)
+int io_uring_wait_cqe(struct io_uring *ring, struct io_uring_cqe **cqe_ptr)
{
- return __io_uring_get_completion(ring, cqe_ptr, 1);
+ return __io_uring_get_cqe(ring, cqe_ptr, 1);
}
/*
@@ -78,7 +69,7 @@ int io_uring_submit(struct io_uring *ring)
{
struct io_uring_sq *sq = &ring->sq;
const unsigned mask = *sq->kring_mask;
- unsigned ktail, ktail_next, submitted;
+ unsigned ktail, ktail_next, submitted, to_submit;
int ret;
/*
@@ -100,7 +91,8 @@ int io_uring_submit(struct io_uring *ring)
*/
submitted = 0;
ktail = ktail_next = *sq->ktail;
- while (sq->sqe_head < sq->sqe_tail) {
+ to_submit = sq->sqe_tail - sq->sqe_head;
+ while (to_submit--) {
ktail_next++;
read_barrier();
@@ -136,7 +128,7 @@ submit:
if (ret < 0)
return -errno;
- return 0;
+ return ret;
}
/*
diff --git a/tools/io_uring/setup.c b/tools/io_uring/setup.c
index 4da19a77132c..0b50fcd78520 100644
--- a/tools/io_uring/setup.c
+++ b/tools/io_uring/setup.c
@@ -27,7 +27,7 @@ static int io_uring_mmap(int fd, struct io_uring_params *p,
sq->kdropped = ptr + p->sq_off.dropped;
sq->array = ptr + p->sq_off.array;
- size = p->sq_entries * sizeof(struct io_uring_sqe),
+ size = p->sq_entries * sizeof(struct io_uring_sqe);
sq->sqes = mmap(0, size, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_POPULATE, fd,
IORING_OFF_SQES);
@@ -79,7 +79,7 @@ int io_uring_queue_mmap(int fd, struct io_uring_params *p, struct io_uring *ring
int io_uring_queue_init(unsigned entries, struct io_uring *ring, unsigned flags)
{
struct io_uring_params p;
- int fd;
+ int fd, ret;
memset(&p, 0, sizeof(p));
p.flags = flags;
@@ -88,7 +88,11 @@ int io_uring_queue_init(unsigned entries, struct io_uring *ring, unsigned flags)
if (fd < 0)
return fd;
- return io_uring_queue_mmap(fd, &p, ring);
+ ret = io_uring_queue_mmap(fd, &p, ring);
+ if (ret)
+ close(fd);
+
+ return ret;
}
void io_uring_queue_exit(struct io_uring *ring)
diff --git a/tools/io_uring/syscall.c b/tools/io_uring/syscall.c
index 6b835e5c6a5b..b22e0aa54e9d 100644
--- a/tools/io_uring/syscall.c
+++ b/tools/io_uring/syscall.c
@@ -7,34 +7,46 @@
#include <signal.h>
#include "liburing.h"
-#if defined(__x86_64) || defined(__i386__)
-#ifndef __NR_sys_io_uring_setup
-#define __NR_sys_io_uring_setup 425
-#endif
-#ifndef __NR_sys_io_uring_enter
-#define __NR_sys_io_uring_enter 426
-#endif
-#ifndef __NR_sys_io_uring_register
-#define __NR_sys_io_uring_register 427
-#endif
-#else
-#error "Arch not supported yet"
+#ifdef __alpha__
+/*
+ * alpha is the only exception, all other architectures
+ * have common numbers for new system calls.
+ */
+# ifndef __NR_io_uring_setup
+# define __NR_io_uring_setup 535
+# endif
+# ifndef __NR_io_uring_enter
+# define __NR_io_uring_enter 536
+# endif
+# ifndef __NR_io_uring_register
+# define __NR_io_uring_register 537
+# endif
+#else /* !__alpha__ */
+# ifndef __NR_io_uring_setup
+# define __NR_io_uring_setup 425
+# endif
+# ifndef __NR_io_uring_enter
+# define __NR_io_uring_enter 426
+# endif
+# ifndef __NR_io_uring_register
+# define __NR_io_uring_register 427
+# endif
#endif
int io_uring_register(int fd, unsigned int opcode, void *arg,
unsigned int nr_args)
{
- return syscall(__NR_sys_io_uring_register, fd, opcode, arg, nr_args);
+ return syscall(__NR_io_uring_register, fd, opcode, arg, nr_args);
}
-int io_uring_setup(unsigned entries, struct io_uring_params *p)
+int io_uring_setup(unsigned int entries, struct io_uring_params *p)
{
- return syscall(__NR_sys_io_uring_setup, entries, p);
+ return syscall(__NR_io_uring_setup, entries, p);
}
-int io_uring_enter(unsigned fd, unsigned to_submit, unsigned min_complete,
- unsigned flags, sigset_t *sig)
+int io_uring_enter(int fd, unsigned int to_submit, unsigned int min_complete,
+ unsigned int flags, sigset_t *sig)
{
- return syscall(__NR_sys_io_uring_enter, fd, to_submit, min_complete,
+ return syscall(__NR_io_uring_enter, fd, to_submit, min_complete,
flags, sig, _NSIG / 8);
}
diff --git a/tools/kvm/kvm_stat/kvm_stat b/tools/kvm/kvm_stat/kvm_stat
index 2ed395b817cb..ad1b9e646c49 100755
--- a/tools/kvm/kvm_stat/kvm_stat
+++ b/tools/kvm/kvm_stat/kvm_stat
@@ -1,4 +1,5 @@
#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-only
#
# top-like utility for displaying kvm statistics
#
@@ -8,8 +9,6 @@
# Authors:
# Avi Kivity <avi@redhat.com>
#
-# This work is licensed under the terms of the GNU GPL, version 2. See
-# the COPYING file in the top-level directory.
"""The kvm_stat module outputs statistics about running KVM VMs
Three different ways of output formatting are available:
@@ -575,8 +574,12 @@ class TracepointProvider(Provider):
def update_fields(self, fields_filter):
"""Refresh fields, applying fields_filter"""
self.fields = [field for field in self._get_available_fields()
- if self.is_field_wanted(fields_filter, field) or
- ARCH.tracepoint_is_child(field)]
+ if self.is_field_wanted(fields_filter, field)]
+ # add parents for child fields - otherwise we won't see any output!
+ for field in self._fields:
+ parent = ARCH.tracepoint_is_child(field)
+ if (parent and parent not in self._fields):
+ self.fields.append(parent)
@staticmethod
def _get_online_cpus():
@@ -735,8 +738,12 @@ class DebugfsProvider(Provider):
def update_fields(self, fields_filter):
"""Refresh fields, applying fields_filter"""
self._fields = [field for field in self._get_available_fields()
- if self.is_field_wanted(fields_filter, field) or
- ARCH.debugfs_is_child(field)]
+ if self.is_field_wanted(fields_filter, field)]
+ # add parents for child fields - otherwise we won't see any output!
+ for field in self._fields:
+ parent = ARCH.debugfs_is_child(field)
+ if (parent and parent not in self._fields):
+ self.fields.append(parent)
@property
def fields(self):
diff --git a/tools/kvm/kvm_stat/kvm_stat.txt b/tools/kvm/kvm_stat/kvm_stat.txt
index 0811d860fe75..c057ba52364e 100644
--- a/tools/kvm/kvm_stat/kvm_stat.txt
+++ b/tools/kvm/kvm_stat/kvm_stat.txt
@@ -34,6 +34,8 @@ INTERACTIVE COMMANDS
*c*:: clear filter
*f*:: filter by regular expression
+ :: *Note*: Child events pull in their parents, and parents' stats summarize
+ all child events, not just the filtered ones
*g*:: filter by guest name/PID
diff --git a/tools/laptop/freefall/freefall.c b/tools/laptop/freefall/freefall.c
index 5e44b20b1848..d29a86cda87f 100644
--- a/tools/laptop/freefall/freefall.c
+++ b/tools/laptop/freefall/freefall.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Disk protection for HP/DELL machines.
*
* Copyright 2008 Eric Piel
* Copyright 2009 Pavel Machek <pavel@ucw.cz>
* Copyright 2012 Sonal Santan
* Copyright 2014 Pali Rohár <pali.rohar@gmail.com>
- *
- * GPLv2.
*/
#include <stdio.h>
diff --git a/tools/leds/get_led_device_info.sh b/tools/leds/get_led_device_info.sh
new file mode 100755
index 000000000000..ccfa442db5fe
--- /dev/null
+++ b/tools/leds/get_led_device_info.sh
@@ -0,0 +1,201 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+
+led_common_defs_path="include/dt-bindings/leds/common.h"
+
+num_args=$#
+if [ $num_args -eq 1 ]; then
+ linux_top=$(dirname `realpath $0` | awk -F/ \
+ '{ \
+ i=1; \
+ while (i <= NF - 2) { \
+ printf $i"/"; \
+ i++; \
+ }; \
+ }')
+ led_defs_path=$linux_top/$led_common_defs_path
+elif [ $num_args -eq 2 ]; then
+ led_defs_path=`realpath $2`
+else
+ echo "Usage: get_led_device_info.sh LED_CDEV_PATH [LED_COMMON_DEFS_PATH]"
+ exit 1
+fi
+
+if [ ! -f $led_defs_path ]; then
+ echo "$led_defs_path doesn't exist"
+ exit 1
+fi
+
+led_cdev_path=`echo $1 | sed s'/\/$//'`
+
+ls "$led_cdev_path/brightness" > /dev/null 2>&1
+if [ $? -ne 0 ]; then
+ echo "Device \"$led_cdev_path\" does not exist."
+ exit 1
+fi
+
+bus=`readlink $led_cdev_path/device/subsystem | sed s'/.*\///'`
+usb_subdev=`readlink $led_cdev_path | grep usb | sed s'/\(.*usb[0-9]*\/[0-9]*-[0-9]*\)\/.*/\1/'`
+ls "$led_cdev_path/device/of_node/compatible" > /dev/null 2>&1
+of_node_missing=$?
+
+if [ "$bus" = "input" ]; then
+ input_node=`readlink $led_cdev_path/device | sed s'/.*\///'`
+ if [ ! -z "$usb_subdev" ]; then
+ bus="usb"
+ fi
+fi
+
+if [ "$bus" = "usb" ]; then
+ usb_interface=`readlink $led_cdev_path | sed s'/.*\(usb[0-9]*\)/\1/' | cut -d\/ -f3`
+ cd $led_cdev_path/../$usb_subdev
+ driver=`readlink $usb_interface/driver | sed s'/.*\///'`
+ if [ -d "$usb_interface/ieee80211" ]; then
+ wifi_phy=`ls -l $usb_interface/ieee80211 | grep phy | awk '{print $9}'`
+ fi
+ idVendor=`cat idVendor`
+ idProduct=`cat idProduct`
+ manufacturer=`cat manufacturer`
+ product=`cat product`
+elif [ "$bus" = "input" ]; then
+ cd $led_cdev_path
+ product=`cat device/name`
+ driver=`cat device/device/driver/description`
+elif [ $of_node_missing -eq 0 ]; then
+ cd $led_cdev_path
+ compatible=`cat device/of_node/compatible`
+ if [ "$compatible" = "gpio-leds" ]; then
+ driver="leds-gpio"
+ elif [ "$compatible" = "pwm-leds" ]; then
+ driver="leds-pwm"
+ else
+ manufacturer=`echo $compatible | awk -F, '{print $1}'`
+ product=`echo $compatible | awk -F, '{print $2}'`
+ fi
+else
+ echo "Unknown device type."
+ exit 1
+fi
+
+printf "\n#####################################\n"
+printf "# LED class device hardware details #\n"
+printf "#####################################\n\n"
+
+printf "bus:\t\t\t$bus\n"
+
+if [ ! -z "$idVendor" ]; then
+ printf "idVendor:\t\t$idVendor\n"
+fi
+
+if [ ! -z "$idProduct" ]; then
+ printf "idProduct:\t\t$idProduct\n"
+fi
+
+if [ ! -z "$manufacturer" ]; then
+ printf "manufacturer:\t\t$manufacturer\n"
+fi
+
+if [ ! -z "$product" ]; then
+ printf "product:\t\t$product\n"
+fi
+
+if [ ! -z "$driver" ]; then
+ printf "driver:\t\t\t$driver\n"
+fi
+
+if [ ! -z "$input_node" ]; then
+ printf "associated input node:\t$input_node\n"
+fi
+
+printf "\n####################################\n"
+printf "# LED class device name validation #\n"
+printf "####################################\n\n"
+
+led_name=`echo $led_cdev_path | sed s'/.*\///'`
+
+num_sections=`echo $led_name | awk -F: '{print NF}'`
+
+if [ $num_sections -eq 1 ]; then
+ printf "\":\" delimiter not detected.\t[ FAILED ]\n"
+ exit 1
+elif [ $num_sections -eq 2 ]; then
+ color=`echo $led_name | cut -d: -f1`
+ function=`echo $led_name | cut -d: -f2`
+elif [ $num_sections -eq 3 ]; then
+ devicename=`echo $led_name | cut -d: -f1`
+ color=`echo $led_name | cut -d: -f2`
+ function=`echo $led_name | cut -d: -f3`
+else
+ printf "Detected %d sections in the LED class device name - should the script be updated?\n" $num_sections
+ exit 1
+fi
+
+S_DEV="devicename"
+S_CLR="color "
+S_FUN="function "
+status_tab=20
+
+print_msg_ok()
+{
+ local section_name="$1"
+ local section_val="$2"
+ local msg="$3"
+ printf "$section_name :\t%-${status_tab}.${status_tab}s %s %s\n" "$section_val" "[ OK ] " "$msg"
+}
+
+print_msg_failed()
+{
+ local section_name="$1"
+ local section_val="$2"
+ local msg="$3"
+ printf "$section_name :\t%-${status_tab}.${status_tab}s %s %s\n" "$section_val" "[ FAILED ]" "$msg"
+}
+
+if [ ! -z "$input_node" ]; then
+ expected_devname=$input_node
+elif [ ! -z "$wifi_phy" ]; then
+ expected_devname=$wifi_phy
+fi
+
+if [ ! -z "$devicename" ]; then
+ if [ ! -z "$expected_devname" ]; then
+ if [ "$devicename" = "$expected_devname" ]; then
+ print_msg_ok "$S_DEV" "$devicename"
+ else
+ print_msg_failed "$S_DEV" "$devicename" "Expected: $expected_devname"
+ fi
+ else
+ if [ "$devicename" = "$manufacturer" ]; then
+ print_msg_failed "$S_DEV" "$devicename" "Redundant: use of vendor name is discouraged"
+ elif [ "$devicename" = "$product" ]; then
+ print_msg_failed "$S_DEV" "$devicename" "Redundant: use of product name is discouraged"
+ else
+ print_msg_failed "$S_DEV" "$devicename" "Unknown devicename - should the script be updated?"
+ fi
+ fi
+elif [ ! -z "$expected_devname" ]; then
+ print_msg_failed "$S_DEV" "blank" "Expected: $expected_devname"
+fi
+
+if [ ! -z "$color" ]; then
+ color_upper=`echo $color | tr [:lower:] [:upper:]`
+ color_id_definition=$(cat $led_defs_path | grep "_$color_upper\s" | awk '{print $2}')
+ if [ ! -z "$color_id_definition" ]; then
+ print_msg_ok "$S_CLR" "$color" "Matching definition: $color_id_definition"
+ else
+ print_msg_failed "$S_CLR" "$color" "Definition not found in $led_defs_path"
+ fi
+
+fi
+
+if [ ! -z "$function" ]; then
+ # strip optional enumerator
+ function=`echo $function | sed s'/\(.*\)-[0-9]*$/\1/'`
+ fun_definition=$(cat $led_defs_path | grep "\"$function\"" | awk '{print $2}')
+ if [ ! -z "$fun_definition" ]; then
+ print_msg_ok "$S_FUN" "$function" "Matching definition: $fun_definition"
+ else
+ print_msg_failed "$S_FUN" "$function" "Definition not found in $led_defs_path"
+ fi
+
+fi
diff --git a/tools/lib/api/fd/array.c b/tools/lib/api/fd/array.c
index b0a035fc87b3..58d44d5eee31 100644
--- a/tools/lib/api/fd/array.c
+++ b/tools/lib/api/fd/array.c
@@ -1,7 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2014, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
- *
- * Released under the GPL v2. (and only v2, not any later version)
*/
#include "array.h"
#include <errno.h>
diff --git a/tools/lib/argv_split.c b/tools/lib/argv_split.c
new file mode 100644
index 000000000000..0a58ccf3f761
--- /dev/null
+++ b/tools/lib/argv_split.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Helper function for splitting a string into an argv-like array.
+ */
+
+#include <stdlib.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+
+static const char *skip_arg(const char *cp)
+{
+ while (*cp && !isspace(*cp))
+ cp++;
+
+ return cp;
+}
+
+static int count_argc(const char *str)
+{
+ int count = 0;
+
+ while (*str) {
+ str = skip_spaces(str);
+ if (*str) {
+ count++;
+ str = skip_arg(str);
+ }
+ }
+
+ return count;
+}
+
+/**
+ * argv_free - free an argv
+ * @argv - the argument vector to be freed
+ *
+ * Frees an argv and the strings it points to.
+ */
+void argv_free(char **argv)
+{
+ char **p;
+ for (p = argv; *p; p++) {
+ free(*p);
+ *p = NULL;
+ }
+
+ free(argv);
+}
+
+/**
+ * argv_split - split a string at whitespace, returning an argv
+ * @str: the string to be split
+ * @argcp: returned argument count
+ *
+ * Returns an array of pointers to strings which are split out from
+ * @str. This is performed by strictly splitting on white-space; no
+ * quote processing is performed. Multiple whitespace characters are
+ * considered to be a single argument separator. The returned array
+ * is always NULL-terminated. Returns NULL on memory allocation
+ * failure.
+ */
+char **argv_split(const char *str, int *argcp)
+{
+ int argc = count_argc(str);
+ char **argv = calloc(argc + 1, sizeof(*argv));
+ char **argvp;
+
+ if (argv == NULL)
+ goto out;
+
+ if (argcp)
+ *argcp = argc;
+
+ argvp = argv;
+
+ while (*str) {
+ str = skip_spaces(str);
+
+ if (*str) {
+ const char *p = str;
+ char *t;
+
+ str = skip_arg(str);
+
+ t = strndup(p, str-p);
+ if (t == NULL)
+ goto fail;
+ *argvp++ = t;
+ }
+ }
+ *argvp = NULL;
+
+out:
+ return argv;
+
+fail:
+ argv_free(argv);
+ return NULL;
+}
diff --git a/tools/lib/bitmap.c b/tools/lib/bitmap.c
index 38748b0e342f..38494782be06 100644
--- a/tools/lib/bitmap.c
+++ b/tools/lib/bitmap.c
@@ -1,9 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* From lib/bitmap.c
* Helper functions for bitmap.h.
- *
- * This source code is licensed under the GNU General Public License,
- * Version 2. See the file COPYING for more details.
*/
#include <linux/bitmap.h>
diff --git a/tools/lib/bpf/Build b/tools/lib/bpf/Build
index ee9d5362f35b..e3962cfbc9a6 100644
--- a/tools/lib/bpf/Build
+++ b/tools/lib/bpf/Build
@@ -1 +1,3 @@
-libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o libbpf_probes.o xsk.o
+libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o \
+ netlink.o bpf_prog_linfo.o libbpf_probes.o xsk.o hashmap.o \
+ btf_dump.o
diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile
index f91639bf5650..c6f94cffe06e 100644
--- a/tools/lib/bpf/Makefile
+++ b/tools/lib/bpf/Makefile
@@ -1,9 +1,10 @@
# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
# Most of this file is copied from tools/lib/traceevent/Makefile
-BPF_VERSION = 0
-BPF_PATCHLEVEL = 0
-BPF_EXTRAVERSION = 3
+LIBBPF_VERSION := $(shell \
+ grep -oE '^LIBBPF_([0-9.]+)' libbpf.map | \
+ sort -rV | head -n1 | cut -d'_' -f2)
+LIBBPF_MAJOR_VERSION := $(firstword $(subst ., ,$(LIBBPF_VERSION)))
MAKEFLAGS += --no-print-directory
@@ -79,15 +80,9 @@ export prefix libdir src obj
libdir_SQ = $(subst ','\'',$(libdir))
libdir_relative_SQ = $(subst ','\'',$(libdir_relative))
-VERSION = $(BPF_VERSION)
-PATCHLEVEL = $(BPF_PATCHLEVEL)
-EXTRAVERSION = $(BPF_EXTRAVERSION)
-
OBJ = $@
N =
-LIBBPF_VERSION = $(BPF_VERSION).$(BPF_PATCHLEVEL).$(BPF_EXTRAVERSION)
-
LIB_TARGET = libbpf.a libbpf.so.$(LIBBPF_VERSION)
LIB_FILE = libbpf.a libbpf.so*
PC_FILE = libbpf.pc
@@ -113,6 +108,7 @@ override CFLAGS += -Werror -Wall
override CFLAGS += -fPIC
override CFLAGS += $(INCLUDES)
override CFLAGS += -fvisibility=hidden
+override CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
ifeq ($(VERBOSE),1)
Q =
@@ -138,7 +134,9 @@ LIB_FILE := $(addprefix $(OUTPUT),$(LIB_FILE))
PC_FILE := $(addprefix $(OUTPUT),$(PC_FILE))
GLOBAL_SYM_COUNT = $(shell readelf -s --wide $(BPF_IN) | \
- awk '/GLOBAL/ && /DEFAULT/ && !/UND/ {s++} END{print s}')
+ cut -d "@" -f1 | sed 's/_v[0-9]_[0-9]_[0-9].*//' | \
+ awk '/GLOBAL/ && /DEFAULT/ && !/UND/ {print $$8}' | \
+ sort -u | wc -l)
VERSIONED_SYM_COUNT = $(shell readelf -s --wide $(OUTPUT)libbpf.so | \
grep -Eo '[^ ]+@LIBBPF_' | cut -d@ -f1 | sort -u | wc -l)
@@ -178,10 +176,10 @@ $(BPF_IN): force elfdep bpfdep
$(OUTPUT)libbpf.so: $(OUTPUT)libbpf.so.$(LIBBPF_VERSION)
$(OUTPUT)libbpf.so.$(LIBBPF_VERSION): $(BPF_IN)
- $(QUIET_LINK)$(CC) --shared -Wl,-soname,libbpf.so.$(VERSION) \
+ $(QUIET_LINK)$(CC) --shared -Wl,-soname,libbpf.so.$(LIBBPF_MAJOR_VERSION) \
-Wl,--version-script=$(VERSION_SCRIPT) $^ -lelf -o $@
@ln -sf $(@F) $(OUTPUT)libbpf.so
- @ln -sf $(@F) $(OUTPUT)libbpf.so.$(VERSION)
+ @ln -sf $(@F) $(OUTPUT)libbpf.so.$(LIBBPF_MAJOR_VERSION)
$(OUTPUT)libbpf.a: $(BPF_IN)
$(QUIET_LINK)$(RM) $@; $(AR) rcs $@ $^
@@ -204,6 +202,17 @@ check_abi: $(OUTPUT)libbpf.so
"versioned symbols in $^ ($(VERSIONED_SYM_COUNT))." \
"Please make sure all LIBBPF_API symbols are" \
"versioned in $(VERSION_SCRIPT)." >&2; \
+ readelf -s --wide $(OUTPUT)libbpf-in.o | \
+ cut -d "@" -f1 | sed 's/_v[0-9]_[0-9]_[0-9].*//' | \
+ awk '/GLOBAL/ && /DEFAULT/ && !/UND/ {print $$8}'| \
+ sort -u > $(OUTPUT)libbpf_global_syms.tmp; \
+ readelf -s --wide $(OUTPUT)libbpf.so | \
+ grep -Eo '[^ ]+@LIBBPF_' | cut -d@ -f1 | \
+ sort -u > $(OUTPUT)libbpf_versioned_syms.tmp; \
+ diff -u $(OUTPUT)libbpf_global_syms.tmp \
+ $(OUTPUT)libbpf_versioned_syms.tmp; \
+ rm $(OUTPUT)libbpf_global_syms.tmp \
+ $(OUTPUT)libbpf_versioned_syms.tmp; \
exit 1; \
fi
@@ -247,7 +256,8 @@ config-clean:
clean:
$(call QUIET_CLEAN, libbpf) $(RM) $(TARGETS) $(CXX_TEST_TARGET) \
- *.o *~ *.a *.so *.so.$(VERSION) .*.d .*.cmd *.pc LIBBPF-CFLAGS
+ *.o *~ *.a *.so *.so.$(LIBBPF_MAJOR_VERSION) .*.d .*.cmd \
+ *.pc LIBBPF-CFLAGS
$(call QUIET_CLEAN, core-gen) $(RM) $(OUTPUT)FEATURE-DUMP.libbpf
diff --git a/tools/lib/bpf/README.rst b/tools/lib/bpf/README.rst
index cef7b77eab69..8928f7787f2d 100644
--- a/tools/lib/bpf/README.rst
+++ b/tools/lib/bpf/README.rst
@@ -9,7 +9,8 @@ described here. It's recommended to follow these conventions whenever a
new function or type is added to keep libbpf API clean and consistent.
All types and functions provided by libbpf API should have one of the
-following prefixes: ``bpf_``, ``btf_``, ``libbpf_``, ``xsk_``.
+following prefixes: ``bpf_``, ``btf_``, ``libbpf_``, ``xsk_``,
+``perf_buffer_``.
System call wrappers
--------------------
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index c4a48086dc9a..cbb933532981 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -26,10 +26,11 @@
#include <memory.h>
#include <unistd.h>
#include <asm/unistd.h>
+#include <errno.h>
#include <linux/bpf.h>
#include "bpf.h"
#include "libbpf.h"
-#include <errno.h>
+#include "libbpf_internal.h"
/*
* When building perf, unistd.h is overridden. __NR_bpf is
@@ -53,10 +54,6 @@
# endif
#endif
-#ifndef min
-#define min(x, y) ((x) < (y) ? (x) : (y))
-#endif
-
static inline __u64 ptr_to_u64(const void *ptr)
{
return (__u64) (unsigned long) ptr;
@@ -256,6 +253,7 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
if (load_attr->name)
memcpy(attr.prog_name, load_attr->name,
min(strlen(load_attr->name), BPF_OBJ_NAME_LEN - 1));
+ attr.prog_flags = load_attr->prog_flags;
fd = sys_bpf_prog_load(&attr, sizeof(attr));
if (fd >= 0)
@@ -570,7 +568,7 @@ int bpf_prog_test_run_xattr(struct bpf_prog_test_run_attr *test_attr)
return ret;
}
-int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id)
+static int bpf_obj_get_next_id(__u32 start_id, __u32 *next_id, int cmd)
{
union bpf_attr attr;
int err;
@@ -578,26 +576,26 @@ int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id)
memset(&attr, 0, sizeof(attr));
attr.start_id = start_id;
- err = sys_bpf(BPF_PROG_GET_NEXT_ID, &attr, sizeof(attr));
+ err = sys_bpf(cmd, &attr, sizeof(attr));
if (!err)
*next_id = attr.next_id;
return err;
}
-int bpf_map_get_next_id(__u32 start_id, __u32 *next_id)
+int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id)
{
- union bpf_attr attr;
- int err;
-
- memset(&attr, 0, sizeof(attr));
- attr.start_id = start_id;
+ return bpf_obj_get_next_id(start_id, next_id, BPF_PROG_GET_NEXT_ID);
+}
- err = sys_bpf(BPF_MAP_GET_NEXT_ID, &attr, sizeof(attr));
- if (!err)
- *next_id = attr.next_id;
+int bpf_map_get_next_id(__u32 start_id, __u32 *next_id)
+{
+ return bpf_obj_get_next_id(start_id, next_id, BPF_MAP_GET_NEXT_ID);
+}
- return err;
+int bpf_btf_get_next_id(__u32 start_id, __u32 *next_id)
+{
+ return bpf_obj_get_next_id(start_id, next_id, BPF_BTF_GET_NEXT_ID);
}
int bpf_prog_get_fd_by_id(__u32 id)
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
index 9593fec75652..0db01334740f 100644
--- a/tools/lib/bpf/bpf.h
+++ b/tools/lib/bpf/bpf.h
@@ -87,6 +87,7 @@ struct bpf_load_program_attr {
const void *line_info;
__u32 line_info_cnt;
__u32 log_level;
+ __u32 prog_flags;
};
/* Flags to direct loading requirements */
@@ -155,6 +156,7 @@ LIBBPF_API int bpf_prog_test_run(int prog_fd, int repeat, void *data,
__u32 *retval, __u32 *duration);
LIBBPF_API int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id);
LIBBPF_API int bpf_map_get_next_id(__u32 start_id, __u32 *next_id);
+LIBBPF_API int bpf_btf_get_next_id(__u32 start_id, __u32 *next_id);
LIBBPF_API int bpf_prog_get_fd_by_id(__u32 id);
LIBBPF_API int bpf_map_get_fd_by_id(__u32 id);
LIBBPF_API int bpf_btf_get_fd_by_id(__u32 id);
diff --git a/tools/lib/bpf/bpf_prog_linfo.c b/tools/lib/bpf/bpf_prog_linfo.c
index 6978314ea7f6..8c67561c93b0 100644
--- a/tools/lib/bpf/bpf_prog_linfo.c
+++ b/tools/lib/bpf/bpf_prog_linfo.c
@@ -6,10 +6,7 @@
#include <linux/err.h>
#include <linux/bpf.h>
#include "libbpf.h"
-
-#ifndef min
-#define min(x, y) ((x) < (y) ? (x) : (y))
-#endif
+#include "libbpf_internal.h"
struct bpf_prog_linfo {
void *raw_linfo;
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index 75eaf10b9e1a..1aa189a9112a 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -1,31 +1,25 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
/* Copyright (c) 2018 Facebook */
+#include <endian.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <linux/err.h>
#include <linux/btf.h>
+#include <gelf.h>
#include "btf.h"
#include "bpf.h"
#include "libbpf.h"
-#include "libbpf_util.h"
-
-#define max(a, b) ((a) > (b) ? (a) : (b))
-#define min(a, b) ((a) < (b) ? (a) : (b))
+#include "libbpf_internal.h"
+#include "hashmap.h"
#define BTF_MAX_NR_TYPES 0x7fffffff
#define BTF_MAX_STR_OFFSET 0x7fffffff
-#define IS_MODIFIER(k) (((k) == BTF_KIND_TYPEDEF) || \
- ((k) == BTF_KIND_VOLATILE) || \
- ((k) == BTF_KIND_CONST) || \
- ((k) == BTF_KIND_RESTRICT))
-
-#define IS_VAR(k) ((k) == BTF_KIND_VAR)
-
static struct btf_type btf_void;
struct btf {
@@ -42,47 +36,6 @@ struct btf {
int fd;
};
-struct btf_ext_info {
- /*
- * info points to the individual info section (e.g. func_info and
- * line_info) from the .BTF.ext. It does not include the __u32 rec_size.
- */
- void *info;
- __u32 rec_size;
- __u32 len;
-};
-
-struct btf_ext {
- union {
- struct btf_ext_header *hdr;
- void *data;
- };
- struct btf_ext_info func_info;
- struct btf_ext_info line_info;
- __u32 data_size;
-};
-
-struct btf_ext_info_sec {
- __u32 sec_name_off;
- __u32 num_info;
- /* Followed by num_info * record_size number of bytes */
- __u8 data[0];
-};
-
-/* The minimum bpf_func_info checked by the loader */
-struct bpf_func_info_min {
- __u32 insn_off;
- __u32 type_id;
-};
-
-/* The minimum bpf_line_info checked by the loader */
-struct bpf_line_info_min {
- __u32 insn_off;
- __u32 file_name_off;
- __u32 line_off;
- __u32 line_col;
-};
-
static inline __u64 ptr_to_u64(const void *ptr)
{
return (__u64) (unsigned long) ptr;
@@ -192,9 +145,9 @@ static int btf_parse_str_sec(struct btf *btf)
static int btf_type_size(struct btf_type *t)
{
int base_size = sizeof(struct btf_type);
- __u16 vlen = BTF_INFO_VLEN(t->info);
+ __u16 vlen = btf_vlen(t);
- switch (BTF_INFO_KIND(t->info)) {
+ switch (btf_kind(t)) {
case BTF_KIND_FWD:
case BTF_KIND_CONST:
case BTF_KIND_VOLATILE:
@@ -219,7 +172,7 @@ static int btf_type_size(struct btf_type *t)
case BTF_KIND_DATASEC:
return base_size + vlen * sizeof(struct btf_var_secinfo);
default:
- pr_debug("Unsupported BTF_KIND:%u\n", BTF_INFO_KIND(t->info));
+ pr_debug("Unsupported BTF_KIND:%u\n", btf_kind(t));
return -EINVAL;
}
}
@@ -263,7 +216,7 @@ const struct btf_type *btf__type_by_id(const struct btf *btf, __u32 type_id)
static bool btf_type_is_void(const struct btf_type *t)
{
- return t == &btf_void || BTF_INFO_KIND(t->info) == BTF_KIND_FWD;
+ return t == &btf_void || btf_is_fwd(t);
}
static bool btf_type_is_void_or_null(const struct btf_type *t)
@@ -284,7 +237,7 @@ __s64 btf__resolve_size(const struct btf *btf, __u32 type_id)
t = btf__type_by_id(btf, type_id);
for (i = 0; i < MAX_RESOLVE_DEPTH && !btf_type_is_void_or_null(t);
i++) {
- switch (BTF_INFO_KIND(t->info)) {
+ switch (btf_kind(t)) {
case BTF_KIND_INT:
case BTF_KIND_STRUCT:
case BTF_KIND_UNION:
@@ -303,7 +256,7 @@ __s64 btf__resolve_size(const struct btf *btf, __u32 type_id)
type_id = t->type;
break;
case BTF_KIND_ARRAY:
- array = (const struct btf_array *)(t + 1);
+ array = btf_array(t);
if (nelems && array->nelems > UINT32_MAX / nelems)
return -E2BIG;
nelems *= array->nelems;
@@ -334,8 +287,7 @@ int btf__resolve_type(const struct btf *btf, __u32 type_id)
t = btf__type_by_id(btf, type_id);
while (depth < MAX_RESOLVE_DEPTH &&
!btf_type_is_void_or_null(t) &&
- (IS_MODIFIER(BTF_INFO_KIND(t->info)) ||
- IS_VAR(BTF_INFO_KIND(t->info)))) {
+ (btf_is_mod(t) || btf_is_typedef(t) || btf_is_var(t))) {
type_id = t->type;
t = btf__type_by_id(btf, type_id);
depth++;
@@ -417,6 +369,132 @@ done:
return btf;
}
+static bool btf_check_endianness(const GElf_Ehdr *ehdr)
+{
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ return ehdr->e_ident[EI_DATA] == ELFDATA2LSB;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ return ehdr->e_ident[EI_DATA] == ELFDATA2MSB;
+#else
+# error "Unrecognized __BYTE_ORDER__"
+#endif
+}
+
+struct btf *btf__parse_elf(const char *path, struct btf_ext **btf_ext)
+{
+ Elf_Data *btf_data = NULL, *btf_ext_data = NULL;
+ int err = 0, fd = -1, idx = 0;
+ struct btf *btf = NULL;
+ Elf_Scn *scn = NULL;
+ Elf *elf = NULL;
+ GElf_Ehdr ehdr;
+
+ if (elf_version(EV_CURRENT) == EV_NONE) {
+ pr_warning("failed to init libelf for %s\n", path);
+ return ERR_PTR(-LIBBPF_ERRNO__LIBELF);
+ }
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ err = -errno;
+ pr_warning("failed to open %s: %s\n", path, strerror(errno));
+ return ERR_PTR(err);
+ }
+
+ err = -LIBBPF_ERRNO__FORMAT;
+
+ elf = elf_begin(fd, ELF_C_READ, NULL);
+ if (!elf) {
+ pr_warning("failed to open %s as ELF file\n", path);
+ goto done;
+ }
+ if (!gelf_getehdr(elf, &ehdr)) {
+ pr_warning("failed to get EHDR from %s\n", path);
+ goto done;
+ }
+ if (!btf_check_endianness(&ehdr)) {
+ pr_warning("non-native ELF endianness is not supported\n");
+ goto done;
+ }
+ if (!elf_rawdata(elf_getscn(elf, ehdr.e_shstrndx), NULL)) {
+ pr_warning("failed to get e_shstrndx from %s\n", path);
+ goto done;
+ }
+
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+ GElf_Shdr sh;
+ char *name;
+
+ idx++;
+ if (gelf_getshdr(scn, &sh) != &sh) {
+ pr_warning("failed to get section(%d) header from %s\n",
+ idx, path);
+ goto done;
+ }
+ name = elf_strptr(elf, ehdr.e_shstrndx, sh.sh_name);
+ if (!name) {
+ pr_warning("failed to get section(%d) name from %s\n",
+ idx, path);
+ goto done;
+ }
+ if (strcmp(name, BTF_ELF_SEC) == 0) {
+ btf_data = elf_getdata(scn, 0);
+ if (!btf_data) {
+ pr_warning("failed to get section(%d, %s) data from %s\n",
+ idx, name, path);
+ goto done;
+ }
+ continue;
+ } else if (btf_ext && strcmp(name, BTF_EXT_ELF_SEC) == 0) {
+ btf_ext_data = elf_getdata(scn, 0);
+ if (!btf_ext_data) {
+ pr_warning("failed to get section(%d, %s) data from %s\n",
+ idx, name, path);
+ goto done;
+ }
+ continue;
+ }
+ }
+
+ err = 0;
+
+ if (!btf_data) {
+ err = -ENOENT;
+ goto done;
+ }
+ btf = btf__new(btf_data->d_buf, btf_data->d_size);
+ if (IS_ERR(btf))
+ goto done;
+
+ if (btf_ext && btf_ext_data) {
+ *btf_ext = btf_ext__new(btf_ext_data->d_buf,
+ btf_ext_data->d_size);
+ if (IS_ERR(*btf_ext))
+ goto done;
+ } else if (btf_ext) {
+ *btf_ext = NULL;
+ }
+done:
+ if (elf)
+ elf_end(elf);
+ close(fd);
+
+ if (err)
+ return ERR_PTR(err);
+ /*
+ * btf is always parsed before btf_ext, so no need to clean up
+ * btf_ext, if btf loading failed
+ */
+ if (IS_ERR(btf))
+ return btf;
+ if (btf_ext && IS_ERR(*btf_ext)) {
+ btf__free(btf);
+ err = PTR_ERR(*btf_ext);
+ return ERR_PTR(err);
+ }
+ return btf;
+}
+
static int compare_vsi_off(const void *_a, const void *_b)
{
const struct btf_var_secinfo *a = _a;
@@ -428,11 +506,11 @@ static int compare_vsi_off(const void *_a, const void *_b)
static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf,
struct btf_type *t)
{
- __u32 size = 0, off = 0, i, vars = BTF_INFO_VLEN(t->info);
+ __u32 size = 0, off = 0, i, vars = btf_vlen(t);
const char *name = btf__name_by_offset(btf, t->name_off);
const struct btf_type *t_var;
struct btf_var_secinfo *vsi;
- struct btf_var *var;
+ const struct btf_var *var;
int ret;
if (!name) {
@@ -448,12 +526,11 @@ static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf,
t->size = size;
- for (i = 0, vsi = (struct btf_var_secinfo *)(t + 1);
- i < vars; i++, vsi++) {
+ for (i = 0, vsi = btf_var_secinfos(t); i < vars; i++, vsi++) {
t_var = btf__type_by_id(btf, vsi->type);
- var = (struct btf_var *)(t_var + 1);
+ var = btf_var(t_var);
- if (BTF_INFO_KIND(t_var->info) != BTF_KIND_VAR) {
+ if (!btf_is_var(t_var)) {
pr_debug("Non-VAR type seen in section %s\n", name);
return -EINVAL;
}
@@ -469,7 +546,8 @@ static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf,
ret = bpf_object__variable_offset(obj, name, &off);
if (ret) {
- pr_debug("No offset found in symbol table for VAR %s\n", name);
+ pr_debug("No offset found in symbol table for VAR %s\n",
+ name);
return -ENOENT;
}
@@ -493,7 +571,7 @@ int btf__finalize_data(struct bpf_object *obj, struct btf *btf)
* is section size and global variable offset. We use
* the info from the ELF itself for this purpose.
*/
- if (BTF_INFO_KIND(t->info) == BTF_KIND_DATASEC) {
+ if (btf_is_datasec(t)) {
err = btf_fixup_datasec(obj, btf, t);
if (err)
break;
@@ -648,14 +726,13 @@ int btf__get_map_kv_tids(const struct btf *btf, const char *map_name,
return -EINVAL;
}
- if (BTF_INFO_KIND(container_type->info) != BTF_KIND_STRUCT ||
- BTF_INFO_VLEN(container_type->info) < 2) {
+ if (!btf_is_struct(container_type) || btf_vlen(container_type) < 2) {
pr_warning("map:%s container_name:%s is an invalid container struct\n",
map_name, container_name);
return -EINVAL;
}
- key = (struct btf_member *)(container_type + 1);
+ key = btf_members(container_type);
value = key + 1;
key_size = btf__resolve_size(btf, key->type);
@@ -705,6 +782,9 @@ static int btf_ext_setup_info(struct btf_ext *btf_ext,
/* The start of the info sec (including the __u32 record_size). */
void *info;
+ if (ext_sec->len == 0)
+ return 0;
+
if (ext_sec->off & 0x03) {
pr_debug(".BTF.ext %s section is not aligned to 4 bytes\n",
ext_sec->desc);
@@ -808,11 +888,24 @@ static int btf_ext_setup_line_info(struct btf_ext *btf_ext)
return btf_ext_setup_info(btf_ext, &param);
}
+static int btf_ext_setup_offset_reloc(struct btf_ext *btf_ext)
+{
+ struct btf_ext_sec_setup_param param = {
+ .off = btf_ext->hdr->offset_reloc_off,
+ .len = btf_ext->hdr->offset_reloc_len,
+ .min_rec_size = sizeof(struct bpf_offset_reloc),
+ .ext_info = &btf_ext->offset_reloc_info,
+ .desc = "offset_reloc",
+ };
+
+ return btf_ext_setup_info(btf_ext, &param);
+}
+
static int btf_ext_parse_hdr(__u8 *data, __u32 data_size)
{
const struct btf_ext_header *hdr = (struct btf_ext_header *)data;
- if (data_size < offsetof(struct btf_ext_header, func_info_off) ||
+ if (data_size < offsetofend(struct btf_ext_header, hdr_len) ||
data_size < hdr->hdr_len) {
pr_debug("BTF.ext header not found");
return -EINVAL;
@@ -870,6 +963,9 @@ struct btf_ext *btf_ext__new(__u8 *data, __u32 size)
}
memcpy(btf_ext->data, data, size);
+ if (btf_ext->hdr->hdr_len <
+ offsetofend(struct btf_ext_header, line_info_len))
+ goto done;
err = btf_ext_setup_func_info(btf_ext);
if (err)
goto done;
@@ -878,6 +974,13 @@ struct btf_ext *btf_ext__new(__u8 *data, __u32 size)
if (err)
goto done;
+ if (btf_ext->hdr->hdr_len <
+ offsetofend(struct btf_ext_header, offset_reloc_len))
+ goto done;
+ err = btf_ext_setup_offset_reloc(btf_ext);
+ if (err)
+ goto done;
+
done:
if (err) {
btf_ext__free(btf_ext);
@@ -1165,16 +1268,9 @@ done:
return err;
}
-#define BTF_DEDUP_TABLE_DEFAULT_SIZE (1 << 14)
-#define BTF_DEDUP_TABLE_MAX_SIZE_LOG 31
#define BTF_UNPROCESSED_ID ((__u32)-1)
#define BTF_IN_PROGRESS_ID ((__u32)-2)
-struct btf_dedup_node {
- struct btf_dedup_node *next;
- __u32 type_id;
-};
-
struct btf_dedup {
/* .BTF section to be deduped in-place */
struct btf *btf;
@@ -1190,7 +1286,7 @@ struct btf_dedup {
* candidates, which is fine because we rely on subsequent
* btf_xxx_equal() checks to authoritatively verify type equality.
*/
- struct btf_dedup_node **dedup_table;
+ struct hashmap *dedup_table;
/* Canonical types map */
__u32 *map;
/* Hypothetical mapping, used during type graph equivalence checks */
@@ -1215,30 +1311,18 @@ struct btf_str_ptrs {
__u32 cap;
};
-static inline __u32 hash_combine(__u32 h, __u32 value)
+static long hash_combine(long h, long value)
{
-/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */
-#define GOLDEN_RATIO_PRIME 0x9e370001UL
- return h * 37 + value * GOLDEN_RATIO_PRIME;
-#undef GOLDEN_RATIO_PRIME
+ return h * 31 + value;
}
-#define for_each_dedup_cand(d, hash, node) \
- for (node = d->dedup_table[hash & (d->opts.dedup_table_size - 1)]; \
- node; \
- node = node->next)
+#define for_each_dedup_cand(d, node, hash) \
+ hashmap__for_each_key_entry(d->dedup_table, node, (void *)hash)
-static int btf_dedup_table_add(struct btf_dedup *d, __u32 hash, __u32 type_id)
+static int btf_dedup_table_add(struct btf_dedup *d, long hash, __u32 type_id)
{
- struct btf_dedup_node *node = malloc(sizeof(struct btf_dedup_node));
- int bucket = hash & (d->opts.dedup_table_size - 1);
-
- if (!node)
- return -ENOMEM;
- node->type_id = type_id;
- node->next = d->dedup_table[bucket];
- d->dedup_table[bucket] = node;
- return 0;
+ return hashmap__append(d->dedup_table,
+ (void *)hash, (void *)(long)type_id);
}
static int btf_dedup_hypot_map_add(struct btf_dedup *d,
@@ -1267,36 +1351,10 @@ static void btf_dedup_clear_hypot_map(struct btf_dedup *d)
d->hypot_cnt = 0;
}
-static void btf_dedup_table_free(struct btf_dedup *d)
-{
- struct btf_dedup_node *head, *tmp;
- int i;
-
- if (!d->dedup_table)
- return;
-
- for (i = 0; i < d->opts.dedup_table_size; i++) {
- while (d->dedup_table[i]) {
- tmp = d->dedup_table[i];
- d->dedup_table[i] = tmp->next;
- free(tmp);
- }
-
- head = d->dedup_table[i];
- while (head) {
- tmp = head;
- head = head->next;
- free(tmp);
- }
- }
-
- free(d->dedup_table);
- d->dedup_table = NULL;
-}
-
static void btf_dedup_free(struct btf_dedup *d)
{
- btf_dedup_table_free(d);
+ hashmap__free(d->dedup_table);
+ d->dedup_table = NULL;
free(d->map);
d->map = NULL;
@@ -1310,40 +1368,43 @@ static void btf_dedup_free(struct btf_dedup *d)
free(d);
}
-/* Find closest power of two >= to size, capped at 2^max_size_log */
-static __u32 roundup_pow2_max(__u32 size, int max_size_log)
+static size_t btf_dedup_identity_hash_fn(const void *key, void *ctx)
{
- int i;
+ return (size_t)key;
+}
- for (i = 0; i < max_size_log && (1U << i) < size; i++)
- ;
- return 1U << i;
+static size_t btf_dedup_collision_hash_fn(const void *key, void *ctx)
+{
+ return 0;
}
+static bool btf_dedup_equal_fn(const void *k1, const void *k2, void *ctx)
+{
+ return k1 == k2;
+}
static struct btf_dedup *btf_dedup_new(struct btf *btf, struct btf_ext *btf_ext,
const struct btf_dedup_opts *opts)
{
struct btf_dedup *d = calloc(1, sizeof(struct btf_dedup));
+ hashmap_hash_fn hash_fn = btf_dedup_identity_hash_fn;
int i, err = 0;
- __u32 sz;
if (!d)
return ERR_PTR(-ENOMEM);
d->opts.dont_resolve_fwds = opts && opts->dont_resolve_fwds;
- sz = opts && opts->dedup_table_size ? opts->dedup_table_size
- : BTF_DEDUP_TABLE_DEFAULT_SIZE;
- sz = roundup_pow2_max(sz, BTF_DEDUP_TABLE_MAX_SIZE_LOG);
- d->opts.dedup_table_size = sz;
+ /* dedup_table_size is now used only to force collisions in tests */
+ if (opts && opts->dedup_table_size == 1)
+ hash_fn = btf_dedup_collision_hash_fn;
d->btf = btf;
d->btf_ext = btf_ext;
- d->dedup_table = calloc(d->opts.dedup_table_size,
- sizeof(struct btf_dedup_node *));
- if (!d->dedup_table) {
- err = -ENOMEM;
+ d->dedup_table = hashmap__new(hash_fn, btf_dedup_equal_fn, NULL);
+ if (IS_ERR(d->dedup_table)) {
+ err = PTR_ERR(d->dedup_table);
+ d->dedup_table = NULL;
goto done;
}
@@ -1356,10 +1417,9 @@ static struct btf_dedup *btf_dedup_new(struct btf *btf, struct btf_ext *btf_ext,
d->map[0] = 0;
for (i = 1; i <= btf->nr_types; i++) {
struct btf_type *t = d->btf->types[i];
- __u16 kind = BTF_INFO_KIND(t->info);
/* VAR and DATASEC are never deduped and are self-canonical */
- if (kind == BTF_KIND_VAR || kind == BTF_KIND_DATASEC)
+ if (btf_is_var(t) || btf_is_datasec(t))
d->map[i] = i;
else
d->map[i] = BTF_UNPROCESSED_ID;
@@ -1400,11 +1460,11 @@ static int btf_for_each_str_off(struct btf_dedup *d, str_off_fn_t fn, void *ctx)
if (r)
return r;
- switch (BTF_INFO_KIND(t->info)) {
+ switch (btf_kind(t)) {
case BTF_KIND_STRUCT:
case BTF_KIND_UNION: {
- struct btf_member *m = (struct btf_member *)(t + 1);
- __u16 vlen = BTF_INFO_VLEN(t->info);
+ struct btf_member *m = btf_members(t);
+ __u16 vlen = btf_vlen(t);
for (j = 0; j < vlen; j++) {
r = fn(&m->name_off, ctx);
@@ -1415,8 +1475,8 @@ static int btf_for_each_str_off(struct btf_dedup *d, str_off_fn_t fn, void *ctx)
break;
}
case BTF_KIND_ENUM: {
- struct btf_enum *m = (struct btf_enum *)(t + 1);
- __u16 vlen = BTF_INFO_VLEN(t->info);
+ struct btf_enum *m = btf_enum(t);
+ __u16 vlen = btf_vlen(t);
for (j = 0; j < vlen; j++) {
r = fn(&m->name_off, ctx);
@@ -1427,8 +1487,8 @@ static int btf_for_each_str_off(struct btf_dedup *d, str_off_fn_t fn, void *ctx)
break;
}
case BTF_KIND_FUNC_PROTO: {
- struct btf_param *m = (struct btf_param *)(t + 1);
- __u16 vlen = BTF_INFO_VLEN(t->info);
+ struct btf_param *m = btf_params(t);
+ __u16 vlen = btf_vlen(t);
for (j = 0; j < vlen; j++) {
r = fn(&m->name_off, ctx);
@@ -1662,9 +1722,9 @@ done:
return err;
}
-static __u32 btf_hash_common(struct btf_type *t)
+static long btf_hash_common(struct btf_type *t)
{
- __u32 h;
+ long h;
h = hash_combine(0, t->name_off);
h = hash_combine(h, t->info);
@@ -1680,10 +1740,10 @@ static bool btf_equal_common(struct btf_type *t1, struct btf_type *t2)
}
/* Calculate type signature hash of INT. */
-static __u32 btf_hash_int(struct btf_type *t)
+static long btf_hash_int(struct btf_type *t)
{
__u32 info = *(__u32 *)(t + 1);
- __u32 h;
+ long h;
h = btf_hash_common(t);
h = hash_combine(h, info);
@@ -1703,9 +1763,9 @@ static bool btf_equal_int(struct btf_type *t1, struct btf_type *t2)
}
/* Calculate type signature hash of ENUM. */
-static __u32 btf_hash_enum(struct btf_type *t)
+static long btf_hash_enum(struct btf_type *t)
{
- __u32 h;
+ long h;
/* don't hash vlen and enum members to support enum fwd resolving */
h = hash_combine(0, t->name_off);
@@ -1717,16 +1777,16 @@ static __u32 btf_hash_enum(struct btf_type *t)
/* Check structural equality of two ENUMs. */
static bool btf_equal_enum(struct btf_type *t1, struct btf_type *t2)
{
- struct btf_enum *m1, *m2;
+ const struct btf_enum *m1, *m2;
__u16 vlen;
int i;
if (!btf_equal_common(t1, t2))
return false;
- vlen = BTF_INFO_VLEN(t1->info);
- m1 = (struct btf_enum *)(t1 + 1);
- m2 = (struct btf_enum *)(t2 + 1);
+ vlen = btf_vlen(t1);
+ m1 = btf_enum(t1);
+ m2 = btf_enum(t2);
for (i = 0; i < vlen; i++) {
if (m1->name_off != m2->name_off || m1->val != m2->val)
return false;
@@ -1738,8 +1798,7 @@ static bool btf_equal_enum(struct btf_type *t1, struct btf_type *t2)
static inline bool btf_is_enum_fwd(struct btf_type *t)
{
- return BTF_INFO_KIND(t->info) == BTF_KIND_ENUM &&
- BTF_INFO_VLEN(t->info) == 0;
+ return btf_is_enum(t) && btf_vlen(t) == 0;
}
static bool btf_compat_enum(struct btf_type *t1, struct btf_type *t2)
@@ -1757,11 +1816,11 @@ static bool btf_compat_enum(struct btf_type *t1, struct btf_type *t2)
* as referenced type IDs equivalence is established separately during type
* graph equivalence check algorithm.
*/
-static __u32 btf_hash_struct(struct btf_type *t)
+static long btf_hash_struct(struct btf_type *t)
{
- struct btf_member *member = (struct btf_member *)(t + 1);
- __u32 vlen = BTF_INFO_VLEN(t->info);
- __u32 h = btf_hash_common(t);
+ const struct btf_member *member = btf_members(t);
+ __u32 vlen = btf_vlen(t);
+ long h = btf_hash_common(t);
int i;
for (i = 0; i < vlen; i++) {
@@ -1780,16 +1839,16 @@ static __u32 btf_hash_struct(struct btf_type *t)
*/
static bool btf_shallow_equal_struct(struct btf_type *t1, struct btf_type *t2)
{
- struct btf_member *m1, *m2;
+ const struct btf_member *m1, *m2;
__u16 vlen;
int i;
if (!btf_equal_common(t1, t2))
return false;
- vlen = BTF_INFO_VLEN(t1->info);
- m1 = (struct btf_member *)(t1 + 1);
- m2 = (struct btf_member *)(t2 + 1);
+ vlen = btf_vlen(t1);
+ m1 = btf_members(t1);
+ m2 = btf_members(t2);
for (i = 0; i < vlen; i++) {
if (m1->name_off != m2->name_off || m1->offset != m2->offset)
return false;
@@ -1804,10 +1863,10 @@ static bool btf_shallow_equal_struct(struct btf_type *t1, struct btf_type *t2)
* under assumption that they were already resolved to canonical type IDs and
* are not going to change.
*/
-static __u32 btf_hash_array(struct btf_type *t)
+static long btf_hash_array(struct btf_type *t)
{
- struct btf_array *info = (struct btf_array *)(t + 1);
- __u32 h = btf_hash_common(t);
+ const struct btf_array *info = btf_array(t);
+ long h = btf_hash_common(t);
h = hash_combine(h, info->type);
h = hash_combine(h, info->index_type);
@@ -1824,13 +1883,13 @@ static __u32 btf_hash_array(struct btf_type *t)
*/
static bool btf_equal_array(struct btf_type *t1, struct btf_type *t2)
{
- struct btf_array *info1, *info2;
+ const struct btf_array *info1, *info2;
if (!btf_equal_common(t1, t2))
return false;
- info1 = (struct btf_array *)(t1 + 1);
- info2 = (struct btf_array *)(t2 + 1);
+ info1 = btf_array(t1);
+ info2 = btf_array(t2);
return info1->type == info2->type &&
info1->index_type == info2->index_type &&
info1->nelems == info2->nelems;
@@ -1843,14 +1902,10 @@ static bool btf_equal_array(struct btf_type *t1, struct btf_type *t2)
*/
static bool btf_compat_array(struct btf_type *t1, struct btf_type *t2)
{
- struct btf_array *info1, *info2;
-
if (!btf_equal_common(t1, t2))
return false;
- info1 = (struct btf_array *)(t1 + 1);
- info2 = (struct btf_array *)(t2 + 1);
- return info1->nelems == info2->nelems;
+ return btf_array(t1)->nelems == btf_array(t2)->nelems;
}
/*
@@ -1858,11 +1913,11 @@ static bool btf_compat_array(struct btf_type *t1, struct btf_type *t2)
* under assumption that they were already resolved to canonical type IDs and
* are not going to change.
*/
-static inline __u32 btf_hash_fnproto(struct btf_type *t)
+static long btf_hash_fnproto(struct btf_type *t)
{
- struct btf_param *member = (struct btf_param *)(t + 1);
- __u16 vlen = BTF_INFO_VLEN(t->info);
- __u32 h = btf_hash_common(t);
+ const struct btf_param *member = btf_params(t);
+ __u16 vlen = btf_vlen(t);
+ long h = btf_hash_common(t);
int i;
for (i = 0; i < vlen; i++) {
@@ -1880,18 +1935,18 @@ static inline __u32 btf_hash_fnproto(struct btf_type *t)
* This function is called during reference types deduplication to compare
* FUNC_PROTO to potential canonical representative.
*/
-static inline bool btf_equal_fnproto(struct btf_type *t1, struct btf_type *t2)
+static bool btf_equal_fnproto(struct btf_type *t1, struct btf_type *t2)
{
- struct btf_param *m1, *m2;
+ const struct btf_param *m1, *m2;
__u16 vlen;
int i;
if (!btf_equal_common(t1, t2))
return false;
- vlen = BTF_INFO_VLEN(t1->info);
- m1 = (struct btf_param *)(t1 + 1);
- m2 = (struct btf_param *)(t2 + 1);
+ vlen = btf_vlen(t1);
+ m1 = btf_params(t1);
+ m2 = btf_params(t2);
for (i = 0; i < vlen; i++) {
if (m1->name_off != m2->name_off || m1->type != m2->type)
return false;
@@ -1906,9 +1961,9 @@ static inline bool btf_equal_fnproto(struct btf_type *t1, struct btf_type *t2)
* IDs. This check is performed during type graph equivalence check and
* referenced types equivalence is checked separately.
*/
-static inline bool btf_compat_fnproto(struct btf_type *t1, struct btf_type *t2)
+static bool btf_compat_fnproto(struct btf_type *t1, struct btf_type *t2)
{
- struct btf_param *m1, *m2;
+ const struct btf_param *m1, *m2;
__u16 vlen;
int i;
@@ -1916,9 +1971,9 @@ static inline bool btf_compat_fnproto(struct btf_type *t1, struct btf_type *t2)
if (t1->name_off != t2->name_off || t1->info != t2->info)
return false;
- vlen = BTF_INFO_VLEN(t1->info);
- m1 = (struct btf_param *)(t1 + 1);
- m2 = (struct btf_param *)(t2 + 1);
+ vlen = btf_vlen(t1);
+ m1 = btf_params(t1);
+ m2 = btf_params(t2);
for (i = 0; i < vlen; i++) {
if (m1->name_off != m2->name_off)
return false;
@@ -1937,13 +1992,14 @@ static inline bool btf_compat_fnproto(struct btf_type *t1, struct btf_type *t2)
static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id)
{
struct btf_type *t = d->btf->types[type_id];
+ struct hashmap_entry *hash_entry;
struct btf_type *cand;
- struct btf_dedup_node *cand_node;
/* if we don't find equivalent type, then we are canonical */
__u32 new_id = type_id;
- __u32 h;
+ __u32 cand_id;
+ long h;
- switch (BTF_INFO_KIND(t->info)) {
+ switch (btf_kind(t)) {
case BTF_KIND_CONST:
case BTF_KIND_VOLATILE:
case BTF_KIND_RESTRICT:
@@ -1960,10 +2016,11 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id)
case BTF_KIND_INT:
h = btf_hash_int(t);
- for_each_dedup_cand(d, h, cand_node) {
- cand = d->btf->types[cand_node->type_id];
+ for_each_dedup_cand(d, hash_entry, h) {
+ cand_id = (__u32)(long)hash_entry->value;
+ cand = d->btf->types[cand_id];
if (btf_equal_int(t, cand)) {
- new_id = cand_node->type_id;
+ new_id = cand_id;
break;
}
}
@@ -1971,10 +2028,11 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id)
case BTF_KIND_ENUM:
h = btf_hash_enum(t);
- for_each_dedup_cand(d, h, cand_node) {
- cand = d->btf->types[cand_node->type_id];
+ for_each_dedup_cand(d, hash_entry, h) {
+ cand_id = (__u32)(long)hash_entry->value;
+ cand = d->btf->types[cand_id];
if (btf_equal_enum(t, cand)) {
- new_id = cand_node->type_id;
+ new_id = cand_id;
break;
}
if (d->opts.dont_resolve_fwds)
@@ -1982,21 +2040,22 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id)
if (btf_compat_enum(t, cand)) {
if (btf_is_enum_fwd(t)) {
/* resolve fwd to full enum */
- new_id = cand_node->type_id;
+ new_id = cand_id;
break;
}
/* resolve canonical enum fwd to full enum */
- d->map[cand_node->type_id] = type_id;
+ d->map[cand_id] = type_id;
}
}
break;
case BTF_KIND_FWD:
h = btf_hash_common(t);
- for_each_dedup_cand(d, h, cand_node) {
- cand = d->btf->types[cand_node->type_id];
+ for_each_dedup_cand(d, hash_entry, h) {
+ cand_id = (__u32)(long)hash_entry->value;
+ cand = d->btf->types[cand_id];
if (btf_equal_common(t, cand)) {
- new_id = cand_node->type_id;
+ new_id = cand_id;
break;
}
}
@@ -2053,13 +2112,13 @@ static uint32_t resolve_fwd_id(struct btf_dedup *d, uint32_t type_id)
{
__u32 orig_type_id = type_id;
- if (BTF_INFO_KIND(d->btf->types[type_id]->info) != BTF_KIND_FWD)
+ if (!btf_is_fwd(d->btf->types[type_id]))
return type_id;
while (is_type_mapped(d, type_id) && d->map[type_id] != type_id)
type_id = d->map[type_id];
- if (BTF_INFO_KIND(d->btf->types[type_id]->info) != BTF_KIND_FWD)
+ if (!btf_is_fwd(d->btf->types[type_id]))
return type_id;
return orig_type_id;
@@ -2068,7 +2127,7 @@ static uint32_t resolve_fwd_id(struct btf_dedup *d, uint32_t type_id)
static inline __u16 btf_fwd_kind(struct btf_type *t)
{
- return BTF_INFO_KFLAG(t->info) ? BTF_KIND_UNION : BTF_KIND_STRUCT;
+ return btf_kflag(t) ? BTF_KIND_UNION : BTF_KIND_STRUCT;
}
/*
@@ -2189,8 +2248,8 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,
cand_type = d->btf->types[cand_id];
canon_type = d->btf->types[canon_id];
- cand_kind = BTF_INFO_KIND(cand_type->info);
- canon_kind = BTF_INFO_KIND(canon_type->info);
+ cand_kind = btf_kind(cand_type);
+ canon_kind = btf_kind(canon_type);
if (cand_type->name_off != canon_type->name_off)
return 0;
@@ -2239,12 +2298,12 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,
return btf_dedup_is_equiv(d, cand_type->type, canon_type->type);
case BTF_KIND_ARRAY: {
- struct btf_array *cand_arr, *canon_arr;
+ const struct btf_array *cand_arr, *canon_arr;
if (!btf_compat_array(cand_type, canon_type))
return 0;
- cand_arr = (struct btf_array *)(cand_type + 1);
- canon_arr = (struct btf_array *)(canon_type + 1);
+ cand_arr = btf_array(cand_type);
+ canon_arr = btf_array(canon_type);
eq = btf_dedup_is_equiv(d,
cand_arr->index_type, canon_arr->index_type);
if (eq <= 0)
@@ -2254,14 +2313,14 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,
case BTF_KIND_STRUCT:
case BTF_KIND_UNION: {
- struct btf_member *cand_m, *canon_m;
+ const struct btf_member *cand_m, *canon_m;
__u16 vlen;
if (!btf_shallow_equal_struct(cand_type, canon_type))
return 0;
- vlen = BTF_INFO_VLEN(cand_type->info);
- cand_m = (struct btf_member *)(cand_type + 1);
- canon_m = (struct btf_member *)(canon_type + 1);
+ vlen = btf_vlen(cand_type);
+ cand_m = btf_members(cand_type);
+ canon_m = btf_members(canon_type);
for (i = 0; i < vlen; i++) {
eq = btf_dedup_is_equiv(d, cand_m->type, canon_m->type);
if (eq <= 0)
@@ -2274,7 +2333,7 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,
}
case BTF_KIND_FUNC_PROTO: {
- struct btf_param *cand_p, *canon_p;
+ const struct btf_param *cand_p, *canon_p;
__u16 vlen;
if (!btf_compat_fnproto(cand_type, canon_type))
@@ -2282,9 +2341,9 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,
eq = btf_dedup_is_equiv(d, cand_type->type, canon_type->type);
if (eq <= 0)
return eq;
- vlen = BTF_INFO_VLEN(cand_type->info);
- cand_p = (struct btf_param *)(cand_type + 1);
- canon_p = (struct btf_param *)(canon_type + 1);
+ vlen = btf_vlen(cand_type);
+ cand_p = btf_params(cand_type);
+ canon_p = btf_params(canon_type);
for (i = 0; i < vlen; i++) {
eq = btf_dedup_is_equiv(d, cand_p->type, canon_p->type);
if (eq <= 0)
@@ -2339,8 +2398,8 @@ static void btf_dedup_merge_hypot_map(struct btf_dedup *d)
targ_type_id = d->hypot_map[cand_type_id];
t_id = resolve_type_id(d, targ_type_id);
c_id = resolve_type_id(d, cand_type_id);
- t_kind = BTF_INFO_KIND(d->btf->types[t_id]->info);
- c_kind = BTF_INFO_KIND(d->btf->types[c_id]->info);
+ t_kind = btf_kind(d->btf->types[t_id]);
+ c_kind = btf_kind(d->btf->types[c_id]);
/*
* Resolve FWD into STRUCT/UNION.
* It's ok to resolve FWD into STRUCT/UNION that's not yet
@@ -2397,25 +2456,26 @@ static void btf_dedup_merge_hypot_map(struct btf_dedup *d)
*/
static int btf_dedup_struct_type(struct btf_dedup *d, __u32 type_id)
{
- struct btf_dedup_node *cand_node;
struct btf_type *cand_type, *t;
+ struct hashmap_entry *hash_entry;
/* if we don't find equivalent type, then we are canonical */
__u32 new_id = type_id;
__u16 kind;
- __u32 h;
+ long h;
/* already deduped or is in process of deduping (loop detected) */
if (d->map[type_id] <= BTF_MAX_NR_TYPES)
return 0;
t = d->btf->types[type_id];
- kind = BTF_INFO_KIND(t->info);
+ kind = btf_kind(t);
if (kind != BTF_KIND_STRUCT && kind != BTF_KIND_UNION)
return 0;
h = btf_hash_struct(t);
- for_each_dedup_cand(d, h, cand_node) {
+ for_each_dedup_cand(d, hash_entry, h) {
+ __u32 cand_id = (__u32)(long)hash_entry->value;
int eq;
/*
@@ -2428,17 +2488,17 @@ static int btf_dedup_struct_type(struct btf_dedup *d, __u32 type_id)
* creating a loop (FWD -> STRUCT and STRUCT -> FWD), because
* FWD and compatible STRUCT/UNION are considered equivalent.
*/
- cand_type = d->btf->types[cand_node->type_id];
+ cand_type = d->btf->types[cand_id];
if (!btf_shallow_equal_struct(t, cand_type))
continue;
btf_dedup_clear_hypot_map(d);
- eq = btf_dedup_is_equiv(d, type_id, cand_node->type_id);
+ eq = btf_dedup_is_equiv(d, type_id, cand_id);
if (eq < 0)
return eq;
if (!eq)
continue;
- new_id = cand_node->type_id;
+ new_id = cand_id;
btf_dedup_merge_hypot_map(d);
break;
}
@@ -2488,12 +2548,12 @@ static int btf_dedup_struct_types(struct btf_dedup *d)
*/
static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id)
{
- struct btf_dedup_node *cand_node;
+ struct hashmap_entry *hash_entry;
+ __u32 new_id = type_id, cand_id;
struct btf_type *t, *cand;
/* if we don't find equivalent type, then we are representative type */
- __u32 new_id = type_id;
int ref_type_id;
- __u32 h;
+ long h;
if (d->map[type_id] == BTF_IN_PROGRESS_ID)
return -ELOOP;
@@ -2503,7 +2563,7 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id)
t = d->btf->types[type_id];
d->map[type_id] = BTF_IN_PROGRESS_ID;
- switch (BTF_INFO_KIND(t->info)) {
+ switch (btf_kind(t)) {
case BTF_KIND_CONST:
case BTF_KIND_VOLATILE:
case BTF_KIND_RESTRICT:
@@ -2516,17 +2576,18 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id)
t->type = ref_type_id;
h = btf_hash_common(t);
- for_each_dedup_cand(d, h, cand_node) {
- cand = d->btf->types[cand_node->type_id];
+ for_each_dedup_cand(d, hash_entry, h) {
+ cand_id = (__u32)(long)hash_entry->value;
+ cand = d->btf->types[cand_id];
if (btf_equal_common(t, cand)) {
- new_id = cand_node->type_id;
+ new_id = cand_id;
break;
}
}
break;
case BTF_KIND_ARRAY: {
- struct btf_array *info = (struct btf_array *)(t + 1);
+ struct btf_array *info = btf_array(t);
ref_type_id = btf_dedup_ref_type(d, info->type);
if (ref_type_id < 0)
@@ -2539,10 +2600,11 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id)
info->index_type = ref_type_id;
h = btf_hash_array(t);
- for_each_dedup_cand(d, h, cand_node) {
- cand = d->btf->types[cand_node->type_id];
+ for_each_dedup_cand(d, hash_entry, h) {
+ cand_id = (__u32)(long)hash_entry->value;
+ cand = d->btf->types[cand_id];
if (btf_equal_array(t, cand)) {
- new_id = cand_node->type_id;
+ new_id = cand_id;
break;
}
}
@@ -2559,8 +2621,8 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id)
return ref_type_id;
t->type = ref_type_id;
- vlen = BTF_INFO_VLEN(t->info);
- param = (struct btf_param *)(t + 1);
+ vlen = btf_vlen(t);
+ param = btf_params(t);
for (i = 0; i < vlen; i++) {
ref_type_id = btf_dedup_ref_type(d, param->type);
if (ref_type_id < 0)
@@ -2570,10 +2632,11 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id)
}
h = btf_hash_fnproto(t);
- for_each_dedup_cand(d, h, cand_node) {
- cand = d->btf->types[cand_node->type_id];
+ for_each_dedup_cand(d, hash_entry, h) {
+ cand_id = (__u32)(long)hash_entry->value;
+ cand = d->btf->types[cand_id];
if (btf_equal_fnproto(t, cand)) {
- new_id = cand_node->type_id;
+ new_id = cand_id;
break;
}
}
@@ -2600,7 +2663,9 @@ static int btf_dedup_ref_types(struct btf_dedup *d)
if (err < 0)
return err;
}
- btf_dedup_table_free(d);
+ /* we won't need d->dedup_table anymore */
+ hashmap__free(d->dedup_table);
+ d->dedup_table = NULL;
return 0;
}
@@ -2697,7 +2762,7 @@ static int btf_dedup_remap_type(struct btf_dedup *d, __u32 type_id)
struct btf_type *t = d->btf->types[type_id];
int i, r;
- switch (BTF_INFO_KIND(t->info)) {
+ switch (btf_kind(t)) {
case BTF_KIND_INT:
case BTF_KIND_ENUM:
break;
@@ -2717,7 +2782,7 @@ static int btf_dedup_remap_type(struct btf_dedup *d, __u32 type_id)
break;
case BTF_KIND_ARRAY: {
- struct btf_array *arr_info = (struct btf_array *)(t + 1);
+ struct btf_array *arr_info = btf_array(t);
r = btf_dedup_remap_type_id(d, arr_info->type);
if (r < 0)
@@ -2732,8 +2797,8 @@ static int btf_dedup_remap_type(struct btf_dedup *d, __u32 type_id)
case BTF_KIND_STRUCT:
case BTF_KIND_UNION: {
- struct btf_member *member = (struct btf_member *)(t + 1);
- __u16 vlen = BTF_INFO_VLEN(t->info);
+ struct btf_member *member = btf_members(t);
+ __u16 vlen = btf_vlen(t);
for (i = 0; i < vlen; i++) {
r = btf_dedup_remap_type_id(d, member->type);
@@ -2746,8 +2811,8 @@ static int btf_dedup_remap_type(struct btf_dedup *d, __u32 type_id)
}
case BTF_KIND_FUNC_PROTO: {
- struct btf_param *param = (struct btf_param *)(t + 1);
- __u16 vlen = BTF_INFO_VLEN(t->info);
+ struct btf_param *param = btf_params(t);
+ __u16 vlen = btf_vlen(t);
r = btf_dedup_remap_type_id(d, t->type);
if (r < 0)
@@ -2765,8 +2830,8 @@ static int btf_dedup_remap_type(struct btf_dedup *d, __u32 type_id)
}
case BTF_KIND_DATASEC: {
- struct btf_var_secinfo *var = (struct btf_var_secinfo *)(t + 1);
- __u16 vlen = BTF_INFO_VLEN(t->info);
+ struct btf_var_secinfo *var = btf_var_secinfos(t);
+ __u16 vlen = btf_vlen(t);
for (i = 0; i < vlen; i++) {
r = btf_dedup_remap_type_id(d, var->type);
diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h
index c7b399e81fce..9cb44b4fbf60 100644
--- a/tools/lib/bpf/btf.h
+++ b/tools/lib/bpf/btf.h
@@ -4,6 +4,8 @@
#ifndef __LIBBPF_BTF_H
#define __LIBBPF_BTF_H
+#include <stdarg.h>
+#include <linux/btf.h>
#include <linux/types.h>
#ifdef __cplusplus
@@ -16,6 +18,7 @@ extern "C" {
#define BTF_ELF_SEC ".BTF"
#define BTF_EXT_ELF_SEC ".BTF.ext"
+#define MAPS_ELF_SEC ".maps"
struct btf;
struct btf_ext;
@@ -55,10 +58,16 @@ struct btf_ext_header {
__u32 func_info_len;
__u32 line_info_off;
__u32 line_info_len;
+
+ /* optional part of .BTF.ext header */
+ __u32 offset_reloc_off;
+ __u32 offset_reloc_len;
};
LIBBPF_API void btf__free(struct btf *btf);
LIBBPF_API struct btf *btf__new(__u8 *data, __u32 size);
+LIBBPF_API struct btf *btf__parse_elf(const char *path,
+ struct btf_ext **btf_ext);
LIBBPF_API int btf__finalize_data(struct bpf_object *obj, struct btf *btf);
LIBBPF_API int btf__load(struct btf *btf);
LIBBPF_API __s32 btf__find_by_name(const struct btf *btf,
@@ -100,6 +109,199 @@ struct btf_dedup_opts {
LIBBPF_API int btf__dedup(struct btf *btf, struct btf_ext *btf_ext,
const struct btf_dedup_opts *opts);
+struct btf_dump;
+
+struct btf_dump_opts {
+ void *ctx;
+};
+
+typedef void (*btf_dump_printf_fn_t)(void *ctx, const char *fmt, va_list args);
+
+LIBBPF_API struct btf_dump *btf_dump__new(const struct btf *btf,
+ const struct btf_ext *btf_ext,
+ const struct btf_dump_opts *opts,
+ btf_dump_printf_fn_t printf_fn);
+LIBBPF_API void btf_dump__free(struct btf_dump *d);
+
+LIBBPF_API int btf_dump__dump_type(struct btf_dump *d, __u32 id);
+
+/*
+ * A set of helpers for easier BTF types handling
+ */
+static inline __u16 btf_kind(const struct btf_type *t)
+{
+ return BTF_INFO_KIND(t->info);
+}
+
+static inline __u16 btf_vlen(const struct btf_type *t)
+{
+ return BTF_INFO_VLEN(t->info);
+}
+
+static inline bool btf_kflag(const struct btf_type *t)
+{
+ return BTF_INFO_KFLAG(t->info);
+}
+
+static inline bool btf_is_int(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_INT;
+}
+
+static inline bool btf_is_ptr(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_PTR;
+}
+
+static inline bool btf_is_array(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_ARRAY;
+}
+
+static inline bool btf_is_struct(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_STRUCT;
+}
+
+static inline bool btf_is_union(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_UNION;
+}
+
+static inline bool btf_is_composite(const struct btf_type *t)
+{
+ __u16 kind = btf_kind(t);
+
+ return kind == BTF_KIND_STRUCT || kind == BTF_KIND_UNION;
+}
+
+static inline bool btf_is_enum(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_ENUM;
+}
+
+static inline bool btf_is_fwd(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_FWD;
+}
+
+static inline bool btf_is_typedef(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_TYPEDEF;
+}
+
+static inline bool btf_is_volatile(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_VOLATILE;
+}
+
+static inline bool btf_is_const(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_CONST;
+}
+
+static inline bool btf_is_restrict(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_RESTRICT;
+}
+
+static inline bool btf_is_mod(const struct btf_type *t)
+{
+ __u16 kind = btf_kind(t);
+
+ return kind == BTF_KIND_VOLATILE ||
+ kind == BTF_KIND_CONST ||
+ kind == BTF_KIND_RESTRICT;
+}
+
+static inline bool btf_is_func(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_FUNC;
+}
+
+static inline bool btf_is_func_proto(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_FUNC_PROTO;
+}
+
+static inline bool btf_is_var(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_VAR;
+}
+
+static inline bool btf_is_datasec(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_DATASEC;
+}
+
+static inline __u8 btf_int_encoding(const struct btf_type *t)
+{
+ return BTF_INT_ENCODING(*(__u32 *)(t + 1));
+}
+
+static inline __u8 btf_int_offset(const struct btf_type *t)
+{
+ return BTF_INT_OFFSET(*(__u32 *)(t + 1));
+}
+
+static inline __u8 btf_int_bits(const struct btf_type *t)
+{
+ return BTF_INT_BITS(*(__u32 *)(t + 1));
+}
+
+static inline struct btf_array *btf_array(const struct btf_type *t)
+{
+ return (struct btf_array *)(t + 1);
+}
+
+static inline struct btf_enum *btf_enum(const struct btf_type *t)
+{
+ return (struct btf_enum *)(t + 1);
+}
+
+static inline struct btf_member *btf_members(const struct btf_type *t)
+{
+ return (struct btf_member *)(t + 1);
+}
+
+/* Get bit offset of a member with specified index. */
+static inline __u32 btf_member_bit_offset(const struct btf_type *t,
+ __u32 member_idx)
+{
+ const struct btf_member *m = btf_members(t) + member_idx;
+ bool kflag = btf_kflag(t);
+
+ return kflag ? BTF_MEMBER_BIT_OFFSET(m->offset) : m->offset;
+}
+/*
+ * Get bitfield size of a member, assuming t is BTF_KIND_STRUCT or
+ * BTF_KIND_UNION. If member is not a bitfield, zero is returned.
+ */
+static inline __u32 btf_member_bitfield_size(const struct btf_type *t,
+ __u32 member_idx)
+{
+ const struct btf_member *m = btf_members(t) + member_idx;
+ bool kflag = btf_kflag(t);
+
+ return kflag ? BTF_MEMBER_BITFIELD_SIZE(m->offset) : 0;
+}
+
+static inline struct btf_param *btf_params(const struct btf_type *t)
+{
+ return (struct btf_param *)(t + 1);
+}
+
+static inline struct btf_var *btf_var(const struct btf_type *t)
+{
+ return (struct btf_var *)(t + 1);
+}
+
+static inline struct btf_var_secinfo *
+btf_var_secinfos(const struct btf_type *t)
+{
+ return (struct btf_var_secinfo *)(t + 1);
+}
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c
new file mode 100644
index 000000000000..715967762312
--- /dev/null
+++ b/tools/lib/bpf/btf_dump.c
@@ -0,0 +1,1287 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+/*
+ * BTF-to-C type converter.
+ *
+ * Copyright (c) 2019 Facebook
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <linux/err.h>
+#include <linux/btf.h>
+#include "btf.h"
+#include "hashmap.h"
+#include "libbpf.h"
+#include "libbpf_internal.h"
+
+static const char PREFIXES[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t";
+static const size_t PREFIX_CNT = sizeof(PREFIXES) - 1;
+
+static const char *pfx(int lvl)
+{
+ return lvl >= PREFIX_CNT ? PREFIXES : &PREFIXES[PREFIX_CNT - lvl];
+}
+
+enum btf_dump_type_order_state {
+ NOT_ORDERED,
+ ORDERING,
+ ORDERED,
+};
+
+enum btf_dump_type_emit_state {
+ NOT_EMITTED,
+ EMITTING,
+ EMITTED,
+};
+
+/* per-type auxiliary state */
+struct btf_dump_type_aux_state {
+ /* topological sorting state */
+ enum btf_dump_type_order_state order_state: 2;
+ /* emitting state used to determine the need for forward declaration */
+ enum btf_dump_type_emit_state emit_state: 2;
+ /* whether forward declaration was already emitted */
+ __u8 fwd_emitted: 1;
+ /* whether unique non-duplicate name was already assigned */
+ __u8 name_resolved: 1;
+};
+
+struct btf_dump {
+ const struct btf *btf;
+ const struct btf_ext *btf_ext;
+ btf_dump_printf_fn_t printf_fn;
+ struct btf_dump_opts opts;
+
+ /* per-type auxiliary state */
+ struct btf_dump_type_aux_state *type_states;
+ /* per-type optional cached unique name, must be freed, if present */
+ const char **cached_names;
+
+ /* topo-sorted list of dependent type definitions */
+ __u32 *emit_queue;
+ int emit_queue_cap;
+ int emit_queue_cnt;
+
+ /*
+ * stack of type declarations (e.g., chain of modifiers, arrays,
+ * funcs, etc)
+ */
+ __u32 *decl_stack;
+ int decl_stack_cap;
+ int decl_stack_cnt;
+
+ /* maps struct/union/enum name to a number of name occurrences */
+ struct hashmap *type_names;
+ /*
+ * maps typedef identifiers and enum value names to a number of such
+ * name occurrences
+ */
+ struct hashmap *ident_names;
+};
+
+static size_t str_hash_fn(const void *key, void *ctx)
+{
+ const char *s = key;
+ size_t h = 0;
+
+ while (*s) {
+ h = h * 31 + *s;
+ s++;
+ }
+ return h;
+}
+
+static bool str_equal_fn(const void *a, const void *b, void *ctx)
+{
+ return strcmp(a, b) == 0;
+}
+
+static const char *btf_name_of(const struct btf_dump *d, __u32 name_off)
+{
+ return btf__name_by_offset(d->btf, name_off);
+}
+
+static void btf_dump_printf(const struct btf_dump *d, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ d->printf_fn(d->opts.ctx, fmt, args);
+ va_end(args);
+}
+
+struct btf_dump *btf_dump__new(const struct btf *btf,
+ const struct btf_ext *btf_ext,
+ const struct btf_dump_opts *opts,
+ btf_dump_printf_fn_t printf_fn)
+{
+ struct btf_dump *d;
+ int err;
+
+ d = calloc(1, sizeof(struct btf_dump));
+ if (!d)
+ return ERR_PTR(-ENOMEM);
+
+ d->btf = btf;
+ d->btf_ext = btf_ext;
+ d->printf_fn = printf_fn;
+ d->opts.ctx = opts ? opts->ctx : NULL;
+
+ d->type_names = hashmap__new(str_hash_fn, str_equal_fn, NULL);
+ if (IS_ERR(d->type_names)) {
+ err = PTR_ERR(d->type_names);
+ d->type_names = NULL;
+ btf_dump__free(d);
+ return ERR_PTR(err);
+ }
+ d->ident_names = hashmap__new(str_hash_fn, str_equal_fn, NULL);
+ if (IS_ERR(d->ident_names)) {
+ err = PTR_ERR(d->ident_names);
+ d->ident_names = NULL;
+ btf_dump__free(d);
+ return ERR_PTR(err);
+ }
+
+ return d;
+}
+
+void btf_dump__free(struct btf_dump *d)
+{
+ int i, cnt;
+
+ if (!d)
+ return;
+
+ free(d->type_states);
+ if (d->cached_names) {
+ /* any set cached name is owned by us and should be freed */
+ for (i = 0, cnt = btf__get_nr_types(d->btf); i <= cnt; i++) {
+ if (d->cached_names[i])
+ free((void *)d->cached_names[i]);
+ }
+ }
+ free(d->cached_names);
+ free(d->emit_queue);
+ free(d->decl_stack);
+ hashmap__free(d->type_names);
+ hashmap__free(d->ident_names);
+
+ free(d);
+}
+
+static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr);
+static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id);
+
+/*
+ * Dump BTF type in a compilable C syntax, including all the necessary
+ * dependent types, necessary for compilation. If some of the dependent types
+ * were already emitted as part of previous btf_dump__dump_type() invocation
+ * for another type, they won't be emitted again. This API allows callers to
+ * filter out BTF types according to user-defined criterias and emitted only
+ * minimal subset of types, necessary to compile everything. Full struct/union
+ * definitions will still be emitted, even if the only usage is through
+ * pointer and could be satisfied with just a forward declaration.
+ *
+ * Dumping is done in two high-level passes:
+ * 1. Topologically sort type definitions to satisfy C rules of compilation.
+ * 2. Emit type definitions in C syntax.
+ *
+ * Returns 0 on success; <0, otherwise.
+ */
+int btf_dump__dump_type(struct btf_dump *d, __u32 id)
+{
+ int err, i;
+
+ if (id > btf__get_nr_types(d->btf))
+ return -EINVAL;
+
+ /* type states are lazily allocated, as they might not be needed */
+ if (!d->type_states) {
+ d->type_states = calloc(1 + btf__get_nr_types(d->btf),
+ sizeof(d->type_states[0]));
+ if (!d->type_states)
+ return -ENOMEM;
+ d->cached_names = calloc(1 + btf__get_nr_types(d->btf),
+ sizeof(d->cached_names[0]));
+ if (!d->cached_names)
+ return -ENOMEM;
+
+ /* VOID is special */
+ d->type_states[0].order_state = ORDERED;
+ d->type_states[0].emit_state = EMITTED;
+ }
+
+ d->emit_queue_cnt = 0;
+ err = btf_dump_order_type(d, id, false);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < d->emit_queue_cnt; i++)
+ btf_dump_emit_type(d, d->emit_queue[i], 0 /*top-level*/);
+
+ return 0;
+}
+
+static int btf_dump_add_emit_queue_id(struct btf_dump *d, __u32 id)
+{
+ __u32 *new_queue;
+ size_t new_cap;
+
+ if (d->emit_queue_cnt >= d->emit_queue_cap) {
+ new_cap = max(16, d->emit_queue_cap * 3 / 2);
+ new_queue = realloc(d->emit_queue,
+ new_cap * sizeof(new_queue[0]));
+ if (!new_queue)
+ return -ENOMEM;
+ d->emit_queue = new_queue;
+ d->emit_queue_cap = new_cap;
+ }
+
+ d->emit_queue[d->emit_queue_cnt++] = id;
+ return 0;
+}
+
+/*
+ * Determine order of emitting dependent types and specified type to satisfy
+ * C compilation rules. This is done through topological sorting with an
+ * additional complication which comes from C rules. The main idea for C is
+ * that if some type is "embedded" into a struct/union, it's size needs to be
+ * known at the time of definition of containing type. E.g., for:
+ *
+ * struct A {};
+ * struct B { struct A x; }
+ *
+ * struct A *HAS* to be defined before struct B, because it's "embedded",
+ * i.e., it is part of struct B layout. But in the following case:
+ *
+ * struct A;
+ * struct B { struct A *x; }
+ * struct A {};
+ *
+ * it's enough to just have a forward declaration of struct A at the time of
+ * struct B definition, as struct B has a pointer to struct A, so the size of
+ * field x is known without knowing struct A size: it's sizeof(void *).
+ *
+ * Unfortunately, there are some trickier cases we need to handle, e.g.:
+ *
+ * struct A {}; // if this was forward-declaration: compilation error
+ * struct B {
+ * struct { // anonymous struct
+ * struct A y;
+ * } *x;
+ * };
+ *
+ * In this case, struct B's field x is a pointer, so it's size is known
+ * regardless of the size of (anonymous) struct it points to. But because this
+ * struct is anonymous and thus defined inline inside struct B, *and* it
+ * embeds struct A, compiler requires full definition of struct A to be known
+ * before struct B can be defined. This creates a transitive dependency
+ * between struct A and struct B. If struct A was forward-declared before
+ * struct B definition and fully defined after struct B definition, that would
+ * trigger compilation error.
+ *
+ * All this means that while we are doing topological sorting on BTF type
+ * graph, we need to determine relationships between different types (graph
+ * nodes):
+ * - weak link (relationship) between X and Y, if Y *CAN* be
+ * forward-declared at the point of X definition;
+ * - strong link, if Y *HAS* to be fully-defined before X can be defined.
+ *
+ * The rule is as follows. Given a chain of BTF types from X to Y, if there is
+ * BTF_KIND_PTR type in the chain and at least one non-anonymous type
+ * Z (excluding X, including Y), then link is weak. Otherwise, it's strong.
+ * Weak/strong relationship is determined recursively during DFS traversal and
+ * is returned as a result from btf_dump_order_type().
+ *
+ * btf_dump_order_type() is trying to avoid unnecessary forward declarations,
+ * but it is not guaranteeing that no extraneous forward declarations will be
+ * emitted.
+ *
+ * To avoid extra work, algorithm marks some of BTF types as ORDERED, when
+ * it's done with them, but not for all (e.g., VOLATILE, CONST, RESTRICT,
+ * ARRAY, FUNC_PROTO), as weak/strong semantics for those depends on the
+ * entire graph path, so depending where from one came to that BTF type, it
+ * might cause weak or strong ordering. For types like STRUCT/UNION/INT/ENUM,
+ * once they are processed, there is no need to do it again, so they are
+ * marked as ORDERED. We can mark PTR as ORDERED as well, as it semi-forces
+ * weak link, unless subsequent referenced STRUCT/UNION/ENUM is anonymous. But
+ * in any case, once those are processed, no need to do it again, as the
+ * result won't change.
+ *
+ * Returns:
+ * - 1, if type is part of strong link (so there is strong topological
+ * ordering requirements);
+ * - 0, if type is part of weak link (so can be satisfied through forward
+ * declaration);
+ * - <0, on error (e.g., unsatisfiable type loop detected).
+ */
+static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr)
+{
+ /*
+ * Order state is used to detect strong link cycles, but only for BTF
+ * kinds that are or could be an independent definition (i.e.,
+ * stand-alone fwd decl, enum, typedef, struct, union). Ptrs, arrays,
+ * func_protos, modifiers are just means to get to these definitions.
+ * Int/void don't need definitions, they are assumed to be always
+ * properly defined. We also ignore datasec, var, and funcs for now.
+ * So for all non-defining kinds, we never even set ordering state,
+ * for defining kinds we set ORDERING and subsequently ORDERED if it
+ * forms a strong link.
+ */
+ struct btf_dump_type_aux_state *tstate = &d->type_states[id];
+ const struct btf_type *t;
+ __u16 vlen;
+ int err, i;
+
+ /* return true, letting typedefs know that it's ok to be emitted */
+ if (tstate->order_state == ORDERED)
+ return 1;
+
+ t = btf__type_by_id(d->btf, id);
+
+ if (tstate->order_state == ORDERING) {
+ /* type loop, but resolvable through fwd declaration */
+ if (btf_is_composite(t) && through_ptr && t->name_off != 0)
+ return 0;
+ pr_warning("unsatisfiable type cycle, id:[%u]\n", id);
+ return -ELOOP;
+ }
+
+ switch (btf_kind(t)) {
+ case BTF_KIND_INT:
+ tstate->order_state = ORDERED;
+ return 0;
+
+ case BTF_KIND_PTR:
+ err = btf_dump_order_type(d, t->type, true);
+ tstate->order_state = ORDERED;
+ return err;
+
+ case BTF_KIND_ARRAY:
+ return btf_dump_order_type(d, btf_array(t)->type, through_ptr);
+
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION: {
+ const struct btf_member *m = btf_members(t);
+ /*
+ * struct/union is part of strong link, only if it's embedded
+ * (so no ptr in a path) or it's anonymous (so has to be
+ * defined inline, even if declared through ptr)
+ */
+ if (through_ptr && t->name_off != 0)
+ return 0;
+
+ tstate->order_state = ORDERING;
+
+ vlen = btf_vlen(t);
+ for (i = 0; i < vlen; i++, m++) {
+ err = btf_dump_order_type(d, m->type, false);
+ if (err < 0)
+ return err;
+ }
+
+ if (t->name_off != 0) {
+ err = btf_dump_add_emit_queue_id(d, id);
+ if (err < 0)
+ return err;
+ }
+
+ tstate->order_state = ORDERED;
+ return 1;
+ }
+ case BTF_KIND_ENUM:
+ case BTF_KIND_FWD:
+ if (t->name_off != 0) {
+ err = btf_dump_add_emit_queue_id(d, id);
+ if (err)
+ return err;
+ }
+ tstate->order_state = ORDERED;
+ return 1;
+
+ case BTF_KIND_TYPEDEF: {
+ int is_strong;
+
+ is_strong = btf_dump_order_type(d, t->type, through_ptr);
+ if (is_strong < 0)
+ return is_strong;
+
+ /* typedef is similar to struct/union w.r.t. fwd-decls */
+ if (through_ptr && !is_strong)
+ return 0;
+
+ /* typedef is always a named definition */
+ err = btf_dump_add_emit_queue_id(d, id);
+ if (err)
+ return err;
+
+ d->type_states[id].order_state = ORDERED;
+ return 1;
+ }
+ case BTF_KIND_VOLATILE:
+ case BTF_KIND_CONST:
+ case BTF_KIND_RESTRICT:
+ return btf_dump_order_type(d, t->type, through_ptr);
+
+ case BTF_KIND_FUNC_PROTO: {
+ const struct btf_param *p = btf_params(t);
+ bool is_strong;
+
+ err = btf_dump_order_type(d, t->type, through_ptr);
+ if (err < 0)
+ return err;
+ is_strong = err > 0;
+
+ vlen = btf_vlen(t);
+ for (i = 0; i < vlen; i++, p++) {
+ err = btf_dump_order_type(d, p->type, through_ptr);
+ if (err < 0)
+ return err;
+ if (err > 0)
+ is_strong = true;
+ }
+ return is_strong;
+ }
+ case BTF_KIND_FUNC:
+ case BTF_KIND_VAR:
+ case BTF_KIND_DATASEC:
+ d->type_states[id].order_state = ORDERED;
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static void btf_dump_emit_struct_fwd(struct btf_dump *d, __u32 id,
+ const struct btf_type *t);
+static void btf_dump_emit_struct_def(struct btf_dump *d, __u32 id,
+ const struct btf_type *t, int lvl);
+
+static void btf_dump_emit_enum_fwd(struct btf_dump *d, __u32 id,
+ const struct btf_type *t);
+static void btf_dump_emit_enum_def(struct btf_dump *d, __u32 id,
+ const struct btf_type *t, int lvl);
+
+static void btf_dump_emit_fwd_def(struct btf_dump *d, __u32 id,
+ const struct btf_type *t);
+
+static void btf_dump_emit_typedef_def(struct btf_dump *d, __u32 id,
+ const struct btf_type *t, int lvl);
+
+/* a local view into a shared stack */
+struct id_stack {
+ const __u32 *ids;
+ int cnt;
+};
+
+static void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id,
+ const char *fname, int lvl);
+static void btf_dump_emit_type_chain(struct btf_dump *d,
+ struct id_stack *decl_stack,
+ const char *fname, int lvl);
+
+static const char *btf_dump_type_name(struct btf_dump *d, __u32 id);
+static const char *btf_dump_ident_name(struct btf_dump *d, __u32 id);
+static size_t btf_dump_name_dups(struct btf_dump *d, struct hashmap *name_map,
+ const char *orig_name);
+
+static bool btf_dump_is_blacklisted(struct btf_dump *d, __u32 id)
+{
+ const struct btf_type *t = btf__type_by_id(d->btf, id);
+
+ /* __builtin_va_list is a compiler built-in, which causes compilation
+ * errors, when compiling w/ different compiler, then used to compile
+ * original code (e.g., GCC to compile kernel, Clang to use generated
+ * C header from BTF). As it is built-in, it should be already defined
+ * properly internally in compiler.
+ */
+ if (t->name_off == 0)
+ return false;
+ return strcmp(btf_name_of(d, t->name_off), "__builtin_va_list") == 0;
+}
+
+/*
+ * Emit C-syntax definitions of types from chains of BTF types.
+ *
+ * High-level handling of determining necessary forward declarations are handled
+ * by btf_dump_emit_type() itself, but all nitty-gritty details of emitting type
+ * declarations/definitions in C syntax are handled by a combo of
+ * btf_dump_emit_type_decl()/btf_dump_emit_type_chain() w/ delegation to
+ * corresponding btf_dump_emit_*_{def,fwd}() functions.
+ *
+ * We also keep track of "containing struct/union type ID" to determine when
+ * we reference it from inside and thus can avoid emitting unnecessary forward
+ * declaration.
+ *
+ * This algorithm is designed in such a way, that even if some error occurs
+ * (either technical, e.g., out of memory, or logical, i.e., malformed BTF
+ * that doesn't comply to C rules completely), algorithm will try to proceed
+ * and produce as much meaningful output as possible.
+ */
+static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id)
+{
+ struct btf_dump_type_aux_state *tstate = &d->type_states[id];
+ bool top_level_def = cont_id == 0;
+ const struct btf_type *t;
+ __u16 kind;
+
+ if (tstate->emit_state == EMITTED)
+ return;
+
+ t = btf__type_by_id(d->btf, id);
+ kind = btf_kind(t);
+
+ if (top_level_def && t->name_off == 0) {
+ pr_warning("unexpected nameless definition, id:[%u]\n", id);
+ return;
+ }
+
+ if (tstate->emit_state == EMITTING) {
+ if (tstate->fwd_emitted)
+ return;
+
+ switch (kind) {
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION:
+ /*
+ * if we are referencing a struct/union that we are
+ * part of - then no need for fwd declaration
+ */
+ if (id == cont_id)
+ return;
+ if (t->name_off == 0) {
+ pr_warning("anonymous struct/union loop, id:[%u]\n",
+ id);
+ return;
+ }
+ btf_dump_emit_struct_fwd(d, id, t);
+ btf_dump_printf(d, ";\n\n");
+ tstate->fwd_emitted = 1;
+ break;
+ case BTF_KIND_TYPEDEF:
+ /*
+ * for typedef fwd_emitted means typedef definition
+ * was emitted, but it can be used only for "weak"
+ * references through pointer only, not for embedding
+ */
+ if (!btf_dump_is_blacklisted(d, id)) {
+ btf_dump_emit_typedef_def(d, id, t, 0);
+ btf_dump_printf(d, ";\n\n");
+ };
+ tstate->fwd_emitted = 1;
+ break;
+ default:
+ break;
+ }
+
+ return;
+ }
+
+ switch (kind) {
+ case BTF_KIND_INT:
+ tstate->emit_state = EMITTED;
+ break;
+ case BTF_KIND_ENUM:
+ if (top_level_def) {
+ btf_dump_emit_enum_def(d, id, t, 0);
+ btf_dump_printf(d, ";\n\n");
+ }
+ tstate->emit_state = EMITTED;
+ break;
+ case BTF_KIND_PTR:
+ case BTF_KIND_VOLATILE:
+ case BTF_KIND_CONST:
+ case BTF_KIND_RESTRICT:
+ btf_dump_emit_type(d, t->type, cont_id);
+ break;
+ case BTF_KIND_ARRAY:
+ btf_dump_emit_type(d, btf_array(t)->type, cont_id);
+ break;
+ case BTF_KIND_FWD:
+ btf_dump_emit_fwd_def(d, id, t);
+ btf_dump_printf(d, ";\n\n");
+ tstate->emit_state = EMITTED;
+ break;
+ case BTF_KIND_TYPEDEF:
+ tstate->emit_state = EMITTING;
+ btf_dump_emit_type(d, t->type, id);
+ /*
+ * typedef can server as both definition and forward
+ * declaration; at this stage someone depends on
+ * typedef as a forward declaration (refers to it
+ * through pointer), so unless we already did it,
+ * emit typedef as a forward declaration
+ */
+ if (!tstate->fwd_emitted && !btf_dump_is_blacklisted(d, id)) {
+ btf_dump_emit_typedef_def(d, id, t, 0);
+ btf_dump_printf(d, ";\n\n");
+ }
+ tstate->emit_state = EMITTED;
+ break;
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION:
+ tstate->emit_state = EMITTING;
+ /* if it's a top-level struct/union definition or struct/union
+ * is anonymous, then in C we'll be emitting all fields and
+ * their types (as opposed to just `struct X`), so we need to
+ * make sure that all types, referenced from struct/union
+ * members have necessary forward-declarations, where
+ * applicable
+ */
+ if (top_level_def || t->name_off == 0) {
+ const struct btf_member *m = btf_members(t);
+ __u16 vlen = btf_vlen(t);
+ int i, new_cont_id;
+
+ new_cont_id = t->name_off == 0 ? cont_id : id;
+ for (i = 0; i < vlen; i++, m++)
+ btf_dump_emit_type(d, m->type, new_cont_id);
+ } else if (!tstate->fwd_emitted && id != cont_id) {
+ btf_dump_emit_struct_fwd(d, id, t);
+ btf_dump_printf(d, ";\n\n");
+ tstate->fwd_emitted = 1;
+ }
+
+ if (top_level_def) {
+ btf_dump_emit_struct_def(d, id, t, 0);
+ btf_dump_printf(d, ";\n\n");
+ tstate->emit_state = EMITTED;
+ } else {
+ tstate->emit_state = NOT_EMITTED;
+ }
+ break;
+ case BTF_KIND_FUNC_PROTO: {
+ const struct btf_param *p = btf_params(t);
+ __u16 vlen = btf_vlen(t);
+ int i;
+
+ btf_dump_emit_type(d, t->type, cont_id);
+ for (i = 0; i < vlen; i++, p++)
+ btf_dump_emit_type(d, p->type, cont_id);
+
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static int btf_align_of(const struct btf *btf, __u32 id)
+{
+ const struct btf_type *t = btf__type_by_id(btf, id);
+ __u16 kind = btf_kind(t);
+
+ switch (kind) {
+ case BTF_KIND_INT:
+ case BTF_KIND_ENUM:
+ return min(sizeof(void *), t->size);
+ case BTF_KIND_PTR:
+ return sizeof(void *);
+ case BTF_KIND_TYPEDEF:
+ case BTF_KIND_VOLATILE:
+ case BTF_KIND_CONST:
+ case BTF_KIND_RESTRICT:
+ return btf_align_of(btf, t->type);
+ case BTF_KIND_ARRAY:
+ return btf_align_of(btf, btf_array(t)->type);
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION: {
+ const struct btf_member *m = btf_members(t);
+ __u16 vlen = btf_vlen(t);
+ int i, align = 1;
+
+ for (i = 0; i < vlen; i++, m++)
+ align = max(align, btf_align_of(btf, m->type));
+
+ return align;
+ }
+ default:
+ pr_warning("unsupported BTF_KIND:%u\n", btf_kind(t));
+ return 1;
+ }
+}
+
+static bool btf_is_struct_packed(const struct btf *btf, __u32 id,
+ const struct btf_type *t)
+{
+ const struct btf_member *m;
+ int align, i, bit_sz;
+ __u16 vlen;
+
+ align = btf_align_of(btf, id);
+ /* size of a non-packed struct has to be a multiple of its alignment*/
+ if (t->size % align)
+ return true;
+
+ m = btf_members(t);
+ vlen = btf_vlen(t);
+ /* all non-bitfield fields have to be naturally aligned */
+ for (i = 0; i < vlen; i++, m++) {
+ align = btf_align_of(btf, m->type);
+ bit_sz = btf_member_bitfield_size(t, i);
+ if (bit_sz == 0 && m->offset % (8 * align) != 0)
+ return true;
+ }
+
+ /*
+ * if original struct was marked as packed, but its layout is
+ * naturally aligned, we'll detect that it's not packed
+ */
+ return false;
+}
+
+static int chip_away_bits(int total, int at_most)
+{
+ return total % at_most ? : at_most;
+}
+
+static void btf_dump_emit_bit_padding(const struct btf_dump *d,
+ int cur_off, int m_off, int m_bit_sz,
+ int align, int lvl)
+{
+ int off_diff = m_off - cur_off;
+ int ptr_bits = sizeof(void *) * 8;
+
+ if (off_diff <= 0)
+ /* no gap */
+ return;
+ if (m_bit_sz == 0 && off_diff < align * 8)
+ /* natural padding will take care of a gap */
+ return;
+
+ while (off_diff > 0) {
+ const char *pad_type;
+ int pad_bits;
+
+ if (ptr_bits > 32 && off_diff > 32) {
+ pad_type = "long";
+ pad_bits = chip_away_bits(off_diff, ptr_bits);
+ } else if (off_diff > 16) {
+ pad_type = "int";
+ pad_bits = chip_away_bits(off_diff, 32);
+ } else if (off_diff > 8) {
+ pad_type = "short";
+ pad_bits = chip_away_bits(off_diff, 16);
+ } else {
+ pad_type = "char";
+ pad_bits = chip_away_bits(off_diff, 8);
+ }
+ btf_dump_printf(d, "\n%s%s: %d;", pfx(lvl), pad_type, pad_bits);
+ off_diff -= pad_bits;
+ }
+}
+
+static void btf_dump_emit_struct_fwd(struct btf_dump *d, __u32 id,
+ const struct btf_type *t)
+{
+ btf_dump_printf(d, "%s %s",
+ btf_is_struct(t) ? "struct" : "union",
+ btf_dump_type_name(d, id));
+}
+
+static void btf_dump_emit_struct_def(struct btf_dump *d,
+ __u32 id,
+ const struct btf_type *t,
+ int lvl)
+{
+ const struct btf_member *m = btf_members(t);
+ bool is_struct = btf_is_struct(t);
+ int align, i, packed, off = 0;
+ __u16 vlen = btf_vlen(t);
+
+ packed = is_struct ? btf_is_struct_packed(d->btf, id, t) : 0;
+ align = packed ? 1 : btf_align_of(d->btf, id);
+
+ btf_dump_printf(d, "%s%s%s {",
+ is_struct ? "struct" : "union",
+ t->name_off ? " " : "",
+ btf_dump_type_name(d, id));
+
+ for (i = 0; i < vlen; i++, m++) {
+ const char *fname;
+ int m_off, m_sz;
+
+ fname = btf_name_of(d, m->name_off);
+ m_sz = btf_member_bitfield_size(t, i);
+ m_off = btf_member_bit_offset(t, i);
+ align = packed ? 1 : btf_align_of(d->btf, m->type);
+
+ btf_dump_emit_bit_padding(d, off, m_off, m_sz, align, lvl + 1);
+ btf_dump_printf(d, "\n%s", pfx(lvl + 1));
+ btf_dump_emit_type_decl(d, m->type, fname, lvl + 1);
+
+ if (m_sz) {
+ btf_dump_printf(d, ": %d", m_sz);
+ off = m_off + m_sz;
+ } else {
+ m_sz = max(0, btf__resolve_size(d->btf, m->type));
+ off = m_off + m_sz * 8;
+ }
+ btf_dump_printf(d, ";");
+ }
+
+ if (vlen)
+ btf_dump_printf(d, "\n");
+ btf_dump_printf(d, "%s}", pfx(lvl));
+ if (packed)
+ btf_dump_printf(d, " __attribute__((packed))");
+}
+
+static void btf_dump_emit_enum_fwd(struct btf_dump *d, __u32 id,
+ const struct btf_type *t)
+{
+ btf_dump_printf(d, "enum %s", btf_dump_type_name(d, id));
+}
+
+static void btf_dump_emit_enum_def(struct btf_dump *d, __u32 id,
+ const struct btf_type *t,
+ int lvl)
+{
+ const struct btf_enum *v = btf_enum(t);
+ __u16 vlen = btf_vlen(t);
+ const char *name;
+ size_t dup_cnt;
+ int i;
+
+ btf_dump_printf(d, "enum%s%s",
+ t->name_off ? " " : "",
+ btf_dump_type_name(d, id));
+
+ if (vlen) {
+ btf_dump_printf(d, " {");
+ for (i = 0; i < vlen; i++, v++) {
+ name = btf_name_of(d, v->name_off);
+ /* enumerators share namespace with typedef idents */
+ dup_cnt = btf_dump_name_dups(d, d->ident_names, name);
+ if (dup_cnt > 1) {
+ btf_dump_printf(d, "\n%s%s___%zu = %d,",
+ pfx(lvl + 1), name, dup_cnt,
+ (__s32)v->val);
+ } else {
+ btf_dump_printf(d, "\n%s%s = %d,",
+ pfx(lvl + 1), name,
+ (__s32)v->val);
+ }
+ }
+ btf_dump_printf(d, "\n%s}", pfx(lvl));
+ }
+}
+
+static void btf_dump_emit_fwd_def(struct btf_dump *d, __u32 id,
+ const struct btf_type *t)
+{
+ const char *name = btf_dump_type_name(d, id);
+
+ if (btf_kflag(t))
+ btf_dump_printf(d, "union %s", name);
+ else
+ btf_dump_printf(d, "struct %s", name);
+}
+
+static void btf_dump_emit_typedef_def(struct btf_dump *d, __u32 id,
+ const struct btf_type *t, int lvl)
+{
+ const char *name = btf_dump_ident_name(d, id);
+
+ btf_dump_printf(d, "typedef ");
+ btf_dump_emit_type_decl(d, t->type, name, lvl);
+}
+
+static int btf_dump_push_decl_stack_id(struct btf_dump *d, __u32 id)
+{
+ __u32 *new_stack;
+ size_t new_cap;
+
+ if (d->decl_stack_cnt >= d->decl_stack_cap) {
+ new_cap = max(16, d->decl_stack_cap * 3 / 2);
+ new_stack = realloc(d->decl_stack,
+ new_cap * sizeof(new_stack[0]));
+ if (!new_stack)
+ return -ENOMEM;
+ d->decl_stack = new_stack;
+ d->decl_stack_cap = new_cap;
+ }
+
+ d->decl_stack[d->decl_stack_cnt++] = id;
+
+ return 0;
+}
+
+/*
+ * Emit type declaration (e.g., field type declaration in a struct or argument
+ * declaration in function prototype) in correct C syntax.
+ *
+ * For most types it's trivial, but there are few quirky type declaration
+ * cases worth mentioning:
+ * - function prototypes (especially nesting of function prototypes);
+ * - arrays;
+ * - const/volatile/restrict for pointers vs other types.
+ *
+ * For a good discussion of *PARSING* C syntax (as a human), see
+ * Peter van der Linden's "Expert C Programming: Deep C Secrets",
+ * Ch.3 "Unscrambling Declarations in C".
+ *
+ * It won't help with BTF to C conversion much, though, as it's an opposite
+ * problem. So we came up with this algorithm in reverse to van der Linden's
+ * parsing algorithm. It goes from structured BTF representation of type
+ * declaration to a valid compilable C syntax.
+ *
+ * For instance, consider this C typedef:
+ * typedef const int * const * arr[10] arr_t;
+ * It will be represented in BTF with this chain of BTF types:
+ * [typedef] -> [array] -> [ptr] -> [const] -> [ptr] -> [const] -> [int]
+ *
+ * Notice how [const] modifier always goes before type it modifies in BTF type
+ * graph, but in C syntax, const/volatile/restrict modifiers are written to
+ * the right of pointers, but to the left of other types. There are also other
+ * quirks, like function pointers, arrays of them, functions returning other
+ * functions, etc.
+ *
+ * We handle that by pushing all the types to a stack, until we hit "terminal"
+ * type (int/enum/struct/union/fwd). Then depending on the kind of a type on
+ * top of a stack, modifiers are handled differently. Array/function pointers
+ * have also wildly different syntax and how nesting of them are done. See
+ * code for authoritative definition.
+ *
+ * To avoid allocating new stack for each independent chain of BTF types, we
+ * share one bigger stack, with each chain working only on its own local view
+ * of a stack frame. Some care is required to "pop" stack frames after
+ * processing type declaration chain.
+ */
+static void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id,
+ const char *fname, int lvl)
+{
+ struct id_stack decl_stack;
+ const struct btf_type *t;
+ int err, stack_start;
+
+ stack_start = d->decl_stack_cnt;
+ for (;;) {
+ err = btf_dump_push_decl_stack_id(d, id);
+ if (err < 0) {
+ /*
+ * if we don't have enough memory for entire type decl
+ * chain, restore stack, emit warning, and try to
+ * proceed nevertheless
+ */
+ pr_warning("not enough memory for decl stack:%d", err);
+ d->decl_stack_cnt = stack_start;
+ return;
+ }
+
+ /* VOID */
+ if (id == 0)
+ break;
+
+ t = btf__type_by_id(d->btf, id);
+ switch (btf_kind(t)) {
+ case BTF_KIND_PTR:
+ case BTF_KIND_VOLATILE:
+ case BTF_KIND_CONST:
+ case BTF_KIND_RESTRICT:
+ case BTF_KIND_FUNC_PROTO:
+ id = t->type;
+ break;
+ case BTF_KIND_ARRAY:
+ id = btf_array(t)->type;
+ break;
+ case BTF_KIND_INT:
+ case BTF_KIND_ENUM:
+ case BTF_KIND_FWD:
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION:
+ case BTF_KIND_TYPEDEF:
+ goto done;
+ default:
+ pr_warning("unexpected type in decl chain, kind:%u, id:[%u]\n",
+ btf_kind(t), id);
+ goto done;
+ }
+ }
+done:
+ /*
+ * We might be inside a chain of declarations (e.g., array of function
+ * pointers returning anonymous (so inlined) structs, having another
+ * array field). Each of those needs its own "stack frame" to handle
+ * emitting of declarations. Those stack frames are non-overlapping
+ * portions of shared btf_dump->decl_stack. To make it a bit nicer to
+ * handle this set of nested stacks, we create a view corresponding to
+ * our own "stack frame" and work with it as an independent stack.
+ * We'll need to clean up after emit_type_chain() returns, though.
+ */
+ decl_stack.ids = d->decl_stack + stack_start;
+ decl_stack.cnt = d->decl_stack_cnt - stack_start;
+ btf_dump_emit_type_chain(d, &decl_stack, fname, lvl);
+ /*
+ * emit_type_chain() guarantees that it will pop its entire decl_stack
+ * frame before returning. But it works with a read-only view into
+ * decl_stack, so it doesn't actually pop anything from the
+ * perspective of shared btf_dump->decl_stack, per se. We need to
+ * reset decl_stack state to how it was before us to avoid it growing
+ * all the time.
+ */
+ d->decl_stack_cnt = stack_start;
+}
+
+static void btf_dump_emit_mods(struct btf_dump *d, struct id_stack *decl_stack)
+{
+ const struct btf_type *t;
+ __u32 id;
+
+ while (decl_stack->cnt) {
+ id = decl_stack->ids[decl_stack->cnt - 1];
+ t = btf__type_by_id(d->btf, id);
+
+ switch (btf_kind(t)) {
+ case BTF_KIND_VOLATILE:
+ btf_dump_printf(d, "volatile ");
+ break;
+ case BTF_KIND_CONST:
+ btf_dump_printf(d, "const ");
+ break;
+ case BTF_KIND_RESTRICT:
+ btf_dump_printf(d, "restrict ");
+ break;
+ default:
+ return;
+ }
+ decl_stack->cnt--;
+ }
+}
+
+static void btf_dump_emit_name(const struct btf_dump *d,
+ const char *name, bool last_was_ptr)
+{
+ bool separate = name[0] && !last_was_ptr;
+
+ btf_dump_printf(d, "%s%s", separate ? " " : "", name);
+}
+
+static void btf_dump_emit_type_chain(struct btf_dump *d,
+ struct id_stack *decls,
+ const char *fname, int lvl)
+{
+ /*
+ * last_was_ptr is used to determine if we need to separate pointer
+ * asterisk (*) from previous part of type signature with space, so
+ * that we get `int ***`, instead of `int * * *`. We default to true
+ * for cases where we have single pointer in a chain. E.g., in ptr ->
+ * func_proto case. func_proto will start a new emit_type_chain call
+ * with just ptr, which should be emitted as (*) or (*<fname>), so we
+ * don't want to prepend space for that last pointer.
+ */
+ bool last_was_ptr = true;
+ const struct btf_type *t;
+ const char *name;
+ __u16 kind;
+ __u32 id;
+
+ while (decls->cnt) {
+ id = decls->ids[--decls->cnt];
+ if (id == 0) {
+ /* VOID is a special snowflake */
+ btf_dump_emit_mods(d, decls);
+ btf_dump_printf(d, "void");
+ last_was_ptr = false;
+ continue;
+ }
+
+ t = btf__type_by_id(d->btf, id);
+ kind = btf_kind(t);
+
+ switch (kind) {
+ case BTF_KIND_INT:
+ btf_dump_emit_mods(d, decls);
+ name = btf_name_of(d, t->name_off);
+ btf_dump_printf(d, "%s", name);
+ break;
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION:
+ btf_dump_emit_mods(d, decls);
+ /* inline anonymous struct/union */
+ if (t->name_off == 0)
+ btf_dump_emit_struct_def(d, id, t, lvl);
+ else
+ btf_dump_emit_struct_fwd(d, id, t);
+ break;
+ case BTF_KIND_ENUM:
+ btf_dump_emit_mods(d, decls);
+ /* inline anonymous enum */
+ if (t->name_off == 0)
+ btf_dump_emit_enum_def(d, id, t, lvl);
+ else
+ btf_dump_emit_enum_fwd(d, id, t);
+ break;
+ case BTF_KIND_FWD:
+ btf_dump_emit_mods(d, decls);
+ btf_dump_emit_fwd_def(d, id, t);
+ break;
+ case BTF_KIND_TYPEDEF:
+ btf_dump_emit_mods(d, decls);
+ btf_dump_printf(d, "%s", btf_dump_ident_name(d, id));
+ break;
+ case BTF_KIND_PTR:
+ btf_dump_printf(d, "%s", last_was_ptr ? "*" : " *");
+ break;
+ case BTF_KIND_VOLATILE:
+ btf_dump_printf(d, " volatile");
+ break;
+ case BTF_KIND_CONST:
+ btf_dump_printf(d, " const");
+ break;
+ case BTF_KIND_RESTRICT:
+ btf_dump_printf(d, " restrict");
+ break;
+ case BTF_KIND_ARRAY: {
+ const struct btf_array *a = btf_array(t);
+ const struct btf_type *next_t;
+ __u32 next_id;
+ bool multidim;
+ /*
+ * GCC has a bug
+ * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=8354)
+ * which causes it to emit extra const/volatile
+ * modifiers for an array, if array's element type has
+ * const/volatile modifiers. Clang doesn't do that.
+ * In general, it doesn't seem very meaningful to have
+ * a const/volatile modifier for array, so we are
+ * going to silently skip them here.
+ */
+ while (decls->cnt) {
+ next_id = decls->ids[decls->cnt - 1];
+ next_t = btf__type_by_id(d->btf, next_id);
+ if (btf_is_mod(next_t))
+ decls->cnt--;
+ else
+ break;
+ }
+
+ if (decls->cnt == 0) {
+ btf_dump_emit_name(d, fname, last_was_ptr);
+ btf_dump_printf(d, "[%u]", a->nelems);
+ return;
+ }
+
+ next_t = btf__type_by_id(d->btf, next_id);
+ multidim = btf_is_array(next_t);
+ /* we need space if we have named non-pointer */
+ if (fname[0] && !last_was_ptr)
+ btf_dump_printf(d, " ");
+ /* no parentheses for multi-dimensional array */
+ if (!multidim)
+ btf_dump_printf(d, "(");
+ btf_dump_emit_type_chain(d, decls, fname, lvl);
+ if (!multidim)
+ btf_dump_printf(d, ")");
+ btf_dump_printf(d, "[%u]", a->nelems);
+ return;
+ }
+ case BTF_KIND_FUNC_PROTO: {
+ const struct btf_param *p = btf_params(t);
+ __u16 vlen = btf_vlen(t);
+ int i;
+
+ btf_dump_emit_mods(d, decls);
+ if (decls->cnt) {
+ btf_dump_printf(d, " (");
+ btf_dump_emit_type_chain(d, decls, fname, lvl);
+ btf_dump_printf(d, ")");
+ } else {
+ btf_dump_emit_name(d, fname, last_was_ptr);
+ }
+ btf_dump_printf(d, "(");
+ /*
+ * Clang for BPF target generates func_proto with no
+ * args as a func_proto with a single void arg (e.g.,
+ * `int (*f)(void)` vs just `int (*f)()`). We are
+ * going to pretend there are no args for such case.
+ */
+ if (vlen == 1 && p->type == 0) {
+ btf_dump_printf(d, ")");
+ return;
+ }
+
+ for (i = 0; i < vlen; i++, p++) {
+ if (i > 0)
+ btf_dump_printf(d, ", ");
+
+ /* last arg of type void is vararg */
+ if (i == vlen - 1 && p->type == 0) {
+ btf_dump_printf(d, "...");
+ break;
+ }
+
+ name = btf_name_of(d, p->name_off);
+ btf_dump_emit_type_decl(d, p->type, name, lvl);
+ }
+
+ btf_dump_printf(d, ")");
+ return;
+ }
+ default:
+ pr_warning("unexpected type in decl chain, kind:%u, id:[%u]\n",
+ kind, id);
+ return;
+ }
+
+ last_was_ptr = kind == BTF_KIND_PTR;
+ }
+
+ btf_dump_emit_name(d, fname, last_was_ptr);
+}
+
+/* return number of duplicates (occurrences) of a given name */
+static size_t btf_dump_name_dups(struct btf_dump *d, struct hashmap *name_map,
+ const char *orig_name)
+{
+ size_t dup_cnt = 0;
+
+ hashmap__find(name_map, orig_name, (void **)&dup_cnt);
+ dup_cnt++;
+ hashmap__set(name_map, orig_name, (void *)dup_cnt, NULL, NULL);
+
+ return dup_cnt;
+}
+
+static const char *btf_dump_resolve_name(struct btf_dump *d, __u32 id,
+ struct hashmap *name_map)
+{
+ struct btf_dump_type_aux_state *s = &d->type_states[id];
+ const struct btf_type *t = btf__type_by_id(d->btf, id);
+ const char *orig_name = btf_name_of(d, t->name_off);
+ const char **cached_name = &d->cached_names[id];
+ size_t dup_cnt;
+
+ if (t->name_off == 0)
+ return "";
+
+ if (s->name_resolved)
+ return *cached_name ? *cached_name : orig_name;
+
+ dup_cnt = btf_dump_name_dups(d, name_map, orig_name);
+ if (dup_cnt > 1) {
+ const size_t max_len = 256;
+ char new_name[max_len];
+
+ snprintf(new_name, max_len, "%s___%zu", orig_name, dup_cnt);
+ *cached_name = strdup(new_name);
+ }
+
+ s->name_resolved = 1;
+ return *cached_name ? *cached_name : orig_name;
+}
+
+static const char *btf_dump_type_name(struct btf_dump *d, __u32 id)
+{
+ return btf_dump_resolve_name(d, id, d->type_names);
+}
+
+static const char *btf_dump_ident_name(struct btf_dump *d, __u32 id)
+{
+ return btf_dump_resolve_name(d, id, d->ident_names);
+}
diff --git a/tools/lib/bpf/hashmap.c b/tools/lib/bpf/hashmap.c
new file mode 100644
index 000000000000..6122272943e6
--- /dev/null
+++ b/tools/lib/bpf/hashmap.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+/*
+ * Generic non-thread safe hash map implementation.
+ *
+ * Copyright (c) 2019 Facebook
+ */
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <linux/err.h>
+#include "hashmap.h"
+
+/* start with 4 buckets */
+#define HASHMAP_MIN_CAP_BITS 2
+
+static void hashmap_add_entry(struct hashmap_entry **pprev,
+ struct hashmap_entry *entry)
+{
+ entry->next = *pprev;
+ *pprev = entry;
+}
+
+static void hashmap_del_entry(struct hashmap_entry **pprev,
+ struct hashmap_entry *entry)
+{
+ *pprev = entry->next;
+ entry->next = NULL;
+}
+
+void hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn, void *ctx)
+{
+ map->hash_fn = hash_fn;
+ map->equal_fn = equal_fn;
+ map->ctx = ctx;
+
+ map->buckets = NULL;
+ map->cap = 0;
+ map->cap_bits = 0;
+ map->sz = 0;
+}
+
+struct hashmap *hashmap__new(hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn,
+ void *ctx)
+{
+ struct hashmap *map = malloc(sizeof(struct hashmap));
+
+ if (!map)
+ return ERR_PTR(-ENOMEM);
+ hashmap__init(map, hash_fn, equal_fn, ctx);
+ return map;
+}
+
+void hashmap__clear(struct hashmap *map)
+{
+ free(map->buckets);
+ map->cap = map->cap_bits = map->sz = 0;
+}
+
+void hashmap__free(struct hashmap *map)
+{
+ if (!map)
+ return;
+
+ hashmap__clear(map);
+ free(map);
+}
+
+size_t hashmap__size(const struct hashmap *map)
+{
+ return map->sz;
+}
+
+size_t hashmap__capacity(const struct hashmap *map)
+{
+ return map->cap;
+}
+
+static bool hashmap_needs_to_grow(struct hashmap *map)
+{
+ /* grow if empty or more than 75% filled */
+ return (map->cap == 0) || ((map->sz + 1) * 4 / 3 > map->cap);
+}
+
+static int hashmap_grow(struct hashmap *map)
+{
+ struct hashmap_entry **new_buckets;
+ struct hashmap_entry *cur, *tmp;
+ size_t new_cap_bits, new_cap;
+ size_t h;
+ int bkt;
+
+ new_cap_bits = map->cap_bits + 1;
+ if (new_cap_bits < HASHMAP_MIN_CAP_BITS)
+ new_cap_bits = HASHMAP_MIN_CAP_BITS;
+
+ new_cap = 1UL << new_cap_bits;
+ new_buckets = calloc(new_cap, sizeof(new_buckets[0]));
+ if (!new_buckets)
+ return -ENOMEM;
+
+ hashmap__for_each_entry_safe(map, cur, tmp, bkt) {
+ h = hash_bits(map->hash_fn(cur->key, map->ctx), new_cap_bits);
+ hashmap_add_entry(&new_buckets[h], cur);
+ }
+
+ map->cap = new_cap;
+ map->cap_bits = new_cap_bits;
+ free(map->buckets);
+ map->buckets = new_buckets;
+
+ return 0;
+}
+
+static bool hashmap_find_entry(const struct hashmap *map,
+ const void *key, size_t hash,
+ struct hashmap_entry ***pprev,
+ struct hashmap_entry **entry)
+{
+ struct hashmap_entry *cur, **prev_ptr;
+
+ if (!map->buckets)
+ return false;
+
+ for (prev_ptr = &map->buckets[hash], cur = *prev_ptr;
+ cur;
+ prev_ptr = &cur->next, cur = cur->next) {
+ if (map->equal_fn(cur->key, key, map->ctx)) {
+ if (pprev)
+ *pprev = prev_ptr;
+ *entry = cur;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int hashmap__insert(struct hashmap *map, const void *key, void *value,
+ enum hashmap_insert_strategy strategy,
+ const void **old_key, void **old_value)
+{
+ struct hashmap_entry *entry;
+ size_t h;
+ int err;
+
+ if (old_key)
+ *old_key = NULL;
+ if (old_value)
+ *old_value = NULL;
+
+ h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
+ if (strategy != HASHMAP_APPEND &&
+ hashmap_find_entry(map, key, h, NULL, &entry)) {
+ if (old_key)
+ *old_key = entry->key;
+ if (old_value)
+ *old_value = entry->value;
+
+ if (strategy == HASHMAP_SET || strategy == HASHMAP_UPDATE) {
+ entry->key = key;
+ entry->value = value;
+ return 0;
+ } else if (strategy == HASHMAP_ADD) {
+ return -EEXIST;
+ }
+ }
+
+ if (strategy == HASHMAP_UPDATE)
+ return -ENOENT;
+
+ if (hashmap_needs_to_grow(map)) {
+ err = hashmap_grow(map);
+ if (err)
+ return err;
+ h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
+ }
+
+ entry = malloc(sizeof(struct hashmap_entry));
+ if (!entry)
+ return -ENOMEM;
+
+ entry->key = key;
+ entry->value = value;
+ hashmap_add_entry(&map->buckets[h], entry);
+ map->sz++;
+
+ return 0;
+}
+
+bool hashmap__find(const struct hashmap *map, const void *key, void **value)
+{
+ struct hashmap_entry *entry;
+ size_t h;
+
+ h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
+ if (!hashmap_find_entry(map, key, h, NULL, &entry))
+ return false;
+
+ if (value)
+ *value = entry->value;
+ return true;
+}
+
+bool hashmap__delete(struct hashmap *map, const void *key,
+ const void **old_key, void **old_value)
+{
+ struct hashmap_entry **pprev, *entry;
+ size_t h;
+
+ h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
+ if (!hashmap_find_entry(map, key, h, &pprev, &entry))
+ return false;
+
+ if (old_key)
+ *old_key = entry->key;
+ if (old_value)
+ *old_value = entry->value;
+
+ hashmap_del_entry(pprev, entry);
+ free(entry);
+ map->sz--;
+
+ return true;
+}
+
diff --git a/tools/lib/bpf/hashmap.h b/tools/lib/bpf/hashmap.h
new file mode 100644
index 000000000000..bae8879cdf58
--- /dev/null
+++ b/tools/lib/bpf/hashmap.h
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+
+/*
+ * Generic non-thread safe hash map implementation.
+ *
+ * Copyright (c) 2019 Facebook
+ */
+#ifndef __LIBBPF_HASHMAP_H
+#define __LIBBPF_HASHMAP_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#ifdef __GLIBC__
+#include <bits/wordsize.h>
+#else
+#include <bits/reg.h>
+#endif
+#include "libbpf_internal.h"
+
+static inline size_t hash_bits(size_t h, int bits)
+{
+ /* shuffle bits and return requested number of upper bits */
+ return (h * 11400714819323198485llu) >> (__WORDSIZE - bits);
+}
+
+typedef size_t (*hashmap_hash_fn)(const void *key, void *ctx);
+typedef bool (*hashmap_equal_fn)(const void *key1, const void *key2, void *ctx);
+
+struct hashmap_entry {
+ const void *key;
+ void *value;
+ struct hashmap_entry *next;
+};
+
+struct hashmap {
+ hashmap_hash_fn hash_fn;
+ hashmap_equal_fn equal_fn;
+ void *ctx;
+
+ struct hashmap_entry **buckets;
+ size_t cap;
+ size_t cap_bits;
+ size_t sz;
+};
+
+#define HASHMAP_INIT(hash_fn, equal_fn, ctx) { \
+ .hash_fn = (hash_fn), \
+ .equal_fn = (equal_fn), \
+ .ctx = (ctx), \
+ .buckets = NULL, \
+ .cap = 0, \
+ .cap_bits = 0, \
+ .sz = 0, \
+}
+
+void hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn, void *ctx);
+struct hashmap *hashmap__new(hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn,
+ void *ctx);
+void hashmap__clear(struct hashmap *map);
+void hashmap__free(struct hashmap *map);
+
+size_t hashmap__size(const struct hashmap *map);
+size_t hashmap__capacity(const struct hashmap *map);
+
+/*
+ * Hashmap insertion strategy:
+ * - HASHMAP_ADD - only add key/value if key doesn't exist yet;
+ * - HASHMAP_SET - add key/value pair if key doesn't exist yet; otherwise,
+ * update value;
+ * - HASHMAP_UPDATE - update value, if key already exists; otherwise, do
+ * nothing and return -ENOENT;
+ * - HASHMAP_APPEND - always add key/value pair, even if key already exists.
+ * This turns hashmap into a multimap by allowing multiple values to be
+ * associated with the same key. Most useful read API for such hashmap is
+ * hashmap__for_each_key_entry() iteration. If hashmap__find() is still
+ * used, it will return last inserted key/value entry (first in a bucket
+ * chain).
+ */
+enum hashmap_insert_strategy {
+ HASHMAP_ADD,
+ HASHMAP_SET,
+ HASHMAP_UPDATE,
+ HASHMAP_APPEND,
+};
+
+/*
+ * hashmap__insert() adds key/value entry w/ various semantics, depending on
+ * provided strategy value. If a given key/value pair replaced already
+ * existing key/value pair, both old key and old value will be returned
+ * through old_key and old_value to allow calling code do proper memory
+ * management.
+ */
+int hashmap__insert(struct hashmap *map, const void *key, void *value,
+ enum hashmap_insert_strategy strategy,
+ const void **old_key, void **old_value);
+
+static inline int hashmap__add(struct hashmap *map,
+ const void *key, void *value)
+{
+ return hashmap__insert(map, key, value, HASHMAP_ADD, NULL, NULL);
+}
+
+static inline int hashmap__set(struct hashmap *map,
+ const void *key, void *value,
+ const void **old_key, void **old_value)
+{
+ return hashmap__insert(map, key, value, HASHMAP_SET,
+ old_key, old_value);
+}
+
+static inline int hashmap__update(struct hashmap *map,
+ const void *key, void *value,
+ const void **old_key, void **old_value)
+{
+ return hashmap__insert(map, key, value, HASHMAP_UPDATE,
+ old_key, old_value);
+}
+
+static inline int hashmap__append(struct hashmap *map,
+ const void *key, void *value)
+{
+ return hashmap__insert(map, key, value, HASHMAP_APPEND, NULL, NULL);
+}
+
+bool hashmap__delete(struct hashmap *map, const void *key,
+ const void **old_key, void **old_value);
+
+bool hashmap__find(const struct hashmap *map, const void *key, void **value);
+
+/*
+ * hashmap__for_each_entry - iterate over all entries in hashmap
+ * @map: hashmap to iterate
+ * @cur: struct hashmap_entry * used as a loop cursor
+ * @bkt: integer used as a bucket loop cursor
+ */
+#define hashmap__for_each_entry(map, cur, bkt) \
+ for (bkt = 0; bkt < map->cap; bkt++) \
+ for (cur = map->buckets[bkt]; cur; cur = cur->next)
+
+/*
+ * hashmap__for_each_entry_safe - iterate over all entries in hashmap, safe
+ * against removals
+ * @map: hashmap to iterate
+ * @cur: struct hashmap_entry * used as a loop cursor
+ * @tmp: struct hashmap_entry * used as a temporary next cursor storage
+ * @bkt: integer used as a bucket loop cursor
+ */
+#define hashmap__for_each_entry_safe(map, cur, tmp, bkt) \
+ for (bkt = 0; bkt < map->cap; bkt++) \
+ for (cur = map->buckets[bkt]; \
+ cur && ({tmp = cur->next; true; }); \
+ cur = tmp)
+
+/*
+ * hashmap__for_each_key_entry - iterate over entries associated with given key
+ * @map: hashmap to iterate
+ * @cur: struct hashmap_entry * used as a loop cursor
+ * @key: key to iterate entries for
+ */
+#define hashmap__for_each_key_entry(map, cur, _key) \
+ for (cur = ({ size_t bkt = hash_bits(map->hash_fn((_key), map->ctx),\
+ map->cap_bits); \
+ map->buckets ? map->buckets[bkt] : NULL; }); \
+ cur; \
+ cur = cur->next) \
+ if (map->equal_fn(cur->key, (_key), map->ctx))
+
+#define hashmap__for_each_key_entry_safe(map, cur, tmp, _key) \
+ for (cur = ({ size_t bkt = hash_bits(map->hash_fn((_key), map->ctx),\
+ map->cap_bits); \
+ cur = map->buckets ? map->buckets[bkt] : NULL; }); \
+ cur && ({ tmp = cur->next; true; }); \
+ cur = tmp) \
+ if (map->equal_fn(cur->key, (_key), map->ctx))
+
+#endif /* __LIBBPF_HASHMAP_H */
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 7e3b79d7c25f..e0276520171b 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -20,6 +20,7 @@
#include <inttypes.h>
#include <string.h>
#include <unistd.h>
+#include <endian.h>
#include <fcntl.h>
#include <errno.h>
#include <asm/unistd.h>
@@ -32,9 +33,13 @@
#include <linux/limits.h>
#include <linux/perf_event.h>
#include <linux/ring_buffer.h>
+#include <sys/epoll.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/vfs.h>
+#include <sys/utsname.h>
#include <tools/libc_compat.h>
#include <libelf.h>
#include <gelf.h>
@@ -43,8 +48,8 @@
#include "bpf.h"
#include "btf.h"
#include "str_error.h"
-#include "libbpf_util.h"
#include "libbpf_internal.h"
+#include "hashmap.h"
#ifndef EM_BPF
#define EM_BPF 247
@@ -72,9 +77,12 @@ static int __base_pr(enum libbpf_print_level level, const char *format,
static libbpf_print_fn_t __libbpf_pr = __base_pr;
-void libbpf_set_print(libbpf_print_fn_t fn)
+libbpf_print_fn_t libbpf_set_print(libbpf_print_fn_t fn)
{
+ libbpf_print_fn_t old_print_fn = __libbpf_pr;
+
__libbpf_pr = fn;
+ return old_print_fn;
}
__printf(2, 3)
@@ -179,7 +187,6 @@ struct bpf_program {
bpf_program_clear_priv_t clear_priv;
enum bpf_attach_type expected_attach_type;
- int btf_fd;
void *func_info;
__u32 func_info_rec_size;
__u32 func_info_cnt;
@@ -189,6 +196,7 @@ struct bpf_program {
void *line_info;
__u32 line_info_rec_size;
__u32 line_info_cnt;
+ __u32 prog_flags;
};
enum libbpf_map_type {
@@ -207,7 +215,8 @@ static const char * const libbpf_type_to_btf_name[] = {
struct bpf_map {
int fd;
char *name;
- size_t offset;
+ int sec_idx;
+ size_t sec_offset;
int map_ifindex;
int inner_map_fd;
struct bpf_map_def def;
@@ -234,6 +243,7 @@ struct bpf_object {
size_t nr_programs;
struct bpf_map *maps;
size_t nr_maps;
+ size_t maps_cap;
struct bpf_secdata sections;
bool loaded;
@@ -260,6 +270,7 @@ struct bpf_object {
} *reloc;
int nr_reloc;
int maps_shndx;
+ int btf_maps_shndx;
int text_shndx;
int data_shndx;
int rodata_shndx;
@@ -306,7 +317,6 @@ void bpf_program__unload(struct bpf_program *prog)
prog->instances.nr = -1;
zfree(&prog->instances.fds);
- zclose(prog->btf_fd);
zfree(&prog->func_info);
zfree(&prog->line_info);
}
@@ -349,8 +359,11 @@ static int
bpf_program__init(void *data, size_t size, char *section_name, int idx,
struct bpf_program *prog)
{
- if (size < sizeof(struct bpf_insn)) {
- pr_warning("corrupted section '%s'\n", section_name);
+ const size_t bpf_insn_sz = sizeof(struct bpf_insn);
+
+ if (size == 0 || size % bpf_insn_sz) {
+ pr_warning("corrupted section '%s', size: %zu\n",
+ section_name, size);
return -EINVAL;
}
@@ -376,14 +389,12 @@ bpf_program__init(void *data, size_t size, char *section_name, int idx,
section_name);
goto errout;
}
- prog->insns_cnt = size / sizeof(struct bpf_insn);
- memcpy(prog->insns, data,
- prog->insns_cnt * sizeof(struct bpf_insn));
+ prog->insns_cnt = size / bpf_insn_sz;
+ memcpy(prog->insns, data, size);
prog->idx = idx;
prog->instances.fds = NULL;
prog->instances.nr = -1;
prog->type = BPF_PROG_TYPE_UNSPEC;
- prog->btf_fd = -1;
return 0;
errout:
@@ -495,15 +506,14 @@ static struct bpf_object *bpf_object__new(const char *path,
strcpy(obj->path, path);
/* Using basename() GNU version which doesn't modify arg. */
- strncpy(obj->name, basename((void *)path),
- sizeof(obj->name) - 1);
+ strncpy(obj->name, basename((void *)path), sizeof(obj->name) - 1);
end = strchr(obj->name, '.');
if (end)
*end = 0;
obj->efile.fd = -1;
/*
- * Caller of this function should also calls
+ * Caller of this function should also call
* bpf_object__elf_finish() after data collection to return
* obj_buf to user. If not, we should duplicate the buffer to
* avoid user freeing them before elf finish.
@@ -511,6 +521,7 @@ static struct bpf_object *bpf_object__new(const char *path,
obj->efile.obj_buf = obj_buf;
obj->efile.obj_buf_sz = obj_buf_sz;
obj->efile.maps_shndx = -1;
+ obj->efile.btf_maps_shndx = -1;
obj->efile.data_shndx = -1;
obj->efile.rodata_shndx = -1;
obj->efile.bss_shndx = -1;
@@ -563,38 +574,35 @@ static int bpf_object__elf_init(struct bpf_object *obj)
} else {
obj->efile.fd = open(obj->path, O_RDONLY);
if (obj->efile.fd < 0) {
- char errmsg[STRERR_BUFSIZE];
- char *cp = libbpf_strerror_r(errno, errmsg,
- sizeof(errmsg));
+ char errmsg[STRERR_BUFSIZE], *cp;
+ err = -errno;
+ cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
pr_warning("failed to open %s: %s\n", obj->path, cp);
- return -errno;
+ return err;
}
obj->efile.elf = elf_begin(obj->efile.fd,
- LIBBPF_ELF_C_READ_MMAP,
- NULL);
+ LIBBPF_ELF_C_READ_MMAP, NULL);
}
if (!obj->efile.elf) {
- pr_warning("failed to open %s as ELF file\n",
- obj->path);
+ pr_warning("failed to open %s as ELF file\n", obj->path);
err = -LIBBPF_ERRNO__LIBELF;
goto errout;
}
if (!gelf_getehdr(obj->efile.elf, &obj->efile.ehdr)) {
- pr_warning("failed to get EHDR from %s\n",
- obj->path);
+ pr_warning("failed to get EHDR from %s\n", obj->path);
err = -LIBBPF_ERRNO__FORMAT;
goto errout;
}
ep = &obj->efile.ehdr;
/* Old LLVM set e_machine to EM_NONE */
- if ((ep->e_type != ET_REL) || (ep->e_machine && (ep->e_machine != EM_BPF))) {
- pr_warning("%s is not an eBPF object file\n",
- obj->path);
+ if (ep->e_type != ET_REL ||
+ (ep->e_machine && ep->e_machine != EM_BPF)) {
+ pr_warning("%s is not an eBPF object file\n", obj->path);
err = -LIBBPF_ERRNO__FORMAT;
goto errout;
}
@@ -605,47 +613,31 @@ errout:
return err;
}
-static int
-bpf_object__check_endianness(struct bpf_object *obj)
+static int bpf_object__check_endianness(struct bpf_object *obj)
{
- static unsigned int const endian = 1;
-
- switch (obj->efile.ehdr.e_ident[EI_DATA]) {
- case ELFDATA2LSB:
- /* We are big endian, BPF obj is little endian. */
- if (*(unsigned char const *)&endian != 1)
- goto mismatch;
- break;
-
- case ELFDATA2MSB:
- /* We are little endian, BPF obj is big endian. */
- if (*(unsigned char const *)&endian != 0)
- goto mismatch;
- break;
- default:
- return -LIBBPF_ERRNO__ENDIAN;
- }
-
- return 0;
-
-mismatch:
- pr_warning("Error: endianness mismatch.\n");
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ if (obj->efile.ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+ return 0;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ if (obj->efile.ehdr.e_ident[EI_DATA] == ELFDATA2MSB)
+ return 0;
+#else
+# error "Unrecognized __BYTE_ORDER__"
+#endif
+ pr_warning("endianness mismatch.\n");
return -LIBBPF_ERRNO__ENDIAN;
}
static int
-bpf_object__init_license(struct bpf_object *obj,
- void *data, size_t size)
+bpf_object__init_license(struct bpf_object *obj, void *data, size_t size)
{
- memcpy(obj->license, data,
- min(size, sizeof(obj->license) - 1));
+ memcpy(obj->license, data, min(size, sizeof(obj->license) - 1));
pr_debug("license of %s is %s\n", obj->path, obj->license);
return 0;
}
static int
-bpf_object__init_kversion(struct bpf_object *obj,
- void *data, size_t size)
+bpf_object__init_kversion(struct bpf_object *obj, void *data, size_t size)
{
__u32 kver;
@@ -655,8 +647,7 @@ bpf_object__init_kversion(struct bpf_object *obj,
}
memcpy(&kver, data, sizeof(kver));
obj->kern_version = kver;
- pr_debug("kernel version of %s is %x\n", obj->path,
- obj->kern_version);
+ pr_debug("kernel version of %s is %x\n", obj->path, obj->kern_version);
return 0;
}
@@ -665,7 +656,9 @@ static int compare_bpf_map(const void *_a, const void *_b)
const struct bpf_map *a = _a;
const struct bpf_map *b = _b;
- return a->offset - b->offset;
+ if (a->sec_idx != b->sec_idx)
+ return a->sec_idx - b->sec_idx;
+ return a->sec_offset - b->sec_offset;
}
static bool bpf_map_type__is_map_in_map(enum bpf_map_type type)
@@ -782,24 +775,55 @@ int bpf_object__variable_offset(const struct bpf_object *obj, const char *name,
return -ENOENT;
}
-static bool bpf_object__has_maps(const struct bpf_object *obj)
+static struct bpf_map *bpf_object__add_map(struct bpf_object *obj)
{
- return obj->efile.maps_shndx >= 0 ||
- obj->efile.data_shndx >= 0 ||
- obj->efile.rodata_shndx >= 0 ||
- obj->efile.bss_shndx >= 0;
+ struct bpf_map *new_maps;
+ size_t new_cap;
+ int i;
+
+ if (obj->nr_maps < obj->maps_cap)
+ return &obj->maps[obj->nr_maps++];
+
+ new_cap = max((size_t)4, obj->maps_cap * 3 / 2);
+ new_maps = realloc(obj->maps, new_cap * sizeof(*obj->maps));
+ if (!new_maps) {
+ pr_warning("alloc maps for object failed\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ obj->maps_cap = new_cap;
+ obj->maps = new_maps;
+
+ /* zero out new maps */
+ memset(obj->maps + obj->nr_maps, 0,
+ (obj->maps_cap - obj->nr_maps) * sizeof(*obj->maps));
+ /*
+ * fill all fd with -1 so won't close incorrect fd (fd=0 is stdin)
+ * when failure (zclose won't close negative fd)).
+ */
+ for (i = obj->nr_maps; i < obj->maps_cap; i++) {
+ obj->maps[i].fd = -1;
+ obj->maps[i].inner_map_fd = -1;
+ }
+
+ return &obj->maps[obj->nr_maps++];
}
static int
-bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map,
- enum libbpf_map_type type, Elf_Data *data,
- void **data_buff)
+bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
+ int sec_idx, Elf_Data *data, void **data_buff)
{
- struct bpf_map_def *def = &map->def;
char map_name[BPF_OBJ_NAME_LEN];
+ struct bpf_map_def *def;
+ struct bpf_map *map;
+
+ map = bpf_object__add_map(obj);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
map->libbpf_type = type;
- map->offset = ~(typeof(map->offset))0;
+ map->sec_idx = sec_idx;
+ map->sec_offset = 0;
snprintf(map_name, sizeof(map_name), "%.8s%.7s", obj->name,
libbpf_type_to_btf_name[type]);
map->name = strdup(map_name);
@@ -807,13 +831,15 @@ bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map,
pr_warning("failed to alloc map name\n");
return -ENOMEM;
}
+ pr_debug("map '%s' (global data): at sec_idx %d, offset %zu.\n",
+ map_name, map->sec_idx, map->sec_offset);
+ def = &map->def;
def->type = BPF_MAP_TYPE_ARRAY;
def->key_size = sizeof(int);
def->value_size = data->d_size;
def->max_entries = 1;
- def->map_flags = type == LIBBPF_MAP_RODATA ?
- BPF_F_RDONLY_PROG : 0;
+ def->map_flags = type == LIBBPF_MAP_RODATA ? BPF_F_RDONLY_PROG : 0;
if (data_buff) {
*data_buff = malloc(data->d_size);
if (!*data_buff) {
@@ -828,30 +854,61 @@ bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map,
return 0;
}
-static int
-bpf_object__init_maps(struct bpf_object *obj, int flags)
+static int bpf_object__init_global_data_maps(struct bpf_object *obj)
+{
+ int err;
+
+ if (!obj->caps.global_data)
+ return 0;
+ /*
+ * Populate obj->maps with libbpf internal maps.
+ */
+ if (obj->efile.data_shndx >= 0) {
+ err = bpf_object__init_internal_map(obj, LIBBPF_MAP_DATA,
+ obj->efile.data_shndx,
+ obj->efile.data,
+ &obj->sections.data);
+ if (err)
+ return err;
+ }
+ if (obj->efile.rodata_shndx >= 0) {
+ err = bpf_object__init_internal_map(obj, LIBBPF_MAP_RODATA,
+ obj->efile.rodata_shndx,
+ obj->efile.rodata,
+ &obj->sections.rodata);
+ if (err)
+ return err;
+ }
+ if (obj->efile.bss_shndx >= 0) {
+ err = bpf_object__init_internal_map(obj, LIBBPF_MAP_BSS,
+ obj->efile.bss_shndx,
+ obj->efile.bss, NULL);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict)
{
- int i, map_idx, map_def_sz = 0, nr_syms, nr_maps = 0, nr_maps_glob = 0;
- bool strict = !(flags & MAPS_RELAX_COMPAT);
Elf_Data *symbols = obj->efile.symbols;
+ int i, map_def_sz = 0, nr_maps = 0, nr_syms;
Elf_Data *data = NULL;
- int ret = 0;
+ Elf_Scn *scn;
+
+ if (obj->efile.maps_shndx < 0)
+ return 0;
if (!symbols)
return -EINVAL;
- nr_syms = symbols->d_size / sizeof(GElf_Sym);
-
- if (obj->efile.maps_shndx >= 0) {
- Elf_Scn *scn = elf_getscn(obj->efile.elf,
- obj->efile.maps_shndx);
- if (scn)
- data = elf_getdata(scn, NULL);
- if (!scn || !data) {
- pr_warning("failed to get Elf_Data from map section %d\n",
- obj->efile.maps_shndx);
- return -EINVAL;
- }
+ scn = elf_getscn(obj->efile.elf, obj->efile.maps_shndx);
+ if (scn)
+ data = elf_getdata(scn, NULL);
+ if (!scn || !data) {
+ pr_warning("failed to get Elf_Data from map section %d\n",
+ obj->efile.maps_shndx);
+ return -EINVAL;
}
/*
@@ -861,16 +918,8 @@ bpf_object__init_maps(struct bpf_object *obj, int flags)
*
* TODO: Detect array of map and report error.
*/
- if (obj->caps.global_data) {
- if (obj->efile.data_shndx >= 0)
- nr_maps_glob++;
- if (obj->efile.rodata_shndx >= 0)
- nr_maps_glob++;
- if (obj->efile.bss_shndx >= 0)
- nr_maps_glob++;
- }
-
- for (i = 0; data && i < nr_syms; i++) {
+ nr_syms = symbols->d_size / sizeof(GElf_Sym);
+ for (i = 0; i < nr_syms; i++) {
GElf_Sym sym;
if (!gelf_getsym(symbols, i, &sym))
@@ -879,74 +928,59 @@ bpf_object__init_maps(struct bpf_object *obj, int flags)
continue;
nr_maps++;
}
-
- if (!nr_maps && !nr_maps_glob)
- return 0;
-
/* Assume equally sized map definitions */
- if (data) {
- pr_debug("maps in %s: %d maps in %zd bytes\n", obj->path,
- nr_maps, data->d_size);
-
- map_def_sz = data->d_size / nr_maps;
- if (!data->d_size || (data->d_size % nr_maps) != 0) {
- pr_warning("unable to determine map definition size "
- "section %s, %d maps in %zd bytes\n",
- obj->path, nr_maps, data->d_size);
- return -EINVAL;
- }
- }
-
- nr_maps += nr_maps_glob;
- obj->maps = calloc(nr_maps, sizeof(obj->maps[0]));
- if (!obj->maps) {
- pr_warning("alloc maps for object failed\n");
- return -ENOMEM;
- }
- obj->nr_maps = nr_maps;
-
- for (i = 0; i < nr_maps; i++) {
- /*
- * fill all fd with -1 so won't close incorrect
- * fd (fd=0 is stdin) when failure (zclose won't close
- * negative fd)).
- */
- obj->maps[i].fd = -1;
- obj->maps[i].inner_map_fd = -1;
+ pr_debug("maps in %s: %d maps in %zd bytes\n",
+ obj->path, nr_maps, data->d_size);
+
+ map_def_sz = data->d_size / nr_maps;
+ if (!data->d_size || (data->d_size % nr_maps) != 0) {
+ pr_warning("unable to determine map definition size "
+ "section %s, %d maps in %zd bytes\n",
+ obj->path, nr_maps, data->d_size);
+ return -EINVAL;
}
- /*
- * Fill obj->maps using data in "maps" section.
- */
- for (i = 0, map_idx = 0; data && i < nr_syms; i++) {
+ /* Fill obj->maps using data in "maps" section. */
+ for (i = 0; i < nr_syms; i++) {
GElf_Sym sym;
const char *map_name;
struct bpf_map_def *def;
+ struct bpf_map *map;
if (!gelf_getsym(symbols, i, &sym))
continue;
if (sym.st_shndx != obj->efile.maps_shndx)
continue;
- map_name = elf_strptr(obj->efile.elf,
- obj->efile.strtabidx,
+ map = bpf_object__add_map(obj);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+
+ map_name = elf_strptr(obj->efile.elf, obj->efile.strtabidx,
sym.st_name);
+ if (!map_name) {
+ pr_warning("failed to get map #%d name sym string for obj %s\n",
+ i, obj->path);
+ return -LIBBPF_ERRNO__FORMAT;
+ }
- obj->maps[map_idx].libbpf_type = LIBBPF_MAP_UNSPEC;
- obj->maps[map_idx].offset = sym.st_value;
+ map->libbpf_type = LIBBPF_MAP_UNSPEC;
+ map->sec_idx = sym.st_shndx;
+ map->sec_offset = sym.st_value;
+ pr_debug("map '%s' (legacy): at sec_idx %d, offset %zu.\n",
+ map_name, map->sec_idx, map->sec_offset);
if (sym.st_value + map_def_sz > data->d_size) {
pr_warning("corrupted maps section in %s: last map \"%s\" too small\n",
obj->path, map_name);
return -EINVAL;
}
- obj->maps[map_idx].name = strdup(map_name);
- if (!obj->maps[map_idx].name) {
+ map->name = strdup(map_name);
+ if (!map->name) {
pr_warning("failed to alloc map name\n");
return -ENOMEM;
}
- pr_debug("map %d is \"%s\"\n", map_idx,
- obj->maps[map_idx].name);
+ pr_debug("map %d is \"%s\"\n", i, map->name);
def = (struct bpf_map_def *)(data->d_buf + sym.st_value);
/*
* If the definition of the map in the object file fits in
@@ -955,7 +989,7 @@ bpf_object__init_maps(struct bpf_object *obj, int flags)
* calloc above.
*/
if (map_def_sz <= sizeof(struct bpf_map_def)) {
- memcpy(&obj->maps[map_idx].def, def, map_def_sz);
+ memcpy(&map->def, def, map_def_sz);
} else {
/*
* Here the map structure being read is bigger than what
@@ -975,37 +1009,336 @@ bpf_object__init_maps(struct bpf_object *obj, int flags)
return -EINVAL;
}
}
- memcpy(&obj->maps[map_idx].def, def,
- sizeof(struct bpf_map_def));
+ memcpy(&map->def, def, sizeof(struct bpf_map_def));
}
- map_idx++;
}
+ return 0;
+}
- if (!obj->caps.global_data)
- goto finalize;
+static const struct btf_type *
+skip_mods_and_typedefs(const struct btf *btf, __u32 id, __u32 *res_id)
+{
+ const struct btf_type *t = btf__type_by_id(btf, id);
- /*
- * Populate rest of obj->maps with libbpf internal maps.
- */
- if (obj->efile.data_shndx >= 0)
- ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++],
- LIBBPF_MAP_DATA,
- obj->efile.data,
- &obj->sections.data);
- if (!ret && obj->efile.rodata_shndx >= 0)
- ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++],
- LIBBPF_MAP_RODATA,
- obj->efile.rodata,
- &obj->sections.rodata);
- if (!ret && obj->efile.bss_shndx >= 0)
- ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++],
- LIBBPF_MAP_BSS,
- obj->efile.bss, NULL);
-finalize:
- if (!ret)
+ if (res_id)
+ *res_id = id;
+
+ while (btf_is_mod(t) || btf_is_typedef(t)) {
+ if (res_id)
+ *res_id = t->type;
+ t = btf__type_by_id(btf, t->type);
+ }
+
+ return t;
+}
+
+/*
+ * Fetch integer attribute of BTF map definition. Such attributes are
+ * represented using a pointer to an array, in which dimensionality of array
+ * encodes specified integer value. E.g., int (*type)[BPF_MAP_TYPE_ARRAY];
+ * encodes `type => BPF_MAP_TYPE_ARRAY` key/value pair completely using BTF
+ * type definition, while using only sizeof(void *) space in ELF data section.
+ */
+static bool get_map_field_int(const char *map_name, const struct btf *btf,
+ const struct btf_type *def,
+ const struct btf_member *m, __u32 *res) {
+ const struct btf_type *t = skip_mods_and_typedefs(btf, m->type, NULL);
+ const char *name = btf__name_by_offset(btf, m->name_off);
+ const struct btf_array *arr_info;
+ const struct btf_type *arr_t;
+
+ if (!btf_is_ptr(t)) {
+ pr_warning("map '%s': attr '%s': expected PTR, got %u.\n",
+ map_name, name, btf_kind(t));
+ return false;
+ }
+
+ arr_t = btf__type_by_id(btf, t->type);
+ if (!arr_t) {
+ pr_warning("map '%s': attr '%s': type [%u] not found.\n",
+ map_name, name, t->type);
+ return false;
+ }
+ if (!btf_is_array(arr_t)) {
+ pr_warning("map '%s': attr '%s': expected ARRAY, got %u.\n",
+ map_name, name, btf_kind(arr_t));
+ return false;
+ }
+ arr_info = btf_array(arr_t);
+ *res = arr_info->nelems;
+ return true;
+}
+
+static int bpf_object__init_user_btf_map(struct bpf_object *obj,
+ const struct btf_type *sec,
+ int var_idx, int sec_idx,
+ const Elf_Data *data, bool strict)
+{
+ const struct btf_type *var, *def, *t;
+ const struct btf_var_secinfo *vi;
+ const struct btf_var *var_extra;
+ const struct btf_member *m;
+ const char *map_name;
+ struct bpf_map *map;
+ int vlen, i;
+
+ vi = btf_var_secinfos(sec) + var_idx;
+ var = btf__type_by_id(obj->btf, vi->type);
+ var_extra = btf_var(var);
+ map_name = btf__name_by_offset(obj->btf, var->name_off);
+ vlen = btf_vlen(var);
+
+ if (map_name == NULL || map_name[0] == '\0') {
+ pr_warning("map #%d: empty name.\n", var_idx);
+ return -EINVAL;
+ }
+ if ((__u64)vi->offset + vi->size > data->d_size) {
+ pr_warning("map '%s' BTF data is corrupted.\n", map_name);
+ return -EINVAL;
+ }
+ if (!btf_is_var(var)) {
+ pr_warning("map '%s': unexpected var kind %u.\n",
+ map_name, btf_kind(var));
+ return -EINVAL;
+ }
+ if (var_extra->linkage != BTF_VAR_GLOBAL_ALLOCATED &&
+ var_extra->linkage != BTF_VAR_STATIC) {
+ pr_warning("map '%s': unsupported var linkage %u.\n",
+ map_name, var_extra->linkage);
+ return -EOPNOTSUPP;
+ }
+
+ def = skip_mods_and_typedefs(obj->btf, var->type, NULL);
+ if (!btf_is_struct(def)) {
+ pr_warning("map '%s': unexpected def kind %u.\n",
+ map_name, btf_kind(var));
+ return -EINVAL;
+ }
+ if (def->size > vi->size) {
+ pr_warning("map '%s': invalid def size.\n", map_name);
+ return -EINVAL;
+ }
+
+ map = bpf_object__add_map(obj);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+ map->name = strdup(map_name);
+ if (!map->name) {
+ pr_warning("map '%s': failed to alloc map name.\n", map_name);
+ return -ENOMEM;
+ }
+ map->libbpf_type = LIBBPF_MAP_UNSPEC;
+ map->def.type = BPF_MAP_TYPE_UNSPEC;
+ map->sec_idx = sec_idx;
+ map->sec_offset = vi->offset;
+ pr_debug("map '%s': at sec_idx %d, offset %zu.\n",
+ map_name, map->sec_idx, map->sec_offset);
+
+ vlen = btf_vlen(def);
+ m = btf_members(def);
+ for (i = 0; i < vlen; i++, m++) {
+ const char *name = btf__name_by_offset(obj->btf, m->name_off);
+
+ if (!name) {
+ pr_warning("map '%s': invalid field #%d.\n",
+ map_name, i);
+ return -EINVAL;
+ }
+ if (strcmp(name, "type") == 0) {
+ if (!get_map_field_int(map_name, obj->btf, def, m,
+ &map->def.type))
+ return -EINVAL;
+ pr_debug("map '%s': found type = %u.\n",
+ map_name, map->def.type);
+ } else if (strcmp(name, "max_entries") == 0) {
+ if (!get_map_field_int(map_name, obj->btf, def, m,
+ &map->def.max_entries))
+ return -EINVAL;
+ pr_debug("map '%s': found max_entries = %u.\n",
+ map_name, map->def.max_entries);
+ } else if (strcmp(name, "map_flags") == 0) {
+ if (!get_map_field_int(map_name, obj->btf, def, m,
+ &map->def.map_flags))
+ return -EINVAL;
+ pr_debug("map '%s': found map_flags = %u.\n",
+ map_name, map->def.map_flags);
+ } else if (strcmp(name, "key_size") == 0) {
+ __u32 sz;
+
+ if (!get_map_field_int(map_name, obj->btf, def, m,
+ &sz))
+ return -EINVAL;
+ pr_debug("map '%s': found key_size = %u.\n",
+ map_name, sz);
+ if (map->def.key_size && map->def.key_size != sz) {
+ pr_warning("map '%s': conflicting key size %u != %u.\n",
+ map_name, map->def.key_size, sz);
+ return -EINVAL;
+ }
+ map->def.key_size = sz;
+ } else if (strcmp(name, "key") == 0) {
+ __s64 sz;
+
+ t = btf__type_by_id(obj->btf, m->type);
+ if (!t) {
+ pr_warning("map '%s': key type [%d] not found.\n",
+ map_name, m->type);
+ return -EINVAL;
+ }
+ if (!btf_is_ptr(t)) {
+ pr_warning("map '%s': key spec is not PTR: %u.\n",
+ map_name, btf_kind(t));
+ return -EINVAL;
+ }
+ sz = btf__resolve_size(obj->btf, t->type);
+ if (sz < 0) {
+ pr_warning("map '%s': can't determine key size for type [%u]: %lld.\n",
+ map_name, t->type, sz);
+ return sz;
+ }
+ pr_debug("map '%s': found key [%u], sz = %lld.\n",
+ map_name, t->type, sz);
+ if (map->def.key_size && map->def.key_size != sz) {
+ pr_warning("map '%s': conflicting key size %u != %lld.\n",
+ map_name, map->def.key_size, sz);
+ return -EINVAL;
+ }
+ map->def.key_size = sz;
+ map->btf_key_type_id = t->type;
+ } else if (strcmp(name, "value_size") == 0) {
+ __u32 sz;
+
+ if (!get_map_field_int(map_name, obj->btf, def, m,
+ &sz))
+ return -EINVAL;
+ pr_debug("map '%s': found value_size = %u.\n",
+ map_name, sz);
+ if (map->def.value_size && map->def.value_size != sz) {
+ pr_warning("map '%s': conflicting value size %u != %u.\n",
+ map_name, map->def.value_size, sz);
+ return -EINVAL;
+ }
+ map->def.value_size = sz;
+ } else if (strcmp(name, "value") == 0) {
+ __s64 sz;
+
+ t = btf__type_by_id(obj->btf, m->type);
+ if (!t) {
+ pr_warning("map '%s': value type [%d] not found.\n",
+ map_name, m->type);
+ return -EINVAL;
+ }
+ if (!btf_is_ptr(t)) {
+ pr_warning("map '%s': value spec is not PTR: %u.\n",
+ map_name, btf_kind(t));
+ return -EINVAL;
+ }
+ sz = btf__resolve_size(obj->btf, t->type);
+ if (sz < 0) {
+ pr_warning("map '%s': can't determine value size for type [%u]: %lld.\n",
+ map_name, t->type, sz);
+ return sz;
+ }
+ pr_debug("map '%s': found value [%u], sz = %lld.\n",
+ map_name, t->type, sz);
+ if (map->def.value_size && map->def.value_size != sz) {
+ pr_warning("map '%s': conflicting value size %u != %lld.\n",
+ map_name, map->def.value_size, sz);
+ return -EINVAL;
+ }
+ map->def.value_size = sz;
+ map->btf_value_type_id = t->type;
+ } else {
+ if (strict) {
+ pr_warning("map '%s': unknown field '%s'.\n",
+ map_name, name);
+ return -ENOTSUP;
+ }
+ pr_debug("map '%s': ignoring unknown field '%s'.\n",
+ map_name, name);
+ }
+ }
+
+ if (map->def.type == BPF_MAP_TYPE_UNSPEC) {
+ pr_warning("map '%s': map type isn't specified.\n", map_name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict)
+{
+ const struct btf_type *sec = NULL;
+ int nr_types, i, vlen, err;
+ const struct btf_type *t;
+ const char *name;
+ Elf_Data *data;
+ Elf_Scn *scn;
+
+ if (obj->efile.btf_maps_shndx < 0)
+ return 0;
+
+ scn = elf_getscn(obj->efile.elf, obj->efile.btf_maps_shndx);
+ if (scn)
+ data = elf_getdata(scn, NULL);
+ if (!scn || !data) {
+ pr_warning("failed to get Elf_Data from map section %d (%s)\n",
+ obj->efile.maps_shndx, MAPS_ELF_SEC);
+ return -EINVAL;
+ }
+
+ nr_types = btf__get_nr_types(obj->btf);
+ for (i = 1; i <= nr_types; i++) {
+ t = btf__type_by_id(obj->btf, i);
+ if (!btf_is_datasec(t))
+ continue;
+ name = btf__name_by_offset(obj->btf, t->name_off);
+ if (strcmp(name, MAPS_ELF_SEC) == 0) {
+ sec = t;
+ break;
+ }
+ }
+
+ if (!sec) {
+ pr_warning("DATASEC '%s' not found.\n", MAPS_ELF_SEC);
+ return -ENOENT;
+ }
+
+ vlen = btf_vlen(sec);
+ for (i = 0; i < vlen; i++) {
+ err = bpf_object__init_user_btf_map(obj, sec, i,
+ obj->efile.btf_maps_shndx,
+ data, strict);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int bpf_object__init_maps(struct bpf_object *obj, int flags)
+{
+ bool strict = !(flags & MAPS_RELAX_COMPAT);
+ int err;
+
+ err = bpf_object__init_user_maps(obj, strict);
+ if (err)
+ return err;
+
+ err = bpf_object__init_user_btf_maps(obj, strict);
+ if (err)
+ return err;
+
+ err = bpf_object__init_global_data_maps(obj);
+ if (err)
+ return err;
+
+ if (obj->nr_maps) {
qsort(obj->maps, obj->nr_maps, sizeof(obj->maps[0]),
compare_bpf_map);
- return ret;
+ }
+ return 0;
}
static bool section_have_execinstr(struct bpf_object *obj, int idx)
@@ -1033,24 +1366,27 @@ static void bpf_object__sanitize_btf(struct bpf_object *obj)
struct btf *btf = obj->btf;
struct btf_type *t;
int i, j, vlen;
- __u16 kind;
if (!obj->btf || (has_func && has_datasec))
return;
for (i = 1; i <= btf__get_nr_types(btf); i++) {
t = (struct btf_type *)btf__type_by_id(btf, i);
- kind = BTF_INFO_KIND(t->info);
- if (!has_datasec && kind == BTF_KIND_VAR) {
+ if (!has_datasec && btf_is_var(t)) {
/* replace VAR with INT */
t->info = BTF_INFO_ENC(BTF_KIND_INT, 0, 0);
- t->size = sizeof(int);
- *(int *)(t+1) = BTF_INT_ENC(0, 0, 32);
- } else if (!has_datasec && kind == BTF_KIND_DATASEC) {
+ /*
+ * using size = 1 is the safest choice, 4 will be too
+ * big and cause kernel BTF validation failure if
+ * original variable took less than 4 bytes
+ */
+ t->size = 1;
+ *(int *)(t + 1) = BTF_INT_ENC(0, 0, 8);
+ } else if (!has_datasec && btf_is_datasec(t)) {
/* replace DATASEC with STRUCT */
- struct btf_var_secinfo *v = (void *)(t + 1);
- struct btf_member *m = (void *)(t + 1);
+ const struct btf_var_secinfo *v = btf_var_secinfos(t);
+ struct btf_member *m = btf_members(t);
struct btf_type *vt;
char *name;
@@ -1061,7 +1397,7 @@ static void bpf_object__sanitize_btf(struct bpf_object *obj)
name++;
}
- vlen = BTF_INFO_VLEN(t->info);
+ vlen = btf_vlen(t);
t->info = BTF_INFO_ENC(BTF_KIND_STRUCT, 0, vlen);
for (j = 0; j < vlen; j++, v++, m++) {
/* order of field assignments is important */
@@ -1071,12 +1407,12 @@ static void bpf_object__sanitize_btf(struct bpf_object *obj)
vt = (void *)btf__type_by_id(btf, v->type);
m->name_off = vt->name_off;
}
- } else if (!has_func && kind == BTF_KIND_FUNC_PROTO) {
+ } else if (!has_func && btf_is_func_proto(t)) {
/* replace FUNC_PROTO with ENUM */
- vlen = BTF_INFO_VLEN(t->info);
+ vlen = btf_vlen(t);
t->info = BTF_INFO_ENC(BTF_KIND_ENUM, 0, vlen);
t->size = sizeof(__u32); /* kernel enforced */
- } else if (!has_func && kind == BTF_KIND_FUNC) {
+ } else if (!has_func && btf_is_func(t)) {
/* replace FUNC with TYPEDEF */
t->info = BTF_INFO_ENC(BTF_KIND_TYPEDEF, 0, 0);
}
@@ -1094,6 +1430,92 @@ static void bpf_object__sanitize_btf_ext(struct bpf_object *obj)
}
}
+static bool bpf_object__is_btf_mandatory(const struct bpf_object *obj)
+{
+ return obj->efile.btf_maps_shndx >= 0;
+}
+
+static int bpf_object__init_btf(struct bpf_object *obj,
+ Elf_Data *btf_data,
+ Elf_Data *btf_ext_data)
+{
+ bool btf_required = bpf_object__is_btf_mandatory(obj);
+ int err = 0;
+
+ if (btf_data) {
+ obj->btf = btf__new(btf_data->d_buf, btf_data->d_size);
+ if (IS_ERR(obj->btf)) {
+ pr_warning("Error loading ELF section %s: %d.\n",
+ BTF_ELF_SEC, err);
+ goto out;
+ }
+ err = btf__finalize_data(obj, obj->btf);
+ if (err) {
+ pr_warning("Error finalizing %s: %d.\n",
+ BTF_ELF_SEC, err);
+ goto out;
+ }
+ }
+ if (btf_ext_data) {
+ if (!obj->btf) {
+ pr_debug("Ignore ELF section %s because its depending ELF section %s is not found.\n",
+ BTF_EXT_ELF_SEC, BTF_ELF_SEC);
+ goto out;
+ }
+ obj->btf_ext = btf_ext__new(btf_ext_data->d_buf,
+ btf_ext_data->d_size);
+ if (IS_ERR(obj->btf_ext)) {
+ pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
+ BTF_EXT_ELF_SEC, PTR_ERR(obj->btf_ext));
+ obj->btf_ext = NULL;
+ goto out;
+ }
+ }
+out:
+ if (err || IS_ERR(obj->btf)) {
+ if (btf_required)
+ err = err ? : PTR_ERR(obj->btf);
+ else
+ err = 0;
+ if (!IS_ERR_OR_NULL(obj->btf))
+ btf__free(obj->btf);
+ obj->btf = NULL;
+ }
+ if (btf_required && !obj->btf) {
+ pr_warning("BTF is required, but is missing or corrupted.\n");
+ return err == 0 ? -ENOENT : err;
+ }
+ return 0;
+}
+
+static int bpf_object__sanitize_and_load_btf(struct bpf_object *obj)
+{
+ int err = 0;
+
+ if (!obj->btf)
+ return 0;
+
+ bpf_object__sanitize_btf(obj);
+ bpf_object__sanitize_btf_ext(obj);
+
+ err = btf__load(obj->btf);
+ if (err) {
+ pr_warning("Error loading %s into kernel: %d.\n",
+ BTF_ELF_SEC, err);
+ btf__free(obj->btf);
+ obj->btf = NULL;
+ /* btf_ext can't exist without btf, so free it as well */
+ if (obj->btf_ext) {
+ btf_ext__free(obj->btf_ext);
+ obj->btf_ext = NULL;
+ }
+
+ if (bpf_object__is_btf_mandatory(obj))
+ return err;
+ }
+ return 0;
+}
+
static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
{
Elf *elf = obj->efile.elf;
@@ -1105,8 +1527,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
/* Elf is corrupted/truncated, avoid calling elf_strptr. */
if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) {
- pr_warning("failed to get e_shstrndx from %s\n",
- obj->path);
+ pr_warning("failed to get e_shstrndx from %s\n", obj->path);
return -LIBBPF_ERRNO__FORMAT;
}
@@ -1119,24 +1540,21 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
if (gelf_getshdr(scn, &sh) != &sh) {
pr_warning("failed to get section(%d) header from %s\n",
idx, obj->path);
- err = -LIBBPF_ERRNO__FORMAT;
- goto out;
+ return -LIBBPF_ERRNO__FORMAT;
}
name = elf_strptr(elf, ep->e_shstrndx, sh.sh_name);
if (!name) {
pr_warning("failed to get section(%d) name from %s\n",
idx, obj->path);
- err = -LIBBPF_ERRNO__FORMAT;
- goto out;
+ return -LIBBPF_ERRNO__FORMAT;
}
data = elf_getdata(scn, 0);
if (!data) {
pr_warning("failed to get section(%d) data from %s(%s)\n",
idx, name, obj->path);
- err = -LIBBPF_ERRNO__FORMAT;
- goto out;
+ return -LIBBPF_ERRNO__FORMAT;
}
pr_debug("section(%d) %s, size %ld, link %d, flags %lx, type=%d\n",
idx, name, (unsigned long)data->d_size,
@@ -1147,12 +1565,18 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
err = bpf_object__init_license(obj,
data->d_buf,
data->d_size);
+ if (err)
+ return err;
} else if (strcmp(name, "version") == 0) {
err = bpf_object__init_kversion(obj,
data->d_buf,
data->d_size);
+ if (err)
+ return err;
} else if (strcmp(name, "maps") == 0) {
obj->efile.maps_shndx = idx;
+ } else if (strcmp(name, MAPS_ELF_SEC) == 0) {
+ obj->efile.btf_maps_shndx = idx;
} else if (strcmp(name, BTF_ELF_SEC) == 0) {
btf_data = data;
} else if (strcmp(name, BTF_EXT_ELF_SEC) == 0) {
@@ -1161,11 +1585,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
if (obj->efile.symbols) {
pr_warning("bpf: multiple SYMTAB in %s\n",
obj->path);
- err = -LIBBPF_ERRNO__FORMAT;
- } else {
- obj->efile.symbols = data;
- obj->efile.strtabidx = sh.sh_link;
+ return -LIBBPF_ERRNO__FORMAT;
}
+ obj->efile.symbols = data;
+ obj->efile.strtabidx = sh.sh_link;
} else if (sh.sh_type == SHT_PROGBITS && data->d_size > 0) {
if (sh.sh_flags & SHF_EXECINSTR) {
if (strcmp(name, ".text") == 0)
@@ -1179,6 +1602,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
pr_warning("failed to alloc program %s (%s): %s",
name, obj->path, cp);
+ return err;
}
} else if (strcmp(name, ".data") == 0) {
obj->efile.data = data;
@@ -1190,8 +1614,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
pr_debug("skip section(%d) %s\n", idx, name);
}
} else if (sh.sh_type == SHT_REL) {
+ int nr_reloc = obj->efile.nr_reloc;
void *reloc = obj->efile.reloc;
- int nr_reloc = obj->efile.nr_reloc + 1;
int sec = sh.sh_info; /* points to other section */
/* Only do relo for section with exec instructions */
@@ -1201,79 +1625,37 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
continue;
}
- reloc = reallocarray(reloc, nr_reloc,
+ reloc = reallocarray(reloc, nr_reloc + 1,
sizeof(*obj->efile.reloc));
if (!reloc) {
pr_warning("realloc failed\n");
- err = -ENOMEM;
- } else {
- int n = nr_reloc - 1;
+ return -ENOMEM;
+ }
- obj->efile.reloc = reloc;
- obj->efile.nr_reloc = nr_reloc;
+ obj->efile.reloc = reloc;
+ obj->efile.nr_reloc++;
- obj->efile.reloc[n].shdr = sh;
- obj->efile.reloc[n].data = data;
- }
+ obj->efile.reloc[nr_reloc].shdr = sh;
+ obj->efile.reloc[nr_reloc].data = data;
} else if (sh.sh_type == SHT_NOBITS && strcmp(name, ".bss") == 0) {
obj->efile.bss = data;
obj->efile.bss_shndx = idx;
} else {
pr_debug("skip section(%d) %s\n", idx, name);
}
- if (err)
- goto out;
}
if (!obj->efile.strtabidx || obj->efile.strtabidx >= idx) {
pr_warning("Corrupted ELF file: index of strtab invalid\n");
- return LIBBPF_ERRNO__FORMAT;
- }
- if (btf_data) {
- obj->btf = btf__new(btf_data->d_buf, btf_data->d_size);
- if (IS_ERR(obj->btf)) {
- pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
- BTF_ELF_SEC, PTR_ERR(obj->btf));
- obj->btf = NULL;
- } else {
- err = btf__finalize_data(obj, obj->btf);
- if (!err) {
- bpf_object__sanitize_btf(obj);
- err = btf__load(obj->btf);
- }
- if (err) {
- pr_warning("Error finalizing and loading %s into kernel: %d. Ignored and continue.\n",
- BTF_ELF_SEC, err);
- btf__free(obj->btf);
- obj->btf = NULL;
- err = 0;
- }
- }
- }
- if (btf_ext_data) {
- if (!obj->btf) {
- pr_debug("Ignore ELF section %s because its depending ELF section %s is not found.\n",
- BTF_EXT_ELF_SEC, BTF_ELF_SEC);
- } else {
- obj->btf_ext = btf_ext__new(btf_ext_data->d_buf,
- btf_ext_data->d_size);
- if (IS_ERR(obj->btf_ext)) {
- pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
- BTF_EXT_ELF_SEC,
- PTR_ERR(obj->btf_ext));
- obj->btf_ext = NULL;
- } else {
- bpf_object__sanitize_btf_ext(obj);
- }
- }
+ return -LIBBPF_ERRNO__FORMAT;
}
- if (bpf_object__has_maps(obj)) {
+ err = bpf_object__init_btf(obj, btf_data, btf_ext_data);
+ if (!err)
err = bpf_object__init_maps(obj, flags);
- if (err)
- goto out;
- }
- err = bpf_object__init_prog_names(obj);
-out:
+ if (!err)
+ err = bpf_object__sanitize_and_load_btf(obj);
+ if (!err)
+ err = bpf_object__init_prog_names(obj);
return err;
}
@@ -1292,7 +1674,8 @@ bpf_object__find_prog_by_idx(struct bpf_object *obj, int idx)
}
struct bpf_program *
-bpf_object__find_program_by_title(struct bpf_object *obj, const char *title)
+bpf_object__find_program_by_title(const struct bpf_object *obj,
+ const char *title)
{
struct bpf_program *pos;
@@ -1314,7 +1697,8 @@ static bool bpf_object__shndx_is_data(const struct bpf_object *obj,
static bool bpf_object__shndx_is_maps(const struct bpf_object *obj,
int shndx)
{
- return shndx == obj->efile.maps_shndx;
+ return shndx == obj->efile.maps_shndx ||
+ shndx == obj->efile.btf_maps_shndx;
}
static bool bpf_object__relo_in_known_section(const struct bpf_object *obj,
@@ -1347,8 +1731,7 @@ bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr,
size_t nr_maps = obj->nr_maps;
int i, nrels;
- pr_debug("collecting relocating info for: '%s'\n",
- prog->section_name);
+ pr_debug("collecting relocating info for: '%s'\n", prog->section_name);
nrels = shdr->sh_size / shdr->sh_entsize;
prog->reloc_desc = malloc(sizeof(*prog->reloc_desc) * nrels);
@@ -1359,23 +1742,21 @@ bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr,
prog->nr_reloc = nrels;
for (i = 0; i < nrels; i++) {
- GElf_Sym sym;
- GElf_Rel rel;
- unsigned int insn_idx;
- unsigned int shdr_idx;
struct bpf_insn *insns = prog->insns;
enum libbpf_map_type type;
+ unsigned int insn_idx;
+ unsigned int shdr_idx;
const char *name;
size_t map_idx;
+ GElf_Sym sym;
+ GElf_Rel rel;
if (!gelf_getrel(data, i, &rel)) {
pr_warning("relocation: failed to get %d reloc\n", i);
return -LIBBPF_ERRNO__FORMAT;
}
- if (!gelf_getsym(symbols,
- GELF_R_SYM(rel.r_info),
- &sym)) {
+ if (!gelf_getsym(symbols, GELF_R_SYM(rel.r_info), &sym)) {
pr_warning("relocation: symbol %"PRIx64" not found\n",
GELF_R_SYM(rel.r_info));
return -LIBBPF_ERRNO__FORMAT;
@@ -1389,15 +1770,22 @@ bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr,
(long long) sym.st_value, sym.st_name, name);
shdr_idx = sym.st_shndx;
+ insn_idx = rel.r_offset / sizeof(struct bpf_insn);
+ pr_debug("relocation: insn_idx=%u, shdr_idx=%u\n",
+ insn_idx, shdr_idx);
+
+ if (shdr_idx >= SHN_LORESERVE) {
+ pr_warning("relocation: not yet supported relo for non-static global \'%s\' variable in special section (0x%x) found in insns[%d].code 0x%x\n",
+ name, shdr_idx, insn_idx,
+ insns[insn_idx].code);
+ return -LIBBPF_ERRNO__RELOC;
+ }
if (!bpf_object__relo_in_known_section(obj, shdr_idx)) {
pr_warning("Program '%s' contains unrecognized relo data pointing to section %u\n",
prog->section_name, shdr_idx);
return -LIBBPF_ERRNO__RELOC;
}
- insn_idx = rel.r_offset / sizeof(struct bpf_insn);
- pr_debug("relocation: insn_idx=%u\n", insn_idx);
-
if (insns[insn_idx].code == (BPF_JMP | BPF_CALL)) {
if (insns[insn_idx].src_reg != BPF_PSEUDO_CALL) {
pr_warning("incorrect bpf_call opcode\n");
@@ -1436,16 +1824,19 @@ bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr,
if (maps[map_idx].libbpf_type != type)
continue;
if (type != LIBBPF_MAP_UNSPEC ||
- (type == LIBBPF_MAP_UNSPEC &&
- maps[map_idx].offset == sym.st_value)) {
- pr_debug("relocation: find map %zd (%s) for insn %u\n",
- map_idx, maps[map_idx].name, insn_idx);
+ (maps[map_idx].sec_idx == sym.st_shndx &&
+ maps[map_idx].sec_offset == sym.st_value)) {
+ pr_debug("relocation: found map %zd (%s, sec_idx %d, offset %zu) for insn %u\n",
+ map_idx, maps[map_idx].name,
+ maps[map_idx].sec_idx,
+ maps[map_idx].sec_offset,
+ insn_idx);
break;
}
}
if (map_idx >= nr_maps) {
- pr_warning("bpf relocation: map_idx %d large than %d\n",
+ pr_warning("bpf relocation: map_idx %d larger than %d\n",
(int)map_idx, (int)nr_maps - 1);
return -LIBBPF_ERRNO__RELOC;
}
@@ -1459,14 +1850,18 @@ bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr,
return 0;
}
-static int bpf_map_find_btf_info(struct bpf_map *map, const struct btf *btf)
+static int bpf_map_find_btf_info(struct bpf_object *obj, struct bpf_map *map)
{
struct bpf_map_def *def = &map->def;
__u32 key_type_id = 0, value_type_id = 0;
int ret;
+ /* if it's BTF-defined map, we don't need to search for type IDs */
+ if (map->sec_idx == obj->efile.btf_maps_shndx)
+ return 0;
+
if (!bpf_map__is_internal(map)) {
- ret = btf__get_map_kv_tids(btf, map->name, def->key_size,
+ ret = btf__get_map_kv_tids(obj->btf, map->name, def->key_size,
def->value_size, &key_type_id,
&value_type_id);
} else {
@@ -1474,7 +1869,7 @@ static int bpf_map_find_btf_info(struct bpf_map *map, const struct btf *btf)
* LLVM annotates global data differently in BTF, that is,
* only as '.data', '.bss' or '.rodata'.
*/
- ret = btf__find_by_name(btf,
+ ret = btf__find_by_name(obj->btf,
libbpf_type_to_btf_name[map->libbpf_type]);
}
if (ret < 0)
@@ -1646,14 +2041,16 @@ static int bpf_object__probe_btf_func(struct bpf_object *obj)
/* FUNC x */ /* [3] */
BTF_TYPE_ENC(5, BTF_INFO_ENC(BTF_KIND_FUNC, 0, 0), 2),
};
- int res;
+ int btf_fd;
- res = libbpf__probe_raw_btf((char *)types, sizeof(types),
- strs, sizeof(strs));
- if (res < 0)
- return res;
- if (res > 0)
+ btf_fd = libbpf__load_raw_btf((char *)types, sizeof(types),
+ strs, sizeof(strs));
+ if (btf_fd >= 0) {
obj->caps.btf_func = 1;
+ close(btf_fd);
+ return 1;
+ }
+
return 0;
}
@@ -1671,14 +2068,16 @@ static int bpf_object__probe_btf_datasec(struct bpf_object *obj)
BTF_TYPE_ENC(3, BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 1), 4),
BTF_VAR_SECINFO_ENC(2, 0, 4),
};
- int res;
+ int btf_fd;
- res = libbpf__probe_raw_btf((char *)types, sizeof(types),
- strs, sizeof(strs));
- if (res < 0)
- return res;
- if (res > 0)
+ btf_fd = libbpf__load_raw_btf((char *)types, sizeof(types),
+ strs, sizeof(strs));
+ if (btf_fd >= 0) {
obj->caps.btf_datasec = 1;
+ close(btf_fd);
+ return 1;
+ }
+
return 0;
}
@@ -1696,7 +2095,7 @@ bpf_object__probe_caps(struct bpf_object *obj)
for (i = 0; i < ARRAY_SIZE(probe_fn); i++) {
ret = probe_fn[i](obj);
if (ret < 0)
- return ret;
+ pr_debug("Probe #%d failed with %d.\n", i, ret);
}
return 0;
@@ -1734,6 +2133,7 @@ static int
bpf_object__create_maps(struct bpf_object *obj)
{
struct bpf_create_map_attr create_attr = {};
+ int nr_cpus = 0;
unsigned int i;
int err;
@@ -1756,7 +2156,22 @@ bpf_object__create_maps(struct bpf_object *obj)
create_attr.map_flags = def->map_flags;
create_attr.key_size = def->key_size;
create_attr.value_size = def->value_size;
- create_attr.max_entries = def->max_entries;
+ if (def->type == BPF_MAP_TYPE_PERF_EVENT_ARRAY &&
+ !def->max_entries) {
+ if (!nr_cpus)
+ nr_cpus = libbpf_num_possible_cpus();
+ if (nr_cpus < 0) {
+ pr_warning("failed to determine number of system CPUs: %d\n",
+ nr_cpus);
+ err = nr_cpus;
+ goto err_out;
+ }
+ pr_debug("map '%s': setting size to %d\n",
+ map->name, nr_cpus);
+ create_attr.max_entries = nr_cpus;
+ } else {
+ create_attr.max_entries = def->max_entries;
+ }
create_attr.btf_fd = 0;
create_attr.btf_key_type_id = 0;
create_attr.btf_value_type_id = 0;
@@ -1764,17 +2179,19 @@ bpf_object__create_maps(struct bpf_object *obj)
map->inner_map_fd >= 0)
create_attr.inner_map_fd = map->inner_map_fd;
- if (obj->btf && !bpf_map_find_btf_info(map, obj->btf)) {
+ if (obj->btf && !bpf_map_find_btf_info(obj, map)) {
create_attr.btf_fd = btf__fd(obj->btf);
create_attr.btf_key_type_id = map->btf_key_type_id;
create_attr.btf_value_type_id = map->btf_value_type_id;
}
*pfd = bpf_create_map_xattr(&create_attr);
- if (*pfd < 0 && create_attr.btf_key_type_id) {
- cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg));
+ if (*pfd < 0 && (create_attr.btf_key_type_id ||
+ create_attr.btf_value_type_id)) {
+ err = -errno;
+ cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
pr_warning("Error in bpf_create_map_xattr(%s):%s(%d). Retrying without BTF.\n",
- map->name, cp, errno);
+ map->name, cp, err);
create_attr.btf_fd = 0;
create_attr.btf_key_type_id = 0;
create_attr.btf_value_type_id = 0;
@@ -1786,11 +2203,11 @@ bpf_object__create_maps(struct bpf_object *obj)
if (*pfd < 0) {
size_t j;
- err = *pfd;
+ err = -errno;
err_out:
- cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg));
- pr_warning("failed to create map (name: '%s'): %s\n",
- map->name, cp);
+ cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
+ pr_warning("failed to create map (name: '%s'): %s(%d)\n",
+ map->name, cp, err);
for (j = 0; j < i; j++)
zclose(obj->maps[j].fd);
return err;
@@ -1804,7 +2221,7 @@ err_out:
}
}
- pr_debug("create map %s: fd=%d\n", map->name, *pfd);
+ pr_debug("created map %s: fd=%d\n", map->name, *pfd);
}
return 0;
@@ -1825,18 +2242,14 @@ check_btf_ext_reloc_err(struct bpf_program *prog, int err,
if (btf_prog_info) {
/*
* Some info has already been found but has problem
- * in the last btf_ext reloc. Must have to error
- * out.
+ * in the last btf_ext reloc. Must have to error out.
*/
pr_warning("Error in relocating %s for sec %s.\n",
info_name, prog->section_name);
return err;
}
- /*
- * Have problem loading the very first info. Ignore
- * the rest.
- */
+ /* Have problem loading the very first info. Ignore the rest. */
pr_warning("Cannot find %s for main program sec %s. Ignore all %s.\n",
info_name, prog->section_name, info_name);
return 0;
@@ -1880,12 +2293,897 @@ bpf_program_reloc_btf_ext(struct bpf_program *prog, struct bpf_object *obj,
prog->line_info_rec_size = btf_ext__line_info_rec_size(obj->btf_ext);
}
- if (!insn_offset)
- prog->btf_fd = btf__fd(obj->btf);
+ return 0;
+}
+
+#define BPF_CORE_SPEC_MAX_LEN 64
+
+/* represents BPF CO-RE field or array element accessor */
+struct bpf_core_accessor {
+ __u32 type_id; /* struct/union type or array element type */
+ __u32 idx; /* field index or array index */
+ const char *name; /* field name or NULL for array accessor */
+};
+
+struct bpf_core_spec {
+ const struct btf *btf;
+ /* high-level spec: named fields and array indices only */
+ struct bpf_core_accessor spec[BPF_CORE_SPEC_MAX_LEN];
+ /* high-level spec length */
+ int len;
+ /* raw, low-level spec: 1-to-1 with accessor spec string */
+ int raw_spec[BPF_CORE_SPEC_MAX_LEN];
+ /* raw spec length */
+ int raw_len;
+ /* field byte offset represented by spec */
+ __u32 offset;
+};
+
+static bool str_is_empty(const char *s)
+{
+ return !s || !s[0];
+}
+
+/*
+ * Turn bpf_offset_reloc into a low- and high-level spec representation,
+ * validating correctness along the way, as well as calculating resulting
+ * field offset (in bytes), specified by accessor string. Low-level spec
+ * captures every single level of nestedness, including traversing anonymous
+ * struct/union members. High-level one only captures semantically meaningful
+ * "turning points": named fields and array indicies.
+ * E.g., for this case:
+ *
+ * struct sample {
+ * int __unimportant;
+ * struct {
+ * int __1;
+ * int __2;
+ * int a[7];
+ * };
+ * };
+ *
+ * struct sample *s = ...;
+ *
+ * int x = &s->a[3]; // access string = '0:1:2:3'
+ *
+ * Low-level spec has 1:1 mapping with each element of access string (it's
+ * just a parsed access string representation): [0, 1, 2, 3].
+ *
+ * High-level spec will capture only 3 points:
+ * - intial zero-index access by pointer (&s->... is the same as &s[0]...);
+ * - field 'a' access (corresponds to '2' in low-level spec);
+ * - array element #3 access (corresponds to '3' in low-level spec).
+ *
+ */
+static int bpf_core_spec_parse(const struct btf *btf,
+ __u32 type_id,
+ const char *spec_str,
+ struct bpf_core_spec *spec)
+{
+ int access_idx, parsed_len, i;
+ const struct btf_type *t;
+ const char *name;
+ __u32 id;
+ __s64 sz;
+
+ if (str_is_empty(spec_str) || *spec_str == ':')
+ return -EINVAL;
+
+ memset(spec, 0, sizeof(*spec));
+ spec->btf = btf;
+
+ /* parse spec_str="0:1:2:3:4" into array raw_spec=[0, 1, 2, 3, 4] */
+ while (*spec_str) {
+ if (*spec_str == ':')
+ ++spec_str;
+ if (sscanf(spec_str, "%d%n", &access_idx, &parsed_len) != 1)
+ return -EINVAL;
+ if (spec->raw_len == BPF_CORE_SPEC_MAX_LEN)
+ return -E2BIG;
+ spec_str += parsed_len;
+ spec->raw_spec[spec->raw_len++] = access_idx;
+ }
+
+ if (spec->raw_len == 0)
+ return -EINVAL;
+
+ /* first spec value is always reloc type array index */
+ t = skip_mods_and_typedefs(btf, type_id, &id);
+ if (!t)
+ return -EINVAL;
+
+ access_idx = spec->raw_spec[0];
+ spec->spec[0].type_id = id;
+ spec->spec[0].idx = access_idx;
+ spec->len++;
+
+ sz = btf__resolve_size(btf, id);
+ if (sz < 0)
+ return sz;
+ spec->offset = access_idx * sz;
+
+ for (i = 1; i < spec->raw_len; i++) {
+ t = skip_mods_and_typedefs(btf, id, &id);
+ if (!t)
+ return -EINVAL;
+
+ access_idx = spec->raw_spec[i];
+
+ if (btf_is_composite(t)) {
+ const struct btf_member *m;
+ __u32 offset;
+
+ if (access_idx >= btf_vlen(t))
+ return -EINVAL;
+ if (btf_member_bitfield_size(t, access_idx))
+ return -EINVAL;
+
+ offset = btf_member_bit_offset(t, access_idx);
+ if (offset % 8)
+ return -EINVAL;
+ spec->offset += offset / 8;
+
+ m = btf_members(t) + access_idx;
+ if (m->name_off) {
+ name = btf__name_by_offset(btf, m->name_off);
+ if (str_is_empty(name))
+ return -EINVAL;
+
+ spec->spec[spec->len].type_id = id;
+ spec->spec[spec->len].idx = access_idx;
+ spec->spec[spec->len].name = name;
+ spec->len++;
+ }
+
+ id = m->type;
+ } else if (btf_is_array(t)) {
+ const struct btf_array *a = btf_array(t);
+
+ t = skip_mods_and_typedefs(btf, a->type, &id);
+ if (!t || access_idx >= a->nelems)
+ return -EINVAL;
+
+ spec->spec[spec->len].type_id = id;
+ spec->spec[spec->len].idx = access_idx;
+ spec->len++;
+
+ sz = btf__resolve_size(btf, id);
+ if (sz < 0)
+ return sz;
+ spec->offset += access_idx * sz;
+ } else {
+ pr_warning("relo for [%u] %s (at idx %d) captures type [%d] of unexpected kind %d\n",
+ type_id, spec_str, i, id, btf_kind(t));
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static bool bpf_core_is_flavor_sep(const char *s)
+{
+ /* check X___Y name pattern, where X and Y are not underscores */
+ return s[0] != '_' && /* X */
+ s[1] == '_' && s[2] == '_' && s[3] == '_' && /* ___ */
+ s[4] != '_'; /* Y */
+}
+
+/* Given 'some_struct_name___with_flavor' return the length of a name prefix
+ * before last triple underscore. Struct name part after last triple
+ * underscore is ignored by BPF CO-RE relocation during relocation matching.
+ */
+static size_t bpf_core_essential_name_len(const char *name)
+{
+ size_t n = strlen(name);
+ int i;
+
+ for (i = n - 5; i >= 0; i--) {
+ if (bpf_core_is_flavor_sep(name + i))
+ return i + 1;
+ }
+ return n;
+}
+
+/* dynamically sized list of type IDs */
+struct ids_vec {
+ __u32 *data;
+ int len;
+};
+
+static void bpf_core_free_cands(struct ids_vec *cand_ids)
+{
+ free(cand_ids->data);
+ free(cand_ids);
+}
+
+static struct ids_vec *bpf_core_find_cands(const struct btf *local_btf,
+ __u32 local_type_id,
+ const struct btf *targ_btf)
+{
+ size_t local_essent_len, targ_essent_len;
+ const char *local_name, *targ_name;
+ const struct btf_type *t;
+ struct ids_vec *cand_ids;
+ __u32 *new_ids;
+ int i, err, n;
+
+ t = btf__type_by_id(local_btf, local_type_id);
+ if (!t)
+ return ERR_PTR(-EINVAL);
+
+ local_name = btf__name_by_offset(local_btf, t->name_off);
+ if (str_is_empty(local_name))
+ return ERR_PTR(-EINVAL);
+ local_essent_len = bpf_core_essential_name_len(local_name);
+
+ cand_ids = calloc(1, sizeof(*cand_ids));
+ if (!cand_ids)
+ return ERR_PTR(-ENOMEM);
+
+ n = btf__get_nr_types(targ_btf);
+ for (i = 1; i <= n; i++) {
+ t = btf__type_by_id(targ_btf, i);
+ targ_name = btf__name_by_offset(targ_btf, t->name_off);
+ if (str_is_empty(targ_name))
+ continue;
+
+ targ_essent_len = bpf_core_essential_name_len(targ_name);
+ if (targ_essent_len != local_essent_len)
+ continue;
+
+ if (strncmp(local_name, targ_name, local_essent_len) == 0) {
+ pr_debug("[%d] %s: found candidate [%d] %s\n",
+ local_type_id, local_name, i, targ_name);
+ new_ids = realloc(cand_ids->data, cand_ids->len + 1);
+ if (!new_ids) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+ cand_ids->data = new_ids;
+ cand_ids->data[cand_ids->len++] = i;
+ }
+ }
+ return cand_ids;
+err_out:
+ bpf_core_free_cands(cand_ids);
+ return ERR_PTR(err);
+}
+
+/* Check two types for compatibility, skipping const/volatile/restrict and
+ * typedefs, to ensure we are relocating offset to the compatible entities:
+ * - any two STRUCTs/UNIONs are compatible and can be mixed;
+ * - any two FWDs are compatible;
+ * - any two PTRs are always compatible;
+ * - for ENUMs, check sizes, names are ignored;
+ * - for INT, size and bitness should match, signedness is ignored;
+ * - for ARRAY, dimensionality is ignored, element types are checked for
+ * compatibility recursively;
+ * - everything else shouldn't be ever a target of relocation.
+ * These rules are not set in stone and probably will be adjusted as we get
+ * more experience with using BPF CO-RE relocations.
+ */
+static int bpf_core_fields_are_compat(const struct btf *local_btf,
+ __u32 local_id,
+ const struct btf *targ_btf,
+ __u32 targ_id)
+{
+ const struct btf_type *local_type, *targ_type;
+
+recur:
+ local_type = skip_mods_and_typedefs(local_btf, local_id, &local_id);
+ targ_type = skip_mods_and_typedefs(targ_btf, targ_id, &targ_id);
+ if (!local_type || !targ_type)
+ return -EINVAL;
+
+ if (btf_is_composite(local_type) && btf_is_composite(targ_type))
+ return 1;
+ if (btf_kind(local_type) != btf_kind(targ_type))
+ return 0;
+
+ switch (btf_kind(local_type)) {
+ case BTF_KIND_FWD:
+ case BTF_KIND_PTR:
+ return 1;
+ case BTF_KIND_ENUM:
+ return local_type->size == targ_type->size;
+ case BTF_KIND_INT:
+ return btf_int_offset(local_type) == 0 &&
+ btf_int_offset(targ_type) == 0 &&
+ local_type->size == targ_type->size &&
+ btf_int_bits(local_type) == btf_int_bits(targ_type);
+ case BTF_KIND_ARRAY:
+ local_id = btf_array(local_type)->type;
+ targ_id = btf_array(targ_type)->type;
+ goto recur;
+ default:
+ pr_warning("unexpected kind %d relocated, local [%d], target [%d]\n",
+ btf_kind(local_type), local_id, targ_id);
+ return 0;
+ }
+}
+
+/*
+ * Given single high-level named field accessor in local type, find
+ * corresponding high-level accessor for a target type. Along the way,
+ * maintain low-level spec for target as well. Also keep updating target
+ * offset.
+ *
+ * Searching is performed through recursive exhaustive enumeration of all
+ * fields of a struct/union. If there are any anonymous (embedded)
+ * structs/unions, they are recursively searched as well. If field with
+ * desired name is found, check compatibility between local and target types,
+ * before returning result.
+ *
+ * 1 is returned, if field is found.
+ * 0 is returned if no compatible field is found.
+ * <0 is returned on error.
+ */
+static int bpf_core_match_member(const struct btf *local_btf,
+ const struct bpf_core_accessor *local_acc,
+ const struct btf *targ_btf,
+ __u32 targ_id,
+ struct bpf_core_spec *spec,
+ __u32 *next_targ_id)
+{
+ const struct btf_type *local_type, *targ_type;
+ const struct btf_member *local_member, *m;
+ const char *local_name, *targ_name;
+ __u32 local_id;
+ int i, n, found;
+
+ targ_type = skip_mods_and_typedefs(targ_btf, targ_id, &targ_id);
+ if (!targ_type)
+ return -EINVAL;
+ if (!btf_is_composite(targ_type))
+ return 0;
+
+ local_id = local_acc->type_id;
+ local_type = btf__type_by_id(local_btf, local_id);
+ local_member = btf_members(local_type) + local_acc->idx;
+ local_name = btf__name_by_offset(local_btf, local_member->name_off);
+
+ n = btf_vlen(targ_type);
+ m = btf_members(targ_type);
+ for (i = 0; i < n; i++, m++) {
+ __u32 offset;
+
+ /* bitfield relocations not supported */
+ if (btf_member_bitfield_size(targ_type, i))
+ continue;
+ offset = btf_member_bit_offset(targ_type, i);
+ if (offset % 8)
+ continue;
+
+ /* too deep struct/union/array nesting */
+ if (spec->raw_len == BPF_CORE_SPEC_MAX_LEN)
+ return -E2BIG;
+
+ /* speculate this member will be the good one */
+ spec->offset += offset / 8;
+ spec->raw_spec[spec->raw_len++] = i;
+
+ targ_name = btf__name_by_offset(targ_btf, m->name_off);
+ if (str_is_empty(targ_name)) {
+ /* embedded struct/union, we need to go deeper */
+ found = bpf_core_match_member(local_btf, local_acc,
+ targ_btf, m->type,
+ spec, next_targ_id);
+ if (found) /* either found or error */
+ return found;
+ } else if (strcmp(local_name, targ_name) == 0) {
+ /* matching named field */
+ struct bpf_core_accessor *targ_acc;
+
+ targ_acc = &spec->spec[spec->len++];
+ targ_acc->type_id = targ_id;
+ targ_acc->idx = i;
+ targ_acc->name = targ_name;
+
+ *next_targ_id = m->type;
+ found = bpf_core_fields_are_compat(local_btf,
+ local_member->type,
+ targ_btf, m->type);
+ if (!found)
+ spec->len--; /* pop accessor */
+ return found;
+ }
+ /* member turned out not to be what we looked for */
+ spec->offset -= offset / 8;
+ spec->raw_len--;
+ }
+
+ return 0;
+}
+
+/*
+ * Try to match local spec to a target type and, if successful, produce full
+ * target spec (high-level, low-level + offset).
+ */
+static int bpf_core_spec_match(struct bpf_core_spec *local_spec,
+ const struct btf *targ_btf, __u32 targ_id,
+ struct bpf_core_spec *targ_spec)
+{
+ const struct btf_type *targ_type;
+ const struct bpf_core_accessor *local_acc;
+ struct bpf_core_accessor *targ_acc;
+ int i, sz, matched;
+ memset(targ_spec, 0, sizeof(*targ_spec));
+ targ_spec->btf = targ_btf;
+
+ local_acc = &local_spec->spec[0];
+ targ_acc = &targ_spec->spec[0];
+
+ for (i = 0; i < local_spec->len; i++, local_acc++, targ_acc++) {
+ targ_type = skip_mods_and_typedefs(targ_spec->btf, targ_id,
+ &targ_id);
+ if (!targ_type)
+ return -EINVAL;
+
+ if (local_acc->name) {
+ matched = bpf_core_match_member(local_spec->btf,
+ local_acc,
+ targ_btf, targ_id,
+ targ_spec, &targ_id);
+ if (matched <= 0)
+ return matched;
+ } else {
+ /* for i=0, targ_id is already treated as array element
+ * type (because it's the original struct), for others
+ * we should find array element type first
+ */
+ if (i > 0) {
+ const struct btf_array *a;
+
+ if (!btf_is_array(targ_type))
+ return 0;
+
+ a = btf_array(targ_type);
+ if (local_acc->idx >= a->nelems)
+ return 0;
+ if (!skip_mods_and_typedefs(targ_btf, a->type,
+ &targ_id))
+ return -EINVAL;
+ }
+
+ /* too deep struct/union/array nesting */
+ if (targ_spec->raw_len == BPF_CORE_SPEC_MAX_LEN)
+ return -E2BIG;
+
+ targ_acc->type_id = targ_id;
+ targ_acc->idx = local_acc->idx;
+ targ_acc->name = NULL;
+ targ_spec->len++;
+ targ_spec->raw_spec[targ_spec->raw_len] = targ_acc->idx;
+ targ_spec->raw_len++;
+
+ sz = btf__resolve_size(targ_btf, targ_id);
+ if (sz < 0)
+ return sz;
+ targ_spec->offset += local_acc->idx * sz;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Patch relocatable BPF instruction.
+ * Expected insn->imm value is provided for validation, as well as the new
+ * relocated value.
+ *
+ * Currently three kinds of BPF instructions are supported:
+ * 1. rX = <imm> (assignment with immediate operand);
+ * 2. rX += <imm> (arithmetic operations with immediate operand);
+ * 3. *(rX) = <imm> (indirect memory assignment with immediate operand).
+ *
+ * If actual insn->imm value is wrong, bail out.
+ */
+static int bpf_core_reloc_insn(struct bpf_program *prog, int insn_off,
+ __u32 orig_off, __u32 new_off)
+{
+ struct bpf_insn *insn;
+ int insn_idx;
+ __u8 class;
+
+ if (insn_off % sizeof(struct bpf_insn))
+ return -EINVAL;
+ insn_idx = insn_off / sizeof(struct bpf_insn);
+
+ insn = &prog->insns[insn_idx];
+ class = BPF_CLASS(insn->code);
+
+ if (class == BPF_ALU || class == BPF_ALU64) {
+ if (BPF_SRC(insn->code) != BPF_K)
+ return -EINVAL;
+ if (insn->imm != orig_off)
+ return -EINVAL;
+ insn->imm = new_off;
+ pr_debug("prog '%s': patched insn #%d (ALU/ALU64) imm %d -> %d\n",
+ bpf_program__title(prog, false),
+ insn_idx, orig_off, new_off);
+ } else {
+ pr_warning("prog '%s': trying to relocate unrecognized insn #%d, code:%x, src:%x, dst:%x, off:%x, imm:%x\n",
+ bpf_program__title(prog, false),
+ insn_idx, insn->code, insn->src_reg, insn->dst_reg,
+ insn->off, insn->imm);
+ return -EINVAL;
+ }
return 0;
}
+static struct btf *btf_load_raw(const char *path)
+{
+ struct btf *btf;
+ size_t read_cnt;
+ struct stat st;
+ void *data;
+ FILE *f;
+
+ if (stat(path, &st))
+ return ERR_PTR(-errno);
+
+ data = malloc(st.st_size);
+ if (!data)
+ return ERR_PTR(-ENOMEM);
+
+ f = fopen(path, "rb");
+ if (!f) {
+ btf = ERR_PTR(-errno);
+ goto cleanup;
+ }
+
+ read_cnt = fread(data, 1, st.st_size, f);
+ fclose(f);
+ if (read_cnt < st.st_size) {
+ btf = ERR_PTR(-EBADF);
+ goto cleanup;
+ }
+
+ btf = btf__new(data, read_cnt);
+
+cleanup:
+ free(data);
+ return btf;
+}
+
+/*
+ * Probe few well-known locations for vmlinux kernel image and try to load BTF
+ * data out of it to use for target BTF.
+ */
+static struct btf *bpf_core_find_kernel_btf(void)
+{
+ struct {
+ const char *path_fmt;
+ bool raw_btf;
+ } locations[] = {
+ /* try canonical vmlinux BTF through sysfs first */
+ { "/sys/kernel/btf/vmlinux", true /* raw BTF */ },
+ /* fall back to trying to find vmlinux ELF on disk otherwise */
+ { "/boot/vmlinux-%1$s" },
+ { "/lib/modules/%1$s/vmlinux-%1$s" },
+ { "/lib/modules/%1$s/build/vmlinux" },
+ { "/usr/lib/modules/%1$s/kernel/vmlinux" },
+ { "/usr/lib/debug/boot/vmlinux-%1$s" },
+ { "/usr/lib/debug/boot/vmlinux-%1$s.debug" },
+ { "/usr/lib/debug/lib/modules/%1$s/vmlinux" },
+ };
+ char path[PATH_MAX + 1];
+ struct utsname buf;
+ struct btf *btf;
+ int i;
+
+ uname(&buf);
+
+ for (i = 0; i < ARRAY_SIZE(locations); i++) {
+ snprintf(path, PATH_MAX, locations[i].path_fmt, buf.release);
+
+ if (access(path, R_OK))
+ continue;
+
+ if (locations[i].raw_btf)
+ btf = btf_load_raw(path);
+ else
+ btf = btf__parse_elf(path, NULL);
+
+ pr_debug("loading kernel BTF '%s': %ld\n",
+ path, IS_ERR(btf) ? PTR_ERR(btf) : 0);
+ if (IS_ERR(btf))
+ continue;
+
+ return btf;
+ }
+
+ pr_warning("failed to find valid kernel BTF\n");
+ return ERR_PTR(-ESRCH);
+}
+
+/* Output spec definition in the format:
+ * [<type-id>] (<type-name>) + <raw-spec> => <offset>@<spec>,
+ * where <spec> is a C-syntax view of recorded field access, e.g.: x.a[3].b
+ */
+static void bpf_core_dump_spec(int level, const struct bpf_core_spec *spec)
+{
+ const struct btf_type *t;
+ const char *s;
+ __u32 type_id;
+ int i;
+
+ type_id = spec->spec[0].type_id;
+ t = btf__type_by_id(spec->btf, type_id);
+ s = btf__name_by_offset(spec->btf, t->name_off);
+ libbpf_print(level, "[%u] %s + ", type_id, s);
+
+ for (i = 0; i < spec->raw_len; i++)
+ libbpf_print(level, "%d%s", spec->raw_spec[i],
+ i == spec->raw_len - 1 ? " => " : ":");
+
+ libbpf_print(level, "%u @ &x", spec->offset);
+
+ for (i = 0; i < spec->len; i++) {
+ if (spec->spec[i].name)
+ libbpf_print(level, ".%s", spec->spec[i].name);
+ else
+ libbpf_print(level, "[%u]", spec->spec[i].idx);
+ }
+
+}
+
+static size_t bpf_core_hash_fn(const void *key, void *ctx)
+{
+ return (size_t)key;
+}
+
+static bool bpf_core_equal_fn(const void *k1, const void *k2, void *ctx)
+{
+ return k1 == k2;
+}
+
+static void *u32_as_hash_key(__u32 x)
+{
+ return (void *)(uintptr_t)x;
+}
+
+/*
+ * CO-RE relocate single instruction.
+ *
+ * The outline and important points of the algorithm:
+ * 1. For given local type, find corresponding candidate target types.
+ * Candidate type is a type with the same "essential" name, ignoring
+ * everything after last triple underscore (___). E.g., `sample`,
+ * `sample___flavor_one`, `sample___flavor_another_one`, are all candidates
+ * for each other. Names with triple underscore are referred to as
+ * "flavors" and are useful, among other things, to allow to
+ * specify/support incompatible variations of the same kernel struct, which
+ * might differ between different kernel versions and/or build
+ * configurations.
+ *
+ * N.B. Struct "flavors" could be generated by bpftool's BTF-to-C
+ * converter, when deduplicated BTF of a kernel still contains more than
+ * one different types with the same name. In that case, ___2, ___3, etc
+ * are appended starting from second name conflict. But start flavors are
+ * also useful to be defined "locally", in BPF program, to extract same
+ * data from incompatible changes between different kernel
+ * versions/configurations. For instance, to handle field renames between
+ * kernel versions, one can use two flavors of the struct name with the
+ * same common name and use conditional relocations to extract that field,
+ * depending on target kernel version.
+ * 2. For each candidate type, try to match local specification to this
+ * candidate target type. Matching involves finding corresponding
+ * high-level spec accessors, meaning that all named fields should match,
+ * as well as all array accesses should be within the actual bounds. Also,
+ * types should be compatible (see bpf_core_fields_are_compat for details).
+ * 3. It is supported and expected that there might be multiple flavors
+ * matching the spec. As long as all the specs resolve to the same set of
+ * offsets across all candidates, there is not error. If there is any
+ * ambiguity, CO-RE relocation will fail. This is necessary to accomodate
+ * imprefection of BTF deduplication, which can cause slight duplication of
+ * the same BTF type, if some directly or indirectly referenced (by
+ * pointer) type gets resolved to different actual types in different
+ * object files. If such situation occurs, deduplicated BTF will end up
+ * with two (or more) structurally identical types, which differ only in
+ * types they refer to through pointer. This should be OK in most cases and
+ * is not an error.
+ * 4. Candidate types search is performed by linearly scanning through all
+ * types in target BTF. It is anticipated that this is overall more
+ * efficient memory-wise and not significantly worse (if not better)
+ * CPU-wise compared to prebuilding a map from all local type names to
+ * a list of candidate type names. It's also sped up by caching resolved
+ * list of matching candidates per each local "root" type ID, that has at
+ * least one bpf_offset_reloc associated with it. This list is shared
+ * between multiple relocations for the same type ID and is updated as some
+ * of the candidates are pruned due to structural incompatibility.
+ */
+static int bpf_core_reloc_offset(struct bpf_program *prog,
+ const struct bpf_offset_reloc *relo,
+ int relo_idx,
+ const struct btf *local_btf,
+ const struct btf *targ_btf,
+ struct hashmap *cand_cache)
+{
+ const char *prog_name = bpf_program__title(prog, false);
+ struct bpf_core_spec local_spec, cand_spec, targ_spec;
+ const void *type_key = u32_as_hash_key(relo->type_id);
+ const struct btf_type *local_type, *cand_type;
+ const char *local_name, *cand_name;
+ struct ids_vec *cand_ids;
+ __u32 local_id, cand_id;
+ const char *spec_str;
+ int i, j, err;
+
+ local_id = relo->type_id;
+ local_type = btf__type_by_id(local_btf, local_id);
+ if (!local_type)
+ return -EINVAL;
+
+ local_name = btf__name_by_offset(local_btf, local_type->name_off);
+ if (str_is_empty(local_name))
+ return -EINVAL;
+
+ spec_str = btf__name_by_offset(local_btf, relo->access_str_off);
+ if (str_is_empty(spec_str))
+ return -EINVAL;
+
+ err = bpf_core_spec_parse(local_btf, local_id, spec_str, &local_spec);
+ if (err) {
+ pr_warning("prog '%s': relo #%d: parsing [%d] %s + %s failed: %d\n",
+ prog_name, relo_idx, local_id, local_name, spec_str,
+ err);
+ return -EINVAL;
+ }
+
+ pr_debug("prog '%s': relo #%d: spec is ", prog_name, relo_idx);
+ bpf_core_dump_spec(LIBBPF_DEBUG, &local_spec);
+ libbpf_print(LIBBPF_DEBUG, "\n");
+
+ if (!hashmap__find(cand_cache, type_key, (void **)&cand_ids)) {
+ cand_ids = bpf_core_find_cands(local_btf, local_id, targ_btf);
+ if (IS_ERR(cand_ids)) {
+ pr_warning("prog '%s': relo #%d: target candidate search failed for [%d] %s: %ld",
+ prog_name, relo_idx, local_id, local_name,
+ PTR_ERR(cand_ids));
+ return PTR_ERR(cand_ids);
+ }
+ err = hashmap__set(cand_cache, type_key, cand_ids, NULL, NULL);
+ if (err) {
+ bpf_core_free_cands(cand_ids);
+ return err;
+ }
+ }
+
+ for (i = 0, j = 0; i < cand_ids->len; i++) {
+ cand_id = cand_ids->data[i];
+ cand_type = btf__type_by_id(targ_btf, cand_id);
+ cand_name = btf__name_by_offset(targ_btf, cand_type->name_off);
+
+ err = bpf_core_spec_match(&local_spec, targ_btf,
+ cand_id, &cand_spec);
+ pr_debug("prog '%s': relo #%d: matching candidate #%d %s against spec ",
+ prog_name, relo_idx, i, cand_name);
+ bpf_core_dump_spec(LIBBPF_DEBUG, &cand_spec);
+ libbpf_print(LIBBPF_DEBUG, ": %d\n", err);
+ if (err < 0) {
+ pr_warning("prog '%s': relo #%d: matching error: %d\n",
+ prog_name, relo_idx, err);
+ return err;
+ }
+ if (err == 0)
+ continue;
+
+ if (j == 0) {
+ targ_spec = cand_spec;
+ } else if (cand_spec.offset != targ_spec.offset) {
+ /* if there are many candidates, they should all
+ * resolve to the same offset
+ */
+ pr_warning("prog '%s': relo #%d: offset ambiguity: %u != %u\n",
+ prog_name, relo_idx, cand_spec.offset,
+ targ_spec.offset);
+ return -EINVAL;
+ }
+
+ cand_ids->data[j++] = cand_spec.spec[0].type_id;
+ }
+
+ cand_ids->len = j;
+ if (cand_ids->len == 0) {
+ pr_warning("prog '%s': relo #%d: no matching targets found for [%d] %s + %s\n",
+ prog_name, relo_idx, local_id, local_name, spec_str);
+ return -ESRCH;
+ }
+
+ err = bpf_core_reloc_insn(prog, relo->insn_off,
+ local_spec.offset, targ_spec.offset);
+ if (err) {
+ pr_warning("prog '%s': relo #%d: failed to patch insn at offset %d: %d\n",
+ prog_name, relo_idx, relo->insn_off, err);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+bpf_core_reloc_offsets(struct bpf_object *obj, const char *targ_btf_path)
+{
+ const struct btf_ext_info_sec *sec;
+ const struct bpf_offset_reloc *rec;
+ const struct btf_ext_info *seg;
+ struct hashmap_entry *entry;
+ struct hashmap *cand_cache = NULL;
+ struct bpf_program *prog;
+ struct btf *targ_btf;
+ const char *sec_name;
+ int i, err = 0;
+
+ if (targ_btf_path)
+ targ_btf = btf__parse_elf(targ_btf_path, NULL);
+ else
+ targ_btf = bpf_core_find_kernel_btf();
+ if (IS_ERR(targ_btf)) {
+ pr_warning("failed to get target BTF: %ld\n",
+ PTR_ERR(targ_btf));
+ return PTR_ERR(targ_btf);
+ }
+
+ cand_cache = hashmap__new(bpf_core_hash_fn, bpf_core_equal_fn, NULL);
+ if (IS_ERR(cand_cache)) {
+ err = PTR_ERR(cand_cache);
+ goto out;
+ }
+
+ seg = &obj->btf_ext->offset_reloc_info;
+ for_each_btf_ext_sec(seg, sec) {
+ sec_name = btf__name_by_offset(obj->btf, sec->sec_name_off);
+ if (str_is_empty(sec_name)) {
+ err = -EINVAL;
+ goto out;
+ }
+ prog = bpf_object__find_program_by_title(obj, sec_name);
+ if (!prog) {
+ pr_warning("failed to find program '%s' for CO-RE offset relocation\n",
+ sec_name);
+ err = -EINVAL;
+ goto out;
+ }
+
+ pr_debug("prog '%s': performing %d CO-RE offset relocs\n",
+ sec_name, sec->num_info);
+
+ for_each_btf_ext_rec(seg, sec, i, rec) {
+ err = bpf_core_reloc_offset(prog, rec, i, obj->btf,
+ targ_btf, cand_cache);
+ if (err) {
+ pr_warning("prog '%s': relo #%d: failed to relocate: %d\n",
+ sec_name, i, err);
+ goto out;
+ }
+ }
+ }
+
+out:
+ btf__free(targ_btf);
+ if (!IS_ERR_OR_NULL(cand_cache)) {
+ hashmap__for_each_entry(cand_cache, entry, i) {
+ bpf_core_free_cands(entry->value);
+ }
+ hashmap__free(cand_cache);
+ }
+ return err;
+}
+
+static int
+bpf_object__relocate_core(struct bpf_object *obj, const char *targ_btf_path)
+{
+ int err = 0;
+
+ if (obj->btf_ext->offset_reloc_info.len)
+ err = bpf_core_reloc_offsets(obj, targ_btf_path);
+
+ return err;
+}
+
static int
bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj,
struct reloc_desc *relo)
@@ -1993,14 +3291,21 @@ bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj)
return 0;
}
-
static int
-bpf_object__relocate(struct bpf_object *obj)
+bpf_object__relocate(struct bpf_object *obj, const char *targ_btf_path)
{
struct bpf_program *prog;
size_t i;
int err;
+ if (obj->btf_ext) {
+ err = bpf_object__relocate_core(obj, targ_btf_path);
+ if (err) {
+ pr_warning("failed to perform CO-RE relocations: %d\n",
+ err);
+ return err;
+ }
+ }
for (i = 0; i < obj->nr_programs; i++) {
prog = &obj->programs[i];
@@ -2040,9 +3345,7 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
return -LIBBPF_ERRNO__RELOC;
}
- err = bpf_program__collect_reloc(prog,
- shdr, data,
- obj);
+ err = bpf_program__collect_reloc(prog, shdr, data, obj);
if (err)
return err;
}
@@ -2057,7 +3360,10 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
char *cp, errmsg[STRERR_BUFSIZE];
int log_buf_size = BPF_LOG_BUF_SIZE;
char *log_buf;
- int ret;
+ int btf_fd, ret;
+
+ if (!insns || !insns_cnt)
+ return -EINVAL;
memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
load_attr.prog_type = prog->type;
@@ -2069,7 +3375,12 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
load_attr.license = license;
load_attr.kern_version = kern_version;
load_attr.prog_ifindex = prog->prog_ifindex;
- load_attr.prog_btf_fd = prog->btf_fd >= 0 ? prog->btf_fd : 0;
+ /* if .BTF.ext was loaded, kernel supports associated BTF for prog */
+ if (prog->obj->btf_ext)
+ btf_fd = bpf_object__btf_fd(prog->obj);
+ else
+ btf_fd = -1;
+ load_attr.prog_btf_fd = btf_fd >= 0 ? btf_fd : 0;
load_attr.func_info = prog->func_info;
load_attr.func_info_rec_size = prog->func_info_rec_size;
load_attr.func_info_cnt = prog->func_info_cnt;
@@ -2077,8 +3388,7 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
load_attr.line_info_rec_size = prog->line_info_rec_size;
load_attr.line_info_cnt = prog->line_info_cnt;
load_attr.log_level = prog->log_level;
- if (!load_attr.insns || !load_attr.insns_cnt)
- return -EINVAL;
+ load_attr.prog_flags = prog->prog_flags;
retry_load:
log_buf = malloc(log_buf_size);
@@ -2216,14 +3526,14 @@ out:
return err;
}
-static bool bpf_program__is_function_storage(struct bpf_program *prog,
- struct bpf_object *obj)
+static bool bpf_program__is_function_storage(const struct bpf_program *prog,
+ const struct bpf_object *obj)
{
return prog->idx == obj->efile.text_shndx && obj->has_pseudo_calls;
}
static int
-bpf_object__load_progs(struct bpf_object *obj)
+bpf_object__load_progs(struct bpf_object *obj, int log_level)
{
size_t i;
int err;
@@ -2231,6 +3541,7 @@ bpf_object__load_progs(struct bpf_object *obj)
for (i = 0; i < obj->nr_programs; i++) {
if (bpf_program__is_function_storage(&obj->programs[i], obj))
continue;
+ obj->programs[i].log_level |= log_level;
err = bpf_program__load(&obj->programs[i],
obj->license,
obj->kern_version);
@@ -2267,6 +3578,7 @@ static bool bpf_prog_type__needs_kver(enum bpf_prog_type type)
case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE:
case BPF_PROG_TYPE_PERF_EVENT:
case BPF_PROG_TYPE_CGROUP_SYSCTL:
+ case BPF_PROG_TYPE_CGROUP_SOCKOPT:
return false;
case BPF_PROG_TYPE_KPROBE:
default:
@@ -2357,11 +3669,9 @@ struct bpf_object *bpf_object__open_buffer(void *obj_buf,
snprintf(tmp_name, sizeof(tmp_name), "%lx-%lx",
(unsigned long)obj_buf,
(unsigned long)obj_buf_sz);
- tmp_name[sizeof(tmp_name) - 1] = '\0';
name = tmp_name;
}
- pr_debug("loading object '%s' from buffer\n",
- name);
+ pr_debug("loading object '%s' from buffer\n", name);
return __bpf_object__open(name, obj_buf, obj_buf_sz, true, true);
}
@@ -2382,10 +3692,14 @@ int bpf_object__unload(struct bpf_object *obj)
return 0;
}
-int bpf_object__load(struct bpf_object *obj)
+int bpf_object__load_xattr(struct bpf_object_load_attr *attr)
{
+ struct bpf_object *obj;
int err;
+ if (!attr)
+ return -EINVAL;
+ obj = attr->obj;
if (!obj)
return -EINVAL;
@@ -2397,8 +3711,8 @@ int bpf_object__load(struct bpf_object *obj)
obj->loaded = true;
CHECK_ERR(bpf_object__create_maps(obj), err, out);
- CHECK_ERR(bpf_object__relocate(obj), err, out);
- CHECK_ERR(bpf_object__load_progs(obj), err, out);
+ CHECK_ERR(bpf_object__relocate(obj, attr->target_btf_path), err, out);
+ CHECK_ERR(bpf_object__load_progs(obj, attr->log_level), err, out);
return 0;
out:
@@ -2407,6 +3721,15 @@ out:
return err;
}
+int bpf_object__load(struct bpf_object *obj)
+{
+ struct bpf_object_load_attr attr = {
+ .obj = obj,
+ };
+
+ return bpf_object__load_xattr(&attr);
+}
+
static int check_path(const char *path)
{
char *cp, errmsg[STRERR_BUFSIZE];
@@ -2911,17 +4234,17 @@ bpf_object__next(struct bpf_object *prev)
return next;
}
-const char *bpf_object__name(struct bpf_object *obj)
+const char *bpf_object__name(const struct bpf_object *obj)
{
return obj ? obj->path : ERR_PTR(-EINVAL);
}
-unsigned int bpf_object__kversion(struct bpf_object *obj)
+unsigned int bpf_object__kversion(const struct bpf_object *obj)
{
return obj ? obj->kern_version : 0;
}
-struct btf *bpf_object__btf(struct bpf_object *obj)
+struct btf *bpf_object__btf(const struct bpf_object *obj)
{
return obj ? obj->btf : NULL;
}
@@ -2942,13 +4265,14 @@ int bpf_object__set_priv(struct bpf_object *obj, void *priv,
return 0;
}
-void *bpf_object__priv(struct bpf_object *obj)
+void *bpf_object__priv(const struct bpf_object *obj)
{
return obj ? obj->priv : ERR_PTR(-EINVAL);
}
static struct bpf_program *
-__bpf_program__iter(struct bpf_program *p, struct bpf_object *obj, bool forward)
+__bpf_program__iter(const struct bpf_program *p, const struct bpf_object *obj,
+ bool forward)
{
size_t nr_programs = obj->nr_programs;
ssize_t idx;
@@ -2973,7 +4297,7 @@ __bpf_program__iter(struct bpf_program *p, struct bpf_object *obj, bool forward)
}
struct bpf_program *
-bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
+bpf_program__next(struct bpf_program *prev, const struct bpf_object *obj)
{
struct bpf_program *prog = prev;
@@ -2985,7 +4309,7 @@ bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
}
struct bpf_program *
-bpf_program__prev(struct bpf_program *next, struct bpf_object *obj)
+bpf_program__prev(struct bpf_program *next, const struct bpf_object *obj)
{
struct bpf_program *prog = next;
@@ -3007,7 +4331,7 @@ int bpf_program__set_priv(struct bpf_program *prog, void *priv,
return 0;
}
-void *bpf_program__priv(struct bpf_program *prog)
+void *bpf_program__priv(const struct bpf_program *prog)
{
return prog ? prog->priv : ERR_PTR(-EINVAL);
}
@@ -3017,7 +4341,7 @@ void bpf_program__set_ifindex(struct bpf_program *prog, __u32 ifindex)
prog->prog_ifindex = ifindex;
}
-const char *bpf_program__title(struct bpf_program *prog, bool needs_copy)
+const char *bpf_program__title(const struct bpf_program *prog, bool needs_copy)
{
const char *title;
@@ -3033,7 +4357,7 @@ const char *bpf_program__title(struct bpf_program *prog, bool needs_copy)
return title;
}
-int bpf_program__fd(struct bpf_program *prog)
+int bpf_program__fd(const struct bpf_program *prog)
{
return bpf_program__nth_fd(prog, 0);
}
@@ -3066,7 +4390,7 @@ int bpf_program__set_prep(struct bpf_program *prog, int nr_instances,
return 0;
}
-int bpf_program__nth_fd(struct bpf_program *prog, int n)
+int bpf_program__nth_fd(const struct bpf_program *prog, int n)
{
int fd;
@@ -3094,25 +4418,25 @@ void bpf_program__set_type(struct bpf_program *prog, enum bpf_prog_type type)
prog->type = type;
}
-static bool bpf_program__is_type(struct bpf_program *prog,
+static bool bpf_program__is_type(const struct bpf_program *prog,
enum bpf_prog_type type)
{
return prog ? (prog->type == type) : false;
}
-#define BPF_PROG_TYPE_FNS(NAME, TYPE) \
-int bpf_program__set_##NAME(struct bpf_program *prog) \
-{ \
- if (!prog) \
- return -EINVAL; \
- bpf_program__set_type(prog, TYPE); \
- return 0; \
-} \
- \
-bool bpf_program__is_##NAME(struct bpf_program *prog) \
-{ \
- return bpf_program__is_type(prog, TYPE); \
-} \
+#define BPF_PROG_TYPE_FNS(NAME, TYPE) \
+int bpf_program__set_##NAME(struct bpf_program *prog) \
+{ \
+ if (!prog) \
+ return -EINVAL; \
+ bpf_program__set_type(prog, TYPE); \
+ return 0; \
+} \
+ \
+bool bpf_program__is_##NAME(const struct bpf_program *prog) \
+{ \
+ return bpf_program__is_type(prog, TYPE); \
+} \
BPF_PROG_TYPE_FNS(socket_filter, BPF_PROG_TYPE_SOCKET_FILTER);
BPF_PROG_TYPE_FNS(kprobe, BPF_PROG_TYPE_KPROBE);
@@ -3207,8 +4531,16 @@ static const struct {
BPF_CGROUP_UDP4_SENDMSG),
BPF_EAPROG_SEC("cgroup/sendmsg6", BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
BPF_CGROUP_UDP6_SENDMSG),
+ BPF_EAPROG_SEC("cgroup/recvmsg4", BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
+ BPF_CGROUP_UDP4_RECVMSG),
+ BPF_EAPROG_SEC("cgroup/recvmsg6", BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
+ BPF_CGROUP_UDP6_RECVMSG),
BPF_EAPROG_SEC("cgroup/sysctl", BPF_PROG_TYPE_CGROUP_SYSCTL,
BPF_CGROUP_SYSCTL),
+ BPF_EAPROG_SEC("cgroup/getsockopt", BPF_PROG_TYPE_CGROUP_SOCKOPT,
+ BPF_CGROUP_GETSOCKOPT),
+ BPF_EAPROG_SEC("cgroup/setsockopt", BPF_PROG_TYPE_CGROUP_SOCKOPT,
+ BPF_CGROUP_SETSOCKOPT),
};
#undef BPF_PROG_SEC_IMPL
@@ -3307,17 +4639,17 @@ bpf_program__identify_section(struct bpf_program *prog,
expected_attach_type);
}
-int bpf_map__fd(struct bpf_map *map)
+int bpf_map__fd(const struct bpf_map *map)
{
return map ? map->fd : -EINVAL;
}
-const struct bpf_map_def *bpf_map__def(struct bpf_map *map)
+const struct bpf_map_def *bpf_map__def(const struct bpf_map *map)
{
return map ? &map->def : ERR_PTR(-EINVAL);
}
-const char *bpf_map__name(struct bpf_map *map)
+const char *bpf_map__name(const struct bpf_map *map)
{
return map ? map->name : NULL;
}
@@ -3348,17 +4680,17 @@ int bpf_map__set_priv(struct bpf_map *map, void *priv,
return 0;
}
-void *bpf_map__priv(struct bpf_map *map)
+void *bpf_map__priv(const struct bpf_map *map)
{
return map ? map->priv : ERR_PTR(-EINVAL);
}
-bool bpf_map__is_offload_neutral(struct bpf_map *map)
+bool bpf_map__is_offload_neutral(const struct bpf_map *map)
{
return map->def.type == BPF_MAP_TYPE_PERF_EVENT_ARRAY;
}
-bool bpf_map__is_internal(struct bpf_map *map)
+bool bpf_map__is_internal(const struct bpf_map *map)
{
return map->libbpf_type != LIBBPF_MAP_UNSPEC;
}
@@ -3383,7 +4715,7 @@ int bpf_map__set_inner_map_fd(struct bpf_map *map, int fd)
}
static struct bpf_map *
-__bpf_map__iter(struct bpf_map *m, struct bpf_object *obj, int i)
+__bpf_map__iter(const struct bpf_map *m, const struct bpf_object *obj, int i)
{
ssize_t idx;
struct bpf_map *s, *e;
@@ -3407,7 +4739,7 @@ __bpf_map__iter(struct bpf_map *m, struct bpf_object *obj, int i)
}
struct bpf_map *
-bpf_map__next(struct bpf_map *prev, struct bpf_object *obj)
+bpf_map__next(const struct bpf_map *prev, const struct bpf_object *obj)
{
if (prev == NULL)
return obj->maps;
@@ -3416,7 +4748,7 @@ bpf_map__next(struct bpf_map *prev, struct bpf_object *obj)
}
struct bpf_map *
-bpf_map__prev(struct bpf_map *next, struct bpf_object *obj)
+bpf_map__prev(const struct bpf_map *next, const struct bpf_object *obj)
{
if (next == NULL) {
if (!obj->nr_maps)
@@ -3428,7 +4760,7 @@ bpf_map__prev(struct bpf_map *next, struct bpf_object *obj)
}
struct bpf_map *
-bpf_object__find_map_by_name(struct bpf_object *obj, const char *name)
+bpf_object__find_map_by_name(const struct bpf_object *obj, const char *name)
{
struct bpf_map *pos;
@@ -3440,7 +4772,7 @@ bpf_object__find_map_by_name(struct bpf_object *obj, const char *name)
}
int
-bpf_object__find_map_fd_by_name(struct bpf_object *obj, const char *name)
+bpf_object__find_map_fd_by_name(const struct bpf_object *obj, const char *name)
{
return bpf_map__fd(bpf_object__find_map_by_name(obj, name));
}
@@ -3448,20 +4780,12 @@ bpf_object__find_map_fd_by_name(struct bpf_object *obj, const char *name)
struct bpf_map *
bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset)
{
- int i;
-
- for (i = 0; i < obj->nr_maps; i++) {
- if (obj->maps[i].offset == offset)
- return &obj->maps[i];
- }
- return ERR_PTR(-ENOENT);
+ return ERR_PTR(-ENOTSUP);
}
long libbpf_get_error(const void *ptr)
{
- if (IS_ERR(ptr))
- return PTR_ERR(ptr);
- return 0;
+ return PTR_ERR_OR_ZERO(ptr);
}
int bpf_prog_load(const char *file, enum bpf_prog_type type,
@@ -3480,10 +4804,7 @@ int bpf_prog_load(const char *file, enum bpf_prog_type type,
int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
struct bpf_object **pobj, int *prog_fd)
{
- struct bpf_object_open_attr open_attr = {
- .file = attr->file,
- .prog_type = attr->prog_type,
- };
+ struct bpf_object_open_attr open_attr = {};
struct bpf_program *prog, *first_prog = NULL;
enum bpf_attach_type expected_attach_type;
enum bpf_prog_type prog_type;
@@ -3496,6 +4817,9 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
if (!attr->file)
return -EINVAL;
+ open_attr.file = attr->file;
+ open_attr.prog_type = attr->prog_type;
+
obj = bpf_object__open_xattr(&open_attr);
if (IS_ERR_OR_NULL(obj))
return -ENOENT;
@@ -3522,6 +4846,7 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
expected_attach_type);
prog->log_level = attr->log_level;
+ prog->prog_flags = attr->prog_flags;
if (!first_prog)
first_prog = prog;
}
@@ -3548,6 +4873,372 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
return 0;
}
+struct bpf_link {
+ int (*destroy)(struct bpf_link *link);
+};
+
+int bpf_link__destroy(struct bpf_link *link)
+{
+ int err;
+
+ if (!link)
+ return 0;
+
+ err = link->destroy(link);
+ free(link);
+
+ return err;
+}
+
+struct bpf_link_fd {
+ struct bpf_link link; /* has to be at the top of struct */
+ int fd; /* hook FD */
+};
+
+static int bpf_link__destroy_perf_event(struct bpf_link *link)
+{
+ struct bpf_link_fd *l = (void *)link;
+ int err;
+
+ err = ioctl(l->fd, PERF_EVENT_IOC_DISABLE, 0);
+ if (err)
+ err = -errno;
+
+ close(l->fd);
+ return err;
+}
+
+struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog,
+ int pfd)
+{
+ char errmsg[STRERR_BUFSIZE];
+ struct bpf_link_fd *link;
+ int prog_fd, err;
+
+ if (pfd < 0) {
+ pr_warning("program '%s': invalid perf event FD %d\n",
+ bpf_program__title(prog, false), pfd);
+ return ERR_PTR(-EINVAL);
+ }
+ prog_fd = bpf_program__fd(prog);
+ if (prog_fd < 0) {
+ pr_warning("program '%s': can't attach BPF program w/o FD (did you load it?)\n",
+ bpf_program__title(prog, false));
+ return ERR_PTR(-EINVAL);
+ }
+
+ link = malloc(sizeof(*link));
+ if (!link)
+ return ERR_PTR(-ENOMEM);
+ link->link.destroy = &bpf_link__destroy_perf_event;
+ link->fd = pfd;
+
+ if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, prog_fd) < 0) {
+ err = -errno;
+ free(link);
+ pr_warning("program '%s': failed to attach to pfd %d: %s\n",
+ bpf_program__title(prog, false), pfd,
+ libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
+ return ERR_PTR(err);
+ }
+ if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
+ err = -errno;
+ free(link);
+ pr_warning("program '%s': failed to enable pfd %d: %s\n",
+ bpf_program__title(prog, false), pfd,
+ libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
+ return ERR_PTR(err);
+ }
+ return (struct bpf_link *)link;
+}
+
+/*
+ * this function is expected to parse integer in the range of [0, 2^31-1] from
+ * given file using scanf format string fmt. If actual parsed value is
+ * negative, the result might be indistinguishable from error
+ */
+static int parse_uint_from_file(const char *file, const char *fmt)
+{
+ char buf[STRERR_BUFSIZE];
+ int err, ret;
+ FILE *f;
+
+ f = fopen(file, "r");
+ if (!f) {
+ err = -errno;
+ pr_debug("failed to open '%s': %s\n", file,
+ libbpf_strerror_r(err, buf, sizeof(buf)));
+ return err;
+ }
+ err = fscanf(f, fmt, &ret);
+ if (err != 1) {
+ err = err == EOF ? -EIO : -errno;
+ pr_debug("failed to parse '%s': %s\n", file,
+ libbpf_strerror_r(err, buf, sizeof(buf)));
+ fclose(f);
+ return err;
+ }
+ fclose(f);
+ return ret;
+}
+
+static int determine_kprobe_perf_type(void)
+{
+ const char *file = "/sys/bus/event_source/devices/kprobe/type";
+
+ return parse_uint_from_file(file, "%d\n");
+}
+
+static int determine_uprobe_perf_type(void)
+{
+ const char *file = "/sys/bus/event_source/devices/uprobe/type";
+
+ return parse_uint_from_file(file, "%d\n");
+}
+
+static int determine_kprobe_retprobe_bit(void)
+{
+ const char *file = "/sys/bus/event_source/devices/kprobe/format/retprobe";
+
+ return parse_uint_from_file(file, "config:%d\n");
+}
+
+static int determine_uprobe_retprobe_bit(void)
+{
+ const char *file = "/sys/bus/event_source/devices/uprobe/format/retprobe";
+
+ return parse_uint_from_file(file, "config:%d\n");
+}
+
+static int perf_event_open_probe(bool uprobe, bool retprobe, const char *name,
+ uint64_t offset, int pid)
+{
+ struct perf_event_attr attr = {};
+ char errmsg[STRERR_BUFSIZE];
+ int type, pfd, err;
+
+ type = uprobe ? determine_uprobe_perf_type()
+ : determine_kprobe_perf_type();
+ if (type < 0) {
+ pr_warning("failed to determine %s perf type: %s\n",
+ uprobe ? "uprobe" : "kprobe",
+ libbpf_strerror_r(type, errmsg, sizeof(errmsg)));
+ return type;
+ }
+ if (retprobe) {
+ int bit = uprobe ? determine_uprobe_retprobe_bit()
+ : determine_kprobe_retprobe_bit();
+
+ if (bit < 0) {
+ pr_warning("failed to determine %s retprobe bit: %s\n",
+ uprobe ? "uprobe" : "kprobe",
+ libbpf_strerror_r(bit, errmsg,
+ sizeof(errmsg)));
+ return bit;
+ }
+ attr.config |= 1 << bit;
+ }
+ attr.size = sizeof(attr);
+ attr.type = type;
+ attr.config1 = ptr_to_u64(name); /* kprobe_func or uprobe_path */
+ attr.config2 = offset; /* kprobe_addr or probe_offset */
+
+ /* pid filter is meaningful only for uprobes */
+ pfd = syscall(__NR_perf_event_open, &attr,
+ pid < 0 ? -1 : pid /* pid */,
+ pid == -1 ? 0 : -1 /* cpu */,
+ -1 /* group_fd */, PERF_FLAG_FD_CLOEXEC);
+ if (pfd < 0) {
+ err = -errno;
+ pr_warning("%s perf_event_open() failed: %s\n",
+ uprobe ? "uprobe" : "kprobe",
+ libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
+ return err;
+ }
+ return pfd;
+}
+
+struct bpf_link *bpf_program__attach_kprobe(struct bpf_program *prog,
+ bool retprobe,
+ const char *func_name)
+{
+ char errmsg[STRERR_BUFSIZE];
+ struct bpf_link *link;
+ int pfd, err;
+
+ pfd = perf_event_open_probe(false /* uprobe */, retprobe, func_name,
+ 0 /* offset */, -1 /* pid */);
+ if (pfd < 0) {
+ pr_warning("program '%s': failed to create %s '%s' perf event: %s\n",
+ bpf_program__title(prog, false),
+ retprobe ? "kretprobe" : "kprobe", func_name,
+ libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
+ return ERR_PTR(pfd);
+ }
+ link = bpf_program__attach_perf_event(prog, pfd);
+ if (IS_ERR(link)) {
+ close(pfd);
+ err = PTR_ERR(link);
+ pr_warning("program '%s': failed to attach to %s '%s': %s\n",
+ bpf_program__title(prog, false),
+ retprobe ? "kretprobe" : "kprobe", func_name,
+ libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
+ return link;
+ }
+ return link;
+}
+
+struct bpf_link *bpf_program__attach_uprobe(struct bpf_program *prog,
+ bool retprobe, pid_t pid,
+ const char *binary_path,
+ size_t func_offset)
+{
+ char errmsg[STRERR_BUFSIZE];
+ struct bpf_link *link;
+ int pfd, err;
+
+ pfd = perf_event_open_probe(true /* uprobe */, retprobe,
+ binary_path, func_offset, pid);
+ if (pfd < 0) {
+ pr_warning("program '%s': failed to create %s '%s:0x%zx' perf event: %s\n",
+ bpf_program__title(prog, false),
+ retprobe ? "uretprobe" : "uprobe",
+ binary_path, func_offset,
+ libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
+ return ERR_PTR(pfd);
+ }
+ link = bpf_program__attach_perf_event(prog, pfd);
+ if (IS_ERR(link)) {
+ close(pfd);
+ err = PTR_ERR(link);
+ pr_warning("program '%s': failed to attach to %s '%s:0x%zx': %s\n",
+ bpf_program__title(prog, false),
+ retprobe ? "uretprobe" : "uprobe",
+ binary_path, func_offset,
+ libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
+ return link;
+ }
+ return link;
+}
+
+static int determine_tracepoint_id(const char *tp_category,
+ const char *tp_name)
+{
+ char file[PATH_MAX];
+ int ret;
+
+ ret = snprintf(file, sizeof(file),
+ "/sys/kernel/debug/tracing/events/%s/%s/id",
+ tp_category, tp_name);
+ if (ret < 0)
+ return -errno;
+ if (ret >= sizeof(file)) {
+ pr_debug("tracepoint %s/%s path is too long\n",
+ tp_category, tp_name);
+ return -E2BIG;
+ }
+ return parse_uint_from_file(file, "%d\n");
+}
+
+static int perf_event_open_tracepoint(const char *tp_category,
+ const char *tp_name)
+{
+ struct perf_event_attr attr = {};
+ char errmsg[STRERR_BUFSIZE];
+ int tp_id, pfd, err;
+
+ tp_id = determine_tracepoint_id(tp_category, tp_name);
+ if (tp_id < 0) {
+ pr_warning("failed to determine tracepoint '%s/%s' perf event ID: %s\n",
+ tp_category, tp_name,
+ libbpf_strerror_r(tp_id, errmsg, sizeof(errmsg)));
+ return tp_id;
+ }
+
+ attr.type = PERF_TYPE_TRACEPOINT;
+ attr.size = sizeof(attr);
+ attr.config = tp_id;
+
+ pfd = syscall(__NR_perf_event_open, &attr, -1 /* pid */, 0 /* cpu */,
+ -1 /* group_fd */, PERF_FLAG_FD_CLOEXEC);
+ if (pfd < 0) {
+ err = -errno;
+ pr_warning("tracepoint '%s/%s' perf_event_open() failed: %s\n",
+ tp_category, tp_name,
+ libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
+ return err;
+ }
+ return pfd;
+}
+
+struct bpf_link *bpf_program__attach_tracepoint(struct bpf_program *prog,
+ const char *tp_category,
+ const char *tp_name)
+{
+ char errmsg[STRERR_BUFSIZE];
+ struct bpf_link *link;
+ int pfd, err;
+
+ pfd = perf_event_open_tracepoint(tp_category, tp_name);
+ if (pfd < 0) {
+ pr_warning("program '%s': failed to create tracepoint '%s/%s' perf event: %s\n",
+ bpf_program__title(prog, false),
+ tp_category, tp_name,
+ libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
+ return ERR_PTR(pfd);
+ }
+ link = bpf_program__attach_perf_event(prog, pfd);
+ if (IS_ERR(link)) {
+ close(pfd);
+ err = PTR_ERR(link);
+ pr_warning("program '%s': failed to attach to tracepoint '%s/%s': %s\n",
+ bpf_program__title(prog, false),
+ tp_category, tp_name,
+ libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
+ return link;
+ }
+ return link;
+}
+
+static int bpf_link__destroy_fd(struct bpf_link *link)
+{
+ struct bpf_link_fd *l = (void *)link;
+
+ return close(l->fd);
+}
+
+struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
+ const char *tp_name)
+{
+ char errmsg[STRERR_BUFSIZE];
+ struct bpf_link_fd *link;
+ int prog_fd, pfd;
+
+ prog_fd = bpf_program__fd(prog);
+ if (prog_fd < 0) {
+ pr_warning("program '%s': can't attach before loaded\n",
+ bpf_program__title(prog, false));
+ return ERR_PTR(-EINVAL);
+ }
+
+ link = malloc(sizeof(*link));
+ if (!link)
+ return ERR_PTR(-ENOMEM);
+ link->link.destroy = &bpf_link__destroy_fd;
+
+ pfd = bpf_raw_tracepoint_open(tp_name, prog_fd);
+ if (pfd < 0) {
+ pfd = -errno;
+ free(link);
+ pr_warning("program '%s': failed to attach to raw tracepoint '%s': %s\n",
+ bpf_program__title(prog, false), tp_name,
+ libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
+ return ERR_PTR(pfd);
+ }
+ link->fd = pfd;
+ return (struct bpf_link *)link;
+}
+
enum bpf_perf_event_ret
bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
void **copy_mem, size_t *copy_size,
@@ -3596,6 +5287,370 @@ bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
return ret;
}
+struct perf_buffer;
+
+struct perf_buffer_params {
+ struct perf_event_attr *attr;
+ /* if event_cb is specified, it takes precendence */
+ perf_buffer_event_fn event_cb;
+ /* sample_cb and lost_cb are higher-level common-case callbacks */
+ perf_buffer_sample_fn sample_cb;
+ perf_buffer_lost_fn lost_cb;
+ void *ctx;
+ int cpu_cnt;
+ int *cpus;
+ int *map_keys;
+};
+
+struct perf_cpu_buf {
+ struct perf_buffer *pb;
+ void *base; /* mmap()'ed memory */
+ void *buf; /* for reconstructing segmented data */
+ size_t buf_size;
+ int fd;
+ int cpu;
+ int map_key;
+};
+
+struct perf_buffer {
+ perf_buffer_event_fn event_cb;
+ perf_buffer_sample_fn sample_cb;
+ perf_buffer_lost_fn lost_cb;
+ void *ctx; /* passed into callbacks */
+
+ size_t page_size;
+ size_t mmap_size;
+ struct perf_cpu_buf **cpu_bufs;
+ struct epoll_event *events;
+ int cpu_cnt;
+ int epoll_fd; /* perf event FD */
+ int map_fd; /* BPF_MAP_TYPE_PERF_EVENT_ARRAY BPF map FD */
+};
+
+static void perf_buffer__free_cpu_buf(struct perf_buffer *pb,
+ struct perf_cpu_buf *cpu_buf)
+{
+ if (!cpu_buf)
+ return;
+ if (cpu_buf->base &&
+ munmap(cpu_buf->base, pb->mmap_size + pb->page_size))
+ pr_warning("failed to munmap cpu_buf #%d\n", cpu_buf->cpu);
+ if (cpu_buf->fd >= 0) {
+ ioctl(cpu_buf->fd, PERF_EVENT_IOC_DISABLE, 0);
+ close(cpu_buf->fd);
+ }
+ free(cpu_buf->buf);
+ free(cpu_buf);
+}
+
+void perf_buffer__free(struct perf_buffer *pb)
+{
+ int i;
+
+ if (!pb)
+ return;
+ if (pb->cpu_bufs) {
+ for (i = 0; i < pb->cpu_cnt && pb->cpu_bufs[i]; i++) {
+ struct perf_cpu_buf *cpu_buf = pb->cpu_bufs[i];
+
+ bpf_map_delete_elem(pb->map_fd, &cpu_buf->map_key);
+ perf_buffer__free_cpu_buf(pb, cpu_buf);
+ }
+ free(pb->cpu_bufs);
+ }
+ if (pb->epoll_fd >= 0)
+ close(pb->epoll_fd);
+ free(pb->events);
+ free(pb);
+}
+
+static struct perf_cpu_buf *
+perf_buffer__open_cpu_buf(struct perf_buffer *pb, struct perf_event_attr *attr,
+ int cpu, int map_key)
+{
+ struct perf_cpu_buf *cpu_buf;
+ char msg[STRERR_BUFSIZE];
+ int err;
+
+ cpu_buf = calloc(1, sizeof(*cpu_buf));
+ if (!cpu_buf)
+ return ERR_PTR(-ENOMEM);
+
+ cpu_buf->pb = pb;
+ cpu_buf->cpu = cpu;
+ cpu_buf->map_key = map_key;
+
+ cpu_buf->fd = syscall(__NR_perf_event_open, attr, -1 /* pid */, cpu,
+ -1, PERF_FLAG_FD_CLOEXEC);
+ if (cpu_buf->fd < 0) {
+ err = -errno;
+ pr_warning("failed to open perf buffer event on cpu #%d: %s\n",
+ cpu, libbpf_strerror_r(err, msg, sizeof(msg)));
+ goto error;
+ }
+
+ cpu_buf->base = mmap(NULL, pb->mmap_size + pb->page_size,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ cpu_buf->fd, 0);
+ if (cpu_buf->base == MAP_FAILED) {
+ cpu_buf->base = NULL;
+ err = -errno;
+ pr_warning("failed to mmap perf buffer on cpu #%d: %s\n",
+ cpu, libbpf_strerror_r(err, msg, sizeof(msg)));
+ goto error;
+ }
+
+ if (ioctl(cpu_buf->fd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
+ err = -errno;
+ pr_warning("failed to enable perf buffer event on cpu #%d: %s\n",
+ cpu, libbpf_strerror_r(err, msg, sizeof(msg)));
+ goto error;
+ }
+
+ return cpu_buf;
+
+error:
+ perf_buffer__free_cpu_buf(pb, cpu_buf);
+ return (struct perf_cpu_buf *)ERR_PTR(err);
+}
+
+static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt,
+ struct perf_buffer_params *p);
+
+struct perf_buffer *perf_buffer__new(int map_fd, size_t page_cnt,
+ const struct perf_buffer_opts *opts)
+{
+ struct perf_buffer_params p = {};
+ struct perf_event_attr attr = { 0, };
+
+ attr.config = PERF_COUNT_SW_BPF_OUTPUT,
+ attr.type = PERF_TYPE_SOFTWARE;
+ attr.sample_type = PERF_SAMPLE_RAW;
+ attr.sample_period = 1;
+ attr.wakeup_events = 1;
+
+ p.attr = &attr;
+ p.sample_cb = opts ? opts->sample_cb : NULL;
+ p.lost_cb = opts ? opts->lost_cb : NULL;
+ p.ctx = opts ? opts->ctx : NULL;
+
+ return __perf_buffer__new(map_fd, page_cnt, &p);
+}
+
+struct perf_buffer *
+perf_buffer__new_raw(int map_fd, size_t page_cnt,
+ const struct perf_buffer_raw_opts *opts)
+{
+ struct perf_buffer_params p = {};
+
+ p.attr = opts->attr;
+ p.event_cb = opts->event_cb;
+ p.ctx = opts->ctx;
+ p.cpu_cnt = opts->cpu_cnt;
+ p.cpus = opts->cpus;
+ p.map_keys = opts->map_keys;
+
+ return __perf_buffer__new(map_fd, page_cnt, &p);
+}
+
+static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt,
+ struct perf_buffer_params *p)
+{
+ struct bpf_map_info map = {};
+ char msg[STRERR_BUFSIZE];
+ struct perf_buffer *pb;
+ __u32 map_info_len;
+ int err, i;
+
+ if (page_cnt & (page_cnt - 1)) {
+ pr_warning("page count should be power of two, but is %zu\n",
+ page_cnt);
+ return ERR_PTR(-EINVAL);
+ }
+
+ map_info_len = sizeof(map);
+ err = bpf_obj_get_info_by_fd(map_fd, &map, &map_info_len);
+ if (err) {
+ err = -errno;
+ pr_warning("failed to get map info for map FD %d: %s\n",
+ map_fd, libbpf_strerror_r(err, msg, sizeof(msg)));
+ return ERR_PTR(err);
+ }
+
+ if (map.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) {
+ pr_warning("map '%s' should be BPF_MAP_TYPE_PERF_EVENT_ARRAY\n",
+ map.name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ pb = calloc(1, sizeof(*pb));
+ if (!pb)
+ return ERR_PTR(-ENOMEM);
+
+ pb->event_cb = p->event_cb;
+ pb->sample_cb = p->sample_cb;
+ pb->lost_cb = p->lost_cb;
+ pb->ctx = p->ctx;
+
+ pb->page_size = getpagesize();
+ pb->mmap_size = pb->page_size * page_cnt;
+ pb->map_fd = map_fd;
+
+ pb->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+ if (pb->epoll_fd < 0) {
+ err = -errno;
+ pr_warning("failed to create epoll instance: %s\n",
+ libbpf_strerror_r(err, msg, sizeof(msg)));
+ goto error;
+ }
+
+ if (p->cpu_cnt > 0) {
+ pb->cpu_cnt = p->cpu_cnt;
+ } else {
+ pb->cpu_cnt = libbpf_num_possible_cpus();
+ if (pb->cpu_cnt < 0) {
+ err = pb->cpu_cnt;
+ goto error;
+ }
+ if (map.max_entries < pb->cpu_cnt)
+ pb->cpu_cnt = map.max_entries;
+ }
+
+ pb->events = calloc(pb->cpu_cnt, sizeof(*pb->events));
+ if (!pb->events) {
+ err = -ENOMEM;
+ pr_warning("failed to allocate events: out of memory\n");
+ goto error;
+ }
+ pb->cpu_bufs = calloc(pb->cpu_cnt, sizeof(*pb->cpu_bufs));
+ if (!pb->cpu_bufs) {
+ err = -ENOMEM;
+ pr_warning("failed to allocate buffers: out of memory\n");
+ goto error;
+ }
+
+ for (i = 0; i < pb->cpu_cnt; i++) {
+ struct perf_cpu_buf *cpu_buf;
+ int cpu, map_key;
+
+ cpu = p->cpu_cnt > 0 ? p->cpus[i] : i;
+ map_key = p->cpu_cnt > 0 ? p->map_keys[i] : i;
+
+ cpu_buf = perf_buffer__open_cpu_buf(pb, p->attr, cpu, map_key);
+ if (IS_ERR(cpu_buf)) {
+ err = PTR_ERR(cpu_buf);
+ goto error;
+ }
+
+ pb->cpu_bufs[i] = cpu_buf;
+
+ err = bpf_map_update_elem(pb->map_fd, &map_key,
+ &cpu_buf->fd, 0);
+ if (err) {
+ err = -errno;
+ pr_warning("failed to set cpu #%d, key %d -> perf FD %d: %s\n",
+ cpu, map_key, cpu_buf->fd,
+ libbpf_strerror_r(err, msg, sizeof(msg)));
+ goto error;
+ }
+
+ pb->events[i].events = EPOLLIN;
+ pb->events[i].data.ptr = cpu_buf;
+ if (epoll_ctl(pb->epoll_fd, EPOLL_CTL_ADD, cpu_buf->fd,
+ &pb->events[i]) < 0) {
+ err = -errno;
+ pr_warning("failed to epoll_ctl cpu #%d perf FD %d: %s\n",
+ cpu, cpu_buf->fd,
+ libbpf_strerror_r(err, msg, sizeof(msg)));
+ goto error;
+ }
+ }
+
+ return pb;
+
+error:
+ if (pb)
+ perf_buffer__free(pb);
+ return ERR_PTR(err);
+}
+
+struct perf_sample_raw {
+ struct perf_event_header header;
+ uint32_t size;
+ char data[0];
+};
+
+struct perf_sample_lost {
+ struct perf_event_header header;
+ uint64_t id;
+ uint64_t lost;
+ uint64_t sample_id;
+};
+
+static enum bpf_perf_event_ret
+perf_buffer__process_record(struct perf_event_header *e, void *ctx)
+{
+ struct perf_cpu_buf *cpu_buf = ctx;
+ struct perf_buffer *pb = cpu_buf->pb;
+ void *data = e;
+
+ /* user wants full control over parsing perf event */
+ if (pb->event_cb)
+ return pb->event_cb(pb->ctx, cpu_buf->cpu, e);
+
+ switch (e->type) {
+ case PERF_RECORD_SAMPLE: {
+ struct perf_sample_raw *s = data;
+
+ if (pb->sample_cb)
+ pb->sample_cb(pb->ctx, cpu_buf->cpu, s->data, s->size);
+ break;
+ }
+ case PERF_RECORD_LOST: {
+ struct perf_sample_lost *s = data;
+
+ if (pb->lost_cb)
+ pb->lost_cb(pb->ctx, cpu_buf->cpu, s->lost);
+ break;
+ }
+ default:
+ pr_warning("unknown perf sample type %d\n", e->type);
+ return LIBBPF_PERF_EVENT_ERROR;
+ }
+ return LIBBPF_PERF_EVENT_CONT;
+}
+
+static int perf_buffer__process_records(struct perf_buffer *pb,
+ struct perf_cpu_buf *cpu_buf)
+{
+ enum bpf_perf_event_ret ret;
+
+ ret = bpf_perf_event_read_simple(cpu_buf->base, pb->mmap_size,
+ pb->page_size, &cpu_buf->buf,
+ &cpu_buf->buf_size,
+ perf_buffer__process_record, cpu_buf);
+ if (ret != LIBBPF_PERF_EVENT_CONT)
+ return ret;
+ return 0;
+}
+
+int perf_buffer__poll(struct perf_buffer *pb, int timeout_ms)
+{
+ int i, cnt, err;
+
+ cnt = epoll_wait(pb->epoll_fd, pb->events, pb->cpu_cnt, timeout_ms);
+ for (i = 0; i < cnt; i++) {
+ struct perf_cpu_buf *cpu_buf = pb->events[i].data.ptr;
+
+ err = perf_buffer__process_records(pb, cpu_buf);
+ if (err) {
+ pr_warning("error while processing records: %d\n", err);
+ return err;
+ }
+ }
+ return cnt < 0 ? -errno : cnt;
+}
+
struct bpf_prog_info_array_desc {
int array_offset; /* e.g. offset of jited_prog_insns */
int count_offset; /* e.g. offset of jited_prog_len */
@@ -3841,3 +5896,64 @@ void bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear)
desc->array_offset, addr);
}
}
+
+int libbpf_num_possible_cpus(void)
+{
+ static const char *fcpu = "/sys/devices/system/cpu/possible";
+ int len = 0, n = 0, il = 0, ir = 0;
+ unsigned int start = 0, end = 0;
+ int tmp_cpus = 0;
+ static int cpus;
+ char buf[128];
+ int error = 0;
+ int fd = -1;
+
+ tmp_cpus = READ_ONCE(cpus);
+ if (tmp_cpus > 0)
+ return tmp_cpus;
+
+ fd = open(fcpu, O_RDONLY);
+ if (fd < 0) {
+ error = errno;
+ pr_warning("Failed to open file %s: %s\n",
+ fcpu, strerror(error));
+ return -error;
+ }
+ len = read(fd, buf, sizeof(buf));
+ close(fd);
+ if (len <= 0) {
+ error = len ? errno : EINVAL;
+ pr_warning("Failed to read # of possible cpus from %s: %s\n",
+ fcpu, strerror(error));
+ return -error;
+ }
+ if (len == sizeof(buf)) {
+ pr_warning("File %s size overflow\n", fcpu);
+ return -EOVERFLOW;
+ }
+ buf[len] = '\0';
+
+ for (ir = 0, tmp_cpus = 0; ir <= len; ir++) {
+ /* Each sub string separated by ',' has format \d+-\d+ or \d+ */
+ if (buf[ir] == ',' || buf[ir] == '\0') {
+ buf[ir] = '\0';
+ n = sscanf(&buf[il], "%u-%u", &start, &end);
+ if (n <= 0) {
+ pr_warning("Failed to get # CPUs from %s\n",
+ &buf[il]);
+ return -EINVAL;
+ } else if (n == 1) {
+ end = start;
+ }
+ tmp_cpus += end - start + 1;
+ il = ir + 1;
+ }
+ }
+ if (tmp_cpus <= 0) {
+ pr_warning("Invalid #CPUs %d from %s\n", tmp_cpus, fcpu);
+ return -EINVAL;
+ }
+
+ WRITE_ONCE(cpus, tmp_cpus);
+ return tmp_cpus;
+}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index c5ff00515ce7..e8f70977d137 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -57,7 +57,7 @@ enum libbpf_print_level {
typedef int (*libbpf_print_fn_t)(enum libbpf_print_level level,
const char *, va_list ap);
-LIBBPF_API void libbpf_set_print(libbpf_print_fn_t fn);
+LIBBPF_API libbpf_print_fn_t libbpf_set_print(libbpf_print_fn_t fn);
/* Hide internal to user */
struct bpf_object;
@@ -89,18 +89,26 @@ LIBBPF_API int bpf_object__unpin_programs(struct bpf_object *obj,
LIBBPF_API int bpf_object__pin(struct bpf_object *object, const char *path);
LIBBPF_API void bpf_object__close(struct bpf_object *object);
+struct bpf_object_load_attr {
+ struct bpf_object *obj;
+ int log_level;
+ const char *target_btf_path;
+};
+
/* Load/unload object into/from kernel */
LIBBPF_API int bpf_object__load(struct bpf_object *obj);
+LIBBPF_API int bpf_object__load_xattr(struct bpf_object_load_attr *attr);
LIBBPF_API int bpf_object__unload(struct bpf_object *obj);
-LIBBPF_API const char *bpf_object__name(struct bpf_object *obj);
-LIBBPF_API unsigned int bpf_object__kversion(struct bpf_object *obj);
+LIBBPF_API const char *bpf_object__name(const struct bpf_object *obj);
+LIBBPF_API unsigned int bpf_object__kversion(const struct bpf_object *obj);
struct btf;
-LIBBPF_API struct btf *bpf_object__btf(struct bpf_object *obj);
+LIBBPF_API struct btf *bpf_object__btf(const struct bpf_object *obj);
LIBBPF_API int bpf_object__btf_fd(const struct bpf_object *obj);
LIBBPF_API struct bpf_program *
-bpf_object__find_program_by_title(struct bpf_object *obj, const char *title);
+bpf_object__find_program_by_title(const struct bpf_object *obj,
+ const char *title);
LIBBPF_API struct bpf_object *bpf_object__next(struct bpf_object *prev);
#define bpf_object__for_each_safe(pos, tmp) \
@@ -112,7 +120,7 @@ LIBBPF_API struct bpf_object *bpf_object__next(struct bpf_object *prev);
typedef void (*bpf_object_clear_priv_t)(struct bpf_object *, void *);
LIBBPF_API int bpf_object__set_priv(struct bpf_object *obj, void *priv,
bpf_object_clear_priv_t clear_priv);
-LIBBPF_API void *bpf_object__priv(struct bpf_object *prog);
+LIBBPF_API void *bpf_object__priv(const struct bpf_object *prog);
LIBBPF_API int
libbpf_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type,
@@ -123,7 +131,7 @@ LIBBPF_API int libbpf_attach_type_by_name(const char *name,
/* Accessors of bpf_program */
struct bpf_program;
LIBBPF_API struct bpf_program *bpf_program__next(struct bpf_program *prog,
- struct bpf_object *obj);
+ const struct bpf_object *obj);
#define bpf_object__for_each_program(pos, obj) \
for ((pos) = bpf_program__next(NULL, (obj)); \
@@ -131,24 +139,23 @@ LIBBPF_API struct bpf_program *bpf_program__next(struct bpf_program *prog,
(pos) = bpf_program__next((pos), (obj)))
LIBBPF_API struct bpf_program *bpf_program__prev(struct bpf_program *prog,
- struct bpf_object *obj);
+ const struct bpf_object *obj);
-typedef void (*bpf_program_clear_priv_t)(struct bpf_program *,
- void *);
+typedef void (*bpf_program_clear_priv_t)(struct bpf_program *, void *);
LIBBPF_API int bpf_program__set_priv(struct bpf_program *prog, void *priv,
bpf_program_clear_priv_t clear_priv);
-LIBBPF_API void *bpf_program__priv(struct bpf_program *prog);
+LIBBPF_API void *bpf_program__priv(const struct bpf_program *prog);
LIBBPF_API void bpf_program__set_ifindex(struct bpf_program *prog,
__u32 ifindex);
-LIBBPF_API const char *bpf_program__title(struct bpf_program *prog,
+LIBBPF_API const char *bpf_program__title(const struct bpf_program *prog,
bool needs_copy);
LIBBPF_API int bpf_program__load(struct bpf_program *prog, char *license,
__u32 kern_version);
-LIBBPF_API int bpf_program__fd(struct bpf_program *prog);
+LIBBPF_API int bpf_program__fd(const struct bpf_program *prog);
LIBBPF_API int bpf_program__pin_instance(struct bpf_program *prog,
const char *path,
int instance);
@@ -159,6 +166,27 @@ LIBBPF_API int bpf_program__pin(struct bpf_program *prog, const char *path);
LIBBPF_API int bpf_program__unpin(struct bpf_program *prog, const char *path);
LIBBPF_API void bpf_program__unload(struct bpf_program *prog);
+struct bpf_link;
+
+LIBBPF_API int bpf_link__destroy(struct bpf_link *link);
+
+LIBBPF_API struct bpf_link *
+bpf_program__attach_perf_event(struct bpf_program *prog, int pfd);
+LIBBPF_API struct bpf_link *
+bpf_program__attach_kprobe(struct bpf_program *prog, bool retprobe,
+ const char *func_name);
+LIBBPF_API struct bpf_link *
+bpf_program__attach_uprobe(struct bpf_program *prog, bool retprobe,
+ pid_t pid, const char *binary_path,
+ size_t func_offset);
+LIBBPF_API struct bpf_link *
+bpf_program__attach_tracepoint(struct bpf_program *prog,
+ const char *tp_category,
+ const char *tp_name);
+LIBBPF_API struct bpf_link *
+bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
+ const char *tp_name);
+
struct bpf_insn;
/*
@@ -221,7 +249,7 @@ typedef int (*bpf_program_prep_t)(struct bpf_program *prog, int n,
LIBBPF_API int bpf_program__set_prep(struct bpf_program *prog, int nr_instance,
bpf_program_prep_t prep);
-LIBBPF_API int bpf_program__nth_fd(struct bpf_program *prog, int n);
+LIBBPF_API int bpf_program__nth_fd(const struct bpf_program *prog, int n);
/*
* Adjust type of BPF program. Default is kprobe.
@@ -240,14 +268,14 @@ LIBBPF_API void
bpf_program__set_expected_attach_type(struct bpf_program *prog,
enum bpf_attach_type type);
-LIBBPF_API bool bpf_program__is_socket_filter(struct bpf_program *prog);
-LIBBPF_API bool bpf_program__is_tracepoint(struct bpf_program *prog);
-LIBBPF_API bool bpf_program__is_raw_tracepoint(struct bpf_program *prog);
-LIBBPF_API bool bpf_program__is_kprobe(struct bpf_program *prog);
-LIBBPF_API bool bpf_program__is_sched_cls(struct bpf_program *prog);
-LIBBPF_API bool bpf_program__is_sched_act(struct bpf_program *prog);
-LIBBPF_API bool bpf_program__is_xdp(struct bpf_program *prog);
-LIBBPF_API bool bpf_program__is_perf_event(struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_socket_filter(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_tracepoint(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_raw_tracepoint(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_kprobe(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_sched_cls(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_sched_act(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_xdp(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_perf_event(const struct bpf_program *prog);
/*
* No need for __attribute__((packed)), all members of 'bpf_map_def'
@@ -269,10 +297,10 @@ struct bpf_map_def {
*/
struct bpf_map;
LIBBPF_API struct bpf_map *
-bpf_object__find_map_by_name(struct bpf_object *obj, const char *name);
+bpf_object__find_map_by_name(const struct bpf_object *obj, const char *name);
LIBBPF_API int
-bpf_object__find_map_fd_by_name(struct bpf_object *obj, const char *name);
+bpf_object__find_map_fd_by_name(const struct bpf_object *obj, const char *name);
/*
* Get bpf_map through the offset of corresponding struct bpf_map_def
@@ -282,7 +310,7 @@ LIBBPF_API struct bpf_map *
bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset);
LIBBPF_API struct bpf_map *
-bpf_map__next(struct bpf_map *map, struct bpf_object *obj);
+bpf_map__next(const struct bpf_map *map, const struct bpf_object *obj);
#define bpf_object__for_each_map(pos, obj) \
for ((pos) = bpf_map__next(NULL, (obj)); \
(pos) != NULL; \
@@ -290,22 +318,22 @@ bpf_map__next(struct bpf_map *map, struct bpf_object *obj);
#define bpf_map__for_each bpf_object__for_each_map
LIBBPF_API struct bpf_map *
-bpf_map__prev(struct bpf_map *map, struct bpf_object *obj);
+bpf_map__prev(const struct bpf_map *map, const struct bpf_object *obj);
-LIBBPF_API int bpf_map__fd(struct bpf_map *map);
-LIBBPF_API const struct bpf_map_def *bpf_map__def(struct bpf_map *map);
-LIBBPF_API const char *bpf_map__name(struct bpf_map *map);
+LIBBPF_API int bpf_map__fd(const struct bpf_map *map);
+LIBBPF_API const struct bpf_map_def *bpf_map__def(const struct bpf_map *map);
+LIBBPF_API const char *bpf_map__name(const struct bpf_map *map);
LIBBPF_API __u32 bpf_map__btf_key_type_id(const struct bpf_map *map);
LIBBPF_API __u32 bpf_map__btf_value_type_id(const struct bpf_map *map);
typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *);
LIBBPF_API int bpf_map__set_priv(struct bpf_map *map, void *priv,
bpf_map_clear_priv_t clear_priv);
-LIBBPF_API void *bpf_map__priv(struct bpf_map *map);
+LIBBPF_API void *bpf_map__priv(const struct bpf_map *map);
LIBBPF_API int bpf_map__reuse_fd(struct bpf_map *map, int fd);
LIBBPF_API int bpf_map__resize(struct bpf_map *map, __u32 max_entries);
-LIBBPF_API bool bpf_map__is_offload_neutral(struct bpf_map *map);
-LIBBPF_API bool bpf_map__is_internal(struct bpf_map *map);
+LIBBPF_API bool bpf_map__is_offload_neutral(const struct bpf_map *map);
+LIBBPF_API bool bpf_map__is_internal(const struct bpf_map *map);
LIBBPF_API void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex);
LIBBPF_API int bpf_map__pin(struct bpf_map *map, const char *path);
LIBBPF_API int bpf_map__unpin(struct bpf_map *map, const char *path);
@@ -320,6 +348,7 @@ struct bpf_prog_load_attr {
enum bpf_attach_type expected_attach_type;
int ifindex;
int log_level;
+ int prog_flags;
};
LIBBPF_API int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
@@ -330,6 +359,26 @@ LIBBPF_API int bpf_prog_load(const char *file, enum bpf_prog_type type,
LIBBPF_API int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags);
LIBBPF_API int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags);
+struct perf_buffer;
+
+typedef void (*perf_buffer_sample_fn)(void *ctx, int cpu,
+ void *data, __u32 size);
+typedef void (*perf_buffer_lost_fn)(void *ctx, int cpu, __u64 cnt);
+
+/* common use perf buffer options */
+struct perf_buffer_opts {
+ /* if specified, sample_cb is called for each sample */
+ perf_buffer_sample_fn sample_cb;
+ /* if specified, lost_cb is called for each batch of lost samples */
+ perf_buffer_lost_fn lost_cb;
+ /* ctx is provided to sample_cb and lost_cb */
+ void *ctx;
+};
+
+LIBBPF_API struct perf_buffer *
+perf_buffer__new(int map_fd, size_t page_cnt,
+ const struct perf_buffer_opts *opts);
+
enum bpf_perf_event_ret {
LIBBPF_PERF_EVENT_DONE = 0,
LIBBPF_PERF_EVENT_ERROR = -1,
@@ -337,6 +386,35 @@ enum bpf_perf_event_ret {
};
struct perf_event_header;
+
+typedef enum bpf_perf_event_ret
+(*perf_buffer_event_fn)(void *ctx, int cpu, struct perf_event_header *event);
+
+/* raw perf buffer options, giving most power and control */
+struct perf_buffer_raw_opts {
+ /* perf event attrs passed directly into perf_event_open() */
+ struct perf_event_attr *attr;
+ /* raw event callback */
+ perf_buffer_event_fn event_cb;
+ /* ctx is provided to event_cb */
+ void *ctx;
+ /* if cpu_cnt == 0, open all on all possible CPUs (up to the number of
+ * max_entries of given PERF_EVENT_ARRAY map)
+ */
+ int cpu_cnt;
+ /* if cpu_cnt > 0, cpus is an array of CPUs to open ring buffers on */
+ int *cpus;
+ /* if cpu_cnt > 0, map_keys specify map keys to set per-CPU FDs for */
+ int *map_keys;
+};
+
+LIBBPF_API struct perf_buffer *
+perf_buffer__new_raw(int map_fd, size_t page_cnt,
+ const struct perf_buffer_raw_opts *opts);
+
+LIBBPF_API void perf_buffer__free(struct perf_buffer *pb);
+LIBBPF_API int perf_buffer__poll(struct perf_buffer *pb, int timeout_ms);
+
typedef enum bpf_perf_event_ret
(*bpf_perf_event_print_t)(struct perf_event_header *hdr,
void *private_data);
@@ -447,6 +525,22 @@ bpf_program__bpil_addr_to_offs(struct bpf_prog_info_linear *info_linear);
LIBBPF_API void
bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear);
+/*
+ * A helper function to get the number of possible CPUs before looking up
+ * per-CPU maps. Negative errno is returned on failure.
+ *
+ * Example usage:
+ *
+ * int ncpus = libbpf_num_possible_cpus();
+ * if (ncpus < 0) {
+ * // error handling
+ * }
+ * long values[ncpus];
+ * bpf_map_lookup_elem(per_cpu_map_fd, key, values);
+ *
+ */
+LIBBPF_API int libbpf_num_possible_cpus(void);
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index 673001787cba..d04c7cb623ed 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -164,3 +164,29 @@ LIBBPF_0.0.3 {
bpf_map_freeze;
btf__finalize_data;
} LIBBPF_0.0.2;
+
+LIBBPF_0.0.4 {
+ global:
+ bpf_link__destroy;
+ bpf_object__load_xattr;
+ bpf_program__attach_kprobe;
+ bpf_program__attach_perf_event;
+ bpf_program__attach_raw_tracepoint;
+ bpf_program__attach_tracepoint;
+ bpf_program__attach_uprobe;
+ btf_dump__dump_type;
+ btf_dump__free;
+ btf_dump__new;
+ btf__parse_elf;
+ libbpf_num_possible_cpus;
+ perf_buffer__free;
+ perf_buffer__new;
+ perf_buffer__new_raw;
+ perf_buffer__poll;
+ xsk_umem__create;
+} LIBBPF_0.0.3;
+
+LIBBPF_0.0.5 {
+ global:
+ bpf_btf_get_next_id;
+} LIBBPF_0.0.4;
diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
index 789e435b5900..2e83a34f8c79 100644
--- a/tools/lib/bpf/libbpf_internal.h
+++ b/tools/lib/bpf/libbpf_internal.h
@@ -9,6 +9,8 @@
#ifndef __LIBBPF_LIBBPF_INTERNAL_H
#define __LIBBPF_LIBBPF_INTERNAL_H
+#include "libbpf.h"
+
#define BTF_INFO_ENC(kind, kind_flag, vlen) \
((!!(kind_flag) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN))
#define BTF_TYPE_ENC(name, info, size_or_type) (name), (info), (size_or_type)
@@ -21,7 +23,132 @@
#define BTF_PARAM_ENC(name, type) (name), (type)
#define BTF_VAR_SECINFO_ENC(type, offset, size) (type), (offset), (size)
-int libbpf__probe_raw_btf(const char *raw_types, size_t types_len,
- const char *str_sec, size_t str_len);
+#ifndef min
+# define min(x, y) ((x) < (y) ? (x) : (y))
+#endif
+#ifndef max
+# define max(x, y) ((x) < (y) ? (y) : (x))
+#endif
+#ifndef offsetofend
+# define offsetofend(TYPE, FIELD) \
+ (offsetof(TYPE, FIELD) + sizeof(((TYPE *)0)->FIELD))
+#endif
+
+extern void libbpf_print(enum libbpf_print_level level,
+ const char *format, ...)
+ __attribute__((format(printf, 2, 3)));
+
+#define __pr(level, fmt, ...) \
+do { \
+ libbpf_print(level, "libbpf: " fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define pr_warning(fmt, ...) __pr(LIBBPF_WARN, fmt, ##__VA_ARGS__)
+#define pr_info(fmt, ...) __pr(LIBBPF_INFO, fmt, ##__VA_ARGS__)
+#define pr_debug(fmt, ...) __pr(LIBBPF_DEBUG, fmt, ##__VA_ARGS__)
+
+int libbpf__load_raw_btf(const char *raw_types, size_t types_len,
+ const char *str_sec, size_t str_len);
+
+struct btf_ext_info {
+ /*
+ * info points to the individual info section (e.g. func_info and
+ * line_info) from the .BTF.ext. It does not include the __u32 rec_size.
+ */
+ void *info;
+ __u32 rec_size;
+ __u32 len;
+};
+
+#define for_each_btf_ext_sec(seg, sec) \
+ for (sec = (seg)->info; \
+ (void *)sec < (seg)->info + (seg)->len; \
+ sec = (void *)sec + sizeof(struct btf_ext_info_sec) + \
+ (seg)->rec_size * sec->num_info)
+
+#define for_each_btf_ext_rec(seg, sec, i, rec) \
+ for (i = 0, rec = (void *)&(sec)->data; \
+ i < (sec)->num_info; \
+ i++, rec = (void *)rec + (seg)->rec_size)
+
+struct btf_ext {
+ union {
+ struct btf_ext_header *hdr;
+ void *data;
+ };
+ struct btf_ext_info func_info;
+ struct btf_ext_info line_info;
+ struct btf_ext_info offset_reloc_info;
+ __u32 data_size;
+};
+
+struct btf_ext_info_sec {
+ __u32 sec_name_off;
+ __u32 num_info;
+ /* Followed by num_info * record_size number of bytes */
+ __u8 data[0];
+};
+
+/* The minimum bpf_func_info checked by the loader */
+struct bpf_func_info_min {
+ __u32 insn_off;
+ __u32 type_id;
+};
+
+/* The minimum bpf_line_info checked by the loader */
+struct bpf_line_info_min {
+ __u32 insn_off;
+ __u32 file_name_off;
+ __u32 line_off;
+ __u32 line_col;
+};
+
+/* The minimum bpf_offset_reloc checked by the loader
+ *
+ * Offset relocation captures the following data:
+ * - insn_off - instruction offset (in bytes) within a BPF program that needs
+ * its insn->imm field to be relocated with actual offset;
+ * - type_id - BTF type ID of the "root" (containing) entity of a relocatable
+ * offset;
+ * - access_str_off - offset into corresponding .BTF string section. String
+ * itself encodes an accessed field using a sequence of field and array
+ * indicies, separated by colon (:). It's conceptually very close to LLVM's
+ * getelementptr ([0]) instruction's arguments for identifying offset to
+ * a field.
+ *
+ * Example to provide a better feel.
+ *
+ * struct sample {
+ * int a;
+ * struct {
+ * int b[10];
+ * };
+ * };
+ *
+ * struct sample *s = ...;
+ * int x = &s->a; // encoded as "0:0" (a is field #0)
+ * int y = &s->b[5]; // encoded as "0:1:0:5" (anon struct is field #1,
+ * // b is field #0 inside anon struct, accessing elem #5)
+ * int z = &s[10]->b; // encoded as "10:1" (ptr is used as an array)
+ *
+ * type_id for all relocs in this example will capture BTF type id of
+ * `struct sample`.
+ *
+ * Such relocation is emitted when using __builtin_preserve_access_index()
+ * Clang built-in, passing expression that captures field address, e.g.:
+ *
+ * bpf_probe_read(&dst, sizeof(dst),
+ * __builtin_preserve_access_index(&src->a.b.c));
+ *
+ * In this case Clang will emit offset relocation recording necessary data to
+ * be able to find offset of embedded `a.b.c` field within `src` struct.
+ *
+ * [0] https://llvm.org/docs/LangRef.html#getelementptr-instruction
+ */
+struct bpf_offset_reloc {
+ __u32 insn_off;
+ __u32 type_id;
+ __u32 access_str_off;
+};
#endif /* __LIBBPF_LIBBPF_INTERNAL_H */
diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
index 5e2aa83f637a..4b0b0364f5fc 100644
--- a/tools/lib/bpf/libbpf_probes.c
+++ b/tools/lib/bpf/libbpf_probes.c
@@ -101,6 +101,7 @@ probe_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns,
case BPF_PROG_TYPE_SK_REUSEPORT:
case BPF_PROG_TYPE_FLOW_DISSECTOR:
case BPF_PROG_TYPE_CGROUP_SYSCTL:
+ case BPF_PROG_TYPE_CGROUP_SOCKOPT:
default:
break;
}
@@ -133,8 +134,8 @@ bool bpf_probe_prog_type(enum bpf_prog_type prog_type, __u32 ifindex)
return errno != EINVAL && errno != EOPNOTSUPP;
}
-int libbpf__probe_raw_btf(const char *raw_types, size_t types_len,
- const char *str_sec, size_t str_len)
+int libbpf__load_raw_btf(const char *raw_types, size_t types_len,
+ const char *str_sec, size_t str_len)
{
struct btf_header hdr = {
.magic = BTF_MAGIC,
@@ -157,14 +158,9 @@ int libbpf__probe_raw_btf(const char *raw_types, size_t types_len,
memcpy(raw_btf + hdr.hdr_len + hdr.type_len, str_sec, hdr.str_len);
btf_fd = bpf_load_btf(raw_btf, btf_len, NULL, 0, false);
- if (btf_fd < 0) {
- free(raw_btf);
- return 0;
- }
- close(btf_fd);
free(raw_btf);
- return 1;
+ return btf_fd;
}
static int load_sk_storage_btf(void)
@@ -190,7 +186,7 @@ static int load_sk_storage_btf(void)
BTF_MEMBER_ENC(23, 2, 32),/* struct bpf_spin_lock l; */
};
- return libbpf__probe_raw_btf((char *)types, sizeof(types),
+ return libbpf__load_raw_btf((char *)types, sizeof(types),
strs, sizeof(strs));
}
@@ -248,6 +244,7 @@ bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex)
case BPF_MAP_TYPE_ARRAY_OF_MAPS:
case BPF_MAP_TYPE_HASH_OF_MAPS:
case BPF_MAP_TYPE_DEVMAP:
+ case BPF_MAP_TYPE_DEVMAP_HASH:
case BPF_MAP_TYPE_SOCKMAP:
case BPF_MAP_TYPE_CPUMAP:
case BPF_MAP_TYPE_XSKMAP:
diff --git a/tools/lib/bpf/libbpf_util.h b/tools/lib/bpf/libbpf_util.h
index da94c4cb2e4d..59c779c5790c 100644
--- a/tools/lib/bpf/libbpf_util.h
+++ b/tools/lib/bpf/libbpf_util.h
@@ -10,19 +10,6 @@
extern "C" {
#endif
-extern void libbpf_print(enum libbpf_print_level level,
- const char *format, ...)
- __attribute__((format(printf, 2, 3)));
-
-#define __pr(level, fmt, ...) \
-do { \
- libbpf_print(level, "libbpf: " fmt, ##__VA_ARGS__); \
-} while (0)
-
-#define pr_warning(fmt, ...) __pr(LIBBPF_WARN, fmt, ##__VA_ARGS__)
-#define pr_info(fmt, ...) __pr(LIBBPF_INFO, fmt, ##__VA_ARGS__)
-#define pr_debug(fmt, ...) __pr(LIBBPF_DEBUG, fmt, ##__VA_ARGS__)
-
/* Use these barrier functions instead of smp_[rw]mb() when they are
* used in a libbpf header file. That way they can be built into the
* application that uses libbpf.
diff --git a/tools/lib/bpf/str_error.c b/tools/lib/bpf/str_error.c
index 00e48ac5b806..b8064eedc177 100644
--- a/tools/lib/bpf/str_error.c
+++ b/tools/lib/bpf/str_error.c
@@ -11,7 +11,7 @@
*/
char *libbpf_strerror_r(int err, char *dst, int len)
{
- int ret = strerror_r(err, dst, len);
+ int ret = strerror_r(err < 0 ? -err : err, dst, len);
if (ret)
snprintf(dst, len, "ERROR: strerror_r(%d)=%d", err, ret);
return dst;
diff --git a/tools/lib/bpf/xsk.c b/tools/lib/bpf/xsk.c
index a3d1a302bc9c..842c4fd55859 100644
--- a/tools/lib/bpf/xsk.c
+++ b/tools/lib/bpf/xsk.c
@@ -29,7 +29,7 @@
#include "bpf.h"
#include "libbpf.h"
-#include "libbpf_util.h"
+#include "libbpf_internal.h"
#include "xsk.h"
#ifndef SOL_XDP
@@ -60,13 +60,12 @@ struct xsk_socket {
struct xsk_umem *umem;
struct xsk_socket_config config;
int fd;
- int xsks_map;
int ifindex;
int prog_fd;
- int qidconf_map_fd;
int xsks_map_fd;
__u32 queue_id;
char ifname[IFNAMSIZ];
+ bool zc;
};
struct xsk_nl_info {
@@ -75,23 +74,6 @@ struct xsk_nl_info {
int fd;
};
-/* For 32-bit systems, we need to use mmap2 as the offsets are 64-bit.
- * Unfortunately, it is not part of glibc.
- */
-static inline void *xsk_mmap(void *addr, size_t length, int prot, int flags,
- int fd, __u64 offset)
-{
-#ifdef __NR_mmap2
- unsigned int page_shift = __builtin_ffs(getpagesize()) - 1;
- long ret = syscall(__NR_mmap2, addr, length, prot, flags, fd,
- (off_t)(offset >> page_shift));
-
- return (void *)ret;
-#else
- return mmap(addr, length, prot, flags, fd, offset);
-#endif
-}
-
int xsk_umem__fd(const struct xsk_umem *umem)
{
return umem ? umem->fd : -EINVAL;
@@ -117,6 +99,7 @@ static void xsk_set_umem_config(struct xsk_umem_config *cfg,
cfg->comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS;
cfg->frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE;
cfg->frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM;
+ cfg->flags = XSK_UMEM__DEFAULT_FLAGS;
return;
}
@@ -124,6 +107,7 @@ static void xsk_set_umem_config(struct xsk_umem_config *cfg,
cfg->comp_size = usr_cfg->comp_size;
cfg->frame_size = usr_cfg->frame_size;
cfg->frame_headroom = usr_cfg->frame_headroom;
+ cfg->flags = usr_cfg->flags;
}
static int xsk_set_xdp_socket_config(struct xsk_socket_config *cfg,
@@ -150,9 +134,10 @@ static int xsk_set_xdp_socket_config(struct xsk_socket_config *cfg,
return 0;
}
-int xsk_umem__create(struct xsk_umem **umem_ptr, void *umem_area, __u64 size,
- struct xsk_ring_prod *fill, struct xsk_ring_cons *comp,
- const struct xsk_umem_config *usr_config)
+int xsk_umem__create_v0_0_4(struct xsk_umem **umem_ptr, void *umem_area,
+ __u64 size, struct xsk_ring_prod *fill,
+ struct xsk_ring_cons *comp,
+ const struct xsk_umem_config *usr_config)
{
struct xdp_mmap_offsets off;
struct xdp_umem_reg mr;
@@ -183,6 +168,7 @@ int xsk_umem__create(struct xsk_umem **umem_ptr, void *umem_area, __u64 size,
mr.len = size;
mr.chunk_size = umem->config.frame_size;
mr.headroom = umem->config.frame_headroom;
+ mr.flags = umem->config.flags;
err = setsockopt(umem->fd, SOL_XDP, XDP_UMEM_REG, &mr, sizeof(mr));
if (err) {
@@ -211,10 +197,9 @@ int xsk_umem__create(struct xsk_umem **umem_ptr, void *umem_area, __u64 size,
goto out_socket;
}
- map = xsk_mmap(NULL, off.fr.desc +
- umem->config.fill_size * sizeof(__u64),
- PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE,
- umem->fd, XDP_UMEM_PGOFF_FILL_RING);
+ map = mmap(NULL, off.fr.desc + umem->config.fill_size * sizeof(__u64),
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, umem->fd,
+ XDP_UMEM_PGOFF_FILL_RING);
if (map == MAP_FAILED) {
err = -errno;
goto out_socket;
@@ -225,13 +210,13 @@ int xsk_umem__create(struct xsk_umem **umem_ptr, void *umem_area, __u64 size,
fill->size = umem->config.fill_size;
fill->producer = map + off.fr.producer;
fill->consumer = map + off.fr.consumer;
+ fill->flags = map + off.fr.flags;
fill->ring = map + off.fr.desc;
fill->cached_cons = umem->config.fill_size;
- map = xsk_mmap(NULL,
- off.cr.desc + umem->config.comp_size * sizeof(__u64),
- PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE,
- umem->fd, XDP_UMEM_PGOFF_COMPLETION_RING);
+ map = mmap(NULL, off.cr.desc + umem->config.comp_size * sizeof(__u64),
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, umem->fd,
+ XDP_UMEM_PGOFF_COMPLETION_RING);
if (map == MAP_FAILED) {
err = -errno;
goto out_mmap;
@@ -242,6 +227,7 @@ int xsk_umem__create(struct xsk_umem **umem_ptr, void *umem_area, __u64 size,
comp->size = umem->config.comp_size;
comp->producer = map + off.cr.producer;
comp->consumer = map + off.cr.consumer;
+ comp->flags = map + off.cr.flags;
comp->ring = map + off.cr.desc;
*umem_ptr = umem;
@@ -256,6 +242,29 @@ out_umem_alloc:
return err;
}
+struct xsk_umem_config_v1 {
+ __u32 fill_size;
+ __u32 comp_size;
+ __u32 frame_size;
+ __u32 frame_headroom;
+};
+
+int xsk_umem__create_v0_0_2(struct xsk_umem **umem_ptr, void *umem_area,
+ __u64 size, struct xsk_ring_prod *fill,
+ struct xsk_ring_cons *comp,
+ const struct xsk_umem_config *usr_config)
+{
+ struct xsk_umem_config config;
+
+ memcpy(&config, usr_config, sizeof(struct xsk_umem_config_v1));
+ config.flags = 0;
+
+ return xsk_umem__create_v0_0_4(umem_ptr, umem_area, size, fill, comp,
+ &config);
+}
+asm(".symver xsk_umem__create_v0_0_2, xsk_umem__create@LIBBPF_0.0.2");
+asm(".symver xsk_umem__create_v0_0_4, xsk_umem__create@@LIBBPF_0.0.4");
+
static int xsk_load_xdp_prog(struct xsk_socket *xsk)
{
static const int log_buf_size = 16 * 1024;
@@ -265,15 +274,11 @@ static int xsk_load_xdp_prog(struct xsk_socket *xsk)
/* This is the C-program:
* SEC("xdp_sock") int xdp_sock_prog(struct xdp_md *ctx)
* {
- * int *qidconf, index = ctx->rx_queue_index;
+ * int index = ctx->rx_queue_index;
*
* // A set entry here means that the correspnding queue_id
* // has an active AF_XDP socket bound to it.
- * qidconf = bpf_map_lookup_elem(&qidconf_map, &index);
- * if (!qidconf)
- * return XDP_ABORTED;
- *
- * if (*qidconf)
+ * if (bpf_map_lookup_elem(&xsks_map, &index))
* return bpf_redirect_map(&xsks_map, index, 0);
*
* return XDP_PASS;
@@ -286,15 +291,10 @@ static int xsk_load_xdp_prog(struct xsk_socket *xsk)
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_1, -4),
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
- BPF_LD_MAP_FD(BPF_REG_1, xsk->qidconf_map_fd),
+ BPF_LD_MAP_FD(BPF_REG_1, xsk->xsks_map_fd),
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
- BPF_MOV32_IMM(BPF_REG_0, 0),
- /* if r1 == 0 goto +8 */
- BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 8),
BPF_MOV32_IMM(BPF_REG_0, 2),
- /* r1 = *(u32 *)(r1 + 0) */
- BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, 0),
/* if r1 == 0 goto +5 */
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 5),
/* r2 = *(u32 *)(r10 - 4) */
@@ -327,24 +327,24 @@ static int xsk_load_xdp_prog(struct xsk_socket *xsk)
static int xsk_get_max_queues(struct xsk_socket *xsk)
{
- struct ethtool_channels channels;
- struct ifreq ifr;
+ struct ethtool_channels channels = { .cmd = ETHTOOL_GCHANNELS };
+ struct ifreq ifr = {};
int fd, err, ret;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
return -errno;
- channels.cmd = ETHTOOL_GCHANNELS;
ifr.ifr_data = (void *)&channels;
- strncpy(ifr.ifr_name, xsk->ifname, IFNAMSIZ);
+ memcpy(ifr.ifr_name, xsk->ifname, IFNAMSIZ - 1);
+ ifr.ifr_name[IFNAMSIZ - 1] = '\0';
err = ioctl(fd, SIOCETHTOOL, &ifr);
if (err && errno != EOPNOTSUPP) {
ret = -errno;
goto out;
}
- if (channels.max_combined == 0 || errno == EOPNOTSUPP)
+ if (err || channels.max_combined == 0)
/* If the device says it has no channels, then all traffic
* is sent to a single stream, so max queues = 1.
*/
@@ -366,18 +366,11 @@ static int xsk_create_bpf_maps(struct xsk_socket *xsk)
if (max_queues < 0)
return max_queues;
- fd = bpf_create_map_name(BPF_MAP_TYPE_ARRAY, "qidconf_map",
+ fd = bpf_create_map_name(BPF_MAP_TYPE_XSKMAP, "xsks_map",
sizeof(int), sizeof(int), max_queues, 0);
if (fd < 0)
return fd;
- xsk->qidconf_map_fd = fd;
- fd = bpf_create_map_name(BPF_MAP_TYPE_XSKMAP, "xsks_map",
- sizeof(int), sizeof(int), max_queues, 0);
- if (fd < 0) {
- close(xsk->qidconf_map_fd);
- return fd;
- }
xsk->xsks_map_fd = fd;
return 0;
@@ -385,10 +378,8 @@ static int xsk_create_bpf_maps(struct xsk_socket *xsk)
static void xsk_delete_bpf_maps(struct xsk_socket *xsk)
{
- close(xsk->qidconf_map_fd);
+ bpf_map_delete_elem(xsk->xsks_map_fd, &xsk->queue_id);
close(xsk->xsks_map_fd);
- xsk->qidconf_map_fd = -1;
- xsk->xsks_map_fd = -1;
}
static int xsk_lookup_bpf_maps(struct xsk_socket *xsk)
@@ -417,10 +408,9 @@ static int xsk_lookup_bpf_maps(struct xsk_socket *xsk)
if (err)
goto out_map_ids;
- for (i = 0; i < prog_info.nr_map_ids; i++) {
- if (xsk->qidconf_map_fd != -1 && xsk->xsks_map_fd != -1)
- break;
+ xsk->xsks_map_fd = -1;
+ for (i = 0; i < prog_info.nr_map_ids; i++) {
fd = bpf_map_get_fd_by_id(map_ids[i]);
if (fd < 0)
continue;
@@ -431,11 +421,6 @@ static int xsk_lookup_bpf_maps(struct xsk_socket *xsk)
continue;
}
- if (!strcmp(map_info.name, "qidconf_map")) {
- xsk->qidconf_map_fd = fd;
- continue;
- }
-
if (!strcmp(map_info.name, "xsks_map")) {
xsk->xsks_map_fd = fd;
continue;
@@ -445,40 +430,18 @@ static int xsk_lookup_bpf_maps(struct xsk_socket *xsk)
}
err = 0;
- if (xsk->qidconf_map_fd < 0 || xsk->xsks_map_fd < 0) {
+ if (xsk->xsks_map_fd == -1)
err = -ENOENT;
- xsk_delete_bpf_maps(xsk);
- }
out_map_ids:
free(map_ids);
return err;
}
-static void xsk_clear_bpf_maps(struct xsk_socket *xsk)
-{
- int qid = false;
-
- bpf_map_update_elem(xsk->qidconf_map_fd, &xsk->queue_id, &qid, 0);
- bpf_map_delete_elem(xsk->xsks_map_fd, &xsk->queue_id);
-}
-
static int xsk_set_bpf_maps(struct xsk_socket *xsk)
{
- int qid = true, fd = xsk->fd, err;
-
- err = bpf_map_update_elem(xsk->qidconf_map_fd, &xsk->queue_id, &qid, 0);
- if (err)
- goto out;
-
- err = bpf_map_update_elem(xsk->xsks_map_fd, &xsk->queue_id, &fd, 0);
- if (err)
- goto out;
-
- return 0;
-out:
- xsk_clear_bpf_maps(xsk);
- return err;
+ return bpf_map_update_elem(xsk->xsks_map_fd, &xsk->queue_id,
+ &xsk->fd, 0);
}
static int xsk_setup_xdp_prog(struct xsk_socket *xsk)
@@ -497,26 +460,27 @@ static int xsk_setup_xdp_prog(struct xsk_socket *xsk)
return err;
err = xsk_load_xdp_prog(xsk);
- if (err)
- goto out_maps;
+ if (err) {
+ xsk_delete_bpf_maps(xsk);
+ return err;
+ }
} else {
xsk->prog_fd = bpf_prog_get_fd_by_id(prog_id);
err = xsk_lookup_bpf_maps(xsk);
- if (err)
- goto out_load;
+ if (err) {
+ close(xsk->prog_fd);
+ return err;
+ }
}
err = xsk_set_bpf_maps(xsk);
- if (err)
- goto out_load;
+ if (err) {
+ xsk_delete_bpf_maps(xsk);
+ close(xsk->prog_fd);
+ return err;
+ }
return 0;
-
-out_load:
- close(xsk->prog_fd);
-out_maps:
- xsk_delete_bpf_maps(xsk);
- return err;
}
int xsk_socket__create(struct xsk_socket **xsk_ptr, const char *ifname,
@@ -527,6 +491,7 @@ int xsk_socket__create(struct xsk_socket **xsk_ptr, const char *ifname,
void *rx_map = NULL, *tx_map = NULL;
struct sockaddr_xdp sxdp = {};
struct xdp_mmap_offsets off;
+ struct xdp_options opts;
struct xsk_socket *xsk;
socklen_t optlen;
int err;
@@ -561,7 +526,8 @@ int xsk_socket__create(struct xsk_socket **xsk_ptr, const char *ifname,
err = -errno;
goto out_socket;
}
- strncpy(xsk->ifname, ifname, IFNAMSIZ);
+ memcpy(xsk->ifname, ifname, IFNAMSIZ - 1);
+ xsk->ifname[IFNAMSIZ - 1] = '\0';
err = xsk_set_xdp_socket_config(&xsk->config, usr_config);
if (err)
@@ -594,11 +560,10 @@ int xsk_socket__create(struct xsk_socket **xsk_ptr, const char *ifname,
}
if (rx) {
- rx_map = xsk_mmap(NULL, off.rx.desc +
- xsk->config.rx_size * sizeof(struct xdp_desc),
- PROT_READ | PROT_WRITE,
- MAP_SHARED | MAP_POPULATE,
- xsk->fd, XDP_PGOFF_RX_RING);
+ rx_map = mmap(NULL, off.rx.desc +
+ xsk->config.rx_size * sizeof(struct xdp_desc),
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE,
+ xsk->fd, XDP_PGOFF_RX_RING);
if (rx_map == MAP_FAILED) {
err = -errno;
goto out_socket;
@@ -608,16 +573,16 @@ int xsk_socket__create(struct xsk_socket **xsk_ptr, const char *ifname,
rx->size = xsk->config.rx_size;
rx->producer = rx_map + off.rx.producer;
rx->consumer = rx_map + off.rx.consumer;
+ rx->flags = rx_map + off.rx.flags;
rx->ring = rx_map + off.rx.desc;
}
xsk->rx = rx;
if (tx) {
- tx_map = xsk_mmap(NULL, off.tx.desc +
- xsk->config.tx_size * sizeof(struct xdp_desc),
- PROT_READ | PROT_WRITE,
- MAP_SHARED | MAP_POPULATE,
- xsk->fd, XDP_PGOFF_TX_RING);
+ tx_map = mmap(NULL, off.tx.desc +
+ xsk->config.tx_size * sizeof(struct xdp_desc),
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE,
+ xsk->fd, XDP_PGOFF_TX_RING);
if (tx_map == MAP_FAILED) {
err = -errno;
goto out_mmap_rx;
@@ -627,6 +592,7 @@ int xsk_socket__create(struct xsk_socket **xsk_ptr, const char *ifname,
tx->size = xsk->config.tx_size;
tx->producer = tx_map + off.tx.producer;
tx->consumer = tx_map + off.tx.consumer;
+ tx->flags = tx_map + off.tx.flags;
tx->ring = tx_map + off.tx.desc;
tx->cached_cons = xsk->config.tx_size;
}
@@ -643,8 +609,16 @@ int xsk_socket__create(struct xsk_socket **xsk_ptr, const char *ifname,
goto out_mmap_tx;
}
- xsk->qidconf_map_fd = -1;
- xsk->xsks_map_fd = -1;
+ xsk->prog_fd = -1;
+
+ optlen = sizeof(opts);
+ err = getsockopt(xsk->fd, SOL_XDP, XDP_OPTIONS, &opts, &optlen);
+ if (err) {
+ err = -errno;
+ goto out_mmap_tx;
+ }
+
+ xsk->zc = opts.flags & XDP_OPTIONS_ZEROCOPY;
if (!(xsk->config.libbpf_flags & XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD)) {
err = xsk_setup_xdp_prog(xsk);
@@ -708,8 +682,10 @@ void xsk_socket__delete(struct xsk_socket *xsk)
if (!xsk)
return;
- xsk_clear_bpf_maps(xsk);
- xsk_delete_bpf_maps(xsk);
+ if (xsk->prog_fd != -1) {
+ xsk_delete_bpf_maps(xsk);
+ close(xsk->prog_fd);
+ }
optlen = sizeof(off);
err = getsockopt(xsk->fd, SOL_XDP, XDP_MMAP_OFFSETS, &off, &optlen);
diff --git a/tools/lib/bpf/xsk.h b/tools/lib/bpf/xsk.h
index 82ea71a0f3ec..584f6820a639 100644
--- a/tools/lib/bpf/xsk.h
+++ b/tools/lib/bpf/xsk.h
@@ -32,6 +32,7 @@ struct name { \
__u32 *producer; \
__u32 *consumer; \
void *ring; \
+ __u32 *flags; \
}
DEFINE_XSK_RING(xsk_ring_prod);
@@ -76,6 +77,11 @@ xsk_ring_cons__rx_desc(const struct xsk_ring_cons *rx, __u32 idx)
return &descs[idx & rx->mask];
}
+static inline int xsk_ring_prod__needs_wakeup(const struct xsk_ring_prod *r)
+{
+ return *r->flags & XDP_RING_NEED_WAKEUP;
+}
+
static inline __u32 xsk_prod_nb_free(struct xsk_ring_prod *r, __u32 nb)
{
__u32 free_entries = r->cached_cons - r->cached_prod;
@@ -162,20 +168,37 @@ static inline void *xsk_umem__get_data(void *umem_area, __u64 addr)
return &((char *)umem_area)[addr];
}
+static inline __u64 xsk_umem__extract_addr(__u64 addr)
+{
+ return addr & XSK_UNALIGNED_BUF_ADDR_MASK;
+}
+
+static inline __u64 xsk_umem__extract_offset(__u64 addr)
+{
+ return addr >> XSK_UNALIGNED_BUF_OFFSET_SHIFT;
+}
+
+static inline __u64 xsk_umem__add_offset_to_addr(__u64 addr)
+{
+ return xsk_umem__extract_addr(addr) + xsk_umem__extract_offset(addr);
+}
+
LIBBPF_API int xsk_umem__fd(const struct xsk_umem *umem);
LIBBPF_API int xsk_socket__fd(const struct xsk_socket *xsk);
#define XSK_RING_CONS__DEFAULT_NUM_DESCS 2048
#define XSK_RING_PROD__DEFAULT_NUM_DESCS 2048
-#define XSK_UMEM__DEFAULT_FRAME_SHIFT 11 /* 2048 bytes */
+#define XSK_UMEM__DEFAULT_FRAME_SHIFT 12 /* 4096 bytes */
#define XSK_UMEM__DEFAULT_FRAME_SIZE (1 << XSK_UMEM__DEFAULT_FRAME_SHIFT)
#define XSK_UMEM__DEFAULT_FRAME_HEADROOM 0
+#define XSK_UMEM__DEFAULT_FLAGS 0
struct xsk_umem_config {
__u32 fill_size;
__u32 comp_size;
__u32 frame_size;
__u32 frame_headroom;
+ __u32 flags;
};
/* Flags for the libbpf_flags field. */
@@ -195,6 +218,16 @@ LIBBPF_API int xsk_umem__create(struct xsk_umem **umem,
struct xsk_ring_prod *fill,
struct xsk_ring_cons *comp,
const struct xsk_umem_config *config);
+LIBBPF_API int xsk_umem__create_v0_0_2(struct xsk_umem **umem,
+ void *umem_area, __u64 size,
+ struct xsk_ring_prod *fill,
+ struct xsk_ring_cons *comp,
+ const struct xsk_umem_config *config);
+LIBBPF_API int xsk_umem__create_v0_0_4(struct xsk_umem **umem,
+ void *umem_area, __u64 size,
+ struct xsk_ring_prod *fill,
+ struct xsk_ring_cons *comp,
+ const struct xsk_umem_config *config);
LIBBPF_API int xsk_socket__create(struct xsk_socket **xsk,
const char *ifname, __u32 queue_id,
struct xsk_umem *umem,
diff --git a/tools/lib/ctype.c b/tools/lib/ctype.c
new file mode 100644
index 000000000000..4d2e05fd3336
--- /dev/null
+++ b/tools/lib/ctype.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * linux/lib/ctype.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <linux/ctype.h>
+#include <linux/compiler.h>
+
+const unsigned char _ctype[] = {
+_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */
+_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */
+_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */
+_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */
+_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */
+_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */
+_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */
+_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */
+_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */
+_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */
+_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */
+_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */
+_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */
+_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */
+_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */
+_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */
+_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */
+_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */
+_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */
+_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */
+_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */
+_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */
diff --git a/tools/lib/find_bit.c b/tools/lib/find_bit.c
index a88bd507091e..ac37022e9486 100644
--- a/tools/lib/find_bit.c
+++ b/tools/lib/find_bit.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* bit search implementation
*
* Copied from lib/find_bit.c to tools/lib/find_bit.c
@@ -11,11 +12,6 @@
*
* Rewritten by Yury Norov <yury.norov@gmail.com> to decrease
* size and improve performance, 2015.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include <linux/bitops.h>
diff --git a/tools/lib/rbtree.c b/tools/lib/rbtree.c
index 904adb70a4f0..804f145e3113 100644
--- a/tools/lib/rbtree.c
+++ b/tools/lib/rbtree.c
@@ -1,22 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
Red Black Trees
(C) 1999 Andrea Arcangeli <andrea@suse.de>
(C) 2002 David Woodhouse <dwmw2@infradead.org>
(C) 2012 Michel Lespinasse <walken@google.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
linux/lib/rbtree.c
*/
diff --git a/tools/lib/string.c b/tools/lib/string.c
index 93b3d4b6feac..f2ae1b87c719 100644
--- a/tools/lib/string.c
+++ b/tools/lib/string.c
@@ -17,6 +17,7 @@
#include <string.h>
#include <errno.h>
#include <linux/string.h>
+#include <linux/ctype.h>
#include <linux/compiler.h>
/**
@@ -106,3 +107,57 @@ size_t __weak strlcpy(char *dest, const char *src, size_t size)
}
return ret;
}
+
+/**
+ * skip_spaces - Removes leading whitespace from @str.
+ * @str: The string to be stripped.
+ *
+ * Returns a pointer to the first non-whitespace character in @str.
+ */
+char *skip_spaces(const char *str)
+{
+ while (isspace(*str))
+ ++str;
+ return (char *)str;
+}
+
+/**
+ * strim - Removes leading and trailing whitespace from @s.
+ * @s: The string to be stripped.
+ *
+ * Note that the first trailing whitespace is replaced with a %NUL-terminator
+ * in the given string @s. Returns a pointer to the first non-whitespace
+ * character in @s.
+ */
+char *strim(char *s)
+{
+ size_t size;
+ char *end;
+
+ size = strlen(s);
+ if (!size)
+ return s;
+
+ end = s + size - 1;
+ while (end >= s && isspace(*end))
+ end--;
+ *(end + 1) = '\0';
+
+ return skip_spaces(s);
+}
+
+/**
+ * strreplace - Replace all occurrences of character in string.
+ * @s: The string to operate on.
+ * @old: The character being replaced.
+ * @new: The character @old is replaced with.
+ *
+ * Returns pointer to the nul byte at the end of @s.
+ */
+char *strreplace(char *s, char old, char new)
+{
+ for (; *s; ++s)
+ if (*s == old)
+ *s = new;
+ return s;
+}
diff --git a/tools/lib/symbol/kallsyms.c b/tools/lib/symbol/kallsyms.c
index 96d830545bbb..1a7a9f877095 100644
--- a/tools/lib/symbol/kallsyms.c
+++ b/tools/lib/symbol/kallsyms.c
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
-#include <ctype.h>
#include "symbol/kallsyms.h"
#include <stdio.h>
#include <stdlib.h>
@@ -16,6 +15,19 @@ bool kallsyms__is_function(char symbol_type)
return symbol_type == 'T' || symbol_type == 'W';
}
+/*
+ * While we find nice hex chars, build a long_val.
+ * Return number of chars processed.
+ */
+int hex2u64(const char *ptr, u64 *long_val)
+{
+ char *p;
+
+ *long_val = strtoull(ptr, &p, 16);
+
+ return p - ptr;
+}
+
int kallsyms__parse(const char *filename, void *arg,
int (*process_symbol)(void *arg, const char *name,
char type, u64 start))
diff --git a/tools/lib/symbol/kallsyms.h b/tools/lib/symbol/kallsyms.h
index 72ab9870454b..bd988f7b18d4 100644
--- a/tools/lib/symbol/kallsyms.h
+++ b/tools/lib/symbol/kallsyms.h
@@ -18,6 +18,8 @@ static inline u8 kallsyms2elf_binding(char type)
return isupper(type) ? STB_GLOBAL : STB_LOCAL;
}
+int hex2u64(const char *ptr, u64 *long_val);
+
u8 kallsyms2elf_type(char type);
bool kallsyms__is_function(char symbol_type);
diff --git a/tools/lib/traceevent/Makefile b/tools/lib/traceevent/Makefile
index 3292c290654f..a39cdd0d890d 100644
--- a/tools/lib/traceevent/Makefile
+++ b/tools/lib/traceevent/Makefile
@@ -62,15 +62,15 @@ set_plugin_dir := 1
# Set plugin_dir to preffered global plugin location
# If we install under $HOME directory we go under
-# $(HOME)/.traceevent/plugins
+# $(HOME)/.local/lib/traceevent/plugins
#
# We dont set PLUGIN_DIR in case we install under $HOME
# directory, because by default the code looks under:
-# $(HOME)/.traceevent/plugins by default.
+# $(HOME)/.local/lib/traceevent/plugins by default.
#
ifeq ($(plugin_dir),)
ifeq ($(prefix),$(HOME))
-override plugin_dir = $(HOME)/.traceevent/plugins
+override plugin_dir = $(HOME)/.local/lib/traceevent/plugins
set_plugin_dir := 0
else
override plugin_dir = $(libdir)/traceevent/plugins
@@ -266,8 +266,8 @@ endef
define do_generate_dynamic_list_file
symbol_type=`$(NM) -u -D $1 | awk 'NF>1 {print $$1}' | \
- xargs echo "U W w" | tr ' ' '\n' | sort -u | xargs echo`;\
- if [ "$$symbol_type" = "U W w" ];then \
+ xargs echo "U w W" | tr 'w ' 'W\n' | sort -u | xargs echo`;\
+ if [ "$$symbol_type" = "U W" ];then \
(echo '{'; \
$(NM) -u -D $1 | awk 'NF>1 {print "\t"$$2";"}' | sort -u;\
echo '};'; \
diff --git a/tools/lib/traceevent/event-parse-api.c b/tools/lib/traceevent/event-parse-api.c
index 988587840c80..4faf52a65791 100644
--- a/tools/lib/traceevent/event-parse-api.c
+++ b/tools/lib/traceevent/event-parse-api.c
@@ -303,33 +303,6 @@ void tep_set_local_bigendian(struct tep_handle *tep, enum tep_endian endian)
}
/**
- * tep_is_latency_format - get if the latency output format is configured
- * @tep: a handle to the tep_handle
- *
- * This returns true if the latency output format is configured
- * If @tep is NULL, false is returned.
- */
-bool tep_is_latency_format(struct tep_handle *tep)
-{
- if (tep)
- return (tep->latency_format);
- return false;
-}
-
-/**
- * tep_set_latency_format - set the latency output format
- * @tep: a handle to the tep_handle
- * @lat: non zero for latency output format
- *
- * This sets the latency output format
- */
-void tep_set_latency_format(struct tep_handle *tep, int lat)
-{
- if (tep)
- tep->latency_format = lat;
-}
-
-/**
* tep_is_old_format - get if an old kernel is used
* @tep: a handle to the tep_handle
*
@@ -345,19 +318,6 @@ bool tep_is_old_format(struct tep_handle *tep)
}
/**
- * tep_set_print_raw - set a flag to force print in raw format
- * @tep: a handle to the tep_handle
- * @print_raw: the new value of the print_raw flag
- *
- * This sets a flag to force print in raw format
- */
-void tep_set_print_raw(struct tep_handle *tep, int print_raw)
-{
- if (tep)
- tep->print_raw = print_raw;
-}
-
-/**
* tep_set_test_filters - set a flag to test a filter string
* @tep: a handle to the tep_handle
* @test_filters: the new value of the test_filters flag
diff --git a/tools/lib/traceevent/event-parse-local.h b/tools/lib/traceevent/event-parse-local.h
index 09aa142f7fdd..cee469803a34 100644
--- a/tools/lib/traceevent/event-parse-local.h
+++ b/tools/lib/traceevent/event-parse-local.h
@@ -28,8 +28,6 @@ struct tep_handle {
enum tep_endian file_bigendian;
enum tep_endian host_bigendian;
- int latency_format;
-
int old_format;
int cpus;
@@ -70,8 +68,6 @@ struct tep_handle {
int ld_offset;
int ld_size;
- int print_raw;
-
int test_filters;
int flags;
@@ -85,8 +81,6 @@ struct tep_handle {
/* cache */
struct tep_event *last_event;
-
- char *trace_clock;
};
void tep_free_event(struct tep_event *event);
diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c
index b36b536a9fcb..bb22238debfe 100644
--- a/tools/lib/traceevent/event-parse.c
+++ b/tools/lib/traceevent/event-parse.c
@@ -142,6 +142,25 @@ static int cmdline_cmp(const void *a, const void *b)
return 0;
}
+/* Looking for where to place the key */
+static int cmdline_slot_cmp(const void *a, const void *b)
+{
+ const struct tep_cmdline *ca = a;
+ const struct tep_cmdline *cb = b;
+ const struct tep_cmdline *cb1 = cb + 1;
+
+ if (ca->pid < cb->pid)
+ return -1;
+
+ if (ca->pid > cb->pid) {
+ if (ca->pid <= cb1->pid)
+ return 0;
+ return 1;
+ }
+
+ return 0;
+}
+
struct cmdline_list {
struct cmdline_list *next;
char *comm;
@@ -239,6 +258,7 @@ static int add_new_comm(struct tep_handle *tep,
struct tep_cmdline *cmdline;
struct tep_cmdline key;
char *new_comm;
+ int cnt;
if (!pid)
return 0;
@@ -269,21 +289,43 @@ static int add_new_comm(struct tep_handle *tep,
errno = ENOMEM;
return -1;
}
+ tep->cmdlines = cmdlines;
- cmdlines[tep->cmdline_count].comm = strdup(comm);
- if (!cmdlines[tep->cmdline_count].comm) {
- free(cmdlines);
+ key.comm = strdup(comm);
+ if (!key.comm) {
errno = ENOMEM;
return -1;
}
- cmdlines[tep->cmdline_count].pid = pid;
-
- if (cmdlines[tep->cmdline_count].comm)
+ if (!tep->cmdline_count) {
+ /* no entries yet */
+ tep->cmdlines[0] = key;
tep->cmdline_count++;
+ return 0;
+ }
- qsort(cmdlines, tep->cmdline_count, sizeof(*cmdlines), cmdline_cmp);
- tep->cmdlines = cmdlines;
+ /* Now find where we want to store the new cmdline */
+ cmdline = bsearch(&key, tep->cmdlines, tep->cmdline_count - 1,
+ sizeof(*tep->cmdlines), cmdline_slot_cmp);
+
+ cnt = tep->cmdline_count;
+ if (cmdline) {
+ /* cmdline points to the one before the spot we want */
+ cmdline++;
+ cnt -= cmdline - tep->cmdlines;
+
+ } else {
+ /* The new entry is either before or after the list */
+ if (key.pid > tep->cmdlines[tep->cmdline_count - 1].pid) {
+ tep->cmdlines[tep->cmdline_count++] = key;
+ return 0;
+ }
+ cmdline = &tep->cmdlines[0];
+ }
+ memmove(cmdline + 1, cmdline, (cnt * sizeof(*cmdline)));
+ *cmdline = key;
+
+ tep->cmdline_count++;
return 0;
}
@@ -351,16 +393,6 @@ int tep_override_comm(struct tep_handle *tep, const char *comm, int pid)
return _tep_register_comm(tep, comm, pid, true);
}
-int tep_register_trace_clock(struct tep_handle *tep, const char *trace_clock)
-{
- tep->trace_clock = strdup(trace_clock);
- if (!tep->trace_clock) {
- errno = ENOMEM;
- return -1;
- }
- return 0;
-}
-
struct func_map {
unsigned long long addr;
char *func;
@@ -5170,24 +5202,20 @@ out_failed:
}
}
-/**
- * tep_data_latency_format - parse the data for the latency format
- * @tep: a handle to the trace event parser context
- * @s: the trace_seq to write to
- * @record: the record to read from
- *
+/*
* This parses out the Latency format (interrupts disabled,
* need rescheduling, in hard/soft interrupt, preempt count
* and lock depth) and places it into the trace_seq.
*/
-void tep_data_latency_format(struct tep_handle *tep,
- struct trace_seq *s, struct tep_record *record)
+static void data_latency_format(struct tep_handle *tep, struct trace_seq *s,
+ char *format, struct tep_record *record)
{
static int check_lock_depth = 1;
static int check_migrate_disable = 1;
static int lock_depth_exists;
static int migrate_disable_exists;
unsigned int lat_flags;
+ struct trace_seq sq;
unsigned int pc;
int lock_depth = 0;
int migrate_disable = 0;
@@ -5195,6 +5223,7 @@ void tep_data_latency_format(struct tep_handle *tep,
int softirq;
void *data = record->data;
+ trace_seq_init(&sq);
lat_flags = parse_common_flags(tep, data);
pc = parse_common_pc(tep, data);
/* lock_depth may not always exist */
@@ -5222,7 +5251,7 @@ void tep_data_latency_format(struct tep_handle *tep,
hardirq = lat_flags & TRACE_FLAG_HARDIRQ;
softirq = lat_flags & TRACE_FLAG_SOFTIRQ;
- trace_seq_printf(s, "%c%c%c",
+ trace_seq_printf(&sq, "%c%c%c",
(lat_flags & TRACE_FLAG_IRQS_OFF) ? 'd' :
(lat_flags & TRACE_FLAG_IRQS_NOSUPPORT) ?
'X' : '.',
@@ -5232,24 +5261,32 @@ void tep_data_latency_format(struct tep_handle *tep,
hardirq ? 'h' : softirq ? 's' : '.');
if (pc)
- trace_seq_printf(s, "%x", pc);
+ trace_seq_printf(&sq, "%x", pc);
else
- trace_seq_putc(s, '.');
+ trace_seq_printf(&sq, ".");
if (migrate_disable_exists) {
if (migrate_disable < 0)
- trace_seq_putc(s, '.');
+ trace_seq_printf(&sq, ".");
else
- trace_seq_printf(s, "%d", migrate_disable);
+ trace_seq_printf(&sq, "%d", migrate_disable);
}
if (lock_depth_exists) {
if (lock_depth < 0)
- trace_seq_putc(s, '.');
+ trace_seq_printf(&sq, ".");
else
- trace_seq_printf(s, "%d", lock_depth);
+ trace_seq_printf(&sq, "%d", lock_depth);
+ }
+
+ if (sq.state == TRACE_SEQ__MEM_ALLOC_FAILED) {
+ s->state = TRACE_SEQ__MEM_ALLOC_FAILED;
+ return;
}
+ trace_seq_terminate(&sq);
+ trace_seq_puts(s, sq.buffer);
+ trace_seq_destroy(&sq);
trace_seq_terminate(s);
}
@@ -5410,21 +5447,16 @@ int tep_cmdline_pid(struct tep_handle *tep, struct tep_cmdline *cmdline)
return cmdline->pid;
}
-/**
- * tep_event_info - parse the data into the print format
- * @s: the trace_seq to write to
- * @event: the handle to the event
- * @record: the record to read from
- *
+/*
* This parses the raw @data using the given @event information and
* writes the print format into the trace_seq.
*/
-void tep_event_info(struct trace_seq *s, struct tep_event *event,
- struct tep_record *record)
+static void print_event_info(struct trace_seq *s, char *format, bool raw,
+ struct tep_event *event, struct tep_record *record)
{
int print_pretty = 1;
- if (event->tep->print_raw || (event->flags & TEP_EVENT_FL_PRINTRAW))
+ if (raw || (event->flags & TEP_EVENT_FL_PRINTRAW))
tep_print_fields(s, record->data, record->size, event);
else {
@@ -5439,20 +5471,6 @@ void tep_event_info(struct trace_seq *s, struct tep_event *event,
trace_seq_terminate(s);
}
-static bool is_timestamp_in_us(char *trace_clock, bool use_trace_clock)
-{
- if (!trace_clock || !use_trace_clock)
- return true;
-
- if (!strcmp(trace_clock, "local") || !strcmp(trace_clock, "global")
- || !strcmp(trace_clock, "uptime") || !strcmp(trace_clock, "perf")
- || !strncmp(trace_clock, "mono", 4))
- return true;
-
- /* trace_clock is setting in tsc or counter mode */
- return false;
-}
-
/**
* tep_find_event_by_record - return the event from a given record
* @tep: a handle to the trace event parser context
@@ -5476,129 +5494,195 @@ tep_find_event_by_record(struct tep_handle *tep, struct tep_record *record)
return tep_find_event(tep, type);
}
-/**
- * tep_print_event_task - Write the event task comm, pid and CPU
- * @tep: a handle to the trace event parser context
- * @s: the trace_seq to write to
- * @event: the handle to the record's event
- * @record: The record to get the event from
- *
- * Writes the tasks comm, pid and CPU to @s.
+/*
+ * Writes the timestamp of the record into @s. Time divisor and precision can be
+ * specified as part of printf @format string. Example:
+ * "%3.1000d" - divide the time by 1000 and print the first 3 digits
+ * before the dot. Thus, the timestamp "123456000" will be printed as
+ * "123.456"
*/
-void tep_print_event_task(struct tep_handle *tep, struct trace_seq *s,
- struct tep_event *event,
- struct tep_record *record)
+static void print_event_time(struct tep_handle *tep, struct trace_seq *s,
+ char *format, struct tep_event *event,
+ struct tep_record *record)
+{
+ unsigned long long time;
+ char *divstr;
+ int prec = 0, pr;
+ int div = 0;
+ int p10 = 1;
+
+ if (isdigit(*(format + 1)))
+ prec = atoi(format + 1);
+ divstr = strchr(format, '.');
+ if (divstr && isdigit(*(divstr + 1)))
+ div = atoi(divstr + 1);
+ time = record->ts;
+ if (div)
+ time /= div;
+ pr = prec;
+ while (pr--)
+ p10 *= 10;
+
+ if (p10 > 1 && p10 < time)
+ trace_seq_printf(s, "%5llu.%0*llu", time / p10, prec, time % p10);
+ else
+ trace_seq_printf(s, "%12llu\n", time);
+}
+
+struct print_event_type {
+ enum {
+ EVENT_TYPE_INT = 1,
+ EVENT_TYPE_STRING,
+ EVENT_TYPE_UNKNOWN,
+ } type;
+ char format[32];
+};
+
+static void print_string(struct tep_handle *tep, struct trace_seq *s,
+ struct tep_record *record, struct tep_event *event,
+ const char *arg, struct print_event_type *type)
{
- void *data = record->data;
const char *comm;
int pid;
- pid = parse_common_pid(tep, data);
- comm = find_cmdline(tep, pid);
+ if (strncmp(arg, TEP_PRINT_LATENCY, strlen(TEP_PRINT_LATENCY)) == 0) {
+ data_latency_format(tep, s, type->format, record);
+ } else if (strncmp(arg, TEP_PRINT_COMM, strlen(TEP_PRINT_COMM)) == 0) {
+ pid = parse_common_pid(tep, record->data);
+ comm = find_cmdline(tep, pid);
+ trace_seq_printf(s, type->format, comm);
+ } else if (strncmp(arg, TEP_PRINT_INFO_RAW, strlen(TEP_PRINT_INFO_RAW)) == 0) {
+ print_event_info(s, type->format, true, event, record);
+ } else if (strncmp(arg, TEP_PRINT_INFO, strlen(TEP_PRINT_INFO)) == 0) {
+ print_event_info(s, type->format, false, event, record);
+ } else if (strncmp(arg, TEP_PRINT_NAME, strlen(TEP_PRINT_NAME)) == 0) {
+ trace_seq_printf(s, type->format, event->name);
+ } else {
+ trace_seq_printf(s, "[UNKNOWN TEP TYPE %s]", arg);
+ }
- if (tep->latency_format)
- trace_seq_printf(s, "%8.8s-%-5d %3d", comm, pid, record->cpu);
- else
- trace_seq_printf(s, "%16s-%-5d [%03d]", comm, pid, record->cpu);
}
-/**
- * tep_print_event_time - Write the event timestamp
- * @tep: a handle to the trace event parser context
- * @s: the trace_seq to write to
- * @event: the handle to the record's event
- * @record: The record to get the event from
- * @use_trace_clock: Set to parse according to the @tep->trace_clock
- *
- * Writes the timestamp of the record into @s.
- */
-void tep_print_event_time(struct tep_handle *tep, struct trace_seq *s,
- struct tep_event *event,
- struct tep_record *record,
- bool use_trace_clock)
+static void print_int(struct tep_handle *tep, struct trace_seq *s,
+ struct tep_record *record, struct tep_event *event,
+ int arg, struct print_event_type *type)
{
- unsigned long secs;
- unsigned long usecs;
- unsigned long nsecs;
- int p;
- bool use_usec_format;
+ int param;
- use_usec_format = is_timestamp_in_us(tep->trace_clock, use_trace_clock);
- if (use_usec_format) {
- secs = record->ts / NSEC_PER_SEC;
- nsecs = record->ts - secs * NSEC_PER_SEC;
+ switch (arg) {
+ case TEP_PRINT_CPU:
+ param = record->cpu;
+ break;
+ case TEP_PRINT_PID:
+ param = parse_common_pid(tep, record->data);
+ break;
+ case TEP_PRINT_TIME:
+ return print_event_time(tep, s, type->format, event, record);
+ default:
+ return;
}
+ trace_seq_printf(s, type->format, param);
+}
- if (tep->latency_format) {
- tep_data_latency_format(tep, s, record);
- }
+static int tep_print_event_param_type(char *format,
+ struct print_event_type *type)
+{
+ char *str = format + 1;
+ int i = 1;
- if (use_usec_format) {
- if (tep->flags & TEP_NSEC_OUTPUT) {
- usecs = nsecs;
- p = 9;
- } else {
- usecs = (nsecs + 500) / NSEC_PER_USEC;
- /* To avoid usecs larger than 1 sec */
- if (usecs >= USEC_PER_SEC) {
- usecs -= USEC_PER_SEC;
- secs++;
- }
- p = 6;
+ type->type = EVENT_TYPE_UNKNOWN;
+ while (*str) {
+ switch (*str) {
+ case 'd':
+ case 'u':
+ case 'i':
+ case 'x':
+ case 'X':
+ case 'o':
+ type->type = EVENT_TYPE_INT;
+ break;
+ case 's':
+ type->type = EVENT_TYPE_STRING;
+ break;
}
-
- trace_seq_printf(s, " %5lu.%0*lu:", secs, p, usecs);
- } else
- trace_seq_printf(s, " %12llu:", record->ts);
+ str++;
+ i++;
+ if (type->type != EVENT_TYPE_UNKNOWN)
+ break;
+ }
+ memset(type->format, 0, 32);
+ memcpy(type->format, format, i < 32 ? i : 31);
+ return i;
}
/**
- * tep_print_event_data - Write the event data section
+ * tep_print_event - Write various event information
* @tep: a handle to the trace event parser context
* @s: the trace_seq to write to
- * @event: the handle to the record's event
* @record: The record to get the event from
- *
- * Writes the parsing of the record's data to @s.
+ * @format: a printf format string. Supported event fileds:
+ * TEP_PRINT_PID, "%d" - event PID
+ * TEP_PRINT_CPU, "%d" - event CPU
+ * TEP_PRINT_COMM, "%s" - event command string
+ * TEP_PRINT_NAME, "%s" - event name
+ * TEP_PRINT_LATENCY, "%s" - event latency
+ * TEP_PRINT_TIME, %d - event time stamp. A divisor and precision
+ * can be specified as part of this format string:
+ * "%precision.divisord". Example:
+ * "%3.1000d" - divide the time by 1000 and print the first
+ * 3 digits before the dot. Thus, the time stamp
+ * "123456000" will be printed as "123.456"
+ * TEP_PRINT_INFO, "%s" - event information. If any width is specified in
+ * the format string, the event information will be printed
+ * in raw format.
+ * Writes the specified event information into @s.
*/
-void tep_print_event_data(struct tep_handle *tep, struct trace_seq *s,
- struct tep_event *event,
- struct tep_record *record)
-{
- static const char *spaces = " "; /* 20 spaces */
- int len;
-
- trace_seq_printf(s, " %s: ", event->name);
-
- /* Space out the event names evenly. */
- len = strlen(event->name);
- if (len < 20)
- trace_seq_printf(s, "%.*s", 20 - len, spaces);
-
- tep_event_info(s, event, record);
-}
-
void tep_print_event(struct tep_handle *tep, struct trace_seq *s,
- struct tep_record *record, bool use_trace_clock)
-{
+ struct tep_record *record, const char *fmt, ...)
+{
+ struct print_event_type type;
+ char *format = strdup(fmt);
+ char *current = format;
+ char *str = format;
+ int offset;
+ va_list args;
struct tep_event *event;
- event = tep_find_event_by_record(tep, record);
- if (!event) {
- int i;
- int type = trace_parse_common_type(tep, record->data);
-
- do_warning("ug! no event found for type %d", type);
- trace_seq_printf(s, "[UNKNOWN TYPE %d]", type);
- for (i = 0; i < record->size; i++)
- trace_seq_printf(s, " %02x",
- ((unsigned char *)record->data)[i]);
+ if (!format)
return;
- }
- tep_print_event_task(tep, s, event, record);
- tep_print_event_time(tep, s, event, record, use_trace_clock);
- tep_print_event_data(tep, s, event, record);
+ event = tep_find_event_by_record(tep, record);
+ va_start(args, fmt);
+ while (*current) {
+ current = strchr(str, '%');
+ if (!current) {
+ trace_seq_puts(s, str);
+ break;
+ }
+ memset(&type, 0, sizeof(type));
+ offset = tep_print_event_param_type(current, &type);
+ *current = '\0';
+ trace_seq_puts(s, str);
+ current += offset;
+ switch (type.type) {
+ case EVENT_TYPE_STRING:
+ print_string(tep, s, record, event,
+ va_arg(args, char*), &type);
+ break;
+ case EVENT_TYPE_INT:
+ print_int(tep, s, record, event,
+ va_arg(args, int), &type);
+ break;
+ case EVENT_TYPE_UNKNOWN:
+ default:
+ trace_seq_printf(s, "[UNKNOWN TYPE]");
+ break;
+ }
+ str = current;
+
+ }
+ va_end(args);
+ free(format);
}
static int events_id_cmp(const void *a, const void *b)
@@ -6963,7 +7047,6 @@ void tep_free(struct tep_handle *tep)
free_handler(handle);
}
- free(tep->trace_clock);
free(tep->events);
free(tep->sort_events);
free(tep->func_resolver);
diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h
index 642f68ab5fb2..d438ee44289f 100644
--- a/tools/lib/traceevent/event-parse.h
+++ b/tools/lib/traceevent/event-parse.h
@@ -435,25 +435,24 @@ int tep_set_function_resolver(struct tep_handle *tep,
void tep_reset_function_resolver(struct tep_handle *tep);
int tep_register_comm(struct tep_handle *tep, const char *comm, int pid);
int tep_override_comm(struct tep_handle *tep, const char *comm, int pid);
-int tep_register_trace_clock(struct tep_handle *tep, const char *trace_clock);
int tep_register_function(struct tep_handle *tep, char *name,
unsigned long long addr, char *mod);
int tep_register_print_string(struct tep_handle *tep, const char *fmt,
unsigned long long addr);
bool tep_is_pid_registered(struct tep_handle *tep, int pid);
-void tep_print_event_task(struct tep_handle *tep, struct trace_seq *s,
- struct tep_event *event,
- struct tep_record *record);
-void tep_print_event_time(struct tep_handle *tep, struct trace_seq *s,
- struct tep_event *event,
- struct tep_record *record,
- bool use_trace_clock);
-void tep_print_event_data(struct tep_handle *tep, struct trace_seq *s,
- struct tep_event *event,
- struct tep_record *record);
+#define TEP_PRINT_INFO "INFO"
+#define TEP_PRINT_INFO_RAW "INFO_RAW"
+#define TEP_PRINT_COMM "COMM"
+#define TEP_PRINT_LATENCY "LATENCY"
+#define TEP_PRINT_NAME "NAME"
+#define TEP_PRINT_PID 1U
+#define TEP_PRINT_TIME 2U
+#define TEP_PRINT_CPU 3U
+
void tep_print_event(struct tep_handle *tep, struct trace_seq *s,
- struct tep_record *record, bool use_trace_clock);
+ struct tep_record *record, const char *fmt, ...)
+ __attribute__ ((format (printf, 4, 5)));
int tep_parse_header_page(struct tep_handle *tep, char *buf, unsigned long size,
int long_size);
@@ -525,8 +524,6 @@ tep_find_event_by_name(struct tep_handle *tep, const char *sys, const char *name
struct tep_event *
tep_find_event_by_record(struct tep_handle *tep, struct tep_record *record);
-void tep_data_latency_format(struct tep_handle *tep,
- struct trace_seq *s, struct tep_record *record);
int tep_data_type(struct tep_handle *tep, struct tep_record *rec);
int tep_data_pid(struct tep_handle *tep, struct tep_record *rec);
int tep_data_preempt_count(struct tep_handle *tep, struct tep_record *rec);
@@ -541,8 +538,6 @@ void tep_print_field(struct trace_seq *s, void *data,
struct tep_format_field *field);
void tep_print_fields(struct trace_seq *s, void *data,
int size __maybe_unused, struct tep_event *event);
-void tep_event_info(struct trace_seq *s, struct tep_event *event,
- struct tep_record *record);
int tep_strerror(struct tep_handle *tep, enum tep_errno errnum,
char *buf, size_t buflen);
@@ -566,12 +561,9 @@ bool tep_is_file_bigendian(struct tep_handle *tep);
void tep_set_file_bigendian(struct tep_handle *tep, enum tep_endian endian);
bool tep_is_local_bigendian(struct tep_handle *tep);
void tep_set_local_bigendian(struct tep_handle *tep, enum tep_endian endian);
-bool tep_is_latency_format(struct tep_handle *tep);
-void tep_set_latency_format(struct tep_handle *tep, int lat);
int tep_get_header_page_size(struct tep_handle *tep);
int tep_get_header_timestamp_size(struct tep_handle *tep);
bool tep_is_old_format(struct tep_handle *tep);
-void tep_set_print_raw(struct tep_handle *tep, int print_raw);
void tep_set_test_filters(struct tep_handle *tep, int test_filters);
struct tep_handle *tep_alloc(void);
diff --git a/tools/lib/traceevent/event-plugin.c b/tools/lib/traceevent/event-plugin.c
index 8ca28de9337a..e1f7ddd5a6cf 100644
--- a/tools/lib/traceevent/event-plugin.c
+++ b/tools/lib/traceevent/event-plugin.c
@@ -18,7 +18,7 @@
#include "event-utils.h"
#include "trace-seq.h"
-#define LOCAL_PLUGIN_DIR ".traceevent/plugins"
+#define LOCAL_PLUGIN_DIR ".local/lib/traceevent/plugins/"
static struct registered_plugin_options {
struct registered_plugin_options *next;
diff --git a/tools/lib/vsprintf.c b/tools/lib/vsprintf.c
index e08ee147eab4..8780b4cdab21 100644
--- a/tools/lib/vsprintf.c
+++ b/tools/lib/vsprintf.c
@@ -23,3 +23,22 @@ int scnprintf(char * buf, size_t size, const char * fmt, ...)
return (i >= ssize) ? (ssize - 1) : i;
}
+
+int scnprintf_pad(char * buf, size_t size, const char * fmt, ...)
+{
+ ssize_t ssize = size;
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i = vscnprintf(buf, size, fmt, args);
+ va_end(args);
+
+ if (i < (int) size) {
+ for (; i < (int) size; i++)
+ buf[i] = ' ';
+ buf[i] = 0x0;
+ }
+
+ return (i >= ssize) ? (ssize - 1) : i;
+}
diff --git a/tools/lib/zalloc.c b/tools/lib/zalloc.c
new file mode 100644
index 000000000000..9c856d59f56e
--- /dev/null
+++ b/tools/lib/zalloc.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: LGPL-2.1
+
+#include <stdlib.h>
+#include <linux/zalloc.h>
+
+void *zalloc(size_t size)
+{
+ return calloc(1, size);
+}
+
+void __zfree(void **ptr)
+{
+ free(*ptr);
+ *ptr = NULL;
+}
diff --git a/tools/memory-model/Documentation/explanation.txt b/tools/memory-model/Documentation/explanation.txt
index 68caa9a976d0..488f11f6c588 100644
--- a/tools/memory-model/Documentation/explanation.txt
+++ b/tools/memory-model/Documentation/explanation.txt
@@ -42,7 +42,8 @@ linux-kernel.bell and linux-kernel.cat files that make up the formal
version of the model; they are extremely terse and their meanings are
far from clear.
-This document describes the ideas underlying the LKMM. It is meant
+This document describes the ideas underlying the LKMM, but excluding
+the modeling of bare C (or plain) shared memory accesses. It is meant
for people who want to understand how the model was designed. It does
not go into the details of the code in the .bell and .cat files;
rather, it explains in English what the code expresses symbolically.
@@ -354,31 +355,25 @@ be extremely complex.
Optimizing compilers have great freedom in the way they translate
source code to object code. They are allowed to apply transformations
that add memory accesses, eliminate accesses, combine them, split them
-into pieces, or move them around. Faced with all these possibilities,
-the LKMM basically gives up. It insists that the code it analyzes
-must contain no ordinary accesses to shared memory; all accesses must
-be performed using READ_ONCE(), WRITE_ONCE(), or one of the other
-atomic or synchronization primitives. These primitives prevent a
-large number of compiler optimizations. In particular, it is
-guaranteed that the compiler will not remove such accesses from the
-generated code (unless it can prove the accesses will never be
-executed), it will not change the order in which they occur in the
-code (within limits imposed by the C standard), and it will not
-introduce extraneous accesses.
-
-This explains why the MP and SB examples above used READ_ONCE() and
-WRITE_ONCE() rather than ordinary memory accesses. Thanks to this
-usage, we can be certain that in the MP example, P0's write event to
-buf really is po-before its write event to flag, and similarly for the
-other shared memory accesses in the examples.
-
-Private variables are not subject to this restriction. Since they are
-not shared between CPUs, they can be accessed normally without
-READ_ONCE() or WRITE_ONCE(), and there will be no ill effects. In
-fact, they need not even be stored in normal memory at all -- in
-principle a private variable could be stored in a CPU register (hence
-the convention that these variables have names starting with the
-letter 'r').
+into pieces, or move them around. The use of READ_ONCE(), WRITE_ONCE(),
+or one of the other atomic or synchronization primitives prevents a
+large number of compiler optimizations. In particular, it is guaranteed
+that the compiler will not remove such accesses from the generated code
+(unless it can prove the accesses will never be executed), it will not
+change the order in which they occur in the code (within limits imposed
+by the C standard), and it will not introduce extraneous accesses.
+
+The MP and SB examples above used READ_ONCE() and WRITE_ONCE() rather
+than ordinary memory accesses. Thanks to this usage, we can be certain
+that in the MP example, the compiler won't reorder P0's write event to
+buf and P0's write event to flag, and similarly for the other shared
+memory accesses in the examples.
+
+Since private variables are not shared between CPUs, they can be
+accessed normally without READ_ONCE() or WRITE_ONCE(). In fact, they
+need not even be stored in normal memory at all -- in principle a
+private variable could be stored in a CPU register (hence the convention
+that these variables have names starting with the letter 'r').
A WARNING
@@ -1302,7 +1297,7 @@ followed by an arbitrary number of cumul-fence links, ending with an
rfe link. You can concoct more exotic examples, containing more than
one fence, although this quickly leads to diminishing returns in terms
of complexity. For instance, here's an example containing a coe link
-followed by two fences and an rfe link, utilizing the fact that
+followed by two cumul-fences and an rfe link, utilizing the fact that
release fences are A-cumulative:
int x, y, z;
@@ -1334,10 +1329,10 @@ If x = 2, r0 = 1, and r2 = 1 after this code runs then there is a prop
link from P0's store to its load. This is because P0's store gets
overwritten by P1's store since x = 2 at the end (a coe link), the
smp_wmb() ensures that P1's store to x propagates to P2 before the
-store to y does (the first fence), the store to y propagates to P2
+store to y does (the first cumul-fence), the store to y propagates to P2
before P2's load and store execute, P2's smp_store_release()
guarantees that the stores to x and y both propagate to P0 before the
-store to z does (the second fence), and P0's load executes after the
+store to z does (the second cumul-fence), and P0's load executes after the
store to z has propagated to P0 (an rfe link).
In summary, the fact that the hb relation links memory access events
diff --git a/tools/memory-model/README b/tools/memory-model/README
index 2b87f3971548..fc07b52f2028 100644
--- a/tools/memory-model/README
+++ b/tools/memory-model/README
@@ -167,15 +167,15 @@ scripts Various scripts, see scripts/README.
LIMITATIONS
===========
-The Linux-kernel memory model has the following limitations:
-
-1. Compiler optimizations are not modeled. Of course, the use
- of READ_ONCE() and WRITE_ONCE() limits the compiler's ability
- to optimize, but there is Linux-kernel code that uses bare C
- memory accesses. Handling this code is on the to-do list.
- For more information, see Documentation/explanation.txt (in
- particular, the "THE PROGRAM ORDER RELATION: po AND po-loc"
- and "A WARNING" sections).
+The Linux-kernel memory model (LKMM) has the following limitations:
+
+1. Compiler optimizations are not accurately modeled. Of course,
+ the use of READ_ONCE() and WRITE_ONCE() limits the compiler's
+ ability to optimize, but under some circumstances it is possible
+ for the compiler to undermine the memory model. For more
+ information, see Documentation/explanation.txt (in particular,
+ the "THE PROGRAM ORDER RELATION: po AND po-loc" and "A WARNING"
+ sections).
Note that this limitation in turn limits LKMM's ability to
accurately model address, control, and data dependencies.
diff --git a/tools/memory-model/linux-kernel.bell b/tools/memory-model/linux-kernel.bell
index def9131d3d8e..5be86b1025e8 100644
--- a/tools/memory-model/linux-kernel.bell
+++ b/tools/memory-model/linux-kernel.bell
@@ -24,6 +24,7 @@ instructions RMW[{'once,'acquire,'release}]
enum Barriers = 'wmb (*smp_wmb*) ||
'rmb (*smp_rmb*) ||
'mb (*smp_mb*) ||
+ 'barrier (*barrier*) ||
'rcu-lock (*rcu_read_lock*) ||
'rcu-unlock (*rcu_read_unlock*) ||
'sync-rcu (*synchronize_rcu*) ||
@@ -76,3 +77,8 @@ flag ~empty rcu-rscs & (po ; [Sync-srcu] ; po) as invalid-sleep
(* Validate SRCU dynamic match *)
flag ~empty different-values(srcu-rscs) as srcu-bad-nesting
+
+(* Compute marked and plain memory accesses *)
+let Marked = (~M) | IW | Once | Release | Acquire | domain(rmw) | range(rmw) |
+ LKR | LKW | UL | LF | RL | RU
+let Plain = M \ Marked
diff --git a/tools/memory-model/linux-kernel.cat b/tools/memory-model/linux-kernel.cat
index 8dcb37835b61..ea2ff4b94074 100644
--- a/tools/memory-model/linux-kernel.cat
+++ b/tools/memory-model/linux-kernel.cat
@@ -24,8 +24,14 @@ include "lock.cat"
(* Basic relations *)
(*******************)
+(* Release Acquire *)
+let acq-po = [Acquire] ; po ; [M]
+let po-rel = [M] ; po ; [Release]
+let po-unlock-rf-lock-po = po ; [UL] ; rf ; [LKR] ; po
+
(* Fences *)
-let rmb = [R \ Noreturn] ; fencerel(Rmb) ; [R \ Noreturn]
+let R4rmb = R \ Noreturn (* Reads for which rmb works *)
+let rmb = [R4rmb] ; fencerel(Rmb) ; [R4rmb]
let wmb = [W] ; fencerel(Wmb) ; [W]
let mb = ([M] ; fencerel(Mb) ; [M]) |
([M] ; fencerel(Before-atomic) ; [RMW] ; po? ; [M]) |
@@ -34,13 +40,14 @@ let mb = ([M] ; fencerel(Mb) ; [M]) |
([M] ; po ; [UL] ; (co | po) ; [LKW] ;
fencerel(After-unlock-lock) ; [M])
let gp = po ; [Sync-rcu | Sync-srcu] ; po?
-
let strong-fence = mb | gp
-(* Release Acquire *)
-let acq-po = [Acquire] ; po ; [M]
-let po-rel = [M] ; po ; [Release]
-let po-unlock-rf-lock-po = po ; [UL] ; rf ; [LKR] ; po
+let nonrw-fence = strong-fence | po-rel | acq-po
+let fence = nonrw-fence | wmb | rmb
+let barrier = fencerel(Barrier | Rmb | Wmb | Mb | Sync-rcu | Sync-srcu |
+ Before-atomic | After-atomic | Acquire | Release |
+ Rcu-lock | Rcu-unlock | Srcu-lock | Srcu-unlock) |
+ (po ; [Release]) | ([Acquire] ; po)
(**********************************)
(* Fundamental coherence ordering *)
@@ -61,21 +68,22 @@ empty rmw & (fre ; coe) as atomic
let dep = addr | data
let rwdep = (dep | ctrl) ; [W]
let overwrite = co | fr
-let to-w = rwdep | (overwrite & int)
-let to-r = addr | (dep ; rfi)
-let fence = strong-fence | wmb | po-rel | rmb | acq-po
+let to-w = rwdep | (overwrite & int) | (addr ; [Plain] ; wmb)
+let to-r = addr | (dep ; [Marked] ; rfi)
let ppo = to-r | to-w | fence | (po-unlock-rf-lock-po & int)
(* Propagation: Ordering from release operations and strong fences. *)
-let A-cumul(r) = rfe? ; r
-let cumul-fence = A-cumul(strong-fence | po-rel) | wmb | po-unlock-rf-lock-po
-let prop = (overwrite & ext)? ; cumul-fence* ; rfe?
+let A-cumul(r) = (rfe ; [Marked])? ; r
+let cumul-fence = [Marked] ; (A-cumul(strong-fence | po-rel) | wmb |
+ po-unlock-rf-lock-po) ; [Marked]
+let prop = [Marked] ; (overwrite & ext)? ; cumul-fence* ;
+ [Marked] ; rfe? ; [Marked]
(*
* Happens Before: Ordering from the passage of time.
* No fences needed here for prop because relation confined to one process.
*)
-let hb = ppo | rfe | ((prop \ id) & int)
+let hb = [Marked] ; (ppo | rfe | ((prop \ id) & int)) ; [Marked]
acyclic hb as happens-before
(****************************************)
@@ -83,7 +91,7 @@ acyclic hb as happens-before
(****************************************)
(* Propagation: Each non-rf link needs a strong fence. *)
-let pb = prop ; strong-fence ; hb*
+let pb = prop ; strong-fence ; hb* ; [Marked]
acyclic pb as propagation
(*******)
@@ -114,24 +122,28 @@ let rcu-link = po? ; hb* ; pb* ; prop ; po
(*
* Any sequence containing at least as many grace periods as RCU read-side
- * critical sections (joined by rcu-link) acts as a generalized strong fence.
+ * critical sections (joined by rcu-link) induces order like a generalized
+ * inter-CPU strong fence.
* Likewise for SRCU grace periods and read-side critical sections, provided
* the synchronize_srcu() and srcu_read_[un]lock() calls refer to the same
* struct srcu_struct location.
*)
-let rec rcu-fence = rcu-gp | srcu-gp |
+let rec rcu-order = rcu-gp | srcu-gp |
(rcu-gp ; rcu-link ; rcu-rscsi) |
((srcu-gp ; rcu-link ; srcu-rscsi) & loc) |
(rcu-rscsi ; rcu-link ; rcu-gp) |
((srcu-rscsi ; rcu-link ; srcu-gp) & loc) |
- (rcu-gp ; rcu-link ; rcu-fence ; rcu-link ; rcu-rscsi) |
- ((srcu-gp ; rcu-link ; rcu-fence ; rcu-link ; srcu-rscsi) & loc) |
- (rcu-rscsi ; rcu-link ; rcu-fence ; rcu-link ; rcu-gp) |
- ((srcu-rscsi ; rcu-link ; rcu-fence ; rcu-link ; srcu-gp) & loc) |
- (rcu-fence ; rcu-link ; rcu-fence)
+ (rcu-gp ; rcu-link ; rcu-order ; rcu-link ; rcu-rscsi) |
+ ((srcu-gp ; rcu-link ; rcu-order ; rcu-link ; srcu-rscsi) & loc) |
+ (rcu-rscsi ; rcu-link ; rcu-order ; rcu-link ; rcu-gp) |
+ ((srcu-rscsi ; rcu-link ; rcu-order ; rcu-link ; srcu-gp) & loc) |
+ (rcu-order ; rcu-link ; rcu-order)
+let rcu-fence = po ; rcu-order ; po?
+let fence = fence | rcu-fence
+let strong-fence = strong-fence | rcu-fence
(* rb orders instructions just as pb does *)
-let rb = prop ; po ; rcu-fence ; po? ; hb* ; pb*
+let rb = prop ; rcu-fence ; hb* ; pb* ; [Marked]
irreflexive rb as rcu
@@ -143,3 +155,49 @@ irreflexive rb as rcu
* let xb = hb | pb | rb
* acyclic xb as executes-before
*)
+
+(*********************************)
+(* Plain accesses and data races *)
+(*********************************)
+
+(* Warn about plain writes and marked accesses in the same region *)
+let mixed-accesses = ([Plain & W] ; (po-loc \ barrier) ; [Marked]) |
+ ([Marked] ; (po-loc \ barrier) ; [Plain & W])
+flag ~empty mixed-accesses as mixed-accesses
+
+(* Executes-before and visibility *)
+let xbstar = (hb | pb | rb)*
+let vis = cumul-fence* ; rfe? ; [Marked] ;
+ ((strong-fence ; [Marked] ; xbstar) | (xbstar & int))
+
+(* Boundaries for lifetimes of plain accesses *)
+let w-pre-bounded = [Marked] ; (addr | fence)?
+let r-pre-bounded = [Marked] ; (addr | nonrw-fence |
+ ([R4rmb] ; fencerel(Rmb) ; [~Noreturn]))?
+let w-post-bounded = fence? ; [Marked]
+let r-post-bounded = (nonrw-fence | ([~Noreturn] ; fencerel(Rmb) ; [R4rmb]))? ;
+ [Marked]
+
+(* Visibility and executes-before for plain accesses *)
+let ww-vis = fence | (strong-fence ; xbstar ; w-pre-bounded) |
+ (w-post-bounded ; vis ; w-pre-bounded)
+let wr-vis = fence | (strong-fence ; xbstar ; r-pre-bounded) |
+ (w-post-bounded ; vis ; r-pre-bounded)
+let rw-xbstar = fence | (r-post-bounded ; xbstar ; w-pre-bounded)
+
+(* Potential races *)
+let pre-race = ext & ((Plain * M) | ((M \ IW) * Plain))
+
+(* Coherence requirements for plain accesses *)
+let wr-incoh = pre-race & rf & rw-xbstar^-1
+let rw-incoh = pre-race & fr & wr-vis^-1
+let ww-incoh = pre-race & co & ww-vis^-1
+empty (wr-incoh | rw-incoh | ww-incoh) as plain-coherence
+
+(* Actual races *)
+let ww-nonrace = ww-vis & ((Marked * W) | rw-xbstar) & ((W * Marked) | wr-vis)
+let ww-race = (pre-race & co) \ ww-nonrace
+let wr-race = (pre-race & (co? ; rf)) \ wr-vis
+let rw-race = (pre-race & fr) \ rw-xbstar
+
+flag ~empty (ww-race | wr-race | rw-race) as data-race
diff --git a/tools/memory-model/linux-kernel.def b/tools/memory-model/linux-kernel.def
index 551eeaa389d4..ef0f3c1850de 100644
--- a/tools/memory-model/linux-kernel.def
+++ b/tools/memory-model/linux-kernel.def
@@ -24,6 +24,7 @@ smp_mb__before_atomic() { __fence{before-atomic}; }
smp_mb__after_atomic() { __fence{after-atomic}; }
smp_mb__after_spinlock() { __fence{after-spinlock}; }
smp_mb__after_unlock_lock() { __fence{after-unlock-lock}; }
+barrier() { __fence{barrier}; }
// Exchange
xchg(X,V) __xchg{mb}(X,V)
diff --git a/tools/memory-model/litmus-tests/MP+poonceonces.litmus b/tools/memory-model/litmus-tests/MP+poonceonces.litmus
index b2b60b84fb9d..172f0145301c 100644
--- a/tools/memory-model/litmus-tests/MP+poonceonces.litmus
+++ b/tools/memory-model/litmus-tests/MP+poonceonces.litmus
@@ -1,7 +1,7 @@
C MP+poonceonces
(*
- * Result: Maybe
+ * Result: Sometimes
*
* Can the counter-intuitive message-passing outcome be prevented with
* no ordering at all?
diff --git a/tools/memory-model/litmus-tests/README b/tools/memory-model/litmus-tests/README
index 5ee08f129094..681f9067fa9e 100644
--- a/tools/memory-model/litmus-tests/README
+++ b/tools/memory-model/litmus-tests/README
@@ -244,7 +244,7 @@ produce the name:
Adding the ".litmus" suffix: SB+rfionceonce-poonceonces.litmus
The descriptors that describe connections between consecutive accesses
-within the cycle through a given litmus test can be provided by the herd
+within the cycle through a given litmus test can be provided by the herd7
tool (Rfi, Po, Fre, and so on) or by the linux-kernel.bell file (Once,
Release, Acquire, and so on).
diff --git a/tools/memory-model/lock.cat b/tools/memory-model/lock.cat
index a059d1a6d8a2..6b52f365d73a 100644
--- a/tools/memory-model/lock.cat
+++ b/tools/memory-model/lock.cat
@@ -11,7 +11,7 @@
include "cross.cat"
(*
- * The lock-related events generated by herd are as follows:
+ * The lock-related events generated by herd7 are as follows:
*
* LKR Lock-Read: the read part of a spin_lock() or successful
* spin_trylock() read-modify-write event pair
diff --git a/tools/memory-model/scripts/README b/tools/memory-model/scripts/README
index 29375a1fbbfa..095c7eb36f9f 100644
--- a/tools/memory-model/scripts/README
+++ b/tools/memory-model/scripts/README
@@ -22,7 +22,7 @@ checklitmushist.sh
Run all litmus tests having .litmus.out files from previous
initlitmushist.sh or newlitmushist.sh runs, comparing the
- herd output to that of the original runs.
+ herd7 output to that of the original runs.
checklitmus.sh
@@ -43,7 +43,7 @@ initlitmushist.sh
judgelitmus.sh
- Given a .litmus file and its .litmus.out herd output, check the
+ Given a .litmus file and its .litmus.out herd7 output, check the
.litmus.out file against the .litmus file's "Result:" comment to
judge whether the test ran correctly. Not normally run manually,
provided instead for use by other scripts.
diff --git a/tools/memory-model/scripts/checkalllitmus.sh b/tools/memory-model/scripts/checkalllitmus.sh
index b35fcd61ecf6..3c0c7fbbd223 100755
--- a/tools/memory-model/scripts/checkalllitmus.sh
+++ b/tools/memory-model/scripts/checkalllitmus.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0+
#
-# Run herd tests on all .litmus files in the litmus-tests directory
+# Run herd7 tests on all .litmus files in the litmus-tests directory
# and check each file's result against a "Result:" comment within that
# litmus test. If the verification result does not match that specified
# in the litmus test, this script prints an error message prefixed with
diff --git a/tools/memory-model/scripts/checkghlitmus.sh b/tools/memory-model/scripts/checkghlitmus.sh
index 6589fbb6f653..6589fbb6f653 100644..100755
--- a/tools/memory-model/scripts/checkghlitmus.sh
+++ b/tools/memory-model/scripts/checkghlitmus.sh
diff --git a/tools/memory-model/scripts/checklitmus.sh b/tools/memory-model/scripts/checklitmus.sh
index dd08801a30b0..11461ed40b5e 100755
--- a/tools/memory-model/scripts/checklitmus.sh
+++ b/tools/memory-model/scripts/checklitmus.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0+
#
-# Run a herd test and invokes judgelitmus.sh to check the result against
+# Run a herd7 test and invokes judgelitmus.sh to check the result against
# a "Result:" comment within the litmus test. It also outputs verification
# results to a file whose name is that of the specified litmus test, but
# with ".out" appended.
diff --git a/tools/memory-model/scripts/checklitmushist.sh b/tools/memory-model/scripts/checklitmushist.sh
index 1d210ffb7c8a..1d210ffb7c8a 100644..100755
--- a/tools/memory-model/scripts/checklitmushist.sh
+++ b/tools/memory-model/scripts/checklitmushist.sh
diff --git a/tools/memory-model/scripts/cmplitmushist.sh b/tools/memory-model/scripts/cmplitmushist.sh
index 0f498aeeccf5..0f498aeeccf5 100644..100755
--- a/tools/memory-model/scripts/cmplitmushist.sh
+++ b/tools/memory-model/scripts/cmplitmushist.sh
diff --git a/tools/memory-model/scripts/initlitmushist.sh b/tools/memory-model/scripts/initlitmushist.sh
index 956b6957484d..956b6957484d 100644..100755
--- a/tools/memory-model/scripts/initlitmushist.sh
+++ b/tools/memory-model/scripts/initlitmushist.sh
diff --git a/tools/memory-model/scripts/judgelitmus.sh b/tools/memory-model/scripts/judgelitmus.sh
index 0cc63875e395..0cc63875e395 100644..100755
--- a/tools/memory-model/scripts/judgelitmus.sh
+++ b/tools/memory-model/scripts/judgelitmus.sh
diff --git a/tools/memory-model/scripts/newlitmushist.sh b/tools/memory-model/scripts/newlitmushist.sh
index 991f8f814881..991f8f814881 100644..100755
--- a/tools/memory-model/scripts/newlitmushist.sh
+++ b/tools/memory-model/scripts/newlitmushist.sh
diff --git a/tools/memory-model/scripts/parseargs.sh b/tools/memory-model/scripts/parseargs.sh
index 859e1d581e05..40f52080fdbd 100644..100755
--- a/tools/memory-model/scripts/parseargs.sh
+++ b/tools/memory-model/scripts/parseargs.sh
@@ -91,7 +91,7 @@ do
shift
;;
--herdopts|--herdopt)
- checkarg --destdir "(herd options)" "$#" "$2" '.*' '^--'
+ checkarg --destdir "(herd7 options)" "$#" "$2" '.*' '^--'
LKMM_HERD_OPTIONS="$2"
shift
;;
diff --git a/tools/memory-model/scripts/runlitmushist.sh b/tools/memory-model/scripts/runlitmushist.sh
index e507f5f933d5..6ed376f495bb 100644..100755
--- a/tools/memory-model/scripts/runlitmushist.sh
+++ b/tools/memory-model/scripts/runlitmushist.sh
@@ -79,7 +79,7 @@ then
echo ' ---' Summary: 1>&2
grep '!!!' $T/*.sh.out 1>&2
nfail="`grep '!!!' $T/*.sh.out | wc -l`"
- echo 'Number of failed herd runs (e.g., timeout): ' $nfail 1>&2
+ echo 'Number of failed herd7 runs (e.g., timeout): ' $nfail 1>&2
exit 1
else
echo All runs completed successfully. 1>&2
diff --git a/tools/objtool/Build b/tools/objtool/Build
index 749becdf5b90..8dc4f0848362 100644
--- a/tools/objtool/Build
+++ b/tools/objtool/Build
@@ -9,6 +9,7 @@ objtool-y += special.o
objtool-y += objtool.o
objtool-y += libstring.o
+objtool-y += libctype.o
objtool-y += str_error_r.o
CFLAGS += -I$(srctree)/tools/lib
@@ -17,6 +18,10 @@ $(OUTPUT)libstring.o: ../lib/string.c FORCE
$(call rule_mkdir)
$(call if_changed_dep,cc_o_c)
+$(OUTPUT)libctype.o: ../lib/ctype.c FORCE
+ $(call rule_mkdir)
+ $(call if_changed_dep,cc_o_c)
+
$(OUTPUT)str_error_r.o: ../lib/str_error_r.c FORCE
$(call rule_mkdir)
$(call if_changed_dep,cc_o_c)
diff --git a/tools/objtool/Documentation/stack-validation.txt b/tools/objtool/Documentation/stack-validation.txt
index 4dd11a554b9b..de094670050b 100644
--- a/tools/objtool/Documentation/stack-validation.txt
+++ b/tools/objtool/Documentation/stack-validation.txt
@@ -21,7 +21,7 @@ instructions). Similarly, it knows how to follow switch statements, for
which gcc sometimes uses jump tables.
(Objtool also has an 'orc generate' subcommand which generates debuginfo
-for the ORC unwinder. See Documentation/x86/orc-unwinder.txt in the
+for the ORC unwinder. See Documentation/x86/orc-unwinder.rst in the
kernel tree for more details.)
@@ -101,7 +101,7 @@ b) ORC (Oops Rewind Capability) unwind table generation
band. So it doesn't affect runtime performance and it can be
reliable even when interrupts or exceptions are involved.
- For more details, see Documentation/x86/orc-unwinder.txt.
+ For more details, see Documentation/x86/orc-unwinder.rst.
c) Higher live patching compatibility rate
diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index 88158239622b..d2a19b0bc05a 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -33,9 +33,9 @@ all: $(OBJTOOL)
INCLUDES := -I$(srctree)/tools/include \
-I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi \
- -I$(srctree)/tools/objtool/arch/$(ARCH)/include
+ -I$(srctree)/tools/arch/$(ARCH)/include
WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed
-CFLAGS += -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS)
+CFLAGS := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS)
LDFLAGS += $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS)
# Allow old libelf to be used:
@@ -60,7 +60,7 @@ $(LIBSUBCMD): fixdep FORCE
clean:
$(call QUIET_CLEAN, objtool) $(RM) $(OBJTOOL)
$(Q)find $(OUTPUT) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
- $(Q)$(RM) $(OUTPUT)arch/x86/lib/inat-tables.c $(OUTPUT)fixdep
+ $(Q)$(RM) $(OUTPUT)arch/x86/inat-tables.c $(OUTPUT)fixdep
FORCE:
diff --git a/tools/objtool/arch.h b/tools/objtool/arch.h
index 7a111a77b7aa..ced3765c4f44 100644
--- a/tools/objtool/arch.h
+++ b/tools/objtool/arch.h
@@ -1,18 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _ARCH_H
@@ -23,22 +11,24 @@
#include "elf.h"
#include "cfi.h"
-#define INSN_JUMP_CONDITIONAL 1
-#define INSN_JUMP_UNCONDITIONAL 2
-#define INSN_JUMP_DYNAMIC 3
-#define INSN_CALL 4
-#define INSN_CALL_DYNAMIC 5
-#define INSN_RETURN 6
-#define INSN_CONTEXT_SWITCH 7
-#define INSN_STACK 8
-#define INSN_BUG 9
-#define INSN_NOP 10
-#define INSN_STAC 11
-#define INSN_CLAC 12
-#define INSN_STD 13
-#define INSN_CLD 14
-#define INSN_OTHER 15
-#define INSN_LAST INSN_OTHER
+enum insn_type {
+ INSN_JUMP_CONDITIONAL,
+ INSN_JUMP_UNCONDITIONAL,
+ INSN_JUMP_DYNAMIC,
+ INSN_JUMP_DYNAMIC_CONDITIONAL,
+ INSN_CALL,
+ INSN_CALL_DYNAMIC,
+ INSN_RETURN,
+ INSN_CONTEXT_SWITCH,
+ INSN_STACK,
+ INSN_BUG,
+ INSN_NOP,
+ INSN_STAC,
+ INSN_CLAC,
+ INSN_STD,
+ INSN_CLD,
+ INSN_OTHER,
+};
enum op_dest_type {
OP_DEST_REG,
@@ -80,7 +70,7 @@ void arch_initial_func_cfi_state(struct cfi_state *state);
int arch_decode_instruction(struct elf *elf, struct section *sec,
unsigned long offset, unsigned int maxlen,
- unsigned int *len, unsigned char *type,
+ unsigned int *len, enum insn_type *type,
unsigned long *immediate, struct stack_op *op);
bool arch_callee_saved_reg(unsigned char reg);
diff --git a/tools/objtool/arch/x86/Build b/tools/objtool/arch/x86/Build
index b998412c017d..7c5004008e97 100644
--- a/tools/objtool/arch/x86/Build
+++ b/tools/objtool/arch/x86/Build
@@ -1,7 +1,7 @@
objtool-y += decode.o
-inat_tables_script = arch/x86/tools/gen-insn-attr-x86.awk
-inat_tables_maps = arch/x86/lib/x86-opcode-map.txt
+inat_tables_script = ../arch/x86/tools/gen-insn-attr-x86.awk
+inat_tables_maps = ../arch/x86/lib/x86-opcode-map.txt
$(OUTPUT)arch/x86/lib/inat-tables.c: $(inat_tables_script) $(inat_tables_maps)
$(call rule_mkdir)
diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c
index 472e991f6512..a62e032863a8 100644
--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -1,18 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -20,8 +8,8 @@
#define unlikely(cond) (cond)
#include <asm/insn.h>
-#include "lib/inat.c"
-#include "lib/insn.c"
+#include "../../../arch/x86/lib/inat.c"
+#include "../../../arch/x86/lib/insn.c"
#include "../../elf.h"
#include "../../arch.h"
@@ -80,7 +68,7 @@ bool arch_callee_saved_reg(unsigned char reg)
int arch_decode_instruction(struct elf *elf, struct section *sec,
unsigned long offset, unsigned int maxlen,
- unsigned int *len, unsigned char *type,
+ unsigned int *len, enum insn_type *type,
unsigned long *immediate, struct stack_op *op)
{
struct insn insn;
diff --git a/tools/objtool/arch/x86/include/asm/inat.h b/tools/objtool/arch/x86/include/asm/inat.h
deleted file mode 100644
index 1c78580e58be..000000000000
--- a/tools/objtool/arch/x86/include/asm/inat.h
+++ /dev/null
@@ -1,244 +0,0 @@
-#ifndef _ASM_X86_INAT_H
-#define _ASM_X86_INAT_H
-/*
- * x86 instruction attributes
- *
- * Written by Masami Hiramatsu <mhiramat@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-#include <asm/inat_types.h>
-
-/*
- * Internal bits. Don't use bitmasks directly, because these bits are
- * unstable. You should use checking functions.
- */
-
-#define INAT_OPCODE_TABLE_SIZE 256
-#define INAT_GROUP_TABLE_SIZE 8
-
-/* Legacy last prefixes */
-#define INAT_PFX_OPNDSZ 1 /* 0x66 */ /* LPFX1 */
-#define INAT_PFX_REPE 2 /* 0xF3 */ /* LPFX2 */
-#define INAT_PFX_REPNE 3 /* 0xF2 */ /* LPFX3 */
-/* Other Legacy prefixes */
-#define INAT_PFX_LOCK 4 /* 0xF0 */
-#define INAT_PFX_CS 5 /* 0x2E */
-#define INAT_PFX_DS 6 /* 0x3E */
-#define INAT_PFX_ES 7 /* 0x26 */
-#define INAT_PFX_FS 8 /* 0x64 */
-#define INAT_PFX_GS 9 /* 0x65 */
-#define INAT_PFX_SS 10 /* 0x36 */
-#define INAT_PFX_ADDRSZ 11 /* 0x67 */
-/* x86-64 REX prefix */
-#define INAT_PFX_REX 12 /* 0x4X */
-/* AVX VEX prefixes */
-#define INAT_PFX_VEX2 13 /* 2-bytes VEX prefix */
-#define INAT_PFX_VEX3 14 /* 3-bytes VEX prefix */
-#define INAT_PFX_EVEX 15 /* EVEX prefix */
-
-#define INAT_LSTPFX_MAX 3
-#define INAT_LGCPFX_MAX 11
-
-/* Immediate size */
-#define INAT_IMM_BYTE 1
-#define INAT_IMM_WORD 2
-#define INAT_IMM_DWORD 3
-#define INAT_IMM_QWORD 4
-#define INAT_IMM_PTR 5
-#define INAT_IMM_VWORD32 6
-#define INAT_IMM_VWORD 7
-
-/* Legacy prefix */
-#define INAT_PFX_OFFS 0
-#define INAT_PFX_BITS 4
-#define INAT_PFX_MAX ((1 << INAT_PFX_BITS) - 1)
-#define INAT_PFX_MASK (INAT_PFX_MAX << INAT_PFX_OFFS)
-/* Escape opcodes */
-#define INAT_ESC_OFFS (INAT_PFX_OFFS + INAT_PFX_BITS)
-#define INAT_ESC_BITS 2
-#define INAT_ESC_MAX ((1 << INAT_ESC_BITS) - 1)
-#define INAT_ESC_MASK (INAT_ESC_MAX << INAT_ESC_OFFS)
-/* Group opcodes (1-16) */
-#define INAT_GRP_OFFS (INAT_ESC_OFFS + INAT_ESC_BITS)
-#define INAT_GRP_BITS 5
-#define INAT_GRP_MAX ((1 << INAT_GRP_BITS) - 1)
-#define INAT_GRP_MASK (INAT_GRP_MAX << INAT_GRP_OFFS)
-/* Immediates */
-#define INAT_IMM_OFFS (INAT_GRP_OFFS + INAT_GRP_BITS)
-#define INAT_IMM_BITS 3
-#define INAT_IMM_MASK (((1 << INAT_IMM_BITS) - 1) << INAT_IMM_OFFS)
-/* Flags */
-#define INAT_FLAG_OFFS (INAT_IMM_OFFS + INAT_IMM_BITS)
-#define INAT_MODRM (1 << (INAT_FLAG_OFFS))
-#define INAT_FORCE64 (1 << (INAT_FLAG_OFFS + 1))
-#define INAT_SCNDIMM (1 << (INAT_FLAG_OFFS + 2))
-#define INAT_MOFFSET (1 << (INAT_FLAG_OFFS + 3))
-#define INAT_VARIANT (1 << (INAT_FLAG_OFFS + 4))
-#define INAT_VEXOK (1 << (INAT_FLAG_OFFS + 5))
-#define INAT_VEXONLY (1 << (INAT_FLAG_OFFS + 6))
-#define INAT_EVEXONLY (1 << (INAT_FLAG_OFFS + 7))
-/* Attribute making macros for attribute tables */
-#define INAT_MAKE_PREFIX(pfx) (pfx << INAT_PFX_OFFS)
-#define INAT_MAKE_ESCAPE(esc) (esc << INAT_ESC_OFFS)
-#define INAT_MAKE_GROUP(grp) ((grp << INAT_GRP_OFFS) | INAT_MODRM)
-#define INAT_MAKE_IMM(imm) (imm << INAT_IMM_OFFS)
-
-/* Identifiers for segment registers */
-#define INAT_SEG_REG_IGNORE 0
-#define INAT_SEG_REG_DEFAULT 1
-#define INAT_SEG_REG_CS 2
-#define INAT_SEG_REG_SS 3
-#define INAT_SEG_REG_DS 4
-#define INAT_SEG_REG_ES 5
-#define INAT_SEG_REG_FS 6
-#define INAT_SEG_REG_GS 7
-
-/* Attribute search APIs */
-extern insn_attr_t inat_get_opcode_attribute(insn_byte_t opcode);
-extern int inat_get_last_prefix_id(insn_byte_t last_pfx);
-extern insn_attr_t inat_get_escape_attribute(insn_byte_t opcode,
- int lpfx_id,
- insn_attr_t esc_attr);
-extern insn_attr_t inat_get_group_attribute(insn_byte_t modrm,
- int lpfx_id,
- insn_attr_t esc_attr);
-extern insn_attr_t inat_get_avx_attribute(insn_byte_t opcode,
- insn_byte_t vex_m,
- insn_byte_t vex_pp);
-
-/* Attribute checking functions */
-static inline int inat_is_legacy_prefix(insn_attr_t attr)
-{
- attr &= INAT_PFX_MASK;
- return attr && attr <= INAT_LGCPFX_MAX;
-}
-
-static inline int inat_is_address_size_prefix(insn_attr_t attr)
-{
- return (attr & INAT_PFX_MASK) == INAT_PFX_ADDRSZ;
-}
-
-static inline int inat_is_operand_size_prefix(insn_attr_t attr)
-{
- return (attr & INAT_PFX_MASK) == INAT_PFX_OPNDSZ;
-}
-
-static inline int inat_is_rex_prefix(insn_attr_t attr)
-{
- return (attr & INAT_PFX_MASK) == INAT_PFX_REX;
-}
-
-static inline int inat_last_prefix_id(insn_attr_t attr)
-{
- if ((attr & INAT_PFX_MASK) > INAT_LSTPFX_MAX)
- return 0;
- else
- return attr & INAT_PFX_MASK;
-}
-
-static inline int inat_is_vex_prefix(insn_attr_t attr)
-{
- attr &= INAT_PFX_MASK;
- return attr == INAT_PFX_VEX2 || attr == INAT_PFX_VEX3 ||
- attr == INAT_PFX_EVEX;
-}
-
-static inline int inat_is_evex_prefix(insn_attr_t attr)
-{
- return (attr & INAT_PFX_MASK) == INAT_PFX_EVEX;
-}
-
-static inline int inat_is_vex3_prefix(insn_attr_t attr)
-{
- return (attr & INAT_PFX_MASK) == INAT_PFX_VEX3;
-}
-
-static inline int inat_is_escape(insn_attr_t attr)
-{
- return attr & INAT_ESC_MASK;
-}
-
-static inline int inat_escape_id(insn_attr_t attr)
-{
- return (attr & INAT_ESC_MASK) >> INAT_ESC_OFFS;
-}
-
-static inline int inat_is_group(insn_attr_t attr)
-{
- return attr & INAT_GRP_MASK;
-}
-
-static inline int inat_group_id(insn_attr_t attr)
-{
- return (attr & INAT_GRP_MASK) >> INAT_GRP_OFFS;
-}
-
-static inline int inat_group_common_attribute(insn_attr_t attr)
-{
- return attr & ~INAT_GRP_MASK;
-}
-
-static inline int inat_has_immediate(insn_attr_t attr)
-{
- return attr & INAT_IMM_MASK;
-}
-
-static inline int inat_immediate_size(insn_attr_t attr)
-{
- return (attr & INAT_IMM_MASK) >> INAT_IMM_OFFS;
-}
-
-static inline int inat_has_modrm(insn_attr_t attr)
-{
- return attr & INAT_MODRM;
-}
-
-static inline int inat_is_force64(insn_attr_t attr)
-{
- return attr & INAT_FORCE64;
-}
-
-static inline int inat_has_second_immediate(insn_attr_t attr)
-{
- return attr & INAT_SCNDIMM;
-}
-
-static inline int inat_has_moffset(insn_attr_t attr)
-{
- return attr & INAT_MOFFSET;
-}
-
-static inline int inat_has_variant(insn_attr_t attr)
-{
- return attr & INAT_VARIANT;
-}
-
-static inline int inat_accept_vex(insn_attr_t attr)
-{
- return attr & INAT_VEXOK;
-}
-
-static inline int inat_must_vex(insn_attr_t attr)
-{
- return attr & (INAT_VEXONLY | INAT_EVEXONLY);
-}
-
-static inline int inat_must_evex(insn_attr_t attr)
-{
- return attr & INAT_EVEXONLY;
-}
-#endif
diff --git a/tools/objtool/arch/x86/include/asm/inat_types.h b/tools/objtool/arch/x86/include/asm/inat_types.h
deleted file mode 100644
index cb3c20ce39cf..000000000000
--- a/tools/objtool/arch/x86/include/asm/inat_types.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef _ASM_X86_INAT_TYPES_H
-#define _ASM_X86_INAT_TYPES_H
-/*
- * x86 instruction attributes
- *
- * Written by Masami Hiramatsu <mhiramat@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-/* Instruction attributes */
-typedef unsigned int insn_attr_t;
-typedef unsigned char insn_byte_t;
-typedef signed int insn_value_t;
-
-#endif
diff --git a/tools/objtool/arch/x86/include/asm/insn.h b/tools/objtool/arch/x86/include/asm/insn.h
deleted file mode 100644
index c2c01f84df75..000000000000
--- a/tools/objtool/arch/x86/include/asm/insn.h
+++ /dev/null
@@ -1,229 +0,0 @@
-#ifndef _ASM_X86_INSN_H
-#define _ASM_X86_INSN_H
-/*
- * x86 instruction analysis
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) IBM Corporation, 2009
- */
-
-/* insn_attr_t is defined in inat.h */
-#include <asm/inat.h>
-
-struct insn_field {
- union {
- insn_value_t value;
- insn_byte_t bytes[4];
- };
- /* !0 if we've run insn_get_xxx() for this field */
- unsigned char got;
- unsigned char nbytes;
-};
-
-struct insn {
- struct insn_field prefixes; /*
- * Prefixes
- * prefixes.bytes[3]: last prefix
- */
- struct insn_field rex_prefix; /* REX prefix */
- struct insn_field vex_prefix; /* VEX prefix */
- struct insn_field opcode; /*
- * opcode.bytes[0]: opcode1
- * opcode.bytes[1]: opcode2
- * opcode.bytes[2]: opcode3
- */
- struct insn_field modrm;
- struct insn_field sib;
- struct insn_field displacement;
- union {
- struct insn_field immediate;
- struct insn_field moffset1; /* for 64bit MOV */
- struct insn_field immediate1; /* for 64bit imm or off16/32 */
- };
- union {
- struct insn_field moffset2; /* for 64bit MOV */
- struct insn_field immediate2; /* for 64bit imm or seg16 */
- };
-
- insn_attr_t attr;
- unsigned char opnd_bytes;
- unsigned char addr_bytes;
- unsigned char length;
- unsigned char x86_64;
-
- const insn_byte_t *kaddr; /* kernel address of insn to analyze */
- const insn_byte_t *end_kaddr; /* kernel address of last insn in buffer */
- const insn_byte_t *next_byte;
-};
-
-#define MAX_INSN_SIZE 15
-
-#define X86_MODRM_MOD(modrm) (((modrm) & 0xc0) >> 6)
-#define X86_MODRM_REG(modrm) (((modrm) & 0x38) >> 3)
-#define X86_MODRM_RM(modrm) ((modrm) & 0x07)
-
-#define X86_SIB_SCALE(sib) (((sib) & 0xc0) >> 6)
-#define X86_SIB_INDEX(sib) (((sib) & 0x38) >> 3)
-#define X86_SIB_BASE(sib) ((sib) & 0x07)
-
-#define X86_REX_W(rex) ((rex) & 8)
-#define X86_REX_R(rex) ((rex) & 4)
-#define X86_REX_X(rex) ((rex) & 2)
-#define X86_REX_B(rex) ((rex) & 1)
-
-/* VEX bit flags */
-#define X86_VEX_W(vex) ((vex) & 0x80) /* VEX3 Byte2 */
-#define X86_VEX_R(vex) ((vex) & 0x80) /* VEX2/3 Byte1 */
-#define X86_VEX_X(vex) ((vex) & 0x40) /* VEX3 Byte1 */
-#define X86_VEX_B(vex) ((vex) & 0x20) /* VEX3 Byte1 */
-#define X86_VEX_L(vex) ((vex) & 0x04) /* VEX3 Byte2, VEX2 Byte1 */
-/* VEX bit fields */
-#define X86_EVEX_M(vex) ((vex) & 0x03) /* EVEX Byte1 */
-#define X86_VEX3_M(vex) ((vex) & 0x1f) /* VEX3 Byte1 */
-#define X86_VEX2_M 1 /* VEX2.M always 1 */
-#define X86_VEX_V(vex) (((vex) & 0x78) >> 3) /* VEX3 Byte2, VEX2 Byte1 */
-#define X86_VEX_P(vex) ((vex) & 0x03) /* VEX3 Byte2, VEX2 Byte1 */
-#define X86_VEX_M_MAX 0x1f /* VEX3.M Maximum value */
-
-extern void insn_init(struct insn *insn, const void *kaddr, int buf_len, int x86_64);
-extern void insn_get_prefixes(struct insn *insn);
-extern void insn_get_opcode(struct insn *insn);
-extern void insn_get_modrm(struct insn *insn);
-extern void insn_get_sib(struct insn *insn);
-extern void insn_get_displacement(struct insn *insn);
-extern void insn_get_immediate(struct insn *insn);
-extern void insn_get_length(struct insn *insn);
-
-/* Attribute will be determined after getting ModRM (for opcode groups) */
-static inline void insn_get_attribute(struct insn *insn)
-{
- insn_get_modrm(insn);
-}
-
-/* Instruction uses RIP-relative addressing */
-extern int insn_rip_relative(struct insn *insn);
-
-/* Init insn for kernel text */
-static inline void kernel_insn_init(struct insn *insn,
- const void *kaddr, int buf_len)
-{
-#ifdef CONFIG_X86_64
- insn_init(insn, kaddr, buf_len, 1);
-#else /* CONFIG_X86_32 */
- insn_init(insn, kaddr, buf_len, 0);
-#endif
-}
-
-static inline int insn_is_avx(struct insn *insn)
-{
- if (!insn->prefixes.got)
- insn_get_prefixes(insn);
- return (insn->vex_prefix.value != 0);
-}
-
-static inline int insn_is_evex(struct insn *insn)
-{
- if (!insn->prefixes.got)
- insn_get_prefixes(insn);
- return (insn->vex_prefix.nbytes == 4);
-}
-
-/* Ensure this instruction is decoded completely */
-static inline int insn_complete(struct insn *insn)
-{
- return insn->opcode.got && insn->modrm.got && insn->sib.got &&
- insn->displacement.got && insn->immediate.got;
-}
-
-static inline insn_byte_t insn_vex_m_bits(struct insn *insn)
-{
- if (insn->vex_prefix.nbytes == 2) /* 2 bytes VEX */
- return X86_VEX2_M;
- else if (insn->vex_prefix.nbytes == 3) /* 3 bytes VEX */
- return X86_VEX3_M(insn->vex_prefix.bytes[1]);
- else /* EVEX */
- return X86_EVEX_M(insn->vex_prefix.bytes[1]);
-}
-
-static inline insn_byte_t insn_vex_p_bits(struct insn *insn)
-{
- if (insn->vex_prefix.nbytes == 2) /* 2 bytes VEX */
- return X86_VEX_P(insn->vex_prefix.bytes[1]);
- else
- return X86_VEX_P(insn->vex_prefix.bytes[2]);
-}
-
-/* Get the last prefix id from last prefix or VEX prefix */
-static inline int insn_last_prefix_id(struct insn *insn)
-{
- if (insn_is_avx(insn))
- return insn_vex_p_bits(insn); /* VEX_p is a SIMD prefix id */
-
- if (insn->prefixes.bytes[3])
- return inat_get_last_prefix_id(insn->prefixes.bytes[3]);
-
- return 0;
-}
-
-/* Offset of each field from kaddr */
-static inline int insn_offset_rex_prefix(struct insn *insn)
-{
- return insn->prefixes.nbytes;
-}
-static inline int insn_offset_vex_prefix(struct insn *insn)
-{
- return insn_offset_rex_prefix(insn) + insn->rex_prefix.nbytes;
-}
-static inline int insn_offset_opcode(struct insn *insn)
-{
- return insn_offset_vex_prefix(insn) + insn->vex_prefix.nbytes;
-}
-static inline int insn_offset_modrm(struct insn *insn)
-{
- return insn_offset_opcode(insn) + insn->opcode.nbytes;
-}
-static inline int insn_offset_sib(struct insn *insn)
-{
- return insn_offset_modrm(insn) + insn->modrm.nbytes;
-}
-static inline int insn_offset_displacement(struct insn *insn)
-{
- return insn_offset_sib(insn) + insn->sib.nbytes;
-}
-static inline int insn_offset_immediate(struct insn *insn)
-{
- return insn_offset_displacement(insn) + insn->displacement.nbytes;
-}
-
-#define POP_SS_OPCODE 0x1f
-#define MOV_SREG_OPCODE 0x8e
-
-/*
- * Intel SDM Vol.3A 6.8.3 states;
- * "Any single-step trap that would be delivered following the MOV to SS
- * instruction or POP to SS instruction (because EFLAGS.TF is 1) is
- * suppressed."
- * This function returns true if @insn is MOV SS or POP SS. On these
- * instructions, single stepping is suppressed.
- */
-static inline int insn_masking_exception(struct insn *insn)
-{
- return insn->opcode.bytes[0] == POP_SS_OPCODE ||
- (insn->opcode.bytes[0] == MOV_SREG_OPCODE &&
- X86_MODRM_REG(insn->modrm.bytes[0]) == 2);
-}
-
-#endif /* _ASM_X86_INSN_H */
diff --git a/tools/objtool/arch/x86/lib/inat.c b/tools/objtool/arch/x86/lib/inat.c
deleted file mode 100644
index c1f01a8e9f65..000000000000
--- a/tools/objtool/arch/x86/lib/inat.c
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * x86 instruction attribute tables
- *
- * Written by Masami Hiramatsu <mhiramat@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-#include <asm/insn.h>
-
-/* Attribute tables are generated from opcode map */
-#include "inat-tables.c"
-
-/* Attribute search APIs */
-insn_attr_t inat_get_opcode_attribute(insn_byte_t opcode)
-{
- return inat_primary_table[opcode];
-}
-
-int inat_get_last_prefix_id(insn_byte_t last_pfx)
-{
- insn_attr_t lpfx_attr;
-
- lpfx_attr = inat_get_opcode_attribute(last_pfx);
- return inat_last_prefix_id(lpfx_attr);
-}
-
-insn_attr_t inat_get_escape_attribute(insn_byte_t opcode, int lpfx_id,
- insn_attr_t esc_attr)
-{
- const insn_attr_t *table;
- int n;
-
- n = inat_escape_id(esc_attr);
-
- table = inat_escape_tables[n][0];
- if (!table)
- return 0;
- if (inat_has_variant(table[opcode]) && lpfx_id) {
- table = inat_escape_tables[n][lpfx_id];
- if (!table)
- return 0;
- }
- return table[opcode];
-}
-
-insn_attr_t inat_get_group_attribute(insn_byte_t modrm, int lpfx_id,
- insn_attr_t grp_attr)
-{
- const insn_attr_t *table;
- int n;
-
- n = inat_group_id(grp_attr);
-
- table = inat_group_tables[n][0];
- if (!table)
- return inat_group_common_attribute(grp_attr);
- if (inat_has_variant(table[X86_MODRM_REG(modrm)]) && lpfx_id) {
- table = inat_group_tables[n][lpfx_id];
- if (!table)
- return inat_group_common_attribute(grp_attr);
- }
- return table[X86_MODRM_REG(modrm)] |
- inat_group_common_attribute(grp_attr);
-}
-
-insn_attr_t inat_get_avx_attribute(insn_byte_t opcode, insn_byte_t vex_m,
- insn_byte_t vex_p)
-{
- const insn_attr_t *table;
- if (vex_m > X86_VEX_M_MAX || vex_p > INAT_LSTPFX_MAX)
- return 0;
- /* At first, this checks the master table */
- table = inat_avx_tables[vex_m][0];
- if (!table)
- return 0;
- if (!inat_is_group(table[opcode]) && vex_p) {
- /* If this is not a group, get attribute directly */
- table = inat_avx_tables[vex_m][vex_p];
- if (!table)
- return 0;
- }
- return table[opcode];
-}
-
diff --git a/tools/objtool/arch/x86/lib/insn.c b/tools/objtool/arch/x86/lib/insn.c
deleted file mode 100644
index 1088eb8f3a5f..000000000000
--- a/tools/objtool/arch/x86/lib/insn.c
+++ /dev/null
@@ -1,606 +0,0 @@
-/*
- * x86 instruction analysis
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright (C) IBM Corporation, 2002, 2004, 2009
- */
-
-#ifdef __KERNEL__
-#include <linux/string.h>
-#else
-#include <string.h>
-#endif
-#include <asm/inat.h>
-#include <asm/insn.h>
-
-/* Verify next sizeof(t) bytes can be on the same instruction */
-#define validate_next(t, insn, n) \
- ((insn)->next_byte + sizeof(t) + n <= (insn)->end_kaddr)
-
-#define __get_next(t, insn) \
- ({ t r = *(t*)insn->next_byte; insn->next_byte += sizeof(t); r; })
-
-#define __peek_nbyte_next(t, insn, n) \
- ({ t r = *(t*)((insn)->next_byte + n); r; })
-
-#define get_next(t, insn) \
- ({ if (unlikely(!validate_next(t, insn, 0))) goto err_out; __get_next(t, insn); })
-
-#define peek_nbyte_next(t, insn, n) \
- ({ if (unlikely(!validate_next(t, insn, n))) goto err_out; __peek_nbyte_next(t, insn, n); })
-
-#define peek_next(t, insn) peek_nbyte_next(t, insn, 0)
-
-/**
- * insn_init() - initialize struct insn
- * @insn: &struct insn to be initialized
- * @kaddr: address (in kernel memory) of instruction (or copy thereof)
- * @x86_64: !0 for 64-bit kernel or 64-bit app
- */
-void insn_init(struct insn *insn, const void *kaddr, int buf_len, int x86_64)
-{
- /*
- * Instructions longer than MAX_INSN_SIZE (15 bytes) are invalid
- * even if the input buffer is long enough to hold them.
- */
- if (buf_len > MAX_INSN_SIZE)
- buf_len = MAX_INSN_SIZE;
-
- memset(insn, 0, sizeof(*insn));
- insn->kaddr = kaddr;
- insn->end_kaddr = kaddr + buf_len;
- insn->next_byte = kaddr;
- insn->x86_64 = x86_64 ? 1 : 0;
- insn->opnd_bytes = 4;
- if (x86_64)
- insn->addr_bytes = 8;
- else
- insn->addr_bytes = 4;
-}
-
-/**
- * insn_get_prefixes - scan x86 instruction prefix bytes
- * @insn: &struct insn containing instruction
- *
- * Populates the @insn->prefixes bitmap, and updates @insn->next_byte
- * to point to the (first) opcode. No effect if @insn->prefixes.got
- * is already set.
- */
-void insn_get_prefixes(struct insn *insn)
-{
- struct insn_field *prefixes = &insn->prefixes;
- insn_attr_t attr;
- insn_byte_t b, lb;
- int i, nb;
-
- if (prefixes->got)
- return;
-
- nb = 0;
- lb = 0;
- b = peek_next(insn_byte_t, insn);
- attr = inat_get_opcode_attribute(b);
- while (inat_is_legacy_prefix(attr)) {
- /* Skip if same prefix */
- for (i = 0; i < nb; i++)
- if (prefixes->bytes[i] == b)
- goto found;
- if (nb == 4)
- /* Invalid instruction */
- break;
- prefixes->bytes[nb++] = b;
- if (inat_is_address_size_prefix(attr)) {
- /* address size switches 2/4 or 4/8 */
- if (insn->x86_64)
- insn->addr_bytes ^= 12;
- else
- insn->addr_bytes ^= 6;
- } else if (inat_is_operand_size_prefix(attr)) {
- /* oprand size switches 2/4 */
- insn->opnd_bytes ^= 6;
- }
-found:
- prefixes->nbytes++;
- insn->next_byte++;
- lb = b;
- b = peek_next(insn_byte_t, insn);
- attr = inat_get_opcode_attribute(b);
- }
- /* Set the last prefix */
- if (lb && lb != insn->prefixes.bytes[3]) {
- if (unlikely(insn->prefixes.bytes[3])) {
- /* Swap the last prefix */
- b = insn->prefixes.bytes[3];
- for (i = 0; i < nb; i++)
- if (prefixes->bytes[i] == lb)
- prefixes->bytes[i] = b;
- }
- insn->prefixes.bytes[3] = lb;
- }
-
- /* Decode REX prefix */
- if (insn->x86_64) {
- b = peek_next(insn_byte_t, insn);
- attr = inat_get_opcode_attribute(b);
- if (inat_is_rex_prefix(attr)) {
- insn->rex_prefix.value = b;
- insn->rex_prefix.nbytes = 1;
- insn->next_byte++;
- if (X86_REX_W(b))
- /* REX.W overrides opnd_size */
- insn->opnd_bytes = 8;
- }
- }
- insn->rex_prefix.got = 1;
-
- /* Decode VEX prefix */
- b = peek_next(insn_byte_t, insn);
- attr = inat_get_opcode_attribute(b);
- if (inat_is_vex_prefix(attr)) {
- insn_byte_t b2 = peek_nbyte_next(insn_byte_t, insn, 1);
- if (!insn->x86_64) {
- /*
- * In 32-bits mode, if the [7:6] bits (mod bits of
- * ModRM) on the second byte are not 11b, it is
- * LDS or LES or BOUND.
- */
- if (X86_MODRM_MOD(b2) != 3)
- goto vex_end;
- }
- insn->vex_prefix.bytes[0] = b;
- insn->vex_prefix.bytes[1] = b2;
- if (inat_is_evex_prefix(attr)) {
- b2 = peek_nbyte_next(insn_byte_t, insn, 2);
- insn->vex_prefix.bytes[2] = b2;
- b2 = peek_nbyte_next(insn_byte_t, insn, 3);
- insn->vex_prefix.bytes[3] = b2;
- insn->vex_prefix.nbytes = 4;
- insn->next_byte += 4;
- if (insn->x86_64 && X86_VEX_W(b2))
- /* VEX.W overrides opnd_size */
- insn->opnd_bytes = 8;
- } else if (inat_is_vex3_prefix(attr)) {
- b2 = peek_nbyte_next(insn_byte_t, insn, 2);
- insn->vex_prefix.bytes[2] = b2;
- insn->vex_prefix.nbytes = 3;
- insn->next_byte += 3;
- if (insn->x86_64 && X86_VEX_W(b2))
- /* VEX.W overrides opnd_size */
- insn->opnd_bytes = 8;
- } else {
- /*
- * For VEX2, fake VEX3-like byte#2.
- * Makes it easier to decode vex.W, vex.vvvv,
- * vex.L and vex.pp. Masking with 0x7f sets vex.W == 0.
- */
- insn->vex_prefix.bytes[2] = b2 & 0x7f;
- insn->vex_prefix.nbytes = 2;
- insn->next_byte += 2;
- }
- }
-vex_end:
- insn->vex_prefix.got = 1;
-
- prefixes->got = 1;
-
-err_out:
- return;
-}
-
-/**
- * insn_get_opcode - collect opcode(s)
- * @insn: &struct insn containing instruction
- *
- * Populates @insn->opcode, updates @insn->next_byte to point past the
- * opcode byte(s), and set @insn->attr (except for groups).
- * If necessary, first collects any preceding (prefix) bytes.
- * Sets @insn->opcode.value = opcode1. No effect if @insn->opcode.got
- * is already 1.
- */
-void insn_get_opcode(struct insn *insn)
-{
- struct insn_field *opcode = &insn->opcode;
- insn_byte_t op;
- int pfx_id;
- if (opcode->got)
- return;
- if (!insn->prefixes.got)
- insn_get_prefixes(insn);
-
- /* Get first opcode */
- op = get_next(insn_byte_t, insn);
- opcode->bytes[0] = op;
- opcode->nbytes = 1;
-
- /* Check if there is VEX prefix or not */
- if (insn_is_avx(insn)) {
- insn_byte_t m, p;
- m = insn_vex_m_bits(insn);
- p = insn_vex_p_bits(insn);
- insn->attr = inat_get_avx_attribute(op, m, p);
- if ((inat_must_evex(insn->attr) && !insn_is_evex(insn)) ||
- (!inat_accept_vex(insn->attr) &&
- !inat_is_group(insn->attr)))
- insn->attr = 0; /* This instruction is bad */
- goto end; /* VEX has only 1 byte for opcode */
- }
-
- insn->attr = inat_get_opcode_attribute(op);
- while (inat_is_escape(insn->attr)) {
- /* Get escaped opcode */
- op = get_next(insn_byte_t, insn);
- opcode->bytes[opcode->nbytes++] = op;
- pfx_id = insn_last_prefix_id(insn);
- insn->attr = inat_get_escape_attribute(op, pfx_id, insn->attr);
- }
- if (inat_must_vex(insn->attr))
- insn->attr = 0; /* This instruction is bad */
-end:
- opcode->got = 1;
-
-err_out:
- return;
-}
-
-/**
- * insn_get_modrm - collect ModRM byte, if any
- * @insn: &struct insn containing instruction
- *
- * Populates @insn->modrm and updates @insn->next_byte to point past the
- * ModRM byte, if any. If necessary, first collects the preceding bytes
- * (prefixes and opcode(s)). No effect if @insn->modrm.got is already 1.
- */
-void insn_get_modrm(struct insn *insn)
-{
- struct insn_field *modrm = &insn->modrm;
- insn_byte_t pfx_id, mod;
- if (modrm->got)
- return;
- if (!insn->opcode.got)
- insn_get_opcode(insn);
-
- if (inat_has_modrm(insn->attr)) {
- mod = get_next(insn_byte_t, insn);
- modrm->value = mod;
- modrm->nbytes = 1;
- if (inat_is_group(insn->attr)) {
- pfx_id = insn_last_prefix_id(insn);
- insn->attr = inat_get_group_attribute(mod, pfx_id,
- insn->attr);
- if (insn_is_avx(insn) && !inat_accept_vex(insn->attr))
- insn->attr = 0; /* This is bad */
- }
- }
-
- if (insn->x86_64 && inat_is_force64(insn->attr))
- insn->opnd_bytes = 8;
- modrm->got = 1;
-
-err_out:
- return;
-}
-
-
-/**
- * insn_rip_relative() - Does instruction use RIP-relative addressing mode?
- * @insn: &struct insn containing instruction
- *
- * If necessary, first collects the instruction up to and including the
- * ModRM byte. No effect if @insn->x86_64 is 0.
- */
-int insn_rip_relative(struct insn *insn)
-{
- struct insn_field *modrm = &insn->modrm;
-
- if (!insn->x86_64)
- return 0;
- if (!modrm->got)
- insn_get_modrm(insn);
- /*
- * For rip-relative instructions, the mod field (top 2 bits)
- * is zero and the r/m field (bottom 3 bits) is 0x5.
- */
- return (modrm->nbytes && (modrm->value & 0xc7) == 0x5);
-}
-
-/**
- * insn_get_sib() - Get the SIB byte of instruction
- * @insn: &struct insn containing instruction
- *
- * If necessary, first collects the instruction up to and including the
- * ModRM byte.
- */
-void insn_get_sib(struct insn *insn)
-{
- insn_byte_t modrm;
-
- if (insn->sib.got)
- return;
- if (!insn->modrm.got)
- insn_get_modrm(insn);
- if (insn->modrm.nbytes) {
- modrm = (insn_byte_t)insn->modrm.value;
- if (insn->addr_bytes != 2 &&
- X86_MODRM_MOD(modrm) != 3 && X86_MODRM_RM(modrm) == 4) {
- insn->sib.value = get_next(insn_byte_t, insn);
- insn->sib.nbytes = 1;
- }
- }
- insn->sib.got = 1;
-
-err_out:
- return;
-}
-
-
-/**
- * insn_get_displacement() - Get the displacement of instruction
- * @insn: &struct insn containing instruction
- *
- * If necessary, first collects the instruction up to and including the
- * SIB byte.
- * Displacement value is sign-expanded.
- */
-void insn_get_displacement(struct insn *insn)
-{
- insn_byte_t mod, rm, base;
-
- if (insn->displacement.got)
- return;
- if (!insn->sib.got)
- insn_get_sib(insn);
- if (insn->modrm.nbytes) {
- /*
- * Interpreting the modrm byte:
- * mod = 00 - no displacement fields (exceptions below)
- * mod = 01 - 1-byte displacement field
- * mod = 10 - displacement field is 4 bytes, or 2 bytes if
- * address size = 2 (0x67 prefix in 32-bit mode)
- * mod = 11 - no memory operand
- *
- * If address size = 2...
- * mod = 00, r/m = 110 - displacement field is 2 bytes
- *
- * If address size != 2...
- * mod != 11, r/m = 100 - SIB byte exists
- * mod = 00, SIB base = 101 - displacement field is 4 bytes
- * mod = 00, r/m = 101 - rip-relative addressing, displacement
- * field is 4 bytes
- */
- mod = X86_MODRM_MOD(insn->modrm.value);
- rm = X86_MODRM_RM(insn->modrm.value);
- base = X86_SIB_BASE(insn->sib.value);
- if (mod == 3)
- goto out;
- if (mod == 1) {
- insn->displacement.value = get_next(signed char, insn);
- insn->displacement.nbytes = 1;
- } else if (insn->addr_bytes == 2) {
- if ((mod == 0 && rm == 6) || mod == 2) {
- insn->displacement.value =
- get_next(short, insn);
- insn->displacement.nbytes = 2;
- }
- } else {
- if ((mod == 0 && rm == 5) || mod == 2 ||
- (mod == 0 && base == 5)) {
- insn->displacement.value = get_next(int, insn);
- insn->displacement.nbytes = 4;
- }
- }
- }
-out:
- insn->displacement.got = 1;
-
-err_out:
- return;
-}
-
-/* Decode moffset16/32/64. Return 0 if failed */
-static int __get_moffset(struct insn *insn)
-{
- switch (insn->addr_bytes) {
- case 2:
- insn->moffset1.value = get_next(short, insn);
- insn->moffset1.nbytes = 2;
- break;
- case 4:
- insn->moffset1.value = get_next(int, insn);
- insn->moffset1.nbytes = 4;
- break;
- case 8:
- insn->moffset1.value = get_next(int, insn);
- insn->moffset1.nbytes = 4;
- insn->moffset2.value = get_next(int, insn);
- insn->moffset2.nbytes = 4;
- break;
- default: /* opnd_bytes must be modified manually */
- goto err_out;
- }
- insn->moffset1.got = insn->moffset2.got = 1;
-
- return 1;
-
-err_out:
- return 0;
-}
-
-/* Decode imm v32(Iz). Return 0 if failed */
-static int __get_immv32(struct insn *insn)
-{
- switch (insn->opnd_bytes) {
- case 2:
- insn->immediate.value = get_next(short, insn);
- insn->immediate.nbytes = 2;
- break;
- case 4:
- case 8:
- insn->immediate.value = get_next(int, insn);
- insn->immediate.nbytes = 4;
- break;
- default: /* opnd_bytes must be modified manually */
- goto err_out;
- }
-
- return 1;
-
-err_out:
- return 0;
-}
-
-/* Decode imm v64(Iv/Ov), Return 0 if failed */
-static int __get_immv(struct insn *insn)
-{
- switch (insn->opnd_bytes) {
- case 2:
- insn->immediate1.value = get_next(short, insn);
- insn->immediate1.nbytes = 2;
- break;
- case 4:
- insn->immediate1.value = get_next(int, insn);
- insn->immediate1.nbytes = 4;
- break;
- case 8:
- insn->immediate1.value = get_next(int, insn);
- insn->immediate1.nbytes = 4;
- insn->immediate2.value = get_next(int, insn);
- insn->immediate2.nbytes = 4;
- break;
- default: /* opnd_bytes must be modified manually */
- goto err_out;
- }
- insn->immediate1.got = insn->immediate2.got = 1;
-
- return 1;
-err_out:
- return 0;
-}
-
-/* Decode ptr16:16/32(Ap) */
-static int __get_immptr(struct insn *insn)
-{
- switch (insn->opnd_bytes) {
- case 2:
- insn->immediate1.value = get_next(short, insn);
- insn->immediate1.nbytes = 2;
- break;
- case 4:
- insn->immediate1.value = get_next(int, insn);
- insn->immediate1.nbytes = 4;
- break;
- case 8:
- /* ptr16:64 is not exist (no segment) */
- return 0;
- default: /* opnd_bytes must be modified manually */
- goto err_out;
- }
- insn->immediate2.value = get_next(unsigned short, insn);
- insn->immediate2.nbytes = 2;
- insn->immediate1.got = insn->immediate2.got = 1;
-
- return 1;
-err_out:
- return 0;
-}
-
-/**
- * insn_get_immediate() - Get the immediates of instruction
- * @insn: &struct insn containing instruction
- *
- * If necessary, first collects the instruction up to and including the
- * displacement bytes.
- * Basically, most of immediates are sign-expanded. Unsigned-value can be
- * get by bit masking with ((1 << (nbytes * 8)) - 1)
- */
-void insn_get_immediate(struct insn *insn)
-{
- if (insn->immediate.got)
- return;
- if (!insn->displacement.got)
- insn_get_displacement(insn);
-
- if (inat_has_moffset(insn->attr)) {
- if (!__get_moffset(insn))
- goto err_out;
- goto done;
- }
-
- if (!inat_has_immediate(insn->attr))
- /* no immediates */
- goto done;
-
- switch (inat_immediate_size(insn->attr)) {
- case INAT_IMM_BYTE:
- insn->immediate.value = get_next(signed char, insn);
- insn->immediate.nbytes = 1;
- break;
- case INAT_IMM_WORD:
- insn->immediate.value = get_next(short, insn);
- insn->immediate.nbytes = 2;
- break;
- case INAT_IMM_DWORD:
- insn->immediate.value = get_next(int, insn);
- insn->immediate.nbytes = 4;
- break;
- case INAT_IMM_QWORD:
- insn->immediate1.value = get_next(int, insn);
- insn->immediate1.nbytes = 4;
- insn->immediate2.value = get_next(int, insn);
- insn->immediate2.nbytes = 4;
- break;
- case INAT_IMM_PTR:
- if (!__get_immptr(insn))
- goto err_out;
- break;
- case INAT_IMM_VWORD32:
- if (!__get_immv32(insn))
- goto err_out;
- break;
- case INAT_IMM_VWORD:
- if (!__get_immv(insn))
- goto err_out;
- break;
- default:
- /* Here, insn must have an immediate, but failed */
- goto err_out;
- }
- if (inat_has_second_immediate(insn->attr)) {
- insn->immediate2.value = get_next(signed char, insn);
- insn->immediate2.nbytes = 1;
- }
-done:
- insn->immediate.got = 1;
-
-err_out:
- return;
-}
-
-/**
- * insn_get_length() - Get the length of instruction
- * @insn: &struct insn containing instruction
- *
- * If necessary, first collects the instruction up to and including the
- * immediates bytes.
- */
-void insn_get_length(struct insn *insn)
-{
- if (insn->length)
- return;
- if (!insn->immediate.got)
- insn_get_immediate(insn);
- insn->length = (unsigned char)((unsigned long)insn->next_byte
- - (unsigned long)insn->kaddr);
-}
diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index f3b378126011..c807984a03c1 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -1,18 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
diff --git a/tools/objtool/builtin-orc.c b/tools/objtool/builtin-orc.c
index 77ea2b97117d..5f7cc6157edd 100644
--- a/tools/objtool/builtin-orc.c
+++ b/tools/objtool/builtin-orc.c
@@ -1,18 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
diff --git a/tools/objtool/builtin.h b/tools/objtool/builtin.h
index 69762f9c5602..a32736f8d2a4 100644
--- a/tools/objtool/builtin.h
+++ b/tools/objtool/builtin.h
@@ -1,18 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _BUILTIN_H
#define _BUILTIN_H
diff --git a/tools/objtool/cfi.h b/tools/objtool/cfi.h
index 2fe883c665c7..4427bf8ed686 100644
--- a/tools/objtool/cfi.h
+++ b/tools/objtool/cfi.h
@@ -1,18 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _OBJTOOL_CFI_H
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 7325d89ccad9..176f2f084060 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -1,18 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
@@ -30,6 +18,8 @@
#define FAKE_JUMP_OFFSET -1
+#define C_JUMP_TABLE_SECTION ".rodata..c_jump_table"
+
struct alternative {
struct list_head list;
struct instruction *insn;
@@ -107,6 +97,20 @@ static struct instruction *next_insn_same_func(struct objtool_file *file,
for (insn = next_insn_same_sec(file, insn); insn; \
insn = next_insn_same_sec(file, insn))
+static bool is_sibling_call(struct instruction *insn)
+{
+ /* An indirect jump is either a sibling call or a jump to a table. */
+ if (insn->type == INSN_JUMP_DYNAMIC)
+ return list_empty(&insn->alts);
+
+ if (insn->type != INSN_JUMP_CONDITIONAL &&
+ insn->type != INSN_JUMP_UNCONDITIONAL)
+ return false;
+
+ /* add_jump_destinations() sets insn->call_dest for sibling calls. */
+ return !!insn->call_dest;
+}
+
/*
* This checks to see if the given function is a "noreturn" function.
*
@@ -115,14 +119,9 @@ static struct instruction *next_insn_same_func(struct objtool_file *file,
*
* For local functions, we have to detect them manually by simply looking for
* the lack of a return instruction.
- *
- * Returns:
- * -1: error
- * 0: no dead end
- * 1: dead end
*/
-static int __dead_end_function(struct objtool_file *file, struct symbol *func,
- int recursion)
+static bool __dead_end_function(struct objtool_file *file, struct symbol *func,
+ int recursion)
{
int i;
struct instruction *insn;
@@ -148,30 +147,33 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
"rewind_stack_do_exit",
};
+ if (!func)
+ return false;
+
if (func->bind == STB_WEAK)
- return 0;
+ return false;
if (func->bind == STB_GLOBAL)
for (i = 0; i < ARRAY_SIZE(global_noreturns); i++)
if (!strcmp(func->name, global_noreturns[i]))
- return 1;
+ return true;
if (!func->len)
- return 0;
+ return false;
insn = find_insn(file, func->sec, func->offset);
if (!insn->func)
- return 0;
+ return false;
func_for_each_insn_all(file, func, insn) {
empty = false;
if (insn->type == INSN_RETURN)
- return 0;
+ return false;
}
if (empty)
- return 0;
+ return false;
/*
* A function can have a sibling call instead of a return. In that
@@ -179,40 +181,31 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
* of the sibling call returns.
*/
func_for_each_insn_all(file, func, insn) {
- if (insn->type == INSN_JUMP_UNCONDITIONAL) {
+ if (is_sibling_call(insn)) {
struct instruction *dest = insn->jump_dest;
if (!dest)
/* sibling call to another file */
- return 0;
-
- if (dest->func && dest->func->pfunc != insn->func->pfunc) {
-
- /* local sibling call */
- if (recursion == 5) {
- /*
- * Infinite recursion: two functions
- * have sibling calls to each other.
- * This is a very rare case. It means
- * they aren't dead ends.
- */
- return 0;
- }
+ return false;
- return __dead_end_function(file, dest->func,
- recursion + 1);
+ /* local sibling call */
+ if (recursion == 5) {
+ /*
+ * Infinite recursion: two functions have
+ * sibling calls to each other. This is a very
+ * rare case. It means they aren't dead ends.
+ */
+ return false;
}
- }
- if (insn->type == INSN_JUMP_DYNAMIC && list_empty(&insn->alts))
- /* sibling call */
- return 0;
+ return __dead_end_function(file, dest->func, recursion+1);
+ }
}
- return 1;
+ return true;
}
-static int dead_end_function(struct objtool_file *file, struct symbol *func)
+static bool dead_end_function(struct objtool_file *file, struct symbol *func)
{
return __dead_end_function(file, func, 0);
}
@@ -274,19 +267,12 @@ static int decode_instructions(struct objtool_file *file)
if (ret)
goto err;
- if (!insn->type || insn->type > INSN_LAST) {
- WARN_FUNC("invalid instruction type %d",
- insn->sec, insn->offset, insn->type);
- ret = -1;
- goto err;
- }
-
hash_add(file->insn_hash, &insn->hash, insn->offset);
list_add_tail(&insn->list, &file->insn_list);
}
list_for_each_entry(func, &sec->symbol_list, list) {
- if (func->type != STT_FUNC)
+ if (func->type != STT_FUNC || func->alias != func)
continue;
if (!find_insn(file, sec, func->offset)) {
@@ -296,8 +282,7 @@ static int decode_instructions(struct objtool_file *file)
}
func_for_each_insn(file, func, insn)
- if (!insn->func)
- insn->func = func;
+ insn->func = func;
}
}
@@ -500,6 +485,7 @@ static const char *uaccess_safe_builtin[] = {
/* misc */
"csum_partial_copy_generic",
"__memcpy_mcsafe",
+ "mcsafe_handle_tail",
"ftrace_likely_update", /* CONFIG_TRACE_BRANCH_PROFILING */
NULL
};
@@ -517,7 +503,7 @@ static void add_uaccess_safe(struct objtool_file *file)
if (!func)
continue;
- func->alias->uaccess_safe = true;
+ func->uaccess_safe = true;
}
}
@@ -589,13 +575,16 @@ static int add_jump_destinations(struct objtool_file *file)
* Retpoline jumps are really dynamic jumps in
* disguise, so convert them accordingly.
*/
- insn->type = INSN_JUMP_DYNAMIC;
+ if (insn->type == INSN_JUMP_UNCONDITIONAL)
+ insn->type = INSN_JUMP_DYNAMIC;
+ else
+ insn->type = INSN_JUMP_DYNAMIC_CONDITIONAL;
+
insn->retpoline_safe = true;
continue;
} else {
- /* sibling call */
+ /* external sibling call */
insn->call_dest = rela->sym;
- insn->jump_dest = NULL;
continue;
}
@@ -635,7 +624,7 @@ static int add_jump_destinations(struct objtool_file *file)
* However this code can't completely replace the
* read_symbols() code because this doesn't detect the
* case where the parent function's only reference to a
- * subfunction is through a switch table.
+ * subfunction is through a jump table.
*/
if (!strstr(insn->func->name, ".cold.") &&
strstr(insn->jump_dest->func->name, ".cold.")) {
@@ -645,9 +634,8 @@ static int add_jump_destinations(struct objtool_file *file)
} else if (insn->jump_dest->func->pfunc != insn->func->pfunc &&
insn->jump_dest->offset == insn->jump_dest->func->offset) {
- /* sibling class */
+ /* internal sibling call */
insn->call_dest = insn->jump_dest->func;
- insn->jump_dest = NULL;
}
}
}
@@ -908,20 +896,26 @@ out:
return ret;
}
-static int add_switch_table(struct objtool_file *file, struct instruction *insn,
- struct rela *table, struct rela *next_table)
+static int add_jump_table(struct objtool_file *file, struct instruction *insn,
+ struct rela *table)
{
struct rela *rela = table;
- struct instruction *alt_insn;
+ struct instruction *dest_insn;
struct alternative *alt;
struct symbol *pfunc = insn->func->pfunc;
unsigned int prev_offset = 0;
- list_for_each_entry_from(rela, &table->rela_sec->rela_list, list) {
- if (rela == next_table)
+ /*
+ * Each @rela is a switch table relocation which points to the target
+ * instruction.
+ */
+ list_for_each_entry_from(rela, &table->sec->rela_list, list) {
+
+ /* Check for the end of the table: */
+ if (rela != table && rela->jump_table_start)
break;
- /* Make sure the switch table entries are consecutive: */
+ /* Make sure the table entries are consecutive: */
if (prev_offset && rela->offset != prev_offset + 8)
break;
@@ -930,12 +924,12 @@ static int add_switch_table(struct objtool_file *file, struct instruction *insn,
rela->addend == pfunc->offset)
break;
- alt_insn = find_insn(file, rela->sym->sec, rela->addend);
- if (!alt_insn)
+ dest_insn = find_insn(file, rela->sym->sec, rela->addend);
+ if (!dest_insn)
break;
- /* Make sure the jmp dest is in the function or subfunction: */
- if (alt_insn->func->pfunc != pfunc)
+ /* Make sure the destination is in the same function: */
+ if (!dest_insn->func || dest_insn->func->pfunc != pfunc)
break;
alt = malloc(sizeof(*alt));
@@ -944,7 +938,7 @@ static int add_switch_table(struct objtool_file *file, struct instruction *insn,
return -1;
}
- alt->insn = alt_insn;
+ alt->insn = dest_insn;
list_add_tail(&alt->list, &insn->alts);
prev_offset = rela->offset;
}
@@ -959,7 +953,7 @@ static int add_switch_table(struct objtool_file *file, struct instruction *insn,
}
/*
- * find_switch_table() - Given a dynamic jump, find the switch jump table in
+ * find_jump_table() - Given a dynamic jump, find the switch jump table in
* .rodata associated with it.
*
* There are 3 basic patterns:
@@ -1001,13 +995,13 @@ static int add_switch_table(struct objtool_file *file, struct instruction *insn,
*
* NOTE: RETPOLINE made it harder still to decode dynamic jumps.
*/
-static struct rela *find_switch_table(struct objtool_file *file,
+static struct rela *find_jump_table(struct objtool_file *file,
struct symbol *func,
struct instruction *insn)
{
- struct rela *text_rela, *rodata_rela;
+ struct rela *text_rela, *table_rela;
struct instruction *orig_insn = insn;
- struct section *rodata_sec;
+ struct section *table_sec;
unsigned long table_offset;
/*
@@ -1040,42 +1034,52 @@ static struct rela *find_switch_table(struct objtool_file *file,
continue;
table_offset = text_rela->addend;
- rodata_sec = text_rela->sym->sec;
+ table_sec = text_rela->sym->sec;
if (text_rela->type == R_X86_64_PC32)
table_offset += 4;
/*
* Make sure the .rodata address isn't associated with a
- * symbol. gcc jump tables are anonymous data.
+ * symbol. GCC jump tables are anonymous data.
+ *
+ * Also support C jump tables which are in the same format as
+ * switch jump tables. For objtool to recognize them, they
+ * need to be placed in the C_JUMP_TABLE_SECTION section. They
+ * have symbols associated with them.
*/
- if (find_symbol_containing(rodata_sec, table_offset))
+ if (find_symbol_containing(table_sec, table_offset) &&
+ strcmp(table_sec->name, C_JUMP_TABLE_SECTION))
continue;
- rodata_rela = find_rela_by_dest(rodata_sec, table_offset);
- if (rodata_rela) {
- /*
- * Use of RIP-relative switch jumps is quite rare, and
- * indicates a rare GCC quirk/bug which can leave dead
- * code behind.
- */
- if (text_rela->type == R_X86_64_PC32)
- file->ignore_unreachables = true;
+ /* Each table entry has a rela associated with it. */
+ table_rela = find_rela_by_dest(table_sec, table_offset);
+ if (!table_rela)
+ continue;
- return rodata_rela;
- }
+ /*
+ * Use of RIP-relative switch jumps is quite rare, and
+ * indicates a rare GCC quirk/bug which can leave dead code
+ * behind.
+ */
+ if (text_rela->type == R_X86_64_PC32)
+ file->ignore_unreachables = true;
+
+ return table_rela;
}
return NULL;
}
-
-static int add_func_switch_tables(struct objtool_file *file,
- struct symbol *func)
+/*
+ * First pass: Mark the head of each jump table so that in the next pass,
+ * we know when a given jump table ends and the next one starts.
+ */
+static void mark_func_jump_tables(struct objtool_file *file,
+ struct symbol *func)
{
- struct instruction *insn, *last = NULL, *prev_jump = NULL;
- struct rela *rela, *prev_rela = NULL;
- int ret;
+ struct instruction *insn, *last = NULL;
+ struct rela *rela;
func_for_each_insn_all(file, func, insn) {
if (!last)
@@ -1083,7 +1087,7 @@ static int add_func_switch_tables(struct objtool_file *file,
/*
* Store back-pointers for unconditional forward jumps such
- * that find_switch_table() can back-track using those and
+ * that find_jump_table() can back-track using those and
* avoid some potentially confusing code.
*/
if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest &&
@@ -1098,27 +1102,25 @@ static int add_func_switch_tables(struct objtool_file *file,
if (insn->type != INSN_JUMP_DYNAMIC)
continue;
- rela = find_switch_table(file, func, insn);
- if (!rela)
- continue;
-
- /*
- * We found a switch table, but we don't know yet how big it
- * is. Don't add it until we reach the end of the function or
- * the beginning of another switch table in the same function.
- */
- if (prev_jump) {
- ret = add_switch_table(file, prev_jump, prev_rela, rela);
- if (ret)
- return ret;
+ rela = find_jump_table(file, func, insn);
+ if (rela) {
+ rela->jump_table_start = true;
+ insn->jump_table = rela;
}
-
- prev_jump = insn;
- prev_rela = rela;
}
+}
- if (prev_jump) {
- ret = add_switch_table(file, prev_jump, prev_rela, NULL);
+static int add_func_jump_tables(struct objtool_file *file,
+ struct symbol *func)
+{
+ struct instruction *insn;
+ int ret;
+
+ func_for_each_insn_all(file, func, insn) {
+ if (!insn->jump_table)
+ continue;
+
+ ret = add_jump_table(file, insn, insn->jump_table);
if (ret)
return ret;
}
@@ -1131,7 +1133,7 @@ static int add_func_switch_tables(struct objtool_file *file,
* section which contains a list of addresses within the function to jump to.
* This finds these jump tables and adds them to the insn->alts lists.
*/
-static int add_switch_table_alts(struct objtool_file *file)
+static int add_jump_table_alts(struct objtool_file *file)
{
struct section *sec;
struct symbol *func;
@@ -1145,7 +1147,8 @@ static int add_switch_table_alts(struct objtool_file *file)
if (func->type != STT_FUNC)
continue;
- ret = add_func_switch_tables(file, func);
+ mark_func_jump_tables(file, func);
+ ret = add_func_jump_tables(file, func);
if (ret)
return ret;
}
@@ -1289,13 +1292,18 @@ static void mark_rodata(struct objtool_file *file)
bool found = false;
/*
- * This searches for the .rodata section or multiple .rodata.func_name
- * sections if -fdata-sections is being used. The .str.1.1 and .str.1.8
- * rodata sections are ignored as they don't contain jump tables.
+ * Search for the following rodata sections, each of which can
+ * potentially contain jump tables:
+ *
+ * - .rodata: can contain GCC switch tables
+ * - .rodata.<func>: same, if -fdata-sections is being used
+ * - .rodata..c_jump_table: contains C annotated jump tables
+ *
+ * .rodata.str1.* sections are ignored; they don't contain jump tables.
*/
for_each_sec(file, sec) {
- if (!strncmp(sec->name, ".rodata", 7) &&
- !strstr(sec->name, ".str1.")) {
+ if ((!strncmp(sec->name, ".rodata", 7) && !strstr(sec->name, ".str1.")) ||
+ !strcmp(sec->name, C_JUMP_TABLE_SECTION)) {
sec->rodata = true;
found = true;
}
@@ -1337,7 +1345,7 @@ static int decode_sections(struct objtool_file *file)
if (ret)
return ret;
- ret = add_switch_table_alts(file);
+ ret = add_jump_table_alts(file);
if (ret)
return ret;
@@ -1885,12 +1893,12 @@ static bool insn_state_match(struct instruction *insn, struct insn_state *state)
static inline bool func_uaccess_safe(struct symbol *func)
{
if (func)
- return func->alias->uaccess_safe;
+ return func->uaccess_safe;
return false;
}
-static inline const char *insn_dest_name(struct instruction *insn)
+static inline const char *call_dest_name(struct instruction *insn)
{
if (insn->call_dest)
return insn->call_dest->name;
@@ -1902,13 +1910,13 @@ static int validate_call(struct instruction *insn, struct insn_state *state)
{
if (state->uaccess && !func_uaccess_safe(insn->call_dest)) {
WARN_FUNC("call to %s() with UACCESS enabled",
- insn->sec, insn->offset, insn_dest_name(insn));
+ insn->sec, insn->offset, call_dest_name(insn));
return 1;
}
if (state->df) {
WARN_FUNC("call to %s() with DF set",
- insn->sec, insn->offset, insn_dest_name(insn));
+ insn->sec, insn->offset, call_dest_name(insn));
return 1;
}
@@ -1932,13 +1940,13 @@ static int validate_sibling_call(struct instruction *insn, struct insn_state *st
* each instruction and validate all the rules described in
* tools/objtool/Documentation/stack-validation.txt.
*/
-static int validate_branch(struct objtool_file *file, struct instruction *first,
- struct insn_state state)
+static int validate_branch(struct objtool_file *file, struct symbol *func,
+ struct instruction *first, struct insn_state state)
{
struct alternative *alt;
struct instruction *insn, *next_insn;
struct section *sec;
- struct symbol *func = NULL;
+ u8 visited;
int ret;
insn = first;
@@ -1959,21 +1967,18 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
return 1;
}
- if (insn->func)
- func = insn->func->pfunc;
-
if (func && insn->ignore) {
WARN_FUNC("BUG: why am I validating an ignored function?",
sec, insn->offset);
return 1;
}
+ visited = 1 << state.uaccess;
if (insn->visited) {
if (!insn->hint && !insn_state_match(insn, &state))
return 1;
- /* If we were here with AC=0, but now have AC=1, go again */
- if (insn->state.uaccess || !state.uaccess)
+ if (insn->visited & visited)
return 0;
}
@@ -1983,7 +1988,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
i = insn;
save_insn = NULL;
- func_for_each_insn_continue_reverse(file, insn->func, i) {
+ func_for_each_insn_continue_reverse(file, func, i) {
if (i->save) {
save_insn = i;
break;
@@ -2020,7 +2025,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
} else
insn->state = state;
- insn->visited = true;
+ insn->visited |= visited;
if (!insn->ignore_alts) {
bool skip_orig = false;
@@ -2029,7 +2034,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
if (alt->skip_orig)
skip_orig = true;
- ret = validate_branch(file, alt->insn, state);
+ ret = validate_branch(file, func, alt->insn, state);
if (ret) {
if (backtrace)
BT_FUNC("(alt)", insn);
@@ -2067,7 +2072,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
if (state.bp_scratch) {
WARN("%s uses BP as a scratch register",
- insn->func->name);
+ func->name);
return 1;
}
@@ -2079,36 +2084,28 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
if (ret)
return ret;
- if (insn->type == INSN_CALL) {
- if (is_fentry_call(insn))
- break;
-
- ret = dead_end_function(file, insn->call_dest);
- if (ret == 1)
- return 0;
- if (ret == -1)
- return 1;
- }
-
- if (!no_fp && func && !has_valid_stack_frame(&state)) {
+ if (!no_fp && func && !is_fentry_call(insn) &&
+ !has_valid_stack_frame(&state)) {
WARN_FUNC("call without frame pointer save/setup",
sec, insn->offset);
return 1;
}
+
+ if (dead_end_function(file, insn->call_dest))
+ return 0;
+
break;
case INSN_JUMP_CONDITIONAL:
case INSN_JUMP_UNCONDITIONAL:
- if (func && !insn->jump_dest) {
+ if (func && is_sibling_call(insn)) {
ret = validate_sibling_call(insn, &state);
if (ret)
return ret;
- } else if (insn->jump_dest &&
- (!func || !insn->jump_dest->func ||
- insn->jump_dest->func->pfunc == func)) {
- ret = validate_branch(file, insn->jump_dest,
- state);
+ } else if (insn->jump_dest) {
+ ret = validate_branch(file, func,
+ insn->jump_dest, state);
if (ret) {
if (backtrace)
BT_FUNC("(branch)", insn);
@@ -2122,13 +2119,17 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
break;
case INSN_JUMP_DYNAMIC:
- if (func && list_empty(&insn->alts)) {
+ case INSN_JUMP_DYNAMIC_CONDITIONAL:
+ if (func && is_sibling_call(insn)) {
ret = validate_sibling_call(insn, &state);
if (ret)
return ret;
}
- return 0;
+ if (insn->type == INSN_JUMP_DYNAMIC)
+ return 0;
+
+ break;
case INSN_CONTEXT_SWITCH:
if (func && (!next_insn || !next_insn->hint)) {
@@ -2174,7 +2175,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
break;
case INSN_CLAC:
- if (!state.uaccess && insn->func) {
+ if (!state.uaccess && func) {
WARN_FUNC("redundant UACCESS disable", sec, insn->offset);
return 1;
}
@@ -2195,7 +2196,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
break;
case INSN_CLD:
- if (!state.df && insn->func)
+ if (!state.df && func)
WARN_FUNC("redundant CLD", sec, insn->offset);
state.df = false;
@@ -2234,7 +2235,7 @@ static int validate_unwind_hints(struct objtool_file *file)
for_each_insn(file, insn) {
if (insn->hint && !insn->visited) {
- ret = validate_branch(file, insn, state);
+ ret = validate_branch(file, insn->func, insn, state);
if (ret && backtrace)
BT_FUNC("<=== (hint)", insn);
warnings += ret;
@@ -2357,16 +2358,25 @@ static int validate_functions(struct objtool_file *file)
for_each_sec(file, sec) {
list_for_each_entry(func, &sec->symbol_list, list) {
- if (func->type != STT_FUNC || func->pfunc != func)
+ if (func->type != STT_FUNC)
+ continue;
+
+ if (!func->len) {
+ WARN("%s() is missing an ELF size annotation",
+ func->name);
+ warnings++;
+ }
+
+ if (func->pfunc != func || func->alias != func)
continue;
insn = find_insn(file, sec, func->offset);
- if (!insn || insn->ignore)
+ if (!insn || insn->ignore || insn->visited)
continue;
- state.uaccess = func->alias->uaccess_safe;
+ state.uaccess = func->uaccess_safe;
- ret = validate_branch(file, insn, state);
+ ret = validate_branch(file, func, insn, state);
if (ret && backtrace)
BT_FUNC("<=== (func)", insn);
warnings += ret;
@@ -2419,7 +2429,7 @@ int check(const char *_objname, bool orc)
objname = _objname;
- file.elf = elf_open(objname, orc ? O_RDWR : O_RDONLY);
+ file.elf = elf_read(objname, orc ? O_RDWR : O_RDONLY);
if (!file.elf)
return 1;
diff --git a/tools/objtool/check.h b/tools/objtool/check.h
index 71e54f97dbcd..6d875ca6fce0 100644
--- a/tools/objtool/check.h
+++ b/tools/objtool/check.h
@@ -1,18 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _CHECK_H
@@ -43,13 +31,15 @@ struct instruction {
struct section *sec;
unsigned long offset;
unsigned int len;
- unsigned char type;
+ enum insn_type type;
unsigned long immediate;
- bool alt_group, visited, dead_end, ignore, hint, save, restore, ignore_alts;
+ bool alt_group, dead_end, ignore, hint, save, restore, ignore_alts;
bool retpoline_safe;
+ u8 visited;
struct symbol *call_dest;
struct instruction *jump_dest;
struct instruction *first_jump_src;
+ struct rela *jump_table;
struct list_head alts;
struct symbol *func;
struct stack_op stack_op;
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index dd198d53387d..edba4745f25a 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -1,22 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* elf.c - ELF access library
*
* Adapted from kpatch (https://github.com/dynup/kpatch):
* Copyright (C) 2013-2015 Josh Poimboeuf <jpoimboe@redhat.com>
* Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
@@ -290,7 +278,7 @@ static int read_symbols(struct elf *elf)
}
if (sym->offset == s->offset) {
- if (sym->len == s->len && alias == sym)
+ if (sym->len && sym->len == s->len && alias == sym)
alias = s;
if (sym->len >= s->len) {
@@ -397,7 +385,7 @@ static int read_relas(struct elf *elf)
rela->offset = rela->rela.r_offset;
symndx = GELF_R_SYM(rela->rela.r_info);
rela->sym = find_symbol_by_index(elf, symndx);
- rela->rela_sec = sec;
+ rela->sec = sec;
if (!rela->sym) {
WARN("can't find rela entry symbol %d for %s",
symndx, sec->name);
@@ -413,7 +401,7 @@ static int read_relas(struct elf *elf)
return 0;
}
-struct elf *elf_open(const char *name, int flags)
+struct elf *elf_read(const char *name, int flags)
{
struct elf *elf;
Elf_Cmd cmd;
@@ -475,7 +463,7 @@ struct section *elf_create_section(struct elf *elf, const char *name,
{
struct section *sec, *shstrtab;
size_t size = entsize * nr;
- struct Elf_Scn *s;
+ Elf_Scn *s;
Elf_Data *data;
sec = malloc(sizeof(*sec));
diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h
index 2cc2ed49322d..44150204db4d 100644
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -1,18 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _OBJTOOL_ELF_H
@@ -69,11 +57,12 @@ struct rela {
struct list_head list;
struct hlist_node hash;
GElf_Rela rela;
- struct section *rela_sec;
+ struct section *sec;
struct symbol *sym;
unsigned int type;
unsigned long offset;
int addend;
+ bool jump_table_start;
};
struct elf {
@@ -86,7 +75,7 @@ struct elf {
};
-struct elf *elf_open(const char *name, int flags);
+struct elf *elf_read(const char *name, int flags);
struct section *find_section_by_name(struct elf *elf, const char *name);
struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
struct symbol *find_symbol_by_name(struct elf *elf, const char *name);
diff --git a/tools/objtool/objtool.c b/tools/objtool/objtool.c
index 07f329919828..0b3528f05053 100644
--- a/tools/objtool/objtool.c
+++ b/tools/objtool/objtool.c
@@ -1,18 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
diff --git a/tools/objtool/orc.h b/tools/objtool/orc.h
index b0e92a6d0903..ee2832221e62 100644
--- a/tools/objtool/orc.h
+++ b/tools/objtool/orc.h
@@ -1,18 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _ORC_H
diff --git a/tools/objtool/orc_dump.c b/tools/objtool/orc_dump.c
index faa444270ee3..13ccf775a83a 100644
--- a/tools/objtool/orc_dump.c
+++ b/tools/objtool/orc_dump.c
@@ -1,18 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <unistd.h>
diff --git a/tools/objtool/orc_gen.c b/tools/objtool/orc_gen.c
index 3f98dcfbc177..27a4112848c2 100644
--- a/tools/objtool/orc_gen.c
+++ b/tools/objtool/orc_gen.c
@@ -1,18 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
diff --git a/tools/objtool/special.c b/tools/objtool/special.c
index 4e50563d87c6..fdbaa611146d 100644
--- a/tools/objtool/special.c
+++ b/tools/objtool/special.c
@@ -1,18 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
diff --git a/tools/objtool/special.h b/tools/objtool/special.h
index d5c062e718ef..35061530e46e 100644
--- a/tools/objtool/special.h
+++ b/tools/objtool/special.h
@@ -1,18 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _SPECIAL_H
diff --git a/tools/objtool/sync-check.sh b/tools/objtool/sync-check.sh
index 1470e74e9d66..0a832e265a50 100755
--- a/tools/objtool/sync-check.sh
+++ b/tools/objtool/sync-check.sh
@@ -2,28 +2,50 @@
# SPDX-License-Identifier: GPL-2.0
FILES='
-arch/x86/lib/insn.c
-arch/x86/lib/inat.c
-arch/x86/lib/x86-opcode-map.txt
-arch/x86/tools/gen-insn-attr-x86.awk
-arch/x86/include/asm/insn.h
-arch/x86/include/asm/inat.h
arch/x86/include/asm/inat_types.h
arch/x86/include/asm/orc_types.h
+arch/x86/lib/x86-opcode-map.txt
+arch/x86/tools/gen-insn-attr-x86.awk
'
-check()
-{
- local file=$1
+check_2 () {
+ file1=$1
+ file2=$2
- diff $file ../../$file > /dev/null ||
- echo "Warning: synced file at 'tools/objtool/$file' differs from latest kernel version at '$file'"
+ shift
+ shift
+
+ cmd="diff $* $file1 $file2 > /dev/null"
+
+ test -f $file2 && {
+ eval $cmd || {
+ echo "Warning: Kernel ABI header at '$file1' differs from latest version at '$file2'" >&2
+ echo diff -u $file1 $file2
+ }
+ }
+}
+
+check () {
+ file=$1
+
+ shift
+
+ check_2 tools/$file $file $*
}
if [ ! -d ../../kernel ] || [ ! -d ../../tools ] || [ ! -d ../objtool ]; then
exit 0
fi
+cd ../..
+
for i in $FILES; do
check $i
done
+
+check arch/x86/include/asm/inat.h '-I "^#include [\"<]\(asm/\)*inat_types.h[\">]"'
+check arch/x86/include/asm/insn.h '-I "^#include [\"<]\(asm/\)*inat.h[\">]"'
+check arch/x86/lib/inat.c '-I "^#include [\"<]\(../include/\)*asm/insn.h[\">]"'
+check arch/x86/lib/insn.c '-I "^#include [\"<]\(../include/\)*asm/in\(at\|sn\).h[\">]"'
+
+cd -
diff --git a/tools/objtool/warn.h b/tools/objtool/warn.h
index f4fbb972b611..cbb0a02b7480 100644
--- a/tools/objtool/warn.h
+++ b/tools/objtool/warn.h
@@ -1,18 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _WARN_H
diff --git a/tools/pci/Makefile b/tools/pci/Makefile
index 6876ee4bd78c..4b95a5176355 100644
--- a/tools/pci/Makefile
+++ b/tools/pci/Makefile
@@ -18,7 +18,6 @@ ALL_TARGETS := pcitest
ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
SCRIPTS := pcitest.sh
-ALL_SCRIPTS := $(patsubst %,$(OUTPUT)%,$(SCRIPTS))
all: $(ALL_PROGRAMS)
@@ -47,10 +46,10 @@ clean:
install: $(ALL_PROGRAMS)
install -d -m 755 $(DESTDIR)$(bindir); \
- for program in $(ALL_PROGRAMS) pcitest.sh; do \
+ for program in $(ALL_PROGRAMS); do \
install $$program $(DESTDIR)$(bindir); \
done; \
- for script in $(ALL_SCRIPTS); do \
+ for script in $(SCRIPTS); do \
install $$script $(DESTDIR)$(bindir); \
done
diff --git a/tools/pci/pcitest.c b/tools/pci/pcitest.c
index 5fa5c2bdd427..cb1e51fcc84e 100644
--- a/tools/pci/pcitest.c
+++ b/tools/pci/pcitest.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* Userspace PCI Endpoint Test Module
*
* Copyright (C) 2017 Texas Instruments
* Author: Kishon Vijay Abraham I <kishon@ti.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 of
- * the License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
@@ -47,15 +36,15 @@ struct pci_test {
unsigned long size;
};
-static void run_test(struct pci_test *test)
+static int run_test(struct pci_test *test)
{
- long ret;
+ int ret = -EINVAL;
int fd;
fd = open(test->device, O_RDWR);
if (fd < 0) {
perror("can't open PCI Endpoint Test device");
- return;
+ return -ENODEV;
}
if (test->barnum >= 0 && test->barnum <= 5) {
@@ -223,7 +212,7 @@ usage:
"\t-r Read buffer test\n"
"\t-w Write buffer test\n"
"\t-c Copy buffer test\n"
- "\t-s <size> Size of buffer {default: 100KB}\n",
+ "\t-s <size> Size of buffer {default: 100KB}\n"
"\t-h Print this help message\n",
argv[0]);
return -EINVAL;
diff --git a/tools/pcmcia/crc32hash.c b/tools/pcmcia/crc32hash.c
index 44f8beea7260..1a18da9cb6a1 100644
--- a/tools/pcmcia/crc32hash.c
+++ b/tools/pcmcia/crc32hash.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* crc32hash.c - derived from linux/lib/crc32.c, GNU GPL v2 */
/* Usage example:
$ ./crc32hash "Dual Speed"
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore
index 3e5135dded16..bf1252dc2cb0 100644
--- a/tools/perf/.gitignore
+++ b/tools/perf/.gitignore
@@ -34,3 +34,6 @@ arch/*/include/generated/
trace/beauty/generated/
pmu-events/pmu-events.c
pmu-events/jevents
+feature/
+fixdep
+libtraceevent-dynamic-list
diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile
index ac841bc5c35b..adc5a7e44b98 100644
--- a/tools/perf/Documentation/Makefile
+++ b/tools/perf/Documentation/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
include ../../scripts/Makefile.include
include ../../scripts/utilities.mak
@@ -241,7 +242,7 @@ $(OUTPUT)doc.dep : $(wildcard *.txt) build-docdep.perl
$(PERL_PATH) ./build-docdep.perl >$@+ $(QUIET_STDERR) && \
mv $@+ $@
--include $(OUPTUT)doc.dep
+-include $(OUTPUT)doc.dep
_cmds_txt = cmds-ancillaryinterrogators.txt \
cmds-ancillarymanipulators.txt \
diff --git a/tools/perf/Documentation/db-export.txt b/tools/perf/Documentation/db-export.txt
new file mode 100644
index 000000000000..52ffccb02d55
--- /dev/null
+++ b/tools/perf/Documentation/db-export.txt
@@ -0,0 +1,41 @@
+Database Export
+===============
+
+perf tool's python scripting engine:
+
+ tools/perf/util/scripting-engines/trace-event-python.c
+
+supports scripts:
+
+ tools/perf/scripts/python/export-to-sqlite.py
+ tools/perf/scripts/python/export-to-postgresql.py
+
+which export data to a SQLite3 or PostgreSQL database.
+
+The export process provides records with unique sequential ids which allows the
+data to be imported directly to a database and provides the relationships
+between tables.
+
+Over time it is possible to continue to expand the export while maintaining
+backward and forward compatibility, by following some simple rules:
+
+1. Because of the nature of SQL, existing tables and columns can continue to be
+used so long as the names and meanings (and to some extent data types) remain
+the same.
+
+2. New tables and columns can be added, without affecting existing SQL queries,
+so long as the new names are unique.
+
+3. Scripts that use a database (e.g. exported-sql-viewer.py) can maintain
+backward compatibility by testing for the presence of new tables and columns
+before using them. e.g. function IsSelectable() in exported-sql-viewer.py
+
+4. The export scripts themselves maintain forward compatibility (i.e. an existing
+script will continue to work with new versions of perf) by accepting a variable
+number of arguments (e.g. def call_return_table(*x)) i.e. perf can pass more
+arguments which old scripts will ignore.
+
+5. The scripting engine tests for the existence of script handler functions
+before calling them. The scripting engine can also test for the support of new
+or optional features by checking for the existence and value of script global
+variables.
diff --git a/tools/perf/Documentation/intel-pt.txt b/tools/perf/Documentation/intel-pt.txt
index 115eaacc455f..e0d9e7dd4f17 100644
--- a/tools/perf/Documentation/intel-pt.txt
+++ b/tools/perf/Documentation/intel-pt.txt
@@ -88,21 +88,51 @@ smaller.
To represent software control flow, "branches" samples are produced. By default
a branch sample is synthesized for every single branch. To get an idea what
-data is available you can use the 'perf script' tool with no parameters, which
-will list all the samples.
+data is available you can use the 'perf script' tool with all itrace sampling
+options, which will list all the samples.
perf record -e intel_pt//u ls
- perf script
+ perf script --itrace=ibxwpe
An interesting field that is not printed by default is 'flags' which can be
displayed as follows:
- perf script -Fcomm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr,symoff,flags
+ perf script --itrace=ibxwpe -F+flags
The flags are "bcrosyiABEx" which stand for branch, call, return, conditional,
system, asynchronous, interrupt, transaction abort, trace begin, trace end, and
in transaction, respectively.
+Another interesting field that is not printed by default is 'ipc' which can be
+displayed as follows:
+
+ perf script --itrace=be -F+ipc
+
+There are two ways that instructions-per-cycle (IPC) can be calculated depending
+on the recording.
+
+If the 'cyc' config term (see config terms section below) was used, then IPC is
+calculated using the cycle count from CYC packets, otherwise MTC packets are
+used - refer to the 'mtc' config term. When MTC is used, however, the values
+are less accurate because the timing is less accurate.
+
+Because Intel PT does not update the cycle count on every branch or instruction,
+the values will often be zero. When there are values, they will be the number
+of instructions and number of cycles since the last update, and thus represent
+the average IPC since the last IPC for that event type. Note IPC for "branches"
+events is calculated separately from IPC for "instructions" events.
+
+Also note that the IPC instruction count may or may not include the current
+instruction. If the cycle count is associated with an asynchronous branch
+(e.g. page fault or interrupt), then the instruction count does not include the
+current instruction, otherwise it does. That is consistent with whether or not
+that instruction has retired when the cycle count is updated.
+
+Another note, in the case of "branches" events, non-taken branches are not
+presently sampled, so IPC values for them do not appear e.g. a CYC packet with a
+TNT packet that starts with a non-taken branch. To see every possible IPC
+value, "instructions" events can be used e.g. --itrace=i0ns
+
While it is possible to create scripts to analyze the data, an alternative
approach is available to export the data to a sqlite or postgresql database.
Refer to script export-to-sqlite.py or export-to-postgresql.py for more details,
@@ -713,7 +743,7 @@ Having no option is the same as
which, in turn, is the same as
- --itrace=ibxwpe
+ --itrace=cepwx
The letters are:
@@ -889,3 +919,18 @@ amended to take the number of elements as a parameter.
Note there is currently no advantage to using Intel PT instead of LBR, but
that may change in the future if greater use is made of the data.
+
+
+PEBS via Intel PT
+=================
+
+Some hardware has the feature to redirect PEBS records to the Intel PT trace.
+Recording is selected by using the aux-output config term e.g.
+
+ perf record -c 10000 -e '{intel_pt/branch=0/,cycles/aux-output/ppp}' uname
+
+Note that currently, software only supports redirecting at most one PEBS event.
+
+To display PEBS events from the Intel PT trace, use the itrace 'o' option e.g.
+
+ perf script --itrace=oe
diff --git a/tools/perf/Documentation/itrace.txt b/tools/perf/Documentation/itrace.txt
index c2182cbabde3..82ff7dad40c2 100644
--- a/tools/perf/Documentation/itrace.txt
+++ b/tools/perf/Documentation/itrace.txt
@@ -5,6 +5,8 @@
x synthesize transactions events
w synthesize ptwrite events
p synthesize power events
+ o synthesize other events recorded due to the use
+ of aux-output (refer to perf record)
e synthesize error events
d create a debug log
g synthesize a call chain (use with i or x)
diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt
index 462b3cde0675..c599623a1f3d 100644
--- a/tools/perf/Documentation/perf-config.txt
+++ b/tools/perf/Documentation/perf-config.txt
@@ -40,6 +40,10 @@ The '$HOME/.perfconfig' file is used to store a per-user configuration.
The file '$(sysconfdir)/perfconfig' can be used to
store a system-wide default configuration.
+One an disable reading config files by setting the PERF_CONFIG environment
+variable to /dev/null, or provide an alternate config file by setting that
+variable.
+
When reading or writing, the values are read from the system and user
configuration files by default, and options '--system' and '--user'
can be used to tell the command to read from or write to only that location.
@@ -564,9 +568,12 @@ llvm.*::
llvm.clang-bpf-cmd-template::
Cmdline template. Below lines show its default value. Environment
variable is used to pass options.
- "$CLANG_EXEC -D__KERNEL__ $CLANG_OPTIONS $KERNEL_INC_OPTIONS \
- -Wno-unused-value -Wno-pointer-sign -working-directory \
- $WORKING_DIR -c $CLANG_SOURCE -target bpf -O2 -o -"
+ "$CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS "\
+ "-DLINUX_VERSION_CODE=$LINUX_VERSION_CODE " \
+ "$CLANG_OPTIONS $PERF_BPF_INC_OPTIONS $KERNEL_INC_OPTIONS " \
+ "-Wno-unused-value -Wno-pointer-sign " \
+ "-working-directory $WORKING_DIR " \
+ "-c \"$CLANG_SOURCE\" -target bpf $CLANG_EMIT_LLVM -O2 -o - $LLVM_OPTIONS_PIPE"
llvm.clang-opt::
Options passed to clang.
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt
index da7809b15cc9..d5cc15e651cf 100644
--- a/tools/perf/Documentation/perf-diff.txt
+++ b/tools/perf/Documentation/perf-diff.txt
@@ -90,9 +90,10 @@ OPTIONS
-c::
--compute::
- Differential computation selection - delta, ratio, wdiff, delta-abs
- (default is delta-abs). Default can be changed using diff.compute
- config option. See COMPARISON METHODS section for more info.
+ Differential computation selection - delta, ratio, wdiff, cycles,
+ delta-abs (default is delta-abs). Default can be changed using
+ diff.compute config option. See COMPARISON METHODS section for
+ more info.
-p::
--period::
@@ -142,12 +143,14 @@ OPTIONS
perf diff --time 0%-10%,30%-40%
It also supports analyzing samples within a given time window
- <start>,<stop>. Times have the format seconds.microseconds. If 'start'
- is not given (i.e., time string is ',x.y') then analysis starts at
- the beginning of the file. If stop time is not given (i.e, time
- string is 'x.y,') then analysis goes to the end of the file. Time string is
- 'a1.b1,c1.d1:a2.b2,c2.d2'. Use ':' to separate timestamps for different
- perf.data files.
+ <start>,<stop>. Times have the format seconds.nanoseconds. If 'start'
+ is not given (i.e. time string is ',x.y') then analysis starts at
+ the beginning of the file. If stop time is not given (i.e. time
+ string is 'x.y,') then analysis goes to the end of the file.
+ Multiple ranges can be separated by spaces, which requires the argument
+ to be quoted e.g. --time "1234.567,1234.789 1235,"
+ Time string is'a1.b1,c1.d1:a2.b2,c2.d2'. Use ':' to separate timestamps
+ for different perf.data files.
For example, we get the timestamp information from 'perf script'.
@@ -278,6 +281,16 @@ If specified the 'Weighted diff' column is displayed with value 'd' computed as:
- WEIGHT-A being the weight of the data file
- WEIGHT-B being the weight of the baseline data file
+cycles
+~~~~~~
+If specified the '[Program Block Range] Cycles Diff' column is displayed.
+It displays the cycles difference of same program basic block amongst
+two perf.data. The program basic block is the code between two branches.
+
+'[Program Block Range]' indicates the range of a program basic block.
+Source line is reported if it can be found otherwise uses symbol+offset
+instead.
+
SEE ALSO
--------
linkperf:perf-record[1], linkperf:perf-report[1]
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index b6866a05edd2..ed3ecfa422e1 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -194,12 +194,13 @@ PROBE ARGUMENT
--------------
Each probe argument follows below syntax.
- [NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE]
+ [NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE][@user]
'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), local array with fixed index (e.g. array[1], var->array[0], var->pointer[2]), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.)
'$vars' and '$params' special arguments are also available for NAME, '$vars' is expanded to the local variables (including function parameters) which can access at given probe point. '$params' is expanded to only the function parameters.
'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo (*). Currently, basic types (u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal integers (x/x8/x16/x32/x64), signedness casting (u/s), "string" and bitfield are supported. (see TYPES for detail)
On x86 systems %REG is always the short form of the register: for example %AX. %RAX or %EAX is not valid.
+"@user" is a special attribute which means the LOCALVAR will be treated as a user-space memory. This is only valid for kprobe event.
TYPES
-----
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index de269430720a..c6f9f31b6039 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -60,6 +60,8 @@ OPTIONS
- 'name' : User defined event name. Single quotes (') may be used to
escape symbols in the name from parsing by shell and tool
like this: name=\'CPU_CLK_UNHALTED.THREAD:cmask=0x1\'.
+ - 'aux-output': Generate AUX records instead of events. This requires
+ that an AUX area event is also provided.
See the linkperf:perf-list[1] man page for more parameters.
@@ -422,9 +424,14 @@ CLOCK_BOOTTIME, CLOCK_REALTIME and CLOCK_TAI.
-S::
--snapshot::
Select AUX area tracing Snapshot Mode. This option is valid only with an
-AUX area tracing event. Optionally the number of bytes to capture per
-snapshot can be specified. In Snapshot Mode, trace data is captured only when
-signal SIGUSR2 is received.
+AUX area tracing event. Optionally, certain snapshot capturing parameters
+can be specified in a string that follows this option:
+ 'e': take one last snapshot on exit; guarantees that there is at least one
+ snapshot in the output file;
+ <size>: if the PMU supports this, specify the desired snapshot size.
+
+In Snapshot Mode trace data is captured only when signal SIGUSR2 is received
+and on exit if the above 'e' option is given.
--proc-map-timeout::
When processing pre-existing threads /proc/XXX/mmap, it may take a long time,
@@ -490,6 +497,17 @@ Configure all used events to run in kernel space.
--all-user::
Configure all used events to run in user space.
+--kernel-callchains::
+Collect callchains only from kernel space. I.e. this option sets
+perf_event_attr.exclude_callchain_user to 1.
+
+--user-callchains::
+Collect callchains only from user space. I.e. this option sets
+perf_event_attr.exclude_callchain_kernel to 1.
+
+Don't use both --kernel-callchains and --user-callchains at the same time or no
+callchains will be collected.
+
--timestamp-filename
Append timestamp to output file name.
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index f441baa794ce..7315f155803f 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -89,7 +89,7 @@ OPTIONS
- socket: processor socket number the task ran at the time of sample
- srcline: filename and line number executed at the time of sample. The
DWARF debugging info must be provided.
- - srcfile: file name of the source file of the same. Requires dwarf
+ - srcfile: file name of the source file of the samples. Requires dwarf
information.
- weight: Event specific weight, e.g. memory latency or transaction
abort cost. This is the global weight.
@@ -412,12 +412,13 @@ OPTIONS
--time::
Only analyze samples within given time window: <start>,<stop>. Times
- have the format seconds.microseconds. If start is not given (i.e., time
+ have the format seconds.nanoseconds. If start is not given (i.e. time
string is ',x.y') then analysis starts at the beginning of the file. If
- stop time is not given (i.e, time string is 'x.y,') then analysis goes
- to end of file.
+ stop time is not given (i.e. time string is 'x.y,') then analysis goes
+ to end of file. Multiple ranges can be separated by spaces, which
+ requires the argument to be quoted e.g. --time "1234.567,1234.789 1235,"
- Also support time percent with multiple time range. Time string is
+ Also support time percent with multiple time ranges. Time string is
'a%/n,b%/m,...' or 'a%-b%,c%-%d,...'.
For example:
@@ -437,6 +438,23 @@ OPTIONS
perf report --time 0%-10%,30%-40%
+--switch-on EVENT_NAME::
+ Only consider events after this event is found.
+
+ This may be interesting to measure a workload only after some initialization
+ phase is over, i.e. insert a perf probe at that point and then using this
+ option with that probe.
+
+--switch-off EVENT_NAME::
+ Stop considering events after this event is found.
+
+--show-on-off-events::
+ Show the --switch-on/off events too. This has no effect in 'perf report' now
+ but probably we'll make the default not to show the switch-on/off events
+ on the --group mode and if there is only one event besides the off/on ones,
+ go straight to the histogram browser, just like 'perf report' with no events
+ explicitely specified does.
+
--itrace::
Options for decoding instruction tracing data. The options are:
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt
index 9b0d04dd2a61..2599b057e47b 100644
--- a/tools/perf/Documentation/perf-script.txt
+++ b/tools/perf/Documentation/perf-script.txt
@@ -117,7 +117,7 @@ OPTIONS
Comma separated list of fields to print. Options are:
comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff,
srcline, period, iregs, uregs, brstack, brstacksym, flags, bpf-output, brstackinsn,
- brstackoff, callindent, insn, insnlen, synth, phys_addr, metric, misc, srccode.
+ brstackoff, callindent, insn, insnlen, synth, phys_addr, metric, misc, srccode, ipc.
Field list can be prepended with the type, trace, sw or hw,
to indicate to which event type the field list applies.
e.g., -F sw:comm,tid,time,ip,sym and -F trace:time,cpu,trace
@@ -203,6 +203,9 @@ OPTIONS
The synth field is used by synthesized events which may be created when
Instruction Trace decoding.
+ The ipc (instructions per cycle) field is synthesized and may have a value when
+ Instruction Trace decoding.
+
Finally, a user may not set fields to none for all event types.
i.e., -F "" is not allowed.
@@ -225,11 +228,11 @@ OPTIONS
With the metric option perf script can compute metrics for
sampling periods, similar to perf stat. This requires
- specifying a group with multiple metrics with the :S option
+ specifying a group with multiple events defining metrics with the :S option
for perf record. perf will sample on the first event, and
- compute metrics for all the events in the group. Please note
+ print computed metrics for all the events in the group. Please note
that the metric computed is averaged over the whole sampling
- period, not just for the sample point.
+ period (since the last sample), not just for the sample point.
For sample events it's possible to display misc field with -F +misc option,
following letters are displayed for each bit:
@@ -313,6 +316,9 @@ OPTIONS
--show-round-events
Display finished round events i.e. events of type PERF_RECORD_FINISHED_ROUND.
+--show-bpf-events
+ Display bpf events i.e. events of type PERF_RECORD_KSYMBOL and PERF_RECORD_BPF_EVENT.
+
--demangle::
Demangle symbol names to human readable form. It's enabled by default,
disable with --no-demangle.
@@ -355,12 +361,13 @@ include::itrace.txt[]
--time::
Only analyze samples within given time window: <start>,<stop>. Times
- have the format seconds.microseconds. If start is not given (i.e., time
+ have the format seconds.nanoseconds. If start is not given (i.e. time
string is ',x.y') then analysis starts at the beginning of the file. If
- stop time is not given (i.e, time string is 'x.y,') then analysis goes
- to end of file.
+ stop time is not given (i.e. time string is 'x.y,') then analysis goes
+ to end of file. Multiple ranges can be separated by spaces, which
+ requires the argument to be quoted e.g. --time "1234.567,1234.789 1235,"
- Also support time percent with multipe time range. Time string is
+ Also support time percent with multiple time ranges. Time string is
'a%/n,b%/m,...' or 'a%-b%,c%-%d,...'.
For example:
@@ -377,7 +384,7 @@ include::itrace.txt[]
perf script --time 0%-10%,30%-40%
--max-blocks::
- Set the maximum number of program blocks to print with brstackasm for
+ Set the maximum number of program blocks to print with brstackinsn for
each sample.
--reltime::
@@ -410,6 +417,15 @@ include::itrace.txt[]
For itrace only show specified functions and their callees for
itrace. Multiple functions can be separated by comma.
+--switch-on EVENT_NAME::
+ Only consider events after this event is found.
+
+--switch-off EVENT_NAME::
+ Stop considering events after this event is found.
+
+--show-on-off-events::
+ Show the --switch-on/off events too.
+
SEE ALSO
--------
linkperf:perf-record[1], linkperf:perf-script-perl[1],
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt
index 1e312c2672e4..930c51c01201 100644
--- a/tools/perf/Documentation/perf-stat.txt
+++ b/tools/perf/Documentation/perf-stat.txt
@@ -200,6 +200,13 @@ use --per-socket in addition to -a. (system-wide). The output includes the
socket number and the number of online processors on that socket. This is
useful to gauge the amount of aggregation.
+--per-die::
+Aggregate counts per processor die for system-wide mode measurements. This
+is a useful mode to detect imbalance between dies. To enable this mode,
+use --per-die in addition to -a. (system-wide). The output includes the
+die number and the number of online processors on that die. This is
+useful to gauge the amount of aggregation.
+
--per-core::
Aggregate counts per physical processor for system-wide mode measurements. This
is a useful mode to detect imbalance between physical cores. To enable this mode,
@@ -239,6 +246,9 @@ Input file name.
--per-socket::
Aggregate counts per processor socket for system-wide mode measurements.
+--per-die::
+Aggregate counts per processor die for system-wide mode measurements.
+
--per-core::
Aggregate counts per physical processor for system-wide mode measurements.
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
index 44d89fb9c788..5596129a71cf 100644
--- a/tools/perf/Documentation/perf-top.txt
+++ b/tools/perf/Documentation/perf-top.txt
@@ -262,6 +262,49 @@ Default is to monitor all CPUS.
The number of threads to run when synthesizing events for existing processes.
By default, the number of threads equals to the number of online CPUs.
+--namespaces::
+ Record events of type PERF_RECORD_NAMESPACES and display it with the
+ 'cgroup_id' sort key.
+
+--switch-on EVENT_NAME::
+ Only consider events after this event is found.
+
+ E.g.:
+
+ Find out where broadcast packets are handled
+
+ perf probe -L icmp_rcv
+
+ Insert a probe there:
+
+ perf probe icmp_rcv:59
+
+ Start perf top and ask it to only consider the cycles events when a
+ broadcast packet arrives This will show a menu with two entries and
+ will start counting when a broadcast packet arrives:
+
+ perf top -e cycles,probe:icmp_rcv --switch-on=probe:icmp_rcv
+
+ Alternatively one can ask for --group and then two overhead columns
+ will appear, the first for cycles and the second for the switch-on event.
+
+ perf top --group -e cycles,probe:icmp_rcv --switch-on=probe:icmp_rcv
+
+ This may be interesting to measure a workload only after some initialization
+ phase is over, i.e. insert a perf probe at that point and use the above
+ examples replacing probe:icmp_rcv with the just-after-init probe.
+
+--switch-off EVENT_NAME::
+ Stop considering events after this event is found.
+
+--show-on-off-events::
+ Show the --switch-on/off events too. This has no effect in 'perf top' now
+ but probably we'll make the default not to show the switch-on/off events
+ on the --group mode and if there is only one event besides the off/on ones,
+ go straight to the histogram browser, just like 'perf top' with no events
+ explicitely specified does.
+
+
INTERACTIVE PROMPTING KEYS
--------------------------
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt
index fc6e43262c41..25b74fdb36fa 100644
--- a/tools/perf/Documentation/perf-trace.txt
+++ b/tools/perf/Documentation/perf-trace.txt
@@ -176,6 +176,15 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs.
only at exit time or when a syscall is interrupted, i.e. in those cases this
option is equivalent to the number of lines printed.
+--switch-on EVENT_NAME::
+ Only consider events after this event is found.
+
+--switch-off EVENT_NAME::
+ Stop considering events after this event is found.
+
+--show-on-off-events::
+ Show the --switch-on/off events too.
+
--max-stack::
Set the stack depth limit when parsing the callchain, anything
beyond the specified depth will be ignored. Note that at this point
diff --git a/tools/perf/Documentation/perf.data-file-format.txt b/tools/perf/Documentation/perf.data-file-format.txt
index 6967e9b02be5..b0152e1095c5 100644
--- a/tools/perf/Documentation/perf.data-file-format.txt
+++ b/tools/perf/Documentation/perf.data-file-format.txt
@@ -126,7 +126,7 @@ vendor,family,model,stepping. For example: GenuineIntel,6,69,1
HEADER_TOTAL_MEM = 10,
-An uint64_t with the total memory in bytes.
+An uint64_t with the total memory in kilobytes.
HEADER_CMDLINE = 11,
@@ -151,25 +151,45 @@ struct {
HEADER_CPU_TOPOLOGY = 13,
-String lists defining the core and CPU threads topology.
-The string lists are followed by a variable length array
-which contains core_id and socket_id of each cpu.
-The number of entries can be determined by the size of the
-section minus the sizes of both string lists.
-
struct {
+ /*
+ * First revision of HEADER_CPU_TOPOLOGY
+ *
+ * See 'struct perf_header_string_list' definition earlier
+ * in this file.
+ */
+
struct perf_header_string_list cores; /* Variable length */
struct perf_header_string_list threads; /* Variable length */
+
+ /*
+ * Second revision of HEADER_CPU_TOPOLOGY, older tools
+ * will not consider what comes next
+ */
+
struct {
uint32_t core_id;
uint32_t socket_id;
} cpus[nr]; /* Variable length records */
+ /* 'nr' comes from previously processed HEADER_NRCPUS's nr_cpu_avail */
+
+ /*
+ * Third revision of HEADER_CPU_TOPOLOGY, older tools
+ * will not consider what comes next
+ */
+
+ struct perf_header_string_list dies; /* Variable length */
+ uint32_t die_id[nr_cpus_avail]; /* from previously processed HEADER_NR_CPUS, VLA */
};
Example:
- sibling cores : 0-3
+ sibling sockets : 0-8
+ sibling dies : 0-3
+ sibling dies : 4-7
sibling threads : 0-1
sibling threads : 2-3
+ sibling threads : 4-5
+ sibling threads : 6-7
HEADER_NUMA_TOPOLOGY = 14,
@@ -272,6 +292,74 @@ struct {
Two uint64_t for the time of first sample and the time of last sample.
+ HEADER_SAMPLE_TOPOLOGY = 22,
+
+Physical memory map and its node assignments.
+
+The format of data in MEM_TOPOLOGY is as follows:
+
+ u64 version; // Currently 1
+ u64 block_size_bytes; // /sys/devices/system/memory/block_size_bytes
+ u64 count; // number of nodes
+
+struct memory_node {
+ u64 node_id; // node index
+ u64 size; // size of bitmap
+ struct bitmap {
+ /* size of bitmap again */
+ u64 bitmapsize;
+ /* bitmap of memory indexes that belongs to node */
+ /* /sys/devices/system/node/node<NODE>/memory<INDEX> */
+ u64 entries[(bitmapsize/64)+1];
+ }
+}[count];
+
+The MEM_TOPOLOGY can be displayed with following command:
+
+$ perf report --header-only -I
+...
+# memory nodes (nr 1, block size 0x8000000):
+# 0 [7G]: 0-23,32-69
+
+ HEADER_CLOCKID = 23,
+
+One uint64_t for the clockid frequency, specified, for instance, via 'perf
+record -k' (see clock_gettime()), to enable timestamps derived metrics
+conversion into wall clock time on the reporting stage.
+
+ HEADER_DIR_FORMAT = 24,
+
+The data files layout is described by HEADER_DIR_FORMAT feature. Currently it
+holds only version number (1):
+
+ uint64_t version;
+
+The current version holds only version value (1) means that data files:
+
+- Follow the 'data.*' name format.
+
+- Contain raw events data in standard perf format as read from kernel (and need
+ to be sorted)
+
+Future versions are expected to describe different data files layout according
+to special needs.
+
+ HEADER_BPF_PROG_INFO = 25,
+
+struct bpf_prog_info_linear, which contains detailed information about
+a BPF program, including type, id, tag, jited/xlated instructions, etc.
+
+ HEADER_BPF_BTF = 26,
+
+Contains BPF Type Format (BTF). For more information about BTF, please
+refer to Documentation/bpf/btf.rst.
+
+struct {
+ u32 id;
+ u32 data_size;
+ char data[];
+};
+
HEADER_COMPRESSED = 27,
struct {
diff --git a/tools/perf/Documentation/tips.txt b/tools/perf/Documentation/tips.txt
index 869965d629ce..825745a645c1 100644
--- a/tools/perf/Documentation/tips.txt
+++ b/tools/perf/Documentation/tips.txt
@@ -38,6 +38,6 @@ To report cacheline events from previous recording: perf c2c report
To browse sample contexts use perf report --sample 10 and select in context menu
To separate samples by time use perf report --sort time,overhead,sym
To set sample time separation other than 100ms with --sort time use --time-quantum
-Add -I to perf report to sample register values visible in perf report context.
+Add -I to perf record to sample register values, which will be visible in perf report sample context.
To show IPC for sampling periods use perf record -e '{cycles,instructions}:S' and then browse context
To show context switches in perf report sample context add --switch-events to perf record.
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST
index 627b7cada144..70f1ff4e2eb4 100644
--- a/tools/perf/MANIFEST
+++ b/tools/perf/MANIFEST
@@ -7,6 +7,8 @@ tools/lib/traceevent
tools/lib/api
tools/lib/bpf
tools/lib/subcmd
+tools/lib/argv_split.c
+tools/lib/ctype.c
tools/lib/hweight.c
tools/lib/rbtree.c
tools/lib/string.c
@@ -16,3 +18,4 @@ tools/lib/find_bit.c
tools/lib/bitmap.c
tools/lib/str_error_r.c
tools/lib/vsprintf.c
+tools/lib/zalloc.c
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index e1bb5288ab1f..a269d78456b6 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
ifeq ($(src-perf),)
src-perf := $(srctree)/tools/perf
@@ -59,6 +60,10 @@ ifeq ($(SRCARCH),arm64)
LIBUNWIND_LIBS = -lunwind -lunwind-aarch64
endif
+ifeq ($(SRCARCH),riscv)
+ NO_PERF_REGS := 0
+endif
+
ifeq ($(SRCARCH),csky)
NO_PERF_REGS := 0
endif
@@ -81,7 +86,7 @@ endif
# Disable it on all other architectures in case libdw unwind
# support is detected in system. Add supported architectures
# to the check.
-ifneq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc s390 csky))
+ifneq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc s390 csky riscv))
NO_LIBDW_DWARF_UNWIND := 1
endif
@@ -276,11 +281,12 @@ ifeq ($(DEBUG),0)
endif
endif
+INC_FLAGS += -I$(src-perf)/lib/include
INC_FLAGS += -I$(src-perf)/util/include
INC_FLAGS += -I$(src-perf)/arch/$(SRCARCH)/include
-INC_FLAGS += -I$(srctree)/tools/include/uapi
INC_FLAGS += -I$(srctree)/tools/include/
INC_FLAGS += -I$(srctree)/tools/arch/$(SRCARCH)/include/uapi
+INC_FLAGS += -I$(srctree)/tools/include/uapi
INC_FLAGS += -I$(srctree)/tools/arch/$(SRCARCH)/include/
INC_FLAGS += -I$(srctree)/tools/arch/$(SRCARCH)/
@@ -331,6 +337,10 @@ ifeq ($(feature-get_current_dir_name), 1)
CFLAGS += -DHAVE_GET_CURRENT_DIR_NAME
endif
+ifeq ($(feature-gettid), 1)
+ CFLAGS += -DHAVE_GETTID
+endif
+
ifdef NO_LIBELF
NO_DWARF := 1
NO_DEMANGLE := 1
@@ -412,6 +422,9 @@ ifdef CORESIGHT
$(call feature_check,libopencsd)
ifeq ($(feature-libopencsd), 1)
CFLAGS += -DHAVE_CSTRACE_SUPPORT $(LIBOPENCSD_CFLAGS)
+ ifeq ($(feature-reallocarray), 0)
+ CFLAGS += -DCOMPAT_NEED_REALLOCARRAY
+ endif
LDFLAGS += $(LIBOPENCSD_LDFLAGS)
EXTLIBS += $(OPENCSDLIBS)
$(call detected,CONFIG_LIBOPENCSD)
@@ -636,11 +649,15 @@ endif
ifndef NO_SLANG
ifneq ($(feature-libslang), 1)
- msg := $(warning slang not found, disables TUI support. Please install slang-devel, libslang-dev or libslang2-dev);
- NO_SLANG := 1
- else
+ ifneq ($(feature-libslang-include-subdir), 1)
+ msg := $(warning slang not found, disables TUI support. Please install slang-devel, libslang-dev or libslang2-dev);
+ NO_SLANG := 1
+ else
+ CFLAGS += -DHAVE_SLANG_INCLUDE_SUBDIR
+ endif
+ endif
+ ifndef NO_SLANG
# Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h
- CFLAGS += -I/usr/include/slang
CFLAGS += -DHAVE_SLANG_SUPPORT
EXTLIBS += -lslang
$(call detected,CONFIG_SLANG)
@@ -811,6 +828,17 @@ ifndef NO_LIBZSTD
endif
endif
+ifndef NO_LIBCAP
+ ifeq ($(feature-libcap), 1)
+ CFLAGS += -DHAVE_LIBCAP_SUPPORT
+ EXTLIBS += -lcap
+ $(call detected,CONFIG_LIBCAP)
+ else
+ msg := $(warning No libcap found, disables capability support, please install libcap-devel/libcap-dev);
+ NO_LIBCAP := 1
+ endif
+endif
+
ifndef NO_BACKTRACE
ifeq ($(feature-backtrace), 1)
CFLAGS += -DHAVE_BACKTRACE_SUPPORT
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index c706548d5b10..f9807d8c005b 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
include ../scripts/Makefile.include
include ../scripts/Makefile.arch
@@ -87,6 +88,8 @@ include ../scripts/utilities.mak
#
# Define NO_LIBBPF if you do not want BPF support
#
+# Define NO_LIBCAP if you do not want process capabilities considered by perf
+#
# Define NO_SDT if you do not want to define SDT event in perf tools,
# note that it doesn't disable SDT scanning support.
#
@@ -223,6 +226,7 @@ LIB_DIR = $(srctree)/tools/lib/api/
TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/
BPF_DIR = $(srctree)/tools/lib/bpf/
SUBCMD_DIR = $(srctree)/tools/lib/subcmd/
+LIBPERF_DIR = $(srctree)/tools/perf/lib/
# Set FEATURE_TESTS to 'all' so all possible feature checkers are executed.
# Without this setting the output feature dump file misses some features, for
@@ -271,6 +275,7 @@ ifneq ($(OUTPUT),)
TE_PATH=$(OUTPUT)
BPF_PATH=$(OUTPUT)
SUBCMD_PATH=$(OUTPUT)
+ LIBPERF_PATH=$(OUTPUT)
ifneq ($(subdir),)
API_PATH=$(OUTPUT)/../lib/api/
else
@@ -281,6 +286,7 @@ else
API_PATH=$(LIB_DIR)
BPF_PATH=$(BPF_DIR)
SUBCMD_PATH=$(SUBCMD_DIR)
+ LIBPERF_PATH=$(LIBPERF_DIR)
endif
LIBTRACEEVENT = $(TE_PATH)libtraceevent.a
@@ -302,6 +308,9 @@ LIBBPF = $(BPF_PATH)libbpf.a
LIBSUBCMD = $(SUBCMD_PATH)libsubcmd.a
+LIBPERF = $(LIBPERF_PATH)libperf.a
+export LIBPERF
+
# python extension build directories
PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/
PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/
@@ -347,9 +356,7 @@ endif
export PERL_PATH
-LIBPERF_A=$(OUTPUT)libperf.a
-
-PERFLIBS = $(LIBAPI) $(LIBTRACEEVENT) $(LIBSUBCMD)
+PERFLIBS = $(LIBAPI) $(LIBTRACEEVENT) $(LIBSUBCMD) $(LIBPERF)
ifndef NO_LIBBPF
PERFLIBS += $(LIBBPF)
endif
@@ -419,6 +426,24 @@ fadvise_advice_tbl := $(srctree)/tools/perf/trace/beauty/fadvise.sh
$(fadvise_advice_array): $(linux_uapi_dir)/in.h $(fadvise_advice_tbl)
$(Q)$(SHELL) '$(fadvise_advice_tbl)' $(linux_uapi_dir) > $@
+fsmount_arrays := $(beauty_outdir)/fsmount_arrays.c
+fsmount_tbls := $(srctree)/tools/perf/trace/beauty/fsmount.sh
+
+$(fsmount_arrays): $(linux_uapi_dir)/fs.h $(fsmount_tbls)
+ $(Q)$(SHELL) '$(fsmount_tbls)' $(linux_uapi_dir) > $@
+
+fspick_arrays := $(beauty_outdir)/fspick_arrays.c
+fspick_tbls := $(srctree)/tools/perf/trace/beauty/fspick.sh
+
+$(fspick_arrays): $(linux_uapi_dir)/fs.h $(fspick_tbls)
+ $(Q)$(SHELL) '$(fspick_tbls)' $(linux_uapi_dir) > $@
+
+fsconfig_arrays := $(beauty_outdir)/fsconfig_arrays.c
+fsconfig_tbls := $(srctree)/tools/perf/trace/beauty/fsconfig.sh
+
+$(fsconfig_arrays): $(linux_uapi_dir)/fs.h $(fsconfig_tbls)
+ $(Q)$(SHELL) '$(fsconfig_tbls)' $(linux_uapi_dir) > $@
+
pkey_alloc_access_rights_array := $(beauty_outdir)/pkey_alloc_access_rights_array.c
asm_generic_hdr_dir := $(srctree)/tools/include/uapi/asm-generic/
pkey_alloc_access_rights_tbl := $(srctree)/tools/perf/trace/beauty/pkey_alloc_access_rights.sh
@@ -493,6 +518,12 @@ mount_flags_tbl := $(srctree)/tools/perf/trace/beauty/mount_flags.sh
$(mount_flags_array): $(linux_uapi_dir)/fs.h $(mount_flags_tbl)
$(Q)$(SHELL) '$(mount_flags_tbl)' $(linux_uapi_dir) > $@
+move_mount_flags_array := $(beauty_outdir)/move_mount_flags_array.c
+move_mount_flags_tbl := $(srctree)/tools/perf/trace/beauty/move_mount_flags.sh
+
+$(move_mount_flags_array): $(linux_uapi_dir)/fs.h $(move_mount_flags_tbl)
+ $(Q)$(SHELL) '$(move_mount_flags_tbl)' $(linux_uapi_dir) > $@
+
prctl_option_array := $(beauty_outdir)/prctl_option_array.c
prctl_hdr_dir := $(srctree)/tools/include/uapi/linux/
prctl_option_tbl := $(srctree)/tools/perf/trace/beauty/prctl_option.sh
@@ -525,6 +556,12 @@ arch_errno_tbl := $(srctree)/tools/perf/trace/beauty/arch_errno_names.sh
$(arch_errno_name_array): $(arch_errno_tbl)
$(Q)$(SHELL) '$(arch_errno_tbl)' $(CC) $(arch_errno_hdr_dir) > $@
+sync_file_range_arrays := $(beauty_outdir)/sync_file_range_arrays.c
+sync_file_range_tbls := $(srctree)/tools/perf/trace/beauty/sync_file_range.sh
+
+$(sync_file_range_arrays): $(linux_uapi_dir)/fs.h $(sync_file_range_tbls)
+ $(Q)$(SHELL) '$(sync_file_range_tbls)' $(linux_uapi_dir) > $@
+
all: shell_compatibility_test $(ALL_PROGRAMS) $(LANG_BINDINGS) $(OTHER_PROGRAMS)
# Create python binding output directory if not already present
@@ -552,8 +589,6 @@ JEVENTS_IN := $(OUTPUT)pmu-events/jevents-in.o
PMU_EVENTS_IN := $(OUTPUT)pmu-events/pmu-events-in.o
-LIBPERF_IN := $(OUTPUT)libperf-in.o
-
export JEVENTS
build := -f $(srctree)/tools/build/Makefile.build dir=. obj
@@ -570,12 +605,9 @@ $(JEVENTS): $(JEVENTS_IN)
$(PMU_EVENTS_IN): $(JEVENTS) FORCE
$(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=pmu-events obj=pmu-events
-$(LIBPERF_IN): prepare FORCE
- $(Q)$(MAKE) $(build)=libperf
-
-$(OUTPUT)perf: $(PERFLIBS) $(PERF_IN) $(PMU_EVENTS_IN) $(LIBPERF_IN) $(LIBTRACEEVENT_DYNAMIC_LIST)
+$(OUTPUT)perf: $(PERFLIBS) $(PERF_IN) $(PMU_EVENTS_IN) $(LIBTRACEEVENT_DYNAMIC_LIST)
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS) \
- $(PERF_IN) $(PMU_EVENTS_IN) $(LIBPERF_IN) $(LIBS) -o $@
+ $(PERF_IN) $(PMU_EVENTS_IN) $(LIBS) -o $@
$(GTK_IN): FORCE
$(Q)$(MAKE) $(build)=gtk
@@ -628,6 +660,9 @@ build-dir = $(if $(__build-dir),$(__build-dir),.)
prepare: $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h archheaders $(drm_ioctl_array) \
$(fadvise_advice_array) \
+ $(fsconfig_arrays) \
+ $(fsmount_arrays) \
+ $(fspick_arrays) \
$(pkey_alloc_access_rights_array) \
$(sndrv_pcm_ioctl_array) \
$(sndrv_ctl_ioctl_array) \
@@ -638,12 +673,14 @@ prepare: $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h archheaders $(drm_ioc
$(madvise_behavior_array) \
$(mmap_flags_array) \
$(mount_flags_array) \
+ $(move_mount_flags_array) \
$(perf_ioctl_array) \
$(prctl_option_array) \
$(usbdevfs_ioctl_array) \
$(x86_arch_prctl_code_array) \
$(rename_flags_array) \
- $(arch_errno_name_array)
+ $(arch_errno_name_array) \
+ $(sync_file_range_arrays)
$(OUTPUT)%.o: %.c prepare FORCE
$(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=$(build-dir) $@
@@ -691,9 +728,6 @@ endif
$(patsubst perf-%,%.o,$(PROGRAMS)): $(wildcard */*.h)
-$(LIBPERF_A): $(LIBPERF_IN)
- $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIBPERF_IN) $(LIB_OBJS)
-
LIBTRACEEVENT_FLAGS += plugin_dir=$(plugindir_SQ) 'EXTRA_CFLAGS=$(EXTRA_CFLAGS)' 'LDFLAGS=$(LDFLAGS)'
$(LIBTRACEEVENT): FORCE
@@ -726,6 +760,13 @@ $(LIBBPF)-clean:
$(call QUIET_CLEAN, libbpf)
$(Q)$(MAKE) -C $(BPF_DIR) O=$(OUTPUT) clean >/dev/null
+$(LIBPERF): FORCE
+ $(Q)$(MAKE) -C $(LIBPERF_DIR) O=$(OUTPUT) $(OUTPUT)libperf.a
+
+$(LIBPERF)-clean:
+ $(call QUIET_CLEAN, libperf)
+ $(Q)$(MAKE) -C $(LIBPERF_DIR) O=$(OUTPUT) clean >/dev/null
+
$(LIBSUBCMD): FORCE
$(Q)$(MAKE) -C $(SUBCMD_DIR) O=$(OUTPUT) $(OUTPUT)libsubcmd.a
@@ -912,7 +953,7 @@ config-clean:
python-clean:
$(python-clean)
-clean:: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean config-clean fixdep-clean python-clean
+clean:: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean $(LIBPERF)-clean config-clean fixdep-clean python-clean
$(call QUIET_CLEAN, core-objs) $(RM) $(LIBPERF_A) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS)
$(Q)find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
$(Q)$(RM) $(OUTPUT).config-detected
@@ -922,9 +963,13 @@ clean:: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clea
$(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c \
$(OUTPUT)pmu-events/pmu-events.c \
$(OUTPUT)$(fadvise_advice_array) \
+ $(OUTPUT)$(fsconfig_arrays) \
+ $(OUTPUT)$(fsmount_arrays) \
+ $(OUTPUT)$(fspick_arrays) \
$(OUTPUT)$(madvise_behavior_array) \
$(OUTPUT)$(mmap_flags_array) \
$(OUTPUT)$(mount_flags_array) \
+ $(OUTPUT)$(move_mount_flags_array) \
$(OUTPUT)$(drm_ioctl_array) \
$(OUTPUT)$(pkey_alloc_access_rights_array) \
$(OUTPUT)$(sndrv_ctl_ioctl_array) \
@@ -938,7 +983,8 @@ clean:: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clea
$(OUTPUT)$(usbdevfs_ioctl_array) \
$(OUTPUT)$(x86_arch_prctl_code_array) \
$(OUTPUT)$(rename_flags_array) \
- $(OUTPUT)$(arch_errno_name_array)
+ $(OUTPUT)$(arch_errno_name_array) \
+ $(OUTPUT)$(sync_file_range_arrays)
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean
#
diff --git a/tools/perf/arch/arm/Makefile b/tools/perf/arch/arm/Makefile
index 18b13518d8d8..1d88fdab13bf 100644
--- a/tools/perf/arch/arm/Makefile
+++ b/tools/perf/arch/arm/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
ifndef NO_DWARF
PERF_HAVE_DWARF_REGS := 1
endif
diff --git a/tools/perf/arch/arm/annotate/instructions.c b/tools/perf/arch/arm/annotate/instructions.c
index f64516d5b23e..e1d4b484cc4b 100644
--- a/tools/perf/arch/arm/annotate/instructions.c
+++ b/tools/perf/arch/arm/annotate/instructions.c
@@ -1,7 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
+#include <linux/zalloc.h>
#include <sys/types.h>
#include <regex.h>
+#include <stdlib.h>
struct arm_annotate {
regex_t call_insn,
diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c
index 1ce6bdbda561..0a6e75b8777a 100644
--- a/tools/perf/arch/arm/util/auxtrace.c
+++ b/tools/perf/arch/arm/util/auxtrace.c
@@ -6,8 +6,10 @@
#include <stdbool.h>
#include <linux/coresight-pmu.h>
+#include <linux/zalloc.h>
#include "../../util/auxtrace.h"
+#include "../../util/debug.h"
#include "../../util/evlist.h"
#include "../../util/pmu.h"
#include "cs-etm.h"
@@ -49,10 +51,10 @@ static struct perf_pmu **find_all_arm_spe_pmus(int *nr_spes, int *err)
}
struct auxtrace_record
-*auxtrace_record__init(struct perf_evlist *evlist, int *err)
+*auxtrace_record__init(struct evlist *evlist, int *err)
{
struct perf_pmu *cs_etm_pmu;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
bool found_etm = false;
bool found_spe = false;
static struct perf_pmu **arm_spe_pmus = NULL;
@@ -69,14 +71,14 @@ struct auxtrace_record
evlist__for_each_entry(evlist, evsel) {
if (cs_etm_pmu &&
- evsel->attr.type == cs_etm_pmu->type)
+ evsel->core.attr.type == cs_etm_pmu->type)
found_etm = true;
if (!nr_spes)
continue;
for (i = 0; i < nr_spes; i++) {
- if (evsel->attr.type == arm_spe_pmus[i]->type) {
+ if (evsel->core.attr.type == arm_spe_pmus[i]->type) {
found_spe = true;
break;
}
diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/cs-etm.c
index 911426721170..c32db09baf0d 100644
--- a/tools/perf/arch/arm/util/cs-etm.c
+++ b/tools/perf/arch/arm/util/cs-etm.c
@@ -11,17 +11,22 @@
#include <linux/coresight-pmu.h>
#include <linux/kernel.h>
#include <linux/log2.h>
+#include <linux/string.h>
#include <linux/types.h>
+#include <linux/zalloc.h>
#include "cs-etm.h"
-#include "../../perf.h"
+#include "../../util/debug.h"
+#include "../../util/record.h"
#include "../../util/auxtrace.h"
#include "../../util/cpumap.h"
+#include "../../util/event.h"
#include "../../util/evlist.h"
#include "../../util/evsel.h"
#include "../../util/pmu.h"
-#include "../../util/thread_map.h"
#include "../../util/cs-etm.h"
+#include "../../util/util.h"
+#include "../../util/session.h"
#include <errno.h>
#include <stdlib.h>
@@ -30,13 +35,159 @@
struct cs_etm_recording {
struct auxtrace_record itr;
struct perf_pmu *cs_etm_pmu;
- struct perf_evlist *evlist;
+ struct evlist *evlist;
+ int wrapped_cnt;
+ bool *wrapped;
bool snapshot_mode;
size_t snapshot_size;
};
+static const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = {
+ [CS_ETM_ETMCCER] = "mgmt/etmccer",
+ [CS_ETM_ETMIDR] = "mgmt/etmidr",
+};
+
+static const char *metadata_etmv4_ro[CS_ETMV4_PRIV_MAX] = {
+ [CS_ETMV4_TRCIDR0] = "trcidr/trcidr0",
+ [CS_ETMV4_TRCIDR1] = "trcidr/trcidr1",
+ [CS_ETMV4_TRCIDR2] = "trcidr/trcidr2",
+ [CS_ETMV4_TRCIDR8] = "trcidr/trcidr8",
+ [CS_ETMV4_TRCAUTHSTATUS] = "mgmt/trcauthstatus",
+};
+
static bool cs_etm_is_etmv4(struct auxtrace_record *itr, int cpu);
+static int cs_etm_set_context_id(struct auxtrace_record *itr,
+ struct evsel *evsel, int cpu)
+{
+ struct cs_etm_recording *ptr;
+ struct perf_pmu *cs_etm_pmu;
+ char path[PATH_MAX];
+ int err = -EINVAL;
+ u32 val;
+
+ ptr = container_of(itr, struct cs_etm_recording, itr);
+ cs_etm_pmu = ptr->cs_etm_pmu;
+
+ if (!cs_etm_is_etmv4(itr, cpu))
+ goto out;
+
+ /* Get a handle on TRCIRD2 */
+ snprintf(path, PATH_MAX, "cpu%d/%s",
+ cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR2]);
+ err = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val);
+
+ /* There was a problem reading the file, bailing out */
+ if (err != 1) {
+ pr_err("%s: can't read file %s\n",
+ CORESIGHT_ETM_PMU_NAME, path);
+ goto out;
+ }
+
+ /*
+ * TRCIDR2.CIDSIZE, bit [9-5], indicates whether contextID tracing
+ * is supported:
+ * 0b00000 Context ID tracing is not supported.
+ * 0b00100 Maximum of 32-bit Context ID size.
+ * All other values are reserved.
+ */
+ val = BMVAL(val, 5, 9);
+ if (!val || val != 0x4) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* All good, let the kernel know */
+ evsel->core.attr.config |= (1 << ETM_OPT_CTXTID);
+ err = 0;
+
+out:
+
+ return err;
+}
+
+static int cs_etm_set_timestamp(struct auxtrace_record *itr,
+ struct evsel *evsel, int cpu)
+{
+ struct cs_etm_recording *ptr;
+ struct perf_pmu *cs_etm_pmu;
+ char path[PATH_MAX];
+ int err = -EINVAL;
+ u32 val;
+
+ ptr = container_of(itr, struct cs_etm_recording, itr);
+ cs_etm_pmu = ptr->cs_etm_pmu;
+
+ if (!cs_etm_is_etmv4(itr, cpu))
+ goto out;
+
+ /* Get a handle on TRCIRD0 */
+ snprintf(path, PATH_MAX, "cpu%d/%s",
+ cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR0]);
+ err = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val);
+
+ /* There was a problem reading the file, bailing out */
+ if (err != 1) {
+ pr_err("%s: can't read file %s\n",
+ CORESIGHT_ETM_PMU_NAME, path);
+ goto out;
+ }
+
+ /*
+ * TRCIDR0.TSSIZE, bit [28-24], indicates whether global timestamping
+ * is supported:
+ * 0b00000 Global timestamping is not implemented
+ * 0b00110 Implementation supports a maximum timestamp of 48bits.
+ * 0b01000 Implementation supports a maximum timestamp of 64bits.
+ */
+ val &= GENMASK(28, 24);
+ if (!val) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* All good, let the kernel know */
+ evsel->core.attr.config |= (1 << ETM_OPT_TS);
+ err = 0;
+
+out:
+ return err;
+}
+
+static int cs_etm_set_option(struct auxtrace_record *itr,
+ struct evsel *evsel, u32 option)
+{
+ int i, err = -EINVAL;
+ struct perf_cpu_map *event_cpus = evsel->evlist->core.cpus;
+ struct perf_cpu_map *online_cpus = perf_cpu_map__new(NULL);
+
+ /* Set option of each CPU we have */
+ for (i = 0; i < cpu__max_cpu(); i++) {
+ if (!cpu_map__has(event_cpus, i) ||
+ !cpu_map__has(online_cpus, i))
+ continue;
+
+ if (option & ETM_OPT_CTXTID) {
+ err = cs_etm_set_context_id(itr, evsel, i);
+ if (err)
+ goto out;
+ }
+ if (option & ETM_OPT_TS) {
+ err = cs_etm_set_timestamp(itr, evsel, i);
+ if (err)
+ goto out;
+ }
+ if (option & ~(ETM_OPT_CTXTID | ETM_OPT_TS))
+ /* Nothing else is currently supported */
+ goto out;
+ }
+
+ err = 0;
+out:
+ perf_cpu_map__put(online_cpus);
+ return err;
+}
+
static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr,
struct record_opts *opts,
const char *str)
@@ -60,14 +211,14 @@ static int cs_etm_parse_snapshot_options(struct auxtrace_record *itr,
}
static int cs_etm_set_sink_attr(struct perf_pmu *pmu,
- struct perf_evsel *evsel)
+ struct evsel *evsel)
{
char msg[BUFSIZ], path[PATH_MAX], *sink;
struct perf_evsel_config_term *term;
int ret = -EINVAL;
u32 hash;
- if (evsel->attr.config2 & GENMASK(31, 0))
+ if (evsel->core.attr.config2 & GENMASK(31, 0))
return 0;
list_for_each_entry(term, &evsel->config_terms, list) {
@@ -85,7 +236,7 @@ static int cs_etm_set_sink_attr(struct perf_pmu *pmu,
return ret;
}
- evsel->attr.config2 |= hash;
+ evsel->core.attr.config2 |= hash;
return 0;
}
@@ -97,29 +248,33 @@ static int cs_etm_set_sink_attr(struct perf_pmu *pmu,
}
static int cs_etm_recording_options(struct auxtrace_record *itr,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
struct record_opts *opts)
{
int ret;
struct cs_etm_recording *ptr =
container_of(itr, struct cs_etm_recording, itr);
struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
- struct perf_evsel *evsel, *cs_etm_evsel = NULL;
- const struct cpu_map *cpus = evlist->cpus;
- bool privileged = (geteuid() == 0 || perf_event_paranoid() < 0);
+ struct evsel *evsel, *cs_etm_evsel = NULL;
+ struct perf_cpu_map *cpus = evlist->core.cpus;
+ bool privileged = perf_event_paranoid_check(-1);
+ int err = 0;
ptr->evlist = evlist;
ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
+ if (perf_can_record_switch_events())
+ opts->record_switch_events = true;
+
evlist__for_each_entry(evlist, evsel) {
- if (evsel->attr.type == cs_etm_pmu->type) {
+ if (evsel->core.attr.type == cs_etm_pmu->type) {
if (cs_etm_evsel) {
pr_err("There may be only one %s event\n",
CORESIGHT_ETM_PMU_NAME);
return -EINVAL;
}
- evsel->attr.freq = 0;
- evsel->attr.sample_period = 1;
+ evsel->core.attr.freq = 0;
+ evsel->core.attr.sample_period = 1;
cs_etm_evsel = evsel;
opts->full_auxtrace = true;
}
@@ -241,32 +396,39 @@ static int cs_etm_recording_options(struct auxtrace_record *itr,
/*
* In the case of per-cpu mmaps, we need the CPU on the
- * AUX event.
+ * AUX event. We also need the contextID in order to be notified
+ * when a context switch happened.
*/
- if (!cpu_map__empty(cpus))
+ if (!perf_cpu_map__empty(cpus)) {
perf_evsel__set_sample_bit(cs_etm_evsel, CPU);
+ err = cs_etm_set_option(itr, cs_etm_evsel,
+ ETM_OPT_CTXTID | ETM_OPT_TS);
+ if (err)
+ goto out;
+ }
+
/* Add dummy event to keep tracking */
if (opts->full_auxtrace) {
- struct perf_evsel *tracking_evsel;
- int err;
+ struct evsel *tracking_evsel;
err = parse_events(evlist, "dummy:u", NULL);
if (err)
- return err;
+ goto out;
tracking_evsel = perf_evlist__last(evlist);
perf_evlist__set_tracking_event(evlist, tracking_evsel);
- tracking_evsel->attr.freq = 0;
- tracking_evsel->attr.sample_period = 1;
+ tracking_evsel->core.attr.freq = 0;
+ tracking_evsel->core.attr.sample_period = 1;
/* In per-cpu case, always need the time of mmap events etc */
- if (!cpu_map__empty(cpus))
+ if (!perf_cpu_map__empty(cpus))
perf_evsel__set_sample_bit(tracking_evsel, TIME);
}
- return 0;
+out:
+ return err;
}
static u64 cs_etm_get_config(struct auxtrace_record *itr)
@@ -275,11 +437,11 @@ static u64 cs_etm_get_config(struct auxtrace_record *itr)
struct cs_etm_recording *ptr =
container_of(itr, struct cs_etm_recording, itr);
struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
- struct perf_evlist *evlist = ptr->evlist;
- struct perf_evsel *evsel;
+ struct evlist *evlist = ptr->evlist;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) {
- if (evsel->attr.type == cs_etm_pmu->type) {
+ if (evsel->core.attr.type == cs_etm_pmu->type) {
/*
* Variable perf_event_attr::config is assigned to
* ETMv3/PTM. The bit fields have been made to match
@@ -288,7 +450,7 @@ static u64 cs_etm_get_config(struct auxtrace_record *itr)
* drivers/hwtracing/coresight/coresight-perf.c for
* details.
*/
- config = evsel->attr.config;
+ config = evsel->core.attr.config;
break;
}
}
@@ -314,6 +476,8 @@ static u64 cs_etmv4_get_config(struct auxtrace_record *itr)
config_opts = cs_etm_get_config(itr);
if (config_opts & BIT(ETM_OPT_CYCACC))
config |= BIT(ETM4_CFG_BIT_CYCACC);
+ if (config_opts & BIT(ETM_OPT_CTXTID))
+ config |= BIT(ETM4_CFG_BIT_CTXTID);
if (config_opts & BIT(ETM_OPT_TS))
config |= BIT(ETM4_CFG_BIT_TS);
if (config_opts & BIT(ETM_OPT_RETSTK))
@@ -324,15 +488,15 @@ static u64 cs_etmv4_get_config(struct auxtrace_record *itr)
static size_t
cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
int i;
int etmv3 = 0, etmv4 = 0;
- struct cpu_map *event_cpus = evlist->cpus;
- struct cpu_map *online_cpus = cpu_map__new(NULL);
+ struct perf_cpu_map *event_cpus = evlist->core.cpus;
+ struct perf_cpu_map *online_cpus = perf_cpu_map__new(NULL);
/* cpu map is not empty, we have specific CPUs to work with */
- if (!cpu_map__empty(event_cpus)) {
+ if (!perf_cpu_map__empty(event_cpus)) {
for (i = 0; i < cpu__max_cpu(); i++) {
if (!cpu_map__has(event_cpus, i) ||
!cpu_map__has(online_cpus, i))
@@ -356,26 +520,13 @@ cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused,
}
}
- cpu_map__put(online_cpus);
+ perf_cpu_map__put(online_cpus);
return (CS_ETM_HEADER_SIZE +
(etmv4 * CS_ETMV4_PRIV_SIZE) +
(etmv3 * CS_ETMV3_PRIV_SIZE));
}
-static const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = {
- [CS_ETM_ETMCCER] = "mgmt/etmccer",
- [CS_ETM_ETMIDR] = "mgmt/etmidr",
-};
-
-static const char *metadata_etmv4_ro[CS_ETMV4_PRIV_MAX] = {
- [CS_ETMV4_TRCIDR0] = "trcidr/trcidr0",
- [CS_ETMV4_TRCIDR1] = "trcidr/trcidr1",
- [CS_ETMV4_TRCIDR2] = "trcidr/trcidr2",
- [CS_ETMV4_TRCIDR8] = "trcidr/trcidr8",
- [CS_ETMV4_TRCAUTHSTATUS] = "mgmt/trcauthstatus",
-};
-
static bool cs_etm_is_etmv4(struct auxtrace_record *itr, int cpu)
{
bool ret = false;
@@ -416,7 +567,7 @@ static int cs_etm_get_ro(struct perf_pmu *pmu, int cpu, const char *path)
static void cs_etm_get_metadata(int cpu, u32 *offset,
struct auxtrace_record *itr,
- struct auxtrace_info_event *info)
+ struct perf_record_auxtrace_info *info)
{
u32 increment;
u64 magic;
@@ -481,15 +632,15 @@ static void cs_etm_get_metadata(int cpu, u32 *offset,
static int cs_etm_info_fill(struct auxtrace_record *itr,
struct perf_session *session,
- struct auxtrace_info_event *info,
+ struct perf_record_auxtrace_info *info,
size_t priv_size)
{
int i;
u32 offset;
u64 nr_cpu, type;
- struct cpu_map *cpu_map;
- struct cpu_map *event_cpus = session->evlist->cpus;
- struct cpu_map *online_cpus = cpu_map__new(NULL);
+ struct perf_cpu_map *cpu_map;
+ struct perf_cpu_map *event_cpus = session->evlist->core.cpus;
+ struct perf_cpu_map *online_cpus = perf_cpu_map__new(NULL);
struct cs_etm_recording *ptr =
container_of(itr, struct cs_etm_recording, itr);
struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
@@ -501,11 +652,11 @@ static int cs_etm_info_fill(struct auxtrace_record *itr,
return -EINVAL;
/* If the cpu_map is empty all online CPUs are involved */
- if (cpu_map__empty(event_cpus)) {
+ if (perf_cpu_map__empty(event_cpus)) {
cpu_map = online_cpus;
} else {
/* Make sure all specified CPUs are online */
- for (i = 0; i < cpu_map__nr(event_cpus); i++) {
+ for (i = 0; i < perf_cpu_map__nr(event_cpus); i++) {
if (cpu_map__has(event_cpus, i) &&
!cpu_map__has(online_cpus, i))
return -EINVAL;
@@ -514,7 +665,7 @@ static int cs_etm_info_fill(struct auxtrace_record *itr,
cpu_map = event_cpus;
}
- nr_cpu = cpu_map__nr(cpu_map);
+ nr_cpu = perf_cpu_map__nr(cpu_map);
/* Get PMU type as dynamically assigned by the core */
type = cs_etm_pmu->type;
@@ -531,21 +682,136 @@ static int cs_etm_info_fill(struct auxtrace_record *itr,
if (cpu_map__has(cpu_map, i))
cs_etm_get_metadata(i, &offset, itr, info);
- cpu_map__put(online_cpus);
+ perf_cpu_map__put(online_cpus);
return 0;
}
-static int cs_etm_find_snapshot(struct auxtrace_record *itr __maybe_unused,
+static int cs_etm_alloc_wrapped_array(struct cs_etm_recording *ptr, int idx)
+{
+ bool *wrapped;
+ int cnt = ptr->wrapped_cnt;
+
+ /* Make @ptr->wrapped as big as @idx */
+ while (cnt <= idx)
+ cnt++;
+
+ /*
+ * Free'ed in cs_etm_recording_free(). Using realloc() to avoid
+ * cross compilation problems where the host's system supports
+ * reallocarray() but not the target.
+ */
+ wrapped = realloc(ptr->wrapped, cnt * sizeof(bool));
+ if (!wrapped)
+ return -ENOMEM;
+
+ wrapped[cnt - 1] = false;
+ ptr->wrapped_cnt = cnt;
+ ptr->wrapped = wrapped;
+
+ return 0;
+}
+
+static bool cs_etm_buffer_has_wrapped(unsigned char *buffer,
+ size_t buffer_size, u64 head)
+{
+ u64 i, watermark;
+ u64 *buf = (u64 *)buffer;
+ size_t buf_size = buffer_size;
+
+ /*
+ * We want to look the very last 512 byte (chosen arbitrarily) in
+ * the ring buffer.
+ */
+ watermark = buf_size - 512;
+
+ /*
+ * @head is continuously increasing - if its value is equal or greater
+ * than the size of the ring buffer, it has wrapped around.
+ */
+ if (head >= buffer_size)
+ return true;
+
+ /*
+ * The value of @head is somewhere within the size of the ring buffer.
+ * This can be that there hasn't been enough data to fill the ring
+ * buffer yet or the trace time was so long that @head has numerically
+ * wrapped around. To find we need to check if we have data at the very
+ * end of the ring buffer. We can reliably do this because mmap'ed
+ * pages are zeroed out and there is a fresh mapping with every new
+ * session.
+ */
+
+ /* @head is less than 512 byte from the end of the ring buffer */
+ if (head > watermark)
+ watermark = head;
+
+ /*
+ * Speed things up by using 64 bit transactions (see "u64 *buf" above)
+ */
+ watermark >>= 3;
+ buf_size >>= 3;
+
+ /*
+ * If we find trace data at the end of the ring buffer, @head has
+ * been there and has numerically wrapped around at least once.
+ */
+ for (i = watermark; i < buf_size; i++)
+ if (buf[i])
+ return true;
+
+ return false;
+}
+
+static int cs_etm_find_snapshot(struct auxtrace_record *itr,
int idx, struct auxtrace_mmap *mm,
- unsigned char *data __maybe_unused,
+ unsigned char *data,
u64 *head, u64 *old)
{
+ int err;
+ bool wrapped;
+ struct cs_etm_recording *ptr =
+ container_of(itr, struct cs_etm_recording, itr);
+
+ /*
+ * Allocate memory to keep track of wrapping if this is the first
+ * time we deal with this *mm.
+ */
+ if (idx >= ptr->wrapped_cnt) {
+ err = cs_etm_alloc_wrapped_array(ptr, idx);
+ if (err)
+ return err;
+ }
+
+ /*
+ * Check to see if *head has wrapped around. If it hasn't only the
+ * amount of data between *head and *old is snapshot'ed to avoid
+ * bloating the perf.data file with zeros. But as soon as *head has
+ * wrapped around the entire size of the AUX ring buffer it taken.
+ */
+ wrapped = ptr->wrapped[idx];
+ if (!wrapped && cs_etm_buffer_has_wrapped(data, mm->len, *head)) {
+ wrapped = true;
+ ptr->wrapped[idx] = true;
+ }
+
pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n",
__func__, idx, (size_t)*old, (size_t)*head, mm->len);
- *old = *head;
- *head += mm->len;
+ /* No wrap has occurred, we can just use *head and *old. */
+ if (!wrapped)
+ return 0;
+
+ /*
+ * *head has wrapped around - adjust *head and *old to pickup the
+ * entire content of the AUX buffer.
+ */
+ if (*head >= mm->len) {
+ *old = *head - mm->len;
+ } else {
+ *head += mm->len;
+ *old = *head - mm->len;
+ }
return 0;
}
@@ -554,11 +820,11 @@ static int cs_etm_snapshot_start(struct auxtrace_record *itr)
{
struct cs_etm_recording *ptr =
container_of(itr, struct cs_etm_recording, itr);
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(ptr->evlist, evsel) {
- if (evsel->attr.type == ptr->cs_etm_pmu->type)
- return perf_evsel__disable(evsel);
+ if (evsel->core.attr.type == ptr->cs_etm_pmu->type)
+ return evsel__disable(evsel);
}
return -EINVAL;
}
@@ -567,11 +833,11 @@ static int cs_etm_snapshot_finish(struct auxtrace_record *itr)
{
struct cs_etm_recording *ptr =
container_of(itr, struct cs_etm_recording, itr);
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(ptr->evlist, evsel) {
- if (evsel->attr.type == ptr->cs_etm_pmu->type)
- return perf_evsel__enable(evsel);
+ if (evsel->core.attr.type == ptr->cs_etm_pmu->type)
+ return evsel__enable(evsel);
}
return -EINVAL;
}
@@ -586,6 +852,8 @@ static void cs_etm_recording_free(struct auxtrace_record *itr)
{
struct cs_etm_recording *ptr =
container_of(itr, struct cs_etm_recording, itr);
+
+ zfree(&ptr->wrapped);
free(ptr);
}
@@ -593,10 +861,10 @@ static int cs_etm_read_finish(struct auxtrace_record *itr, int idx)
{
struct cs_etm_recording *ptr =
container_of(itr, struct cs_etm_recording, itr);
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(ptr->evlist, evsel) {
- if (evsel->attr.type == ptr->cs_etm_pmu->type)
+ if (evsel->core.attr.type == ptr->cs_etm_pmu->type)
return perf_evlist__enable_event_idx(ptr->evlist,
evsel, idx);
}
diff --git a/tools/perf/arch/arm/util/dwarf-regs.c b/tools/perf/arch/arm/util/dwarf-regs.c
index 8bb176a37990..fc5f71c91802 100644
--- a/tools/perf/arch/arm/util/dwarf-regs.c
+++ b/tools/perf/arch/arm/util/dwarf-regs.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Mapping of DWARF debug register numbers into register names.
*
* Copyright (C) 2010 Will Deacon, ARM Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <stddef.h>
diff --git a/tools/perf/arch/arm64/Build b/tools/perf/arch/arm64/Build
index 36222e64bbf7..a7dd46a5b678 100644
--- a/tools/perf/arch/arm64/Build
+++ b/tools/perf/arch/arm64/Build
@@ -1,2 +1,2 @@
perf-y += util/
-perf-$(CONFIG_DWARF_UNWIND) += tests/
+perf-y += tests/
diff --git a/tools/perf/arch/arm64/annotate/instructions.c b/tools/perf/arch/arm64/annotate/instructions.c
index 8f70a1b282df..43aa93ed8414 100644
--- a/tools/perf/arch/arm64/annotate/instructions.c
+++ b/tools/perf/arch/arm64/annotate/instructions.c
@@ -2,6 +2,7 @@
#include <linux/compiler.h>
#include <sys/types.h>
#include <regex.h>
+#include <stdlib.h>
struct arm64_annotate {
regex_t call_insn,
diff --git a/tools/perf/arch/arm64/entry/syscalls/mksyscalltbl b/tools/perf/arch/arm64/entry/syscalls/mksyscalltbl
index c88fd32563eb..459469b7222c 100755
--- a/tools/perf/arch/arm64/entry/syscalls/mksyscalltbl
+++ b/tools/perf/arch/arm64/entry/syscalls/mksyscalltbl
@@ -56,7 +56,7 @@ create_table()
echo "};"
}
-$gcc -E -dM -x c $input \
+$gcc -E -dM -x c -I $incpath/include/uapi $input \
|sed -ne 's/^#define __NR_//p' \
|sort -t' ' -k2 -nu \
|create_table
diff --git a/tools/perf/arch/arm64/tests/Build b/tools/perf/arch/arm64/tests/Build
index 41707fea74b3..a61c06bdb757 100644
--- a/tools/perf/arch/arm64/tests/Build
+++ b/tools/perf/arch/arm64/tests/Build
@@ -1,4 +1,4 @@
perf-y += regs_load.o
-perf-y += dwarf-unwind.o
+perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
perf-y += arch-tests.o
diff --git a/tools/perf/arch/arm64/util/arm-spe.c b/tools/perf/arch/arm64/util/arm-spe.c
index 5ccfce87e693..4b364692da67 100644
--- a/tools/perf/arch/arm64/util/arm-spe.c
+++ b/tools/perf/arch/arm64/util/arm-spe.c
@@ -8,9 +8,11 @@
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/log2.h>
+#include <linux/zalloc.h>
#include <time.h>
#include "../../util/cpumap.h"
+#include "../../util/event.h"
#include "../../util/evsel.h"
#include "../../util/evlist.h"
#include "../../util/session.h"
@@ -18,6 +20,7 @@
#include "../../util/pmu.h"
#include "../../util/debug.h"
#include "../../util/auxtrace.h"
+#include "../../util/record.h"
#include "../../util/arm-spe.h"
#define KiB(x) ((x) * 1024)
@@ -26,19 +29,19 @@
struct arm_spe_recording {
struct auxtrace_record itr;
struct perf_pmu *arm_spe_pmu;
- struct perf_evlist *evlist;
+ struct evlist *evlist;
};
static size_t
arm_spe_info_priv_size(struct auxtrace_record *itr __maybe_unused,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
return ARM_SPE_AUXTRACE_PRIV_SIZE;
}
static int arm_spe_info_fill(struct auxtrace_record *itr,
struct perf_session *session,
- struct auxtrace_info_event *auxtrace_info,
+ struct perf_record_auxtrace_info *auxtrace_info,
size_t priv_size)
{
struct arm_spe_recording *sper =
@@ -58,27 +61,27 @@ static int arm_spe_info_fill(struct auxtrace_record *itr,
}
static int arm_spe_recording_options(struct auxtrace_record *itr,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
struct record_opts *opts)
{
struct arm_spe_recording *sper =
container_of(itr, struct arm_spe_recording, itr);
struct perf_pmu *arm_spe_pmu = sper->arm_spe_pmu;
- struct perf_evsel *evsel, *arm_spe_evsel = NULL;
- bool privileged = geteuid() == 0 || perf_event_paranoid() < 0;
- struct perf_evsel *tracking_evsel;
+ struct evsel *evsel, *arm_spe_evsel = NULL;
+ bool privileged = perf_event_paranoid_check(-1);
+ struct evsel *tracking_evsel;
int err;
sper->evlist = evlist;
evlist__for_each_entry(evlist, evsel) {
- if (evsel->attr.type == arm_spe_pmu->type) {
+ if (evsel->core.attr.type == arm_spe_pmu->type) {
if (arm_spe_evsel) {
pr_err("There may be only one " ARM_SPE_PMU_NAME "x event\n");
return -EINVAL;
}
- evsel->attr.freq = 0;
- evsel->attr.sample_period = 1;
+ evsel->core.attr.freq = 0;
+ evsel->core.attr.sample_period = 1;
arm_spe_evsel = evsel;
opts->full_auxtrace = true;
}
@@ -129,8 +132,8 @@ static int arm_spe_recording_options(struct auxtrace_record *itr,
tracking_evsel = perf_evlist__last(evlist);
perf_evlist__set_tracking_event(evlist, tracking_evsel);
- tracking_evsel->attr.freq = 0;
- tracking_evsel->attr.sample_period = 1;
+ tracking_evsel->core.attr.freq = 0;
+ tracking_evsel->core.attr.sample_period = 1;
perf_evsel__set_sample_bit(tracking_evsel, TIME);
perf_evsel__set_sample_bit(tracking_evsel, CPU);
perf_evsel__reset_sample_bit(tracking_evsel, BRANCH_STACK);
@@ -159,10 +162,10 @@ static int arm_spe_read_finish(struct auxtrace_record *itr, int idx)
{
struct arm_spe_recording *sper =
container_of(itr, struct arm_spe_recording, itr);
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(sper->evlist, evsel) {
- if (evsel->attr.type == sper->arm_spe_pmu->type)
+ if (evsel->core.attr.type == sper->arm_spe_pmu->type)
return perf_evlist__enable_event_idx(sper->evlist,
evsel, idx);
}
diff --git a/tools/perf/arch/arm64/util/dwarf-regs.c b/tools/perf/arch/arm64/util/dwarf-regs.c
index cd764a9fd098..b047b882c5b1 100644
--- a/tools/perf/arch/arm64/util/dwarf-regs.c
+++ b/tools/perf/arch/arm64/util/dwarf-regs.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Mapping of DWARF debug register numbers into register names.
*
* Copyright (C) 2010 Will Deacon, ARM Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <errno.h>
diff --git a/tools/perf/arch/arm64/util/header.c b/tools/perf/arch/arm64/util/header.c
index 534cd2507d83..e41defaaa2e6 100644
--- a/tools/perf/arch/arm64/util/header.c
+++ b/tools/perf/arch/arm64/util/header.c
@@ -1,6 +1,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <api/fs/fs.h>
+#include "debug.h"
#include "header.h"
#define MIDR "/regs/identification/midr_el1"
@@ -16,7 +17,7 @@ char *get_cpuid_str(struct perf_pmu *pmu)
const char *sysfs = sysfs__mountpoint();
int cpu;
u64 midr = 0;
- struct cpu_map *cpus;
+ struct perf_cpu_map *cpus;
FILE *file;
if (!sysfs || !pmu || !pmu->cpus)
@@ -27,7 +28,7 @@ char *get_cpuid_str(struct perf_pmu *pmu)
return NULL;
/* read midr from list of cpus mapped to this pmu */
- cpus = cpu_map__get(pmu->cpus);
+ cpus = perf_cpu_map__get(pmu->cpus);
for (cpu = 0; cpu < cpus->nr; cpu++) {
scnprintf(path, PATH_MAX, "%s/devices/system/cpu/cpu%d"MIDR,
sysfs, cpus->map[cpu]);
@@ -60,6 +61,6 @@ char *get_cpuid_str(struct perf_pmu *pmu)
buf = NULL;
}
- cpu_map__put(cpus);
+ perf_cpu_map__put(cpus);
return buf;
}
diff --git a/tools/perf/arch/arm64/util/sym-handling.c b/tools/perf/arch/arm64/util/sym-handling.c
index 0051b1ee8450..5df788985130 100644
--- a/tools/perf/arch/arm64/util/sym-handling.c
+++ b/tools/perf/arch/arm64/util/sym-handling.c
@@ -1,16 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2, as
- * published by the Free Software Foundation.
*
* Copyright (C) 2015 Naveen N. Rao, IBM Corporation
*/
-#include "debug.h"
-#include "symbol.h"
-#include "map.h"
-#include "probe-event.h"
-#include "probe-file.h"
+#include "symbol.h" // for the elf__needs_adjust_symbols() prototype
+#include <stdbool.h>
+#include <gelf.h>
#ifdef HAVE_LIBELF_SUPPORT
bool elf__needs_adjust_symbols(GElf_Ehdr ehdr)
diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c
index f3824ca7c20b..59dd875fd5e4 100644
--- a/tools/perf/arch/common.c
+++ b/tools/perf/arch/common.c
@@ -1,9 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
+#include <limits.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
#include "common.h"
#include "../util/env.h"
-#include "../util/util.h"
#include "../util/debug.h"
+#include <linux/zalloc.h>
const char *const arc_triplets[] = {
"arc-linux-",
diff --git a/tools/perf/arch/common.h b/tools/perf/arch/common.h
index c298a446d1f6..e965ed8bb328 100644
--- a/tools/perf/arch/common.h
+++ b/tools/perf/arch/common.h
@@ -2,7 +2,9 @@
#ifndef ARCH_PERF_COMMON_H
#define ARCH_PERF_COMMON_H
-#include "../util/env.h"
+#include <stdbool.h>
+
+struct perf_env;
int perf_env__lookup_objdump(struct perf_env *env, const char **path);
bool perf_env__single_address_space(struct perf_env *env);
diff --git a/tools/perf/arch/csky/Makefile b/tools/perf/arch/csky/Makefile
index 7fbca175099e..88c08eed9c7b 100644
--- a/tools/perf/arch/csky/Makefile
+++ b/tools/perf/arch/csky/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
ifndef NO_DWARF
PERF_HAVE_DWARF_REGS := 1
endif
diff --git a/tools/perf/arch/csky/annotate/instructions.c b/tools/perf/arch/csky/annotate/instructions.c
new file mode 100644
index 000000000000..5337bfb7d5fc
--- /dev/null
+++ b/tools/perf/arch/csky/annotate/instructions.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd.
+
+#include <linux/compiler.h>
+
+static struct ins_ops *csky__associate_ins_ops(struct arch *arch,
+ const char *name)
+{
+ struct ins_ops *ops = NULL;
+
+ /* catch all kind of jumps */
+ if (!strcmp(name, "bt") ||
+ !strcmp(name, "bf") ||
+ !strcmp(name, "bez") ||
+ !strcmp(name, "bnez") ||
+ !strcmp(name, "bnezad") ||
+ !strcmp(name, "bhsz") ||
+ !strcmp(name, "bhz") ||
+ !strcmp(name, "blsz") ||
+ !strcmp(name, "blz") ||
+ !strcmp(name, "br") ||
+ !strcmp(name, "jmpi") ||
+ !strcmp(name, "jmp"))
+ ops = &jump_ops;
+
+ /* catch function call */
+ if (!strcmp(name, "bsr") ||
+ !strcmp(name, "jsri") ||
+ !strcmp(name, "jsr"))
+ ops = &call_ops;
+
+ /* catch function return */
+ if (!strcmp(name, "rts"))
+ ops = &ret_ops;
+
+ if (ops)
+ arch__associate_ins_ops(arch, name, ops);
+ return ops;
+}
+
+static int csky__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
+{
+ arch->initialized = true;
+ arch->objdump.comment_char = '/';
+ arch->associate_instruction_ops = csky__associate_ins_ops;
+
+ return 0;
+}
diff --git a/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl b/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl
index db3bbb8744af..43f736ed47f2 100644
--- a/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl
+++ b/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl
@@ -20,7 +20,9 @@
10 common unlink sys_unlink
11 nospu execve sys_execve compat_sys_execve
12 common chdir sys_chdir
-13 common time sys_time compat_sys_time
+13 32 time sys_time32
+13 64 time sys_time
+13 spu time sys_time
14 common mknod sys_mknod
15 common chmod sys_chmod
16 common lchown sys_lchown
@@ -36,14 +38,17 @@
22 spu umount sys_ni_syscall
23 common setuid sys_setuid
24 common getuid sys_getuid
-25 common stime sys_stime compat_sys_stime
+25 32 stime sys_stime32
+25 64 stime sys_stime
+25 spu stime sys_stime
26 nospu ptrace sys_ptrace compat_sys_ptrace
27 common alarm sys_alarm
28 32 oldfstat sys_fstat sys_ni_syscall
28 64 oldfstat sys_ni_syscall
28 spu oldfstat sys_ni_syscall
29 nospu pause sys_pause
-30 nospu utime sys_utime compat_sys_utime
+30 32 utime sys_utime32
+30 64 utime sys_utime
31 common stty sys_ni_syscall
32 common gtty sys_ni_syscall
33 common access sys_access
@@ -157,7 +162,9 @@
121 common setdomainname sys_setdomainname
122 common uname sys_newuname
123 common modify_ldt sys_ni_syscall
-124 common adjtimex sys_adjtimex compat_sys_adjtimex
+124 32 adjtimex sys_adjtimex_time32
+124 64 adjtimex sys_adjtimex
+124 spu adjtimex sys_adjtimex
125 common mprotect sys_mprotect
126 32 sigprocmask sys_sigprocmask compat_sys_sigprocmask
126 64 sigprocmask sys_ni_syscall
@@ -198,8 +205,12 @@
158 common sched_yield sys_sched_yield
159 common sched_get_priority_max sys_sched_get_priority_max
160 common sched_get_priority_min sys_sched_get_priority_min
-161 common sched_rr_get_interval sys_sched_rr_get_interval compat_sys_sched_rr_get_interval
-162 common nanosleep sys_nanosleep compat_sys_nanosleep
+161 32 sched_rr_get_interval sys_sched_rr_get_interval_time32
+161 64 sched_rr_get_interval sys_sched_rr_get_interval
+161 spu sched_rr_get_interval sys_sched_rr_get_interval
+162 32 nanosleep sys_nanosleep_time32
+162 64 nanosleep sys_nanosleep
+162 spu nanosleep sys_nanosleep
163 common mremap sys_mremap
164 common setresuid sys_setresuid
165 common getresuid sys_getresuid
@@ -213,7 +224,8 @@
173 nospu rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction
174 nospu rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask
175 nospu rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending
-176 nospu rt_sigtimedwait sys_rt_sigtimedwait compat_sys_rt_sigtimedwait
+176 32 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32
+176 64 rt_sigtimedwait sys_rt_sigtimedwait
177 nospu rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo
178 nospu rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend
179 common pread64 sys_pread64 compat_sys_pread64
@@ -260,7 +272,9 @@
218 common removexattr sys_removexattr
219 common lremovexattr sys_lremovexattr
220 common fremovexattr sys_fremovexattr
-221 common futex sys_futex compat_sys_futex
+221 32 futex sys_futex_time32
+221 64 futex sys_futex
+221 spu futex sys_futex
222 common sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity
223 common sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity
# 224 unused
@@ -268,7 +282,9 @@
226 32 sendfile64 sys_sendfile64 compat_sys_sendfile64
227 common io_setup sys_io_setup compat_sys_io_setup
228 common io_destroy sys_io_destroy
-229 common io_getevents sys_io_getevents compat_sys_io_getevents
+229 32 io_getevents sys_io_getevents_time32
+229 64 io_getevents sys_io_getevents
+229 spu io_getevents sys_io_getevents
230 common io_submit sys_io_submit compat_sys_io_submit
231 common io_cancel sys_io_cancel
232 nospu set_tid_address sys_set_tid_address
@@ -280,19 +296,33 @@
238 common epoll_wait sys_epoll_wait
239 common remap_file_pages sys_remap_file_pages
240 common timer_create sys_timer_create compat_sys_timer_create
-241 common timer_settime sys_timer_settime compat_sys_timer_settime
-242 common timer_gettime sys_timer_gettime compat_sys_timer_gettime
+241 32 timer_settime sys_timer_settime32
+241 64 timer_settime sys_timer_settime
+241 spu timer_settime sys_timer_settime
+242 32 timer_gettime sys_timer_gettime32
+242 64 timer_gettime sys_timer_gettime
+242 spu timer_gettime sys_timer_gettime
243 common timer_getoverrun sys_timer_getoverrun
244 common timer_delete sys_timer_delete
-245 common clock_settime sys_clock_settime compat_sys_clock_settime
-246 common clock_gettime sys_clock_gettime compat_sys_clock_gettime
-247 common clock_getres sys_clock_getres compat_sys_clock_getres
-248 common clock_nanosleep sys_clock_nanosleep compat_sys_clock_nanosleep
+245 32 clock_settime sys_clock_settime32
+245 64 clock_settime sys_clock_settime
+245 spu clock_settime sys_clock_settime
+246 32 clock_gettime sys_clock_gettime32
+246 64 clock_gettime sys_clock_gettime
+246 spu clock_gettime sys_clock_gettime
+247 32 clock_getres sys_clock_getres_time32
+247 64 clock_getres sys_clock_getres
+247 spu clock_getres sys_clock_getres
+248 32 clock_nanosleep sys_clock_nanosleep_time32
+248 64 clock_nanosleep sys_clock_nanosleep
+248 spu clock_nanosleep sys_clock_nanosleep
249 32 swapcontext ppc_swapcontext ppc32_swapcontext
249 64 swapcontext ppc64_swapcontext
249 spu swapcontext sys_ni_syscall
250 common tgkill sys_tgkill
-251 common utimes sys_utimes compat_sys_utimes
+251 32 utimes sys_utimes_time32
+251 64 utimes sys_utimes
+251 spu utimes sys_utimes
252 common statfs64 sys_statfs64 compat_sys_statfs64
253 common fstatfs64 sys_fstatfs64 compat_sys_fstatfs64
254 32 fadvise64_64 ppc_fadvise64_64
@@ -308,8 +338,10 @@
261 nospu set_mempolicy sys_set_mempolicy compat_sys_set_mempolicy
262 nospu mq_open sys_mq_open compat_sys_mq_open
263 nospu mq_unlink sys_mq_unlink
-264 nospu mq_timedsend sys_mq_timedsend compat_sys_mq_timedsend
-265 nospu mq_timedreceive sys_mq_timedreceive compat_sys_mq_timedreceive
+264 32 mq_timedsend sys_mq_timedsend_time32
+264 64 mq_timedsend sys_mq_timedsend
+265 32 mq_timedreceive sys_mq_timedreceive_time32
+265 64 mq_timedreceive sys_mq_timedreceive
266 nospu mq_notify sys_mq_notify compat_sys_mq_notify
267 nospu mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr
268 nospu kexec_load sys_kexec_load compat_sys_kexec_load
@@ -324,8 +356,10 @@
277 nospu inotify_rm_watch sys_inotify_rm_watch
278 nospu spu_run sys_spu_run
279 nospu spu_create sys_spu_create
-280 nospu pselect6 sys_pselect6 compat_sys_pselect6
-281 nospu ppoll sys_ppoll compat_sys_ppoll
+280 32 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32
+280 64 pselect6 sys_pselect6
+281 32 ppoll sys_ppoll_time32 compat_sys_ppoll_time32
+281 64 ppoll sys_ppoll
282 common unshare sys_unshare
283 common splice sys_splice
284 common tee sys_tee
@@ -334,7 +368,9 @@
287 common mkdirat sys_mkdirat
288 common mknodat sys_mknodat
289 common fchownat sys_fchownat
-290 common futimesat sys_futimesat compat_sys_futimesat
+290 32 futimesat sys_futimesat_time32
+290 64 futimesat sys_futimesat
+290 spu utimesat sys_futimesat
291 32 fstatat64 sys_fstatat64
291 64 newfstatat sys_newfstatat
291 spu newfstatat sys_newfstatat
@@ -350,15 +386,21 @@
301 common move_pages sys_move_pages compat_sys_move_pages
302 common getcpu sys_getcpu
303 nospu epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait
-304 common utimensat sys_utimensat compat_sys_utimensat
+304 32 utimensat sys_utimensat_time32
+304 64 utimensat sys_utimensat
+304 spu utimensat sys_utimensat
305 common signalfd sys_signalfd compat_sys_signalfd
306 common timerfd_create sys_timerfd_create
307 common eventfd sys_eventfd
308 common sync_file_range2 sys_sync_file_range2 compat_sys_sync_file_range2
309 nospu fallocate sys_fallocate compat_sys_fallocate
310 nospu subpage_prot sys_subpage_prot
-311 common timerfd_settime sys_timerfd_settime compat_sys_timerfd_settime
-312 common timerfd_gettime sys_timerfd_gettime compat_sys_timerfd_gettime
+311 32 timerfd_settime sys_timerfd_settime32
+311 64 timerfd_settime sys_timerfd_settime
+311 spu timerfd_settime sys_timerfd_settime
+312 32 timerfd_gettime sys_timerfd_gettime32
+312 64 timerfd_gettime sys_timerfd_gettime
+312 spu timerfd_gettime sys_timerfd_gettime
313 common signalfd4 sys_signalfd4 compat_sys_signalfd4
314 common eventfd2 sys_eventfd2
315 common epoll_create1 sys_epoll_create1
@@ -389,11 +431,15 @@
340 common getsockopt sys_getsockopt compat_sys_getsockopt
341 common sendmsg sys_sendmsg compat_sys_sendmsg
342 common recvmsg sys_recvmsg compat_sys_recvmsg
-343 common recvmmsg sys_recvmmsg compat_sys_recvmmsg
+343 32 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32
+343 64 recvmmsg sys_recvmmsg
+343 spu recvmmsg sys_recvmmsg
344 common accept4 sys_accept4
345 common name_to_handle_at sys_name_to_handle_at
346 common open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at
-347 common clock_adjtime sys_clock_adjtime compat_sys_clock_adjtime
+347 32 clock_adjtime sys_clock_adjtime32
+347 64 clock_adjtime sys_clock_adjtime
+347 spu clock_adjtime sys_clock_adjtime
348 common syncfs sys_syncfs
349 common sendmmsg sys_sendmmsg compat_sys_sendmmsg
350 common setns sys_setns
@@ -414,6 +460,7 @@
363 spu switch_endian sys_ni_syscall
364 common userfaultfd sys_userfaultfd
365 common membarrier sys_membarrier
+# 366-377 originally left for IPC, now unused
378 nospu mlock2 sys_mlock2
379 nospu copy_file_range sys_copy_file_range
380 common preadv2 sys_preadv2 compat_sys_preadv2
@@ -424,4 +471,49 @@
385 nospu pkey_free sys_pkey_free
386 nospu pkey_mprotect sys_pkey_mprotect
387 nospu rseq sys_rseq
-388 nospu io_pgetevents sys_io_pgetevents compat_sys_io_pgetevents
+388 32 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents
+388 64 io_pgetevents sys_io_pgetevents
+# room for arch specific syscalls
+392 64 semtimedop sys_semtimedop
+393 common semget sys_semget
+394 common semctl sys_semctl compat_sys_semctl
+395 common shmget sys_shmget
+396 common shmctl sys_shmctl compat_sys_shmctl
+397 common shmat sys_shmat compat_sys_shmat
+398 common shmdt sys_shmdt
+399 common msgget sys_msgget
+400 common msgsnd sys_msgsnd compat_sys_msgsnd
+401 common msgrcv sys_msgrcv compat_sys_msgrcv
+402 common msgctl sys_msgctl compat_sys_msgctl
+403 32 clock_gettime64 sys_clock_gettime sys_clock_gettime
+404 32 clock_settime64 sys_clock_settime sys_clock_settime
+405 32 clock_adjtime64 sys_clock_adjtime sys_clock_adjtime
+406 32 clock_getres_time64 sys_clock_getres sys_clock_getres
+407 32 clock_nanosleep_time64 sys_clock_nanosleep sys_clock_nanosleep
+408 32 timer_gettime64 sys_timer_gettime sys_timer_gettime
+409 32 timer_settime64 sys_timer_settime sys_timer_settime
+410 32 timerfd_gettime64 sys_timerfd_gettime sys_timerfd_gettime
+411 32 timerfd_settime64 sys_timerfd_settime sys_timerfd_settime
+412 32 utimensat_time64 sys_utimensat sys_utimensat
+413 32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64
+414 32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64
+416 32 io_pgetevents_time64 sys_io_pgetevents sys_io_pgetevents
+417 32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64
+418 32 mq_timedsend_time64 sys_mq_timedsend sys_mq_timedsend
+419 32 mq_timedreceive_time64 sys_mq_timedreceive sys_mq_timedreceive
+420 32 semtimedop_time64 sys_semtimedop sys_semtimedop
+421 32 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64
+422 32 futex_time64 sys_futex sys_futex
+423 32 sched_rr_get_interval_time64 sys_sched_rr_get_interval sys_sched_rr_get_interval
+424 common pidfd_send_signal sys_pidfd_send_signal
+425 common io_uring_setup sys_io_uring_setup
+426 common io_uring_enter sys_io_uring_enter
+427 common io_uring_register sys_io_uring_register
+428 common open_tree sys_open_tree
+429 common move_mount sys_move_mount
+430 common fsopen sys_fsopen
+431 common fsconfig sys_fsconfig
+432 common fsmount sys_fsmount
+433 common fspick sys_fspick
+434 common pidfd_open sys_pidfd_open
+435 nospu clone3 ppc_clone3
diff --git a/tools/perf/arch/powerpc/util/dwarf-regs.c b/tools/perf/arch/powerpc/util/dwarf-regs.c
index 98ac87052a74..4952890b9428 100644
--- a/tools/perf/arch/powerpc/util/dwarf-regs.c
+++ b/tools/perf/arch/powerpc/util/dwarf-regs.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Mapping of DWARF debug register numbers into register names.
*
* Copyright (C) 2010 Ian Munsie, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include <stddef.h>
diff --git a/tools/perf/arch/powerpc/util/kvm-stat.c b/tools/perf/arch/powerpc/util/kvm-stat.c
index f9db341c47b6..f0dbf7b075c8 100644
--- a/tools/perf/arch/powerpc/util/kvm-stat.c
+++ b/tools/perf/arch/powerpc/util/kvm-stat.c
@@ -32,7 +32,7 @@ const char *ppc_book3s_hv_kvm_tp[] = {
const char *kvm_events_tp[NR_TPS + 1];
const char *kvm_exit_reason;
-static void hcall_event_get_key(struct perf_evsel *evsel,
+static void hcall_event_get_key(struct evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
@@ -55,14 +55,14 @@ static const char *get_hcall_exit_reason(u64 exit_code)
return "UNKNOWN";
}
-static bool hcall_event_end(struct perf_evsel *evsel,
+static bool hcall_event_end(struct evsel *evsel,
struct perf_sample *sample __maybe_unused,
struct event_key *key __maybe_unused)
{
return (!strcmp(evsel->name, kvm_events_tp[3]));
}
-static bool hcall_event_begin(struct perf_evsel *evsel,
+static bool hcall_event_begin(struct evsel *evsel,
struct perf_sample *sample, struct event_key *key)
{
if (!strcmp(evsel->name, kvm_events_tp[2])) {
@@ -106,7 +106,7 @@ const char * const kvm_skip_events[] = {
};
-static int is_tracepoint_available(const char *str, struct perf_evlist *evlist)
+static int is_tracepoint_available(const char *str, struct evlist *evlist)
{
struct parse_events_error err;
int ret;
@@ -119,7 +119,7 @@ static int is_tracepoint_available(const char *str, struct perf_evlist *evlist)
}
static int ppc__setup_book3s_hv(struct perf_kvm_stat *kvm,
- struct perf_evlist *evlist)
+ struct evlist *evlist)
{
const char **events_ptr;
int i, nr_tp = 0, err = -1;
@@ -146,7 +146,7 @@ static int ppc__setup_book3s_hv(struct perf_kvm_stat *kvm,
/* Wrapper to setup kvm tracepoints */
static int ppc__setup_kvm_tp(struct perf_kvm_stat *kvm)
{
- struct perf_evlist *evlist = perf_evlist__new();
+ struct evlist *evlist = evlist__new();
if (evlist == NULL)
return -ENOMEM;
diff --git a/tools/perf/arch/powerpc/util/mem-events.c b/tools/perf/arch/powerpc/util/mem-events.c
index d08311f04e95..07fb5e049488 100644
--- a/tools/perf/arch/powerpc/util/mem-events.c
+++ b/tools/perf/arch/powerpc/util/mem-events.c
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
+#include "map_symbol.h"
#include "mem-events.h"
/* PowerPC does not support 'ldlat' parameter. */
diff --git a/tools/perf/arch/powerpc/util/perf_regs.c b/tools/perf/arch/powerpc/util/perf_regs.c
index 34d5134681d9..e9c436eeffc9 100644
--- a/tools/perf/arch/powerpc/util/perf_regs.c
+++ b/tools/perf/arch/powerpc/util/perf_regs.c
@@ -2,12 +2,13 @@
#include <errno.h>
#include <string.h>
#include <regex.h>
+#include <linux/zalloc.h>
-#include "../../perf.h"
-#include "../../util/util.h"
#include "../../util/perf_regs.h"
#include "../../util/debug.h"
+#include <linux/kernel.h>
+
const struct sample_reg sample_reg_masks[] = {
SMPL_REG(r0, PERF_REG_POWERPC_R0),
SMPL_REG(r1, PERF_REG_POWERPC_R1),
diff --git a/tools/perf/arch/powerpc/util/skip-callchain-idx.c b/tools/perf/arch/powerpc/util/skip-callchain-idx.c
index 2918bb16c892..fc9c2f5fcd52 100644
--- a/tools/perf/arch/powerpc/util/skip-callchain-idx.c
+++ b/tools/perf/arch/powerpc/util/skip-callchain-idx.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Use DWARF Debug information to skip unnecessary callchain entries.
*
* Copyright (C) 2014 Sukadev Bhattiprolu, IBM Corporation.
* Copyright (C) 2014 Ulrich Weigand, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include <inttypes.h>
#include <dwarf.h>
diff --git a/tools/perf/arch/powerpc/util/sym-handling.c b/tools/perf/arch/powerpc/util/sym-handling.c
index 10a44e946f77..8a4b717e0a53 100644
--- a/tools/perf/arch/powerpc/util/sym-handling.c
+++ b/tools/perf/arch/powerpc/util/sym-handling.c
@@ -1,12 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2, as
- * published by the Free Software Foundation.
*
* Copyright (C) 2015 Naveen N. Rao, IBM Corporation
*/
#include "debug.h"
+#include "dso.h"
#include "symbol.h"
#include "map.h"
#include "probe-event.h"
diff --git a/tools/perf/arch/powerpc/util/unwind-libdw.c b/tools/perf/arch/powerpc/util/unwind-libdw.c
index 7a1f05ef2fc0..abf2dbc7f829 100644
--- a/tools/perf/arch/powerpc/util/unwind-libdw.c
+++ b/tools/perf/arch/powerpc/util/unwind-libdw.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <elfutils/libdwfl.h>
+#include <linux/kernel.h>
#include "../../util/unwind-libdw.h"
#include "../../util/perf_regs.h"
#include "../../util/event.h"
diff --git a/tools/perf/arch/powerpc/util/unwind-libunwind.c b/tools/perf/arch/powerpc/util/unwind-libunwind.c
index 9e15f92ae49f..90a6beda20de 100644
--- a/tools/perf/arch/powerpc/util/unwind-libunwind.c
+++ b/tools/perf/arch/powerpc/util/unwind-libunwind.c
@@ -1,10 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2016 Chandan Kumar, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include <errno.h>
diff --git a/tools/perf/arch/riscv/Build b/tools/perf/arch/riscv/Build
new file mode 100644
index 000000000000..e4e5f33c84d8
--- /dev/null
+++ b/tools/perf/arch/riscv/Build
@@ -0,0 +1 @@
+perf-y += util/
diff --git a/tools/perf/arch/riscv/Makefile b/tools/perf/arch/riscv/Makefile
new file mode 100644
index 000000000000..1aa9dd772489
--- /dev/null
+++ b/tools/perf/arch/riscv/Makefile
@@ -0,0 +1,4 @@
+ifndef NO_DWARF
+PERF_HAVE_DWARF_REGS := 1
+endif
+PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
diff --git a/tools/perf/arch/riscv/include/perf_regs.h b/tools/perf/arch/riscv/include/perf_regs.h
new file mode 100644
index 000000000000..7a8bcde7a2b1
--- /dev/null
+++ b/tools/perf/arch/riscv/include/perf_regs.h
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. */
+
+#ifndef ARCH_PERF_REGS_H
+#define ARCH_PERF_REGS_H
+
+#include <stdlib.h>
+#include <linux/types.h>
+#include <asm/perf_regs.h>
+
+#define PERF_REGS_MASK ((1ULL << PERF_REG_RISCV_MAX) - 1)
+#define PERF_REGS_MAX PERF_REG_RISCV_MAX
+#if __riscv_xlen == 64
+#define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_64
+#else
+#define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_32
+#endif
+
+#define PERF_REG_IP PERF_REG_RISCV_PC
+#define PERF_REG_SP PERF_REG_RISCV_SP
+
+static inline const char *perf_reg_name(int id)
+{
+ switch (id) {
+ case PERF_REG_RISCV_PC:
+ return "pc";
+ case PERF_REG_RISCV_RA:
+ return "ra";
+ case PERF_REG_RISCV_SP:
+ return "sp";
+ case PERF_REG_RISCV_GP:
+ return "gp";
+ case PERF_REG_RISCV_TP:
+ return "tp";
+ case PERF_REG_RISCV_T0:
+ return "t0";
+ case PERF_REG_RISCV_T1:
+ return "t1";
+ case PERF_REG_RISCV_T2:
+ return "t2";
+ case PERF_REG_RISCV_S0:
+ return "s0";
+ case PERF_REG_RISCV_S1:
+ return "s1";
+ case PERF_REG_RISCV_A0:
+ return "a0";
+ case PERF_REG_RISCV_A1:
+ return "a1";
+ case PERF_REG_RISCV_A2:
+ return "a2";
+ case PERF_REG_RISCV_A3:
+ return "a3";
+ case PERF_REG_RISCV_A4:
+ return "a4";
+ case PERF_REG_RISCV_A5:
+ return "a5";
+ case PERF_REG_RISCV_A6:
+ return "a6";
+ case PERF_REG_RISCV_A7:
+ return "a7";
+ case PERF_REG_RISCV_S2:
+ return "s2";
+ case PERF_REG_RISCV_S3:
+ return "s3";
+ case PERF_REG_RISCV_S4:
+ return "s4";
+ case PERF_REG_RISCV_S5:
+ return "s5";
+ case PERF_REG_RISCV_S6:
+ return "s6";
+ case PERF_REG_RISCV_S7:
+ return "s7";
+ case PERF_REG_RISCV_S8:
+ return "s8";
+ case PERF_REG_RISCV_S9:
+ return "s9";
+ case PERF_REG_RISCV_S10:
+ return "s10";
+ case PERF_REG_RISCV_S11:
+ return "s11";
+ case PERF_REG_RISCV_T3:
+ return "t3";
+ case PERF_REG_RISCV_T4:
+ return "t4";
+ case PERF_REG_RISCV_T5:
+ return "t5";
+ case PERF_REG_RISCV_T6:
+ return "t6";
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+#endif /* ARCH_PERF_REGS_H */
diff --git a/tools/perf/arch/riscv/util/Build b/tools/perf/arch/riscv/util/Build
new file mode 100644
index 000000000000..1160bb2332ba
--- /dev/null
+++ b/tools/perf/arch/riscv/util/Build
@@ -0,0 +1,2 @@
+perf-$(CONFIG_DWARF) += dwarf-regs.o
+perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
diff --git a/tools/perf/arch/riscv/util/dwarf-regs.c b/tools/perf/arch/riscv/util/dwarf-regs.c
new file mode 100644
index 000000000000..cd0504c02e2e
--- /dev/null
+++ b/tools/perf/arch/riscv/util/dwarf-regs.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd.
+ * Mapping of DWARF debug register numbers into register names.
+ */
+
+#include <stddef.h>
+#include <errno.h> /* for EINVAL */
+#include <string.h> /* for strcmp */
+#include <dwarf-regs.h>
+
+struct pt_regs_dwarfnum {
+ const char *name;
+ unsigned int dwarfnum;
+};
+
+#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num}
+#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0}
+
+struct pt_regs_dwarfnum riscv_dwarf_regs_table[] = {
+ REG_DWARFNUM_NAME("%zero", 0),
+ REG_DWARFNUM_NAME("%ra", 1),
+ REG_DWARFNUM_NAME("%sp", 2),
+ REG_DWARFNUM_NAME("%gp", 3),
+ REG_DWARFNUM_NAME("%tp", 4),
+ REG_DWARFNUM_NAME("%t0", 5),
+ REG_DWARFNUM_NAME("%t1", 6),
+ REG_DWARFNUM_NAME("%t2", 7),
+ REG_DWARFNUM_NAME("%s0", 8),
+ REG_DWARFNUM_NAME("%s1", 9),
+ REG_DWARFNUM_NAME("%a0", 10),
+ REG_DWARFNUM_NAME("%a1", 11),
+ REG_DWARFNUM_NAME("%a2", 12),
+ REG_DWARFNUM_NAME("%a3", 13),
+ REG_DWARFNUM_NAME("%a4", 14),
+ REG_DWARFNUM_NAME("%a5", 15),
+ REG_DWARFNUM_NAME("%a6", 16),
+ REG_DWARFNUM_NAME("%a7", 17),
+ REG_DWARFNUM_NAME("%s2", 18),
+ REG_DWARFNUM_NAME("%s3", 19),
+ REG_DWARFNUM_NAME("%s4", 20),
+ REG_DWARFNUM_NAME("%s5", 21),
+ REG_DWARFNUM_NAME("%s6", 22),
+ REG_DWARFNUM_NAME("%s7", 23),
+ REG_DWARFNUM_NAME("%s8", 24),
+ REG_DWARFNUM_NAME("%s9", 25),
+ REG_DWARFNUM_NAME("%s10", 26),
+ REG_DWARFNUM_NAME("%s11", 27),
+ REG_DWARFNUM_NAME("%t3", 28),
+ REG_DWARFNUM_NAME("%t4", 29),
+ REG_DWARFNUM_NAME("%t5", 30),
+ REG_DWARFNUM_NAME("%t6", 31),
+ REG_DWARFNUM_END,
+};
+
+#define RISCV_MAX_REGS ((sizeof(riscv_dwarf_regs_table) / \
+ sizeof(riscv_dwarf_regs_table[0])) - 1)
+
+const char *get_arch_regstr(unsigned int n)
+{
+ return (n < RISCV_MAX_REGS) ? riscv_dwarf_regs_table[n].name : NULL;
+}
+
+int regs_query_register_offset(const char *name)
+{
+ const struct pt_regs_dwarfnum *roff;
+
+ for (roff = riscv_dwarf_regs_table; roff->name; roff++)
+ if (!strcmp(roff->name, name))
+ return roff->dwarfnum;
+ return -EINVAL;
+}
diff --git a/tools/perf/arch/riscv/util/unwind-libdw.c b/tools/perf/arch/riscv/util/unwind-libdw.c
new file mode 100644
index 000000000000..19536e172850
--- /dev/null
+++ b/tools/perf/arch/riscv/util/unwind-libdw.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. */
+
+#include <elfutils/libdwfl.h>
+#include "../../util/unwind-libdw.h"
+#include "../../util/perf_regs.h"
+#include "../../util/event.h"
+
+bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
+{
+ struct unwind_info *ui = arg;
+ struct regs_dump *user_regs = &ui->sample->user_regs;
+ Dwarf_Word dwarf_regs[32];
+
+#define REG(r) ({ \
+ Dwarf_Word val = 0; \
+ perf_reg_value(&val, user_regs, PERF_REG_RISCV_##r); \
+ val; \
+})
+
+ dwarf_regs[0] = 0;
+ dwarf_regs[1] = REG(RA);
+ dwarf_regs[2] = REG(SP);
+ dwarf_regs[3] = REG(GP);
+ dwarf_regs[4] = REG(TP);
+ dwarf_regs[5] = REG(T0);
+ dwarf_regs[6] = REG(T1);
+ dwarf_regs[7] = REG(T2);
+ dwarf_regs[8] = REG(S0);
+ dwarf_regs[9] = REG(S1);
+ dwarf_regs[10] = REG(A0);
+ dwarf_regs[11] = REG(A1);
+ dwarf_regs[12] = REG(A2);
+ dwarf_regs[13] = REG(A3);
+ dwarf_regs[14] = REG(A4);
+ dwarf_regs[15] = REG(A5);
+ dwarf_regs[16] = REG(A6);
+ dwarf_regs[17] = REG(A7);
+ dwarf_regs[18] = REG(S2);
+ dwarf_regs[19] = REG(S3);
+ dwarf_regs[20] = REG(S4);
+ dwarf_regs[21] = REG(S5);
+ dwarf_regs[22] = REG(S6);
+ dwarf_regs[23] = REG(S7);
+ dwarf_regs[24] = REG(S8);
+ dwarf_regs[25] = REG(S9);
+ dwarf_regs[26] = REG(S10);
+ dwarf_regs[27] = REG(S11);
+ dwarf_regs[28] = REG(T3);
+ dwarf_regs[29] = REG(T4);
+ dwarf_regs[30] = REG(T5);
+ dwarf_regs[31] = REG(T6);
+ dwfl_thread_state_register_pc(thread, REG(PC));
+
+ return dwfl_thread_state_registers(thread, 0, PERF_REG_RISCV_MAX,
+ dwarf_regs);
+}
diff --git a/tools/perf/arch/s390/Makefile b/tools/perf/arch/s390/Makefile
index dfa6e3103437..cb198787570a 100644
--- a/tools/perf/arch/s390/Makefile
+++ b/tools/perf/arch/s390/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
ifndef NO_DWARF
PERF_HAVE_DWARF_REGS := 1
endif
diff --git a/tools/perf/arch/s390/util/auxtrace.c b/tools/perf/arch/s390/util/auxtrace.c
index 44c857388897..b0fb70e38960 100644
--- a/tools/perf/arch/s390/util/auxtrace.c
+++ b/tools/perf/arch/s390/util/auxtrace.c
@@ -3,10 +3,12 @@
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/log2.h>
+#include <linux/zalloc.h>
#include "../../util/evlist.h"
#include "../../util/auxtrace.h"
#include "../../util/evsel.h"
+#include "../../util/record.h"
#define PERF_EVENT_CPUM_SF 0xB0000 /* Event: Basic-sampling */
#define PERF_EVENT_CPUM_SF_DIAG 0xBD000 /* Event: Combined-sampling */
@@ -19,7 +21,7 @@ static void cpumsf_free(struct auxtrace_record *itr)
}
static size_t cpumsf_info_priv_size(struct auxtrace_record *itr __maybe_unused,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
return 0;
}
@@ -27,7 +29,7 @@ static size_t cpumsf_info_priv_size(struct auxtrace_record *itr __maybe_unused,
static int
cpumsf_info_fill(struct auxtrace_record *itr __maybe_unused,
struct perf_session *session __maybe_unused,
- struct auxtrace_info_event *auxtrace_info __maybe_unused,
+ struct perf_record_auxtrace_info *auxtrace_info __maybe_unused,
size_t priv_size __maybe_unused)
{
auxtrace_info->type = PERF_AUXTRACE_S390_CPUMSF;
@@ -42,7 +44,7 @@ cpumsf_reference(struct auxtrace_record *itr __maybe_unused)
static int
cpumsf_recording_options(struct auxtrace_record *ar __maybe_unused,
- struct perf_evlist *evlist __maybe_unused,
+ struct evlist *evlist __maybe_unused,
struct record_opts *opts)
{
unsigned int factor = 1;
@@ -81,19 +83,19 @@ cpumsf_parse_snapshot_options(struct auxtrace_record *itr __maybe_unused,
* auxtrace_record__init is called when perf record
* check if the event really need auxtrace
*/
-struct auxtrace_record *auxtrace_record__init(struct perf_evlist *evlist,
+struct auxtrace_record *auxtrace_record__init(struct evlist *evlist,
int *err)
{
struct auxtrace_record *aux;
- struct perf_evsel *pos;
+ struct evsel *pos;
int diagnose = 0;
*err = 0;
- if (evlist->nr_entries == 0)
+ if (evlist->core.nr_entries == 0)
return NULL;
evlist__for_each_entry(evlist, pos) {
- if (pos->attr.config == PERF_EVENT_CPUM_SF_DIAG) {
+ if (pos->core.attr.config == PERF_EVENT_CPUM_SF_DIAG) {
diagnose = 1;
break;
}
diff --git a/tools/perf/arch/s390/util/header.c b/tools/perf/arch/s390/util/header.c
index 163b92f33998..8b0b018d896a 100644
--- a/tools/perf/arch/s390/util/header.c
+++ b/tools/perf/arch/s390/util/header.c
@@ -1,23 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Implementation of get_cpuid().
*
* Copyright IBM Corp. 2014, 2018
* Author(s): Alexander Yarygin <yarygin@linux.vnet.ibm.com>
* Thomas Richter <tmricht@linux.vnet.ibm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (version 2 only)
- * as published by the Free Software Foundation.
*/
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
-#include <ctype.h>
+#include <linux/ctype.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
#include "../../util/header.h"
-#include "../../util/util.h"
#define SYSINFO_MANU "Manufacturer:"
#define SYSINFO_TYPE "Type:"
diff --git a/tools/perf/arch/s390/util/kvm-stat.c b/tools/perf/arch/s390/util/kvm-stat.c
index 7e3961a4b292..0fd4e9f49ed0 100644
--- a/tools/perf/arch/s390/util/kvm-stat.c
+++ b/tools/perf/arch/s390/util/kvm-stat.c
@@ -1,15 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Arch specific functions for perf kvm stat.
*
* Copyright 2014 IBM Corp.
* Author(s): Alexander Yarygin <yarygin@linux.vnet.ibm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (version 2 only)
- * as published by the Free Software Foundation.
*/
#include <errno.h>
+#include <string.h>
#include "../../util/kvm-stat.h"
#include "../../util/evsel.h"
#include <asm/sie.h>
@@ -26,7 +24,7 @@ const char *kvm_exit_reason = "icptcode";
const char *kvm_entry_trace = "kvm:kvm_s390_sie_enter";
const char *kvm_exit_trace = "kvm:kvm_s390_sie_exit";
-static void event_icpt_insn_get_key(struct perf_evsel *evsel,
+static void event_icpt_insn_get_key(struct evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
@@ -37,7 +35,7 @@ static void event_icpt_insn_get_key(struct perf_evsel *evsel,
key->exit_reasons = sie_icpt_insn_codes;
}
-static void event_sigp_get_key(struct perf_evsel *evsel,
+static void event_sigp_get_key(struct evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
@@ -45,7 +43,7 @@ static void event_sigp_get_key(struct perf_evsel *evsel,
key->exit_reasons = sie_sigp_order_codes;
}
-static void event_diag_get_key(struct perf_evsel *evsel,
+static void event_diag_get_key(struct evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
@@ -53,7 +51,7 @@ static void event_diag_get_key(struct perf_evsel *evsel,
key->exit_reasons = sie_diagnose_codes;
}
-static void event_icpt_prog_get_key(struct perf_evsel *evsel,
+static void event_icpt_prog_get_key(struct evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
diff --git a/tools/perf/arch/s390/util/machine.c b/tools/perf/arch/s390/util/machine.c
index 0b2054007314..c8c86a0c9b79 100644
--- a/tools/perf/arch/s390/util/machine.c
+++ b/tools/perf/arch/s390/util/machine.c
@@ -5,16 +5,48 @@
#include "util.h"
#include "machine.h"
#include "api/fs/fs.h"
+#include "debug.h"
+#include "symbol.h"
-int arch__fix_module_text_start(u64 *start, const char *name)
+int arch__fix_module_text_start(u64 *start, u64 *size, const char *name)
{
+ u64 m_start = *start;
char path[PATH_MAX];
snprintf(path, PATH_MAX, "module/%.*s/sections/.text",
(int)strlen(name) - 2, name + 1);
-
- if (sysfs__read_ull(path, (unsigned long long *)start) < 0)
- return -1;
+ if (sysfs__read_ull(path, (unsigned long long *)start) < 0) {
+ pr_debug2("Using module %s start:%#lx\n", path, m_start);
+ *start = m_start;
+ } else {
+ /* Successful read of the modules segment text start address.
+ * Calculate difference between module start address
+ * in memory and module text segment start address.
+ * For example module load address is 0x3ff8011b000
+ * (from /proc/modules) and module text segment start
+ * address is 0x3ff8011b870 (from file above).
+ *
+ * Adjust the module size and subtract the GOT table
+ * size located at the beginning of the module.
+ */
+ *size -= (*start - m_start);
+ }
return 0;
}
+
+/* On s390 kernel text segment start is located at very low memory addresses,
+ * for example 0x10000. Modules are located at very high memory addresses,
+ * for example 0x3ff xxxx xxxx. The gap between end of kernel text segment
+ * and beginning of first module's text segment is very big.
+ * Therefore do not fill this gap and do not assign it to the kernel dso map.
+ */
+void arch__symbols__fixup_end(struct symbol *p, struct symbol *c)
+{
+ if (strchr(p->name, '[') == NULL && strchr(c->name, '['))
+ /* Last kernel symbol mapped to end of page */
+ p->end = roundup(p->end, page_size);
+ else
+ p->end = c->start;
+ pr_debug4("%s sym:%s end:%#lx\n", __func__, p->name, p->end);
+}
diff --git a/tools/perf/arch/sh/Makefile b/tools/perf/arch/sh/Makefile
index 7fbca175099e..88c08eed9c7b 100644
--- a/tools/perf/arch/sh/Makefile
+++ b/tools/perf/arch/sh/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
ifndef NO_DWARF
PERF_HAVE_DWARF_REGS := 1
endif
diff --git a/tools/perf/arch/sh/util/dwarf-regs.c b/tools/perf/arch/sh/util/dwarf-regs.c
index f8dfa89696f4..4b17fc86c73b 100644
--- a/tools/perf/arch/sh/util/dwarf-regs.c
+++ b/tools/perf/arch/sh/util/dwarf-regs.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Mapping of DWARF debug register numbers into register names.
*
* Copyright (C) 2010 Matt Fleming <matt@console-pimps.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
*/
#include <stddef.h>
diff --git a/tools/perf/arch/sparc/Makefile b/tools/perf/arch/sparc/Makefile
index 275dea7ff59a..4031db72ba71 100644
--- a/tools/perf/arch/sparc/Makefile
+++ b/tools/perf/arch/sparc/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
ifndef NO_DWARF
PERF_HAVE_DWARF_REGS := 1
endif
diff --git a/tools/perf/arch/sparc/util/dwarf-regs.c b/tools/perf/arch/sparc/util/dwarf-regs.c
index b704fdb9237a..1282cb2dc7bd 100644
--- a/tools/perf/arch/sparc/util/dwarf-regs.c
+++ b/tools/perf/arch/sparc/util/dwarf-regs.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Mapping of DWARF debug register numbers into register names.
*
* Copyright (C) 2010 David S. Miller <davem@davemloft.net>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include <stddef.h>
diff --git a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl
index 92ee0b4378d4..c29976eca4a8 100644
--- a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl
@@ -349,6 +349,14 @@
425 common io_uring_setup __x64_sys_io_uring_setup
426 common io_uring_enter __x64_sys_io_uring_enter
427 common io_uring_register __x64_sys_io_uring_register
+428 common open_tree __x64_sys_open_tree
+429 common move_mount __x64_sys_move_mount
+430 common fsopen __x64_sys_fsopen
+431 common fsconfig __x64_sys_fsconfig
+432 common fsmount __x64_sys_fsmount
+433 common fspick __x64_sys_fspick
+434 common pidfd_open __x64_sys_pidfd_open
+435 common clone3 __x64_sys_clone3/ptregs
#
# x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/tools/perf/arch/x86/include/arch-tests.h b/tools/perf/arch/x86/include/arch-tests.h
index 613709cfbbd0..c41c5affe4be 100644
--- a/tools/perf/arch/x86/include/arch-tests.h
+++ b/tools/perf/arch/x86/include/arch-tests.h
@@ -9,6 +9,7 @@ struct test;
int test__rdpmc(struct test *test __maybe_unused, int subtest);
int test__perf_time_to_tsc(struct test *test __maybe_unused, int subtest);
int test__insn_x86(struct test *test __maybe_unused, int subtest);
+int test__intel_pt_pkt_decoder(struct test *test, int subtest);
int test__bp_modify(struct test *test, int subtest);
#ifdef HAVE_DWARF_UNWIND_SUPPORT
diff --git a/tools/perf/arch/x86/include/perf_regs.h b/tools/perf/arch/x86/include/perf_regs.h
index b7cd91a9014f..b7321337d100 100644
--- a/tools/perf/arch/x86/include/perf_regs.h
+++ b/tools/perf/arch/x86/include/perf_regs.h
@@ -9,7 +9,6 @@
void perf_regs_load(u64 *regs);
#define PERF_REGS_MAX PERF_REG_X86_XMM_MAX
-#define PERF_XMM_REGS_MASK (~((1ULL << PERF_REG_X86_XMM0) - 1))
#ifndef HAVE_ARCH_X86_64_SUPPORT
#define PERF_REGS_MASK ((1ULL << PERF_REG_X86_32_MAX) - 1)
#define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_32
diff --git a/tools/perf/arch/x86/tests/Build b/tools/perf/arch/x86/tests/Build
index 3d83d0c6982d..2997c506550c 100644
--- a/tools/perf/arch/x86/tests/Build
+++ b/tools/perf/arch/x86/tests/Build
@@ -4,5 +4,5 @@ perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
perf-y += arch-tests.o
perf-y += rdpmc.o
perf-y += perf-time-to-tsc.o
-perf-$(CONFIG_AUXTRACE) += insn-x86.o
+perf-$(CONFIG_AUXTRACE) += insn-x86.o intel-pt-pkt-decoder-test.o
perf-$(CONFIG_X86_64) += bp-modify.o
diff --git a/tools/perf/arch/x86/tests/arch-tests.c b/tools/perf/arch/x86/tests/arch-tests.c
index d47d3f8e3c8e..6763135aec17 100644
--- a/tools/perf/arch/x86/tests/arch-tests.c
+++ b/tools/perf/arch/x86/tests/arch-tests.c
@@ -23,6 +23,10 @@ struct test arch_tests[] = {
.desc = "x86 instruction decoder - new instructions",
.func = test__insn_x86,
},
+ {
+ .desc = "Intel PT packet decoder",
+ .func = test__intel_pt_pkt_decoder,
+ },
#endif
#if defined(__x86_64__)
{
diff --git a/tools/perf/arch/x86/tests/bp-modify.c b/tools/perf/arch/x86/tests/bp-modify.c
index f53e4406709f..adcacf1b6609 100644
--- a/tools/perf/arch/x86/tests/bp-modify.c
+++ b/tools/perf/arch/x86/tests/bp-modify.c
@@ -7,6 +7,7 @@
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/ptrace.h>
#include <asm/ptrace.h>
#include <errno.h>
diff --git a/tools/perf/arch/x86/tests/gen-insn-x86-dat.awk b/tools/perf/arch/x86/tests/gen-insn-x86-dat.awk
index a21454835cd4..1a29f6379bde 100644
--- a/tools/perf/arch/x86/tests/gen-insn-x86-dat.awk
+++ b/tools/perf/arch/x86/tests/gen-insn-x86-dat.awk
@@ -1,15 +1,8 @@
#!/bin/awk -f
+# SPDX-License-Identifier: GPL-2.0-only
# gen-insn-x86-dat.awk: script to convert data for the insn-x86 test
# Copyright (c) 2015, Intel Corporation.
#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms and conditions of the GNU General Public License,
-# version 2, as published by the Free Software Foundation.
-#
-# This program is distributed in the hope it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-# more details.
BEGIN {
print "/*"
diff --git a/tools/perf/arch/x86/tests/gen-insn-x86-dat.sh b/tools/perf/arch/x86/tests/gen-insn-x86-dat.sh
index 2d4ef94cff98..0d0a003a9c5e 100755
--- a/tools/perf/arch/x86/tests/gen-insn-x86-dat.sh
+++ b/tools/perf/arch/x86/tests/gen-insn-x86-dat.sh
@@ -1,15 +1,8 @@
#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
# gen-insn-x86-dat: generate data for the insn-x86 test
# Copyright (c) 2015, Intel Corporation.
#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms and conditions of the GNU General Public License,
-# version 2, as published by the Free Software Foundation.
-#
-# This program is distributed in the hope it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-# more details.
set -e
diff --git a/tools/perf/arch/x86/tests/insn-x86.c b/tools/perf/arch/x86/tests/insn-x86.c
index c3e5f4ab0d3e..745f29adb14b 100644
--- a/tools/perf/arch/x86/tests/insn-x86.c
+++ b/tools/perf/arch/x86/tests/insn-x86.c
@@ -1,11 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/types.h>
+#include "../../../../arch/x86/include/asm/insn.h"
+#include <string.h>
#include "debug.h"
#include "tests/tests.h"
#include "arch-tests.h"
-#include "intel-pt-decoder/insn.h"
#include "intel-pt-decoder/intel-pt-insn-decoder.h"
struct test_data {
diff --git a/tools/perf/arch/x86/tests/intel-cqm.c b/tools/perf/arch/x86/tests/intel-cqm.c
index 90a4a8c58a62..3b5cc3373821 100644
--- a/tools/perf/arch/x86/tests/intel-cqm.c
+++ b/tools/perf/arch/x86/tests/intel-cqm.c
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
#include "tests/tests.h"
-#include "perf.h"
#include "cloexec.h"
#include "debug.h"
#include "evlist.h"
#include "evsel.h"
#include "arch-tests.h"
+#include "util.h"
#include <signal.h>
#include <sys/mman.h>
@@ -39,8 +39,8 @@ static pid_t spawn(void)
*/
int test__intel_cqm_count_nmi_context(struct test *test __maybe_unused, int subtest __maybe_unused)
{
- struct perf_evlist *evlist = NULL;
- struct perf_evsel *evsel = NULL;
+ struct evlist *evlist = NULL;
+ struct evsel *evsel = NULL;
struct perf_event_attr pe;
int i, fd[2], flag, ret;
size_t mmap_len;
@@ -50,7 +50,7 @@ int test__intel_cqm_count_nmi_context(struct test *test __maybe_unused, int subt
flag = perf_event_open_cloexec_flag();
- evlist = perf_evlist__new();
+ evlist = evlist__new();
if (!evlist) {
pr_debug("perf_evlist__new failed\n");
return TEST_FAIL;
@@ -123,6 +123,6 @@ int test__intel_cqm_count_nmi_context(struct test *test __maybe_unused, int subt
kill(pid, SIGKILL);
wait(NULL);
out:
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
return err;
}
diff --git a/tools/perf/arch/x86/tests/intel-pt-pkt-decoder-test.c b/tools/perf/arch/x86/tests/intel-pt-pkt-decoder-test.c
new file mode 100644
index 000000000000..901bf1f449c4
--- /dev/null
+++ b/tools/perf/arch/x86/tests/intel-pt-pkt-decoder-test.c
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <string.h>
+
+#include "intel-pt-decoder/intel-pt-pkt-decoder.h"
+
+#include "debug.h"
+#include "tests/tests.h"
+#include "arch-tests.h"
+
+/**
+ * struct test_data - Test data.
+ * @len: number of bytes to decode
+ * @bytes: bytes to decode
+ * @ctx: packet context to decode
+ * @packet: expected packet
+ * @new_ctx: expected new packet context
+ * @ctx_unchanged: the packet context must not change
+ */
+struct test_data {
+ int len;
+ u8 bytes[INTEL_PT_PKT_MAX_SZ];
+ enum intel_pt_pkt_ctx ctx;
+ struct intel_pt_pkt packet;
+ enum intel_pt_pkt_ctx new_ctx;
+ int ctx_unchanged;
+} data[] = {
+ /* Padding Packet */
+ {1, {0}, 0, {INTEL_PT_PAD, 0, 0}, 0, 1 },
+ /* Short Taken/Not Taken Packet */
+ {1, {4}, 0, {INTEL_PT_TNT, 1, 0}, 0, 0 },
+ {1, {6}, 0, {INTEL_PT_TNT, 1, 0x20ULL << 58}, 0, 0 },
+ {1, {0x80}, 0, {INTEL_PT_TNT, 6, 0}, 0, 0 },
+ {1, {0xfe}, 0, {INTEL_PT_TNT, 6, 0x3fULL << 58}, 0, 0 },
+ /* Long Taken/Not Taken Packet */
+ {8, {0x02, 0xa3, 2}, 0, {INTEL_PT_TNT, 1, 0xa302ULL << 47}, 0, 0 },
+ {8, {0x02, 0xa3, 3}, 0, {INTEL_PT_TNT, 1, 0x1a302ULL << 47}, 0, 0 },
+ {8, {0x02, 0xa3, 0, 0, 0, 0, 0, 0x80}, 0, {INTEL_PT_TNT, 47, 0xa302ULL << 1}, 0, 0 },
+ {8, {0x02, 0xa3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, {INTEL_PT_TNT, 47, 0xffffffffffffa302ULL << 1}, 0, 0 },
+ /* Target IP Packet */
+ {1, {0x0d}, 0, {INTEL_PT_TIP, 0, 0}, 0, 0 },
+ {3, {0x2d, 1, 2}, 0, {INTEL_PT_TIP, 1, 0x201}, 0, 0 },
+ {5, {0x4d, 1, 2, 3, 4}, 0, {INTEL_PT_TIP, 2, 0x4030201}, 0, 0 },
+ {7, {0x6d, 1, 2, 3, 4, 5, 6}, 0, {INTEL_PT_TIP, 3, 0x60504030201}, 0, 0 },
+ {7, {0x8d, 1, 2, 3, 4, 5, 6}, 0, {INTEL_PT_TIP, 4, 0x60504030201}, 0, 0 },
+ {9, {0xcd, 1, 2, 3, 4, 5, 6, 7, 8}, 0, {INTEL_PT_TIP, 6, 0x807060504030201}, 0, 0 },
+ /* Packet Generation Enable */
+ {1, {0x11}, 0, {INTEL_PT_TIP_PGE, 0, 0}, 0, 0 },
+ {3, {0x31, 1, 2}, 0, {INTEL_PT_TIP_PGE, 1, 0x201}, 0, 0 },
+ {5, {0x51, 1, 2, 3, 4}, 0, {INTEL_PT_TIP_PGE, 2, 0x4030201}, 0, 0 },
+ {7, {0x71, 1, 2, 3, 4, 5, 6}, 0, {INTEL_PT_TIP_PGE, 3, 0x60504030201}, 0, 0 },
+ {7, {0x91, 1, 2, 3, 4, 5, 6}, 0, {INTEL_PT_TIP_PGE, 4, 0x60504030201}, 0, 0 },
+ {9, {0xd1, 1, 2, 3, 4, 5, 6, 7, 8}, 0, {INTEL_PT_TIP_PGE, 6, 0x807060504030201}, 0, 0 },
+ /* Packet Generation Disable */
+ {1, {0x01}, 0, {INTEL_PT_TIP_PGD, 0, 0}, 0, 0 },
+ {3, {0x21, 1, 2}, 0, {INTEL_PT_TIP_PGD, 1, 0x201}, 0, 0 },
+ {5, {0x41, 1, 2, 3, 4}, 0, {INTEL_PT_TIP_PGD, 2, 0x4030201}, 0, 0 },
+ {7, {0x61, 1, 2, 3, 4, 5, 6}, 0, {INTEL_PT_TIP_PGD, 3, 0x60504030201}, 0, 0 },
+ {7, {0x81, 1, 2, 3, 4, 5, 6}, 0, {INTEL_PT_TIP_PGD, 4, 0x60504030201}, 0, 0 },
+ {9, {0xc1, 1, 2, 3, 4, 5, 6, 7, 8}, 0, {INTEL_PT_TIP_PGD, 6, 0x807060504030201}, 0, 0 },
+ /* Flow Update Packet */
+ {1, {0x1d}, 0, {INTEL_PT_FUP, 0, 0}, 0, 0 },
+ {3, {0x3d, 1, 2}, 0, {INTEL_PT_FUP, 1, 0x201}, 0, 0 },
+ {5, {0x5d, 1, 2, 3, 4}, 0, {INTEL_PT_FUP, 2, 0x4030201}, 0, 0 },
+ {7, {0x7d, 1, 2, 3, 4, 5, 6}, 0, {INTEL_PT_FUP, 3, 0x60504030201}, 0, 0 },
+ {7, {0x9d, 1, 2, 3, 4, 5, 6}, 0, {INTEL_PT_FUP, 4, 0x60504030201}, 0, 0 },
+ {9, {0xdd, 1, 2, 3, 4, 5, 6, 7, 8}, 0, {INTEL_PT_FUP, 6, 0x807060504030201}, 0, 0 },
+ /* Paging Information Packet */
+ {8, {0x02, 0x43, 2, 4, 6, 8, 10, 12}, 0, {INTEL_PT_PIP, 0, 0x60504030201}, 0, 0 },
+ {8, {0x02, 0x43, 3, 4, 6, 8, 10, 12}, 0, {INTEL_PT_PIP, 0, 0x60504030201 | (1ULL << 63)}, 0, 0 },
+ /* Mode Exec Packet */
+ {2, {0x99, 0x00}, 0, {INTEL_PT_MODE_EXEC, 0, 16}, 0, 0 },
+ {2, {0x99, 0x01}, 0, {INTEL_PT_MODE_EXEC, 0, 64}, 0, 0 },
+ {2, {0x99, 0x02}, 0, {INTEL_PT_MODE_EXEC, 0, 32}, 0, 0 },
+ /* Mode TSX Packet */
+ {2, {0x99, 0x20}, 0, {INTEL_PT_MODE_TSX, 0, 0}, 0, 0 },
+ {2, {0x99, 0x21}, 0, {INTEL_PT_MODE_TSX, 0, 1}, 0, 0 },
+ {2, {0x99, 0x22}, 0, {INTEL_PT_MODE_TSX, 0, 2}, 0, 0 },
+ /* Trace Stop Packet */
+ {2, {0x02, 0x83}, 0, {INTEL_PT_TRACESTOP, 0, 0}, 0, 0 },
+ /* Core:Bus Ratio Packet */
+ {4, {0x02, 0x03, 0x12, 0}, 0, {INTEL_PT_CBR, 0, 0x12}, 0, 1 },
+ /* Timestamp Counter Packet */
+ {8, {0x19, 1, 2, 3, 4, 5, 6, 7}, 0, {INTEL_PT_TSC, 0, 0x7060504030201}, 0, 1 },
+ /* Mini Time Counter Packet */
+ {2, {0x59, 0x12}, 0, {INTEL_PT_MTC, 0, 0x12}, 0, 1 },
+ /* TSC / MTC Alignment Packet */
+ {7, {0x02, 0x73}, 0, {INTEL_PT_TMA, 0, 0}, 0, 1 },
+ {7, {0x02, 0x73, 1, 2}, 0, {INTEL_PT_TMA, 0, 0x201}, 0, 1 },
+ {7, {0x02, 0x73, 0, 0, 0, 0xff, 1}, 0, {INTEL_PT_TMA, 0x1ff, 0}, 0, 1 },
+ {7, {0x02, 0x73, 0x80, 0xc0, 0, 0xff, 1}, 0, {INTEL_PT_TMA, 0x1ff, 0xc080}, 0, 1 },
+ /* Cycle Count Packet */
+ {1, {0x03}, 0, {INTEL_PT_CYC, 0, 0}, 0, 1 },
+ {1, {0x0b}, 0, {INTEL_PT_CYC, 0, 1}, 0, 1 },
+ {1, {0xfb}, 0, {INTEL_PT_CYC, 0, 0x1f}, 0, 1 },
+ {2, {0x07, 2}, 0, {INTEL_PT_CYC, 0, 0x20}, 0, 1 },
+ {2, {0xff, 0xfe}, 0, {INTEL_PT_CYC, 0, 0xfff}, 0, 1 },
+ {3, {0x07, 1, 2}, 0, {INTEL_PT_CYC, 0, 0x1000}, 0, 1 },
+ {3, {0xff, 0xff, 0xfe}, 0, {INTEL_PT_CYC, 0, 0x7ffff}, 0, 1 },
+ {4, {0x07, 1, 1, 2}, 0, {INTEL_PT_CYC, 0, 0x80000}, 0, 1 },
+ {4, {0xff, 0xff, 0xff, 0xfe}, 0, {INTEL_PT_CYC, 0, 0x3ffffff}, 0, 1 },
+ {5, {0x07, 1, 1, 1, 2}, 0, {INTEL_PT_CYC, 0, 0x4000000}, 0, 1 },
+ {5, {0xff, 0xff, 0xff, 0xff, 0xfe}, 0, {INTEL_PT_CYC, 0, 0x1ffffffff}, 0, 1 },
+ {6, {0x07, 1, 1, 1, 1, 2}, 0, {INTEL_PT_CYC, 0, 0x200000000}, 0, 1 },
+ {6, {0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}, 0, {INTEL_PT_CYC, 0, 0xffffffffff}, 0, 1 },
+ {7, {0x07, 1, 1, 1, 1, 1, 2}, 0, {INTEL_PT_CYC, 0, 0x10000000000}, 0, 1 },
+ {7, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}, 0, {INTEL_PT_CYC, 0, 0x7fffffffffff}, 0, 1 },
+ {8, {0x07, 1, 1, 1, 1, 1, 1, 2}, 0, {INTEL_PT_CYC, 0, 0x800000000000}, 0, 1 },
+ {8, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}, 0, {INTEL_PT_CYC, 0, 0x3fffffffffffff}, 0, 1 },
+ {9, {0x07, 1, 1, 1, 1, 1, 1, 1, 2}, 0, {INTEL_PT_CYC, 0, 0x40000000000000}, 0, 1 },
+ {9, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}, 0, {INTEL_PT_CYC, 0, 0x1fffffffffffffff}, 0, 1 },
+ {10, {0x07, 1, 1, 1, 1, 1, 1, 1, 1, 2}, 0, {INTEL_PT_CYC, 0, 0x2000000000000000}, 0, 1 },
+ {10, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe}, 0, {INTEL_PT_CYC, 0, 0xffffffffffffffff}, 0, 1 },
+ /* Virtual-Machine Control Structure Packet */
+ {7, {0x02, 0xc8, 1, 2, 3, 4, 5}, 0, {INTEL_PT_VMCS, 5, 0x504030201}, 0, 0 },
+ /* Overflow Packet */
+ {2, {0x02, 0xf3}, 0, {INTEL_PT_OVF, 0, 0}, 0, 0 },
+ {2, {0x02, 0xf3}, INTEL_PT_BLK_4_CTX, {INTEL_PT_OVF, 0, 0}, 0, 0 },
+ {2, {0x02, 0xf3}, INTEL_PT_BLK_8_CTX, {INTEL_PT_OVF, 0, 0}, 0, 0 },
+ /* Packet Stream Boundary*/
+ {16, {0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82}, 0, {INTEL_PT_PSB, 0, 0}, 0, 0 },
+ {16, {0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82}, INTEL_PT_BLK_4_CTX, {INTEL_PT_PSB, 0, 0}, 0, 0 },
+ {16, {0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82}, INTEL_PT_BLK_8_CTX, {INTEL_PT_PSB, 0, 0}, 0, 0 },
+ /* PSB End Packet */
+ {2, {0x02, 0x23}, 0, {INTEL_PT_PSBEND, 0, 0}, 0, 0 },
+ /* Maintenance Packet */
+ {11, {0x02, 0xc3, 0x88, 1, 2, 3, 4, 5, 6, 7}, 0, {INTEL_PT_MNT, 0, 0x7060504030201}, 0, 1 },
+ /* Write Data to PT Packet */
+ {6, {0x02, 0x12, 1, 2, 3, 4}, 0, {INTEL_PT_PTWRITE, 0, 0x4030201}, 0, 0 },
+ {10, {0x02, 0x32, 1, 2, 3, 4, 5, 6, 7, 8}, 0, {INTEL_PT_PTWRITE, 1, 0x807060504030201}, 0, 0 },
+ {6, {0x02, 0x92, 1, 2, 3, 4}, 0, {INTEL_PT_PTWRITE_IP, 0, 0x4030201}, 0, 0 },
+ {10, {0x02, 0xb2, 1, 2, 3, 4, 5, 6, 7, 8}, 0, {INTEL_PT_PTWRITE_IP, 1, 0x807060504030201}, 0, 0 },
+ /* Execution Stop Packet */
+ {2, {0x02, 0x62}, 0, {INTEL_PT_EXSTOP, 0, 0}, 0, 1 },
+ {2, {0x02, 0xe2}, 0, {INTEL_PT_EXSTOP_IP, 0, 0}, 0, 1 },
+ /* Monitor Wait Packet */
+ {10, {0x02, 0xc2}, 0, {INTEL_PT_MWAIT, 0, 0}, 0, 0 },
+ {10, {0x02, 0xc2, 1, 2, 3, 4, 5, 6, 7, 8}, 0, {INTEL_PT_MWAIT, 0, 0x807060504030201}, 0, 0 },
+ {10, {0x02, 0xc2, 0xff, 2, 3, 4, 7, 6, 7, 8}, 0, {INTEL_PT_MWAIT, 0, 0x8070607040302ff}, 0, 0 },
+ /* Power Entry Packet */
+ {4, {0x02, 0x22}, 0, {INTEL_PT_PWRE, 0, 0}, 0, 1 },
+ {4, {0x02, 0x22, 1, 2}, 0, {INTEL_PT_PWRE, 0, 0x0201}, 0, 1 },
+ {4, {0x02, 0x22, 0x80, 0x34}, 0, {INTEL_PT_PWRE, 0, 0x3480}, 0, 1 },
+ {4, {0x02, 0x22, 0x00, 0x56}, 0, {INTEL_PT_PWRE, 0, 0x5600}, 0, 1 },
+ /* Power Exit Packet */
+ {7, {0x02, 0xa2}, 0, {INTEL_PT_PWRX, 0, 0}, 0, 1 },
+ {7, {0x02, 0xa2, 1, 2, 3, 4, 5}, 0, {INTEL_PT_PWRX, 0, 0x504030201}, 0, 1 },
+ {7, {0x02, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, {INTEL_PT_PWRX, 0, 0xffffffffff}, 0, 1 },
+ /* Block Begin Packet */
+ {3, {0x02, 0x63, 0x00}, 0, {INTEL_PT_BBP, 0, 0}, INTEL_PT_BLK_8_CTX, 0 },
+ {3, {0x02, 0x63, 0x80}, 0, {INTEL_PT_BBP, 1, 0}, INTEL_PT_BLK_4_CTX, 0 },
+ {3, {0x02, 0x63, 0x1f}, 0, {INTEL_PT_BBP, 0, 0x1f}, INTEL_PT_BLK_8_CTX, 0 },
+ {3, {0x02, 0x63, 0x9f}, 0, {INTEL_PT_BBP, 1, 0x1f}, INTEL_PT_BLK_4_CTX, 0 },
+ /* 4-byte Block Item Packet */
+ {5, {0x04}, INTEL_PT_BLK_4_CTX, {INTEL_PT_BIP, 0, 0}, INTEL_PT_BLK_4_CTX, 0 },
+ {5, {0xfc}, INTEL_PT_BLK_4_CTX, {INTEL_PT_BIP, 0x1f, 0}, INTEL_PT_BLK_4_CTX, 0 },
+ {5, {0x04, 1, 2, 3, 4}, INTEL_PT_BLK_4_CTX, {INTEL_PT_BIP, 0, 0x04030201}, INTEL_PT_BLK_4_CTX, 0 },
+ {5, {0xfc, 1, 2, 3, 4}, INTEL_PT_BLK_4_CTX, {INTEL_PT_BIP, 0x1f, 0x04030201}, INTEL_PT_BLK_4_CTX, 0 },
+ /* 8-byte Block Item Packet */
+ {9, {0x04}, INTEL_PT_BLK_8_CTX, {INTEL_PT_BIP, 0, 0}, INTEL_PT_BLK_8_CTX, 0 },
+ {9, {0xfc}, INTEL_PT_BLK_8_CTX, {INTEL_PT_BIP, 0x1f, 0}, INTEL_PT_BLK_8_CTX, 0 },
+ {9, {0x04, 1, 2, 3, 4, 5, 6, 7, 8}, INTEL_PT_BLK_8_CTX, {INTEL_PT_BIP, 0, 0x0807060504030201}, INTEL_PT_BLK_8_CTX, 0 },
+ {9, {0xfc, 1, 2, 3, 4, 5, 6, 7, 8}, INTEL_PT_BLK_8_CTX, {INTEL_PT_BIP, 0x1f, 0x0807060504030201}, INTEL_PT_BLK_8_CTX, 0 },
+ /* Block End Packet */
+ {2, {0x02, 0x33}, INTEL_PT_BLK_4_CTX, {INTEL_PT_BEP, 0, 0}, 0, 0 },
+ {2, {0x02, 0xb3}, INTEL_PT_BLK_4_CTX, {INTEL_PT_BEP_IP, 0, 0}, 0, 0 },
+ {2, {0x02, 0x33}, INTEL_PT_BLK_8_CTX, {INTEL_PT_BEP, 0, 0}, 0, 0 },
+ {2, {0x02, 0xb3}, INTEL_PT_BLK_8_CTX, {INTEL_PT_BEP_IP, 0, 0}, 0, 0 },
+ /* Terminator */
+ {0, {0}, 0, {0, 0, 0}, 0, 0 },
+};
+
+static int dump_packet(struct intel_pt_pkt *packet, u8 *bytes, int len)
+{
+ char desc[INTEL_PT_PKT_DESC_MAX];
+ int ret, i;
+
+ for (i = 0; i < len; i++)
+ pr_debug(" %02x", bytes[i]);
+ for (; i < INTEL_PT_PKT_MAX_SZ; i++)
+ pr_debug(" ");
+ pr_debug(" ");
+ ret = intel_pt_pkt_desc(packet, desc, INTEL_PT_PKT_DESC_MAX);
+ if (ret < 0) {
+ pr_debug("intel_pt_pkt_desc failed!\n");
+ return TEST_FAIL;
+ }
+ pr_debug("%s\n", desc);
+
+ return TEST_OK;
+}
+
+static void decoding_failed(struct test_data *d)
+{
+ pr_debug("Decoding failed!\n");
+ pr_debug("Decoding: ");
+ dump_packet(&d->packet, d->bytes, d->len);
+}
+
+static int fail(struct test_data *d, struct intel_pt_pkt *packet, int len,
+ enum intel_pt_pkt_ctx new_ctx)
+{
+ decoding_failed(d);
+
+ if (len != d->len)
+ pr_debug("Expected length: %d Decoded length %d\n",
+ d->len, len);
+
+ if (packet->type != d->packet.type)
+ pr_debug("Expected type: %d Decoded type %d\n",
+ d->packet.type, packet->type);
+
+ if (packet->count != d->packet.count)
+ pr_debug("Expected count: %d Decoded count %d\n",
+ d->packet.count, packet->count);
+
+ if (packet->payload != d->packet.payload)
+ pr_debug("Expected payload: 0x%llx Decoded payload 0x%llx\n",
+ (unsigned long long)d->packet.payload,
+ (unsigned long long)packet->payload);
+
+ if (new_ctx != d->new_ctx)
+ pr_debug("Expected packet context: %d Decoded packet context %d\n",
+ d->new_ctx, new_ctx);
+
+ return TEST_FAIL;
+}
+
+static int test_ctx_unchanged(struct test_data *d, struct intel_pt_pkt *packet,
+ enum intel_pt_pkt_ctx ctx)
+{
+ enum intel_pt_pkt_ctx old_ctx = ctx;
+
+ intel_pt_upd_pkt_ctx(packet, &ctx);
+
+ if (ctx != old_ctx) {
+ decoding_failed(d);
+ pr_debug("Packet context changed!\n");
+ return TEST_FAIL;
+ }
+
+ return TEST_OK;
+}
+
+static int test_one(struct test_data *d)
+{
+ struct intel_pt_pkt packet;
+ enum intel_pt_pkt_ctx ctx = d->ctx;
+ int ret;
+
+ memset(&packet, 0xff, sizeof(packet));
+
+ /* Decode a packet */
+ ret = intel_pt_get_packet(d->bytes, d->len, &packet, &ctx);
+ if (ret < 0 || ret > INTEL_PT_PKT_MAX_SZ) {
+ decoding_failed(d);
+ pr_debug("intel_pt_get_packet returned %d\n", ret);
+ return TEST_FAIL;
+ }
+
+ /* Some packets must always leave the packet context unchanged */
+ if (d->ctx_unchanged) {
+ int err;
+
+ err = test_ctx_unchanged(d, &packet, INTEL_PT_NO_CTX);
+ if (err)
+ return err;
+ err = test_ctx_unchanged(d, &packet, INTEL_PT_BLK_4_CTX);
+ if (err)
+ return err;
+ err = test_ctx_unchanged(d, &packet, INTEL_PT_BLK_8_CTX);
+ if (err)
+ return err;
+ }
+
+ /* Compare to the expected values */
+ if (ret != d->len || packet.type != d->packet.type ||
+ packet.count != d->packet.count ||
+ packet.payload != d->packet.payload || ctx != d->new_ctx)
+ return fail(d, &packet, ret, ctx);
+
+ pr_debug("Decoded ok:");
+ ret = dump_packet(&d->packet, d->bytes, d->len);
+
+ return ret;
+}
+
+/*
+ * This test feeds byte sequences to the Intel PT packet decoder and checks the
+ * results. Changes to the packet context are also checked.
+ */
+int test__intel_pt_pkt_decoder(struct test *test __maybe_unused, int subtest __maybe_unused)
+{
+ struct test_data *d = data;
+ int ret;
+
+ for (d = data; d->len; d++) {
+ ret = test_one(d);
+ if (ret)
+ return ret;
+ }
+
+ return TEST_OK;
+}
diff --git a/tools/perf/arch/x86/tests/perf-time-to-tsc.c b/tools/perf/arch/x86/tests/perf-time-to-tsc.c
index 7a7721604b86..eb3635941c2b 100644
--- a/tools/perf/arch/x86/tests/perf-time-to-tsc.c
+++ b/tools/perf/arch/x86/tests/perf-time-to-tsc.c
@@ -1,16 +1,22 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <linux/types.h>
#include <sys/prctl.h>
+#include <perf/cpumap.h>
+#include <perf/evlist.h>
+#include "debug.h"
#include "parse-events.h"
#include "evlist.h"
#include "evsel.h"
#include "thread_map.h"
#include "cpumap.h"
+#include "record.h"
#include "tsc.h"
#include "tests/tests.h"
@@ -49,10 +55,10 @@ int test__perf_time_to_tsc(struct test *test __maybe_unused, int subtest __maybe
},
.sample_time = true,
};
- struct thread_map *threads = NULL;
- struct cpu_map *cpus = NULL;
- struct perf_evlist *evlist = NULL;
- struct perf_evsel *evsel = NULL;
+ struct perf_thread_map *threads = NULL;
+ struct perf_cpu_map *cpus = NULL;
+ struct evlist *evlist = NULL;
+ struct evsel *evsel = NULL;
int err = -1, ret, i;
const char *comm1, *comm2;
struct perf_tsc_conversion tc;
@@ -65,13 +71,13 @@ int test__perf_time_to_tsc(struct test *test __maybe_unused, int subtest __maybe
threads = thread_map__new(-1, getpid(), UINT_MAX);
CHECK_NOT_NULL__(threads);
- cpus = cpu_map__new(NULL);
+ cpus = perf_cpu_map__new(NULL);
CHECK_NOT_NULL__(cpus);
- evlist = perf_evlist__new();
+ evlist = evlist__new();
CHECK_NOT_NULL__(evlist);
- perf_evlist__set_maps(evlist, cpus, threads);
+ perf_evlist__set_maps(&evlist->core, cpus, threads);
CHECK__(parse_events(evlist, "cycles:u", NULL));
@@ -79,11 +85,11 @@ int test__perf_time_to_tsc(struct test *test __maybe_unused, int subtest __maybe
evsel = perf_evlist__first(evlist);
- evsel->attr.comm = 1;
- evsel->attr.disabled = 1;
- evsel->attr.enable_on_exec = 0;
+ evsel->core.attr.comm = 1;
+ evsel->core.attr.disabled = 1;
+ evsel->core.attr.enable_on_exec = 0;
- CHECK__(perf_evlist__open(evlist));
+ CHECK__(evlist__open(evlist));
CHECK__(perf_evlist__mmap(evlist, UINT_MAX));
@@ -97,7 +103,7 @@ int test__perf_time_to_tsc(struct test *test __maybe_unused, int subtest __maybe
goto out_err;
}
- perf_evlist__enable(evlist);
+ evlist__enable(evlist);
comm1 = "Test COMM 1";
CHECK__(prctl(PR_SET_NAME, (unsigned long)comm1, 0, 0, 0));
@@ -107,7 +113,7 @@ int test__perf_time_to_tsc(struct test *test __maybe_unused, int subtest __maybe
comm2 = "Test COMM 2";
CHECK__(prctl(PR_SET_NAME, (unsigned long)comm2, 0, 0, 0));
- perf_evlist__disable(evlist);
+ evlist__disable(evlist);
for (i = 0; i < evlist->nr_mmaps; i++) {
md = &evlist->mmap[i];
@@ -163,6 +169,6 @@ next_event:
err = 0;
out_err:
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
return err;
}
diff --git a/tools/perf/arch/x86/tests/rdpmc.c b/tools/perf/arch/x86/tests/rdpmc.c
index 7a11f02d6c6c..6e67cee792b1 100644
--- a/tools/perf/arch/x86/tests/rdpmc.c
+++ b/tools/perf/arch/x86/tests/rdpmc.c
@@ -6,11 +6,13 @@
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <linux/string.h>
#include <linux/types.h>
-#include "perf.h"
+#include "perf-sys.h"
#include "debug.h"
#include "tests/tests.h"
#include "cloexec.h"
+#include "event.h"
#include "util.h"
#include "arch-tests.h"
diff --git a/tools/perf/arch/x86/util/archinsn.c b/tools/perf/arch/x86/util/archinsn.c
index 4237bb2e7fa2..9876c7a7ed7c 100644
--- a/tools/perf/arch/x86/util/archinsn.c
+++ b/tools/perf/arch/x86/util/archinsn.c
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
-#include "perf.h"
+#include "../../../../arch/x86/include/asm/insn.h"
#include "archinsn.h"
-#include "util/intel-pt-decoder/insn.h"
#include "machine.h"
#include "thread.h"
#include "symbol.h"
diff --git a/tools/perf/arch/x86/util/auxtrace.c b/tools/perf/arch/x86/util/auxtrace.c
index b135af62011c..96f4a2c11893 100644
--- a/tools/perf/arch/x86/util/auxtrace.c
+++ b/tools/perf/arch/x86/util/auxtrace.c
@@ -1,16 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* auxtrace.c: AUX area tracing support
* Copyright (c) 2013-2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#include <errno.h>
@@ -25,12 +16,12 @@
#include "../../util/evlist.h"
static
-struct auxtrace_record *auxtrace_record__init_intel(struct perf_evlist *evlist,
+struct auxtrace_record *auxtrace_record__init_intel(struct evlist *evlist,
int *err)
{
struct perf_pmu *intel_pt_pmu;
struct perf_pmu *intel_bts_pmu;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
bool found_pt = false;
bool found_bts = false;
@@ -38,9 +29,9 @@ struct auxtrace_record *auxtrace_record__init_intel(struct perf_evlist *evlist,
intel_bts_pmu = perf_pmu__find(INTEL_BTS_PMU_NAME);
evlist__for_each_entry(evlist, evsel) {
- if (intel_pt_pmu && evsel->attr.type == intel_pt_pmu->type)
+ if (intel_pt_pmu && evsel->core.attr.type == intel_pt_pmu->type)
found_pt = true;
- if (intel_bts_pmu && evsel->attr.type == intel_bts_pmu->type)
+ if (intel_bts_pmu && evsel->core.attr.type == intel_bts_pmu->type)
found_bts = true;
}
@@ -59,7 +50,7 @@ struct auxtrace_record *auxtrace_record__init_intel(struct perf_evlist *evlist,
return NULL;
}
-struct auxtrace_record *auxtrace_record__init(struct perf_evlist *evlist,
+struct auxtrace_record *auxtrace_record__init(struct evlist *evlist,
int *err)
{
char buffer[64];
diff --git a/tools/perf/arch/x86/util/dwarf-regs.c b/tools/perf/arch/x86/util/dwarf-regs.c
index 1f86ee8fb831..530934805710 100644
--- a/tools/perf/arch/x86/util/dwarf-regs.c
+++ b/tools/perf/arch/x86/util/dwarf-regs.c
@@ -1,23 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* dwarf-regs.c : Mapping of DWARF debug register numbers into register names.
* Extracted from probe-finder.c
*
* Written by Masami Hiramatsu <mhiramat@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
*/
#include <stddef.h>
diff --git a/tools/perf/arch/x86/util/event.c b/tools/perf/arch/x86/util/event.c
index 675a0213044d..a3a0b6884779 100644
--- a/tools/perf/arch/x86/util/event.c
+++ b/tools/perf/arch/x86/util/event.c
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/types.h>
#include <linux/string.h>
+#include <linux/zalloc.h>
#include "../../util/machine.h"
#include "../../util/tool.h"
#include "../../util/map.h"
-#include "../../util/util.h"
#include "../../util/debug.h"
#if defined(__x86_64__)
diff --git a/tools/perf/arch/x86/util/header.c b/tools/perf/arch/x86/util/header.c
index af9a9f2600be..662ecf84a421 100644
--- a/tools/perf/arch/x86/util/header.c
+++ b/tools/perf/arch/x86/util/header.c
@@ -6,6 +6,7 @@
#include <string.h>
#include <regex.h>
+#include "../../util/debug.h"
#include "../../util/header.h"
static inline void
diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c
index 781df40b2966..d263430c045f 100644
--- a/tools/perf/arch/x86/util/intel-bts.c
+++ b/tools/perf/arch/x86/util/intel-bts.c
@@ -1,16 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* intel-bts.c: Intel Processor Trace support
* Copyright (c) 2013-2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#include <errno.h>
@@ -18,17 +9,20 @@
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/log2.h>
+#include <linux/zalloc.h>
#include "../../util/cpumap.h"
+#include "../../util/event.h"
#include "../../util/evsel.h"
#include "../../util/evlist.h"
#include "../../util/session.h"
-#include "../../util/util.h"
#include "../../util/pmu.h"
#include "../../util/debug.h"
+#include "../../util/record.h"
#include "../../util/tsc.h"
#include "../../util/auxtrace.h"
#include "../../util/intel-bts.h"
+#include "../../util/util.h"
#define KiB(x) ((x) * 1024)
#define MiB(x) ((x) * 1024 * 1024)
@@ -44,7 +38,7 @@ struct intel_bts_snapshot_ref {
struct intel_bts_recording {
struct auxtrace_record itr;
struct perf_pmu *intel_bts_pmu;
- struct perf_evlist *evlist;
+ struct evlist *evlist;
bool snapshot_mode;
size_t snapshot_size;
int snapshot_ref_cnt;
@@ -59,14 +53,14 @@ struct branch {
static size_t
intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
return INTEL_BTS_AUXTRACE_PRIV_SIZE;
}
static int intel_bts_info_fill(struct auxtrace_record *itr,
struct perf_session *session,
- struct auxtrace_info_event *auxtrace_info,
+ struct perf_record_auxtrace_info *auxtrace_info,
size_t priv_size)
{
struct intel_bts_recording *btsr =
@@ -108,27 +102,27 @@ static int intel_bts_info_fill(struct auxtrace_record *itr,
}
static int intel_bts_recording_options(struct auxtrace_record *itr,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
struct record_opts *opts)
{
struct intel_bts_recording *btsr =
container_of(itr, struct intel_bts_recording, itr);
struct perf_pmu *intel_bts_pmu = btsr->intel_bts_pmu;
- struct perf_evsel *evsel, *intel_bts_evsel = NULL;
- const struct cpu_map *cpus = evlist->cpus;
- bool privileged = geteuid() == 0 || perf_event_paranoid() < 0;
+ struct evsel *evsel, *intel_bts_evsel = NULL;
+ const struct perf_cpu_map *cpus = evlist->core.cpus;
+ bool privileged = perf_event_paranoid_check(-1);
btsr->evlist = evlist;
btsr->snapshot_mode = opts->auxtrace_snapshot_mode;
evlist__for_each_entry(evlist, evsel) {
- if (evsel->attr.type == intel_bts_pmu->type) {
+ if (evsel->core.attr.type == intel_bts_pmu->type) {
if (intel_bts_evsel) {
pr_err("There may be only one " INTEL_BTS_PMU_NAME " event\n");
return -EINVAL;
}
- evsel->attr.freq = 0;
- evsel->attr.sample_period = 1;
+ evsel->core.attr.freq = 0;
+ evsel->core.attr.sample_period = 1;
intel_bts_evsel = evsel;
opts->full_auxtrace = true;
}
@@ -142,7 +136,7 @@ static int intel_bts_recording_options(struct auxtrace_record *itr,
if (!opts->full_auxtrace)
return 0;
- if (opts->full_auxtrace && !cpu_map__empty(cpus)) {
+ if (opts->full_auxtrace && !perf_cpu_map__empty(cpus)) {
pr_err(INTEL_BTS_PMU_NAME " does not support per-cpu recording\n");
return -EINVAL;
}
@@ -223,13 +217,13 @@ static int intel_bts_recording_options(struct auxtrace_record *itr,
* In the case of per-cpu mmaps, we need the CPU on the
* AUX event.
*/
- if (!cpu_map__empty(cpus))
+ if (!perf_cpu_map__empty(cpus))
perf_evsel__set_sample_bit(intel_bts_evsel, CPU);
}
/* Add dummy event to keep tracking */
if (opts->full_auxtrace) {
- struct perf_evsel *tracking_evsel;
+ struct evsel *tracking_evsel;
int err;
err = parse_events(evlist, "dummy:u", NULL);
@@ -240,8 +234,8 @@ static int intel_bts_recording_options(struct auxtrace_record *itr,
perf_evlist__set_tracking_event(evlist, tracking_evsel);
- tracking_evsel->attr.freq = 0;
- tracking_evsel->attr.sample_period = 1;
+ tracking_evsel->core.attr.freq = 0;
+ tracking_evsel->core.attr.sample_period = 1;
}
return 0;
@@ -322,11 +316,11 @@ static int intel_bts_snapshot_start(struct auxtrace_record *itr)
{
struct intel_bts_recording *btsr =
container_of(itr, struct intel_bts_recording, itr);
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(btsr->evlist, evsel) {
- if (evsel->attr.type == btsr->intel_bts_pmu->type)
- return perf_evsel__disable(evsel);
+ if (evsel->core.attr.type == btsr->intel_bts_pmu->type)
+ return evsel__disable(evsel);
}
return -EINVAL;
}
@@ -335,11 +329,11 @@ static int intel_bts_snapshot_finish(struct auxtrace_record *itr)
{
struct intel_bts_recording *btsr =
container_of(itr, struct intel_bts_recording, itr);
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(btsr->evlist, evsel) {
- if (evsel->attr.type == btsr->intel_bts_pmu->type)
- return perf_evsel__enable(evsel);
+ if (evsel->core.attr.type == btsr->intel_bts_pmu->type)
+ return evsel__enable(evsel);
}
return -EINVAL;
}
@@ -417,10 +411,10 @@ static int intel_bts_read_finish(struct auxtrace_record *itr, int idx)
{
struct intel_bts_recording *btsr =
container_of(itr, struct intel_bts_recording, itr);
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(btsr->evlist, evsel) {
- if (evsel->attr.type == btsr->intel_bts_pmu->type)
+ if (evsel->core.attr.type == btsr->intel_bts_pmu->type)
return perf_evlist__enable_event_idx(btsr->evlist,
evsel, idx);
}
diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c
index ba8ecaf52200..cb7cf16af79c 100644
--- a/tools/perf/arch/x86/util/intel-pt.c
+++ b/tools/perf/arch/x86/util/intel-pt.c
@@ -1,16 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* intel_pt.c: Intel Processor Trace support
* Copyright (c) 2013-2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#include <errno.h>
@@ -19,9 +10,9 @@
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/log2.h>
+#include <linux/zalloc.h>
#include <cpuid.h>
-#include "../../perf.h"
#include "../../util/session.h"
#include "../../util/event.h"
#include "../../util/evlist.h"
@@ -32,7 +23,10 @@
#include "../../util/pmu.h"
#include "../../util/debug.h"
#include "../../util/auxtrace.h"
+#include "../../util/record.h"
+#include "../../util/target.h"
#include "../../util/tsc.h"
+#include "../../util/util.h"
#include "../../util/intel-pt.h"
#define KiB(x) ((x) * 1024)
@@ -52,7 +46,7 @@ struct intel_pt_recording {
struct auxtrace_record itr;
struct perf_pmu *intel_pt_pmu;
int have_sched_switch;
- struct perf_evlist *evlist;
+ struct evlist *evlist;
bool snapshot_mode;
bool snapshot_init_done;
size_t snapshot_size;
@@ -118,9 +112,9 @@ static u64 intel_pt_masked_bits(u64 mask, u64 bits)
}
static int intel_pt_read_config(struct perf_pmu *intel_pt_pmu, const char *str,
- struct perf_evlist *evlist, u64 *res)
+ struct evlist *evlist, u64 *res)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
u64 mask;
*res = 0;
@@ -130,8 +124,8 @@ static int intel_pt_read_config(struct perf_pmu *intel_pt_pmu, const char *str,
return -EINVAL;
evlist__for_each_entry(evlist, evsel) {
- if (evsel->attr.type == intel_pt_pmu->type) {
- *res = intel_pt_masked_bits(mask, evsel->attr.config);
+ if (evsel->core.attr.type == intel_pt_pmu->type) {
+ *res = intel_pt_masked_bits(mask, evsel->core.attr.config);
return 0;
}
}
@@ -140,7 +134,7 @@ static int intel_pt_read_config(struct perf_pmu *intel_pt_pmu, const char *str,
}
static size_t intel_pt_psb_period(struct perf_pmu *intel_pt_pmu,
- struct perf_evlist *evlist)
+ struct evlist *evlist)
{
u64 val;
int err, topa_multiple_entries;
@@ -276,13 +270,13 @@ intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu)
return attr;
}
-static const char *intel_pt_find_filter(struct perf_evlist *evlist,
+static const char *intel_pt_find_filter(struct evlist *evlist,
struct perf_pmu *intel_pt_pmu)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) {
- if (evsel->attr.type == intel_pt_pmu->type)
+ if (evsel->core.attr.type == intel_pt_pmu->type)
return evsel->filter;
}
@@ -297,7 +291,7 @@ static size_t intel_pt_filter_bytes(const char *filter)
}
static size_t
-intel_pt_info_priv_size(struct auxtrace_record *itr, struct perf_evlist *evlist)
+intel_pt_info_priv_size(struct auxtrace_record *itr, struct evlist *evlist)
{
struct intel_pt_recording *ptr =
container_of(itr, struct intel_pt_recording, itr);
@@ -320,7 +314,7 @@ static void intel_pt_tsc_ctc_ratio(u32 *n, u32 *d)
static int intel_pt_info_fill(struct auxtrace_record *itr,
struct perf_session *session,
- struct auxtrace_info_event *auxtrace_info,
+ struct perf_record_auxtrace_info *auxtrace_info,
size_t priv_size)
{
struct intel_pt_recording *ptr =
@@ -334,7 +328,7 @@ static int intel_pt_info_fill(struct auxtrace_record *itr,
unsigned long max_non_turbo_ratio;
size_t filter_str_len;
const char *filter;
- u64 *info;
+ __u64 *info;
int err;
if (priv_size != ptr->priv_size)
@@ -373,7 +367,7 @@ static int intel_pt_info_fill(struct auxtrace_record *itr,
ui__warning("Intel Processor Trace: TSC not available\n");
}
- per_cpu_mmaps = !cpu_map__empty(session->evlist->cpus);
+ per_cpu_mmaps = !perf_cpu_map__empty(session->evlist->core.cpus);
auxtrace_info->type = PERF_AUXTRACE_INTEL_PT;
auxtrace_info->priv[INTEL_PT_PMU_TYPE] = intel_pt_pmu->type;
@@ -406,10 +400,10 @@ static int intel_pt_info_fill(struct auxtrace_record *itr,
return 0;
}
-static int intel_pt_track_switches(struct perf_evlist *evlist)
+static int intel_pt_track_switches(struct evlist *evlist)
{
const char *sched_switch = "sched:sched_switch";
- struct perf_evsel *evsel;
+ struct evsel *evsel;
int err;
if (!perf_evlist__can_select_event(evlist, sched_switch))
@@ -521,7 +515,7 @@ out_err:
}
static int intel_pt_validate_config(struct perf_pmu *intel_pt_pmu,
- struct perf_evsel *evsel)
+ struct evsel *evsel)
{
int err;
char c;
@@ -534,39 +528,59 @@ static int intel_pt_validate_config(struct perf_pmu *intel_pt_pmu,
* sets pt=0, which avoids senseless kernel errors.
*/
if (perf_pmu__scan_file(intel_pt_pmu, "format/pt", "%c", &c) == 1 &&
- !(evsel->attr.config & 1)) {
+ !(evsel->core.attr.config & 1)) {
pr_warning("pt=0 doesn't make sense, forcing pt=1\n");
- evsel->attr.config |= 1;
+ evsel->core.attr.config |= 1;
}
err = intel_pt_val_config_term(intel_pt_pmu, "caps/cycle_thresholds",
"cyc_thresh", "caps/psb_cyc",
- evsel->attr.config);
+ evsel->core.attr.config);
if (err)
return err;
err = intel_pt_val_config_term(intel_pt_pmu, "caps/mtc_periods",
"mtc_period", "caps/mtc",
- evsel->attr.config);
+ evsel->core.attr.config);
if (err)
return err;
return intel_pt_val_config_term(intel_pt_pmu, "caps/psb_periods",
"psb_period", "caps/psb_cyc",
- evsel->attr.config);
+ evsel->core.attr.config);
+}
+
+/*
+ * Currently, there is not enough information to disambiguate different PEBS
+ * events, so only allow one.
+ */
+static bool intel_pt_too_many_aux_output(struct evlist *evlist)
+{
+ struct evsel *evsel;
+ int aux_output_cnt = 0;
+
+ evlist__for_each_entry(evlist, evsel)
+ aux_output_cnt += !!evsel->core.attr.aux_output;
+
+ if (aux_output_cnt > 1) {
+ pr_err(INTEL_PT_PMU_NAME " supports at most one event with aux-output\n");
+ return true;
+ }
+
+ return false;
}
static int intel_pt_recording_options(struct auxtrace_record *itr,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
struct record_opts *opts)
{
struct intel_pt_recording *ptr =
container_of(itr, struct intel_pt_recording, itr);
struct perf_pmu *intel_pt_pmu = ptr->intel_pt_pmu;
bool have_timing_info, need_immediate = false;
- struct perf_evsel *evsel, *intel_pt_evsel = NULL;
- const struct cpu_map *cpus = evlist->cpus;
- bool privileged = geteuid() == 0 || perf_event_paranoid() < 0;
+ struct evsel *evsel, *intel_pt_evsel = NULL;
+ const struct perf_cpu_map *cpus = evlist->core.cpus;
+ bool privileged = perf_event_paranoid_check(-1);
u64 tsc_bit;
int err;
@@ -574,13 +588,13 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
evlist__for_each_entry(evlist, evsel) {
- if (evsel->attr.type == intel_pt_pmu->type) {
+ if (evsel->core.attr.type == intel_pt_pmu->type) {
if (intel_pt_evsel) {
pr_err("There may be only one " INTEL_PT_PMU_NAME " event\n");
return -EINVAL;
}
- evsel->attr.freq = 0;
- evsel->attr.sample_period = 1;
+ evsel->core.attr.freq = 0;
+ evsel->core.attr.sample_period = 1;
intel_pt_evsel = evsel;
opts->full_auxtrace = true;
}
@@ -596,6 +610,9 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
return -EINVAL;
}
+ if (intel_pt_too_many_aux_output(evlist))
+ return -EINVAL;
+
if (!opts->full_auxtrace)
return 0;
@@ -678,7 +695,7 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
intel_pt_parse_terms(&intel_pt_pmu->format, "tsc", &tsc_bit);
- if (opts->full_auxtrace && (intel_pt_evsel->attr.config & tsc_bit))
+ if (opts->full_auxtrace && (intel_pt_evsel->core.attr.config & tsc_bit))
have_timing_info = true;
else
have_timing_info = false;
@@ -687,13 +704,13 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
* Per-cpu recording needs sched_switch events to distinguish different
* threads.
*/
- if (have_timing_info && !cpu_map__empty(cpus)) {
+ if (have_timing_info && !perf_cpu_map__empty(cpus)) {
if (perf_can_record_switch_events()) {
bool cpu_wide = !target__none(&opts->target) &&
!target__has_task(&opts->target);
if (!cpu_wide && perf_can_record_cpu_wide()) {
- struct perf_evsel *switch_evsel;
+ struct evsel *switch_evsel;
err = parse_events(evlist, "dummy:u", NULL);
if (err)
@@ -701,9 +718,9 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
switch_evsel = perf_evlist__last(evlist);
- switch_evsel->attr.freq = 0;
- switch_evsel->attr.sample_period = 1;
- switch_evsel->attr.context_switch = 1;
+ switch_evsel->core.attr.freq = 0;
+ switch_evsel->core.attr.sample_period = 1;
+ switch_evsel->core.attr.context_switch = 1;
switch_evsel->system_wide = true;
switch_evsel->no_aux_samples = true;
@@ -745,13 +762,13 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
* In the case of per-cpu mmaps, we need the CPU on the
* AUX event.
*/
- if (!cpu_map__empty(cpus))
+ if (!perf_cpu_map__empty(cpus))
perf_evsel__set_sample_bit(intel_pt_evsel, CPU);
}
/* Add dummy event to keep tracking */
if (opts->full_auxtrace) {
- struct perf_evsel *tracking_evsel;
+ struct evsel *tracking_evsel;
err = parse_events(evlist, "dummy:u", NULL);
if (err)
@@ -761,15 +778,15 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
perf_evlist__set_tracking_event(evlist, tracking_evsel);
- tracking_evsel->attr.freq = 0;
- tracking_evsel->attr.sample_period = 1;
+ tracking_evsel->core.attr.freq = 0;
+ tracking_evsel->core.attr.sample_period = 1;
tracking_evsel->no_aux_samples = true;
if (need_immediate)
tracking_evsel->immediate = true;
/* In per-cpu case, always need the time of mmap events etc */
- if (!cpu_map__empty(cpus)) {
+ if (!perf_cpu_map__empty(cpus)) {
perf_evsel__set_sample_bit(tracking_evsel, TIME);
/* And the CPU for switch events */
perf_evsel__set_sample_bit(tracking_evsel, CPU);
@@ -781,7 +798,7 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
* Warn the user when we do not have enough information to decode i.e.
* per-cpu with no sched_switch (except workload-only).
*/
- if (!ptr->have_sched_switch && !cpu_map__empty(cpus) &&
+ if (!ptr->have_sched_switch && !perf_cpu_map__empty(cpus) &&
!target__none(&opts->target))
ui__warning("Intel Processor Trace decoding will not be possible except for kernel tracing!\n");
@@ -792,11 +809,11 @@ static int intel_pt_snapshot_start(struct auxtrace_record *itr)
{
struct intel_pt_recording *ptr =
container_of(itr, struct intel_pt_recording, itr);
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(ptr->evlist, evsel) {
- if (evsel->attr.type == ptr->intel_pt_pmu->type)
- return perf_evsel__disable(evsel);
+ if (evsel->core.attr.type == ptr->intel_pt_pmu->type)
+ return evsel__disable(evsel);
}
return -EINVAL;
}
@@ -805,11 +822,11 @@ static int intel_pt_snapshot_finish(struct auxtrace_record *itr)
{
struct intel_pt_recording *ptr =
container_of(itr, struct intel_pt_recording, itr);
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(ptr->evlist, evsel) {
- if (evsel->attr.type == ptr->intel_pt_pmu->type)
- return perf_evsel__enable(evsel);
+ if (evsel->core.attr.type == ptr->intel_pt_pmu->type)
+ return evsel__enable(evsel);
}
return -EINVAL;
}
@@ -1078,10 +1095,10 @@ static int intel_pt_read_finish(struct auxtrace_record *itr, int idx)
{
struct intel_pt_recording *ptr =
container_of(itr, struct intel_pt_recording, itr);
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(ptr->evlist, evsel) {
- if (evsel->attr.type == ptr->intel_pt_pmu->type)
+ if (evsel->core.attr.type == ptr->intel_pt_pmu->type)
return perf_evlist__enable_event_idx(ptr->evlist, evsel,
idx);
}
diff --git a/tools/perf/arch/x86/util/kvm-stat.c b/tools/perf/arch/x86/util/kvm-stat.c
index 865a9762f22e..c0775c39227f 100644
--- a/tools/perf/arch/x86/util/kvm-stat.c
+++ b/tools/perf/arch/x86/util/kvm-stat.c
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
-#include "../../util/kvm-stat.h"
-#include "../../util/evsel.h"
+#include <string.h>
+#include "../../../util/kvm-stat.h"
+#include "../../../util/evsel.h"
#include <asm/svm.h>
#include <asm/vmx.h>
#include <asm/kvm.h>
@@ -27,7 +28,7 @@ const char *kvm_exit_trace = "kvm:kvm_exit";
* the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry
* the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...).
*/
-static void mmio_event_get_key(struct perf_evsel *evsel, struct perf_sample *sample,
+static void mmio_event_get_key(struct evsel *evsel, struct perf_sample *sample,
struct event_key *key)
{
key->key = perf_evsel__intval(evsel, sample, "gpa");
@@ -38,7 +39,7 @@ static void mmio_event_get_key(struct perf_evsel *evsel, struct perf_sample *sam
#define KVM_TRACE_MMIO_READ 1
#define KVM_TRACE_MMIO_WRITE 2
-static bool mmio_event_begin(struct perf_evsel *evsel,
+static bool mmio_event_begin(struct evsel *evsel,
struct perf_sample *sample, struct event_key *key)
{
/* MMIO read begin event in kernel. */
@@ -55,7 +56,7 @@ static bool mmio_event_begin(struct perf_evsel *evsel,
return false;
}
-static bool mmio_event_end(struct perf_evsel *evsel, struct perf_sample *sample,
+static bool mmio_event_end(struct evsel *evsel, struct perf_sample *sample,
struct event_key *key)
{
/* MMIO write end event in kernel. */
@@ -89,7 +90,7 @@ static struct kvm_events_ops mmio_events = {
};
/* The time of emulation pio access is from kvm_pio to kvm_entry. */
-static void ioport_event_get_key(struct perf_evsel *evsel,
+static void ioport_event_get_key(struct evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
@@ -97,7 +98,7 @@ static void ioport_event_get_key(struct perf_evsel *evsel,
key->info = perf_evsel__intval(evsel, sample, "rw");
}
-static bool ioport_event_begin(struct perf_evsel *evsel,
+static bool ioport_event_begin(struct evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
@@ -109,7 +110,7 @@ static bool ioport_event_begin(struct perf_evsel *evsel,
return false;
}
-static bool ioport_event_end(struct perf_evsel *evsel,
+static bool ioport_event_end(struct evsel *evsel,
struct perf_sample *sample __maybe_unused,
struct event_key *key __maybe_unused)
{
diff --git a/tools/perf/arch/x86/util/machine.c b/tools/perf/arch/x86/util/machine.c
index 4520ac53caa9..1e9ec783b9a1 100644
--- a/tools/perf/arch/x86/util/machine.c
+++ b/tools/perf/arch/x86/util/machine.c
@@ -3,10 +3,11 @@
#include <linux/string.h>
#include <stdlib.h>
+#include "../../util/util.h"
#include "../../util/machine.h"
#include "../../util/map.h"
#include "../../util/symbol.h"
-#include "../../util/sane_ctype.h"
+#include <linux/ctype.h>
#include <symbol/kallsyms.h>
diff --git a/tools/perf/arch/x86/util/perf_regs.c b/tools/perf/arch/x86/util/perf_regs.c
index 7886ca5263e3..c218b83e063b 100644
--- a/tools/perf/arch/x86/util/perf_regs.c
+++ b/tools/perf/arch/x86/util/perf_regs.c
@@ -2,11 +2,13 @@
#include <errno.h>
#include <string.h>
#include <regex.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
-#include "../../perf.h"
-#include "../../util/util.h"
+#include "../../perf-sys.h"
#include "../../util/perf_regs.h"
#include "../../util/debug.h"
+#include "../../util/event.h"
const struct sample_reg sample_reg_masks[] = {
SMPL_REG(AX, PERF_REG_X86_AX),
@@ -277,7 +279,7 @@ uint64_t arch__intr_reg_mask(void)
.type = PERF_TYPE_HARDWARE,
.config = PERF_COUNT_HW_CPU_CYCLES,
.sample_type = PERF_SAMPLE_REGS_INTR,
- .sample_regs_intr = PERF_XMM_REGS_MASK,
+ .sample_regs_intr = PERF_REG_EXTENDED_MASK,
.precise_ip = 1,
.disabled = 1,
.exclude_kernel = 1,
@@ -293,7 +295,7 @@ uint64_t arch__intr_reg_mask(void)
fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
if (fd != -1) {
close(fd);
- return (PERF_XMM_REGS_MASK | PERF_REGS_MASK);
+ return (PERF_REG_EXTENDED_MASK | PERF_REGS_MASK);
}
return PERF_REGS_MASK;
diff --git a/tools/perf/arch/x86/util/tsc.c b/tools/perf/arch/x86/util/tsc.c
index 950539f9a4f7..c5197a15119b 100644
--- a/tools/perf/arch/x86/util/tsc.c
+++ b/tools/perf/arch/x86/util/tsc.c
@@ -5,10 +5,10 @@
#include <linux/stddef.h>
#include <linux/perf_event.h>
-#include "../../perf.h"
#include <linux/types.h>
-#include "../../util/debug.h"
-#include "../../util/tsc.h"
+#include <asm/barrier.h>
+#include "../../../util/debug.h"
+#include "../../../util/tsc.h"
int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
struct perf_tsc_conversion *tc)
@@ -57,7 +57,7 @@ int perf_event__synth_time_conv(const struct perf_event_mmap_page *pc,
.time_conv = {
.header = {
.type = PERF_RECORD_TIME_CONV,
- .size = sizeof(struct time_conv_event),
+ .size = sizeof(struct perf_record_time_conv),
},
},
};
diff --git a/tools/perf/arch/xtensa/Makefile b/tools/perf/arch/xtensa/Makefile
index 7fbca175099e..88c08eed9c7b 100644
--- a/tools/perf/arch/xtensa/Makefile
+++ b/tools/perf/arch/xtensa/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
ifndef NO_DWARF
PERF_HAVE_DWARF_REGS := 1
endif
diff --git a/tools/perf/arch/xtensa/util/dwarf-regs.c b/tools/perf/arch/xtensa/util/dwarf-regs.c
index 4dba76bfb4ce..12f5457300f5 100644
--- a/tools/perf/arch/xtensa/util/dwarf-regs.c
+++ b/tools/perf/arch/xtensa/util/dwarf-regs.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Mapping of DWARF debug register numbers into register names.
*
* Copyright (c) 2015 Cadence Design Systems Inc.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include <stddef.h>
diff --git a/tools/perf/bench/epoll-ctl.c b/tools/perf/bench/epoll-ctl.c
index 2af067859966..d1caa4a0a12a 100644
--- a/tools/perf/bench/epoll-ctl.c
+++ b/tools/perf/bench/epoll-ctl.c
@@ -14,12 +14,14 @@
#include <inttypes.h>
#include <signal.h>
#include <stdlib.h>
+#include <unistd.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
+#include <perf/cpumap.h>
#include "../util/stat.h"
#include <subcmd/parse-options.h>
@@ -219,7 +221,7 @@ static void init_fdmaps(struct worker *w, int pct)
}
}
-static int do_threads(struct worker *worker, struct cpu_map *cpu)
+static int do_threads(struct worker *worker, struct perf_cpu_map *cpu)
{
pthread_attr_t thread_attr, *attrp = NULL;
cpu_set_t cpuset;
@@ -301,7 +303,7 @@ int bench_epoll_ctl(int argc, const char **argv)
int j, ret = 0;
struct sigaction act;
struct worker *worker = NULL;
- struct cpu_map *cpu;
+ struct perf_cpu_map *cpu;
struct rlimit rl, prevrl;
unsigned int i;
@@ -315,7 +317,7 @@ int bench_epoll_ctl(int argc, const char **argv)
act.sa_sigaction = toggle_done;
sigaction(SIGINT, &act, NULL);
- cpu = cpu_map__new(NULL);
+ cpu = perf_cpu_map__new(NULL);
if (!cpu)
goto errmem;
diff --git a/tools/perf/bench/epoll-wait.c b/tools/perf/bench/epoll-wait.c
index fe85448abd45..f6b4472847d2 100644
--- a/tools/perf/bench/epoll-wait.c
+++ b/tools/perf/bench/epoll-wait.c
@@ -63,6 +63,7 @@
/* For the CLR_() macros */
#include <string.h>
#include <pthread.h>
+#include <unistd.h>
#include <errno.h>
#include <inttypes.h>
@@ -75,6 +76,7 @@
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/types.h>
+#include <perf/cpumap.h>
#include "../util/stat.h"
#include <subcmd/parse-options.h>
@@ -288,7 +290,7 @@ static void print_summary(void)
(int) runtime.tv_sec);
}
-static int do_threads(struct worker *worker, struct cpu_map *cpu)
+static int do_threads(struct worker *worker, struct perf_cpu_map *cpu)
{
pthread_attr_t thread_attr, *attrp = NULL;
cpu_set_t cpuset;
@@ -415,7 +417,7 @@ int bench_epoll_wait(int argc, const char **argv)
struct sigaction act;
unsigned int i;
struct worker *worker = NULL;
- struct cpu_map *cpu;
+ struct perf_cpu_map *cpu;
pthread_t wthread;
struct rlimit rl, prevrl;
@@ -429,7 +431,7 @@ int bench_epoll_wait(int argc, const char **argv)
act.sa_sigaction = toggle_done;
sigaction(SIGINT, &act, NULL);
- cpu = cpu_map__new(NULL);
+ cpu = perf_cpu_map__new(NULL);
if (!cpu)
goto errmem;
diff --git a/tools/perf/bench/futex-hash.c b/tools/perf/bench/futex-hash.c
index 9aa3a674829b..80e138904c66 100644
--- a/tools/perf/bench/futex-hash.c
+++ b/tools/perf/bench/futex-hash.c
@@ -18,7 +18,9 @@
#include <stdlib.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
+#include <linux/zalloc.h>
#include <sys/time.h>
+#include <perf/cpumap.h>
#include "../util/stat.h"
#include <subcmd/parse-options.h>
@@ -123,7 +125,7 @@ int bench_futex_hash(int argc, const char **argv)
unsigned int i;
pthread_attr_t thread_attr;
struct worker *worker = NULL;
- struct cpu_map *cpu;
+ struct perf_cpu_map *cpu;
argc = parse_options(argc, argv, options, bench_futex_hash_usage, 0);
if (argc) {
@@ -131,7 +133,7 @@ int bench_futex_hash(int argc, const char **argv)
exit(EXIT_FAILURE);
}
- cpu = cpu_map__new(NULL);
+ cpu = perf_cpu_map__new(NULL);
if (!cpu)
goto errmem;
@@ -214,7 +216,7 @@ int bench_futex_hash(int argc, const char **argv)
&worker[i].futex[nfutexes-1], t);
}
- free(worker[i].futex);
+ zfree(&worker[i].futex);
}
print_summary();
diff --git a/tools/perf/bench/futex-lock-pi.c b/tools/perf/bench/futex-lock-pi.c
index 8e9c4753e304..c5d6d0abbaa9 100644
--- a/tools/perf/bench/futex-lock-pi.c
+++ b/tools/perf/bench/futex-lock-pi.c
@@ -12,7 +12,9 @@
#include <subcmd/parse-options.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
+#include <linux/zalloc.h>
#include <errno.h>
+#include <perf/cpumap.h>
#include "bench.h"
#include "futex.h"
#include "cpumap.h"
@@ -115,7 +117,7 @@ static void *workerfn(void *arg)
}
static void create_threads(struct worker *w, pthread_attr_t thread_attr,
- struct cpu_map *cpu)
+ struct perf_cpu_map *cpu)
{
cpu_set_t cpuset;
unsigned int i;
@@ -149,13 +151,13 @@ int bench_futex_lock_pi(int argc, const char **argv)
unsigned int i;
struct sigaction act;
pthread_attr_t thread_attr;
- struct cpu_map *cpu;
+ struct perf_cpu_map *cpu;
argc = parse_options(argc, argv, options, bench_futex_lock_pi_usage, 0);
if (argc)
goto err;
- cpu = cpu_map__new(NULL);
+ cpu = perf_cpu_map__new(NULL);
if (!cpu)
err(EXIT_FAILURE, "calloc");
@@ -217,7 +219,7 @@ int bench_futex_lock_pi(int argc, const char **argv)
worker[i].tid, worker[i].futex, t);
if (multi)
- free(worker[i].futex);
+ zfree(&worker[i].futex);
}
print_summary();
diff --git a/tools/perf/bench/futex-requeue.c b/tools/perf/bench/futex-requeue.c
index fc692efa0c05..75d3418c1a88 100644
--- a/tools/perf/bench/futex-requeue.c
+++ b/tools/perf/bench/futex-requeue.c
@@ -20,6 +20,7 @@
#include <linux/kernel.h>
#include <linux/time64.h>
#include <errno.h>
+#include <perf/cpumap.h>
#include "bench.h"
#include "futex.h"
#include "cpumap.h"
@@ -84,7 +85,7 @@ static void *workerfn(void *arg __maybe_unused)
}
static void block_threads(pthread_t *w,
- pthread_attr_t thread_attr, struct cpu_map *cpu)
+ pthread_attr_t thread_attr, struct perf_cpu_map *cpu)
{
cpu_set_t cpuset;
unsigned int i;
@@ -117,13 +118,13 @@ int bench_futex_requeue(int argc, const char **argv)
unsigned int i, j;
struct sigaction act;
pthread_attr_t thread_attr;
- struct cpu_map *cpu;
+ struct perf_cpu_map *cpu;
argc = parse_options(argc, argv, options, bench_futex_requeue_usage, 0);
if (argc)
goto err;
- cpu = cpu_map__new(NULL);
+ cpu = perf_cpu_map__new(NULL);
if (!cpu)
err(EXIT_FAILURE, "cpu_map__new");
diff --git a/tools/perf/bench/futex-wake-parallel.c b/tools/perf/bench/futex-wake-parallel.c
index 69d8fdc87315..163fe16c275a 100644
--- a/tools/perf/bench/futex-wake-parallel.c
+++ b/tools/perf/bench/futex-wake-parallel.c
@@ -138,7 +138,7 @@ static void *blocked_workerfn(void *arg __maybe_unused)
}
static void block_threads(pthread_t *w, pthread_attr_t thread_attr,
- struct cpu_map *cpu)
+ struct perf_cpu_map *cpu)
{
cpu_set_t cpuset;
unsigned int i;
@@ -224,7 +224,7 @@ int bench_futex_wake_parallel(int argc, const char **argv)
struct sigaction act;
pthread_attr_t thread_attr;
struct thread_data *waking_worker;
- struct cpu_map *cpu;
+ struct perf_cpu_map *cpu;
argc = parse_options(argc, argv, options,
bench_futex_wake_parallel_usage, 0);
@@ -237,7 +237,7 @@ int bench_futex_wake_parallel(int argc, const char **argv)
act.sa_sigaction = toggle_done;
sigaction(SIGINT, &act, NULL);
- cpu = cpu_map__new(NULL);
+ cpu = perf_cpu_map__new(NULL);
if (!cpu)
err(EXIT_FAILURE, "calloc");
diff --git a/tools/perf/bench/futex-wake.c b/tools/perf/bench/futex-wake.c
index e8181ad7d088..77dcdc13618a 100644
--- a/tools/perf/bench/futex-wake.c
+++ b/tools/perf/bench/futex-wake.c
@@ -20,6 +20,7 @@
#include <linux/kernel.h>
#include <linux/time64.h>
#include <errno.h>
+#include <perf/cpumap.h>
#include "bench.h"
#include "futex.h"
#include "cpumap.h"
@@ -90,7 +91,7 @@ static void print_summary(void)
}
static void block_threads(pthread_t *w,
- pthread_attr_t thread_attr, struct cpu_map *cpu)
+ pthread_attr_t thread_attr, struct perf_cpu_map *cpu)
{
cpu_set_t cpuset;
unsigned int i;
@@ -123,7 +124,7 @@ int bench_futex_wake(int argc, const char **argv)
unsigned int i, j;
struct sigaction act;
pthread_attr_t thread_attr;
- struct cpu_map *cpu;
+ struct perf_cpu_map *cpu;
argc = parse_options(argc, argv, options, bench_futex_wake_usage, 0);
if (argc) {
@@ -131,7 +132,7 @@ int bench_futex_wake(int argc, const char **argv)
exit(EXIT_FAILURE);
}
- cpu = cpu_map__new(NULL);
+ cpu = perf_cpu_map__new(NULL);
if (!cpu)
err(EXIT_FAILURE, "calloc");
diff --git a/tools/perf/bench/mem-functions.c b/tools/perf/bench/mem-functions.c
index 0251dd348124..9235b76501be 100644
--- a/tools/perf/bench/mem-functions.c
+++ b/tools/perf/bench/mem-functions.c
@@ -8,8 +8,7 @@
*/
#include "debug.h"
-#include "../perf.h"
-#include "../util/util.h"
+#include "../perf-sys.h"
#include <subcmd/parse-options.h>
#include "../util/header.h"
#include "../util/cloexec.h"
@@ -21,9 +20,11 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#include <sys/time.h>
#include <errno.h>
#include <linux/time64.h>
+#include <linux/zalloc.h>
#define K 1024
diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c
index a7784554a80d..62b8ef4bcb1f 100644
--- a/tools/perf/bench/numa.c
+++ b/tools/perf/bench/numa.c
@@ -9,9 +9,7 @@
/* For the CLR_() macros */
#include <pthread.h>
-#include "../perf.h"
#include "../builtin.h"
-#include "../util/util.h"
#include <subcmd/parse-options.h>
#include "../util/cloexec.h"
@@ -35,6 +33,7 @@
#include <linux/kernel.h>
#include <linux/time64.h>
#include <linux/numa.h>
+#include <linux/zalloc.h>
#include <numa.h>
#include <numaif.h>
@@ -379,8 +378,10 @@ static u8 *alloc_data(ssize_t bytes0, int map_flags,
/* Allocate and initialize all memory on CPU#0: */
if (init_cpu0) {
- orig_mask = bind_to_node(0);
- bind_to_memnode(0);
+ int node = numa_node_of_cpu(0);
+
+ orig_mask = bind_to_node(node);
+ bind_to_memnode(node);
}
bytes = bytes0 + HPSIZE;
diff --git a/tools/perf/bench/sched-messaging.c b/tools/perf/bench/sched-messaging.c
index f9d7641ae833..c63eb9a46346 100644
--- a/tools/perf/bench/sched-messaging.c
+++ b/tools/perf/bench/sched-messaging.c
@@ -10,7 +10,6 @@
*
*/
-#include "../perf.h"
#include "../util/util.h"
#include <subcmd/parse-options.h>
#include "../builtin.h"
diff --git a/tools/perf/bench/sched-pipe.c b/tools/perf/bench/sched-pipe.c
index 0591be008f2a..35b07f197d48 100644
--- a/tools/perf/bench/sched-pipe.c
+++ b/tools/perf/bench/sched-pipe.c
@@ -9,7 +9,6 @@
* http://people.redhat.com/mingo/cfs-scheduler/tools/pipe-test-1m.c
* Ported to perf by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
*/
-#include "../perf.h"
#include "../util/util.h"
#include <subcmd/parse-options.h>
#include "../builtin.h"
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 77deb3a40596..4e4d2e76232e 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -8,11 +8,11 @@
*/
#include "builtin.h"
-#include "util/util.h"
#include "util/color.h"
#include <linux/list.h>
#include "util/cache.h"
#include <linux/rbtree.h>
+#include <linux/zalloc.h>
#include "util/symbol.h"
#include "perf.h"
@@ -24,15 +24,17 @@
#include "util/event.h"
#include <subcmd/parse-options.h>
#include "util/parse-events.h"
-#include "util/thread.h"
#include "util/sort.h"
#include "util/hist.h"
+#include "util/dso.h"
#include "util/map.h"
#include "util/session.h"
#include "util/tool.h"
#include "util/data.h"
#include "arch/common.h"
#include "util/block-range.h"
+#include "util/map_symbol.h"
+#include "util/branch.h"
#include <dlfcn.h>
#include <errno.h>
@@ -156,7 +158,7 @@ static int hist_iter__branch_callback(struct hist_entry_iter *iter,
struct hist_entry *he = iter->he;
struct branch_info *bi;
struct perf_sample *sample = iter->sample;
- struct perf_evsel *evsel = iter->evsel;
+ struct evsel *evsel = iter->evsel;
int err;
bi = he->branch_info;
@@ -171,7 +173,7 @@ out:
return err;
}
-static int process_branch_callback(struct perf_evsel *evsel,
+static int process_branch_callback(struct evsel *evsel,
struct perf_sample *sample,
struct addr_location *al __maybe_unused,
struct perf_annotate *ann,
@@ -208,7 +210,7 @@ static bool has_annotation(struct perf_annotate *ann)
return ui__has_annotation() || ann->use_stdio2;
}
-static int perf_evsel__add_sample(struct perf_evsel *evsel,
+static int perf_evsel__add_sample(struct evsel *evsel,
struct perf_sample *sample,
struct addr_location *al,
struct perf_annotate *ann,
@@ -257,7 +259,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,
static int process_sample_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine)
{
struct perf_annotate *ann = container_of(tool, struct perf_annotate, tool);
@@ -293,7 +295,7 @@ static int process_feature_event(struct perf_session *session,
}
static int hist_entry__tty_annotate(struct hist_entry *he,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_annotate *ann)
{
if (!ann->use_stdio2)
@@ -303,7 +305,7 @@ static int hist_entry__tty_annotate(struct hist_entry *he,
}
static void hists__find_annotations(struct hists *hists,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_annotate *ann)
{
struct rb_node *nd = rb_first_cached(&hists->entries), *next;
@@ -333,7 +335,7 @@ find_next:
if (use_browser == 2) {
int ret;
int (*annotate)(struct hist_entry *he,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct hist_browser_timer *hbt);
annotate = dlsym(perf_gtk_handle,
@@ -387,7 +389,7 @@ static int __cmd_annotate(struct perf_annotate *ann)
{
int ret;
struct perf_session *session = ann->session;
- struct perf_evsel *pos;
+ struct evsel *pos;
u64 total_nr_samples;
if (ann->cpu_list) {
diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c
index 334c77ffc1d9..c06fe21c8613 100644
--- a/tools/perf/builtin-bench.c
+++ b/tools/perf/builtin-bench.c
@@ -16,8 +16,6 @@
* futex ... Futex performance
* epoll ... Event poll performance
*/
-#include "perf.h"
-#include "util/util.h"
#include <subcmd/parse-options.h>
#include "builtin.h"
#include "bench/bench.h"
@@ -26,6 +24,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
+#include <linux/zalloc.h>
typedef int (*bench_fn_t)(int argc, const char **argv);
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c
index 10457b10e568..1a69eb565dc0 100644
--- a/tools/perf/builtin-buildid-cache.c
+++ b/tools/perf/builtin-buildid-cache.c
@@ -14,18 +14,20 @@
#include <errno.h>
#include <unistd.h>
#include "builtin.h"
-#include "perf.h"
#include "namespaces.h"
-#include "util/cache.h"
#include "util/debug.h"
#include "util/header.h"
+#include <subcmd/pager.h>
#include <subcmd/parse-options.h>
#include "util/strlist.h"
#include "util/build-id.h"
#include "util/session.h"
+#include "util/dso.h"
#include "util/symbol.h"
#include "util/time-utils.h"
+#include "util/util.h"
#include "util/probe-file.h"
+#include <linux/string.h>
static int build_id_cache__kcore_buildid(const char *proc_dir, char *sbuildid)
{
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c
index f403e19488b5..5a0d8b378cb5 100644
--- a/tools/perf/builtin-buildid-list.c
+++ b/tools/perf/builtin-buildid-list.c
@@ -1,4 +1,3 @@
-// SPDX-License-Identifier: GPL-2.0
/*
* builtin-buildid-list.c
*
@@ -11,8 +10,9 @@
#include "builtin.h"
#include "perf.h"
#include "util/build-id.h"
-#include "util/cache.h"
#include "util/debug.h"
+#include "util/dso.h"
+#include <subcmd/pager.h>
#include <subcmd/parse-options.h>
#include "util/session.h"
#include "util/symbol.h"
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index 9e6cc868bdb4..b09b12e0976b 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -15,17 +15,20 @@
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <linux/stringify.h>
+#include <linux/zalloc.h>
#include <asm/bug.h>
#include <sys/param.h>
-#include "util.h"
#include "debug.h"
#include "builtin.h"
+#include <subcmd/pager.h>
#include <subcmd/parse-options.h>
+#include "map_symbol.h"
#include "mem-events.h"
#include "session.h"
#include "hist.h"
#include "sort.h"
#include "tool.h"
+#include "cacheline.h"
#include "data.h"
#include "event.h"
#include "evlist.h"
@@ -34,6 +37,9 @@
#include "thread.h"
#include "mem2node.h"
#include "symbol.h"
+#include "ui/ui.h"
+#include "ui/progress.h"
+#include "../perf.h"
struct c2c_hists {
struct hists hists;
@@ -248,7 +254,7 @@ static void compute_stats(struct c2c_hist_entry *c2c_he,
static int process_sample_event(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine)
{
struct c2c_hists *c2c_hists = &c2c.hists;
@@ -1106,7 +1112,7 @@ node_entry(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp,
break;
case 1:
{
- int num = bitmap_weight(c2c_he->cpuset, c2c.cpus_cnt);
+ int num = bitmap_weight(set, c2c.cpus_cnt);
struct c2c_stats *stats = &c2c_he->node_stats[node];
ret = scnprintf(hpp->buf, hpp->size, "%2d{%2d ", node, num);
@@ -2027,7 +2033,7 @@ static int setup_nodes(struct perf_session *session)
c2c.node_info = 2;
c2c.nodes_cnt = session->header.env.nr_numa_nodes;
- c2c.cpus_cnt = session->header.env.nr_cpus_online;
+ c2c.cpus_cnt = session->header.env.nr_cpus_avail;
n = session->header.env.numa_nodes;
if (!n)
@@ -2049,7 +2055,7 @@ static int setup_nodes(struct perf_session *session)
c2c.cpu2node = cpu2node;
for (node = 0; node < c2c.nodes_cnt; node++) {
- struct cpu_map *map = n[node].map;
+ struct perf_cpu_map *map = n[node].map;
unsigned long *set;
set = bitmap_alloc(c2c.cpus_cnt);
@@ -2059,7 +2065,7 @@ static int setup_nodes(struct perf_session *session)
nodes[node] = set;
/* empty node, skip */
- if (cpu_map__empty(map))
+ if (perf_cpu_map__empty(map))
continue;
for (cpu = 0; cpu < map->nr; cpu++) {
@@ -2236,8 +2242,8 @@ static void print_pareto(FILE *out)
static void print_c2c_info(FILE *out, struct perf_session *session)
{
- struct perf_evlist *evlist = session->evlist;
- struct perf_evsel *evsel;
+ struct evlist *evlist = session->evlist;
+ struct evsel *evsel;
bool first = true;
fprintf(out, "=================================================\n");
@@ -2567,7 +2573,7 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset)
return parse_callchain_report_opt(arg);
}
-static int setup_callchain(struct perf_evlist *evlist)
+static int setup_callchain(struct evlist *evlist)
{
u64 sample_type = perf_evlist__combined_sample_type(evlist);
enum perf_call_graph_mode mode = CALLCHAIN_NONE;
diff --git a/tools/perf/builtin-config.c b/tools/perf/builtin-config.c
index d76f831f94c7..42d8157e047a 100644
--- a/tools/perf/builtin-config.c
+++ b/tools/perf/builtin-config.c
@@ -7,14 +7,14 @@
*/
#include "builtin.h"
-#include "perf.h"
-
#include "util/cache.h"
#include <subcmd/parse-options.h>
#include "util/util.h"
#include "util/debug.h"
#include "util/config.h"
#include <linux/string.h>
+#include <stdio.h>
+#include <stdlib.h>
static bool use_system_config, use_user_config;
diff --git a/tools/perf/builtin-data.c b/tools/perf/builtin-data.c
index dde25d4ca56d..ca2fb44874e4 100644
--- a/tools/perf/builtin-data.c
+++ b/tools/perf/builtin-data.c
@@ -1,5 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
+#include <stdio.h>
+#include <string.h>
#include "builtin.h"
#include "perf.h"
#include "debug.h"
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 6e7920793729..827e4800d862 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -6,6 +6,7 @@
* DSOs and symbol information, sort them and produce a diff.
*/
#include "builtin.h"
+#include "perf.h"
#include "util/debug.h"
#include "util/event.h"
@@ -15,11 +16,16 @@
#include "util/session.h"
#include "util/tool.h"
#include "util/sort.h"
+#include "util/srcline.h"
#include "util/symbol.h"
-#include "util/util.h"
#include "util/data.h"
#include "util/config.h"
#include "util/time-utils.h"
+#include "util/annotate.h"
+#include "util/map.h"
+#include <linux/zalloc.h>
+#include <subcmd/pager.h>
+#include <subcmd/parse-options.h>
#include <errno.h>
#include <inttypes.h>
@@ -32,6 +38,7 @@ struct perf_diff {
struct perf_time_interval *ptime_range;
int range_size;
int range_num;
+ bool has_br_stack;
};
/* Diff command specific HPP columns. */
@@ -44,6 +51,7 @@ enum {
PERF_HPP_DIFF__WEIGHTED_DIFF,
PERF_HPP_DIFF__FORMULA,
PERF_HPP_DIFF__DELTA_ABS,
+ PERF_HPP_DIFF__CYCLES,
PERF_HPP_DIFF__MAX_INDEX
};
@@ -86,11 +94,14 @@ static s64 compute_wdiff_w2;
static const char *cpu_list;
static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
+static struct addr_location dummy_al;
+
enum {
COMPUTE_DELTA,
COMPUTE_RATIO,
COMPUTE_WEIGHTED_DIFF,
COMPUTE_DELTA_ABS,
+ COMPUTE_CYCLES,
COMPUTE_MAX,
};
@@ -99,6 +110,7 @@ const char *compute_names[COMPUTE_MAX] = {
[COMPUTE_DELTA_ABS] = "delta-abs",
[COMPUTE_RATIO] = "ratio",
[COMPUTE_WEIGHTED_DIFF] = "wdiff",
+ [COMPUTE_CYCLES] = "cycles",
};
static int compute = COMPUTE_DELTA_ABS;
@@ -108,6 +120,7 @@ static int compute_2_hpp[COMPUTE_MAX] = {
[COMPUTE_DELTA_ABS] = PERF_HPP_DIFF__DELTA_ABS,
[COMPUTE_RATIO] = PERF_HPP_DIFF__RATIO,
[COMPUTE_WEIGHTED_DIFF] = PERF_HPP_DIFF__WEIGHTED_DIFF,
+ [COMPUTE_CYCLES] = PERF_HPP_DIFF__CYCLES,
};
#define MAX_COL_WIDTH 70
@@ -146,6 +159,10 @@ static struct header_column {
[PERF_HPP_DIFF__FORMULA] = {
.name = "Formula",
.width = MAX_COL_WIDTH,
+ },
+ [PERF_HPP_DIFF__CYCLES] = {
+ .name = "[Program Block Range] Cycles Diff",
+ .width = 70,
}
};
@@ -335,10 +352,35 @@ static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair,
return -1;
}
+static void *block_hist_zalloc(size_t size)
+{
+ struct block_hist *bh;
+
+ bh = zalloc(size + sizeof(*bh));
+ if (!bh)
+ return NULL;
+
+ return &bh->he;
+}
+
+static void block_hist_free(void *he)
+{
+ struct block_hist *bh;
+
+ bh = container_of(he, struct block_hist, he);
+ hists__delete_entries(&bh->block_hists);
+ free(bh);
+}
+
+struct hist_entry_ops block_hist_ops = {
+ .new = block_hist_zalloc,
+ .free = block_hist_free,
+};
+
static int diff__process_sample_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine)
{
struct perf_diff *pdiff = container_of(tool, struct perf_diff, tool);
@@ -362,9 +404,22 @@ static int diff__process_sample_event(struct perf_tool *tool,
goto out_put;
}
- if (!hists__add_entry(hists, &al, NULL, NULL, NULL, sample, true)) {
- pr_warning("problem incrementing symbol period, skipping event\n");
- goto out_put;
+ if (compute != COMPUTE_CYCLES) {
+ if (!hists__add_entry(hists, &al, NULL, NULL, NULL, sample,
+ true)) {
+ pr_warning("problem incrementing symbol period, "
+ "skipping event\n");
+ goto out_put;
+ }
+ } else {
+ if (!hists__add_entry_ops(hists, &block_hist_ops, &al, NULL,
+ NULL, NULL, sample, true)) {
+ pr_warning("problem incrementing symbol period, "
+ "skipping event\n");
+ goto out_put;
+ }
+
+ hist__account_cycles(sample->branch_stack, &al, sample, false);
}
/*
@@ -397,10 +452,10 @@ static struct perf_diff pdiff = {
},
};
-static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
- struct perf_evlist *evlist)
+static struct evsel *evsel_match(struct evsel *evsel,
+ struct evlist *evlist)
{
- struct perf_evsel *e;
+ struct evsel *e;
evlist__for_each_entry(evlist, e) {
if (perf_evsel__match2(evsel, e))
@@ -410,9 +465,9 @@ static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
return NULL;
}
-static void perf_evlist__collapse_resort(struct perf_evlist *evlist)
+static void perf_evlist__collapse_resort(struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) {
struct hists *hists = evsel__hists(evsel);
@@ -474,6 +529,203 @@ static void hists__baseline_only(struct hists *hists)
}
}
+static int64_t block_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
+ struct hist_entry *left, struct hist_entry *right)
+{
+ struct block_info *bi_l = left->block_info;
+ struct block_info *bi_r = right->block_info;
+ int cmp;
+
+ if (!bi_l->sym || !bi_r->sym) {
+ if (!bi_l->sym && !bi_r->sym)
+ return 0;
+ else if (!bi_l->sym)
+ return -1;
+ else
+ return 1;
+ }
+
+ if (bi_l->sym == bi_r->sym) {
+ if (bi_l->start == bi_r->start) {
+ if (bi_l->end == bi_r->end)
+ return 0;
+ else
+ return (int64_t)(bi_r->end - bi_l->end);
+ } else
+ return (int64_t)(bi_r->start - bi_l->start);
+ } else {
+ cmp = strcmp(bi_l->sym->name, bi_r->sym->name);
+ return cmp;
+ }
+
+ if (bi_l->sym->start != bi_r->sym->start)
+ return (int64_t)(bi_r->sym->start - bi_l->sym->start);
+
+ return (int64_t)(bi_r->sym->end - bi_l->sym->end);
+}
+
+static int64_t block_cycles_diff_cmp(struct hist_entry *left,
+ struct hist_entry *right)
+{
+ bool pairs_left = hist_entry__has_pairs(left);
+ bool pairs_right = hist_entry__has_pairs(right);
+ s64 l, r;
+
+ if (!pairs_left && !pairs_right)
+ return 0;
+
+ l = labs(left->diff.cycles);
+ r = labs(right->diff.cycles);
+ return r - l;
+}
+
+static int64_t block_sort(struct perf_hpp_fmt *fmt __maybe_unused,
+ struct hist_entry *left, struct hist_entry *right)
+{
+ return block_cycles_diff_cmp(right, left);
+}
+
+static void init_block_hist(struct block_hist *bh)
+{
+ __hists__init(&bh->block_hists, &bh->block_list);
+ perf_hpp_list__init(&bh->block_list);
+
+ INIT_LIST_HEAD(&bh->block_fmt.list);
+ INIT_LIST_HEAD(&bh->block_fmt.sort_list);
+ bh->block_fmt.cmp = block_cmp;
+ bh->block_fmt.sort = block_sort;
+ perf_hpp_list__register_sort_field(&bh->block_list,
+ &bh->block_fmt);
+ bh->valid = true;
+}
+
+static void init_block_info(struct block_info *bi, struct symbol *sym,
+ struct cyc_hist *ch, int offset)
+{
+ bi->sym = sym;
+ bi->start = ch->start;
+ bi->end = offset;
+ bi->cycles = ch->cycles;
+ bi->cycles_aggr = ch->cycles_aggr;
+ bi->num = ch->num;
+ bi->num_aggr = ch->num_aggr;
+}
+
+static int process_block_per_sym(struct hist_entry *he)
+{
+ struct annotation *notes;
+ struct cyc_hist *ch;
+ struct block_hist *bh;
+
+ if (!he->ms.map || !he->ms.sym)
+ return 0;
+
+ notes = symbol__annotation(he->ms.sym);
+ if (!notes || !notes->src || !notes->src->cycles_hist)
+ return 0;
+
+ bh = container_of(he, struct block_hist, he);
+ init_block_hist(bh);
+
+ ch = notes->src->cycles_hist;
+ for (unsigned int i = 0; i < symbol__size(he->ms.sym); i++) {
+ if (ch[i].num_aggr) {
+ struct block_info *bi;
+ struct hist_entry *he_block;
+
+ bi = block_info__new();
+ if (!bi)
+ return -1;
+
+ init_block_info(bi, he->ms.sym, &ch[i], i);
+ he_block = hists__add_entry_block(&bh->block_hists,
+ &dummy_al, bi);
+ if (!he_block) {
+ block_info__put(bi);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int block_pair_cmp(struct hist_entry *a, struct hist_entry *b)
+{
+ struct block_info *bi_a = a->block_info;
+ struct block_info *bi_b = b->block_info;
+ int cmp;
+
+ if (!bi_a->sym || !bi_b->sym)
+ return -1;
+
+ cmp = strcmp(bi_a->sym->name, bi_b->sym->name);
+
+ if ((!cmp) && (bi_a->start == bi_b->start) && (bi_a->end == bi_b->end))
+ return 0;
+
+ return -1;
+}
+
+static struct hist_entry *get_block_pair(struct hist_entry *he,
+ struct hists *hists_pair)
+{
+ struct rb_root_cached *root = hists_pair->entries_in;
+ struct rb_node *next = rb_first_cached(root);
+ int cmp;
+
+ while (next != NULL) {
+ struct hist_entry *he_pair = rb_entry(next, struct hist_entry,
+ rb_node_in);
+
+ next = rb_next(&he_pair->rb_node_in);
+
+ cmp = block_pair_cmp(he_pair, he);
+ if (!cmp)
+ return he_pair;
+ }
+
+ return NULL;
+}
+
+static void compute_cycles_diff(struct hist_entry *he,
+ struct hist_entry *pair)
+{
+ pair->diff.computed = true;
+ if (pair->block_info->num && he->block_info->num) {
+ pair->diff.cycles =
+ pair->block_info->cycles_aggr / pair->block_info->num_aggr -
+ he->block_info->cycles_aggr / he->block_info->num_aggr;
+ }
+}
+
+static void block_hists_match(struct hists *hists_base,
+ struct hists *hists_pair)
+{
+ struct rb_root_cached *root = hists_base->entries_in;
+ struct rb_node *next = rb_first_cached(root);
+
+ while (next != NULL) {
+ struct hist_entry *he = rb_entry(next, struct hist_entry,
+ rb_node_in);
+ struct hist_entry *pair = get_block_pair(he, hists_pair);
+
+ next = rb_next(&he->rb_node_in);
+
+ if (pair) {
+ hist_entry__add_pair(pair, he);
+ compute_cycles_diff(he, pair);
+ }
+ }
+}
+
+static int filter_cb(struct hist_entry *he, void *arg __maybe_unused)
+{
+ /* Skip the calculation of column length in output_resort */
+ he->filtered = true;
+ return 0;
+}
+
static void hists__precompute(struct hists *hists)
{
struct rb_root_cached *root;
@@ -486,6 +738,7 @@ static void hists__precompute(struct hists *hists)
next = rb_first_cached(root);
while (next != NULL) {
+ struct block_hist *bh, *pair_bh;
struct hist_entry *he, *pair;
struct data__file *d;
int i;
@@ -493,6 +746,9 @@ static void hists__precompute(struct hists *hists)
he = rb_entry(next, struct hist_entry, rb_node_in);
next = rb_next(&he->rb_node_in);
+ if (compute == COMPUTE_CYCLES)
+ process_block_per_sym(he);
+
data__for_each_file_new(i, d) {
pair = get_pair_data(he, d);
if (!pair)
@@ -509,6 +765,19 @@ static void hists__precompute(struct hists *hists)
case COMPUTE_WEIGHTED_DIFF:
compute_wdiff(he, pair);
break;
+ case COMPUTE_CYCLES:
+ process_block_per_sym(pair);
+ bh = container_of(he, struct block_hist, he);
+ pair_bh = container_of(pair, struct block_hist,
+ he);
+
+ if (bh->valid && pair_bh->valid) {
+ block_hists_match(&bh->block_hists,
+ &pair_bh->block_hists);
+ hists__output_resort_cb(&pair_bh->block_hists,
+ NULL, filter_cb);
+ }
+ break;
default:
BUG_ON(1);
}
@@ -720,6 +989,9 @@ static void hists__process(struct hists *hists)
hists__precompute(hists);
hists__output_resort(hists, NULL);
+ if (compute == COMPUTE_CYCLES)
+ symbol_conf.report_block = true;
+
hists__fprintf(hists, !quiet, 0, 0, 0, stdout,
!symbol_conf.use_callchain);
}
@@ -741,8 +1013,8 @@ static void data__fprintf(void)
static void data_process(void)
{
- struct perf_evlist *evlist_base = data__files[0].session->evlist;
- struct perf_evsel *evsel_base;
+ struct evlist *evlist_base = data__files[0].session->evlist;
+ struct evsel *evsel_base;
bool first = true;
evlist__for_each_entry(evlist_base, evsel_base) {
@@ -751,8 +1023,8 @@ static void data_process(void)
int i;
data__for_each_file_new(i, d) {
- struct perf_evlist *evlist = d->session->evlist;
- struct perf_evsel *evsel;
+ struct evlist *evlist = d->session->evlist;
+ struct evsel *evsel;
struct hists *hists;
evsel = evsel_match(evsel_base, evlist);
@@ -873,6 +1145,31 @@ static int parse_time_str(struct data__file *d, char *abstime_ostr,
return ret;
}
+static int check_file_brstack(void)
+{
+ struct data__file *d;
+ bool has_br_stack;
+ int i;
+
+ data__for_each_file(i, d) {
+ d->session = perf_session__new(&d->data, false, &pdiff.tool);
+ if (!d->session) {
+ pr_err("Failed to open %s\n", d->data.path);
+ return -1;
+ }
+
+ has_br_stack = perf_header__has_feat(&d->session->header,
+ HEADER_BRANCH_STACK);
+ perf_session__delete(d->session);
+ if (!has_br_stack)
+ return 0;
+ }
+
+ /* Set only all files having branch stacks */
+ pdiff.has_br_stack = true;
+ return 0;
+}
+
static int __cmd_diff(void)
{
struct data__file *d;
@@ -950,7 +1247,7 @@ static const struct option options[] = {
OPT_BOOLEAN('b', "baseline-only", &show_baseline_only,
"Show only items with match in baseline"),
OPT_CALLBACK('c', "compute", &compute,
- "delta,delta-abs,ratio,wdiff:w1,w2 (default delta-abs)",
+ "delta,delta-abs,ratio,wdiff:w1,w2 (default delta-abs),cycles",
"Entries differential computation selection",
setup_compute),
OPT_BOOLEAN('p', "period", &show_period,
@@ -1028,6 +1325,49 @@ static int hpp__entry_baseline(struct hist_entry *he, char *buf, size_t size)
return ret;
}
+static int cycles_printf(struct hist_entry *he, struct hist_entry *pair,
+ struct perf_hpp *hpp, int width)
+{
+ struct block_hist *bh = container_of(he, struct block_hist, he);
+ struct block_hist *bh_pair = container_of(pair, struct block_hist, he);
+ struct hist_entry *block_he;
+ struct block_info *bi;
+ char buf[128];
+ char *start_line, *end_line;
+
+ block_he = hists__get_entry(&bh_pair->block_hists, bh->block_idx);
+ if (!block_he) {
+ hpp->skip = true;
+ return 0;
+ }
+
+ /*
+ * Avoid printing the warning "addr2line_init failed for ..."
+ */
+ symbol_conf.disable_add2line_warn = true;
+
+ bi = block_he->block_info;
+
+ start_line = map__srcline(he->ms.map, bi->sym->start + bi->start,
+ he->ms.sym);
+
+ end_line = map__srcline(he->ms.map, bi->sym->start + bi->end,
+ he->ms.sym);
+
+ if ((start_line != SRCLINE_UNKNOWN) && (end_line != SRCLINE_UNKNOWN)) {
+ scnprintf(buf, sizeof(buf), "[%s -> %s] %4ld",
+ start_line, end_line, block_he->diff.cycles);
+ } else {
+ scnprintf(buf, sizeof(buf), "[%7lx -> %7lx] %4ld",
+ bi->start, bi->end, block_he->diff.cycles);
+ }
+
+ free_srcline(start_line);
+ free_srcline(end_line);
+
+ return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
+}
+
static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
struct perf_hpp *hpp, struct hist_entry *he,
int comparison_method)
@@ -1039,8 +1379,17 @@ static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
s64 wdiff;
char pfmt[20] = " ";
- if (!pair)
+ if (!pair) {
+ if (comparison_method == COMPUTE_CYCLES) {
+ struct block_hist *bh;
+
+ bh = container_of(he, struct block_hist, he);
+ if (bh->block_idx)
+ hpp->skip = true;
+ }
+
goto no_print;
+ }
switch (comparison_method) {
case COMPUTE_DELTA:
@@ -1075,6 +1424,8 @@ static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
return color_snprintf(hpp->buf, hpp->size,
get_percent_color(wdiff),
pfmt, wdiff);
+ case COMPUTE_CYCLES:
+ return cycles_printf(he, pair, hpp, dfmt->header_width);
default:
BUG_ON(1);
}
@@ -1104,6 +1455,12 @@ static int hpp__color_wdiff(struct perf_hpp_fmt *fmt,
return __hpp__color_compare(fmt, hpp, he, COMPUTE_WEIGHTED_DIFF);
}
+static int hpp__color_cycles(struct perf_hpp_fmt *fmt,
+ struct perf_hpp *hpp, struct hist_entry *he)
+{
+ return __hpp__color_compare(fmt, hpp, he, COMPUTE_CYCLES);
+}
+
static void
hpp__entry_unpair(struct hist_entry *he, int idx, char *buf, size_t size)
{
@@ -1305,6 +1662,10 @@ static void data__hpp_register(struct data__file *d, int idx)
fmt->color = hpp__color_delta;
fmt->sort = hist_entry__cmp_delta_abs;
break;
+ case PERF_HPP_DIFF__CYCLES:
+ fmt->color = hpp__color_cycles;
+ fmt->sort = hist_entry__cmp_nop;
+ break;
default:
fmt->sort = hist_entry__cmp_nop;
break;
@@ -1385,6 +1746,13 @@ static int ui_init(void)
case COMPUTE_DELTA_ABS:
fmt->sort = hist_entry__cmp_delta_abs_idx;
break;
+ case COMPUTE_CYCLES:
+ /*
+ * Should set since 'fmt->sort' is called without
+ * checking valid during sorting
+ */
+ fmt->sort = hist_entry__cmp_nop;
+ break;
default:
BUG_ON(1);
}
@@ -1481,12 +1849,20 @@ int cmd_diff(int argc, const char **argv)
if (quiet)
perf_quiet_option();
+ symbol__annotation_init();
+
if (symbol__init(NULL) < 0)
return -1;
if (data_init(argc, argv) < 0)
return -1;
+ if (check_file_brstack() < 0)
+ return -1;
+
+ if (compute == COMPUTE_CYCLES && !pdiff.has_br_stack)
+ return -1;
+
if (ui_init() < 0)
return -1;
diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c
index 6e4f63b0da4a..238fa3876805 100644
--- a/tools/perf/builtin-evlist.c
+++ b/tools/perf/builtin-evlist.c
@@ -21,7 +21,7 @@
static int __cmd_evlist(const char *file_name, struct perf_attr_details *details)
{
struct perf_session *session;
- struct perf_evsel *pos;
+ struct evsel *pos;
struct perf_data data = {
.path = file_name,
.mode = PERF_DATA_MODE_READ,
@@ -36,7 +36,7 @@ static int __cmd_evlist(const char *file_name, struct perf_attr_details *details
evlist__for_each_entry(session->evlist, pos) {
perf_evsel__fprintf(pos, details, stdout);
- if (pos->attr.type == PERF_TYPE_TRACEPOINT)
+ if (pos->core.attr.type == PERF_TYPE_TRACEPOINT)
has_tracepoint = true;
}
diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c
index f42f228e8899..d5adc417a4ca 100644
--- a/tools/perf/builtin-ftrace.c
+++ b/tools/perf/builtin-ftrace.c
@@ -1,34 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* builtin-ftrace.c
*
* Copyright (c) 2013 LG Electronics, Namhyung Kim <namhyung@kernel.org>
- *
- * Released under the GPL v2.
*/
#include "builtin.h"
-#include "perf.h"
#include <errno.h>
#include <unistd.h>
#include <signal.h>
+#include <stdlib.h>
#include <fcntl.h>
#include <poll.h>
+#include <linux/capability.h>
+#include <linux/string.h>
#include "debug.h"
+#include <subcmd/pager.h>
#include <subcmd/parse-options.h>
#include <api/fs/tracing_path.h>
#include "evlist.h"
#include "target.h"
#include "cpumap.h"
#include "thread_map.h"
+#include "util/cap.h"
#include "util/config.h"
-
#define DEFAULT_TRACER "function_graph"
struct perf_ftrace {
- struct perf_evlist *evlist;
+ struct evlist *evlist;
struct target target;
const char *tracer;
struct list_head filters;
@@ -157,16 +159,16 @@ static int set_tracing_pid(struct perf_ftrace *ftrace)
if (target__has_cpu(&ftrace->target))
return 0;
- for (i = 0; i < thread_map__nr(ftrace->evlist->threads); i++) {
+ for (i = 0; i < perf_thread_map__nr(ftrace->evlist->core.threads); i++) {
scnprintf(buf, sizeof(buf), "%d",
- ftrace->evlist->threads->map[i]);
+ ftrace->evlist->core.threads->map[i]);
if (append_tracing_file("set_ftrace_pid", buf) < 0)
return -1;
}
return 0;
}
-static int set_tracing_cpumask(struct cpu_map *cpumap)
+static int set_tracing_cpumask(struct perf_cpu_map *cpumap)
{
char *cpumask;
size_t mask_size;
@@ -174,7 +176,7 @@ static int set_tracing_cpumask(struct cpu_map *cpumap)
int last_cpu;
last_cpu = cpu_map__cpu(cpumap, cpumap->nr - 1);
- mask_size = (last_cpu + 3) / 4 + 1;
+ mask_size = last_cpu / 4 + 2; /* one more byte for EOS */
mask_size += last_cpu / 32; /* ',' is needed for every 32th cpus */
cpumask = malloc(mask_size);
@@ -193,7 +195,7 @@ static int set_tracing_cpumask(struct cpu_map *cpumap)
static int set_tracing_cpu(struct perf_ftrace *ftrace)
{
- struct cpu_map *cpumap = ftrace->evlist->cpus;
+ struct perf_cpu_map *cpumap = ftrace->evlist->core.cpus;
if (!target__has_cpu(&ftrace->target))
return 0;
@@ -203,11 +205,11 @@ static int set_tracing_cpu(struct perf_ftrace *ftrace)
static int reset_tracing_cpu(void)
{
- struct cpu_map *cpumap = cpu_map__new(NULL);
+ struct perf_cpu_map *cpumap = perf_cpu_map__new(NULL);
int ret;
ret = set_tracing_cpumask(cpumap);
- cpu_map__put(cpumap);
+ perf_cpu_map__put(cpumap);
return ret;
}
@@ -282,8 +284,14 @@ static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv)
.events = POLLIN,
};
- if (geteuid() != 0) {
- pr_err("ftrace only works for root!\n");
+ if (!perf_cap__capable(CAP_SYS_ADMIN)) {
+ pr_err("ftrace only works for %s!\n",
+#ifdef HAVE_LIBCAP_SUPPORT
+ "users with the SYS_ADMIN capability"
+#else
+ "root"
+#endif
+ );
return -1;
}
@@ -432,7 +440,7 @@ static void delete_filter_func(struct list_head *head)
struct filter_entry *pos, *tmp;
list_for_each_entry_safe(pos, tmp, head, list) {
- list_del(&pos->list);
+ list_del_init(&pos->list);
free(pos);
}
}
@@ -496,7 +504,7 @@ int cmd_ftrace(int argc, const char **argv)
goto out_delete_filters;
}
- ftrace.evlist = perf_evlist__new();
+ ftrace.evlist = evlist__new();
if (ftrace.evlist == NULL) {
ret = -ENOMEM;
goto out_delete_filters;
@@ -509,7 +517,7 @@ int cmd_ftrace(int argc, const char **argv)
ret = __cmd_ftrace(&ftrace, argc, argv);
out_delete_evlist:
- perf_evlist__delete(ftrace.evlist);
+ evlist__delete(ftrace.evlist);
out_delete_filters:
delete_filter_func(&ftrace.filters);
diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c
index 3d29d0524a89..3976aebe3677 100644
--- a/tools/perf/builtin-help.c
+++ b/tools/perf/builtin-help.c
@@ -4,8 +4,9 @@
*
* Builtin help command
*/
-#include "perf.h"
+#include "util/cache.h"
#include "util/config.h"
+#include "util/strbuf.h"
#include "builtin.h"
#include <subcmd/exec-cmd.h>
#include "common-cmds.h"
@@ -14,8 +15,12 @@
#include <subcmd/help.h>
#include "util/debug.h"
#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/zalloc.h>
#include <errno.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index 8e0e06d3edfc..c14f40b858bc 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -8,8 +8,8 @@
*/
#include "builtin.h"
-#include "perf.h"
#include "util/color.h"
+#include "util/dso.h"
#include "util/evlist.h"
#include "util/evsel.h"
#include "util/map.h"
@@ -96,7 +96,7 @@ static int perf_event__repipe_op2_synth(struct perf_session *session,
static int perf_event__repipe_attr(struct perf_tool *tool,
union perf_event *event,
- struct perf_evlist **pevlist)
+ struct evlist **pevlist)
{
struct perf_inject *inject = container_of(tool, struct perf_inject,
tool);
@@ -215,16 +215,16 @@ static int perf_event__drop_aux(struct perf_tool *tool,
typedef int (*inject_handler)(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine);
static int perf_event__repipe_sample(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine)
{
- if (evsel->handler) {
+ if (evsel && evsel->handler) {
inject_handler f = evsel->handler;
return f(tool, event, sample, evsel, machine);
}
@@ -424,7 +424,7 @@ static int dso__inject_build_id(struct dso *dso, struct perf_tool *tool,
static int perf_event__inject_buildid(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel __maybe_unused,
+ struct evsel *evsel __maybe_unused,
struct machine *machine)
{
struct addr_location al;
@@ -465,7 +465,7 @@ repipe:
static int perf_inject__sched_process_exit(struct perf_tool *tool,
union perf_event *event __maybe_unused,
struct perf_sample *sample,
- struct perf_evsel *evsel __maybe_unused,
+ struct evsel *evsel __maybe_unused,
struct machine *machine __maybe_unused)
{
struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
@@ -485,7 +485,7 @@ static int perf_inject__sched_process_exit(struct perf_tool *tool,
static int perf_inject__sched_switch(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine)
{
struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
@@ -509,7 +509,7 @@ static int perf_inject__sched_switch(struct perf_tool *tool,
static int perf_inject__sched_stat(struct perf_tool *tool,
union perf_event *event __maybe_unused,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine)
{
struct event_entry *ent;
@@ -530,8 +530,8 @@ found:
sample_sw.period = sample->period;
sample_sw.time = sample->time;
- perf_event__synthesize_sample(event_sw, evsel->attr.sample_type,
- evsel->attr.read_format, &sample_sw);
+ perf_event__synthesize_sample(event_sw, evsel->core.attr.sample_type,
+ evsel->core.attr.read_format, &sample_sw);
build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine);
return perf_event__repipe(tool, event_sw, &sample_sw, machine);
}
@@ -541,10 +541,10 @@ static void sig_handler(int sig __maybe_unused)
session_done = 1;
}
-static int perf_evsel__check_stype(struct perf_evsel *evsel,
+static int perf_evsel__check_stype(struct evsel *evsel,
u64 sample_type, const char *sample_msg)
{
- struct perf_event_attr *attr = &evsel->attr;
+ struct perf_event_attr *attr = &evsel->core.attr;
const char *name = perf_evsel__name(evsel);
if (!(attr->sample_type & sample_type)) {
@@ -559,7 +559,7 @@ static int perf_evsel__check_stype(struct perf_evsel *evsel,
static int drop_sample(struct perf_tool *tool __maybe_unused,
union perf_event *event __maybe_unused,
struct perf_sample *sample __maybe_unused,
- struct perf_evsel *evsel __maybe_unused,
+ struct evsel *evsel __maybe_unused,
struct machine *machine __maybe_unused)
{
return 0;
@@ -567,8 +567,8 @@ static int drop_sample(struct perf_tool *tool __maybe_unused,
static void strip_init(struct perf_inject *inject)
{
- struct perf_evlist *evlist = inject->session->evlist;
- struct perf_evsel *evsel;
+ struct evlist *evlist = inject->session->evlist;
+ struct evsel *evsel;
inject->tool.context_switch = perf_event__drop;
@@ -576,10 +576,10 @@ static void strip_init(struct perf_inject *inject)
evsel->handler = drop_sample;
}
-static bool has_tracking(struct perf_evsel *evsel)
+static bool has_tracking(struct evsel *evsel)
{
- return evsel->attr.mmap || evsel->attr.mmap2 || evsel->attr.comm ||
- evsel->attr.task;
+ return evsel->core.attr.mmap || evsel->core.attr.mmap2 || evsel->core.attr.comm ||
+ evsel->core.attr.task;
}
#define COMPAT_MASK (PERF_SAMPLE_ID | PERF_SAMPLE_TID | PERF_SAMPLE_TIME | \
@@ -590,10 +590,10 @@ static bool has_tracking(struct perf_evsel *evsel)
* their selected event to exist, except if there is only 1 selected event left
* and it has a compatible sample type.
*/
-static bool ok_to_remove(struct perf_evlist *evlist,
- struct perf_evsel *evsel_to_remove)
+static bool ok_to_remove(struct evlist *evlist,
+ struct evsel *evsel_to_remove)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
int cnt = 0;
bool ok = false;
@@ -603,8 +603,8 @@ static bool ok_to_remove(struct perf_evlist *evlist,
evlist__for_each_entry(evlist, evsel) {
if (evsel->handler != drop_sample) {
cnt += 1;
- if ((evsel->attr.sample_type & COMPAT_MASK) ==
- (evsel_to_remove->attr.sample_type & COMPAT_MASK))
+ if ((evsel->core.attr.sample_type & COMPAT_MASK) ==
+ (evsel_to_remove->core.attr.sample_type & COMPAT_MASK))
ok = true;
}
}
@@ -614,16 +614,16 @@ static bool ok_to_remove(struct perf_evlist *evlist,
static void strip_fini(struct perf_inject *inject)
{
- struct perf_evlist *evlist = inject->session->evlist;
- struct perf_evsel *evsel, *tmp;
+ struct evlist *evlist = inject->session->evlist;
+ struct evsel *evsel, *tmp;
/* Remove non-synthesized evsels if possible */
evlist__for_each_entry_safe(evlist, tmp, evsel) {
if (evsel->handler == drop_sample &&
ok_to_remove(evlist, evsel)) {
pr_debug("Deleting %s\n", perf_evsel__name(evsel));
- perf_evlist__remove(evlist, evsel);
- perf_evsel__delete(evsel);
+ evlist__remove(evlist, evsel);
+ evsel__delete(evsel);
}
}
}
@@ -651,7 +651,7 @@ static int __cmd_inject(struct perf_inject *inject)
if (inject->build_ids) {
inject->tool.sample = perf_event__inject_buildid;
} else if (inject->sched_stat) {
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(session->evlist, evsel) {
const char *name = perf_evsel__name(evsel);
@@ -712,7 +712,7 @@ static int __cmd_inject(struct perf_inject *inject)
* remove the evsel.
*/
if (inject->itrace_synth_opts.set) {
- struct perf_evsel *evsel;
+ struct evsel *evsel;
perf_header__clear_feat(&session->header,
HEADER_AUXTRACE);
@@ -724,8 +724,8 @@ static int __cmd_inject(struct perf_inject *inject)
if (evsel) {
pr_debug("Deleting %s\n",
perf_evsel__name(evsel));
- perf_evlist__remove(session->evlist, evsel);
- perf_evsel__delete(evsel);
+ evlist__remove(session->evlist, evsel);
+ evsel__delete(evsel);
}
if (inject->strip)
strip_fini(inject);
diff --git a/tools/perf/builtin-kallsyms.c b/tools/perf/builtin-kallsyms.c
index bc7a2bc7aed7..c08ee81529e8 100644
--- a/tools/perf/builtin-kallsyms.c
+++ b/tools/perf/builtin-kallsyms.c
@@ -1,17 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* builtin-kallsyms.c
*
* Builtin command: Look for a symbol in the running kernel and its modules
*
* Copyright (C) 2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
- *
- * Released under the GPL v2. (and only v2, not any later version)
*/
#include <inttypes.h>
#include "builtin.h"
#include <linux/compiler.h>
#include <subcmd/parse-options.h>
#include "debug.h"
+#include "dso.h"
#include "machine.h"
#include "map.h"
#include "symbol.h"
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index b80eee455111..b5682beaad72 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -2,9 +2,9 @@
#include "builtin.h"
#include "perf.h"
+#include "util/dso.h"
#include "util/evlist.h"
#include "util/evsel.h"
-#include "util/util.h"
#include "util/config.h"
#include "util/map.h"
#include "util/symbol.h"
@@ -15,22 +15,25 @@
#include "util/callchain.h"
#include "util/time-utils.h"
+#include <subcmd/pager.h>
#include <subcmd/parse-options.h>
#include "util/trace-event.h"
#include "util/data.h"
#include "util/cpumap.h"
#include "util/debug.h"
+#include "util/string2.h"
#include <linux/kernel.h>
#include <linux/rbtree.h>
#include <linux/string.h>
+#include <linux/zalloc.h>
#include <errno.h>
#include <inttypes.h>
#include <locale.h>
#include <regex.h>
-#include "sane_ctype.h"
+#include <linux/ctype.h>
static int kmem_slab;
static int kmem_page;
@@ -165,7 +168,7 @@ static int insert_caller_stat(unsigned long call_site,
return 0;
}
-static int perf_evsel__process_alloc_event(struct perf_evsel *evsel,
+static int perf_evsel__process_alloc_event(struct evsel *evsel,
struct perf_sample *sample)
{
unsigned long ptr = perf_evsel__intval(evsel, sample, "ptr"),
@@ -184,7 +187,7 @@ static int perf_evsel__process_alloc_event(struct perf_evsel *evsel,
return 0;
}
-static int perf_evsel__process_alloc_node_event(struct perf_evsel *evsel,
+static int perf_evsel__process_alloc_node_event(struct evsel *evsel,
struct perf_sample *sample)
{
int ret = perf_evsel__process_alloc_event(evsel, sample);
@@ -228,7 +231,7 @@ static struct alloc_stat *search_alloc_stat(unsigned long ptr,
return NULL;
}
-static int perf_evsel__process_free_event(struct perf_evsel *evsel,
+static int perf_evsel__process_free_event(struct evsel *evsel,
struct perf_sample *sample)
{
unsigned long ptr = perf_evsel__intval(evsel, sample, "ptr");
@@ -380,7 +383,7 @@ static int build_alloc_func_list(void)
* Find first non-memory allocation function from callchain.
* The allocation functions are in the 'alloc_func_list'.
*/
-static u64 find_callsite(struct perf_evsel *evsel, struct perf_sample *sample)
+static u64 find_callsite(struct evsel *evsel, struct perf_sample *sample)
{
struct addr_location al;
struct machine *machine = &kmem_session->machines.host;
@@ -727,7 +730,7 @@ static char *compact_gfp_string(unsigned long gfp_flags)
return NULL;
}
-static int parse_gfp_flags(struct perf_evsel *evsel, struct perf_sample *sample,
+static int parse_gfp_flags(struct evsel *evsel, struct perf_sample *sample,
unsigned int gfp_flags)
{
struct tep_record record = {
@@ -748,7 +751,8 @@ static int parse_gfp_flags(struct perf_evsel *evsel, struct perf_sample *sample,
}
trace_seq_init(&seq);
- tep_event_info(&seq, evsel->tp_format, &record);
+ tep_print_event(evsel->tp_format->tep,
+ &seq, &record, "%s", TEP_PRINT_INFO);
str = strtok_r(seq.buffer, " ", &pos);
while (str) {
@@ -778,7 +782,7 @@ static int parse_gfp_flags(struct perf_evsel *evsel, struct perf_sample *sample,
return 0;
}
-static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,
+static int perf_evsel__process_page_alloc_event(struct evsel *evsel,
struct perf_sample *sample)
{
u64 page;
@@ -851,7 +855,7 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,
return 0;
}
-static int perf_evsel__process_page_free_event(struct perf_evsel *evsel,
+static int perf_evsel__process_page_free_event(struct evsel *evsel,
struct perf_sample *sample)
{
u64 page;
@@ -929,13 +933,13 @@ static bool perf_kmem__skip_sample(struct perf_sample *sample)
return false;
}
-typedef int (*tracepoint_handler)(struct perf_evsel *evsel,
+typedef int (*tracepoint_handler)(struct evsel *evsel,
struct perf_sample *sample);
static int process_sample_event(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine)
{
int err = 0;
@@ -1362,8 +1366,8 @@ static void sort_result(void)
static int __cmd_kmem(struct perf_session *session)
{
int err = -EINVAL;
- struct perf_evsel *evsel;
- const struct perf_evsel_str_handler kmem_tracepoints[] = {
+ struct evsel *evsel;
+ const struct evsel_str_handler kmem_tracepoints[] = {
/* slab allocator */
{ "kmem:kmalloc", perf_evsel__process_alloc_event, },
{ "kmem:kmem_cache_alloc", perf_evsel__process_alloc_event, },
@@ -1966,7 +1970,7 @@ int cmd_kmem(int argc, const char **argv)
}
if (kmem_page) {
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evsel = perf_evlist__find_tracepoint_by_name(session->evlist,
"kmem:mm_page_alloc");
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index dbb6f737a3e2..0a4fcbe32bf6 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -2,16 +2,16 @@
#include "builtin.h"
#include "perf.h"
+#include "util/build-id.h"
#include "util/evsel.h"
#include "util/evlist.h"
#include "util/term.h"
-#include "util/util.h"
-#include "util/cache.h"
#include "util/symbol.h"
#include "util/thread.h"
#include "util/header.h"
#include "util/session.h"
#include "util/intlist.h"
+#include <subcmd/pager.h>
#include <subcmd/parse-options.h>
#include "util/trace-event.h"
#include "util/debug.h"
@@ -20,6 +20,7 @@
#include "util/top.h"
#include "util/data.h"
#include "util/ordered-events.h"
+#include "ui/ui.h"
#include <sys/prctl.h>
#ifdef HAVE_TIMERFD_SUPPORT
@@ -31,7 +32,9 @@
#include <fcntl.h>
#include <linux/kernel.h>
+#include <linux/string.h>
#include <linux/time64.h>
+#include <linux/zalloc.h>
#include <errno.h>
#include <inttypes.h>
#include <poll.h>
@@ -57,7 +60,7 @@ static const char *get_filename_for_perf_kvm(void)
#ifdef HAVE_KVM_STAT_SUPPORT
#include "util/kvm-stat.h"
-void exit_event_get_key(struct perf_evsel *evsel,
+void exit_event_get_key(struct evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
@@ -65,12 +68,12 @@ void exit_event_get_key(struct perf_evsel *evsel,
key->key = perf_evsel__intval(evsel, sample, kvm_exit_reason);
}
-bool kvm_exit_event(struct perf_evsel *evsel)
+bool kvm_exit_event(struct evsel *evsel)
{
return !strcmp(evsel->name, kvm_exit_trace);
}
-bool exit_event_begin(struct perf_evsel *evsel,
+bool exit_event_begin(struct evsel *evsel,
struct perf_sample *sample, struct event_key *key)
{
if (kvm_exit_event(evsel)) {
@@ -81,12 +84,12 @@ bool exit_event_begin(struct perf_evsel *evsel,
return false;
}
-bool kvm_entry_event(struct perf_evsel *evsel)
+bool kvm_entry_event(struct evsel *evsel)
{
return !strcmp(evsel->name, kvm_entry_trace);
}
-bool exit_event_end(struct perf_evsel *evsel,
+bool exit_event_end(struct evsel *evsel,
struct perf_sample *sample __maybe_unused,
struct event_key *key __maybe_unused)
{
@@ -286,7 +289,7 @@ static bool update_kvm_event(struct kvm_event *event, int vcpu_id,
}
static bool is_child_event(struct perf_kvm_stat *kvm,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct event_key *key)
{
@@ -396,7 +399,7 @@ static bool handle_end_event(struct perf_kvm_stat *kvm,
static
struct vcpu_event_record *per_vcpu_record(struct thread *thread,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample)
{
/* Only kvm_entry records vcpu id. */
@@ -419,7 +422,7 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread,
static bool handle_kvm_event(struct perf_kvm_stat *kvm,
struct thread *thread,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample)
{
struct vcpu_event_record *vcpu_record;
@@ -672,7 +675,7 @@ static bool skip_sample(struct perf_kvm_stat *kvm,
static int process_sample_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine)
{
int err = 0;
@@ -743,7 +746,7 @@ static bool verify_vcpu(int vcpu)
static s64 perf_kvm__mmap_read_idx(struct perf_kvm_stat *kvm, int idx,
u64 *mmap_time)
{
- struct perf_evlist *evlist = kvm->evlist;
+ struct evlist *evlist = kvm->evlist;
union perf_event *event;
struct perf_mmap *md;
u64 timestamp;
@@ -972,7 +975,7 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm)
goto out;
/* everything is good - enable the events and process */
- perf_evlist__enable(kvm->evlist);
+ evlist__enable(kvm->evlist);
while (!done) {
struct fdarray *fda = &kvm->evlist->pollfd;
@@ -993,7 +996,7 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm)
err = fdarray__poll(fda, 100);
}
- perf_evlist__disable(kvm->evlist);
+ evlist__disable(kvm->evlist);
if (err == 0) {
sort_result(kvm);
@@ -1011,8 +1014,8 @@ out:
static int kvm_live_open_events(struct perf_kvm_stat *kvm)
{
int err, rc = -1;
- struct perf_evsel *pos;
- struct perf_evlist *evlist = kvm->evlist;
+ struct evsel *pos;
+ struct evlist *evlist = kvm->evlist;
char sbuf[STRERR_BUFSIZE];
perf_evlist__config(evlist, &kvm->opts, NULL);
@@ -1022,7 +1025,7 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm)
* This command processes KVM tracepoints from host only
*/
evlist__for_each_entry(evlist, pos) {
- struct perf_event_attr *attr = &pos->attr;
+ struct perf_event_attr *attr = &pos->core.attr;
/* make sure these *are* set */
perf_evsel__set_sample_bit(pos, TID);
@@ -1048,7 +1051,7 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm)
attr->disabled = 1;
}
- err = perf_evlist__open(evlist);
+ err = evlist__open(evlist);
if (err < 0) {
printf("Couldn't create the events: %s\n",
str_error_r(errno, sbuf, sizeof(sbuf)));
@@ -1058,7 +1061,7 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm)
if (perf_evlist__mmap(evlist, kvm->opts.mmap_pages) < 0) {
ui__error("Failed to mmap the events: %s\n",
str_error_r(errno, sbuf, sizeof(sbuf)));
- perf_evlist__close(evlist);
+ evlist__close(evlist);
goto out;
}
@@ -1283,14 +1286,14 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv)
}
#ifdef HAVE_TIMERFD_SUPPORT
-static struct perf_evlist *kvm_live_event_list(void)
+static struct evlist *kvm_live_event_list(void)
{
- struct perf_evlist *evlist;
+ struct evlist *evlist;
char *tp, *name, *sys;
int err = -1;
const char * const *events_tp;
- evlist = perf_evlist__new();
+ evlist = evlist__new();
if (evlist == NULL)
return NULL;
@@ -1325,7 +1328,7 @@ static struct perf_evlist *kvm_live_event_list(void)
out:
if (err) {
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
evlist = NULL;
}
@@ -1450,7 +1453,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
perf_session__set_id_hdr_size(kvm->session);
ordered_events__set_copy_on_queue(&kvm->session->ordered_events, true);
machine__synthesize_threads(&kvm->session->machines.host, &kvm->opts.target,
- kvm->evlist->threads, false, 1);
+ kvm->evlist->core.threads, false, 1);
err = kvm_live_open_events(kvm);
if (err)
goto out;
@@ -1460,7 +1463,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
out:
perf_session__delete(kvm->session);
kvm->session = NULL;
- perf_evlist__delete(kvm->evlist);
+ evlist__delete(kvm->evlist);
return err;
}
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index e0312a1c4792..e290f6b348d8 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -10,14 +10,13 @@
*/
#include "builtin.h"
-#include "perf.h"
-
#include "util/parse-events.h"
-#include "util/cache.h"
#include "util/pmu.h"
#include "util/debug.h"
#include "util/metricgroup.h"
+#include <subcmd/pager.h>
#include <subcmd/parse-options.h>
+#include <stdio.h>
static bool desc_flag = true;
static bool details_flag;
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index b9810a8d350a..4c2b7f437cdf 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -4,14 +4,13 @@
#include "builtin.h"
#include "perf.h"
-#include "util/evlist.h"
+#include "util/evlist.h" // for struct evsel_str_handler
#include "util/evsel.h"
-#include "util/util.h"
-#include "util/cache.h"
#include "util/symbol.h"
#include "util/thread.h"
#include "util/header.h"
+#include <subcmd/pager.h>
#include <subcmd/parse-options.h>
#include "util/trace-event.h"
@@ -30,6 +29,7 @@
#include <linux/list.h>
#include <linux/hash.h>
#include <linux/kernel.h>
+#include <linux/zalloc.h>
static struct perf_session *session;
@@ -347,16 +347,16 @@ alloc_failed:
}
struct trace_lock_handler {
- int (*acquire_event)(struct perf_evsel *evsel,
+ int (*acquire_event)(struct evsel *evsel,
struct perf_sample *sample);
- int (*acquired_event)(struct perf_evsel *evsel,
+ int (*acquired_event)(struct evsel *evsel,
struct perf_sample *sample);
- int (*contended_event)(struct perf_evsel *evsel,
+ int (*contended_event)(struct evsel *evsel,
struct perf_sample *sample);
- int (*release_event)(struct perf_evsel *evsel,
+ int (*release_event)(struct evsel *evsel,
struct perf_sample *sample);
};
@@ -396,7 +396,7 @@ enum acquire_flags {
READ_LOCK = 2,
};
-static int report_lock_acquire_event(struct perf_evsel *evsel,
+static int report_lock_acquire_event(struct evsel *evsel,
struct perf_sample *sample)
{
void *addr;
@@ -454,7 +454,7 @@ broken:
/* broken lock sequence, discard it */
ls->discard = 1;
bad_hist[BROKEN_ACQUIRE]++;
- list_del(&seq->list);
+ list_del_init(&seq->list);
free(seq);
goto end;
default:
@@ -468,7 +468,7 @@ end:
return 0;
}
-static int report_lock_acquired_event(struct perf_evsel *evsel,
+static int report_lock_acquired_event(struct evsel *evsel,
struct perf_sample *sample)
{
void *addr;
@@ -515,7 +515,7 @@ static int report_lock_acquired_event(struct perf_evsel *evsel,
/* broken lock sequence, discard it */
ls->discard = 1;
bad_hist[BROKEN_ACQUIRED]++;
- list_del(&seq->list);
+ list_del_init(&seq->list);
free(seq);
goto end;
default:
@@ -531,7 +531,7 @@ end:
return 0;
}
-static int report_lock_contended_event(struct perf_evsel *evsel,
+static int report_lock_contended_event(struct evsel *evsel,
struct perf_sample *sample)
{
void *addr;
@@ -570,7 +570,7 @@ static int report_lock_contended_event(struct perf_evsel *evsel,
/* broken lock sequence, discard it */
ls->discard = 1;
bad_hist[BROKEN_CONTENDED]++;
- list_del(&seq->list);
+ list_del_init(&seq->list);
free(seq);
goto end;
default:
@@ -586,7 +586,7 @@ end:
return 0;
}
-static int report_lock_release_event(struct perf_evsel *evsel,
+static int report_lock_release_event(struct evsel *evsel,
struct perf_sample *sample)
{
void *addr;
@@ -639,7 +639,7 @@ static int report_lock_release_event(struct perf_evsel *evsel,
ls->nr_release++;
free_seq:
- list_del(&seq->list);
+ list_del_init(&seq->list);
free(seq);
end:
return 0;
@@ -656,7 +656,7 @@ static struct trace_lock_handler report_lock_ops = {
static struct trace_lock_handler *trace_handler;
-static int perf_evsel__process_lock_acquire(struct perf_evsel *evsel,
+static int perf_evsel__process_lock_acquire(struct evsel *evsel,
struct perf_sample *sample)
{
if (trace_handler->acquire_event)
@@ -664,7 +664,7 @@ static int perf_evsel__process_lock_acquire(struct perf_evsel *evsel,
return 0;
}
-static int perf_evsel__process_lock_acquired(struct perf_evsel *evsel,
+static int perf_evsel__process_lock_acquired(struct evsel *evsel,
struct perf_sample *sample)
{
if (trace_handler->acquired_event)
@@ -672,7 +672,7 @@ static int perf_evsel__process_lock_acquired(struct perf_evsel *evsel,
return 0;
}
-static int perf_evsel__process_lock_contended(struct perf_evsel *evsel,
+static int perf_evsel__process_lock_contended(struct evsel *evsel,
struct perf_sample *sample)
{
if (trace_handler->contended_event)
@@ -680,7 +680,7 @@ static int perf_evsel__process_lock_contended(struct perf_evsel *evsel,
return 0;
}
-static int perf_evsel__process_lock_release(struct perf_evsel *evsel,
+static int perf_evsel__process_lock_release(struct evsel *evsel,
struct perf_sample *sample)
{
if (trace_handler->release_event)
@@ -806,13 +806,13 @@ static int dump_info(void)
return rc;
}
-typedef int (*tracepoint_handler)(struct perf_evsel *evsel,
+typedef int (*tracepoint_handler)(struct evsel *evsel,
struct perf_sample *sample);
static int process_sample_event(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine)
{
int err = 0;
@@ -847,7 +847,7 @@ static void sort_result(void)
}
}
-static const struct perf_evsel_str_handler lock_tracepoints[] = {
+static const struct evsel_str_handler lock_tracepoints[] = {
{ "lock:lock_acquire", perf_evsel__process_lock_acquire, }, /* CONFIG_LOCKDEP */
{ "lock:lock_acquired", perf_evsel__process_lock_acquired, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */
{ "lock:lock_contended", perf_evsel__process_lock_contended, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c
index f45c8b502f63..27d2bde943a8 100644
--- a/tools/perf/builtin-mem.c
+++ b/tools/perf/builtin-mem.c
@@ -11,8 +11,10 @@
#include "util/tool.h"
#include "util/session.h"
#include "util/data.h"
+#include "util/map_symbol.h"
#include "util/mem-events.h"
#include "util/debug.h"
+#include "util/dso.h"
#include "util/map.h"
#include "util/symbol.h"
@@ -230,7 +232,7 @@ out_put:
static int process_sample_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel __maybe_unused,
+ struct evsel *evsel __maybe_unused,
struct machine *machine)
{
return dump_raw_samples(tool, event, sample, machine);
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 46d3c2deeb40..26bc5923e6b5 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* builtin-probe.c
*
* Builtin probe command: Set up probe events by C expression
*
* Written by Masami Hiramatsu <mhiramat@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
*/
#include <sys/utsname.h>
#include <sys/types.h>
@@ -30,18 +16,19 @@
#include <stdlib.h>
#include <string.h>
-#include "perf.h"
#include "builtin.h"
#include "namespaces.h"
-#include "util/util.h"
+#include "util/build-id.h"
#include "util/strlist.h"
#include "util/strfilter.h"
-#include "util/symbol.h"
+#include "util/symbol_conf.h"
#include "util/debug.h"
#include <subcmd/parse-options.h>
#include "util/probe-finder.h"
#include "util/probe-event.h"
#include "util/probe-file.h"
+#include <linux/string.h>
+#include <linux/zalloc.h>
#define DEFAULT_VAR_FILTER "!__k???tab_* & !__crc_*"
#define DEFAULT_FUNC_FILTER "!_*"
@@ -712,6 +699,16 @@ __cmd_probe(int argc, const char **argv)
ret = perf_add_probe_events(params.events, params.nevents);
if (ret < 0) {
+
+ /*
+ * When perf_add_probe_events() fails it calls
+ * cleanup_perf_probe_events(pevs, npevs), i.e.
+ * cleanup_perf_probe_events(params.events, params.nevents), which
+ * will call clear_perf_probe_event(), so set nevents to zero
+ * to avoid cleanup_params() to call clear_perf_probe_event() again
+ * on the same pevs.
+ */
+ params.nevents = 0;
pr_err_with_code(" Error: Failed to add events.", ret);
return ret;
}
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index e2c3a585a61e..1447004eee8a 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -8,10 +8,7 @@
*/
#include "builtin.h"
-#include "perf.h"
-
#include "util/build-id.h"
-#include "util/util.h"
#include <subcmd/parse-options.h>
#include "util/parse-events.h"
#include "util/config.h"
@@ -23,9 +20,11 @@
#include "util/evlist.h"
#include "util/evsel.h"
#include "util/debug.h"
+#include "util/target.h"
#include "util/session.h"
#include "util/tool.h"
#include "util/symbol.h"
+#include "util/record.h"
#include "util/cpumap.h"
#include "util/thread_map.h"
#include "util/data.h"
@@ -43,6 +42,7 @@
#include "util/units.h"
#include "util/bpf-event.h"
#include "asm/bug.h"
+#include "perf.h"
#include <errno.h>
#include <inttypes.h>
@@ -53,7 +53,9 @@
#include <signal.h>
#include <sys/mman.h>
#include <sys/wait.h>
+#include <linux/string.h>
#include <linux/time64.h>
+#include <linux/zalloc.h>
struct switch_output {
bool enabled;
@@ -73,7 +75,7 @@ struct record {
u64 bytes_written;
struct perf_data data;
struct auxtrace_record *itr;
- struct perf_evlist *evlist;
+ struct evlist *evlist;
struct perf_session *session;
int realtime_prio;
bool no_buildid;
@@ -346,7 +348,7 @@ static void record__aio_set_pos(int trace_fd, off_t pos)
static void record__aio_mmap_read_sync(struct record *rec)
{
int i;
- struct perf_evlist *evlist = rec->evlist;
+ struct evlist *evlist = rec->evlist;
struct perf_mmap *maps = evlist->mmap;
if (!record__aio_enabled(rec))
@@ -613,19 +615,35 @@ out:
return rc;
}
-static void record__read_auxtrace_snapshot(struct record *rec)
+static void record__read_auxtrace_snapshot(struct record *rec, bool on_exit)
{
pr_debug("Recording AUX area tracing snapshot\n");
if (record__auxtrace_read_snapshot_all(rec) < 0) {
trigger_error(&auxtrace_snapshot_trigger);
} else {
- if (auxtrace_record__snapshot_finish(rec->itr))
+ if (auxtrace_record__snapshot_finish(rec->itr, on_exit))
trigger_error(&auxtrace_snapshot_trigger);
else
trigger_ready(&auxtrace_snapshot_trigger);
}
}
+static int record__auxtrace_snapshot_exit(struct record *rec)
+{
+ if (trigger_is_error(&auxtrace_snapshot_trigger))
+ return 0;
+
+ if (!auxtrace_record__snapshot_started &&
+ auxtrace_record__snapshot_start(rec->itr))
+ return -1;
+
+ record__read_auxtrace_snapshot(rec, true);
+ if (trigger_is_error(&auxtrace_snapshot_trigger))
+ return -1;
+
+ return 0;
+}
+
static int record__auxtrace_init(struct record *rec)
{
int err;
@@ -654,7 +672,8 @@ int record__auxtrace_mmap_read(struct record *rec __maybe_unused,
}
static inline
-void record__read_auxtrace_snapshot(struct record *rec __maybe_unused)
+void record__read_auxtrace_snapshot(struct record *rec __maybe_unused,
+ bool on_exit __maybe_unused)
{
}
@@ -664,6 +683,12 @@ int auxtrace_record__snapshot_start(struct auxtrace_record *itr __maybe_unused)
return 0;
}
+static inline
+int record__auxtrace_snapshot_exit(struct record *rec __maybe_unused)
+{
+ return 0;
+}
+
static int record__auxtrace_init(struct record *rec __maybe_unused)
{
return 0;
@@ -672,7 +697,7 @@ static int record__auxtrace_init(struct record *rec __maybe_unused)
#endif
static int record__mmap_evlist(struct record *rec,
- struct perf_evlist *evlist)
+ struct evlist *evlist)
{
struct record_opts *opts = &rec->opts;
char msg[512];
@@ -713,8 +738,8 @@ static int record__mmap(struct record *rec)
static int record__open(struct record *rec)
{
char msg[BUFSIZ];
- struct perf_evsel *pos;
- struct perf_evlist *evlist = rec->evlist;
+ struct evsel *pos;
+ struct evlist *evlist = rec->evlist;
struct perf_session *session = rec->session;
struct record_opts *opts = &rec->opts;
int rc = 0;
@@ -732,14 +757,14 @@ static int record__open(struct record *rec)
pos->tracking = 0;
pos = perf_evlist__last(evlist);
pos->tracking = 1;
- pos->attr.enable_on_exec = 1;
+ pos->core.attr.enable_on_exec = 1;
}
perf_evlist__config(evlist, opts, &callchain_param);
evlist__for_each_entry(evlist, pos) {
try_again:
- if (perf_evsel__open(pos, pos->cpus, pos->threads) < 0) {
+ if (evsel__open(pos, pos->core.cpus, pos->core.threads) < 0) {
if (perf_evsel__fallback(pos, errno, msg, sizeof(msg))) {
if (verbose > 0)
ui__warning("%s\n", msg);
@@ -782,7 +807,7 @@ out:
static int process_sample_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine)
{
struct record *rec = container_of(tool, struct record, tool);
@@ -875,7 +900,7 @@ static void record__adjust_affinity(struct record *rec, struct perf_mmap *map)
static size_t process_comp_header(void *record, size_t increment)
{
- struct compressed_event *event = record;
+ struct perf_record_compressed *event = record;
size_t size = sizeof(*event);
if (increment) {
@@ -893,7 +918,7 @@ static size_t zstd_compress(struct perf_session *session, void *dst, size_t dst_
void *src, size_t src_size)
{
size_t compressed;
- size_t max_record_size = PERF_SAMPLE_MAX_SIZE - sizeof(struct compressed_event) - 1;
+ size_t max_record_size = PERF_SAMPLE_MAX_SIZE - sizeof(struct perf_record_compressed) - 1;
compressed = zstd_compress_stream_to_records(&session->zstd_data, dst, dst_size, src, src_size,
max_record_size, process_comp_header);
@@ -904,7 +929,7 @@ static size_t zstd_compress(struct perf_session *session, void *dst, size_t dst_
return compressed;
}
-static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evlist,
+static int record__mmap_read_evlist(struct record *rec, struct evlist *evlist,
bool overwrite, bool synch)
{
u64 bytes_written = rec->bytes_written;
@@ -1002,7 +1027,7 @@ static void record__init_features(struct record *rec)
if (rec->no_buildid)
perf_header__clear_feat(&session->header, HEADER_BUILD_ID);
- if (!have_tracepoints(&rec->evlist->entries))
+ if (!have_tracepoints(&rec->evlist->core.entries))
perf_header__clear_feat(&session->header, HEADER_TRACING_DATA);
if (!rec->opts.branch_stack)
@@ -1047,7 +1072,7 @@ record__finish_output(struct record *rec)
static int record__synthesize_workload(struct record *rec, bool tail)
{
int err;
- struct thread_map *thread_map;
+ struct perf_thread_map *thread_map;
if (rec->opts.tail_synthesize != tail)
return 0;
@@ -1060,7 +1085,7 @@ static int record__synthesize_workload(struct record *rec, bool tail)
process_synthesized_event,
&rec->session->machines.host,
rec->opts.sample_address);
- thread_map__put(thread_map);
+ perf_thread_map__put(thread_map);
return err;
}
@@ -1110,7 +1135,7 @@ record__switch_output(struct record *rec, bool at_exit)
rec->switch_output.cur_file = n;
if (rec->switch_output.filenames[n]) {
remove(rec->switch_output.filenames[n]);
- free(rec->switch_output.filenames[n]);
+ zfree(&rec->switch_output.filenames[n]);
}
rec->switch_output.filenames[n] = new_filename;
} else {
@@ -1165,7 +1190,7 @@ perf_event__synth_time_conv(const struct perf_event_mmap_page *pc __maybe_unused
}
static const struct perf_event_mmap_page *
-perf_evlist__pick_pc(struct perf_evlist *evlist)
+perf_evlist__pick_pc(struct evlist *evlist)
{
if (evlist) {
if (evlist->mmap && evlist->mmap[0].base)
@@ -1218,7 +1243,7 @@ static int record__synthesize(struct record *rec, bool tail)
return err;
}
- if (have_tracepoints(&rec->evlist->entries)) {
+ if (have_tracepoints(&rec->evlist->core.entries)) {
/*
* FIXME err <= 0 here actually means that
* there were no tracepoints so its not really
@@ -1275,7 +1300,7 @@ static int record__synthesize(struct record *rec, bool tail)
if (err)
goto out;
- err = perf_event__synthesize_thread_map2(&rec->tool, rec->evlist->threads,
+ err = perf_event__synthesize_thread_map2(&rec->tool, rec->evlist->core.threads,
process_synthesized_event,
NULL);
if (err < 0) {
@@ -1283,7 +1308,7 @@ static int record__synthesize(struct record *rec, bool tail)
return err;
}
- err = perf_event__synthesize_cpu_map(&rec->tool, rec->evlist->cpus,
+ err = perf_event__synthesize_cpu_map(&rec->tool, rec->evlist->core.cpus,
process_synthesized_event, NULL);
if (err < 0) {
pr_err("Couldn't synthesize cpu map.\n");
@@ -1295,7 +1320,7 @@ static int record__synthesize(struct record *rec, bool tail)
if (err < 0)
pr_warning("Couldn't synthesize bpf events.\n");
- err = __machine__synthesize_threads(machine, tool, &opts->target, rec->evlist->threads,
+ err = __machine__synthesize_threads(machine, tool, &opts->target, rec->evlist->core.threads,
process_synthesized_event, opts->sample_address,
1);
out:
@@ -1313,7 +1338,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
struct perf_data *data = &rec->data;
struct perf_session *session;
bool disabled = false, draining = false;
- struct perf_evlist *sb_evlist = NULL;
+ struct evlist *sb_evlist = NULL;
int fd;
float ratio = 0;
@@ -1375,7 +1400,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
* because we synthesize event name through the pipe
* and need the id for that.
*/
- if (data->is_pipe && rec->evlist->nr_entries == 1)
+ if (data->is_pipe && rec->evlist->core.nr_entries == 1)
rec->opts.sample_id = true;
if (record__open(rec) != 0) {
@@ -1453,7 +1478,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
* so don't spoil it by prematurely enabling them.
*/
if (!target__none(&opts->target) && !opts->initial_delay)
- perf_evlist__enable(rec->evlist);
+ evlist__enable(rec->evlist);
/*
* Let the child rip
@@ -1506,7 +1531,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
if (opts->initial_delay) {
usleep(opts->initial_delay * USEC_PER_MSEC);
- perf_evlist__enable(rec->evlist);
+ evlist__enable(rec->evlist);
}
trigger_ready(&auxtrace_snapshot_trigger);
@@ -1536,7 +1561,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
if (auxtrace_record__snapshot_started) {
auxtrace_record__snapshot_started = 0;
if (!trigger_is_error(&auxtrace_snapshot_trigger))
- record__read_auxtrace_snapshot(rec);
+ record__read_auxtrace_snapshot(rec, false);
if (trigger_is_error(&auxtrace_snapshot_trigger)) {
pr_err("AUX area tracing snapshot failed\n");
err = -1;
@@ -1605,13 +1630,17 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
*/
if (done && !disabled && !target__none(&opts->target)) {
trigger_off(&auxtrace_snapshot_trigger);
- perf_evlist__disable(rec->evlist);
+ evlist__disable(rec->evlist);
disabled = true;
}
}
+
trigger_off(&auxtrace_snapshot_trigger);
trigger_off(&switch_output_trigger);
+ if (opts->auxtrace_snapshot_on_exit)
+ record__auxtrace_snapshot_exit(rec);
+
if (forks && workload_exec_errno) {
char msg[STRERR_BUFSIZE];
const char *emsg = str_error_r(workload_exec_errno, msg, sizeof(msg));
@@ -2191,6 +2220,10 @@ static struct option __record_options[] = {
OPT_BOOLEAN_FLAG(0, "all-user", &record.opts.all_user,
"Configure all used events to run in user space.",
PARSE_OPT_EXCLUSIVE),
+ OPT_BOOLEAN(0, "kernel-callchains", &record.opts.kernel_callchains,
+ "collect kernel callchains"),
+ OPT_BOOLEAN(0, "user-callchains", &record.opts.user_callchains,
+ "collect user callchains"),
OPT_STRING(0, "clang-path", &llvm_param.clang_path, "clang path",
"clang binary to use for compiling BPF scriptlets"),
OPT_STRING(0, "clang-opt", &llvm_param.clang_opt, "clang options",
@@ -2261,7 +2294,7 @@ int cmd_record(int argc, const char **argv)
CPU_ZERO(&rec->affinity_mask);
rec->opts.affinity = PERF_AFFINITY_SYS;
- rec->evlist = perf_evlist__new();
+ rec->evlist = evlist__new();
if (rec->evlist == NULL)
return -ENOMEM;
@@ -2341,7 +2374,7 @@ int cmd_record(int argc, const char **argv)
if (symbol_conf.kptr_restrict && !perf_evlist__exclude_kernel(rec->evlist))
pr_warning(
"WARNING: Kernel address maps (/proc/{kallsyms,modules}) are restricted,\n"
-"check /proc/sys/kernel/kptr_restrict.\n\n"
+"check /proc/sys/kernel/kptr_restrict and /proc/sys/kernel/perf_event_paranoid.\n\n"
"Samples in kernel functions may not be resolved if a suitable vmlinux\n"
"file is not found in the buildid cache or in the vmlinux path.\n\n"
"Samples in kernel modules won't be resolved at all.\n\n"
@@ -2382,7 +2415,7 @@ int cmd_record(int argc, const char **argv)
if (record.opts.overwrite)
record.opts.tail_synthesize = true;
- if (rec->evlist->nr_entries == 0 &&
+ if (rec->evlist->core.nr_entries == 0 &&
__perf_evlist__add_default(rec->evlist, !record.opts.no_samples) < 0) {
pr_err("Not enough memory for event selector list\n");
goto out;
@@ -2445,7 +2478,7 @@ int cmd_record(int argc, const char **argv)
err = __cmd_record(&record, argc, argv);
out:
- perf_evlist__delete(rec->evlist);
+ evlist__delete(rec->evlist);
symbol__exit();
auxtrace_record__free(rec->itr);
return err;
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 1ca533f06a4c..b18fab94d38d 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -8,16 +8,20 @@
*/
#include "builtin.h"
-#include "util/util.h"
#include "util/config.h"
#include "util/annotate.h"
#include "util/color.h"
+#include "util/dso.h"
#include <linux/list.h>
#include <linux/rbtree.h>
#include <linux/err.h>
+#include <linux/zalloc.h>
#include "util/map.h"
#include "util/symbol.h"
+#include "util/map_symbol.h"
+#include "util/mem-events.h"
+#include "util/branch.h"
#include "util/callchain.h"
#include "util/values.h"
@@ -25,8 +29,10 @@
#include "util/debug.h"
#include "util/evlist.h"
#include "util/evsel.h"
+#include "util/evswitch.h"
#include "util/header.h"
#include "util/session.h"
+#include "util/srcline.h"
#include "util/tool.h"
#include <subcmd/parse-options.h>
@@ -42,14 +48,18 @@
#include "util/auxtrace.h"
#include "util/units.h"
#include "util/branch.h"
+#include "util/util.h"
+#include "ui/ui.h"
+#include "ui/progress.h"
#include <dlfcn.h>
#include <errno.h>
#include <inttypes.h>
#include <regex.h>
-#include "sane_ctype.h"
+#include <linux/ctype.h>
#include <signal.h>
#include <linux/bitmap.h>
+#include <linux/string.h>
#include <linux/stringify.h>
#include <linux/time64.h>
#include <sys/types.h>
@@ -60,6 +70,7 @@
struct report {
struct perf_tool tool;
struct perf_session *session;
+ struct evswitch evswitch;
bool use_tui, use_gtk, use_stdio;
bool show_full_info;
bool show_threads;
@@ -128,7 +139,7 @@ static int hist_iter__report_callback(struct hist_entry_iter *iter,
int err = 0;
struct report *rep = arg;
struct hist_entry *he = iter->he;
- struct perf_evsel *evsel = iter->evsel;
+ struct evsel *evsel = iter->evsel;
struct perf_sample *sample = iter->sample;
struct mem_info *mi;
struct branch_info *bi;
@@ -172,7 +183,7 @@ static int hist_iter__branch_callback(struct hist_entry_iter *iter,
struct report *rep = arg;
struct branch_info *bi;
struct perf_sample *sample = iter->sample;
- struct perf_evsel *evsel = iter->evsel;
+ struct evsel *evsel = iter->evsel;
int err;
if (!ui__has_annotation() && !rep->symbol_ipc)
@@ -193,7 +204,7 @@ out:
}
static void setup_forced_leader(struct report *report,
- struct perf_evlist *evlist)
+ struct evlist *evlist)
{
if (report->group_set)
perf_evlist__force_leader(evlist);
@@ -208,7 +219,7 @@ static int process_feature_event(struct perf_session *session,
return perf_event__process_feature(session, event);
if (event->feat.feat_id != HEADER_LAST_FEATURE) {
- pr_err("failed: wrong feature ID: %" PRIu64 "\n",
+ pr_err("failed: wrong feature ID: %" PRI_lu64 "\n",
event->feat.feat_id);
return -1;
}
@@ -225,7 +236,7 @@ static int process_feature_event(struct perf_session *session,
static int process_sample_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine)
{
struct report *rep = container_of(tool, struct report, tool);
@@ -243,6 +254,9 @@ static int process_sample_event(struct perf_tool *tool,
return 0;
}
+ if (evswitch__discard(&rep->evswitch, evsel))
+ return 0;
+
if (machine__resolve(machine, &al, sample) < 0) {
pr_debug("problem processing %d event, skipping it.\n",
event->header.type);
@@ -292,13 +306,13 @@ out_put:
static int process_read_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine __maybe_unused)
{
struct report *rep = container_of(tool, struct report, tool);
if (rep->show_threads) {
- const char *name = evsel ? perf_evsel__name(evsel) : "unknown";
+ const char *name = perf_evsel__name(evsel);
int err = perf_read_values_add_value(&rep->show_threads_values,
event->read.pid, event->read.tid,
evsel->idx,
@@ -400,7 +414,7 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
char unit;
unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
u64 nr_events = hists->stats.total_period;
- struct perf_evsel *evsel = hists_to_evsel(hists);
+ struct evsel *evsel = hists_to_evsel(hists);
char buf[512];
size_t size = sizeof(buf);
int socked_id = hists->socket_filter;
@@ -414,7 +428,7 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
}
if (perf_evsel__is_group_event(evsel)) {
- struct perf_evsel *pos;
+ struct evsel *pos;
perf_evsel__group_desc(evsel, buf, size);
evname = buf;
@@ -436,7 +450,7 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
ret = fprintf(fp, "# Samples: %lu%c", nr_samples, unit);
if (evname != NULL) {
ret += fprintf(fp, " of event%s '%s'",
- evsel->nr_members > 1 ? "s" : "", evname);
+ evsel->core.nr_members > 1 ? "s" : "", evname);
}
if (rep->time_str)
@@ -459,11 +473,11 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
return ret + fprintf(fp, "\n#\n");
}
-static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
+static int perf_evlist__tty_browse_hists(struct evlist *evlist,
struct report *rep,
const char *help)
{
- struct perf_evsel *pos;
+ struct evsel *pos;
if (!quiet) {
fprintf(stdout, "#\n# Total Lost Samples: %" PRIu64 "\n#\n",
@@ -532,7 +546,7 @@ static void report__warn_kptr_restrict(const struct report *rep)
static int report__gtk_browse_hists(struct report *rep, const char *help)
{
- int (*hist_browser)(struct perf_evlist *evlist, const char *help,
+ int (*hist_browser)(struct evlist *evlist, const char *help,
struct hist_browser_timer *timer, float min_pcnt);
hist_browser = dlsym(perf_gtk_handle, "perf_evlist__gtk_browse_hists");
@@ -549,7 +563,7 @@ static int report__browse_hists(struct report *rep)
{
int ret;
struct perf_session *session = rep->session;
- struct perf_evlist *evlist = session->evlist;
+ struct evlist *evlist = session->evlist;
const char *help = perf_tip(system_path(TIPDIR));
if (help == NULL) {
@@ -586,7 +600,7 @@ static int report__browse_hists(struct report *rep)
static int report__collapse_hists(struct report *rep)
{
struct ui_progress prog;
- struct perf_evsel *pos;
+ struct evsel *pos;
int ret = 0;
ui_progress__init(&prog, rep->nr_entries, "Merging related events...");
@@ -623,7 +637,7 @@ static int hists__resort_cb(struct hist_entry *he, void *arg)
struct symbol *sym = he->ms.sym;
if (rep->symbol_ipc && sym && !sym->annotate2) {
- struct perf_evsel *evsel = hists_to_evsel(he->hists);
+ struct evsel *evsel = hists_to_evsel(he->hists);
symbol__annotate2(sym, he->ms.map, evsel,
&annotation__default_options, NULL);
@@ -635,7 +649,7 @@ static int hists__resort_cb(struct hist_entry *he, void *arg)
static void report__output_resort(struct report *rep)
{
struct ui_progress prog;
- struct perf_evsel *pos;
+ struct evsel *pos;
ui_progress__init(&prog, rep->nr_entries, "Sorting events for output...");
@@ -818,7 +832,7 @@ static int __cmd_report(struct report *rep)
{
int ret;
struct perf_session *session = rep->session;
- struct perf_evsel *pos;
+ struct evsel *pos;
struct perf_data *data = session->data;
signal(SIGINT, sig_handler);
@@ -941,8 +955,7 @@ parse_time_quantum(const struct option *opt, const char *arg,
pr_err("time quantum cannot be 0");
return -1;
}
- while (isspace(*end))
- end++;
+ end = skip_spaces(end);
if (*end == 0)
return 0;
if (!strcmp(end, "s")) {
@@ -1190,6 +1203,7 @@ int cmd_report(int argc, const char **argv)
OPT_CALLBACK(0, "time-quantum", &symbol_conf.time_quantum, "time (ms|us|ns|s)",
"Set time quantum for time sort key (default 100ms)",
parse_time_quantum),
+ OPTS_EVSWITCH(&report.evswitch),
OPT_END()
};
struct perf_data data = {
@@ -1258,6 +1272,10 @@ repeat:
if (session == NULL)
return -1;
+ ret = evswitch__init(&report.evswitch, session->evlist, stderr);
+ if (ret)
+ return ret;
+
if (zstd_init(&(session->zstd_data), 0) < 0)
pr_warning("Decompression initialization failed. Reported data may be incomplete.\n");
@@ -1272,6 +1290,8 @@ repeat:
has_br_stack = perf_header__has_feat(&session->header,
HEADER_BRANCH_STACK);
+ if (perf_evlist__combined_sample_type(session->evlist) & PERF_SAMPLE_STACK_USER)
+ has_br_stack = false;
setup_forced_leader(&report, session->evlist);
@@ -1428,6 +1448,10 @@ repeat:
&report.range_num);
if (ret < 0)
goto error;
+
+ itrace_synth_opts__set_time_range(&itrace_synth_opts,
+ report.ptime_range,
+ report.range_num);
}
if (session->tevent.pevent &&
@@ -1449,8 +1473,10 @@ repeat:
ret = 0;
error:
- if (report.ptime_range)
+ if (report.ptime_range) {
+ itrace_synth_opts__clear_time_range(&itrace_synth_opts);
zfree(&report.ptime_range);
+ }
zstd_fini(&(session->zstd_data));
perf_session__delete(session);
return ret;
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 275f2d92a7bf..ec96d64aec69 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -1,10 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
#include "builtin.h"
#include "perf.h"
+#include "perf-sys.h"
-#include "util/util.h"
#include "util/evlist.h"
-#include "util/cache.h"
#include "util/evsel.h"
#include "util/symbol.h"
#include "util/thread.h"
@@ -15,9 +14,11 @@
#include "util/thread_map.h"
#include "util/color.h"
#include "util/stat.h"
+#include "util/string2.h"
#include "util/callchain.h"
#include "util/time-utils.h"
+#include <subcmd/pager.h>
#include <subcmd/parse-options.h>
#include "util/trace-event.h"
@@ -25,6 +26,7 @@
#include <linux/kernel.h>
#include <linux/log2.h>
+#include <linux/zalloc.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <inttypes.h>
@@ -36,7 +38,7 @@
#include <api/fs/fs.h>
#include <linux/time64.h>
-#include "sane_ctype.h"
+#include <linux/ctype.h>
#define PR_SET_NAME 15 /* Set process name */
#define MAX_CPUS 4096
@@ -132,13 +134,13 @@ typedef int (*sort_fn_t)(struct work_atoms *, struct work_atoms *);
struct perf_sched;
struct trace_sched_handler {
- int (*switch_event)(struct perf_sched *sched, struct perf_evsel *evsel,
+ int (*switch_event)(struct perf_sched *sched, struct evsel *evsel,
struct perf_sample *sample, struct machine *machine);
- int (*runtime_event)(struct perf_sched *sched, struct perf_evsel *evsel,
+ int (*runtime_event)(struct perf_sched *sched, struct evsel *evsel,
struct perf_sample *sample, struct machine *machine);
- int (*wakeup_event)(struct perf_sched *sched, struct perf_evsel *evsel,
+ int (*wakeup_event)(struct perf_sched *sched, struct evsel *evsel,
struct perf_sample *sample, struct machine *machine);
/* PERF_RECORD_FORK event, not sched_process_fork tracepoint */
@@ -146,7 +148,7 @@ struct trace_sched_handler {
struct machine *machine);
int (*migrate_task_event)(struct perf_sched *sched,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct machine *machine);
};
@@ -158,11 +160,11 @@ struct perf_sched_map {
DECLARE_BITMAP(comp_cpus_mask, MAX_CPUS);
int *comp_cpus;
bool comp;
- struct thread_map *color_pids;
+ struct perf_thread_map *color_pids;
const char *color_pids_str;
- struct cpu_map *color_cpus;
+ struct perf_cpu_map *color_cpus;
const char *color_cpus_str;
- struct cpu_map *cpus;
+ struct perf_cpu_map *cpus;
const char *cpus_str;
};
@@ -798,7 +800,7 @@ static void test_calibrations(struct perf_sched *sched)
static int
replay_wakeup_event(struct perf_sched *sched,
- struct perf_evsel *evsel, struct perf_sample *sample,
+ struct evsel *evsel, struct perf_sample *sample,
struct machine *machine __maybe_unused)
{
const char *comm = perf_evsel__strval(evsel, sample, "comm");
@@ -819,7 +821,7 @@ replay_wakeup_event(struct perf_sched *sched,
}
static int replay_switch_event(struct perf_sched *sched,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct machine *machine __maybe_unused)
{
@@ -1092,7 +1094,7 @@ add_sched_in_event(struct work_atoms *atoms, u64 timestamp)
}
static int latency_switch_event(struct perf_sched *sched,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct machine *machine)
{
@@ -1162,7 +1164,7 @@ out_put:
}
static int latency_runtime_event(struct perf_sched *sched,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct machine *machine)
{
@@ -1197,7 +1199,7 @@ out_put:
}
static int latency_wakeup_event(struct perf_sched *sched,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct machine *machine)
{
@@ -1258,7 +1260,7 @@ out_put:
}
static int latency_migrate_task_event(struct perf_sched *sched,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct machine *machine)
{
@@ -1469,7 +1471,7 @@ again:
}
static int process_sched_wakeup_event(struct perf_tool *tool,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct machine *machine)
{
@@ -1513,7 +1515,7 @@ map__findnew_thread(struct perf_sched *sched, struct machine *machine, pid_t pid
return thread;
}
-static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
+static int map_switch_event(struct perf_sched *sched, struct evsel *evsel,
struct perf_sample *sample, struct machine *machine)
{
const u32 next_pid = perf_evsel__intval(evsel, sample, "next_pid");
@@ -1654,7 +1656,7 @@ out:
}
static int process_sched_switch_event(struct perf_tool *tool,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct machine *machine)
{
@@ -1680,7 +1682,7 @@ static int process_sched_switch_event(struct perf_tool *tool,
}
static int process_sched_runtime_event(struct perf_tool *tool,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct machine *machine)
{
@@ -1710,7 +1712,7 @@ static int perf_sched__process_fork_event(struct perf_tool *tool,
}
static int process_sched_migrate_task_event(struct perf_tool *tool,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct machine *machine)
{
@@ -1723,14 +1725,14 @@ static int process_sched_migrate_task_event(struct perf_tool *tool,
}
typedef int (*tracepoint_handler)(struct perf_tool *tool,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct machine *machine);
static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_unused,
union perf_event *event __maybe_unused,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine)
{
int err = 0;
@@ -1776,7 +1778,7 @@ static int perf_sched__process_comm(struct perf_tool *tool __maybe_unused,
static int perf_sched__read_events(struct perf_sched *sched)
{
- const struct perf_evsel_str_handler handlers[] = {
+ const struct evsel_str_handler handlers[] = {
{ "sched:sched_switch", process_sched_switch_event, },
{ "sched:sched_stat_runtime", process_sched_runtime_event, },
{ "sched:sched_wakeup", process_sched_wakeup_event, },
@@ -1838,7 +1840,7 @@ static inline void print_sched_time(unsigned long long nsecs, int width)
* returns runtime data for event, allocating memory for it the
* first time it is used.
*/
-static struct evsel_runtime *perf_evsel__get_runtime(struct perf_evsel *evsel)
+static struct evsel_runtime *perf_evsel__get_runtime(struct evsel *evsel)
{
struct evsel_runtime *r = evsel->priv;
@@ -1853,7 +1855,7 @@ static struct evsel_runtime *perf_evsel__get_runtime(struct perf_evsel *evsel)
/*
* save last time event was seen per cpu
*/
-static void perf_evsel__save_time(struct perf_evsel *evsel,
+static void perf_evsel__save_time(struct evsel *evsel,
u64 timestamp, u32 cpu)
{
struct evsel_runtime *r = perf_evsel__get_runtime(evsel);
@@ -1880,7 +1882,7 @@ static void perf_evsel__save_time(struct perf_evsel *evsel,
}
/* returns last time this event was seen on the given cpu */
-static u64 perf_evsel__get_time(struct perf_evsel *evsel, u32 cpu)
+static u64 perf_evsel__get_time(struct evsel *evsel, u32 cpu)
{
struct evsel_runtime *r = perf_evsel__get_runtime(evsel);
@@ -1987,7 +1989,7 @@ static char task_state_char(struct thread *thread, int state)
}
static void timehist_print_sample(struct perf_sched *sched,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct addr_location *al,
struct thread *thread,
@@ -2120,7 +2122,7 @@ static void timehist_update_runtime_stats(struct thread_runtime *r,
}
static bool is_idle_sample(struct perf_sample *sample,
- struct perf_evsel *evsel)
+ struct evsel *evsel)
{
/* pid 0 == swapper == idle task */
if (strcmp(perf_evsel__name(evsel), "sched:sched_switch") == 0)
@@ -2131,7 +2133,7 @@ static bool is_idle_sample(struct perf_sample *sample,
static void save_task_callchain(struct perf_sched *sched,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine)
{
struct callchain_cursor *cursor = &callchain_cursor;
@@ -2285,7 +2287,7 @@ static void save_idle_callchain(struct perf_sched *sched,
static struct thread *timehist_get_thread(struct perf_sched *sched,
struct perf_sample *sample,
struct machine *machine,
- struct perf_evsel *evsel)
+ struct evsel *evsel)
{
struct thread *thread;
@@ -2331,7 +2333,7 @@ static struct thread *timehist_get_thread(struct perf_sched *sched,
static bool timehist_skip_sample(struct perf_sched *sched,
struct thread *thread,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample)
{
bool rc = false;
@@ -2353,7 +2355,7 @@ static bool timehist_skip_sample(struct perf_sched *sched,
}
static void timehist_print_wakeup_event(struct perf_sched *sched,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct machine *machine,
struct thread *awakened)
@@ -2388,7 +2390,7 @@ static void timehist_print_wakeup_event(struct perf_sched *sched,
static int timehist_sched_wakeup_event(struct perf_tool *tool,
union perf_event *event __maybe_unused,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct machine *machine)
{
@@ -2418,7 +2420,7 @@ static int timehist_sched_wakeup_event(struct perf_tool *tool,
}
static void timehist_print_migration_event(struct perf_sched *sched,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct machine *machine,
struct thread *migrated)
@@ -2472,7 +2474,7 @@ static void timehist_print_migration_event(struct perf_sched *sched,
static int timehist_migrate_task_event(struct perf_tool *tool,
union perf_event *event __maybe_unused,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct machine *machine)
{
@@ -2500,7 +2502,7 @@ static int timehist_migrate_task_event(struct perf_tool *tool,
static int timehist_sched_change_event(struct perf_tool *tool,
union perf_event *event,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct machine *machine)
{
@@ -2626,7 +2628,7 @@ out:
static int timehist_sched_switch_event(struct perf_tool *tool,
union perf_event *event,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct machine *machine __maybe_unused)
{
@@ -2642,7 +2644,7 @@ static int process_lost(struct perf_tool *tool __maybe_unused,
timestamp__scnprintf_usec(sample->time, tstr, sizeof(tstr));
printf("%15s ", tstr);
- printf("lost %" PRIu64 " events on cpu %d\n", event->lost.lost, sample->cpu);
+ printf("lost %" PRI_lu64 " events on cpu %d\n", event->lost.lost, sample->cpu);
return 0;
}
@@ -2896,14 +2898,14 @@ static void timehist_print_summary(struct perf_sched *sched,
typedef int (*sched_handler)(struct perf_tool *tool,
union perf_event *event,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct machine *machine);
static int perf_timehist__process_sample(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine)
{
struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
@@ -2923,12 +2925,12 @@ static int perf_timehist__process_sample(struct perf_tool *tool,
}
static int timehist_check_attr(struct perf_sched *sched,
- struct perf_evlist *evlist)
+ struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct evsel_runtime *er;
- list_for_each_entry(evsel, &evlist->entries, node) {
+ list_for_each_entry(evsel, &evlist->core.entries, core.node) {
er = perf_evsel__get_runtime(evsel);
if (er == NULL) {
pr_err("Failed to allocate memory for evsel runtime data\n");
@@ -2947,12 +2949,12 @@ static int timehist_check_attr(struct perf_sched *sched,
static int perf_sched__timehist(struct perf_sched *sched)
{
- const struct perf_evsel_str_handler handlers[] = {
+ const struct evsel_str_handler handlers[] = {
{ "sched:sched_switch", timehist_sched_switch_event, },
{ "sched:sched_wakeup", timehist_sched_wakeup_event, },
{ "sched:sched_wakeup_new", timehist_sched_wakeup_event, },
};
- const struct perf_evsel_str_handler migrate_handlers[] = {
+ const struct evsel_str_handler migrate_handlers[] = {
{ "sched:sched_migrate_task", timehist_migrate_task_event, },
};
struct perf_data data = {
@@ -2962,7 +2964,7 @@ static int perf_sched__timehist(struct perf_sched *sched)
};
struct perf_session *session;
- struct perf_evlist *evlist;
+ struct evlist *evlist;
int err = -1;
/*
@@ -3169,7 +3171,7 @@ static int perf_sched__lat(struct perf_sched *sched)
static int setup_map_cpus(struct perf_sched *sched)
{
- struct cpu_map *map;
+ struct perf_cpu_map *map;
sched->max_cpu = sysconf(_SC_NPROCESSORS_CONF);
@@ -3182,7 +3184,7 @@ static int setup_map_cpus(struct perf_sched *sched)
if (!sched->map.cpus_str)
return 0;
- map = cpu_map__new(sched->map.cpus_str);
+ map = perf_cpu_map__new(sched->map.cpus_str);
if (!map) {
pr_err("failed to get cpus map from %s\n", sched->map.cpus_str);
return -1;
@@ -3194,7 +3196,7 @@ static int setup_map_cpus(struct perf_sched *sched)
static int setup_color_pids(struct perf_sched *sched)
{
- struct thread_map *map;
+ struct perf_thread_map *map;
if (!sched->map.color_pids_str)
return 0;
@@ -3211,12 +3213,12 @@ static int setup_color_pids(struct perf_sched *sched)
static int setup_color_cpus(struct perf_sched *sched)
{
- struct cpu_map *map;
+ struct perf_cpu_map *map;
if (!sched->map.color_cpus_str)
return 0;
- map = cpu_map__new(sched->map.color_cpus_str);
+ map = perf_cpu_map__new(sched->map.color_cpus_str);
if (!map) {
pr_err("failed to get thread map from %s\n", sched->map.color_cpus_str);
return -1;
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 61cfd8f70989..e079b34201f2 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
#include "builtin.h"
-#include "perf.h"
-#include "util/cache.h"
+#include "util/counts.h"
#include "util/debug.h"
+#include "util/dso.h"
#include <subcmd/exec-cmd.h>
#include "util/header.h"
#include <subcmd/parse-options.h>
@@ -11,12 +11,13 @@
#include "util/session.h"
#include "util/tool.h"
#include "util/map.h"
+#include "util/srcline.h"
#include "util/symbol.h"
#include "util/thread.h"
#include "util/trace-event.h"
-#include "util/util.h"
#include "util/evlist.h"
#include "util/evsel.h"
+#include "util/evswitch.h"
#include "util/sort.h"
#include "util/data.h"
#include "util/auxtrace.h"
@@ -28,12 +29,14 @@
#include "util/thread-stack.h"
#include "util/time-utils.h"
#include "util/path.h"
+#include "ui/ui.h"
#include "print_binary.h"
#include "archinsn.h"
#include <linux/bitmap.h>
#include <linux/kernel.h>
#include <linux/stringify.h>
#include <linux/time64.h>
+#include <linux/zalloc.h>
#include <sys/utsname.h>
#include "asm/bug.h"
#include "util/mem-events.h"
@@ -48,8 +51,12 @@
#include <fcntl.h>
#include <unistd.h>
#include <subcmd/pager.h>
+#include <perf/evlist.h>
+#include "util/record.h"
+#include "util/util.h"
+#include "perf.h"
-#include "sane_ctype.h"
+#include <linux/ctype.h>
static char const *script_name;
static char const *generate_script_lang;
@@ -102,6 +109,7 @@ enum perf_output_field {
PERF_OUTPUT_METRIC = 1U << 28,
PERF_OUTPUT_MISC = 1U << 29,
PERF_OUTPUT_SRCCODE = 1U << 30,
+ PERF_OUTPUT_IPC = 1U << 31,
};
struct output_option {
@@ -139,6 +147,7 @@ struct output_option {
{.str = "metric", .field = PERF_OUTPUT_METRIC},
{.str = "misc", .field = PERF_OUTPUT_MISC},
{.str = "srccode", .field = PERF_OUTPUT_SRCCODE},
+ {.str = "ipc", .field = PERF_OUTPUT_IPC},
};
enum {
@@ -240,7 +249,7 @@ static struct {
},
};
-struct perf_evsel_script {
+struct evsel_script {
char *filename;
FILE *fp;
u64 samples;
@@ -249,15 +258,15 @@ struct perf_evsel_script {
int gnum;
};
-static inline struct perf_evsel_script *evsel_script(struct perf_evsel *evsel)
+static inline struct evsel_script *evsel_script(struct evsel *evsel)
{
- return (struct perf_evsel_script *)evsel->priv;
+ return (struct evsel_script *)evsel->priv;
}
-static struct perf_evsel_script *perf_evsel_script__new(struct perf_evsel *evsel,
+static struct evsel_script *perf_evsel_script__new(struct evsel *evsel,
struct perf_data *data)
{
- struct perf_evsel_script *es = zalloc(sizeof(*es));
+ struct evsel_script *es = zalloc(sizeof(*es));
if (es != NULL) {
if (asprintf(&es->filename, "%s.%s.dump", data->file.path, perf_evsel__name(evsel)) < 0)
@@ -275,7 +284,7 @@ out_free:
return NULL;
}
-static void perf_evsel_script__delete(struct perf_evsel_script *es)
+static void perf_evsel_script__delete(struct evsel_script *es)
{
zfree(&es->filename);
fclose(es->fp);
@@ -283,7 +292,7 @@ static void perf_evsel_script__delete(struct perf_evsel_script *es)
free(es);
}
-static int perf_evsel_script__fprintf(struct perf_evsel_script *es, FILE *fp)
+static int perf_evsel_script__fprintf(struct evsel_script *es, FILE *fp)
{
struct stat st;
@@ -338,12 +347,12 @@ static const char *output_field2str(enum perf_output_field field)
#define PRINT_FIELD(x) (output[output_type(attr->type)].fields & PERF_OUTPUT_##x)
-static int perf_evsel__do_check_stype(struct perf_evsel *evsel,
+static int perf_evsel__do_check_stype(struct evsel *evsel,
u64 sample_type, const char *sample_msg,
enum perf_output_field field,
bool allow_user_set)
{
- struct perf_event_attr *attr = &evsel->attr;
+ struct perf_event_attr *attr = &evsel->core.attr;
int type = output_type(attr->type);
const char *evname;
@@ -370,7 +379,7 @@ static int perf_evsel__do_check_stype(struct perf_evsel *evsel,
return 0;
}
-static int perf_evsel__check_stype(struct perf_evsel *evsel,
+static int perf_evsel__check_stype(struct evsel *evsel,
u64 sample_type, const char *sample_msg,
enum perf_output_field field)
{
@@ -378,10 +387,10 @@ static int perf_evsel__check_stype(struct perf_evsel *evsel,
false);
}
-static int perf_evsel__check_attr(struct perf_evsel *evsel,
+static int perf_evsel__check_attr(struct evsel *evsel,
struct perf_session *session)
{
- struct perf_event_attr *attr = &evsel->attr;
+ struct perf_event_attr *attr = &evsel->core.attr;
bool allow_user_set;
if (perf_header__has_feat(&session->header, HEADER_STAT))
@@ -416,7 +425,7 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
return -EINVAL;
if (PRINT_FIELD(SYM) &&
- !(evsel->attr.sample_type & (PERF_SAMPLE_IP|PERF_SAMPLE_ADDR))) {
+ !(evsel->core.attr.sample_type & (PERF_SAMPLE_IP|PERF_SAMPLE_ADDR))) {
pr_err("Display of symbols requested but neither sample IP nor "
"sample address\navailable. Hence, no addresses to convert "
"to symbols.\n");
@@ -428,7 +437,7 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
return -EINVAL;
}
if (PRINT_FIELD(DSO) &&
- !(evsel->attr.sample_type & (PERF_SAMPLE_IP|PERF_SAMPLE_ADDR))) {
+ !(evsel->core.attr.sample_type & (PERF_SAMPLE_IP|PERF_SAMPLE_ADDR))) {
pr_err("Display of DSO requested but no address to convert.\n");
return -EINVAL;
}
@@ -505,7 +514,7 @@ static void set_print_ip_opts(struct perf_event_attr *attr)
static int perf_session__check_output_opt(struct perf_session *session)
{
unsigned int j;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
for (j = 0; j < OUTPUT_TYPE_MAX; ++j) {
evsel = perf_session__find_first_evtype(session, attr_type(j));
@@ -529,7 +538,7 @@ static int perf_session__check_output_opt(struct perf_session *session)
if (evsel == NULL)
continue;
- set_print_ip_opts(&evsel->attr);
+ set_print_ip_opts(&evsel->core.attr);
}
if (!no_callchain) {
@@ -556,7 +565,7 @@ static int perf_session__check_output_opt(struct perf_session *session)
j = PERF_TYPE_TRACEPOINT;
evlist__for_each_entry(session->evlist, evsel) {
- if (evsel->attr.type != j)
+ if (evsel->core.attr.type != j)
continue;
if (evsel__has_callchain(evsel)) {
@@ -564,7 +573,7 @@ static int perf_session__check_output_opt(struct perf_session *session)
output[j].fields |= PERF_OUTPUT_SYM;
output[j].fields |= PERF_OUTPUT_SYMOFFSET;
output[j].fields |= PERF_OUTPUT_DSO;
- set_print_ip_opts(&evsel->attr);
+ set_print_ip_opts(&evsel->core.attr);
goto out;
}
}
@@ -612,10 +621,10 @@ static int perf_sample__fprintf_uregs(struct perf_sample *sample,
static int perf_sample__fprintf_start(struct perf_sample *sample,
struct thread *thread,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
u32 type, FILE *fp)
{
- struct perf_event_attr *attr = &evsel->attr;
+ struct perf_event_attr *attr = &evsel->core.attr;
unsigned long secs;
unsigned long long nsecs;
int printed = 0;
@@ -1057,7 +1066,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
printed += ip__fprintf_sym(ip, thread, x.cpumode, x.cpu, &lastsym, attr, fp);
if (ip == end) {
- printed += ip__fprintf_jump(ip, &br->entries[i], &x, buffer + off, len - off, insn, fp,
+ printed += ip__fprintf_jump(ip, &br->entries[i], &x, buffer + off, len - off, ++insn, fp,
&total_cycles);
if (PRINT_FIELD(SRCCODE))
printed += print_srccode(thread, x.cpumode, ip);
@@ -1160,13 +1169,13 @@ out:
}
static const char *resolve_branch_sym(struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct thread *thread,
struct addr_location *al,
u64 *ip)
{
struct addr_location addr_al;
- struct perf_event_attr *attr = &evsel->attr;
+ struct perf_event_attr *attr = &evsel->core.attr;
const char *name = NULL;
if (sample->flags & (PERF_IP_FLAG_CALL | PERF_IP_FLAG_TRACE_BEGIN)) {
@@ -1189,11 +1198,11 @@ static const char *resolve_branch_sym(struct perf_sample *sample,
}
static int perf_sample__fprintf_callindent(struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct thread *thread,
struct addr_location *al, FILE *fp)
{
- struct perf_event_attr *attr = &evsel->attr;
+ struct perf_event_attr *attr = &evsel->core.attr;
size_t depth = thread_stack__depth(thread, sample->cpu);
const char *name = NULL;
static int spacing;
@@ -1268,13 +1277,27 @@ static int perf_sample__fprintf_insn(struct perf_sample *sample,
return printed;
}
+static int perf_sample__fprintf_ipc(struct perf_sample *sample,
+ struct perf_event_attr *attr, FILE *fp)
+{
+ unsigned int ipc;
+
+ if (!PRINT_FIELD(IPC) || !sample->cyc_cnt || !sample->insn_cnt)
+ return 0;
+
+ ipc = (sample->insn_cnt * 100) / sample->cyc_cnt;
+
+ return fprintf(fp, " \t IPC: %u.%02u (%" PRIu64 "/%" PRIu64 ") ",
+ ipc / 100, ipc % 100, sample->insn_cnt, sample->cyc_cnt);
+}
+
static int perf_sample__fprintf_bts(struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct thread *thread,
struct addr_location *al,
struct machine *machine, FILE *fp)
{
- struct perf_event_attr *attr = &evsel->attr;
+ struct perf_event_attr *attr = &evsel->core.attr;
unsigned int type = output_type(attr->type);
bool print_srcline_last = false;
int printed = 0;
@@ -1306,12 +1329,14 @@ static int perf_sample__fprintf_bts(struct perf_sample *sample,
/* print branch_to information */
if (PRINT_FIELD(ADDR) ||
- ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) &&
+ ((evsel->core.attr.sample_type & PERF_SAMPLE_ADDR) &&
!output[type].user_set)) {
printed += fprintf(fp, " => ");
printed += perf_sample__fprintf_addr(sample, thread, attr, fp);
}
+ printed += perf_sample__fprintf_ipc(sample, attr, fp);
+
if (print_srcline_last)
printed += map__fprintf_srcline(al->map, al->addr, "\n ", fp);
@@ -1575,9 +1600,9 @@ static int perf_sample__fprintf_synth_cbr(struct perf_sample *sample, FILE *fp)
}
static int perf_sample__fprintf_synth(struct perf_sample *sample,
- struct perf_evsel *evsel, FILE *fp)
+ struct evsel *evsel, FILE *fp)
{
- switch (evsel->attr.config) {
+ switch (evsel->core.attr.config) {
case PERF_SYNTH_INTEL_PTWRITE:
return perf_sample__fprintf_synth_ptwrite(sample, fp);
case PERF_SYNTH_INTEL_MWAIT:
@@ -1606,10 +1631,12 @@ struct perf_script {
bool show_namespace_events;
bool show_lost_events;
bool show_round_events;
+ bool show_bpf_events;
bool allocated;
bool per_event_dump;
- struct cpu_map *cpus;
- struct thread_map *threads;
+ struct evswitch evswitch;
+ struct perf_cpu_map *cpus;
+ struct perf_thread_map *threads;
int name_width;
const char *time_str;
struct perf_time_interval *ptime_range;
@@ -1617,9 +1644,9 @@ struct perf_script {
int range_num;
};
-static int perf_evlist__max_name_len(struct perf_evlist *evlist)
+static int perf_evlist__max_name_len(struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
int max = 0;
evlist__for_each_entry(evlist, evsel) {
@@ -1651,7 +1678,7 @@ static int data_src__fprintf(u64 data_src, FILE *fp)
struct metric_ctx {
struct perf_sample *sample;
struct thread *thread;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
FILE *fp;
};
@@ -1686,7 +1713,7 @@ static void script_new_line(struct perf_stat_config *config __maybe_unused,
static void perf_sample__fprint_metric(struct perf_script *script,
struct thread *thread,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
FILE *fp)
{
@@ -1701,7 +1728,7 @@ static void perf_sample__fprint_metric(struct perf_script *script,
},
.force_header = false,
};
- struct perf_evsel *ev2;
+ struct evsel *ev2;
u64 val;
if (!evsel->stats)
@@ -1714,7 +1741,7 @@ static void perf_sample__fprint_metric(struct perf_script *script,
sample->cpu,
&rt_stat);
evsel_script(evsel)->val = val;
- if (evsel_script(evsel->leader)->gnum == evsel->leader->nr_members) {
+ if (evsel_script(evsel->leader)->gnum == evsel->leader->core.nr_members) {
for_each_group_member (ev2, evsel->leader) {
perf_stat__print_shadow_stats(&stat_config, ev2,
evsel_script(ev2)->val,
@@ -1728,7 +1755,7 @@ static void perf_sample__fprint_metric(struct perf_script *script,
}
static bool show_event(struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct thread *thread,
struct addr_location *al)
{
@@ -1769,14 +1796,14 @@ static bool show_event(struct perf_sample *sample,
}
static void process_event(struct perf_script *script,
- struct perf_sample *sample, struct perf_evsel *evsel,
+ struct perf_sample *sample, struct evsel *evsel,
struct addr_location *al,
struct machine *machine)
{
struct thread *thread = al->thread;
- struct perf_event_attr *attr = &evsel->attr;
+ struct perf_event_attr *attr = &evsel->core.attr;
unsigned int type = output_type(attr->type);
- struct perf_evsel_script *es = evsel->priv;
+ struct evsel_script *es = evsel->priv;
FILE *fp = es->fp;
if (output[type].fields == 0)
@@ -1785,6 +1812,9 @@ static void process_event(struct perf_script *script,
if (!show_event(sample, evsel, thread, al))
return;
+ if (evswitch__discard(&script->evswitch, evsel))
+ return;
+
++es->samples;
perf_sample__fprintf_start(sample, thread, evsel,
@@ -1858,6 +1888,9 @@ static void process_event(struct perf_script *script,
if (PRINT_FIELD(PHYS_ADDR))
fprintf(fp, "%16" PRIx64, sample->phys_addr);
+
+ perf_sample__fprintf_ipc(sample, attr, fp);
+
fprintf(fp, "\n");
if (PRINT_FIELD(SRCCODE)) {
@@ -1875,9 +1908,9 @@ static void process_event(struct perf_script *script,
static struct scripting_ops *scripting_ops;
-static void __process_stat(struct perf_evsel *counter, u64 tstamp)
+static void __process_stat(struct evsel *counter, u64 tstamp)
{
- int nthreads = thread_map__nr(counter->threads);
+ int nthreads = perf_thread_map__nr(counter->core.threads);
int ncpus = perf_evsel__nr_cpus(counter);
int cpu, thread;
static int header_printed;
@@ -1898,8 +1931,8 @@ static void __process_stat(struct perf_evsel *counter, u64 tstamp)
counts = perf_counts(counter->counts, cpu, thread);
printf("%3d %8d %15" PRIu64 " %15" PRIu64 " %15" PRIu64 " %15" PRIu64 " %s\n",
- counter->cpus->map[cpu],
- thread_map__pid(counter->threads, thread),
+ counter->core.cpus->map[cpu],
+ perf_thread_map__pid(counter->core.threads, thread),
counts->val,
counts->ena,
counts->run,
@@ -1909,7 +1942,7 @@ static void __process_stat(struct perf_evsel *counter, u64 tstamp)
}
}
-static void process_stat(struct perf_evsel *counter, u64 tstamp)
+static void process_stat(struct evsel *counter, u64 tstamp)
{
if (scripting_ops && scripting_ops->process_stat)
scripting_ops->process_stat(&stat_config, counter, tstamp);
@@ -1951,7 +1984,7 @@ static bool filter_cpu(struct perf_sample *sample)
static int process_sample_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine)
{
struct perf_script *scr = container_of(tool, struct perf_script, tool);
@@ -1996,13 +2029,13 @@ out_put:
}
static int process_attr(struct perf_tool *tool, union perf_event *event,
- struct perf_evlist **pevlist)
+ struct evlist **pevlist)
{
struct perf_script *scr = container_of(tool, struct perf_script, tool);
- struct perf_evlist *evlist;
- struct perf_evsel *evsel, *pos;
+ struct evlist *evlist;
+ struct evsel *evsel, *pos;
int err;
- static struct perf_evsel_script *es;
+ static struct evsel_script *es;
err = perf_event__process_attr(tool, event, pevlist);
if (err)
@@ -2024,18 +2057,18 @@ static int process_attr(struct perf_tool *tool, union perf_event *event,
}
}
- if (evsel->attr.type >= PERF_TYPE_MAX &&
- evsel->attr.type != PERF_TYPE_SYNTH)
+ if (evsel->core.attr.type >= PERF_TYPE_MAX &&
+ evsel->core.attr.type != PERF_TYPE_SYNTH)
return 0;
evlist__for_each_entry(evlist, pos) {
- if (pos->attr.type == evsel->attr.type && pos != evsel)
+ if (pos->core.attr.type == evsel->core.attr.type && pos != evsel)
return 0;
}
- set_print_ip_opts(&evsel->attr);
+ set_print_ip_opts(&evsel->core.attr);
- if (evsel->attr.sample_type)
+ if (evsel->core.attr.sample_type)
err = perf_evsel__check_attr(evsel, scr->session);
return err;
@@ -2049,7 +2082,7 @@ static int process_comm_event(struct perf_tool *tool,
struct thread *thread;
struct perf_script *script = container_of(tool, struct perf_script, tool);
struct perf_session *session = script->session;
- struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
+ struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
int ret = -1;
thread = machine__findnew_thread(machine, event->comm.pid, event->comm.tid);
@@ -2061,7 +2094,7 @@ static int process_comm_event(struct perf_tool *tool,
if (perf_event__process_comm(tool, event, sample, machine) < 0)
goto out;
- if (!evsel->attr.sample_id_all) {
+ if (!evsel->core.attr.sample_id_all) {
sample->cpu = 0;
sample->time = 0;
sample->tid = event->comm.tid;
@@ -2086,7 +2119,7 @@ static int process_namespaces_event(struct perf_tool *tool,
struct thread *thread;
struct perf_script *script = container_of(tool, struct perf_script, tool);
struct perf_session *session = script->session;
- struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
+ struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
int ret = -1;
thread = machine__findnew_thread(machine, event->namespaces.pid,
@@ -2099,7 +2132,7 @@ static int process_namespaces_event(struct perf_tool *tool,
if (perf_event__process_namespaces(tool, event, sample, machine) < 0)
goto out;
- if (!evsel->attr.sample_id_all) {
+ if (!evsel->core.attr.sample_id_all) {
sample->cpu = 0;
sample->time = 0;
sample->tid = event->namespaces.tid;
@@ -2124,7 +2157,7 @@ static int process_fork_event(struct perf_tool *tool,
struct thread *thread;
struct perf_script *script = container_of(tool, struct perf_script, tool);
struct perf_session *session = script->session;
- struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
+ struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
if (perf_event__process_fork(tool, event, sample, machine) < 0)
return -1;
@@ -2135,7 +2168,7 @@ static int process_fork_event(struct perf_tool *tool,
return -1;
}
- if (!evsel->attr.sample_id_all) {
+ if (!evsel->core.attr.sample_id_all) {
sample->cpu = 0;
sample->time = event->fork.time;
sample->tid = event->fork.tid;
@@ -2159,7 +2192,7 @@ static int process_exit_event(struct perf_tool *tool,
struct thread *thread;
struct perf_script *script = container_of(tool, struct perf_script, tool);
struct perf_session *session = script->session;
- struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
+ struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
thread = machine__findnew_thread(machine, event->fork.pid, event->fork.tid);
if (thread == NULL) {
@@ -2167,7 +2200,7 @@ static int process_exit_event(struct perf_tool *tool,
return -1;
}
- if (!evsel->attr.sample_id_all) {
+ if (!evsel->core.attr.sample_id_all) {
sample->cpu = 0;
sample->time = 0;
sample->tid = event->fork.tid;
@@ -2194,7 +2227,7 @@ static int process_mmap_event(struct perf_tool *tool,
struct thread *thread;
struct perf_script *script = container_of(tool, struct perf_script, tool);
struct perf_session *session = script->session;
- struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
+ struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
if (perf_event__process_mmap(tool, event, sample, machine) < 0)
return -1;
@@ -2205,7 +2238,7 @@ static int process_mmap_event(struct perf_tool *tool,
return -1;
}
- if (!evsel->attr.sample_id_all) {
+ if (!evsel->core.attr.sample_id_all) {
sample->cpu = 0;
sample->time = 0;
sample->tid = event->mmap.tid;
@@ -2228,7 +2261,7 @@ static int process_mmap2_event(struct perf_tool *tool,
struct thread *thread;
struct perf_script *script = container_of(tool, struct perf_script, tool);
struct perf_session *session = script->session;
- struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
+ struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
if (perf_event__process_mmap2(tool, event, sample, machine) < 0)
return -1;
@@ -2239,7 +2272,7 @@ static int process_mmap2_event(struct perf_tool *tool,
return -1;
}
- if (!evsel->attr.sample_id_all) {
+ if (!evsel->core.attr.sample_id_all) {
sample->cpu = 0;
sample->time = 0;
sample->tid = event->mmap2.tid;
@@ -2262,11 +2295,17 @@ static int process_switch_event(struct perf_tool *tool,
struct thread *thread;
struct perf_script *script = container_of(tool, struct perf_script, tool);
struct perf_session *session = script->session;
- struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
+ struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
if (perf_event__process_switch(tool, event, sample, machine) < 0)
return -1;
+ if (scripting_ops && scripting_ops->process_switch)
+ scripting_ops->process_switch(event, sample, machine);
+
+ if (!script->show_switch_events)
+ return 0;
+
thread = machine__findnew_thread(machine, sample->pid,
sample->tid);
if (thread == NULL) {
@@ -2291,7 +2330,7 @@ process_lost_event(struct perf_tool *tool,
{
struct perf_script *script = container_of(tool, struct perf_script, tool);
struct perf_session *session = script->session;
- struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
+ struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
struct thread *thread;
thread = machine__findnew_thread(machine, sample->pid,
@@ -2318,6 +2357,41 @@ process_finished_round_event(struct perf_tool *tool __maybe_unused,
return 0;
}
+static int
+process_bpf_events(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct machine *machine)
+{
+ struct thread *thread;
+ struct perf_script *script = container_of(tool, struct perf_script, tool);
+ struct perf_session *session = script->session;
+ struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
+
+ if (machine__process_ksymbol(machine, event, sample) < 0)
+ return -1;
+
+ if (!evsel->core.attr.sample_id_all) {
+ perf_event__fprintf(event, stdout);
+ return 0;
+ }
+
+ thread = machine__findnew_thread(machine, sample->pid, sample->tid);
+ if (thread == NULL) {
+ pr_debug("problem processing MMAP event, skipping it.\n");
+ return -1;
+ }
+
+ if (!filter_cpu(sample)) {
+ perf_sample__fprintf_start(sample, thread, evsel,
+ event->header.type, stdout);
+ perf_event__fprintf(event, stdout);
+ }
+
+ thread__put(thread);
+ return 0;
+}
+
static void sig_handler(int sig __maybe_unused)
{
session_done = 1;
@@ -2325,8 +2399,8 @@ static void sig_handler(int sig __maybe_unused)
static void perf_script__fclose_per_event_dump(struct perf_script *script)
{
- struct perf_evlist *evlist = script->session->evlist;
- struct perf_evsel *evsel;
+ struct evlist *evlist = script->session->evlist;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) {
if (!evsel->priv)
@@ -2338,7 +2412,7 @@ static void perf_script__fclose_per_event_dump(struct perf_script *script)
static int perf_script__fopen_per_event_dump(struct perf_script *script)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(script->session->evlist, evsel) {
/*
@@ -2365,8 +2439,8 @@ out_err_fclose:
static int perf_script__setup_per_event_dump(struct perf_script *script)
{
- struct perf_evsel *evsel;
- static struct perf_evsel_script es_stdout;
+ struct evsel *evsel;
+ static struct evsel_script es_stdout;
if (script->per_event_dump)
return perf_script__fopen_per_event_dump(script);
@@ -2381,10 +2455,10 @@ static int perf_script__setup_per_event_dump(struct perf_script *script)
static void perf_script__exit_per_event_dump_stats(struct perf_script *script)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(script->session->evlist, evsel) {
- struct perf_evsel_script *es = evsel->priv;
+ struct evsel_script *es = evsel->priv;
perf_evsel_script__fprintf(es, stdout);
perf_evsel_script__delete(es);
@@ -2410,7 +2484,7 @@ static int __cmd_script(struct perf_script *script)
script->tool.mmap = process_mmap_event;
script->tool.mmap2 = process_mmap2_event;
}
- if (script->show_switch_events)
+ if (script->show_switch_events || (scripting_ops && scripting_ops->process_switch))
script->tool.context_switch = process_switch_event;
if (script->show_namespace_events)
script->tool.namespaces = process_namespaces_event;
@@ -2420,6 +2494,10 @@ static int __cmd_script(struct perf_script *script)
script->tool.ordered_events = false;
script->tool.finished_round = process_finished_round_event;
}
+ if (script->show_bpf_events) {
+ script->tool.ksymbol = process_bpf_events;
+ script->tool.bpf = process_bpf_events;
+ }
if (perf_script__setup_per_event_dump(script)) {
pr_err("Couldn't create the per event dump files\n");
@@ -2819,7 +2897,7 @@ static int read_script_info(struct script_desc *desc, const char *filename)
return -1;
while (fgets(line, sizeof(line), fp)) {
- p = ltrim(line);
+ p = skip_spaces(line);
if (strlen(p) == 0)
continue;
if (*p != '#')
@@ -2828,19 +2906,19 @@ static int read_script_info(struct script_desc *desc, const char *filename)
if (strlen(p) && *p == '!')
continue;
- p = ltrim(p);
+ p = skip_spaces(p);
if (strlen(p) && p[strlen(p) - 1] == '\n')
p[strlen(p) - 1] = '\0';
if (!strncmp(p, "description:", strlen("description:"))) {
p += strlen("description:");
- desc->half_liner = strdup(ltrim(p));
+ desc->half_liner = strdup(skip_spaces(p));
continue;
}
if (!strncmp(p, "args:", strlen("args:"))) {
p += strlen("args:");
- desc->args = strdup(ltrim(p));
+ desc->args = strdup(skip_spaces(p));
continue;
}
}
@@ -2936,7 +3014,7 @@ static int check_ev_match(char *dir_name, char *scriptname,
{
char filename[MAXPATHLEN], evname[128];
char line[BUFSIZ], *p;
- struct perf_evsel *pos;
+ struct evsel *pos;
int match, len;
FILE *fp;
@@ -2947,7 +3025,7 @@ static int check_ev_match(char *dir_name, char *scriptname,
return -1;
while (fgets(line, sizeof(line), fp)) {
- p = ltrim(line);
+ p = skip_spaces(line);
if (*p == '#')
continue;
@@ -2957,7 +3035,7 @@ static int check_ev_match(char *dir_name, char *scriptname,
break;
p += 2;
- p = ltrim(p);
+ p = skip_spaces(p);
len = strcspn(p, " \t");
if (!len)
break;
@@ -3168,8 +3246,8 @@ static void script__setup_sample_type(struct perf_script *script)
static int process_stat_round_event(struct perf_session *session,
union perf_event *event)
{
- struct stat_round_event *round = &event->stat_round;
- struct perf_evsel *counter;
+ struct perf_record_stat_round *round = &event->stat_round;
+ struct evsel *counter;
evlist__for_each_entry(session->evlist, counter) {
perf_stat_process_counter(&stat_config, counter);
@@ -3189,7 +3267,7 @@ static int process_stat_config_event(struct perf_session *session __maybe_unused
static int set_maps(struct perf_script *script)
{
- struct perf_evlist *evlist = script->session->evlist;
+ struct evlist *evlist = script->session->evlist;
if (!script->cpus || !script->threads)
return 0;
@@ -3197,7 +3275,7 @@ static int set_maps(struct perf_script *script)
if (WARN_ONCE(script->allocated, "stats double allocation\n"))
return -EINVAL;
- perf_evlist__set_maps(evlist, script->cpus, script->threads);
+ perf_evlist__set_maps(&evlist->core, script->cpus, script->threads);
if (perf_evlist__alloc_stats(evlist, true))
return -ENOMEM;
@@ -3297,6 +3375,7 @@ static int parse_call_trace(const struct option *opt __maybe_unused,
parse_output_fields(NULL, "-ip,-addr,-event,-period,+callindent", 0);
itrace_parse_synth_opts(opt, "cewp", 0);
symbol_conf.nanosecs = true;
+ symbol_conf.pad_output_len_dso = 50;
return 0;
}
@@ -3392,7 +3471,7 @@ int cmd_script(int argc, const char **argv)
"Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,"
"addr,symoff,srcline,period,iregs,uregs,brstack,"
"brstacksym,flags,bpf-output,brstackinsn,brstackoff,"
- "callindent,insn,insnlen,synth,phys_addr,metric,misc",
+ "callindent,insn,insnlen,synth,phys_addr,metric,misc,ipc",
parse_output_fields),
OPT_BOOLEAN('a', "all-cpus", &system_wide,
"system-wide collection from all CPUs"),
@@ -3438,6 +3517,8 @@ int cmd_script(int argc, const char **argv)
"Show lost events (if recorded)"),
OPT_BOOLEAN('\0', "show-round-events", &script.show_round_events,
"Show round events (if recorded)"),
+ OPT_BOOLEAN('\0', "show-bpf-events", &script.show_bpf_events,
+ "Show bpf related events (if recorded)"),
OPT_BOOLEAN('\0', "per-event-dump", &script.per_event_dump,
"Dump trace output to files named by the monitored events"),
OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
@@ -3458,6 +3539,16 @@ int cmd_script(int argc, const char **argv)
"Time span of interest (start,stop)"),
OPT_BOOLEAN(0, "inline", &symbol_conf.inline_name,
"Show inline function"),
+ OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory",
+ "guest mount directory under which every guest os"
+ " instance has a subdir"),
+ OPT_STRING(0, "guestvmlinux", &symbol_conf.default_guest_vmlinux_name,
+ "file", "file saving guest os vmlinux"),
+ OPT_STRING(0, "guestkallsyms", &symbol_conf.default_guest_kallsyms,
+ "file", "file saving guest os /proc/kallsyms"),
+ OPT_STRING(0, "guestmodules", &symbol_conf.default_guest_modules,
+ "file", "file saving guest os /proc/modules"),
+ OPTS_EVSWITCH(&script.evswitch),
OPT_END()
};
const char * const script_subcommands[] = { "record", "report", NULL };
@@ -3477,6 +3568,16 @@ int cmd_script(int argc, const char **argv)
argc = parse_options_subcommand(argc, argv, options, script_subcommands, script_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
+ if (symbol_conf.guestmount ||
+ symbol_conf.default_guest_vmlinux_name ||
+ symbol_conf.default_guest_kallsyms ||
+ symbol_conf.default_guest_modules) {
+ /*
+ * Enable guest sample processing.
+ */
+ perf_guest = true;
+ }
+
data.path = input_name;
data.force = symbol_conf.force;
@@ -3669,7 +3770,8 @@ int cmd_script(int argc, const char **argv)
goto out_delete;
uname(&uts);
- if (!strcmp(uts.machine, session->header.env.arch) ||
+ if (data.is_pipe || /* assume pipe_mode indicates native_arch */
+ !strcmp(uts.machine, session->header.env.arch) ||
(!strcmp(uts.machine, "x86_64") &&
!strcmp(session->header.env.arch, "i386")))
native_arch = true;
@@ -3765,15 +3867,25 @@ int cmd_script(int argc, const char **argv)
&script.range_num);
if (err < 0)
goto out_delete;
+
+ itrace_synth_opts__set_time_range(&itrace_synth_opts,
+ script.ptime_range,
+ script.range_num);
}
+ err = evswitch__init(&script.evswitch, session->evlist, stderr);
+ if (err)
+ goto out_delete;
+
err = __cmd_script(&script);
flush_scripting();
out_delete:
- if (script.ptime_range)
+ if (script.ptime_range) {
+ itrace_synth_opts__clear_time_range(&itrace_synth_opts);
zfree(&script.ptime_range);
+ }
perf_evlist__free_stats(session->evlist);
perf_session__delete(session);
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 24b8e690fb69..7e17bf9f700a 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* builtin-stat.c
*
@@ -37,14 +38,11 @@
* Mike Galbraith <efault@gmx.de>
* Paul Mackerras <paulus@samba.org>
* Jaswinder Singh Rajput <jaswinder@kernel.org>
- *
- * Released under the GPL v2. (and only v2, not any later version)
*/
-#include "perf.h"
#include "builtin.h"
+#include "perf.h"
#include "util/cgroup.h"
-#include "util/util.h"
#include <subcmd/parse-options.h>
#include "util/parse-events.h"
#include "util/pmu.h"
@@ -56,7 +54,6 @@
#include "util/stat.h"
#include "util/header.h"
#include "util/cpumap.h"
-#include "util/thread.h"
#include "util/thread_map.h"
#include "util/counts.h"
#include "util/group.h"
@@ -64,10 +61,13 @@
#include "util/tool.h"
#include "util/string2.h"
#include "util/metricgroup.h"
+#include "util/target.h"
+#include "util/time-utils.h"
#include "util/top.h"
#include "asm/bug.h"
#include <linux/time64.h>
+#include <linux/zalloc.h>
#include <api/fs/fs.h>
#include <errno.h>
#include <signal.h>
@@ -83,7 +83,8 @@
#include <sys/time.h>
#include <sys/resource.h>
-#include "sane_ctype.h"
+#include <linux/ctype.h>
+#include <perf/evlist.h>
#define DEFAULT_SEPARATOR " "
#define FREEZE_ON_SMI_PATH "devices/cpu/freeze_on_smi"
@@ -131,7 +132,7 @@ static const char *smi_cost_attrs = {
"}"
};
-static struct perf_evlist *evsel_list;
+static struct evlist *evsel_list;
static struct target target = {
.uid = UINT_MAX,
@@ -165,8 +166,8 @@ struct perf_stat {
u64 bytes_written;
struct perf_tool tool;
bool maps_allocated;
- struct cpu_map *cpus;
- struct thread_map *threads;
+ struct perf_cpu_map *cpus;
+ struct perf_thread_map *threads;
enum aggr_mode aggr_mode;
};
@@ -235,7 +236,7 @@ static int write_stat_round_event(u64 tm, u64 type)
#define SID(e, x, y) xyarray__entry(e->sample_id, x, y)
static int
-perf_evsel__write_stat_event(struct perf_evsel *counter, u32 cpu, u32 thread,
+perf_evsel__write_stat_event(struct evsel *counter, u32 cpu, u32 thread,
struct perf_counts_values *count)
{
struct perf_sample_id *sid = SID(counter, cpu, thread);
@@ -244,7 +245,7 @@ perf_evsel__write_stat_event(struct perf_evsel *counter, u32 cpu, u32 thread,
process_synthesized_event, NULL);
}
-static int read_single_counter(struct perf_evsel *counter, int cpu,
+static int read_single_counter(struct evsel *counter, int cpu,
int thread, struct timespec *rs)
{
if (counter->tool_event == PERF_TOOL_DURATION_TIME) {
@@ -262,9 +263,9 @@ static int read_single_counter(struct perf_evsel *counter, int cpu,
* Read out the results of a single counter:
* do not aggregate counts across CPUs in system-wide mode
*/
-static int read_counter(struct perf_evsel *counter, struct timespec *rs)
+static int read_counter(struct evsel *counter, struct timespec *rs)
{
- int nthreads = thread_map__nr(evsel_list->threads);
+ int nthreads = perf_thread_map__nr(evsel_list->core.threads);
int ncpus, cpu, thread;
if (target__has_cpu(&target) && !target__has_per_thread(&target))
@@ -288,7 +289,7 @@ static int read_counter(struct perf_evsel *counter, struct timespec *rs)
* The leader's group read loads data into its group members
* (via perf_evsel__read_counter) and sets threir count->loaded.
*/
- if (!count->loaded &&
+ if (!perf_counts__is_loaded(counter->counts, cpu, thread) &&
read_single_counter(counter, cpu, thread, rs)) {
counter->counts->scaled = -1;
perf_counts(counter->counts, cpu, thread)->ena = 0;
@@ -296,7 +297,7 @@ static int read_counter(struct perf_evsel *counter, struct timespec *rs)
return -1;
}
- count->loaded = false;
+ perf_counts__set_loaded(counter->counts, cpu, thread, false);
if (STAT_RECORD) {
if (perf_evsel__write_stat_event(counter, cpu, thread, count)) {
@@ -320,7 +321,7 @@ static int read_counter(struct perf_evsel *counter, struct timespec *rs)
static void read_counters(struct timespec *rs)
{
- struct perf_evsel *counter;
+ struct evsel *counter;
int ret;
evlist__for_each_entry(evsel_list, counter) {
@@ -363,7 +364,7 @@ static void enable_counters(void)
* - we have initial delay configured
*/
if (!target__none(&target) || stat_config.initial_delay)
- perf_evlist__enable(evsel_list);
+ evlist__enable(evsel_list);
}
static void disable_counters(void)
@@ -374,7 +375,7 @@ static void disable_counters(void)
* from counting before reading their constituent counters.
*/
if (!target__none(&target))
- perf_evlist__disable(evsel_list);
+ evlist__disable(evsel_list);
}
static volatile int workload_exec_errno;
@@ -390,13 +391,13 @@ static void workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *inf
workload_exec_errno = info->si_value.sival_int;
}
-static bool perf_evsel__should_store_id(struct perf_evsel *counter)
+static bool perf_evsel__should_store_id(struct evsel *counter)
{
- return STAT_RECORD || counter->attr.read_format & PERF_FORMAT_ID;
+ return STAT_RECORD || counter->core.attr.read_format & PERF_FORMAT_ID;
}
static bool is_target_alive(struct target *_target,
- struct thread_map *threads)
+ struct perf_thread_map *threads)
{
struct stat st;
int i;
@@ -424,7 +425,7 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
int timeout = stat_config.timeout;
char msg[BUFSIZ];
unsigned long long t0, t1;
- struct perf_evsel *counter;
+ struct evsel *counter;
struct timespec ts;
size_t l;
int status = 0;
@@ -479,22 +480,22 @@ try_again:
counter->supported = false;
if ((counter->leader != counter) ||
- !(counter->leader->nr_members > 1))
+ !(counter->leader->core.nr_members > 1))
continue;
} else if (perf_evsel__fallback(counter, errno, msg, sizeof(msg))) {
if (verbose > 0)
ui__warning("%s\n", msg);
goto try_again;
} else if (target__has_per_thread(&target) &&
- evsel_list->threads &&
- evsel_list->threads->err_thread != -1) {
+ evsel_list->core.threads &&
+ evsel_list->core.threads->err_thread != -1) {
/*
* For global --per-thread case, skip current
* error thread.
*/
- if (!thread_map__remove(evsel_list->threads,
- evsel_list->threads->err_thread)) {
- evsel_list->threads->err_thread = -1;
+ if (!thread_map__remove(evsel_list->core.threads,
+ evsel_list->core.threads->err_thread)) {
+ evsel_list->core.threads->err_thread = -1;
goto try_again;
}
}
@@ -580,7 +581,7 @@ try_again:
enable_counters();
while (!done) {
nanosleep(&ts, NULL);
- if (!is_target_alive(&target, evsel_list->threads))
+ if (!is_target_alive(&target, evsel_list->core.threads))
break;
if (timeout)
break;
@@ -608,7 +609,13 @@ try_again:
* group leaders.
*/
read_counters(&(struct timespec) { .tv_nsec = t1-t0 });
- perf_evlist__close(evsel_list);
+
+ /*
+ * We need to keep evsel_list alive, because it's processed
+ * later the evsel_list will be closed after.
+ */
+ if (!STAT_RECORD)
+ evlist__close(evsel_list);
return WEXITSTATUS(status);
}
@@ -777,6 +784,8 @@ static struct option stat_options[] = {
"stop workload and print counts after a timeout period in ms (>= 10ms)"),
OPT_SET_UINT(0, "per-socket", &stat_config.aggr_mode,
"aggregate counts per processor socket", AGGR_SOCKET),
+ OPT_SET_UINT(0, "per-die", &stat_config.aggr_mode,
+ "aggregate counts per processor die", AGGR_DIE),
OPT_SET_UINT(0, "per-core", &stat_config.aggr_mode,
"aggregate counts per physical processor core", AGGR_CORE),
OPT_SET_UINT(0, "per-thread", &stat_config.aggr_mode,
@@ -796,18 +805,24 @@ static struct option stat_options[] = {
};
static int perf_stat__get_socket(struct perf_stat_config *config __maybe_unused,
- struct cpu_map *map, int cpu)
+ struct perf_cpu_map *map, int cpu)
{
return cpu_map__get_socket(map, cpu, NULL);
}
+static int perf_stat__get_die(struct perf_stat_config *config __maybe_unused,
+ struct perf_cpu_map *map, int cpu)
+{
+ return cpu_map__get_die(map, cpu, NULL);
+}
+
static int perf_stat__get_core(struct perf_stat_config *config __maybe_unused,
- struct cpu_map *map, int cpu)
+ struct perf_cpu_map *map, int cpu)
{
return cpu_map__get_core(map, cpu, NULL);
}
-static int cpu_map__get_max(struct cpu_map *map)
+static int cpu_map__get_max(struct perf_cpu_map *map)
{
int i, max = -1;
@@ -820,7 +835,7 @@ static int cpu_map__get_max(struct cpu_map *map)
}
static int perf_stat__get_aggr(struct perf_stat_config *config,
- aggr_get_id_t get_id, struct cpu_map *map, int idx)
+ aggr_get_id_t get_id, struct perf_cpu_map *map, int idx)
{
int cpu;
@@ -836,20 +851,26 @@ static int perf_stat__get_aggr(struct perf_stat_config *config,
}
static int perf_stat__get_socket_cached(struct perf_stat_config *config,
- struct cpu_map *map, int idx)
+ struct perf_cpu_map *map, int idx)
{
return perf_stat__get_aggr(config, perf_stat__get_socket, map, idx);
}
+static int perf_stat__get_die_cached(struct perf_stat_config *config,
+ struct perf_cpu_map *map, int idx)
+{
+ return perf_stat__get_aggr(config, perf_stat__get_die, map, idx);
+}
+
static int perf_stat__get_core_cached(struct perf_stat_config *config,
- struct cpu_map *map, int idx)
+ struct perf_cpu_map *map, int idx)
{
return perf_stat__get_aggr(config, perf_stat__get_core, map, idx);
}
static bool term_percore_set(void)
{
- struct perf_evsel *counter;
+ struct evsel *counter;
evlist__for_each_entry(evsel_list, counter) {
if (counter->percore)
@@ -865,14 +886,21 @@ static int perf_stat_init_aggr_mode(void)
switch (stat_config.aggr_mode) {
case AGGR_SOCKET:
- if (cpu_map__build_socket_map(evsel_list->cpus, &stat_config.aggr_map)) {
+ if (cpu_map__build_socket_map(evsel_list->core.cpus, &stat_config.aggr_map)) {
perror("cannot build socket map");
return -1;
}
stat_config.aggr_get_id = perf_stat__get_socket_cached;
break;
+ case AGGR_DIE:
+ if (cpu_map__build_die_map(evsel_list->core.cpus, &stat_config.aggr_map)) {
+ perror("cannot build die map");
+ return -1;
+ }
+ stat_config.aggr_get_id = perf_stat__get_die_cached;
+ break;
case AGGR_CORE:
- if (cpu_map__build_core_map(evsel_list->cpus, &stat_config.aggr_map)) {
+ if (cpu_map__build_core_map(evsel_list->core.cpus, &stat_config.aggr_map)) {
perror("cannot build core map");
return -1;
}
@@ -880,7 +908,7 @@ static int perf_stat_init_aggr_mode(void)
break;
case AGGR_NONE:
if (term_percore_set()) {
- if (cpu_map__build_core_map(evsel_list->cpus,
+ if (cpu_map__build_core_map(evsel_list->core.cpus,
&stat_config.aggr_map)) {
perror("cannot build core map");
return -1;
@@ -900,20 +928,20 @@ static int perf_stat_init_aggr_mode(void)
* taking the highest cpu number to be the size of
* the aggregation translate cpumap.
*/
- nr = cpu_map__get_max(evsel_list->cpus);
- stat_config.cpus_aggr_map = cpu_map__empty_new(nr + 1);
+ nr = cpu_map__get_max(evsel_list->core.cpus);
+ stat_config.cpus_aggr_map = perf_cpu_map__empty_new(nr + 1);
return stat_config.cpus_aggr_map ? 0 : -ENOMEM;
}
static void perf_stat__exit_aggr_mode(void)
{
- cpu_map__put(stat_config.aggr_map);
- cpu_map__put(stat_config.cpus_aggr_map);
+ perf_cpu_map__put(stat_config.aggr_map);
+ perf_cpu_map__put(stat_config.cpus_aggr_map);
stat_config.aggr_map = NULL;
stat_config.cpus_aggr_map = NULL;
}
-static inline int perf_env__get_cpu(struct perf_env *env, struct cpu_map *map, int idx)
+static inline int perf_env__get_cpu(struct perf_env *env, struct perf_cpu_map *map, int idx)
{
int cpu;
@@ -928,7 +956,7 @@ static inline int perf_env__get_cpu(struct perf_env *env, struct cpu_map *map, i
return cpu;
}
-static int perf_env__get_socket(struct cpu_map *map, int idx, void *data)
+static int perf_env__get_socket(struct perf_cpu_map *map, int idx, void *data)
{
struct perf_env *env = data;
int cpu = perf_env__get_cpu(env, map, idx);
@@ -936,46 +964,91 @@ static int perf_env__get_socket(struct cpu_map *map, int idx, void *data)
return cpu == -1 ? -1 : env->cpu[cpu].socket_id;
}
-static int perf_env__get_core(struct cpu_map *map, int idx, void *data)
+static int perf_env__get_die(struct perf_cpu_map *map, int idx, void *data)
{
struct perf_env *env = data;
- int core = -1, cpu = perf_env__get_cpu(env, map, idx);
+ int die_id = -1, cpu = perf_env__get_cpu(env, map, idx);
if (cpu != -1) {
- int socket_id = env->cpu[cpu].socket_id;
+ /*
+ * Encode socket in bit range 15:8
+ * die_id is relative to socket,
+ * we need a global id. So we combine
+ * socket + die id
+ */
+ if (WARN_ONCE(env->cpu[cpu].socket_id >> 8, "The socket id number is too big.\n"))
+ return -1;
+
+ if (WARN_ONCE(env->cpu[cpu].die_id >> 8, "The die id number is too big.\n"))
+ return -1;
+
+ die_id = (env->cpu[cpu].socket_id << 8) | (env->cpu[cpu].die_id & 0xff);
+ }
+ return die_id;
+}
+
+static int perf_env__get_core(struct perf_cpu_map *map, int idx, void *data)
+{
+ struct perf_env *env = data;
+ int core = -1, cpu = perf_env__get_cpu(env, map, idx);
+
+ if (cpu != -1) {
/*
- * Encode socket in upper 16 bits
- * core_id is relative to socket, and
+ * Encode socket in bit range 31:24
+ * encode die id in bit range 23:16
+ * core_id is relative to socket and die,
* we need a global id. So we combine
- * socket + core id.
+ * socket + die id + core id
*/
- core = (socket_id << 16) | (env->cpu[cpu].core_id & 0xffff);
+ if (WARN_ONCE(env->cpu[cpu].socket_id >> 8, "The socket id number is too big.\n"))
+ return -1;
+
+ if (WARN_ONCE(env->cpu[cpu].die_id >> 8, "The die id number is too big.\n"))
+ return -1;
+
+ if (WARN_ONCE(env->cpu[cpu].core_id >> 16, "The core id number is too big.\n"))
+ return -1;
+
+ core = (env->cpu[cpu].socket_id << 24) |
+ (env->cpu[cpu].die_id << 16) |
+ (env->cpu[cpu].core_id & 0xffff);
}
return core;
}
-static int perf_env__build_socket_map(struct perf_env *env, struct cpu_map *cpus,
- struct cpu_map **sockp)
+static int perf_env__build_socket_map(struct perf_env *env, struct perf_cpu_map *cpus,
+ struct perf_cpu_map **sockp)
{
return cpu_map__build_map(cpus, sockp, perf_env__get_socket, env);
}
-static int perf_env__build_core_map(struct perf_env *env, struct cpu_map *cpus,
- struct cpu_map **corep)
+static int perf_env__build_die_map(struct perf_env *env, struct perf_cpu_map *cpus,
+ struct perf_cpu_map **diep)
+{
+ return cpu_map__build_map(cpus, diep, perf_env__get_die, env);
+}
+
+static int perf_env__build_core_map(struct perf_env *env, struct perf_cpu_map *cpus,
+ struct perf_cpu_map **corep)
{
return cpu_map__build_map(cpus, corep, perf_env__get_core, env);
}
static int perf_stat__get_socket_file(struct perf_stat_config *config __maybe_unused,
- struct cpu_map *map, int idx)
+ struct perf_cpu_map *map, int idx)
{
return perf_env__get_socket(map, idx, &perf_stat.session->header.env);
}
+static int perf_stat__get_die_file(struct perf_stat_config *config __maybe_unused,
+ struct perf_cpu_map *map, int idx)
+{
+ return perf_env__get_die(map, idx, &perf_stat.session->header.env);
+}
static int perf_stat__get_core_file(struct perf_stat_config *config __maybe_unused,
- struct cpu_map *map, int idx)
+ struct perf_cpu_map *map, int idx)
{
return perf_env__get_core(map, idx, &perf_stat.session->header.env);
}
@@ -986,14 +1059,21 @@ static int perf_stat_init_aggr_mode_file(struct perf_stat *st)
switch (stat_config.aggr_mode) {
case AGGR_SOCKET:
- if (perf_env__build_socket_map(env, evsel_list->cpus, &stat_config.aggr_map)) {
+ if (perf_env__build_socket_map(env, evsel_list->core.cpus, &stat_config.aggr_map)) {
perror("cannot build socket map");
return -1;
}
stat_config.aggr_get_id = perf_stat__get_socket_file;
break;
+ case AGGR_DIE:
+ if (perf_env__build_die_map(env, evsel_list->core.cpus, &stat_config.aggr_map)) {
+ perror("cannot build die map");
+ return -1;
+ }
+ stat_config.aggr_get_id = perf_stat__get_die_file;
+ break;
case AGGR_CORE:
- if (perf_env__build_core_map(env, evsel_list->cpus, &stat_config.aggr_map)) {
+ if (perf_env__build_core_map(env, evsel_list->core.cpus, &stat_config.aggr_map)) {
perror("cannot build core map");
return -1;
}
@@ -1277,8 +1357,8 @@ static int add_default_attributes(void)
fprintf(stderr,
"Cannot set up top down events %s: %d\n",
str, err);
- free(str);
parse_events_print_error(&errinfo, str);
+ free(str);
return -1;
}
} else {
@@ -1288,7 +1368,7 @@ static int add_default_attributes(void)
free(str);
}
- if (!evsel_list->nr_entries) {
+ if (!evsel_list->core.nr_entries) {
if (target__has_cpu(&target))
default_attrs0[0].config = PERF_COUNT_SW_CPU_CLOCK;
@@ -1383,8 +1463,8 @@ static int __cmd_record(int argc, const char **argv)
static int process_stat_round_event(struct perf_session *session,
union perf_event *event)
{
- struct stat_round_event *stat_round = &event->stat_round;
- struct perf_evsel *counter;
+ struct perf_record_stat_round *stat_round = &event->stat_round;
+ struct evsel *counter;
struct timespec tsh, *ts = NULL;
const char **argv = session->header.env.cmdline_argv;
int argc = session->header.env.nr_cmdline;
@@ -1414,7 +1494,7 @@ int process_stat_config_event(struct perf_session *session,
perf_event__read_stat_config(&stat_config, &event->stat_config);
- if (cpu_map__empty(st->cpus)) {
+ if (perf_cpu_map__empty(st->cpus)) {
if (st->aggr_mode != AGGR_UNSET)
pr_warning("warning: processing task data, aggregation mode not set\n");
return 0;
@@ -1439,7 +1519,7 @@ static int set_maps(struct perf_stat *st)
if (WARN_ONCE(st->maps_allocated, "stats double allocation\n"))
return -EINVAL;
- perf_evlist__set_maps(evsel_list, st->cpus, st->threads);
+ perf_evlist__set_maps(&evsel_list->core, st->cpus, st->threads);
if (perf_evlist__alloc_stats(evsel_list, true))
return -ENOMEM;
@@ -1473,7 +1553,7 @@ int process_cpu_map_event(struct perf_session *session,
{
struct perf_tool *tool = session->tool;
struct perf_stat *st = container_of(tool, struct perf_stat, tool);
- struct cpu_map *cpus;
+ struct perf_cpu_map *cpus;
if (st->cpus) {
pr_warning("Extra cpu map event, ignoring.\n");
@@ -1514,7 +1594,7 @@ static void runtime_stat_delete(struct perf_stat_config *config)
for (i = 0; i < config->stats_num; i++)
runtime_stat__exit(&config->stats[i]);
- free(config->stats);
+ zfree(&config->stats);
}
static const char * const stat_report_usage[] = {
@@ -1542,6 +1622,8 @@ static int __cmd_report(int argc, const char **argv)
OPT_STRING('i', "input", &input_name, "file", "input file name"),
OPT_SET_UINT(0, "per-socket", &perf_stat.aggr_mode,
"aggregate counts per processor socket", AGGR_SOCKET),
+ OPT_SET_UINT(0, "per-die", &perf_stat.aggr_mode,
+ "aggregate counts per processor die", AGGR_DIE),
OPT_SET_UINT(0, "per-core", &perf_stat.aggr_mode,
"aggregate counts per physical processor core", AGGR_CORE),
OPT_SET_UINT('A', "no-aggr", &perf_stat.aggr_mode,
@@ -1596,14 +1678,14 @@ static void setup_system_wide(int forks)
if (!forks)
target.system_wide = true;
else {
- struct perf_evsel *counter;
+ struct evsel *counter;
evlist__for_each_entry(evsel_list, counter) {
if (!counter->system_wide)
return;
}
- if (evsel_list->nr_entries)
+ if (evsel_list->core.nr_entries)
target.system_wide = true;
}
}
@@ -1622,7 +1704,7 @@ int cmd_stat(int argc, const char **argv)
setlocale(LC_ALL, "");
- evsel_list = perf_evlist__new();
+ evsel_list = evlist__new();
if (evsel_list == NULL)
return -ENOMEM;
@@ -1809,10 +1891,10 @@ int cmd_stat(int argc, const char **argv)
* so we could print it out on output.
*/
if (stat_config.aggr_mode == AGGR_THREAD) {
- thread_map__read_comms(evsel_list->threads);
+ thread_map__read_comms(evsel_list->core.threads);
if (target.system_wide) {
if (runtime_stat_new(&stat_config,
- thread_map__nr(evsel_list->threads))) {
+ perf_thread_map__nr(evsel_list->core.threads))) {
goto out;
}
}
@@ -1923,18 +2005,19 @@ int cmd_stat(int argc, const char **argv)
perf_session__write_header(perf_stat.session, evsel_list, fd, true);
}
+ evlist__close(evsel_list);
perf_session__delete(perf_stat.session);
}
perf_stat__exit_aggr_mode();
perf_evlist__free_stats(evsel_list);
out:
- free(stat_config.walltime_run);
+ zfree(&stat_config.walltime_run);
if (smi_cost && smi_reset)
sysfs__write_int(FREEZE_ON_SMI_PATH, 0);
- perf_evlist__delete(evsel_list);
+ evlist__delete(evsel_list);
runtime_stat_delete(&stat_config);
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 9b98687a27b9..e0e822695a29 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* builtin-timechart.c - make an svg timechart of system activity
*
@@ -5,35 +6,27 @@
*
* Authors:
* Arjan van de Ven <arjan@linux.intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; version 2
- * of the License.
*/
#include <errno.h>
#include <inttypes.h>
-#include <traceevent/event-parse.h>
#include "builtin.h"
-
-#include "util/util.h"
-
#include "util/color.h"
#include <linux/list.h>
-#include "util/cache.h"
-#include "util/evlist.h"
+#include "util/evlist.h" // for struct evsel_str_handler
#include "util/evsel.h"
#include <linux/kernel.h>
#include <linux/rbtree.h>
#include <linux/time64.h>
+#include <linux/zalloc.h>
#include "util/symbol.h"
#include "util/thread.h"
#include "util/callchain.h"
#include "perf.h"
#include "util/header.h"
+#include <subcmd/pager.h>
#include <subcmd/parse-options.h>
#include "util/parse-events.h"
#include "util/event.h"
@@ -551,19 +544,19 @@ exit:
}
typedef int (*tracepoint_handler)(struct timechart *tchart,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
const char *backtrace);
static int process_sample_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine)
{
struct timechart *tchart = container_of(tool, struct timechart, tool);
- if (evsel->attr.sample_type & PERF_SAMPLE_TIME) {
+ if (evsel->core.attr.sample_type & PERF_SAMPLE_TIME) {
if (!tchart->first_time || tchart->first_time > sample->time)
tchart->first_time = sample->time;
if (tchart->last_time < sample->time)
@@ -581,7 +574,7 @@ static int process_sample_event(struct perf_tool *tool,
static int
process_sample_cpu_idle(struct timechart *tchart __maybe_unused,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
const char *backtrace __maybe_unused)
{
@@ -597,7 +590,7 @@ process_sample_cpu_idle(struct timechart *tchart __maybe_unused,
static int
process_sample_cpu_frequency(struct timechart *tchart,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
const char *backtrace __maybe_unused)
{
@@ -610,7 +603,7 @@ process_sample_cpu_frequency(struct timechart *tchart,
static int
process_sample_sched_wakeup(struct timechart *tchart,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
const char *backtrace)
{
@@ -624,7 +617,7 @@ process_sample_sched_wakeup(struct timechart *tchart,
static int
process_sample_sched_switch(struct timechart *tchart,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
const char *backtrace)
{
@@ -640,7 +633,7 @@ process_sample_sched_switch(struct timechart *tchart,
#ifdef SUPPORT_OLD_POWER_EVENTS
static int
process_sample_power_start(struct timechart *tchart __maybe_unused,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
const char *backtrace __maybe_unused)
{
@@ -653,7 +646,7 @@ process_sample_power_start(struct timechart *tchart __maybe_unused,
static int
process_sample_power_end(struct timechart *tchart,
- struct perf_evsel *evsel __maybe_unused,
+ struct evsel *evsel __maybe_unused,
struct perf_sample *sample,
const char *backtrace __maybe_unused)
{
@@ -663,7 +656,7 @@ process_sample_power_end(struct timechart *tchart,
static int
process_sample_power_frequency(struct timechart *tchart,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
const char *backtrace __maybe_unused)
{
@@ -846,7 +839,7 @@ static int pid_end_io_sample(struct timechart *tchart, int pid, int type,
static int
process_enter_read(struct timechart *tchart,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample)
{
long fd = perf_evsel__intval(evsel, sample, "fd");
@@ -856,7 +849,7 @@ process_enter_read(struct timechart *tchart,
static int
process_exit_read(struct timechart *tchart,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample)
{
long ret = perf_evsel__intval(evsel, sample, "ret");
@@ -866,7 +859,7 @@ process_exit_read(struct timechart *tchart,
static int
process_enter_write(struct timechart *tchart,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample)
{
long fd = perf_evsel__intval(evsel, sample, "fd");
@@ -876,7 +869,7 @@ process_enter_write(struct timechart *tchart,
static int
process_exit_write(struct timechart *tchart,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample)
{
long ret = perf_evsel__intval(evsel, sample, "ret");
@@ -886,7 +879,7 @@ process_exit_write(struct timechart *tchart,
static int
process_enter_sync(struct timechart *tchart,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample)
{
long fd = perf_evsel__intval(evsel, sample, "fd");
@@ -896,7 +889,7 @@ process_enter_sync(struct timechart *tchart,
static int
process_exit_sync(struct timechart *tchart,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample)
{
long ret = perf_evsel__intval(evsel, sample, "ret");
@@ -906,7 +899,7 @@ process_exit_sync(struct timechart *tchart,
static int
process_enter_tx(struct timechart *tchart,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample)
{
long fd = perf_evsel__intval(evsel, sample, "fd");
@@ -916,7 +909,7 @@ process_enter_tx(struct timechart *tchart,
static int
process_exit_tx(struct timechart *tchart,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample)
{
long ret = perf_evsel__intval(evsel, sample, "ret");
@@ -926,7 +919,7 @@ process_exit_tx(struct timechart *tchart,
static int
process_enter_rx(struct timechart *tchart,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample)
{
long fd = perf_evsel__intval(evsel, sample, "fd");
@@ -936,7 +929,7 @@ process_enter_rx(struct timechart *tchart,
static int
process_exit_rx(struct timechart *tchart,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample)
{
long ret = perf_evsel__intval(evsel, sample, "ret");
@@ -946,7 +939,7 @@ process_exit_rx(struct timechart *tchart,
static int
process_enter_poll(struct timechart *tchart,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample)
{
long fd = perf_evsel__intval(evsel, sample, "fd");
@@ -956,7 +949,7 @@ process_enter_poll(struct timechart *tchart,
static int
process_exit_poll(struct timechart *tchart,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample)
{
long ret = perf_evsel__intval(evsel, sample, "ret");
@@ -1524,10 +1517,7 @@ static int process_header(struct perf_file_section *section __maybe_unused,
if (!tchart->topology)
break;
- if (svg_build_topology_map(ph->env.sibling_cores,
- ph->env.nr_sibling_cores,
- ph->env.sibling_threads,
- ph->env.nr_sibling_threads))
+ if (svg_build_topology_map(&ph->env))
fprintf(stderr, "problem building topology\n");
break;
@@ -1540,7 +1530,7 @@ static int process_header(struct perf_file_section *section __maybe_unused,
static int __cmd_timechart(struct timechart *tchart, const char *output_name)
{
- const struct perf_evsel_str_handler power_tracepoints[] = {
+ const struct evsel_str_handler power_tracepoints[] = {
{ "power:cpu_idle", process_sample_cpu_idle },
{ "power:cpu_frequency", process_sample_cpu_frequency },
{ "sched:sched_wakeup", process_sample_sched_wakeup },
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index fbbb0da43abb..726e3f2dd8c7 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* builtin-top.c
*
@@ -14,8 +15,6 @@
* Wu Fengguang <fengguang.wu@intel.com>
* Mike Galbraith <efault@gmx.de>
* Paul Mackerras <paulus@samba.org>
- *
- * Released under the GPL v2. (and only v2, not any later version)
*/
#include "builtin.h"
@@ -25,6 +24,7 @@
#include "util/bpf-event.h"
#include "util/config.h"
#include "util/color.h"
+#include "util/dso.h"
#include "util/evlist.h"
#include "util/evsel.h"
#include "util/event.h"
@@ -32,19 +32,20 @@
#include "util/map.h"
#include "util/session.h"
#include "util/symbol.h"
-#include "util/thread.h"
-#include "util/thread_map.h"
#include "util/top.h"
+#include "util/util.h"
#include <linux/rbtree.h>
#include <subcmd/parse-options.h>
#include "util/parse-events.h"
+#include "util/callchain.h"
#include "util/cpumap.h"
-#include "util/xyarray.h"
#include "util/sort.h"
+#include "util/string2.h"
#include "util/term.h"
#include "util/intlist.h"
#include "util/parse-branch-options.h"
#include "arch/common.h"
+#include "ui/ui.h"
#include "util/debug.h"
#include "util/ordered-events.h"
@@ -76,7 +77,7 @@
#include <linux/time64.h>
#include <linux/types.h>
-#include "sane_ctype.h"
+#include <linux/ctype.h>
static volatile int done;
static volatile int resize;
@@ -101,7 +102,7 @@ static void perf_top__resize(struct perf_top *top)
static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
{
- struct perf_evsel *evsel = hists_to_evsel(he->hists);
+ struct evsel *evsel;
struct symbol *sym;
struct annotation *notes;
struct map *map;
@@ -110,6 +111,8 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
if (!he || !he->ms.sym)
return -1;
+ evsel = hists_to_evsel(he->hists);
+
sym = he->ms.sym;
map = he->ms.map;
@@ -127,7 +130,7 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
notes = symbol__annotation(sym);
pthread_mutex_lock(&notes->lock);
- if (!symbol__hists(sym, top->evlist->nr_entries)) {
+ if (!symbol__hists(sym, top->evlist->core.nr_entries)) {
pthread_mutex_unlock(&notes->lock);
pr_err("Not enough memory for annotating '%s' symbol!\n",
sym->name);
@@ -184,7 +187,7 @@ static void ui__warn_map_erange(struct map *map, struct symbol *sym, u64 ip)
static void perf_top__record_precise_ip(struct perf_top *top,
struct hist_entry *he,
struct perf_sample *sample,
- struct perf_evsel *evsel, u64 ip)
+ struct evsel *evsel, u64 ip)
{
struct annotation *notes;
struct symbol *sym = he->ms.sym;
@@ -226,7 +229,7 @@ static void perf_top__record_precise_ip(struct perf_top *top,
static void perf_top__show_details(struct perf_top *top)
{
struct hist_entry *he = top->sym_filter_entry;
- struct perf_evsel *evsel = hists_to_evsel(he->hists);
+ struct evsel *evsel;
struct annotation *notes;
struct symbol *symbol;
int more;
@@ -234,6 +237,8 @@ static void perf_top__show_details(struct perf_top *top)
if (!he)
return;
+ evsel = hists_to_evsel(he->hists);
+
symbol = he->ms.sym;
notes = symbol__annotation(symbol);
@@ -261,12 +266,52 @@ out_unlock:
pthread_mutex_unlock(&notes->lock);
}
+static void perf_top__resort_hists(struct perf_top *t)
+{
+ struct evlist *evlist = t->evlist;
+ struct evsel *pos;
+
+ evlist__for_each_entry(evlist, pos) {
+ struct hists *hists = evsel__hists(pos);
+
+ /*
+ * unlink existing entries so that they can be linked
+ * in a correct order in hists__match() below.
+ */
+ hists__unlink(hists);
+
+ if (evlist->enabled) {
+ if (t->zero) {
+ hists__delete_entries(hists);
+ } else {
+ hists__decay_entries(hists, t->hide_user_symbols,
+ t->hide_kernel_symbols);
+ }
+ }
+
+ hists__collapse_resort(hists, NULL);
+
+ /* Non-group events are considered as leader */
+ if (symbol_conf.event_group &&
+ !perf_evsel__is_group_leader(pos)) {
+ struct hists *leader_hists = evsel__hists(pos->leader);
+
+ hists__match(leader_hists, hists);
+ hists__link(leader_hists, hists);
+ }
+ }
+
+ evlist__for_each_entry(evlist, pos) {
+ perf_evsel__output_resort(pos, NULL);
+ }
+}
+
static void perf_top__print_sym_table(struct perf_top *top)
{
char bf[160];
int printed = 0;
const int win_width = top->winsize.ws_col - 1;
- struct perf_evsel *evsel = top->sym_evsel;
+ struct evsel *evsel = top->sym_evsel;
struct hists *hists = evsel__hists(evsel);
puts(CONSOLE_CLEAR);
@@ -292,17 +337,7 @@ static void perf_top__print_sym_table(struct perf_top *top)
return;
}
- if (top->evlist->enabled) {
- if (top->zero) {
- hists__delete_entries(hists);
- } else {
- hists__decay_entries(hists, top->hide_user_symbols,
- top->hide_kernel_symbols);
- }
- }
-
- hists__collapse_resort(hists, NULL);
- perf_evsel__output_resort(evsel, NULL);
+ perf_top__resort_hists(top);
hists__output_recalc_col_len(hists, top->print_entries - printed);
putchar('\n');
@@ -400,7 +435,7 @@ static void perf_top__print_mapped_keys(struct perf_top *top)
fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", top->delay_secs);
fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", top->print_entries);
- if (top->evlist->nr_entries > 1)
+ if (top->evlist->core.nr_entries > 1)
fprintf(stdout, "\t[E] active event counter. \t(%s)\n", perf_evsel__name(top->sym_evsel));
fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", top->count_filter);
@@ -435,7 +470,7 @@ static int perf_top__key_mapped(struct perf_top *top, int c)
case 'S':
return 1;
case 'E':
- return top->evlist->nr_entries > 1 ? 1 : 0;
+ return top->evlist->core.nr_entries > 1 ? 1 : 0;
default:
break;
}
@@ -481,7 +516,7 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c)
}
break;
case 'E':
- if (top->evlist->nr_entries > 1) {
+ if (top->evlist->core.nr_entries > 1) {
/* Select 0 as the default event: */
int counter = 0;
@@ -492,7 +527,7 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c)
prompt_integer(&counter, "Enter details event counter");
- if (counter >= top->evlist->nr_entries) {
+ if (counter >= top->evlist->core.nr_entries) {
top->sym_evsel = perf_evlist__first(top->evlist);
fprintf(stderr, "Sorry, no such event, using %s.\n", perf_evsel__name(top->sym_evsel));
sleep(1);
@@ -550,25 +585,11 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c)
static void perf_top__sort_new_samples(void *arg)
{
struct perf_top *t = arg;
- struct perf_evsel *evsel = t->sym_evsel;
- struct hists *hists;
if (t->evlist->selected != NULL)
t->sym_evsel = t->evlist->selected;
- hists = evsel__hists(evsel);
-
- if (t->evlist->enabled) {
- if (t->zero) {
- hists__delete_entries(hists);
- } else {
- hists__decay_entries(hists, t->hide_user_symbols,
- t->hide_kernel_symbols);
- }
- }
-
- hists__collapse_resort(hists, NULL);
- perf_evsel__output_resort(evsel, NULL);
+ perf_top__resort_hists(t);
if (t->lost || t->drop)
pr_warning("Too slow to read ring buffer (change period (-c/-F) or limit CPUs (-C)\n");
@@ -582,7 +603,7 @@ static void stop_top(void)
static void *display_thread_tui(void *arg)
{
- struct perf_evsel *pos;
+ struct evsel *pos;
struct perf_top *top = arg;
const char *help = "For a higher level overview, try: perf top --sort comm,dso";
struct hist_browser_timer hbt = {
@@ -598,6 +619,8 @@ static void *display_thread_tui(void *arg)
*/
unshare(CLONE_FS);
+ prctl(PR_SET_NAME, "perf-top-UI", 0, 0, 0);
+
perf_top__sort_new_samples(top);
/*
@@ -648,6 +671,8 @@ static void *display_thread(void *arg)
*/
unshare(CLONE_FS);
+ prctl(PR_SET_NAME, "perf-top-UI", 0, 0, 0);
+
display_setup_sig();
pthread__unblock_sigwinch();
repeat:
@@ -689,7 +714,7 @@ static int hist_iter__top_callback(struct hist_entry_iter *iter,
{
struct perf_top *top = arg;
struct hist_entry *he = iter->he;
- struct perf_evsel *evsel = iter->evsel;
+ struct evsel *evsel = iter->evsel;
if (perf_hpp_list.sym && single)
perf_top__record_precise_ip(top, he, iter->sample, evsel, al->addr);
@@ -701,7 +726,7 @@ static int hist_iter__top_callback(struct hist_entry_iter *iter,
static void perf_event__process_sample(struct perf_tool *tool,
const union perf_event *event,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct machine *machine)
{
@@ -741,7 +766,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
if (!perf_evlist__exclude_kernel(top->session->evlist)) {
ui__warning(
"Kernel address maps (/proc/{kallsyms,modules}) are restricted.\n\n"
-"Check /proc/sys/kernel/kptr_restrict.\n\n"
+"Check /proc/sys/kernel/kptr_restrict and /proc/sys/kernel/perf_event_paranoid.\n\n"
"Kernel%s samples will not be resolved.\n",
al.map && map__has_symbols(al.map) ?
" modules" : "");
@@ -809,7 +834,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
static void
perf_top__process_lost(struct perf_top *top, union perf_event *event,
- struct perf_evsel *evsel)
+ struct evsel *evsel)
{
struct hists *hists = evsel__hists(evsel);
@@ -821,7 +846,7 @@ perf_top__process_lost(struct perf_top *top, union perf_event *event,
static void
perf_top__process_lost_samples(struct perf_top *top,
union perf_event *event,
- struct perf_evsel *evsel)
+ struct evsel *evsel)
{
struct hists *hists = evsel__hists(evsel);
@@ -835,7 +860,7 @@ static u64 last_timestamp;
static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
{
struct record_opts *opts = &top->record_opts;
- struct perf_evlist *evlist = top->evlist;
+ struct evlist *evlist = top->evlist;
struct perf_mmap *md;
union perf_event *event;
@@ -870,7 +895,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
static void perf_top__mmap_read(struct perf_top *top)
{
bool overwrite = top->record_opts.overwrite;
- struct perf_evlist *evlist = top->evlist;
+ struct evlist *evlist = top->evlist;
int i;
if (overwrite)
@@ -905,10 +930,10 @@ static void perf_top__mmap_read(struct perf_top *top)
static int perf_top__overwrite_check(struct perf_top *top)
{
struct record_opts *opts = &top->record_opts;
- struct perf_evlist *evlist = top->evlist;
+ struct evlist *evlist = top->evlist;
struct perf_evsel_config_term *term;
struct list_head *config_terms;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
int set, overwrite = -1;
evlist__for_each_entry(evlist, evsel) {
@@ -948,11 +973,11 @@ static int perf_top__overwrite_check(struct perf_top *top)
}
static int perf_top_overwrite_fallback(struct perf_top *top,
- struct perf_evsel *evsel)
+ struct evsel *evsel)
{
struct record_opts *opts = &top->record_opts;
- struct perf_evlist *evlist = top->evlist;
- struct perf_evsel *counter;
+ struct evlist *evlist = top->evlist;
+ struct evsel *counter;
if (!opts->overwrite)
return 0;
@@ -962,7 +987,7 @@ static int perf_top_overwrite_fallback(struct perf_top *top,
return 0;
evlist__for_each_entry(evlist, counter)
- counter->attr.write_backward = false;
+ counter->core.attr.write_backward = false;
opts->overwrite = false;
pr_debug2("fall back to non-overwrite mode\n");
return 1;
@@ -971,8 +996,8 @@ static int perf_top_overwrite_fallback(struct perf_top *top,
static int perf_top__start_counters(struct perf_top *top)
{
char msg[BUFSIZ];
- struct perf_evsel *counter;
- struct perf_evlist *evlist = top->evlist;
+ struct evsel *counter;
+ struct evlist *evlist = top->evlist;
struct record_opts *opts = &top->record_opts;
if (perf_top__overwrite_check(top)) {
@@ -985,8 +1010,8 @@ static int perf_top__start_counters(struct perf_top *top)
evlist__for_each_entry(evlist, counter) {
try_again:
- if (perf_evsel__open(counter, top->evlist->cpus,
- top->evlist->threads) < 0) {
+ if (evsel__open(counter, top->evlist->core.cpus,
+ top->evlist->core.threads) < 0) {
/*
* Specially handle overwrite fall back.
@@ -1096,11 +1121,11 @@ static int deliver_event(struct ordered_events *qe,
struct ordered_event *qevent)
{
struct perf_top *top = qe->data;
- struct perf_evlist *evlist = top->evlist;
+ struct evlist *evlist = top->evlist;
struct perf_session *session = top->session;
union perf_event *event = qevent->event;
struct perf_sample sample;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct machine *machine;
int ret = -1;
@@ -1119,8 +1144,11 @@ static int deliver_event(struct ordered_events *qe,
evsel = perf_evlist__id2evsel(session->evlist, sample.id);
assert(evsel != NULL);
- if (event->header.type == PERF_RECORD_SAMPLE)
+ if (event->header.type == PERF_RECORD_SAMPLE) {
+ if (evswitch__discard(&top->evswitch, evsel))
+ return 0;
++top->samples;
+ }
switch (sample.cpumode) {
case PERF_RECORD_MISC_USER:
@@ -1208,14 +1236,17 @@ static int __cmd_top(struct perf_top *top)
init_process_thread(top);
+ if (opts->record_namespaces)
+ top->tool.namespace_events = true;
+
ret = perf_event__synthesize_bpf_events(top->session, perf_event__process,
&top->session->machines.host,
&top->record_opts);
if (ret < 0)
- pr_warning("Couldn't synthesize bpf events.\n");
+ pr_debug("Couldn't synthesize BPF events: Pre-existing BPF programs won't have symbols resolved.\n");
machine__synthesize_threads(&top->session->machines.host, &opts->target,
- top->evlist->threads, false,
+ top->evlist->core.threads, false,
top->nr_threads_synthesize);
if (top->nr_threads_synthesize > 1)
@@ -1248,7 +1279,7 @@ static int __cmd_top(struct perf_top *top)
* so leave the check here.
*/
if (!target__none(&opts->target))
- perf_evlist__enable(top->evlist);
+ evlist__enable(top->evlist);
ret = -1;
if (pthread_create(&thread_process, NULL, process_thread, top)) {
@@ -1500,9 +1531,12 @@ int cmd_top(int argc, const char **argv)
OPT_BOOLEAN(0, "force", &symbol_conf.force, "don't complain, do it"),
OPT_UINTEGER(0, "num-thread-synthesize", &top.nr_threads_synthesize,
"number of thread to run event synthesize"),
+ OPT_BOOLEAN(0, "namespaces", &opts->record_namespaces,
+ "Record namespaces events"),
+ OPTS_EVSWITCH(&top.evswitch),
OPT_END()
};
- struct perf_evlist *sb_evlist = NULL;
+ struct evlist *sb_evlist = NULL;
const char * const top_usage[] = {
"perf top [<options>]",
NULL
@@ -1515,7 +1549,7 @@ int cmd_top(int argc, const char **argv)
top.annotation_opts.min_pcnt = 5;
top.annotation_opts.context = 4;
- top.evlist = perf_evlist__new();
+ top.evlist = evlist__new();
if (top.evlist == NULL)
return -ENOMEM;
@@ -1527,12 +1561,16 @@ int cmd_top(int argc, const char **argv)
if (argc)
usage_with_options(top_usage, options);
- if (!top.evlist->nr_entries &&
+ if (!top.evlist->core.nr_entries &&
perf_evlist__add_default(top.evlist) < 0) {
pr_err("Not enough memory for event selector list\n");
goto out_delete_evlist;
}
+ status = evswitch__init(&top.evswitch, top.evlist, stderr);
+ if (status)
+ goto out_delete_evlist;
+
if (symbol_conf.report_hierarchy) {
/* disable incompatible options */
symbol_conf.event_group = false;
@@ -1652,7 +1690,7 @@ int cmd_top(int argc, const char **argv)
perf_evlist__stop_sb_thread(sb_evlist);
out_delete_evlist:
- perf_evlist__delete(top.evlist);
+ evlist__delete(top.evlist);
perf_session__delete(top.session);
return status;
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index f5b3a1e9c1dd..0f633f0d6be8 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -12,22 +12,25 @@
* Initially based on the 'trace' prototype by Thomas Gleixner:
*
* http://lwn.net/Articles/415728/ ("Announcing a new utility: 'trace'")
- *
- * Released under the GPL v2. (and only v2, not any later version)
*/
+#include "util/record.h"
#include <traceevent/event-parse.h>
#include <api/fs/tracing_path.h>
#include <bpf/bpf.h>
#include "util/bpf_map.h"
+#include "util/rlimit.h"
#include "builtin.h"
#include "util/cgroup.h"
#include "util/color.h"
#include "util/config.h"
#include "util/debug.h"
+#include "util/dso.h"
#include "util/env.h"
#include "util/event.h"
#include "util/evlist.h"
+#include "util/evswitch.h"
+#include <subcmd/pager.h>
#include <subcmd/exec-cmd.h>
#include "util/machine.h"
#include "util/map.h"
@@ -40,6 +43,8 @@
#include "util/intlist.h"
#include "util/thread_map.h"
#include "util/stat.h"
+#include "util/tool.h"
+#include "util/util.h"
#include "trace/beauty/beauty.h"
#include "trace-event.h"
#include "util/parse-events.h"
@@ -49,6 +54,7 @@
#include "string2.h"
#include "syscalltbl.h"
#include "rb_resort.h"
+#include "../perf.h"
#include <errno.h>
#include <inttypes.h>
@@ -62,10 +68,11 @@
#include <linux/random.h>
#include <linux/stringify.h>
#include <linux/time64.h>
+#include <linux/zalloc.h>
#include <fcntl.h>
#include <sys/sysmacros.h>
-#include "sane_ctype.h"
+#include <linux/ctype.h>
#ifndef O_CLOEXEC
# define O_CLOEXEC 02000000
@@ -79,28 +86,34 @@ struct trace {
struct perf_tool tool;
struct syscalltbl *sctbl;
struct {
- int max;
struct syscall *table;
struct bpf_map *map;
+ struct { // per syscall BPF_MAP_TYPE_PROG_ARRAY
+ struct bpf_map *sys_enter,
+ *sys_exit;
+ } prog_array;
struct {
- struct perf_evsel *sys_enter,
+ struct evsel *sys_enter,
*sys_exit,
*augmented;
} events;
+ struct bpf_program *unaugmented_prog;
} syscalls;
struct {
struct bpf_map *map;
} dump;
struct record_opts opts;
- struct perf_evlist *evlist;
+ struct evlist *evlist;
struct machine *host;
struct thread *current;
+ struct bpf_object *bpf_obj;
struct cgroup *cgroup;
u64 base_time;
FILE *output;
unsigned long nr_events;
unsigned long nr_events_printed;
unsigned long max_events;
+ struct evswitch evswitch;
struct strlist *ev_qualifier;
struct {
size_t nr;
@@ -121,6 +134,7 @@ struct trace {
unsigned int min_stack;
int raw_augmented_syscalls_args_size;
bool raw_augmented_syscalls;
+ bool fd_path_disabled;
bool sort_events;
bool not_ev_qualifier;
bool live;
@@ -236,7 +250,7 @@ struct syscall_tp {
};
};
-static int perf_evsel__init_tp_uint_field(struct perf_evsel *evsel,
+static int perf_evsel__init_tp_uint_field(struct evsel *evsel,
struct tp_field *field,
const char *name)
{
@@ -252,7 +266,7 @@ static int perf_evsel__init_tp_uint_field(struct perf_evsel *evsel,
({ struct syscall_tp *sc = evsel->priv;\
perf_evsel__init_tp_uint_field(evsel, &sc->name, #name); })
-static int perf_evsel__init_tp_ptr_field(struct perf_evsel *evsel,
+static int perf_evsel__init_tp_ptr_field(struct evsel *evsel,
struct tp_field *field,
const char *name)
{
@@ -268,13 +282,13 @@ static int perf_evsel__init_tp_ptr_field(struct perf_evsel *evsel,
({ struct syscall_tp *sc = evsel->priv;\
perf_evsel__init_tp_ptr_field(evsel, &sc->name, #name); })
-static void perf_evsel__delete_priv(struct perf_evsel *evsel)
+static void evsel__delete_priv(struct evsel *evsel)
{
zfree(&evsel->priv);
- perf_evsel__delete(evsel);
+ evsel__delete(evsel);
}
-static int perf_evsel__init_syscall_tp(struct perf_evsel *evsel)
+static int perf_evsel__init_syscall_tp(struct evsel *evsel)
{
struct syscall_tp *sc = evsel->priv = malloc(sizeof(struct syscall_tp));
@@ -291,7 +305,7 @@ out_delete:
return -ENOENT;
}
-static int perf_evsel__init_augmented_syscall_tp(struct perf_evsel *evsel, struct perf_evsel *tp)
+static int perf_evsel__init_augmented_syscall_tp(struct evsel *evsel, struct evsel *tp)
{
struct syscall_tp *sc = evsel->priv = malloc(sizeof(struct syscall_tp));
@@ -313,21 +327,21 @@ out_delete:
return -EINVAL;
}
-static int perf_evsel__init_augmented_syscall_tp_args(struct perf_evsel *evsel)
+static int perf_evsel__init_augmented_syscall_tp_args(struct evsel *evsel)
{
struct syscall_tp *sc = evsel->priv;
return __tp_field__init_ptr(&sc->args, sc->id.offset + sizeof(u64));
}
-static int perf_evsel__init_augmented_syscall_tp_ret(struct perf_evsel *evsel)
+static int perf_evsel__init_augmented_syscall_tp_ret(struct evsel *evsel)
{
struct syscall_tp *sc = evsel->priv;
return __tp_field__init_uint(&sc->ret, sizeof(u64), sc->id.offset + sizeof(u64), evsel->needs_swap);
}
-static int perf_evsel__init_raw_syscall_tp(struct perf_evsel *evsel, void *handler)
+static int perf_evsel__init_raw_syscall_tp(struct evsel *evsel, void *handler)
{
evsel->priv = malloc(sizeof(struct syscall_tp));
if (evsel->priv != NULL) {
@@ -345,9 +359,9 @@ out_delete:
return -ENOENT;
}
-static struct perf_evsel *perf_evsel__raw_syscall_newtp(const char *direction, void *handler)
+static struct evsel *perf_evsel__raw_syscall_newtp(const char *direction, void *handler)
{
- struct perf_evsel *evsel = perf_evsel__newtp("raw_syscalls", direction);
+ struct evsel *evsel = perf_evsel__newtp("raw_syscalls", direction);
/* older kernel (e.g., RHEL6) use syscalls:{enter,exit} */
if (IS_ERR(evsel))
@@ -362,7 +376,7 @@ static struct perf_evsel *perf_evsel__raw_syscall_newtp(const char *direction, v
return evsel;
out_delete:
- perf_evsel__delete_priv(evsel);
+ evsel__delete_priv(evsel);
return NULL;
}
@@ -403,6 +417,11 @@ static size_t syscall_arg__scnprintf_strarray(char *bf, size_t size,
#define SCA_STRARRAY syscall_arg__scnprintf_strarray
+size_t syscall_arg__scnprintf_strarray_flags(char *bf, size_t size, struct syscall_arg *arg)
+{
+ return strarray__scnprintf_flags(arg->parm, bf, size, arg->show_string_prefix, arg->val);
+}
+
size_t strarrays__scnprintf(struct strarrays *sas, char *bf, size_t size, const char *intfmt, bool show_prefix, int val)
{
size_t printed;
@@ -482,6 +501,15 @@ static const char *bpf_cmd[] = {
};
static DEFINE_STRARRAY(bpf_cmd, "BPF_");
+static const char *fsmount_flags[] = {
+ [1] = "CLOEXEC",
+};
+static DEFINE_STRARRAY(fsmount_flags, "FSMOUNT_");
+
+#include "trace/beauty/generated/fsconfig_arrays.c"
+
+static DEFINE_STRARRAY(fsconfig_cmds, "FSCONFIG_");
+
static const char *epoll_ctl_ops[] = { "ADD", "DEL", "MOD", };
static DEFINE_STRARRAY_OFFSET(epoll_ctl_ops, "EPOLL_CTL_", 1);
@@ -642,6 +670,10 @@ static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size,
{ .scnprintf = SCA_STRARRAY, \
.parm = &strarray__##array, }
+#define STRARRAY_FLAGS(name, array) \
+ { .scnprintf = SCA_STRARRAY_FLAGS, \
+ .parm = &strarray__##array, }
+
#include "trace/beauty/arch_errno_names.c"
#include "trace/beauty/eventfd.c"
#include "trace/beauty/futex_op.c"
@@ -669,6 +701,10 @@ struct syscall_arg_fmt {
static struct syscall_fmt {
const char *name;
const char *alias;
+ struct {
+ const char *sys_enter,
+ *sys_exit;
+ } bpf_prog_name;
struct syscall_arg_fmt arg[6];
u8 nr_args;
bool errpid;
@@ -681,7 +717,9 @@ static struct syscall_fmt {
.arg = { [0] = { .scnprintf = SCA_X86_ARCH_PRCTL_CODE, /* code */ },
[1] = { .scnprintf = SCA_PTR, /* arg2 */ }, }, },
{ .name = "bind",
- .arg = { [1] = { .scnprintf = SCA_SOCKADDR, /* umyaddr */ }, }, },
+ .arg = { [0] = { .scnprintf = SCA_INT, /* fd */ },
+ [1] = { .scnprintf = SCA_SOCKADDR, /* umyaddr */ },
+ [2] = { .scnprintf = SCA_INT, /* addrlen */ }, }, },
{ .name = "bpf",
.arg = { [0] = STRARRAY(cmd, bpf_cmd), }, },
{ .name = "brk", .hexret = true,
@@ -697,7 +735,9 @@ static struct syscall_fmt {
{ .name = "close",
.arg = { [0] = { .scnprintf = SCA_CLOSE_FD, /* fd */ }, }, },
{ .name = "connect",
- .arg = { [1] = { .scnprintf = SCA_SOCKADDR, /* servaddr */ }, }, },
+ .arg = { [0] = { .scnprintf = SCA_INT, /* fd */ },
+ [1] = { .scnprintf = SCA_SOCKADDR, /* servaddr */ },
+ [2] = { .scnprintf = SCA_INT, /* addrlen */ }, }, },
{ .name = "epoll_ctl",
.arg = { [1] = STRARRAY(op, epoll_ctl_ops), }, },
{ .name = "eventfd2",
@@ -713,6 +753,15 @@ static struct syscall_fmt {
[2] = { .scnprintf = SCA_FCNTL_ARG, /* arg */ }, }, },
{ .name = "flock",
.arg = { [1] = { .scnprintf = SCA_FLOCK, /* cmd */ }, }, },
+ { .name = "fsconfig",
+ .arg = { [1] = STRARRAY(cmd, fsconfig_cmds), }, },
+ { .name = "fsmount",
+ .arg = { [1] = STRARRAY_FLAGS(flags, fsmount_flags),
+ [2] = { .scnprintf = SCA_FSMOUNT_ATTR_FLAGS, /* attr_flags */ }, }, },
+ { .name = "fspick",
+ .arg = { [0] = { .scnprintf = SCA_FDAT, /* dfd */ },
+ [1] = { .scnprintf = SCA_FILENAME, /* path */ },
+ [2] = { .scnprintf = SCA_FSPICK_FLAGS, /* flags */ }, }, },
{ .name = "fstat", .alias = "newfstat", },
{ .name = "fstatat", .alias = "newfstatat", },
{ .name = "futex",
@@ -775,6 +824,12 @@ static struct syscall_fmt {
.arg = { [0] = { .scnprintf = SCA_FILENAME, /* dev_name */ },
[3] = { .scnprintf = SCA_MOUNT_FLAGS, /* flags */
.mask_val = SCAMV_MOUNT_FLAGS, /* flags */ }, }, },
+ { .name = "move_mount",
+ .arg = { [0] = { .scnprintf = SCA_FDAT, /* from_dfd */ },
+ [1] = { .scnprintf = SCA_FILENAME, /* from_pathname */ },
+ [2] = { .scnprintf = SCA_FDAT, /* to_dfd */ },
+ [3] = { .scnprintf = SCA_FILENAME, /* to_pathname */ },
+ [4] = { .scnprintf = SCA_MOVE_MOUNT_FLAGS, /* flags */ }, }, },
{ .name = "mprotect",
.arg = { [0] = { .scnprintf = SCA_HEX, /* start */ },
[2] = { .scnprintf = SCA_MMAP_PROT, /* prot */ }, }, },
@@ -848,6 +903,7 @@ static struct syscall_fmt {
.arg = { [0] = { .scnprintf = SCA_SECCOMP_OP, /* op */ },
[1] = { .scnprintf = SCA_SECCOMP_FLAGS, /* flags */ }, }, },
{ .name = "select", .timeout = true, },
+ { .name = "sendfile", .alias = "sendfile64", },
{ .name = "sendmmsg",
.arg = { [3] = { .scnprintf = SCA_MSG_FLAGS, /* flags */ }, }, },
{ .name = "sendmsg",
@@ -879,6 +935,8 @@ static struct syscall_fmt {
.arg = { [0] = { .scnprintf = SCA_FILENAME, /* specialfile */ }, }, },
{ .name = "symlinkat",
.arg = { [0] = { .scnprintf = SCA_FDAT, /* dfd */ }, }, },
+ { .name = "sync_file_range",
+ .arg = { [3] = { .scnprintf = SCA_SYNC_FILE_RANGE_FLAGS, /* flags */ }, }, },
{ .name = "tgkill",
.arg = { [2] = { .scnprintf = SCA_SIGNUM, /* sig */ }, }, },
{ .name = "tkill",
@@ -924,21 +982,33 @@ static struct syscall_fmt *syscall_fmt__find_by_alias(const char *alias)
* is_exit: is this "exit" or "exit_group"?
* is_open: is this "open" or "openat"? To associate the fd returned in sys_exit with the pathname in sys_enter.
* args_size: sum of the sizes of the syscall arguments, anything after that is augmented stuff: pathname for openat, etc.
+ * nonexistent: Just a hole in the syscall table, syscall id not allocated
*/
struct syscall {
struct tep_event *tp_format;
int nr_args;
int args_size;
+ struct {
+ struct bpf_program *sys_enter,
+ *sys_exit;
+ } bpf_prog;
bool is_exit;
bool is_open;
+ bool nonexistent;
struct tep_format_field *args;
const char *name;
struct syscall_fmt *fmt;
struct syscall_arg_fmt *arg_fmt;
};
+/*
+ * Must match what is in the BPF program:
+ *
+ * tools/perf/examples/bpf/augmented_raw_syscalls.c
+ */
struct bpf_map_syscall_entry {
bool enabled;
+ u16 string_args_len[6];
};
/*
@@ -998,10 +1068,10 @@ static struct thread_trace *thread_trace__new(void)
{
struct thread_trace *ttrace = zalloc(sizeof(struct thread_trace));
- if (ttrace)
+ if (ttrace) {
ttrace->files.max = -1;
-
- ttrace->syscall_stats = intlist__new(NULL);
+ ttrace->syscall_stats = intlist__new(NULL);
+ }
return ttrace;
}
@@ -1121,7 +1191,7 @@ static const char *thread__fd_path(struct thread *thread, int fd,
{
struct thread_trace *ttrace = thread__priv(thread);
- if (ttrace == NULL)
+ if (ttrace == NULL || trace->fd_path_disabled)
return NULL;
if (fd < 0)
@@ -1192,8 +1262,17 @@ static void thread__set_filename_pos(struct thread *thread, const char *bf,
static size_t syscall_arg__scnprintf_augmented_string(struct syscall_arg *arg, char *bf, size_t size)
{
struct augmented_arg *augmented_arg = arg->augmented.args;
+ size_t printed = scnprintf(bf, size, "\"%.*s\"", augmented_arg->size, augmented_arg->value);
+ /*
+ * So that the next arg with a payload can consume its augmented arg, i.e. for rename* syscalls
+ * we would have two strings, each prefixed by its size.
+ */
+ int consumed = sizeof(*augmented_arg) + augmented_arg->size;
+
+ arg->augmented.args = ((void *)arg->augmented.args) + consumed;
+ arg->augmented.size -= consumed;
- return scnprintf(bf, size, "\"%.*s\"", augmented_arg->size, augmented_arg->value);
+ return printed;
}
static size_t syscall_arg__scnprintf_filename(char *bf, size_t size,
@@ -1308,7 +1387,7 @@ static char *trace__machine__resolve_kernel_addr(void *vmachine, unsigned long l
if (symbol_conf.kptr_restrict) {
pr_warning("Kernel address maps (/proc/{kallsyms,modules}) are restricted.\n\n"
- "Check /proc/sys/kernel/kptr_restrict.\n\n"
+ "Check /proc/sys/kernel/kptr_restrict and /proc/sys/kernel/perf_event_paranoid.\n\n"
"Kernel samples will not be resolved.\n");
machine->kptr_restrict_warned = true;
return NULL;
@@ -1317,7 +1396,7 @@ static char *trace__machine__resolve_kernel_addr(void *vmachine, unsigned long l
return machine__resolve_kernel_addr(vmachine, addrp, modp);
}
-static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist)
+static int trace__symbols_init(struct trace *trace, struct evlist *evlist)
{
int err = symbol__init(NULL);
@@ -1333,7 +1412,7 @@ static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist)
goto out;
err = __machine__synthesize_threads(trace->host, &trace->tool, &trace->opts.target,
- evlist->threads, trace__tool_process, false,
+ evlist->core.threads, trace__tool_process, false,
1);
out:
if (err)
@@ -1381,10 +1460,11 @@ static int syscall__set_arg_fmts(struct syscall *sc)
if (sc->fmt && sc->fmt->arg[idx].scnprintf)
continue;
+ len = strlen(field->name);
+
if (strcmp(field->type, "const char *") == 0 &&
- (strcmp(field->name, "filename") == 0 ||
- strcmp(field->name, "path") == 0 ||
- strcmp(field->name, "pathname") == 0))
+ ((len >= 4 && strcmp(field->name + len - 4, "name") == 0) ||
+ strstr(field->name, "path") != NULL))
sc->arg_fmt[idx].scnprintf = SCA_FILENAME;
else if ((field->flags & TEP_FIELD_IS_POINTER) || strstr(field->name, "addr"))
sc->arg_fmt[idx].scnprintf = SCA_PTR;
@@ -1395,8 +1475,7 @@ static int syscall__set_arg_fmts(struct syscall *sc)
else if ((strcmp(field->type, "int") == 0 ||
strcmp(field->type, "unsigned int") == 0 ||
strcmp(field->type, "long") == 0) &&
- (len = strlen(field->name)) >= 2 &&
- strcmp(field->name + len - 2, "fd") == 0) {
+ len >= 2 && strcmp(field->name + len - 2, "fd") == 0) {
/*
* /sys/kernel/tracing/events/syscalls/sys_enter*
* egrep 'field:.*fd;' .../format|sed -r 's/.*field:([a-z ]+) [a-z_]*fd.+/\1/g'|sort|uniq -c
@@ -1420,29 +1499,22 @@ static int trace__read_syscall_info(struct trace *trace, int id)
struct syscall *sc;
const char *name = syscalltbl__name(trace->sctbl, id);
- if (name == NULL)
- return -1;
-
- if (id > trace->syscalls.max) {
- struct syscall *nsyscalls = realloc(trace->syscalls.table, (id + 1) * sizeof(*sc));
-
- if (nsyscalls == NULL)
- return -1;
+ if (trace->syscalls.table == NULL) {
+ trace->syscalls.table = calloc(trace->sctbl->syscalls.max_id + 1, sizeof(*sc));
+ if (trace->syscalls.table == NULL)
+ return -ENOMEM;
+ }
- if (trace->syscalls.max != -1) {
- memset(nsyscalls + trace->syscalls.max + 1, 0,
- (id - trace->syscalls.max) * sizeof(*sc));
- } else {
- memset(nsyscalls, 0, (id + 1) * sizeof(*sc));
- }
+ sc = trace->syscalls.table + id;
+ if (sc->nonexistent)
+ return 0;
- trace->syscalls.table = nsyscalls;
- trace->syscalls.max = id;
+ if (name == NULL) {
+ sc->nonexistent = true;
+ return 0;
}
- sc = trace->syscalls.table + id;
sc->name = name;
-
sc->fmt = syscall_fmt__find(sc->name);
snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name);
@@ -1454,10 +1526,10 @@ static int trace__read_syscall_info(struct trace *trace, int id)
}
if (syscall__alloc_arg_fmts(sc, IS_ERR(sc->tp_format) ? 6 : sc->tp_format->format.nr_fields))
- return -1;
+ return -ENOMEM;
if (IS_ERR(sc->tp_format))
- return -1;
+ return PTR_ERR(sc->tp_format);
sc->args = sc->tp_format->format.fields;
/*
@@ -1476,14 +1548,21 @@ static int trace__read_syscall_info(struct trace *trace, int id)
return syscall__set_arg_fmts(sc);
}
+static int intcmp(const void *a, const void *b)
+{
+ const int *one = a, *another = b;
+
+ return *one - *another;
+}
+
static int trace__validate_ev_qualifier(struct trace *trace)
{
- int err = 0, i;
- size_t nr_allocated;
+ int err = 0;
+ bool printed_invalid_prefix = false;
struct str_node *pos;
+ size_t nr_used = 0, nr_allocated = strlist__nr_entries(trace->ev_qualifier);
- trace->ev_qualifier_ids.nr = strlist__nr_entries(trace->ev_qualifier);
- trace->ev_qualifier_ids.entries = malloc(trace->ev_qualifier_ids.nr *
+ trace->ev_qualifier_ids.entries = malloc(nr_allocated *
sizeof(trace->ev_qualifier_ids.entries[0]));
if (trace->ev_qualifier_ids.entries == NULL) {
@@ -1493,9 +1572,6 @@ static int trace__validate_ev_qualifier(struct trace *trace)
goto out;
}
- nr_allocated = trace->ev_qualifier_ids.nr;
- i = 0;
-
strlist__for_each_entry(pos, trace->ev_qualifier) {
const char *sc = pos->s;
int id = syscalltbl__id(trace->sctbl, sc), match_next = -1;
@@ -1505,17 +1581,18 @@ static int trace__validate_ev_qualifier(struct trace *trace)
if (id >= 0)
goto matches;
- if (err == 0) {
- fputs("Error:\tInvalid syscall ", trace->output);
- err = -EINVAL;
+ if (!printed_invalid_prefix) {
+ pr_debug("Skipping unknown syscalls: ");
+ printed_invalid_prefix = true;
} else {
- fputs(", ", trace->output);
+ pr_debug(", ");
}
- fputs(sc, trace->output);
+ pr_debug("%s", sc);
+ continue;
}
matches:
- trace->ev_qualifier_ids.entries[i++] = id;
+ trace->ev_qualifier_ids.entries[nr_used++] = id;
if (match_next == -1)
continue;
@@ -1523,7 +1600,7 @@ matches:
id = syscalltbl__strglobmatch_next(trace->sctbl, sc, &match_next);
if (id < 0)
break;
- if (nr_allocated == trace->ev_qualifier_ids.nr) {
+ if (nr_allocated == nr_used) {
void *entries;
nr_allocated += 8;
@@ -1536,20 +1613,36 @@ matches:
}
trace->ev_qualifier_ids.entries = entries;
}
- trace->ev_qualifier_ids.nr++;
- trace->ev_qualifier_ids.entries[i++] = id;
+ trace->ev_qualifier_ids.entries[nr_used++] = id;
}
}
- if (err < 0) {
- fputs("\nHint:\ttry 'perf list syscalls:sys_enter_*'"
- "\nHint:\tand: 'man syscalls'\n", trace->output);
-out_free:
- zfree(&trace->ev_qualifier_ids.entries);
- trace->ev_qualifier_ids.nr = 0;
- }
+ trace->ev_qualifier_ids.nr = nr_used;
+ qsort(trace->ev_qualifier_ids.entries, nr_used, sizeof(int), intcmp);
out:
+ if (printed_invalid_prefix)
+ pr_debug("\n");
return err;
+out_free:
+ zfree(&trace->ev_qualifier_ids.entries);
+ trace->ev_qualifier_ids.nr = 0;
+ goto out;
+}
+
+static __maybe_unused bool trace__syscall_enabled(struct trace *trace, int id)
+{
+ bool in_ev_qualifier;
+
+ if (trace->ev_qualifier_ids.nr == 0)
+ return true;
+
+ in_ev_qualifier = bsearch(&id, trace->ev_qualifier_ids.entries,
+ trace->ev_qualifier_ids.nr, sizeof(int), intcmp) != NULL;
+
+ if (in_ev_qualifier)
+ return !trace->not_ev_qualifier;
+
+ return trace->not_ev_qualifier;
}
/*
@@ -1690,13 +1783,14 @@ next_arg:
return printed;
}
-typedef int (*tracepoint_handler)(struct trace *trace, struct perf_evsel *evsel,
+typedef int (*tracepoint_handler)(struct trace *trace, struct evsel *evsel,
union perf_event *event,
struct perf_sample *sample);
static struct syscall *trace__syscall_info(struct trace *trace,
- struct perf_evsel *evsel, int id)
+ struct evsel *evsel, int id)
{
+ int err = 0;
if (id < 0) {
@@ -1718,19 +1812,28 @@ static struct syscall *trace__syscall_info(struct trace *trace,
return NULL;
}
- if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL) &&
- trace__read_syscall_info(trace, id))
+ err = -EINVAL;
+
+ if (id > trace->sctbl->syscalls.max_id)
+ goto out_cant_read;
+
+ if ((trace->syscalls.table == NULL || trace->syscalls.table[id].name == NULL) &&
+ (err = trace__read_syscall_info(trace, id)) != 0)
goto out_cant_read;
- if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL))
+ if (trace->syscalls.table[id].name == NULL) {
+ if (trace->syscalls.table[id].nonexistent)
+ return NULL;
goto out_cant_read;
+ }
return &trace->syscalls.table[id];
out_cant_read:
if (verbose > 0) {
- fprintf(trace->output, "Problems reading syscall %d", id);
- if (id <= trace->syscalls.max && trace->syscalls.table[id].name != NULL)
+ char sbuf[STRERR_BUFSIZE];
+ fprintf(trace->output, "Problems reading syscall %d: %d (%s)", id, -err, str_error_r(-err, sbuf, sizeof(sbuf)));
+ if (id <= trace->sctbl->syscalls.max_id && trace->syscalls.table[id].name != NULL)
fprintf(trace->output, "(%s)", trace->syscalls.table[id].name);
fputs(" information\n", trace->output);
}
@@ -1791,7 +1894,7 @@ static int trace__printf_interrupted_entry(struct trace *trace)
return printed;
}
-static int trace__fprintf_sample(struct trace *trace, struct perf_evsel *evsel,
+static int trace__fprintf_sample(struct trace *trace, struct evsel *evsel,
struct perf_sample *sample, struct thread *thread)
{
int printed = 0;
@@ -1834,7 +1937,7 @@ static void *syscall__augmented_args(struct syscall *sc, struct perf_sample *sam
return augmented_args;
}
-static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
+static int trace__sys_enter(struct trace *trace, struct evsel *evsel,
union perf_event *event __maybe_unused,
struct perf_sample *sample)
{
@@ -1913,7 +2016,7 @@ out_put:
return err;
}
-static int trace__fprintf_sys_enter(struct trace *trace, struct perf_evsel *evsel,
+static int trace__fprintf_sys_enter(struct trace *trace, struct evsel *evsel,
struct perf_sample *sample)
{
struct thread_trace *ttrace;
@@ -1946,13 +2049,13 @@ out_put:
return err;
}
-static int trace__resolve_callchain(struct trace *trace, struct perf_evsel *evsel,
+static int trace__resolve_callchain(struct trace *trace, struct evsel *evsel,
struct perf_sample *sample,
struct callchain_cursor *cursor)
{
struct addr_location al;
- int max_stack = evsel->attr.sample_max_stack ?
- evsel->attr.sample_max_stack :
+ int max_stack = evsel->core.attr.sample_max_stack ?
+ evsel->core.attr.sample_max_stack :
trace->max_stack;
int err;
@@ -1974,7 +2077,7 @@ static int trace__fprintf_callchain(struct trace *trace, struct perf_sample *sam
return sample__fprintf_callchain(sample, 38, print_opts, &callchain_cursor, trace->output);
}
-static const char *errno_to_name(struct perf_evsel *evsel, int err)
+static const char *errno_to_name(struct evsel *evsel, int err)
{
struct perf_env *env = perf_evsel__env(evsel);
const char *arch_name = perf_env__arch(env);
@@ -1982,7 +2085,7 @@ static const char *errno_to_name(struct perf_evsel *evsel, int err)
return arch_syscalls__strerrno(arch_name, err);
}
-static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
+static int trace__sys_exit(struct trace *trace, struct evsel *evsel,
union perf_event *event __maybe_unused,
struct perf_sample *sample)
{
@@ -2010,7 +2113,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
ret = perf_evsel__sc_tp_uint(evsel, ret, sample);
- if (sc->is_open && ret >= 0 && ttrace->filename.pending_open) {
+ if (!trace->fd_path_disabled && sc->is_open && ret >= 0 && ttrace->filename.pending_open) {
trace__set_fd_pathname(thread, ret, ttrace->filename.name);
ttrace->filename.pending_open = false;
++trace->stats.vfs_getname;
@@ -2116,7 +2219,7 @@ out_put:
return err;
}
-static int trace__vfs_getname(struct trace *trace, struct perf_evsel *evsel,
+static int trace__vfs_getname(struct trace *trace, struct evsel *evsel,
union perf_event *event __maybe_unused,
struct perf_sample *sample)
{
@@ -2177,7 +2280,7 @@ out:
return 0;
}
-static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evsel,
+static int trace__sched_stat_runtime(struct trace *trace, struct evsel *evsel,
union perf_event *event __maybe_unused,
struct perf_sample *sample)
{
@@ -2239,7 +2342,7 @@ static void bpf_output__fprintf(struct trace *trace,
++trace->nr_events_printed;
}
-static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel,
+static int trace__event_handler(struct trace *trace, struct evsel *evsel,
union perf_event *event __maybe_unused,
struct perf_sample *sample)
{
@@ -2305,8 +2408,8 @@ static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel,
++trace->nr_events_printed;
if (evsel->max_events != ULONG_MAX && ++evsel->nr_events_printed == evsel->max_events) {
- perf_evsel__disable(evsel);
- perf_evsel__close(evsel);
+ evsel__disable(evsel);
+ evsel__close(evsel);
}
}
}
@@ -2341,7 +2444,7 @@ static void print_location(FILE *f, struct perf_sample *sample,
}
static int trace__pgfault(struct trace *trace,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
union perf_event *event __maybe_unused,
struct perf_sample *sample)
{
@@ -2367,7 +2470,7 @@ static int trace__pgfault(struct trace *trace,
if (ttrace == NULL)
goto out_put;
- if (evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ)
+ if (evsel->core.attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ)
ttrace->pfmaj++;
else
ttrace->pfmin++;
@@ -2380,7 +2483,7 @@ static int trace__pgfault(struct trace *trace,
trace__fprintf_entry_head(trace, thread, 0, true, sample->time, trace->output);
fprintf(trace->output, "%sfault [",
- evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ ?
+ evsel->core.attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ ?
"maj" : "min");
print_location(trace->output, sample, &al, false, true);
@@ -2416,7 +2519,7 @@ out_put:
}
static void trace__set_base_time(struct trace *trace,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample)
{
/*
@@ -2428,14 +2531,14 @@ static void trace__set_base_time(struct trace *trace,
* appears in our event stream (vfs_getname comes to mind).
*/
if (trace->base_time == 0 && !trace->full_time &&
- (evsel->attr.sample_type & PERF_SAMPLE_TIME))
+ (evsel->core.attr.sample_type & PERF_SAMPLE_TIME))
trace->base_time = sample->time;
}
static int trace__process_sample(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine __maybe_unused)
{
struct trace *trace = container_of(tool, struct trace, tool);
@@ -2521,10 +2624,10 @@ static int trace__record(struct trace *trace, int argc, const char **argv)
static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp);
-static bool perf_evlist__add_vfs_getname(struct perf_evlist *evlist)
+static bool evlist__add_vfs_getname(struct evlist *evlist)
{
bool found = false;
- struct perf_evsel *evsel, *tmp;
+ struct evsel *evsel, *tmp;
struct parse_events_error err = { .idx = 0, };
int ret = parse_events(evlist, "probe:vfs_getname*", &err);
@@ -2541,17 +2644,17 @@ static bool perf_evlist__add_vfs_getname(struct perf_evlist *evlist)
continue;
}
- list_del_init(&evsel->node);
+ list_del_init(&evsel->core.node);
evsel->evlist = NULL;
- perf_evsel__delete(evsel);
+ evsel__delete(evsel);
}
return found;
}
-static struct perf_evsel *perf_evsel__new_pgfault(u64 config)
+static struct evsel *perf_evsel__new_pgfault(u64 config)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct perf_event_attr attr = {
.type = PERF_TYPE_SOFTWARE,
.mmap_data = 1,
@@ -2562,7 +2665,7 @@ static struct perf_evsel *perf_evsel__new_pgfault(u64 config)
event_attr_init(&attr);
- evsel = perf_evsel__new(&attr);
+ evsel = evsel__new(&attr);
if (evsel)
evsel->handler = trace__pgfault;
@@ -2572,7 +2675,7 @@ static struct perf_evsel *perf_evsel__new_pgfault(u64 config)
static void trace__handle_event(struct trace *trace, union perf_event *event, struct perf_sample *sample)
{
const u32 type = event->header.type;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
if (type != PERF_RECORD_SAMPLE) {
trace__process_event(trace, trace->host, event, sample);
@@ -2585,9 +2688,12 @@ static void trace__handle_event(struct trace *trace, union perf_event *event, st
return;
}
+ if (evswitch__discard(&trace->evswitch, evsel))
+ return;
+
trace__set_base_time(trace, evsel, sample);
- if (evsel->attr.type == PERF_TYPE_TRACEPOINT &&
+ if (evsel->core.attr.type == PERF_TYPE_TRACEPOINT &&
sample->raw_data == NULL) {
fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n",
perf_evsel__name(evsel), sample->tid,
@@ -2604,8 +2710,8 @@ static void trace__handle_event(struct trace *trace, union perf_event *event, st
static int trace__add_syscall_newtp(struct trace *trace)
{
int ret = -1;
- struct perf_evlist *evlist = trace->evlist;
- struct perf_evsel *sys_enter, *sys_exit;
+ struct evlist *evlist = trace->evlist;
+ struct evsel *sys_enter, *sys_exit;
sys_enter = perf_evsel__raw_syscall_newtp("sys_enter", trace__sys_enter);
if (sys_enter == NULL)
@@ -2624,8 +2730,8 @@ static int trace__add_syscall_newtp(struct trace *trace)
perf_evsel__config_callchain(sys_enter, &trace->opts, &callchain_param);
perf_evsel__config_callchain(sys_exit, &trace->opts, &callchain_param);
- perf_evlist__add(evlist, sys_enter);
- perf_evlist__add(evlist, sys_exit);
+ evlist__add(evlist, sys_enter);
+ evlist__add(evlist, sys_exit);
if (callchain_param.enabled && !trace->kernel_syscallchains) {
/*
@@ -2633,7 +2739,7 @@ static int trace__add_syscall_newtp(struct trace *trace)
* leading to the syscall, allow overriding that for
* debugging reasons using --kernel_syscall_callchains
*/
- sys_exit->attr.exclude_callchain_kernel = 1;
+ sys_exit->core.attr.exclude_callchain_kernel = 1;
}
trace->syscalls.events.sys_enter = sys_enter;
@@ -2644,16 +2750,16 @@ out:
return ret;
out_delete_sys_exit:
- perf_evsel__delete_priv(sys_exit);
+ evsel__delete_priv(sys_exit);
out_delete_sys_enter:
- perf_evsel__delete_priv(sys_enter);
+ evsel__delete_priv(sys_enter);
goto out;
}
static int trace__set_ev_qualifier_tp_filter(struct trace *trace)
{
int err = -1;
- struct perf_evsel *sys_exit;
+ struct evsel *sys_exit;
char *filter = asprintf_expr_inout_ints("id", !trace->not_ev_qualifier,
trace->ev_qualifier_ids.nr,
trace->ev_qualifier_ids.entries);
@@ -2676,6 +2782,89 @@ out_enomem:
}
#ifdef HAVE_LIBBPF_SUPPORT
+static struct bpf_program *trace__find_bpf_program_by_title(struct trace *trace, const char *name)
+{
+ if (trace->bpf_obj == NULL)
+ return NULL;
+
+ return bpf_object__find_program_by_title(trace->bpf_obj, name);
+}
+
+static struct bpf_program *trace__find_syscall_bpf_prog(struct trace *trace, struct syscall *sc,
+ const char *prog_name, const char *type)
+{
+ struct bpf_program *prog;
+
+ if (prog_name == NULL) {
+ char default_prog_name[256];
+ scnprintf(default_prog_name, sizeof(default_prog_name), "!syscalls:sys_%s_%s", type, sc->name);
+ prog = trace__find_bpf_program_by_title(trace, default_prog_name);
+ if (prog != NULL)
+ goto out_found;
+ if (sc->fmt && sc->fmt->alias) {
+ scnprintf(default_prog_name, sizeof(default_prog_name), "!syscalls:sys_%s_%s", type, sc->fmt->alias);
+ prog = trace__find_bpf_program_by_title(trace, default_prog_name);
+ if (prog != NULL)
+ goto out_found;
+ }
+ goto out_unaugmented;
+ }
+
+ prog = trace__find_bpf_program_by_title(trace, prog_name);
+
+ if (prog != NULL) {
+out_found:
+ return prog;
+ }
+
+ pr_debug("Couldn't find BPF prog \"%s\" to associate with syscalls:sys_%s_%s, not augmenting it\n",
+ prog_name, type, sc->name);
+out_unaugmented:
+ return trace->syscalls.unaugmented_prog;
+}
+
+static void trace__init_syscall_bpf_progs(struct trace *trace, int id)
+{
+ struct syscall *sc = trace__syscall_info(trace, NULL, id);
+
+ if (sc == NULL)
+ return;
+
+ sc->bpf_prog.sys_enter = trace__find_syscall_bpf_prog(trace, sc, sc->fmt ? sc->fmt->bpf_prog_name.sys_enter : NULL, "enter");
+ sc->bpf_prog.sys_exit = trace__find_syscall_bpf_prog(trace, sc, sc->fmt ? sc->fmt->bpf_prog_name.sys_exit : NULL, "exit");
+}
+
+static int trace__bpf_prog_sys_enter_fd(struct trace *trace, int id)
+{
+ struct syscall *sc = trace__syscall_info(trace, NULL, id);
+ return sc ? bpf_program__fd(sc->bpf_prog.sys_enter) : bpf_program__fd(trace->syscalls.unaugmented_prog);
+}
+
+static int trace__bpf_prog_sys_exit_fd(struct trace *trace, int id)
+{
+ struct syscall *sc = trace__syscall_info(trace, NULL, id);
+ return sc ? bpf_program__fd(sc->bpf_prog.sys_exit) : bpf_program__fd(trace->syscalls.unaugmented_prog);
+}
+
+static void trace__init_bpf_map_syscall_args(struct trace *trace, int id, struct bpf_map_syscall_entry *entry)
+{
+ struct syscall *sc = trace__syscall_info(trace, NULL, id);
+ int arg = 0;
+
+ if (sc == NULL)
+ goto out;
+
+ for (; arg < sc->nr_args; ++arg) {
+ entry->string_args_len[arg] = 0;
+ if (sc->arg_fmt[arg].scnprintf == SCA_FILENAME) {
+ /* Should be set like strace -s strsize */
+ entry->string_args_len[arg] = PATH_MAX;
+ }
+ }
+out:
+ for (; arg < 6; ++arg)
+ entry->string_args_len[arg] = 0;
+}
static int trace__set_ev_qualifier_bpf_filter(struct trace *trace)
{
int fd = bpf_map__fd(trace->syscalls.map);
@@ -2688,6 +2877,11 @@ static int trace__set_ev_qualifier_bpf_filter(struct trace *trace)
for (i = 0; i < trace->ev_qualifier_ids.nr; ++i) {
int key = trace->ev_qualifier_ids.entries[i];
+ if (value.enabled) {
+ trace__init_bpf_map_syscall_args(trace, key, &value);
+ trace__init_syscall_bpf_progs(trace, key);
+ }
+
err = bpf_map_update_elem(fd, &key, &value, BPF_EXIST);
if (err)
break;
@@ -2705,6 +2899,9 @@ static int __trace__init_syscalls_bpf_map(struct trace *trace, bool enabled)
int err = 0, key;
for (key = 0; key < trace->sctbl->syscalls.nr_entries; ++key) {
+ if (enabled)
+ trace__init_bpf_map_syscall_args(trace, key, &value);
+
err = bpf_map_update_elem(fd, &key, &value, BPF_ANY);
if (err)
break;
@@ -2722,6 +2919,186 @@ static int trace__init_syscalls_bpf_map(struct trace *trace)
return __trace__init_syscalls_bpf_map(trace, enabled);
}
+
+static struct bpf_program *trace__find_usable_bpf_prog_entry(struct trace *trace, struct syscall *sc)
+{
+ struct tep_format_field *field, *candidate_field;
+ int id;
+
+ /*
+ * We're only interested in syscalls that have a pointer:
+ */
+ for (field = sc->args; field; field = field->next) {
+ if (field->flags & TEP_FIELD_IS_POINTER)
+ goto try_to_find_pair;
+ }
+
+ return NULL;
+
+try_to_find_pair:
+ for (id = 0; id < trace->sctbl->syscalls.nr_entries; ++id) {
+ struct syscall *pair = trace__syscall_info(trace, NULL, id);
+ struct bpf_program *pair_prog;
+ bool is_candidate = false;
+
+ if (pair == NULL || pair == sc ||
+ pair->bpf_prog.sys_enter == trace->syscalls.unaugmented_prog)
+ continue;
+
+ for (field = sc->args, candidate_field = pair->args;
+ field && candidate_field; field = field->next, candidate_field = candidate_field->next) {
+ bool is_pointer = field->flags & TEP_FIELD_IS_POINTER,
+ candidate_is_pointer = candidate_field->flags & TEP_FIELD_IS_POINTER;
+
+ if (is_pointer) {
+ if (!candidate_is_pointer) {
+ // The candidate just doesn't copies our pointer arg, might copy other pointers we want.
+ continue;
+ }
+ } else {
+ if (candidate_is_pointer) {
+ // The candidate might copy a pointer we don't have, skip it.
+ goto next_candidate;
+ }
+ continue;
+ }
+
+ if (strcmp(field->type, candidate_field->type))
+ goto next_candidate;
+
+ is_candidate = true;
+ }
+
+ if (!is_candidate)
+ goto next_candidate;
+
+ /*
+ * Check if the tentative pair syscall augmenter has more pointers, if it has,
+ * then it may be collecting that and we then can't use it, as it would collect
+ * more than what is common to the two syscalls.
+ */
+ if (candidate_field) {
+ for (candidate_field = candidate_field->next; candidate_field; candidate_field = candidate_field->next)
+ if (candidate_field->flags & TEP_FIELD_IS_POINTER)
+ goto next_candidate;
+ }
+
+ pair_prog = pair->bpf_prog.sys_enter;
+ /*
+ * If the pair isn't enabled, then its bpf_prog.sys_enter will not
+ * have been searched for, so search it here and if it returns the
+ * unaugmented one, then ignore it, otherwise we'll reuse that BPF
+ * program for a filtered syscall on a non-filtered one.
+ *
+ * For instance, we have "!syscalls:sys_enter_renameat" and that is
+ * useful for "renameat2".
+ */
+ if (pair_prog == NULL) {
+ pair_prog = trace__find_syscall_bpf_prog(trace, pair, pair->fmt ? pair->fmt->bpf_prog_name.sys_enter : NULL, "enter");
+ if (pair_prog == trace->syscalls.unaugmented_prog)
+ goto next_candidate;
+ }
+
+ pr_debug("Reusing \"%s\" BPF sys_enter augmenter for \"%s\"\n", pair->name, sc->name);
+ return pair_prog;
+ next_candidate:
+ continue;
+ }
+
+ return NULL;
+}
+
+static int trace__init_syscalls_bpf_prog_array_maps(struct trace *trace)
+{
+ int map_enter_fd = bpf_map__fd(trace->syscalls.prog_array.sys_enter),
+ map_exit_fd = bpf_map__fd(trace->syscalls.prog_array.sys_exit);
+ int err = 0, key;
+
+ for (key = 0; key < trace->sctbl->syscalls.nr_entries; ++key) {
+ int prog_fd;
+
+ if (!trace__syscall_enabled(trace, key))
+ continue;
+
+ trace__init_syscall_bpf_progs(trace, key);
+
+ // It'll get at least the "!raw_syscalls:unaugmented"
+ prog_fd = trace__bpf_prog_sys_enter_fd(trace, key);
+ err = bpf_map_update_elem(map_enter_fd, &key, &prog_fd, BPF_ANY);
+ if (err)
+ break;
+ prog_fd = trace__bpf_prog_sys_exit_fd(trace, key);
+ err = bpf_map_update_elem(map_exit_fd, &key, &prog_fd, BPF_ANY);
+ if (err)
+ break;
+ }
+
+ /*
+ * Now lets do a second pass looking for enabled syscalls without
+ * an augmenter that have a signature that is a superset of another
+ * syscall with an augmenter so that we can auto-reuse it.
+ *
+ * I.e. if we have an augmenter for the "open" syscall that has
+ * this signature:
+ *
+ * int open(const char *pathname, int flags, mode_t mode);
+ *
+ * I.e. that will collect just the first string argument, then we
+ * can reuse it for the 'creat' syscall, that has this signature:
+ *
+ * int creat(const char *pathname, mode_t mode);
+ *
+ * and for:
+ *
+ * int stat(const char *pathname, struct stat *statbuf);
+ * int lstat(const char *pathname, struct stat *statbuf);
+ *
+ * Because the 'open' augmenter will collect the first arg as a string,
+ * and leave alone all the other args, which already helps with
+ * beautifying 'stat' and 'lstat''s pathname arg.
+ *
+ * Then, in time, when 'stat' gets an augmenter that collects both
+ * first and second arg (this one on the raw_syscalls:sys_exit prog
+ * array tail call, then that one will be used.
+ */
+ for (key = 0; key < trace->sctbl->syscalls.nr_entries; ++key) {
+ struct syscall *sc = trace__syscall_info(trace, NULL, key);
+ struct bpf_program *pair_prog;
+ int prog_fd;
+
+ if (sc == NULL || sc->bpf_prog.sys_enter == NULL)
+ continue;
+
+ /*
+ * For now we're just reusing the sys_enter prog, and if it
+ * already has an augmenter, we don't need to find one.
+ */
+ if (sc->bpf_prog.sys_enter != trace->syscalls.unaugmented_prog)
+ continue;
+
+ /*
+ * Look at all the other syscalls for one that has a signature
+ * that is close enough that we can share:
+ */
+ pair_prog = trace__find_usable_bpf_prog_entry(trace, sc);
+ if (pair_prog == NULL)
+ continue;
+
+ sc->bpf_prog.sys_enter = pair_prog;
+
+ /*
+ * Update the BPF_MAP_TYPE_PROG_SHARED for raw_syscalls:sys_enter
+ * with the fd for the program we're reusing:
+ */
+ prog_fd = bpf_program__fd(sc->bpf_prog.sys_enter);
+ err = bpf_map_update_elem(map_enter_fd, &key, &prog_fd, BPF_ANY);
+ if (err)
+ break;
+ }
+
+
+ return err;
+}
#else
static int trace__set_ev_qualifier_bpf_filter(struct trace *trace __maybe_unused)
{
@@ -2732,6 +3109,17 @@ static int trace__init_syscalls_bpf_map(struct trace *trace __maybe_unused)
{
return 0;
}
+
+static struct bpf_program *trace__find_bpf_program_by_title(struct trace *trace __maybe_unused,
+ const char *name __maybe_unused)
+{
+ return NULL;
+}
+
+static int trace__init_syscalls_bpf_prog_array_maps(struct trace *trace __maybe_unused)
+{
+ return 0;
+}
#endif // HAVE_LIBBPF_SUPPORT
static int trace__set_ev_qualifier_filter(struct trace *trace)
@@ -2806,7 +3194,7 @@ static int trace__set_filter_pids(struct trace *trace)
err = bpf_map__set_filter_pids(trace->filter_pids.map, trace->filter_pids.nr,
trace->filter_pids.entries);
}
- } else if (thread_map__pid(trace->evlist->threads, 0) == -1) {
+ } else if (perf_thread_map__pid(trace->evlist->core.threads, 0) == -1) {
err = trace__set_filter_loop_pids(trace);
}
@@ -2815,7 +3203,7 @@ static int trace__set_filter_pids(struct trace *trace)
static int __trace__deliver_event(struct trace *trace, union perf_event *event)
{
- struct perf_evlist *evlist = trace->evlist;
+ struct evlist *evlist = trace->evlist;
struct perf_sample sample;
int err;
@@ -2873,8 +3261,8 @@ static int ordered_events__deliver_event(struct ordered_events *oe,
static int trace__run(struct trace *trace, int argc, const char **argv)
{
- struct perf_evlist *evlist = trace->evlist;
- struct perf_evsel *evsel, *pgfault_maj = NULL, *pgfault_min = NULL;
+ struct evlist *evlist = trace->evlist;
+ struct evsel *evsel, *pgfault_maj = NULL, *pgfault_min = NULL;
int err = -1, i;
unsigned long before;
const bool forks = argc > 0;
@@ -2887,7 +3275,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
goto out_error_raw_syscalls;
if (trace->trace_syscalls)
- trace->vfs_getname = perf_evlist__add_vfs_getname(evlist);
+ trace->vfs_getname = evlist__add_vfs_getname(evlist);
}
if ((trace->trace_pgfaults & TRACE_PFMAJ)) {
@@ -2895,7 +3283,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
if (pgfault_maj == NULL)
goto out_error_mem;
perf_evsel__config_callchain(pgfault_maj, &trace->opts, &callchain_param);
- perf_evlist__add(evlist, pgfault_maj);
+ evlist__add(evlist, pgfault_maj);
}
if ((trace->trace_pgfaults & TRACE_PFMIN)) {
@@ -2903,7 +3291,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
if (pgfault_min == NULL)
goto out_error_mem;
perf_evsel__config_callchain(pgfault_min, &trace->opts, &callchain_param);
- perf_evlist__add(evlist, pgfault_min);
+ evlist__add(evlist, pgfault_min);
}
if (trace->sched &&
@@ -2965,7 +3353,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
}
}
- err = perf_evlist__open(evlist);
+ err = evlist__open(evlist);
if (err < 0)
goto out_error_open;
@@ -2986,6 +3374,9 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
if (trace->syscalls.map)
trace__init_syscalls_bpf_map(trace);
+ if (trace->syscalls.prog_array.sys_enter)
+ trace__init_syscalls_bpf_prog_array_maps(trace);
+
if (trace->ev_qualifier_ids.nr > 0) {
err = trace__set_ev_qualifier_filter(trace);
if (err < 0)
@@ -2997,6 +3388,19 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
}
}
+ /*
+ * If the "close" syscall is not traced, then we will not have the
+ * opportunity to, in syscall_arg__scnprintf_close_fd() invalidate the
+ * fd->pathname table and were ending up showing the last value set by
+ * syscalls opening a pathname and associating it with a descriptor or
+ * reading it from /proc/pid/fd/ in cases where that doesn't make
+ * sense.
+ *
+ * So just disable this beautifier (SCA_FD, SCA_FDAT) when 'close' is
+ * not in use.
+ */
+ trace->fd_path_disabled = !trace__syscall_enabled(trace, syscalltbl__id(trace->sctbl, "close"));
+
err = perf_evlist__apply_filters(evlist, &evsel);
if (err < 0)
goto out_error_apply_filters;
@@ -3009,30 +3413,30 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
goto out_error_mmap;
if (!target__none(&trace->opts.target) && !trace->opts.initial_delay)
- perf_evlist__enable(evlist);
+ evlist__enable(evlist);
if (forks)
perf_evlist__start_workload(evlist);
if (trace->opts.initial_delay) {
usleep(trace->opts.initial_delay * 1000);
- perf_evlist__enable(evlist);
+ evlist__enable(evlist);
}
- trace->multiple_threads = thread_map__pid(evlist->threads, 0) == -1 ||
- evlist->threads->nr > 1 ||
- perf_evlist__first(evlist)->attr.inherit;
+ trace->multiple_threads = perf_thread_map__pid(evlist->core.threads, 0) == -1 ||
+ evlist->core.threads->nr > 1 ||
+ perf_evlist__first(evlist)->core.attr.inherit;
/*
- * Now that we already used evsel->attr to ask the kernel to setup the
- * events, lets reuse evsel->attr.sample_max_stack as the limit in
+ * Now that we already used evsel->core.attr to ask the kernel to setup the
+ * events, lets reuse evsel->core.attr.sample_max_stack as the limit in
* trace__resolve_callchain(), allowing per-event max-stack settings
* to override an explicitly set --max-stack global setting.
*/
evlist__for_each_entry(evlist, evsel) {
if (evsel__has_callchain(evsel) &&
- evsel->attr.sample_max_stack == 0)
- evsel->attr.sample_max_stack = trace->max_stack;
+ evsel->core.attr.sample_max_stack == 0)
+ evsel->core.attr.sample_max_stack = trace->max_stack;
}
again:
before = trace->nr_events;
@@ -3058,7 +3462,7 @@ again:
goto out_disable;
if (done && !draining) {
- perf_evlist__disable(evlist);
+ evlist__disable(evlist);
draining = true;
}
}
@@ -3084,7 +3488,7 @@ again:
out_disable:
thread__zput(trace->current);
- perf_evlist__disable(evlist);
+ evlist__disable(evlist);
if (trace->sort_events)
ordered_events__flush(&trace->oe.data, OE_FLUSH__FINAL);
@@ -3105,7 +3509,7 @@ out_disable:
out_delete_evlist:
trace__symbols__exit(trace);
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
cgroup__put(trace->cgroup);
trace->evlist = NULL;
trace->live = false;
@@ -3150,7 +3554,7 @@ out_errno:
static int trace__replay(struct trace *trace)
{
- const struct perf_evsel_str_handler handlers[] = {
+ const struct evsel_str_handler handlers[] = {
{ "probe:vfs_getname", trace__vfs_getname, },
};
struct perf_data data = {
@@ -3159,7 +3563,7 @@ static int trace__replay(struct trace *trace)
.force = trace->force,
};
struct perf_session *session;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
int err = -1;
trace->tool.sample = trace__process_sample;
@@ -3225,10 +3629,10 @@ static int trace__replay(struct trace *trace)
}
evlist__for_each_entry(session->evlist, evsel) {
- if (evsel->attr.type == PERF_TYPE_SOFTWARE &&
- (evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ ||
- evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MIN ||
- evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS))
+ if (evsel->core.attr.type == PERF_TYPE_SOFTWARE &&
+ (evsel->core.attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ ||
+ evsel->core.attr.config == PERF_COUNT_SW_PAGE_FAULTS_MIN ||
+ evsel->core.attr.config == PERF_COUNT_SW_PAGE_FAULTS))
evsel->handler = trace__pgfault;
}
@@ -3450,17 +3854,17 @@ static int parse_pagefaults(const struct option *opt, const char *str,
return 0;
}
-static void evlist__set_evsel_handler(struct perf_evlist *evlist, void *handler)
+static void evlist__set_evsel_handler(struct evlist *evlist, void *handler)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel)
evsel->handler = handler;
}
-static int evlist__set_syscall_tp_fields(struct perf_evlist *evlist)
+static int evlist__set_syscall_tp_fields(struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) {
if (evsel->priv || !evsel->tp_format)
@@ -3587,7 +3991,7 @@ static int trace__parse_cgroups(const struct option *opt, const char *str, int u
{
struct trace *trace = opt->value;
- if (!list_empty(&trace->evlist->entries))
+ if (!list_empty(&trace->evlist->core.entries))
return parse_cgroups(opt, str, unset);
trace->cgroup = evlist__findnew_cgroup(trace->evlist, str);
@@ -3595,28 +3999,24 @@ static int trace__parse_cgroups(const struct option *opt, const char *str, int u
return 0;
}
-static struct bpf_map *bpf__find_map_by_name(const char *name)
+static struct bpf_map *trace__find_bpf_map_by_name(struct trace *trace, const char *name)
{
- struct bpf_object *obj, *tmp;
-
- bpf_object__for_each_safe(obj, tmp) {
- struct bpf_map *map = bpf_object__find_map_by_name(obj, name);
- if (map)
- return map;
-
- }
+ if (trace->bpf_obj == NULL)
+ return NULL;
- return NULL;
+ return bpf_object__find_map_by_name(trace->bpf_obj, name);
}
static void trace__set_bpf_map_filtered_pids(struct trace *trace)
{
- trace->filter_pids.map = bpf__find_map_by_name("pids_filtered");
+ trace->filter_pids.map = trace__find_bpf_map_by_name(trace, "pids_filtered");
}
static void trace__set_bpf_map_syscalls(struct trace *trace)
{
- trace->syscalls.map = bpf__find_map_by_name("syscalls");
+ trace->syscalls.map = trace__find_bpf_map_by_name(trace, "syscalls");
+ trace->syscalls.prog_array.sys_enter = trace__find_bpf_map_by_name(trace, "syscalls_sys_enter");
+ trace->syscalls.prog_array.sys_exit = trace__find_bpf_map_by_name(trace, "syscalls_sys_exit");
}
static int trace__config(const char *var, const char *value, void *arg)
@@ -3628,7 +4028,12 @@ static int trace__config(const char *var, const char *value, void *arg)
struct option o = OPT_CALLBACK('e', "event", &trace->evlist, "event",
"event selector. use 'perf list' to list available events",
parse_events_option);
- err = parse_events_option(&o, value, 0);
+ /*
+ * We can't propagate parse_event_option() return, as it is 1
+ * for failure while perf_config() expects -1.
+ */
+ if (parse_events_option(&o, value, 0))
+ err = -1;
} else if (!strcmp(var, "trace.show_timestamp")) {
trace->show_tstamp = perf_config_bool(var, value);
} else if (!strcmp(var, "trace.show_duration")) {
@@ -3667,9 +4072,6 @@ int cmd_trace(int argc, const char **argv)
NULL
};
struct trace trace = {
- .syscalls = {
- . max = -1,
- },
.opts = {
.target = {
.uid = UINT_MAX,
@@ -3766,11 +4168,12 @@ int cmd_trace(int argc, const char **argv)
OPT_UINTEGER('D', "delay", &trace.opts.initial_delay,
"ms to wait before starting measurement after program "
"start"),
+ OPTS_EVSWITCH(&trace.evswitch),
OPT_END()
};
bool __maybe_unused max_stack_user_set = true;
bool mmap_pages_user_set = true;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
const char * const trace_subcommands[] = { "record", NULL };
int err = -1;
char bf[BUFSIZ];
@@ -3778,7 +4181,7 @@ int cmd_trace(int argc, const char **argv)
signal(SIGSEGV, sighandler_dump_stack);
signal(SIGFPE, sighandler_dump_stack);
- trace.evlist = perf_evlist__new();
+ trace.evlist = evlist__new();
trace.sctbl = syscalltbl__new();
if (trace.evlist == NULL || trace.sctbl == NULL) {
@@ -3787,6 +4190,15 @@ int cmd_trace(int argc, const char **argv)
goto out;
}
+ /*
+ * Parsing .perfconfig may entail creating a BPF event, that may need
+ * to create BPF maps, so bump RLIM_MEMLOCK as the default 64K setting
+ * is too small. This affects just this process, not touching the
+ * global setting. If it fails we'll get something in 'perf trace -v'
+ * to help diagnose the problem.
+ */
+ rlimit__bump_memlock();
+
err = perf_config(trace__config, &trace);
if (err)
goto out;
@@ -3808,8 +4220,23 @@ int cmd_trace(int argc, const char **argv)
if (evsel) {
trace.syscalls.events.augmented = evsel;
+
+ evsel = perf_evlist__find_tracepoint_by_name(trace.evlist, "raw_syscalls:sys_enter");
+ if (evsel == NULL) {
+ pr_err("ERROR: raw_syscalls:sys_enter not found in the augmented BPF object\n");
+ goto out;
+ }
+
+ if (evsel->bpf_obj == NULL) {
+ pr_err("ERROR: raw_syscalls:sys_enter not associated to a BPF object\n");
+ goto out;
+ }
+
+ trace.bpf_obj = evsel->bpf_obj;
+
trace__set_bpf_map_filtered_pids(&trace);
trace__set_bpf_map_syscalls(&trace);
+ trace.syscalls.unaugmented_prog = trace__find_bpf_program_by_title(&trace, "!raw_syscalls:unaugmented");
}
err = bpf__setup_stdout(trace.evlist);
@@ -3822,7 +4249,7 @@ int cmd_trace(int argc, const char **argv)
err = -1;
if (map_dump_str) {
- trace.dump.map = bpf__find_map_by_name(map_dump_str);
+ trace.dump.map = trace__find_bpf_map_by_name(&trace, map_dump_str);
if (trace.dump.map == NULL) {
pr_err("ERROR: BPF map \"%s\" not found\n", map_dump_str);
goto out;
@@ -3855,7 +4282,7 @@ int cmd_trace(int argc, const char **argv)
symbol_conf.use_callchain = true;
}
- if (trace.evlist->nr_entries > 0) {
+ if (trace.evlist->core.nr_entries > 0) {
evlist__set_evsel_handler(trace.evlist, trace__event_handler);
if (evlist__set_syscall_tp_fields(trace.evlist)) {
perror("failed to set syscalls:* tracepoint fields");
@@ -3890,11 +4317,26 @@ int cmd_trace(int argc, const char **argv)
if (trace.syscalls.events.augmented->priv == NULL &&
strstr(perf_evsel__name(evsel), "syscalls:sys_enter")) {
- struct perf_evsel *augmented = trace.syscalls.events.augmented;
+ struct evsel *augmented = trace.syscalls.events.augmented;
if (perf_evsel__init_augmented_syscall_tp(augmented, evsel) ||
perf_evsel__init_augmented_syscall_tp_args(augmented))
goto out;
+ /*
+ * Augmented is __augmented_syscalls__ BPF_OUTPUT event
+ * Above we made sure we can get from the payload the tp fields
+ * that we get from syscalls:sys_enter tracefs format file.
+ */
augmented->handler = trace__sys_enter;
+ /*
+ * Now we do the same for the *syscalls:sys_enter event so that
+ * if we handle it directly, i.e. if the BPF prog returns 0 so
+ * as not to filter it, then we'll handle it just like we would
+ * for the BPF_OUTPUT one:
+ */
+ if (perf_evsel__init_augmented_syscall_tp(evsel, evsel) ||
+ perf_evsel__init_augmented_syscall_tp_args(evsel))
+ goto out;
+ evsel->handler = trace__sys_enter;
}
if (strstarts(perf_evsel__name(evsel), "syscalls:sys_exit_")) {
@@ -3938,7 +4380,7 @@ init_augmented_syscall_tp:
trace.summary = trace.summary_only;
if (!trace.trace_syscalls && !trace.trace_pgfaults &&
- trace.evlist->nr_entries == 0 /* Was --events used? */) {
+ trace.evlist->core.nr_entries == 0 /* Was --events used? */) {
trace.trace_syscalls = true;
}
@@ -3950,6 +4392,10 @@ init_augmented_syscall_tp:
}
}
+ err = evswitch__init(&trace.evswitch, trace.evlist, stderr);
+ if (err)
+ goto out_close;
+
err = target__validate(&trace.opts.target);
if (err) {
target__strerror(&trace.opts.target, err, bf, sizeof(bf));
diff --git a/tools/perf/builtin-version.c b/tools/perf/builtin-version.c
index f470144d1a70..05cf2af9e2c2 100644
--- a/tools/perf/builtin-version.c
+++ b/tools/perf/builtin-version.c
@@ -2,8 +2,8 @@
#include "builtin.h"
#include "perf.h"
#include "color.h"
-#include <linux/compiler.h>
#include <tools/config.h>
+#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <subcmd/parse-options.h>
@@ -19,6 +19,7 @@ static struct version version;
static struct option version_options[] = {
OPT_BOOLEAN(0, "build-options", &version.build_options,
"display the build options"),
+ OPT_END(),
};
static const char * const version_usage[] = {
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
index 999fe9170122..14a2db622a7b 100644
--- a/tools/perf/builtin.h
+++ b/tools/perf/builtin.h
@@ -2,8 +2,6 @@
#ifndef BUILTIN_H
#define BUILTIN_H
-#include "util/util.h"
-
extern const char perf_usage_string[];
extern const char perf_more_info_string[];
diff --git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh
index c68ee06cae63..e2e0f06c97d0 100755
--- a/tools/perf/check-headers.sh
+++ b/tools/perf/check-headers.sh
@@ -1,7 +1,8 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
-HEADERS='
+FILES='
+include/uapi/linux/const.h
include/uapi/drm/drm.h
include/uapi/drm/i915_drm.h
include/uapi/linux/fadvise.h
@@ -19,12 +20,16 @@ include/uapi/linux/usbdevice_fs.h
include/uapi/linux/vhost.h
include/uapi/sound/asound.h
include/linux/bits.h
+include/linux/const.h
include/linux/hash.h
include/uapi/linux/hw_breakpoint.h
arch/x86/include/asm/disabled-features.h
arch/x86/include/asm/required-features.h
arch/x86/include/asm/cpufeatures.h
+arch/x86/include/asm/inat_types.h
arch/x86/include/uapi/asm/prctl.h
+arch/x86/lib/x86-opcode-map.txt
+arch/x86/tools/gen-insn-attr-x86.awk
arch/arm/include/uapi/asm/perf_regs.h
arch/arm64/include/uapi/asm/perf_regs.h
arch/powerpc/include/uapi/asm/perf_regs.h
@@ -96,7 +101,7 @@ test -d ../../include || exit 0
cd ../..
# simple diff check
-for i in $HEADERS; do
+for i in $FILES; do
check $i -B
done
@@ -105,6 +110,12 @@ check arch/x86/lib/memcpy_64.S '-I "^EXPORT_SYMBOL" -I "^#include <asm/ex
check arch/x86/lib/memset_64.S '-I "^EXPORT_SYMBOL" -I "^#include <asm/export.h>"'
check include/uapi/asm-generic/mman.h '-I "^#include <\(uapi/\)*asm-generic/mman-common\(-tools\)*.h>"'
check include/uapi/linux/mman.h '-I "^#include <\(uapi/\)*asm/mman.h>"'
+check include/linux/ctype.h '-I "isdigit("'
+check lib/ctype.c '-I "^EXPORT_SYMBOL" -I "^#include <linux/export.h>" -B'
+check arch/x86/include/asm/inat.h '-I "^#include [\"<]\(asm/\)*inat_types.h[\">]"'
+check arch/x86/include/asm/insn.h '-I "^#include [\"<]\(asm/\)*inat.h[\">]"'
+check arch/x86/lib/inat.c '-I "^#include [\"<]\(../include/\)*asm/insn.h[\">]"'
+check arch/x86/lib/insn.c '-I "^#include [\"<]\(../include/\)*asm/in\(at\|sn\).h[\">]"'
# diff non-symmetric files
check_2 tools/perf/arch/x86/entry/syscalls/syscall_64.tbl arch/x86/entry/syscalls/syscall_64.tbl
diff --git a/tools/perf/examples/bpf/augmented_raw_syscalls.c b/tools/perf/examples/bpf/augmented_raw_syscalls.c
index 2422894a8194..b80437971d80 100644
--- a/tools/perf/examples/bpf/augmented_raw_syscalls.c
+++ b/tools/perf/examples/bpf/augmented_raw_syscalls.c
@@ -16,17 +16,38 @@
#include <unistd.h>
#include <linux/limits.h>
+#include <linux/socket.h>
#include <pid_filter.h>
/* bpf-output associated map */
bpf_map(__augmented_syscalls__, PERF_EVENT_ARRAY, int, u32, __NR_CPUS__);
+/*
+ * string_args_len: one per syscall arg, 0 means not a string or don't copy it,
+ * PATH_MAX for copying everything, any other value to limit
+ * it a la 'strace -s strsize'.
+ */
struct syscall {
bool enabled;
+ u16 string_args_len[6];
};
bpf_map(syscalls, ARRAY, int, struct syscall, 512);
+/*
+ * What to augment at entry?
+ *
+ * Pointer arg payloads (filenames, etc) passed from userspace to the kernel
+ */
+bpf_map(syscalls_sys_enter, PROG_ARRAY, u32, u32, 512);
+
+/*
+ * What to augment at exit?
+ *
+ * Pointer arg payloads returned from the kernel (struct stat, etc) to userspace.
+ */
+bpf_map(syscalls_sys_exit, PROG_ARRAY, u32, u32, 512);
+
struct syscall_enter_args {
unsigned long long common_tp_fields;
long syscall_nr;
@@ -39,272 +60,235 @@ struct syscall_exit_args {
long ret;
};
-struct augmented_filename {
+struct augmented_arg {
unsigned int size;
- int reserved;
+ int err;
char value[PATH_MAX];
};
-/* syscalls where the first arg is a string */
-#define SYS_OPEN 2
-#define SYS_STAT 4
-#define SYS_LSTAT 6
-#define SYS_ACCESS 21
-#define SYS_EXECVE 59
-#define SYS_TRUNCATE 76
-#define SYS_CHDIR 80
-#define SYS_RENAME 82
-#define SYS_MKDIR 83
-#define SYS_RMDIR 84
-#define SYS_CREAT 85
-#define SYS_LINK 86
-#define SYS_UNLINK 87
-#define SYS_SYMLINK 88
-#define SYS_READLINK 89
-#define SYS_CHMOD 90
-#define SYS_CHOWN 92
-#define SYS_LCHOWN 94
-#define SYS_MKNOD 133
-#define SYS_STATFS 137
-#define SYS_PIVOT_ROOT 155
-#define SYS_CHROOT 161
-#define SYS_ACCT 163
-#define SYS_SWAPON 167
-#define SYS_SWAPOFF 168
-#define SYS_DELETE_MODULE 176
-#define SYS_SETXATTR 188
-#define SYS_LSETXATTR 189
-#define SYS_GETXATTR 191
-#define SYS_LGETXATTR 192
-#define SYS_LISTXATTR 194
-#define SYS_LLISTXATTR 195
-#define SYS_REMOVEXATTR 197
-#define SYS_LREMOVEXATTR 198
-#define SYS_MQ_OPEN 240
-#define SYS_MQ_UNLINK 241
-#define SYS_ADD_KEY 248
-#define SYS_REQUEST_KEY 249
-#define SYS_SYMLINKAT 266
-#define SYS_MEMFD_CREATE 319
-
-/* syscalls where the first arg is a string */
-
-#define SYS_PWRITE64 18
-#define SYS_EXECVE 59
-#define SYS_RENAME 82
-#define SYS_QUOTACTL 179
-#define SYS_FSETXATTR 190
-#define SYS_FGETXATTR 193
-#define SYS_FREMOVEXATTR 199
-#define SYS_MQ_TIMEDSEND 242
-#define SYS_REQUEST_KEY 249
-#define SYS_INOTIFY_ADD_WATCH 254
-#define SYS_OPENAT 257
-#define SYS_MKDIRAT 258
-#define SYS_MKNODAT 259
-#define SYS_FCHOWNAT 260
-#define SYS_FUTIMESAT 261
-#define SYS_NEWFSTATAT 262
-#define SYS_UNLINKAT 263
-#define SYS_RENAMEAT 264
-#define SYS_LINKAT 265
-#define SYS_READLINKAT 267
-#define SYS_FCHMODAT 268
-#define SYS_FACCESSAT 269
-#define SYS_UTIMENSAT 280
-#define SYS_NAME_TO_HANDLE_AT 303
-#define SYS_FINIT_MODULE 313
-#define SYS_RENAMEAT2 316
-#define SYS_EXECVEAT 322
-#define SYS_STATX 332
-
pid_filter(pids_filtered);
-struct augmented_args_filename {
+struct augmented_args_payload {
struct syscall_enter_args args;
- struct augmented_filename filename;
+ union {
+ struct {
+ struct augmented_arg arg, arg2;
+ };
+ struct sockaddr_storage saddr;
+ };
};
-bpf_map(augmented_filename_map, PERCPU_ARRAY, int, struct augmented_args_filename, 1);
+// We need more tmp space than the BPF stack can give us
+bpf_map(augmented_args_tmp, PERCPU_ARRAY, int, struct augmented_args_payload, 1);
-SEC("raw_syscalls:sys_enter")
-int sys_enter(struct syscall_enter_args *args)
+static inline struct augmented_args_payload *augmented_args_payload(void)
{
- struct augmented_args_filename *augmented_args;
- unsigned int len = sizeof(*augmented_args);
- const void *filename_arg = NULL;
- struct syscall *syscall;
int key = 0;
+ return bpf_map_lookup_elem(&augmented_args_tmp, &key);
+}
+
+static inline int augmented__output(void *ctx, struct augmented_args_payload *args, int len)
+{
+ /* If perf_event_output fails, return non-zero so that it gets recorded unaugmented */
+ return perf_event_output(ctx, &__augmented_syscalls__, BPF_F_CURRENT_CPU, args, len);
+}
+
+static inline
+unsigned int augmented_arg__read_str(struct augmented_arg *augmented_arg, const void *arg, unsigned int arg_len)
+{
+ unsigned int augmented_len = sizeof(*augmented_arg);
+ int string_len = probe_read_str(&augmented_arg->value, arg_len, arg);
+
+ augmented_arg->size = augmented_arg->err = 0;
+ /*
+ * probe_read_str may return < 0, e.g. -EFAULT
+ * So we leave that in the augmented_arg->size that userspace will
+ */
+ if (string_len > 0) {
+ augmented_len -= sizeof(augmented_arg->value) - string_len;
+ augmented_len &= sizeof(augmented_arg->value) - 1;
+ augmented_arg->size = string_len;
+ } else {
+ /*
+ * So that username notice the error while still being able
+ * to skip this augmented arg record
+ */
+ augmented_arg->err = string_len;
+ augmented_len = offsetof(struct augmented_arg, value);
+ }
+
+ return augmented_len;
+}
+
+SEC("!raw_syscalls:unaugmented")
+int syscall_unaugmented(struct syscall_enter_args *args)
+{
+ return 1;
+}
+
+/*
+ * These will be tail_called from SEC("raw_syscalls:sys_enter"), so will find in
+ * augmented_args_tmp what was read by that raw_syscalls:sys_enter and go
+ * on from there, reading the first syscall arg as a string, i.e. open's
+ * filename.
+ */
+SEC("!syscalls:sys_enter_connect")
+int sys_enter_connect(struct syscall_enter_args *args)
+{
+ struct augmented_args_payload *augmented_args = augmented_args_payload();
+ const void *sockaddr_arg = (const void *)args->args[1];
+ unsigned int socklen = args->args[2];
+ unsigned int len = sizeof(augmented_args->args);
- augmented_args = bpf_map_lookup_elem(&augmented_filename_map, &key);
if (augmented_args == NULL)
- return 1;
+ return 1; /* Failure: don't filter */
- if (pid_filter__has(&pids_filtered, getpid()))
- return 0;
+ if (socklen > sizeof(augmented_args->saddr))
+ socklen = sizeof(augmented_args->saddr);
- probe_read(&augmented_args->args, sizeof(augmented_args->args), args);
+ probe_read(&augmented_args->saddr, socklen, sockaddr_arg);
- syscall = bpf_map_lookup_elem(&syscalls, &augmented_args->args.syscall_nr);
- if (syscall == NULL || !syscall->enabled)
- return 0;
+ return augmented__output(args, augmented_args, len + socklen);
+}
+
+SEC("!syscalls:sys_enter_sendto")
+int sys_enter_sendto(struct syscall_enter_args *args)
+{
+ struct augmented_args_payload *augmented_args = augmented_args_payload();
+ const void *sockaddr_arg = (const void *)args->args[4];
+ unsigned int socklen = args->args[5];
+ unsigned int len = sizeof(augmented_args->args);
+
+ if (augmented_args == NULL)
+ return 1; /* Failure: don't filter */
+
+ if (socklen > sizeof(augmented_args->saddr))
+ socklen = sizeof(augmented_args->saddr);
+
+ probe_read(&augmented_args->saddr, socklen, sockaddr_arg);
+
+ return augmented__output(args, augmented_args, len + socklen);
+}
+
+SEC("!syscalls:sys_enter_open")
+int sys_enter_open(struct syscall_enter_args *args)
+{
+ struct augmented_args_payload *augmented_args = augmented_args_payload();
+ const void *filename_arg = (const void *)args->args[0];
+ unsigned int len = sizeof(augmented_args->args);
+
+ if (augmented_args == NULL)
+ return 1; /* Failure: don't filter */
+
+ len += augmented_arg__read_str(&augmented_args->arg, filename_arg, sizeof(augmented_args->arg.value));
+
+ return augmented__output(args, augmented_args, len);
+}
+
+SEC("!syscalls:sys_enter_openat")
+int sys_enter_openat(struct syscall_enter_args *args)
+{
+ struct augmented_args_payload *augmented_args = augmented_args_payload();
+ const void *filename_arg = (const void *)args->args[1];
+ unsigned int len = sizeof(augmented_args->args);
+
+ if (augmented_args == NULL)
+ return 1; /* Failure: don't filter */
+
+ len += augmented_arg__read_str(&augmented_args->arg, filename_arg, sizeof(augmented_args->arg.value));
+
+ return augmented__output(args, augmented_args, len);
+}
+
+SEC("!syscalls:sys_enter_rename")
+int sys_enter_rename(struct syscall_enter_args *args)
+{
+ struct augmented_args_payload *augmented_args = augmented_args_payload();
+ const void *oldpath_arg = (const void *)args->args[0],
+ *newpath_arg = (const void *)args->args[1];
+ unsigned int len = sizeof(augmented_args->args), oldpath_len;
+
+ if (augmented_args == NULL)
+ return 1; /* Failure: don't filter */
+
+ oldpath_len = augmented_arg__read_str(&augmented_args->arg, oldpath_arg, sizeof(augmented_args->arg.value));
+ len += oldpath_len + augmented_arg__read_str((void *)(&augmented_args->arg) + oldpath_len, newpath_arg, sizeof(augmented_args->arg.value));
+
+ return augmented__output(args, augmented_args, len);
+}
+
+SEC("!syscalls:sys_enter_renameat")
+int sys_enter_renameat(struct syscall_enter_args *args)
+{
+ struct augmented_args_payload *augmented_args = augmented_args_payload();
+ const void *oldpath_arg = (const void *)args->args[1],
+ *newpath_arg = (const void *)args->args[3];
+ unsigned int len = sizeof(augmented_args->args), oldpath_len;
+
+ if (augmented_args == NULL)
+ return 1; /* Failure: don't filter */
+
+ oldpath_len = augmented_arg__read_str(&augmented_args->arg, oldpath_arg, sizeof(augmented_args->arg.value));
+ len += oldpath_len + augmented_arg__read_str((void *)(&augmented_args->arg) + oldpath_len, newpath_arg, sizeof(augmented_args->arg.value));
+
+ return augmented__output(args, augmented_args, len);
+}
+
+SEC("raw_syscalls:sys_enter")
+int sys_enter(struct syscall_enter_args *args)
+{
+ struct augmented_args_payload *augmented_args;
/*
- * Yonghong and Edward Cree sayz:
- *
- * https://www.spinics.net/lists/netdev/msg531645.html
- *
- * >> R0=inv(id=0) R1=inv2 R6=ctx(id=0,off=0,imm=0) R7=inv64 R10=fp0,call_-1
- * >> 10: (bf) r1 = r6
- * >> 11: (07) r1 += 16
- * >> 12: (05) goto pc+2
- * >> 15: (79) r3 = *(u64 *)(r1 +0)
- * >> dereference of modified ctx ptr R1 off=16 disallowed
- * > Aha, we at least got a different error message this time.
- * > And indeed llvm has done that optimisation, rather than the more obvious
- * > 11: r3 = *(u64 *)(r1 +16)
- * > because it wants to have lots of reads share a single insn. You may be able
- * > to defeat that optimisation by adding compiler barriers, idk. Maybe someone
- * > with llvm knowledge can figure out how to stop it (ideally, llvm would know
- * > when it's generating for bpf backend and not do that). -O0? ¯\_(ツ)_/¯
+ * We start len, the amount of data that will be in the perf ring
+ * buffer, if this is not filtered out by one of pid_filter__has(),
+ * syscall->enabled, etc, with the non-augmented raw syscall payload,
+ * i.e. sizeof(augmented_args->args).
*
- * The optimization mostly likes below:
- *
- * br1:
- * ...
- * r1 += 16
- * goto merge
- * br2:
- * ...
- * r1 += 20
- * goto merge
- * merge:
- * *(u64 *)(r1 + 0)
- *
- * The compiler tries to merge common loads. There is no easy way to
- * stop this compiler optimization without turning off a lot of other
- * optimizations. The easiest way is to add barriers:
- *
- * __asm__ __volatile__("": : :"memory")
- *
- * after the ctx memory access to prevent their down stream merging.
+ * We'll add to this as we add augmented syscalls right after that
+ * initial, non-augmented raw_syscalls:sys_enter payload.
*/
+ unsigned int len = sizeof(augmented_args->args);
+ struct syscall *syscall;
+
+ if (pid_filter__has(&pids_filtered, getpid()))
+ return 0;
+
+ augmented_args = augmented_args_payload();
+ if (augmented_args == NULL)
+ return 1;
+
+ probe_read(&augmented_args->args, sizeof(augmented_args->args), args);
+
/*
- * This table of what args are strings will be provided by userspace,
- * in the syscalls map, i.e. we will already have to do the lookup to
- * see if this specific syscall is filtered, so we can as well get more
- * info about what syscall args are strings or pointers, and how many
- * bytes to copy, per arg, etc.
- *
- * For now hard code it, till we have all the basic mechanisms in place
- * to automate everything and make the kernel part be completely driven
- * by information obtained in userspace for each kernel version and
- * processor architecture, making the kernel part the same no matter what
- * kernel version or processor architecture it runs on.
+ * Jump to syscall specific augmenter, even if the default one,
+ * "!raw_syscalls:unaugmented" that will just return 1 to return the
+ * unagmented tracepoint payload.
*/
- switch (augmented_args->args.syscall_nr) {
- case SYS_ACCT:
- case SYS_ADD_KEY:
- case SYS_CHDIR:
- case SYS_CHMOD:
- case SYS_CHOWN:
- case SYS_CHROOT:
- case SYS_CREAT:
- case SYS_DELETE_MODULE:
- case SYS_EXECVE:
- case SYS_GETXATTR:
- case SYS_LCHOWN:
- case SYS_LGETXATTR:
- case SYS_LINK:
- case SYS_LISTXATTR:
- case SYS_LLISTXATTR:
- case SYS_LREMOVEXATTR:
- case SYS_LSETXATTR:
- case SYS_LSTAT:
- case SYS_MEMFD_CREATE:
- case SYS_MKDIR:
- case SYS_MKNOD:
- case SYS_MQ_OPEN:
- case SYS_MQ_UNLINK:
- case SYS_PIVOT_ROOT:
- case SYS_READLINK:
- case SYS_REMOVEXATTR:
- case SYS_RENAME:
- case SYS_REQUEST_KEY:
- case SYS_RMDIR:
- case SYS_SETXATTR:
- case SYS_STAT:
- case SYS_STATFS:
- case SYS_SWAPOFF:
- case SYS_SWAPON:
- case SYS_SYMLINK:
- case SYS_SYMLINKAT:
- case SYS_TRUNCATE:
- case SYS_UNLINK:
- case SYS_ACCESS:
- case SYS_OPEN: filename_arg = (const void *)args->args[0];
- __asm__ __volatile__("": : :"memory");
- break;
- case SYS_EXECVEAT:
- case SYS_FACCESSAT:
- case SYS_FCHMODAT:
- case SYS_FCHOWNAT:
- case SYS_FGETXATTR:
- case SYS_FINIT_MODULE:
- case SYS_FREMOVEXATTR:
- case SYS_FSETXATTR:
- case SYS_FUTIMESAT:
- case SYS_INOTIFY_ADD_WATCH:
- case SYS_LINKAT:
- case SYS_MKDIRAT:
- case SYS_MKNODAT:
- case SYS_MQ_TIMEDSEND:
- case SYS_NAME_TO_HANDLE_AT:
- case SYS_NEWFSTATAT:
- case SYS_PWRITE64:
- case SYS_QUOTACTL:
- case SYS_READLINKAT:
- case SYS_RENAMEAT:
- case SYS_RENAMEAT2:
- case SYS_STATX:
- case SYS_UNLINKAT:
- case SYS_UTIMENSAT:
- case SYS_OPENAT: filename_arg = (const void *)args->args[1];
- break;
- }
+ bpf_tail_call(args, &syscalls_sys_enter, augmented_args->args.syscall_nr);
- if (filename_arg != NULL) {
- augmented_args->filename.reserved = 0;
- augmented_args->filename.size = probe_read_str(&augmented_args->filename.value,
- sizeof(augmented_args->filename.value),
- filename_arg);
- if (augmented_args->filename.size < sizeof(augmented_args->filename.value)) {
- len -= sizeof(augmented_args->filename.value) - augmented_args->filename.size;
- len &= sizeof(augmented_args->filename.value) - 1;
- }
- } else {
- len = sizeof(augmented_args->args);
- }
-
- /* If perf_event_output fails, return non-zero so that it gets recorded unaugmented */
- return perf_event_output(args, &__augmented_syscalls__, BPF_F_CURRENT_CPU, augmented_args, len);
+ // If not found on the PROG_ARRAY syscalls map, then we're filtering it:
+ return 0;
}
SEC("raw_syscalls:sys_exit")
int sys_exit(struct syscall_exit_args *args)
{
struct syscall_exit_args exit_args;
- struct syscall *syscall;
if (pid_filter__has(&pids_filtered, getpid()))
return 0;
probe_read(&exit_args, sizeof(exit_args), args);
-
- syscall = bpf_map_lookup_elem(&syscalls, &exit_args.syscall_nr);
- if (syscall == NULL || !syscall->enabled)
- return 0;
-
- return 1;
+ /*
+ * Jump to syscall specific return augmenter, even if the default one,
+ * "!raw_syscalls:unaugmented" that will just return 1 to return the
+ * unagmented tracepoint payload.
+ */
+ bpf_tail_call(args, &syscalls_sys_exit, exit_args.syscall_nr);
+ /*
+ * If not found on the PROG_ARRAY syscalls map, then we're filtering it:
+ */
+ return 0;
}
license(GPL);
diff --git a/tools/perf/include/bpf/bpf.h b/tools/perf/include/bpf/bpf.h
index 2eac6d804b2d..b422aeef5339 100644
--- a/tools/perf/include/bpf/bpf.h
+++ b/tools/perf/include/bpf/bpf.h
@@ -45,6 +45,8 @@ struct ____btf_map_##name __attribute__((section(".maps." #name), used)) \
static int (*bpf_map_update_elem)(struct bpf_map *map, void *key, void *value, u64 flags) = (void *)BPF_FUNC_map_update_elem;
static void *(*bpf_map_lookup_elem)(struct bpf_map *map, void *key) = (void *)BPF_FUNC_map_lookup_elem;
+static void (*bpf_tail_call)(void *ctx, void *map, int index) = (void *)BPF_FUNC_tail_call;
+
#define SEC(NAME) __attribute__((section(NAME), used))
#define probe(function, vars) \
diff --git a/tools/perf/jvmti/jvmti_agent.c b/tools/perf/jvmti/jvmti_agent.c
index f7eb63cbbc65..88108598d6e9 100644
--- a/tools/perf/jvmti/jvmti_agent.c
+++ b/tools/perf/jvmti/jvmti_agent.c
@@ -45,10 +45,12 @@
static char jit_path[PATH_MAX];
static void *marker_addr;
+#ifndef HAVE_GETTID
static inline pid_t gettid(void)
{
return (pid_t)syscall(__NR_gettid);
}
+#endif
static int get_e_machine(struct jitheader *hdr)
{
diff --git a/tools/perf/jvmti/libjvmti.c b/tools/perf/jvmti/libjvmti.c
index aea7b1fe85aa..c441a34cb1c0 100644
--- a/tools/perf/jvmti/libjvmti.c
+++ b/tools/perf/jvmti/libjvmti.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
+#include <linux/string.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
@@ -162,8 +163,7 @@ copy_class_filename(const char * class_sign, const char * file_name, char * resu
result[i] = '\0';
} else {
/* fallback case */
- size_t file_name_len = strlen(file_name);
- strncpy(result, file_name, file_name_len < max_length ? file_name_len : max_length);
+ strlcpy(result, file_name, max_length);
}
}
diff --git a/tools/perf/lib/Build b/tools/perf/lib/Build
new file mode 100644
index 000000000000..c31f1c111f8f
--- /dev/null
+++ b/tools/perf/lib/Build
@@ -0,0 +1,12 @@
+libperf-y += core.o
+libperf-y += cpumap.o
+libperf-y += threadmap.o
+libperf-y += evsel.o
+libperf-y += evlist.o
+libperf-y += zalloc.o
+libperf-y += xyarray.o
+libperf-y += lib.o
+
+$(OUTPUT)zalloc.o: ../../lib/zalloc.c FORCE
+ $(call rule_mkdir)
+ $(call if_changed_dep,cc_o_c)
diff --git a/tools/perf/lib/Documentation/Makefile b/tools/perf/lib/Documentation/Makefile
new file mode 100644
index 000000000000..586425a88795
--- /dev/null
+++ b/tools/perf/lib/Documentation/Makefile
@@ -0,0 +1,7 @@
+all:
+ rst2man man/libperf.rst > man/libperf.7
+ rst2pdf tutorial/tutorial.rst
+
+clean:
+ rm -f man/libperf.7
+ rm -f tutorial/tutorial.pdf
diff --git a/tools/perf/lib/Documentation/man/libperf.rst b/tools/perf/lib/Documentation/man/libperf.rst
new file mode 100644
index 000000000000..09a270fccb9c
--- /dev/null
+++ b/tools/perf/lib/Documentation/man/libperf.rst
@@ -0,0 +1,100 @@
+.. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+libperf
+
+The libperf library provides an API to access the linux kernel perf
+events subsystem. It provides the following high level objects:
+
+ - struct perf_cpu_map
+ - struct perf_thread_map
+ - struct perf_evlist
+ - struct perf_evsel
+
+reference
+=========
+Function reference by header files:
+
+perf/core.h
+-----------
+.. code-block:: c
+
+ typedef int (\*libperf_print_fn_t)(enum libperf_print_level level,
+ const char \*, va_list ap);
+
+ void libperf_set_print(libperf_print_fn_t fn);
+
+perf/cpumap.h
+-------------
+.. code-block:: c
+
+ struct perf_cpu_map \*perf_cpu_map__dummy_new(void);
+ struct perf_cpu_map \*perf_cpu_map__new(const char \*cpu_list);
+ struct perf_cpu_map \*perf_cpu_map__read(FILE \*file);
+ struct perf_cpu_map \*perf_cpu_map__get(struct perf_cpu_map \*map);
+ void perf_cpu_map__put(struct perf_cpu_map \*map);
+ int perf_cpu_map__cpu(const struct perf_cpu_map \*cpus, int idx);
+ int perf_cpu_map__nr(const struct perf_cpu_map \*cpus);
+ perf_cpu_map__for_each_cpu(cpu, idx, cpus)
+
+perf/threadmap.h
+----------------
+.. code-block:: c
+
+ struct perf_thread_map \*perf_thread_map__new_dummy(void);
+ void perf_thread_map__set_pid(struct perf_thread_map \*map, int thread, pid_t pid);
+ char \*perf_thread_map__comm(struct perf_thread_map \*map, int thread);
+ struct perf_thread_map \*perf_thread_map__get(struct perf_thread_map \*map);
+ void perf_thread_map__put(struct perf_thread_map \*map);
+
+perf/evlist.h
+-------------
+.. code-block::
+
+ void perf_evlist__init(struct perf_evlist \*evlist);
+ void perf_evlist__add(struct perf_evlist \*evlist,
+ struct perf_evsel \*evsel);
+ void perf_evlist__remove(struct perf_evlist \*evlist,
+ struct perf_evsel \*evsel);
+ struct perf_evlist \*perf_evlist__new(void);
+ void perf_evlist__delete(struct perf_evlist \*evlist);
+ struct perf_evsel\* perf_evlist__next(struct perf_evlist \*evlist,
+ struct perf_evsel \*evsel);
+ int perf_evlist__open(struct perf_evlist \*evlist);
+ void perf_evlist__close(struct perf_evlist \*evlist);
+ void perf_evlist__enable(struct perf_evlist \*evlist);
+ void perf_evlist__disable(struct perf_evlist \*evlist);
+ perf_evlist__for_each_evsel(evlist, pos)
+ void perf_evlist__set_maps(struct perf_evlist \*evlist,
+ struct perf_cpu_map \*cpus,
+ struct perf_thread_map \*threads);
+
+perf/evsel.h
+------------
+.. code-block:: c
+
+ struct perf_counts_values {
+ union {
+ struct {
+ uint64_t val;
+ uint64_t ena;
+ uint64_t run;
+ };
+ uint64_t values[3];
+ };
+ };
+
+ void perf_evsel__init(struct perf_evsel \*evsel,
+ struct perf_event_attr \*attr);
+ struct perf_evsel \*perf_evsel__new(struct perf_event_attr \*attr);
+ void perf_evsel__delete(struct perf_evsel \*evsel);
+ int perf_evsel__open(struct perf_evsel \*evsel, struct perf_cpu_map \*cpus,
+ struct perf_thread_map \*threads);
+ void perf_evsel__close(struct perf_evsel \*evsel);
+ int perf_evsel__read(struct perf_evsel \*evsel, int cpu, int thread,
+ struct perf_counts_values \*count);
+ int perf_evsel__enable(struct perf_evsel \*evsel);
+ int perf_evsel__disable(struct perf_evsel \*evsel);
+ int perf_evsel__apply_filter(struct perf_evsel \*evsel, const char \*filter);
+ struct perf_cpu_map \*perf_evsel__cpus(struct perf_evsel \*evsel);
+ struct perf_thread_map \*perf_evsel__threads(struct perf_evsel \*evsel);
+ struct perf_event_attr \*perf_evsel__attr(struct perf_evsel \*evsel);
diff --git a/tools/perf/lib/Documentation/tutorial/tutorial.rst b/tools/perf/lib/Documentation/tutorial/tutorial.rst
new file mode 100644
index 000000000000..7be7bc27b385
--- /dev/null
+++ b/tools/perf/lib/Documentation/tutorial/tutorial.rst
@@ -0,0 +1,123 @@
+.. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+libperf tutorial
+================
+
+Compile and install libperf from kernel sources
+===============================================
+.. code-block:: bash
+
+ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
+ cd linux/tools/perf/lib
+ make
+ sudo make install prefix=/usr
+
+Libperf object
+==============
+The libperf library provides several high level objects:
+
+struct perf_cpu_map
+ Provides a cpu list abstraction.
+
+struct perf_thread_map
+ Provides a thread list abstraction.
+
+struct perf_evsel
+ Provides an abstraction for single a perf event.
+
+struct perf_evlist
+ Gathers several struct perf_evsel object and performs functions on all of them.
+
+The exported API binds these objects together,
+for full reference see the libperf.7 man page.
+
+Examples
+========
+Examples aim to explain libperf functionality on simple use cases.
+They are based in on a checked out linux kernel git tree:
+
+.. code-block:: bash
+
+ $ cd tools/perf/lib/Documentation/tutorial/
+ $ ls -d ex-*
+ ex-1-compile ex-2-evsel-stat ex-3-evlist-stat
+
+ex-1-compile example
+====================
+This example shows the basic usage of *struct perf_cpu_map*,
+how to create it and display its cpus:
+
+.. code-block:: bash
+
+ $ cd ex-1-compile/
+ $ make
+ gcc -o test test.c -lperf
+ $ ./test
+ 0 1 2 3 4 5 6 7
+
+
+The full code listing is here:
+
+.. code-block:: c
+
+ 1 #include <perf/cpumap.h>
+ 2
+ 3 int main(int argc, char **Argv)
+ 4 {
+ 5 struct perf_cpu_map *cpus;
+ 6 int cpu, tmp;
+ 7
+ 8 cpus = perf_cpu_map__new(NULL);
+ 9
+ 10 perf_cpu_map__for_each_cpu(cpu, tmp, cpus)
+ 11 fprintf(stdout, "%d ", cpu);
+ 12
+ 13 fprintf(stdout, "\n");
+ 14
+ 15 perf_cpu_map__put(cpus);
+ 16 return 0;
+ 17 }
+
+
+First you need to include the proper header to have *struct perf_cpumap*
+declaration and functions:
+
+.. code-block:: c
+
+ 1 #include <perf/cpumap.h>
+
+
+The *struct perf_cpumap* object is created by *perf_cpu_map__new* call.
+The *NULL* argument asks it to populate the object with the current online CPUs list:
+
+.. code-block:: c
+
+ 8 cpus = perf_cpu_map__new(NULL);
+
+This is paired with a *perf_cpu_map__put*, that drops its reference at the end, possibly deleting it.
+
+.. code-block:: c
+
+ 15 perf_cpu_map__put(cpus);
+
+The iteration through the *struct perf_cpumap* CPUs is done using the *perf_cpu_map__for_each_cpu*
+macro which requires 3 arguments:
+
+- cpu - the cpu numer
+- tmp - iteration helper variable
+- cpus - the *struct perf_cpumap* object
+
+.. code-block:: c
+
+ 10 perf_cpu_map__for_each_cpu(cpu, tmp, cpus)
+ 11 fprintf(stdout, "%d ", cpu);
+
+ex-2-evsel-stat example
+=======================
+
+TBD
+
+ex-3-evlist-stat example
+========================
+
+TBD
diff --git a/tools/perf/lib/Makefile b/tools/perf/lib/Makefile
new file mode 100644
index 000000000000..a67efb8d9d39
--- /dev/null
+++ b/tools/perf/lib/Makefile
@@ -0,0 +1,158 @@
+# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+# Most of this file is copied from tools/lib/bpf/Makefile
+
+LIBPERF_VERSION = 0
+LIBPERF_PATCHLEVEL = 0
+LIBPERF_EXTRAVERSION = 1
+
+MAKEFLAGS += --no-print-directory
+
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(CURDIR)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+#$(info Determined 'srctree' to be $(srctree))
+endif
+
+INSTALL = install
+
+# Use DESTDIR for installing into a different root directory.
+# This is useful for building a package. The program will be
+# installed in this directory as if it was the root directory.
+# Then the build tool can move it later.
+DESTDIR ?=
+DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))'
+
+include $(srctree)/tools/scripts/Makefile.include
+include $(srctree)/tools/scripts/Makefile.arch
+
+ifeq ($(LP64), 1)
+ libdir_relative = lib64
+else
+ libdir_relative = lib
+endif
+
+prefix ?=
+libdir = $(prefix)/$(libdir_relative)
+
+# Shell quotes
+libdir_SQ = $(subst ','\'',$(libdir))
+libdir_relative_SQ = $(subst ','\'',$(libdir_relative))
+
+ifeq ("$(origin V)", "command line")
+ VERBOSE = $(V)
+endif
+ifndef VERBOSE
+ VERBOSE = 0
+endif
+
+ifeq ($(VERBOSE),1)
+ Q =
+else
+ Q = @
+endif
+
+# Set compile option CFLAGS
+ifdef EXTRA_CFLAGS
+ CFLAGS := $(EXTRA_CFLAGS)
+else
+ CFLAGS := -g -Wall
+endif
+
+INCLUDES = -I$(srctree)/tools/perf/lib/include -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(SRCARCH)/include/ -I$(srctree)/tools/arch/$(SRCARCH)/include/uapi -I$(srctree)/tools/include/uapi
+
+# Append required CFLAGS
+override CFLAGS += $(EXTRA_WARNINGS)
+override CFLAGS += -Werror -Wall
+override CFLAGS += -fPIC
+override CFLAGS += $(INCLUDES)
+override CFLAGS += -fvisibility=hidden
+
+all:
+
+export srctree OUTPUT CC LD CFLAGS V
+export DESTDIR DESTDIR_SQ
+
+include $(srctree)/tools/build/Makefile.include
+
+VERSION_SCRIPT := libperf.map
+
+PATCHLEVEL = $(LIBPERF_PATCHLEVEL)
+EXTRAVERSION = $(LIBPERF_EXTRAVERSION)
+VERSION = $(LIBPERF_VERSION).$(LIBPERF_PATCHLEVEL).$(LIBPERF_EXTRAVERSION)
+
+LIBPERF_SO := $(OUTPUT)libperf.so.$(VERSION)
+LIBPERF_A := $(OUTPUT)libperf.a
+LIBPERF_IN := $(OUTPUT)libperf-in.o
+LIBPERF_PC := $(OUTPUT)libperf.pc
+
+LIBPERF_ALL := $(LIBPERF_A) $(OUTPUT)libperf.so*
+
+$(LIBPERF_IN): FORCE
+ $(Q)$(MAKE) $(build)=libperf
+
+$(LIBPERF_A): $(LIBPERF_IN)
+ $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIBPERF_IN)
+
+$(LIBPERF_SO): $(LIBPERF_IN)
+ $(QUIET_LINK)$(CC) --shared -Wl,-soname,libperf.so \
+ -Wl,--version-script=$(VERSION_SCRIPT) $^ -o $@
+ @ln -sf $(@F) $(OUTPUT)libperf.so
+ @ln -sf $(@F) $(OUTPUT)libperf.so.$(LIBPERF_VERSION)
+
+
+libs: $(LIBPERF_A) $(LIBPERF_SO) $(LIBPERF_PC)
+
+all: fixdep
+ $(Q)$(MAKE) libs
+
+clean:
+ $(call QUIET_CLEAN, libperf) $(RM) $(LIBPERF_A) \
+ *.o *~ *.a *.so *.so.$(VERSION) *.so.$(LIBPERF_VERSION) .*.d .*.cmd LIBPERF-CFLAGS $(LIBPERF_PC)
+ $(Q)$(MAKE) -C tests clean
+
+tests:
+ $(Q)$(MAKE) -C tests
+ $(Q)$(MAKE) -C tests run
+
+$(LIBPERF_PC):
+ $(QUIET_GEN)sed -e "s|@PREFIX@|$(prefix)|" \
+ -e "s|@LIBDIR@|$(libdir_SQ)|" \
+ -e "s|@VERSION@|$(VERSION)|" \
+ < libperf.pc.template > $@
+
+define do_install_mkdir
+ if [ ! -d '$(DESTDIR_SQ)$1' ]; then \
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$1'; \
+ fi
+endef
+
+define do_install
+ if [ ! -d '$(DESTDIR_SQ)$2' ]; then \
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \
+ fi; \
+ $(INSTALL) $1 $(if $3,-m $3,) '$(DESTDIR_SQ)$2'
+endef
+
+install_lib: libs
+ $(call QUIET_INSTALL, $(LIBPERF_ALL)) \
+ $(call do_install_mkdir,$(libdir_SQ)); \
+ cp -fpR $(LIBPERF_ALL) $(DESTDIR)$(libdir_SQ)
+
+install_headers:
+ $(call QUIET_INSTALL, headers) \
+ $(call do_install,include/perf/core.h,$(prefix)/include/perf,644); \
+ $(call do_install,include/perf/cpumap.h,$(prefix)/include/perf,644); \
+ $(call do_install,include/perf/threadmap.h,$(prefix)/include/perf,644); \
+ $(call do_install,include/perf/evlist.h,$(prefix)/include/perf,644); \
+ $(call do_install,include/perf/evsel.h,$(prefix)/include/perf,644);
+
+install_pkgconfig: $(LIBPERF_PC)
+ $(call QUIET_INSTALL, $(LIBPERF_PC)) \
+ $(call do_install,$(LIBPERF_PC),$(libdir_SQ)/pkgconfig,644)
+
+install: install_lib install_headers install_pkgconfig
+
+FORCE:
+
+.PHONY: all install clean tests FORCE
diff --git a/tools/perf/lib/core.c b/tools/perf/lib/core.c
new file mode 100644
index 000000000000..29d5e3348718
--- /dev/null
+++ b/tools/perf/lib/core.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define __printf(a, b) __attribute__((format(printf, a, b)))
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <perf/core.h>
+#include "internal.h"
+
+static int __base_pr(enum libperf_print_level level, const char *format,
+ va_list args)
+{
+ return vfprintf(stderr, format, args);
+}
+
+static libperf_print_fn_t __libperf_pr = __base_pr;
+
+void libperf_set_print(libperf_print_fn_t fn)
+{
+ __libperf_pr = fn;
+}
+
+__printf(2, 3)
+void libperf_print(enum libperf_print_level level, const char *format, ...)
+{
+ va_list args;
+
+ if (!__libperf_pr)
+ return;
+
+ va_start(args, format);
+ __libperf_pr(level, format, args);
+ va_end(args);
+}
diff --git a/tools/perf/lib/cpumap.c b/tools/perf/lib/cpumap.c
new file mode 100644
index 000000000000..1f0e6f334237
--- /dev/null
+++ b/tools/perf/lib/cpumap.c
@@ -0,0 +1,262 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <perf/cpumap.h>
+#include <stdlib.h>
+#include <linux/refcount.h>
+#include <internal/cpumap.h>
+#include <asm/bug.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <limits.h>
+
+struct perf_cpu_map *perf_cpu_map__dummy_new(void)
+{
+ struct perf_cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int));
+
+ if (cpus != NULL) {
+ cpus->nr = 1;
+ cpus->map[0] = -1;
+ refcount_set(&cpus->refcnt, 1);
+ }
+
+ return cpus;
+}
+
+static void cpu_map__delete(struct perf_cpu_map *map)
+{
+ if (map) {
+ WARN_ONCE(refcount_read(&map->refcnt) != 0,
+ "cpu_map refcnt unbalanced\n");
+ free(map);
+ }
+}
+
+struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map)
+{
+ if (map)
+ refcount_inc(&map->refcnt);
+ return map;
+}
+
+void perf_cpu_map__put(struct perf_cpu_map *map)
+{
+ if (map && refcount_dec_and_test(&map->refcnt))
+ cpu_map__delete(map);
+}
+
+static struct perf_cpu_map *cpu_map__default_new(void)
+{
+ struct perf_cpu_map *cpus;
+ int nr_cpus;
+
+ nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+ if (nr_cpus < 0)
+ return NULL;
+
+ cpus = malloc(sizeof(*cpus) + nr_cpus * sizeof(int));
+ if (cpus != NULL) {
+ int i;
+
+ for (i = 0; i < nr_cpus; ++i)
+ cpus->map[i] = i;
+
+ cpus->nr = nr_cpus;
+ refcount_set(&cpus->refcnt, 1);
+ }
+
+ return cpus;
+}
+
+static struct perf_cpu_map *cpu_map__trim_new(int nr_cpus, int *tmp_cpus)
+{
+ size_t payload_size = nr_cpus * sizeof(int);
+ struct perf_cpu_map *cpus = malloc(sizeof(*cpus) + payload_size);
+
+ if (cpus != NULL) {
+ cpus->nr = nr_cpus;
+ memcpy(cpus->map, tmp_cpus, payload_size);
+ refcount_set(&cpus->refcnt, 1);
+ }
+
+ return cpus;
+}
+
+struct perf_cpu_map *perf_cpu_map__read(FILE *file)
+{
+ struct perf_cpu_map *cpus = NULL;
+ int nr_cpus = 0;
+ int *tmp_cpus = NULL, *tmp;
+ int max_entries = 0;
+ int n, cpu, prev;
+ char sep;
+
+ sep = 0;
+ prev = -1;
+ for (;;) {
+ n = fscanf(file, "%u%c", &cpu, &sep);
+ if (n <= 0)
+ break;
+ if (prev >= 0) {
+ int new_max = nr_cpus + cpu - prev - 1;
+
+ WARN_ONCE(new_max >= MAX_NR_CPUS, "Perf can support %d CPUs. "
+ "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS);
+
+ if (new_max >= max_entries) {
+ max_entries = new_max + MAX_NR_CPUS / 2;
+ tmp = realloc(tmp_cpus, max_entries * sizeof(int));
+ if (tmp == NULL)
+ goto out_free_tmp;
+ tmp_cpus = tmp;
+ }
+
+ while (++prev < cpu)
+ tmp_cpus[nr_cpus++] = prev;
+ }
+ if (nr_cpus == max_entries) {
+ max_entries += MAX_NR_CPUS;
+ tmp = realloc(tmp_cpus, max_entries * sizeof(int));
+ if (tmp == NULL)
+ goto out_free_tmp;
+ tmp_cpus = tmp;
+ }
+
+ tmp_cpus[nr_cpus++] = cpu;
+ if (n == 2 && sep == '-')
+ prev = cpu;
+ else
+ prev = -1;
+ if (n == 1 || sep == '\n')
+ break;
+ }
+
+ if (nr_cpus > 0)
+ cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
+ else
+ cpus = cpu_map__default_new();
+out_free_tmp:
+ free(tmp_cpus);
+ return cpus;
+}
+
+static struct perf_cpu_map *cpu_map__read_all_cpu_map(void)
+{
+ struct perf_cpu_map *cpus = NULL;
+ FILE *onlnf;
+
+ onlnf = fopen("/sys/devices/system/cpu/online", "r");
+ if (!onlnf)
+ return cpu_map__default_new();
+
+ cpus = perf_cpu_map__read(onlnf);
+ fclose(onlnf);
+ return cpus;
+}
+
+struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list)
+{
+ struct perf_cpu_map *cpus = NULL;
+ unsigned long start_cpu, end_cpu = 0;
+ char *p = NULL;
+ int i, nr_cpus = 0;
+ int *tmp_cpus = NULL, *tmp;
+ int max_entries = 0;
+
+ if (!cpu_list)
+ return cpu_map__read_all_cpu_map();
+
+ /*
+ * must handle the case of empty cpumap to cover
+ * TOPOLOGY header for NUMA nodes with no CPU
+ * ( e.g., because of CPU hotplug)
+ */
+ if (!isdigit(*cpu_list) && *cpu_list != '\0')
+ goto out;
+
+ while (isdigit(*cpu_list)) {
+ p = NULL;
+ start_cpu = strtoul(cpu_list, &p, 0);
+ if (start_cpu >= INT_MAX
+ || (*p != '\0' && *p != ',' && *p != '-'))
+ goto invalid;
+
+ if (*p == '-') {
+ cpu_list = ++p;
+ p = NULL;
+ end_cpu = strtoul(cpu_list, &p, 0);
+
+ if (end_cpu >= INT_MAX || (*p != '\0' && *p != ','))
+ goto invalid;
+
+ if (end_cpu < start_cpu)
+ goto invalid;
+ } else {
+ end_cpu = start_cpu;
+ }
+
+ WARN_ONCE(end_cpu >= MAX_NR_CPUS, "Perf can support %d CPUs. "
+ "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS);
+
+ for (; start_cpu <= end_cpu; start_cpu++) {
+ /* check for duplicates */
+ for (i = 0; i < nr_cpus; i++)
+ if (tmp_cpus[i] == (int)start_cpu)
+ goto invalid;
+
+ if (nr_cpus == max_entries) {
+ max_entries += MAX_NR_CPUS;
+ tmp = realloc(tmp_cpus, max_entries * sizeof(int));
+ if (tmp == NULL)
+ goto invalid;
+ tmp_cpus = tmp;
+ }
+ tmp_cpus[nr_cpus++] = (int)start_cpu;
+ }
+ if (*p)
+ ++p;
+
+ cpu_list = p;
+ }
+
+ if (nr_cpus > 0)
+ cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
+ else if (*cpu_list != '\0')
+ cpus = cpu_map__default_new();
+ else
+ cpus = perf_cpu_map__dummy_new();
+invalid:
+ free(tmp_cpus);
+out:
+ return cpus;
+}
+
+int perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx)
+{
+ if (idx < cpus->nr)
+ return cpus->map[idx];
+
+ return -1;
+}
+
+int perf_cpu_map__nr(const struct perf_cpu_map *cpus)
+{
+ return cpus ? cpus->nr : 1;
+}
+
+bool perf_cpu_map__empty(const struct perf_cpu_map *map)
+{
+ return map ? map->map[0] == -1 : true;
+}
+
+int perf_cpu_map__idx(struct perf_cpu_map *cpus, int cpu)
+{
+ int i;
+
+ for (i = 0; i < cpus->nr; ++i) {
+ if (cpus->map[i] == cpu)
+ return i;
+ }
+
+ return -1;
+}
diff --git a/tools/perf/lib/evlist.c b/tools/perf/lib/evlist.c
new file mode 100644
index 000000000000..f4dc9a208332
--- /dev/null
+++ b/tools/perf/lib/evlist.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <perf/evlist.h>
+#include <perf/evsel.h>
+#include <linux/list.h>
+#include <internal/evlist.h>
+#include <internal/evsel.h>
+#include <linux/zalloc.h>
+#include <stdlib.h>
+#include <perf/cpumap.h>
+#include <perf/threadmap.h>
+
+void perf_evlist__init(struct perf_evlist *evlist)
+{
+ INIT_LIST_HEAD(&evlist->entries);
+ evlist->nr_entries = 0;
+}
+
+static void __perf_evlist__propagate_maps(struct perf_evlist *evlist,
+ struct perf_evsel *evsel)
+{
+ /*
+ * We already have cpus for evsel (via PMU sysfs) so
+ * keep it, if there's no target cpu list defined.
+ */
+ if (!evsel->own_cpus || evlist->has_user_cpus) {
+ perf_cpu_map__put(evsel->cpus);
+ evsel->cpus = perf_cpu_map__get(evlist->cpus);
+ } else if (evsel->cpus != evsel->own_cpus) {
+ perf_cpu_map__put(evsel->cpus);
+ evsel->cpus = perf_cpu_map__get(evsel->own_cpus);
+ }
+
+ perf_thread_map__put(evsel->threads);
+ evsel->threads = perf_thread_map__get(evlist->threads);
+}
+
+static void perf_evlist__propagate_maps(struct perf_evlist *evlist)
+{
+ struct perf_evsel *evsel;
+
+ perf_evlist__for_each_evsel(evlist, evsel)
+ __perf_evlist__propagate_maps(evlist, evsel);
+}
+
+void perf_evlist__add(struct perf_evlist *evlist,
+ struct perf_evsel *evsel)
+{
+ list_add_tail(&evsel->node, &evlist->entries);
+ evlist->nr_entries += 1;
+ __perf_evlist__propagate_maps(evlist, evsel);
+}
+
+void perf_evlist__remove(struct perf_evlist *evlist,
+ struct perf_evsel *evsel)
+{
+ list_del_init(&evsel->node);
+ evlist->nr_entries -= 1;
+}
+
+struct perf_evlist *perf_evlist__new(void)
+{
+ struct perf_evlist *evlist = zalloc(sizeof(*evlist));
+
+ if (evlist != NULL)
+ perf_evlist__init(evlist);
+
+ return evlist;
+}
+
+struct perf_evsel *
+perf_evlist__next(struct perf_evlist *evlist, struct perf_evsel *prev)
+{
+ struct perf_evsel *next;
+
+ if (!prev) {
+ next = list_first_entry(&evlist->entries,
+ struct perf_evsel,
+ node);
+ } else {
+ next = list_next_entry(prev, node);
+ }
+
+ /* Empty list is noticed here so don't need checking on entry. */
+ if (&next->node == &evlist->entries)
+ return NULL;
+
+ return next;
+}
+
+void perf_evlist__delete(struct perf_evlist *evlist)
+{
+ free(evlist);
+}
+
+void perf_evlist__set_maps(struct perf_evlist *evlist,
+ struct perf_cpu_map *cpus,
+ struct perf_thread_map *threads)
+{
+ /*
+ * Allow for the possibility that one or another of the maps isn't being
+ * changed i.e. don't put it. Note we are assuming the maps that are
+ * being applied are brand new and evlist is taking ownership of the
+ * original reference count of 1. If that is not the case it is up to
+ * the caller to increase the reference count.
+ */
+ if (cpus != evlist->cpus) {
+ perf_cpu_map__put(evlist->cpus);
+ evlist->cpus = perf_cpu_map__get(cpus);
+ }
+
+ if (threads != evlist->threads) {
+ perf_thread_map__put(evlist->threads);
+ evlist->threads = perf_thread_map__get(threads);
+ }
+
+ perf_evlist__propagate_maps(evlist);
+}
+
+int perf_evlist__open(struct perf_evlist *evlist)
+{
+ struct perf_evsel *evsel;
+ int err;
+
+ perf_evlist__for_each_entry(evlist, evsel) {
+ err = perf_evsel__open(evsel, evsel->cpus, evsel->threads);
+ if (err < 0)
+ goto out_err;
+ }
+
+ return 0;
+
+out_err:
+ perf_evlist__close(evlist);
+ return err;
+}
+
+void perf_evlist__close(struct perf_evlist *evlist)
+{
+ struct perf_evsel *evsel;
+
+ perf_evlist__for_each_entry_reverse(evlist, evsel)
+ perf_evsel__close(evsel);
+}
+
+void perf_evlist__enable(struct perf_evlist *evlist)
+{
+ struct perf_evsel *evsel;
+
+ perf_evlist__for_each_entry(evlist, evsel)
+ perf_evsel__enable(evsel);
+}
+
+void perf_evlist__disable(struct perf_evlist *evlist)
+{
+ struct perf_evsel *evsel;
+
+ perf_evlist__for_each_entry(evlist, evsel)
+ perf_evsel__disable(evsel);
+}
diff --git a/tools/perf/lib/evsel.c b/tools/perf/lib/evsel.c
new file mode 100644
index 000000000000..24abc80dd767
--- /dev/null
+++ b/tools/perf/lib/evsel.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <perf/evsel.h>
+#include <perf/cpumap.h>
+#include <perf/threadmap.h>
+#include <linux/list.h>
+#include <internal/evsel.h>
+#include <linux/zalloc.h>
+#include <stdlib.h>
+#include <internal/xyarray.h>
+#include <internal/cpumap.h>
+#include <internal/threadmap.h>
+#include <internal/lib.h>
+#include <linux/string.h>
+#include <sys/ioctl.h>
+
+void perf_evsel__init(struct perf_evsel *evsel, struct perf_event_attr *attr)
+{
+ INIT_LIST_HEAD(&evsel->node);
+ evsel->attr = *attr;
+}
+
+struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr)
+{
+ struct perf_evsel *evsel = zalloc(sizeof(*evsel));
+
+ if (evsel != NULL)
+ perf_evsel__init(evsel, attr);
+
+ return evsel;
+}
+
+void perf_evsel__delete(struct perf_evsel *evsel)
+{
+ free(evsel);
+}
+
+#define FD(e, x, y) (*(int *) xyarray__entry(e->fd, x, y))
+
+int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
+{
+ evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int));
+
+ if (evsel->fd) {
+ int cpu, thread;
+ for (cpu = 0; cpu < ncpus; cpu++) {
+ for (thread = 0; thread < nthreads; thread++) {
+ FD(evsel, cpu, thread) = -1;
+ }
+ }
+ }
+
+ return evsel->fd != NULL ? 0 : -ENOMEM;
+}
+
+static int
+sys_perf_event_open(struct perf_event_attr *attr,
+ pid_t pid, int cpu, int group_fd,
+ unsigned long flags)
+{
+ return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
+}
+
+int perf_evsel__open(struct perf_evsel *evsel, struct perf_cpu_map *cpus,
+ struct perf_thread_map *threads)
+{
+ int cpu, thread, err = 0;
+
+ if (cpus == NULL) {
+ static struct perf_cpu_map *empty_cpu_map;
+
+ if (empty_cpu_map == NULL) {
+ empty_cpu_map = perf_cpu_map__dummy_new();
+ if (empty_cpu_map == NULL)
+ return -ENOMEM;
+ }
+
+ cpus = empty_cpu_map;
+ }
+
+ if (threads == NULL) {
+ static struct perf_thread_map *empty_thread_map;
+
+ if (empty_thread_map == NULL) {
+ empty_thread_map = perf_thread_map__new_dummy();
+ if (empty_thread_map == NULL)
+ return -ENOMEM;
+ }
+
+ threads = empty_thread_map;
+ }
+
+ if (evsel->fd == NULL &&
+ perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0)
+ return -ENOMEM;
+
+ for (cpu = 0; cpu < cpus->nr; cpu++) {
+ for (thread = 0; thread < threads->nr; thread++) {
+ int fd;
+
+ fd = sys_perf_event_open(&evsel->attr,
+ threads->map[thread].pid,
+ cpus->map[cpu], -1, 0);
+
+ if (fd < 0)
+ return -errno;
+
+ FD(evsel, cpu, thread) = fd;
+ }
+ }
+
+ return err;
+}
+
+void perf_evsel__close_fd(struct perf_evsel *evsel)
+{
+ int cpu, thread;
+
+ for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++)
+ for (thread = 0; thread < xyarray__max_y(evsel->fd); ++thread) {
+ close(FD(evsel, cpu, thread));
+ FD(evsel, cpu, thread) = -1;
+ }
+}
+
+void perf_evsel__free_fd(struct perf_evsel *evsel)
+{
+ xyarray__delete(evsel->fd);
+ evsel->fd = NULL;
+}
+
+void perf_evsel__close(struct perf_evsel *evsel)
+{
+ if (evsel->fd == NULL)
+ return;
+
+ perf_evsel__close_fd(evsel);
+ perf_evsel__free_fd(evsel);
+}
+
+int perf_evsel__read_size(struct perf_evsel *evsel)
+{
+ u64 read_format = evsel->attr.read_format;
+ int entry = sizeof(u64); /* value */
+ int size = 0;
+ int nr = 1;
+
+ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
+ size += sizeof(u64);
+
+ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
+ size += sizeof(u64);
+
+ if (read_format & PERF_FORMAT_ID)
+ entry += sizeof(u64);
+
+ if (read_format & PERF_FORMAT_GROUP) {
+ nr = evsel->nr_members;
+ size += sizeof(u64);
+ }
+
+ size += entry * nr;
+ return size;
+}
+
+int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread,
+ struct perf_counts_values *count)
+{
+ size_t size = perf_evsel__read_size(evsel);
+
+ memset(count, 0, sizeof(*count));
+
+ if (FD(evsel, cpu, thread) < 0)
+ return -EINVAL;
+
+ if (readn(FD(evsel, cpu, thread), count->values, size) <= 0)
+ return -errno;
+
+ return 0;
+}
+
+static int perf_evsel__run_ioctl(struct perf_evsel *evsel,
+ int ioc, void *arg)
+{
+ int cpu, thread;
+
+ for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++) {
+ for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) {
+ int fd = FD(evsel, cpu, thread),
+ err = ioctl(fd, ioc, arg);
+
+ if (err)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int perf_evsel__enable(struct perf_evsel *evsel)
+{
+ return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_ENABLE, 0);
+}
+
+int perf_evsel__disable(struct perf_evsel *evsel)
+{
+ return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_DISABLE, 0);
+}
+
+int perf_evsel__apply_filter(struct perf_evsel *evsel, const char *filter)
+{
+ return perf_evsel__run_ioctl(evsel,
+ PERF_EVENT_IOC_SET_FILTER,
+ (void *)filter);
+}
+
+struct perf_cpu_map *perf_evsel__cpus(struct perf_evsel *evsel)
+{
+ return evsel->cpus;
+}
+
+struct perf_thread_map *perf_evsel__threads(struct perf_evsel *evsel)
+{
+ return evsel->threads;
+}
+
+struct perf_event_attr *perf_evsel__attr(struct perf_evsel *evsel)
+{
+ return &evsel->attr;
+}
diff --git a/tools/perf/lib/include/internal/cpumap.h b/tools/perf/lib/include/internal/cpumap.h
new file mode 100644
index 000000000000..840d4032587b
--- /dev/null
+++ b/tools/perf/lib/include/internal/cpumap.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LIBPERF_INTERNAL_CPUMAP_H
+#define __LIBPERF_INTERNAL_CPUMAP_H
+
+#include <linux/refcount.h>
+
+struct perf_cpu_map {
+ refcount_t refcnt;
+ int nr;
+ int map[];
+};
+
+#ifndef MAX_NR_CPUS
+#define MAX_NR_CPUS 2048
+#endif
+
+int perf_cpu_map__idx(struct perf_cpu_map *cpus, int cpu);
+
+#endif /* __LIBPERF_INTERNAL_CPUMAP_H */
diff --git a/tools/perf/lib/include/internal/evlist.h b/tools/perf/lib/include/internal/evlist.h
new file mode 100644
index 000000000000..448891f06e3e
--- /dev/null
+++ b/tools/perf/lib/include/internal/evlist.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LIBPERF_INTERNAL_EVLIST_H
+#define __LIBPERF_INTERNAL_EVLIST_H
+
+#include <linux/list.h>
+
+struct perf_cpu_map;
+struct perf_thread_map;
+
+struct perf_evlist {
+ struct list_head entries;
+ int nr_entries;
+ bool has_user_cpus;
+ struct perf_cpu_map *cpus;
+ struct perf_thread_map *threads;
+};
+
+/**
+ * __perf_evlist__for_each_entry - iterate thru all the evsels
+ * @list: list_head instance to iterate
+ * @evsel: struct perf_evsel iterator
+ */
+#define __perf_evlist__for_each_entry(list, evsel) \
+ list_for_each_entry(evsel, list, node)
+
+/**
+ * evlist__for_each_entry - iterate thru all the evsels
+ * @evlist: perf_evlist instance to iterate
+ * @evsel: struct perf_evsel iterator
+ */
+#define perf_evlist__for_each_entry(evlist, evsel) \
+ __perf_evlist__for_each_entry(&(evlist)->entries, evsel)
+
+/**
+ * __perf_evlist__for_each_entry_reverse - iterate thru all the evsels in reverse order
+ * @list: list_head instance to iterate
+ * @evsel: struct evsel iterator
+ */
+#define __perf_evlist__for_each_entry_reverse(list, evsel) \
+ list_for_each_entry_reverse(evsel, list, node)
+
+/**
+ * perf_evlist__for_each_entry_reverse - iterate thru all the evsels in reverse order
+ * @evlist: evlist instance to iterate
+ * @evsel: struct evsel iterator
+ */
+#define perf_evlist__for_each_entry_reverse(evlist, evsel) \
+ __perf_evlist__for_each_entry_reverse(&(evlist)->entries, evsel)
+
+#endif /* __LIBPERF_INTERNAL_EVLIST_H */
diff --git a/tools/perf/lib/include/internal/evsel.h b/tools/perf/lib/include/internal/evsel.h
new file mode 100644
index 000000000000..8b854d1c9b45
--- /dev/null
+++ b/tools/perf/lib/include/internal/evsel.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LIBPERF_INTERNAL_EVSEL_H
+#define __LIBPERF_INTERNAL_EVSEL_H
+
+#include <linux/types.h>
+#include <linux/perf_event.h>
+
+struct perf_cpu_map;
+struct perf_thread_map;
+
+struct perf_evsel {
+ struct list_head node;
+ struct perf_event_attr attr;
+ struct perf_cpu_map *cpus;
+ struct perf_cpu_map *own_cpus;
+ struct perf_thread_map *threads;
+ struct xyarray *fd;
+
+ /* parse modifier helper */
+ int nr_members;
+};
+
+int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
+void perf_evsel__close_fd(struct perf_evsel *evsel);
+void perf_evsel__free_fd(struct perf_evsel *evsel);
+int perf_evsel__read_size(struct perf_evsel *evsel);
+int perf_evsel__apply_filter(struct perf_evsel *evsel, const char *filter);
+
+#endif /* __LIBPERF_INTERNAL_EVSEL_H */
diff --git a/tools/perf/lib/include/internal/lib.h b/tools/perf/lib/include/internal/lib.h
new file mode 100644
index 000000000000..0b56f1201dc9
--- /dev/null
+++ b/tools/perf/lib/include/internal/lib.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LIBPERF_INTERNAL_LIB_H
+#define __LIBPERF_INTERNAL_LIB_H
+
+#include <unistd.h>
+
+ssize_t readn(int fd, void *buf, size_t n);
+ssize_t writen(int fd, const void *buf, size_t n);
+
+#endif /* __LIBPERF_INTERNAL_CPUMAP_H */
diff --git a/tools/perf/lib/include/internal/tests.h b/tools/perf/lib/include/internal/tests.h
new file mode 100644
index 000000000000..b7a20cd24ee1
--- /dev/null
+++ b/tools/perf/lib/include/internal/tests.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LIBPERF_INTERNAL_TESTS_H
+#define __LIBPERF_INTERNAL_TESTS_H
+
+#include <stdio.h>
+
+#define __T_START fprintf(stdout, "- running %s...", __FILE__)
+#define __T_OK fprintf(stdout, "OK\n")
+#define __T_FAIL fprintf(stdout, "FAIL\n")
+
+#define __T(text, cond) \
+do { \
+ if (!(cond)) { \
+ fprintf(stderr, "FAILED %s:%d %s\n", __FILE__, __LINE__, text); \
+ return -1; \
+ } \
+} while (0)
+
+#endif /* __LIBPERF_INTERNAL_TESTS_H */
diff --git a/tools/perf/lib/include/internal/threadmap.h b/tools/perf/lib/include/internal/threadmap.h
new file mode 100644
index 000000000000..df748baf9eda
--- /dev/null
+++ b/tools/perf/lib/include/internal/threadmap.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LIBPERF_INTERNAL_THREADMAP_H
+#define __LIBPERF_INTERNAL_THREADMAP_H
+
+#include <linux/refcount.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+struct thread_map_data {
+ pid_t pid;
+ char *comm;
+};
+
+struct perf_thread_map {
+ refcount_t refcnt;
+ int nr;
+ int err_thread;
+ struct thread_map_data map[];
+};
+
+struct perf_thread_map *perf_thread_map__realloc(struct perf_thread_map *map, int nr);
+
+#endif /* __LIBPERF_INTERNAL_THREADMAP_H */
diff --git a/tools/perf/util/xyarray.h b/tools/perf/lib/include/internal/xyarray.h
index 7ffe562e7ae7..51e35d6c8ec4 100644
--- a/tools/perf/util/xyarray.h
+++ b/tools/perf/lib/include/internal/xyarray.h
@@ -1,7 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _PERF_XYARRAY_H_
-#define _PERF_XYARRAY_H_ 1
+#ifndef __LIBPERF_INTERNAL_XYARRAY_H
+#define __LIBPERF_INTERNAL_XYARRAY_H
+#include <linux/compiler.h>
#include <sys/types.h>
struct xyarray {
@@ -10,7 +11,7 @@ struct xyarray {
size_t entries;
size_t max_x;
size_t max_y;
- char contents[];
+ char contents[] __aligned(8);
};
struct xyarray *xyarray__new(int xlen, int ylen, size_t entry_size);
@@ -32,4 +33,4 @@ static inline int xyarray__max_x(struct xyarray *xy)
return xy->max_x;
}
-#endif /* _PERF_XYARRAY_H_ */
+#endif /* __LIBPERF_INTERNAL_XYARRAY_H */
diff --git a/tools/perf/lib/include/perf/core.h b/tools/perf/lib/include/perf/core.h
new file mode 100644
index 000000000000..c341a7b2c874
--- /dev/null
+++ b/tools/perf/lib/include/perf/core.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LIBPERF_CORE_H
+#define __LIBPERF_CORE_H
+
+#include <stdarg.h>
+
+#ifndef LIBPERF_API
+#define LIBPERF_API __attribute__((visibility("default")))
+#endif
+
+enum libperf_print_level {
+ LIBPERF_WARN,
+ LIBPERF_INFO,
+ LIBPERF_DEBUG,
+};
+
+typedef int (*libperf_print_fn_t)(enum libperf_print_level level,
+ const char *, va_list ap);
+
+LIBPERF_API void libperf_set_print(libperf_print_fn_t fn);
+
+#endif /* __LIBPERF_CORE_H */
diff --git a/tools/perf/lib/include/perf/cpumap.h b/tools/perf/lib/include/perf/cpumap.h
new file mode 100644
index 000000000000..8aa995c59498
--- /dev/null
+++ b/tools/perf/lib/include/perf/cpumap.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LIBPERF_CPUMAP_H
+#define __LIBPERF_CPUMAP_H
+
+#include <perf/core.h>
+#include <stdio.h>
+#include <stdbool.h>
+
+struct perf_cpu_map;
+
+LIBPERF_API struct perf_cpu_map *perf_cpu_map__dummy_new(void);
+LIBPERF_API struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list);
+LIBPERF_API struct perf_cpu_map *perf_cpu_map__read(FILE *file);
+LIBPERF_API struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map);
+LIBPERF_API void perf_cpu_map__put(struct perf_cpu_map *map);
+LIBPERF_API int perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx);
+LIBPERF_API int perf_cpu_map__nr(const struct perf_cpu_map *cpus);
+LIBPERF_API bool perf_cpu_map__empty(const struct perf_cpu_map *map);
+
+#define perf_cpu_map__for_each_cpu(cpu, idx, cpus) \
+ for ((idx) = 0, (cpu) = perf_cpu_map__cpu(cpus, idx); \
+ (idx) < perf_cpu_map__nr(cpus); \
+ (idx)++, (cpu) = perf_cpu_map__cpu(cpus, idx))
+
+#endif /* __LIBPERF_CPUMAP_H */
diff --git a/tools/perf/lib/include/perf/event.h b/tools/perf/lib/include/perf/event.h
new file mode 100644
index 000000000000..18106899cb4e
--- /dev/null
+++ b/tools/perf/lib/include/perf/event.h
@@ -0,0 +1,385 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LIBPERF_EVENT_H
+#define __LIBPERF_EVENT_H
+
+#include <linux/perf_event.h>
+#include <linux/types.h>
+#include <linux/limits.h>
+#include <linux/bpf.h>
+#include <sys/types.h> /* pid_t */
+
+struct perf_record_mmap {
+ struct perf_event_header header;
+ __u32 pid, tid;
+ __u64 start;
+ __u64 len;
+ __u64 pgoff;
+ char filename[PATH_MAX];
+};
+
+struct perf_record_mmap2 {
+ struct perf_event_header header;
+ __u32 pid, tid;
+ __u64 start;
+ __u64 len;
+ __u64 pgoff;
+ __u32 maj;
+ __u32 min;
+ __u64 ino;
+ __u64 ino_generation;
+ __u32 prot;
+ __u32 flags;
+ char filename[PATH_MAX];
+};
+
+struct perf_record_comm {
+ struct perf_event_header header;
+ __u32 pid, tid;
+ char comm[16];
+};
+
+struct perf_record_namespaces {
+ struct perf_event_header header;
+ __u32 pid, tid;
+ __u64 nr_namespaces;
+ struct perf_ns_link_info link_info[];
+};
+
+struct perf_record_fork {
+ struct perf_event_header header;
+ __u32 pid, ppid;
+ __u32 tid, ptid;
+ __u64 time;
+};
+
+struct perf_record_lost {
+ struct perf_event_header header;
+ __u64 id;
+ __u64 lost;
+};
+
+struct perf_record_lost_samples {
+ struct perf_event_header header;
+ __u64 lost;
+};
+
+/*
+ * PERF_FORMAT_ENABLED | PERF_FORMAT_RUNNING | PERF_FORMAT_ID
+ */
+struct perf_record_read {
+ struct perf_event_header header;
+ __u32 pid, tid;
+ __u64 value;
+ __u64 time_enabled;
+ __u64 time_running;
+ __u64 id;
+};
+
+struct perf_record_throttle {
+ struct perf_event_header header;
+ __u64 time;
+ __u64 id;
+ __u64 stream_id;
+};
+
+#ifndef KSYM_NAME_LEN
+#define KSYM_NAME_LEN 256
+#endif
+
+struct perf_record_ksymbol {
+ struct perf_event_header header;
+ __u64 addr;
+ __u32 len;
+ __u16 ksym_type;
+ __u16 flags;
+ char name[KSYM_NAME_LEN];
+};
+
+struct perf_record_bpf_event {
+ struct perf_event_header header;
+ __u16 type;
+ __u16 flags;
+ __u32 id;
+
+ /* for bpf_prog types */
+ __u8 tag[BPF_TAG_SIZE]; // prog tag
+};
+
+struct perf_record_sample {
+ struct perf_event_header header;
+ __u64 array[];
+};
+
+struct perf_record_switch {
+ struct perf_event_header header;
+ __u32 next_prev_pid;
+ __u32 next_prev_tid;
+};
+
+struct perf_record_header_attr {
+ struct perf_event_header header;
+ struct perf_event_attr attr;
+ __u64 id[];
+};
+
+enum {
+ PERF_CPU_MAP__CPUS = 0,
+ PERF_CPU_MAP__MASK = 1,
+};
+
+struct cpu_map_entries {
+ __u16 nr;
+ __u16 cpu[];
+};
+
+struct perf_record_record_cpu_map {
+ __u16 nr;
+ __u16 long_size;
+ unsigned long mask[];
+};
+
+struct perf_record_cpu_map_data {
+ __u16 type;
+ char data[];
+};
+
+struct perf_record_cpu_map {
+ struct perf_event_header header;
+ struct perf_record_cpu_map_data data;
+};
+
+enum {
+ PERF_EVENT_UPDATE__UNIT = 0,
+ PERF_EVENT_UPDATE__SCALE = 1,
+ PERF_EVENT_UPDATE__NAME = 2,
+ PERF_EVENT_UPDATE__CPUS = 3,
+};
+
+struct perf_record_event_update_cpus {
+ struct perf_record_cpu_map_data cpus;
+};
+
+struct perf_record_event_update_scale {
+ double scale;
+};
+
+struct perf_record_event_update {
+ struct perf_event_header header;
+ __u64 type;
+ __u64 id;
+ char data[];
+};
+
+#define MAX_EVENT_NAME 64
+
+struct perf_trace_event_type {
+ __u64 event_id;
+ char name[MAX_EVENT_NAME];
+};
+
+struct perf_record_header_event_type {
+ struct perf_event_header header;
+ struct perf_trace_event_type event_type;
+};
+
+struct perf_record_header_tracing_data {
+ struct perf_event_header header;
+ __u32 size;
+};
+
+struct perf_record_header_build_id {
+ struct perf_event_header header;
+ pid_t pid;
+ __u8 build_id[24];
+ char filename[];
+};
+
+struct id_index_entry {
+ __u64 id;
+ __u64 idx;
+ __u64 cpu;
+ __u64 tid;
+};
+
+struct perf_record_id_index {
+ struct perf_event_header header;
+ __u64 nr;
+ struct id_index_entry entries[0];
+};
+
+struct perf_record_auxtrace_info {
+ struct perf_event_header header;
+ __u32 type;
+ __u32 reserved__; /* For alignment */
+ __u64 priv[];
+};
+
+struct perf_record_auxtrace {
+ struct perf_event_header header;
+ __u64 size;
+ __u64 offset;
+ __u64 reference;
+ __u32 idx;
+ __u32 tid;
+ __u32 cpu;
+ __u32 reserved__; /* For alignment */
+};
+
+#define MAX_AUXTRACE_ERROR_MSG 64
+
+struct perf_record_auxtrace_error {
+ struct perf_event_header header;
+ __u32 type;
+ __u32 code;
+ __u32 cpu;
+ __u32 pid;
+ __u32 tid;
+ __u32 fmt;
+ __u64 ip;
+ __u64 time;
+ char msg[MAX_AUXTRACE_ERROR_MSG];
+};
+
+struct perf_record_aux {
+ struct perf_event_header header;
+ __u64 aux_offset;
+ __u64 aux_size;
+ __u64 flags;
+};
+
+struct perf_record_itrace_start {
+ struct perf_event_header header;
+ __u32 pid;
+ __u32 tid;
+};
+
+struct perf_record_thread_map_entry {
+ __u64 pid;
+ char comm[16];
+};
+
+struct perf_record_thread_map {
+ struct perf_event_header header;
+ __u64 nr;
+ struct perf_record_thread_map_entry entries[];
+};
+
+enum {
+ PERF_STAT_CONFIG_TERM__AGGR_MODE = 0,
+ PERF_STAT_CONFIG_TERM__INTERVAL = 1,
+ PERF_STAT_CONFIG_TERM__SCALE = 2,
+ PERF_STAT_CONFIG_TERM__MAX = 3,
+};
+
+struct perf_record_stat_config_entry {
+ __u64 tag;
+ __u64 val;
+};
+
+struct perf_record_stat_config {
+ struct perf_event_header header;
+ __u64 nr;
+ struct perf_record_stat_config_entry data[];
+};
+
+struct perf_record_stat {
+ struct perf_event_header header;
+
+ __u64 id;
+ __u32 cpu;
+ __u32 thread;
+
+ union {
+ struct {
+ __u64 val;
+ __u64 ena;
+ __u64 run;
+ };
+ __u64 values[3];
+ };
+};
+
+struct perf_record_stat_round {
+ struct perf_event_header header;
+ __u64 type;
+ __u64 time;
+};
+
+struct perf_record_time_conv {
+ struct perf_event_header header;
+ __u64 time_shift;
+ __u64 time_mult;
+ __u64 time_zero;
+};
+
+struct perf_record_header_feature {
+ struct perf_event_header header;
+ __u64 feat_id;
+ char data[];
+};
+
+struct perf_record_compressed {
+ struct perf_event_header header;
+ char data[];
+};
+
+enum perf_user_event_type { /* above any possible kernel type */
+ PERF_RECORD_USER_TYPE_START = 64,
+ PERF_RECORD_HEADER_ATTR = 64,
+ PERF_RECORD_HEADER_EVENT_TYPE = 65, /* deprecated */
+ PERF_RECORD_HEADER_TRACING_DATA = 66,
+ PERF_RECORD_HEADER_BUILD_ID = 67,
+ PERF_RECORD_FINISHED_ROUND = 68,
+ PERF_RECORD_ID_INDEX = 69,
+ PERF_RECORD_AUXTRACE_INFO = 70,
+ PERF_RECORD_AUXTRACE = 71,
+ PERF_RECORD_AUXTRACE_ERROR = 72,
+ PERF_RECORD_THREAD_MAP = 73,
+ PERF_RECORD_CPU_MAP = 74,
+ PERF_RECORD_STAT_CONFIG = 75,
+ PERF_RECORD_STAT = 76,
+ PERF_RECORD_STAT_ROUND = 77,
+ PERF_RECORD_EVENT_UPDATE = 78,
+ PERF_RECORD_TIME_CONV = 79,
+ PERF_RECORD_HEADER_FEATURE = 80,
+ PERF_RECORD_COMPRESSED = 81,
+ PERF_RECORD_HEADER_MAX
+};
+
+union perf_event {
+ struct perf_event_header header;
+ struct perf_record_mmap mmap;
+ struct perf_record_mmap2 mmap2;
+ struct perf_record_comm comm;
+ struct perf_record_namespaces namespaces;
+ struct perf_record_fork fork;
+ struct perf_record_lost lost;
+ struct perf_record_lost_samples lost_samples;
+ struct perf_record_read read;
+ struct perf_record_throttle throttle;
+ struct perf_record_sample sample;
+ struct perf_record_bpf_event bpf;
+ struct perf_record_ksymbol ksymbol;
+ struct perf_record_header_attr attr;
+ struct perf_record_event_update event_update;
+ struct perf_record_header_event_type event_type;
+ struct perf_record_header_tracing_data tracing_data;
+ struct perf_record_header_build_id build_id;
+ struct perf_record_id_index id_index;
+ struct perf_record_auxtrace_info auxtrace_info;
+ struct perf_record_auxtrace auxtrace;
+ struct perf_record_auxtrace_error auxtrace_error;
+ struct perf_record_aux aux;
+ struct perf_record_itrace_start itrace_start;
+ struct perf_record_switch context_switch;
+ struct perf_record_thread_map thread_map;
+ struct perf_record_cpu_map cpu_map;
+ struct perf_record_stat_config stat_config;
+ struct perf_record_stat stat;
+ struct perf_record_stat_round stat_round;
+ struct perf_record_time_conv time_conv;
+ struct perf_record_header_feature feat;
+ struct perf_record_compressed pack;
+};
+
+#endif /* __LIBPERF_EVENT_H */
diff --git a/tools/perf/lib/include/perf/evlist.h b/tools/perf/lib/include/perf/evlist.h
new file mode 100644
index 000000000000..38365f8f3fba
--- /dev/null
+++ b/tools/perf/lib/include/perf/evlist.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LIBPERF_EVLIST_H
+#define __LIBPERF_EVLIST_H
+
+#include <perf/core.h>
+
+struct perf_evlist;
+struct perf_evsel;
+struct perf_cpu_map;
+struct perf_thread_map;
+
+LIBPERF_API void perf_evlist__init(struct perf_evlist *evlist);
+LIBPERF_API void perf_evlist__add(struct perf_evlist *evlist,
+ struct perf_evsel *evsel);
+LIBPERF_API void perf_evlist__remove(struct perf_evlist *evlist,
+ struct perf_evsel *evsel);
+LIBPERF_API struct perf_evlist *perf_evlist__new(void);
+LIBPERF_API void perf_evlist__delete(struct perf_evlist *evlist);
+LIBPERF_API struct perf_evsel* perf_evlist__next(struct perf_evlist *evlist,
+ struct perf_evsel *evsel);
+LIBPERF_API int perf_evlist__open(struct perf_evlist *evlist);
+LIBPERF_API void perf_evlist__close(struct perf_evlist *evlist);
+LIBPERF_API void perf_evlist__enable(struct perf_evlist *evlist);
+LIBPERF_API void perf_evlist__disable(struct perf_evlist *evlist);
+
+#define perf_evlist__for_each_evsel(evlist, pos) \
+ for ((pos) = perf_evlist__next((evlist), NULL); \
+ (pos) != NULL; \
+ (pos) = perf_evlist__next((evlist), (pos)))
+
+LIBPERF_API void perf_evlist__set_maps(struct perf_evlist *evlist,
+ struct perf_cpu_map *cpus,
+ struct perf_thread_map *threads);
+
+#endif /* __LIBPERF_EVLIST_H */
diff --git a/tools/perf/lib/include/perf/evsel.h b/tools/perf/lib/include/perf/evsel.h
new file mode 100644
index 000000000000..4388667f265c
--- /dev/null
+++ b/tools/perf/lib/include/perf/evsel.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LIBPERF_EVSEL_H
+#define __LIBPERF_EVSEL_H
+
+#include <stdint.h>
+#include <perf/core.h>
+
+struct perf_evsel;
+struct perf_event_attr;
+struct perf_cpu_map;
+struct perf_thread_map;
+
+struct perf_counts_values {
+ union {
+ struct {
+ uint64_t val;
+ uint64_t ena;
+ uint64_t run;
+ };
+ uint64_t values[3];
+ };
+};
+
+LIBPERF_API void perf_evsel__init(struct perf_evsel *evsel,
+ struct perf_event_attr *attr);
+LIBPERF_API struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr);
+LIBPERF_API void perf_evsel__delete(struct perf_evsel *evsel);
+LIBPERF_API int perf_evsel__open(struct perf_evsel *evsel, struct perf_cpu_map *cpus,
+ struct perf_thread_map *threads);
+LIBPERF_API void perf_evsel__close(struct perf_evsel *evsel);
+LIBPERF_API int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread,
+ struct perf_counts_values *count);
+LIBPERF_API int perf_evsel__enable(struct perf_evsel *evsel);
+LIBPERF_API int perf_evsel__disable(struct perf_evsel *evsel);
+LIBPERF_API struct perf_cpu_map *perf_evsel__cpus(struct perf_evsel *evsel);
+LIBPERF_API struct perf_thread_map *perf_evsel__threads(struct perf_evsel *evsel);
+LIBPERF_API struct perf_event_attr *perf_evsel__attr(struct perf_evsel *evsel);
+
+#endif /* __LIBPERF_EVSEL_H */
diff --git a/tools/perf/lib/include/perf/threadmap.h b/tools/perf/lib/include/perf/threadmap.h
new file mode 100644
index 000000000000..a7c50de8d010
--- /dev/null
+++ b/tools/perf/lib/include/perf/threadmap.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LIBPERF_THREADMAP_H
+#define __LIBPERF_THREADMAP_H
+
+#include <perf/core.h>
+#include <sys/types.h>
+
+struct perf_thread_map;
+
+LIBPERF_API struct perf_thread_map *perf_thread_map__new_dummy(void);
+
+LIBPERF_API void perf_thread_map__set_pid(struct perf_thread_map *map, int thread, pid_t pid);
+LIBPERF_API char *perf_thread_map__comm(struct perf_thread_map *map, int thread);
+LIBPERF_API int perf_thread_map__nr(struct perf_thread_map *threads);
+LIBPERF_API pid_t perf_thread_map__pid(struct perf_thread_map *map, int thread);
+
+LIBPERF_API struct perf_thread_map *perf_thread_map__get(struct perf_thread_map *map);
+LIBPERF_API void perf_thread_map__put(struct perf_thread_map *map);
+
+#endif /* __LIBPERF_THREADMAP_H */
diff --git a/tools/perf/lib/internal.h b/tools/perf/lib/internal.h
new file mode 100644
index 000000000000..dc92f241732e
--- /dev/null
+++ b/tools/perf/lib/internal.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LIBPERF_INTERNAL_H
+#define __LIBPERF_INTERNAL_H
+
+void libperf_print(enum libperf_print_level level,
+ const char *format, ...)
+ __attribute__((format(printf, 2, 3)));
+
+#define __pr(level, fmt, ...) \
+do { \
+ libperf_print(level, "libperf: " fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define pr_warning(fmt, ...) __pr(LIBPERF_WARN, fmt, ##__VA_ARGS__)
+#define pr_info(fmt, ...) __pr(LIBPERF_INFO, fmt, ##__VA_ARGS__)
+#define pr_debug(fmt, ...) __pr(LIBPERF_DEBUG, fmt, ##__VA_ARGS__)
+
+#endif /* __LIBPERF_INTERNAL_H */
diff --git a/tools/perf/lib/lib.c b/tools/perf/lib/lib.c
new file mode 100644
index 000000000000..2a81819c3b8c
--- /dev/null
+++ b/tools/perf/lib/lib.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <unistd.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <linux/kernel.h>
+#include <internal/lib.h>
+
+static ssize_t ion(bool is_read, int fd, void *buf, size_t n)
+{
+ void *buf_start = buf;
+ size_t left = n;
+
+ while (left) {
+ /* buf must be treated as const if !is_read. */
+ ssize_t ret = is_read ? read(fd, buf, left) :
+ write(fd, buf, left);
+
+ if (ret < 0 && errno == EINTR)
+ continue;
+ if (ret <= 0)
+ return ret;
+
+ left -= ret;
+ buf += ret;
+ }
+
+ BUG_ON((size_t)(buf - buf_start) != n);
+ return n;
+}
+
+/*
+ * Read exactly 'n' bytes or return an error.
+ */
+ssize_t readn(int fd, void *buf, size_t n)
+{
+ return ion(true, fd, buf, n);
+}
+
+/*
+ * Write exactly 'n' bytes or return an error.
+ */
+ssize_t writen(int fd, const void *buf, size_t n)
+{
+ /* ion does not modify buf. */
+ return ion(false, fd, (void *)buf, n);
+}
diff --git a/tools/perf/lib/libperf.map b/tools/perf/lib/libperf.map
new file mode 100644
index 000000000000..dc4d66363bc4
--- /dev/null
+++ b/tools/perf/lib/libperf.map
@@ -0,0 +1,43 @@
+LIBPERF_0.0.1 {
+ global:
+ libperf_set_print;
+ perf_cpu_map__dummy_new;
+ perf_cpu_map__get;
+ perf_cpu_map__put;
+ perf_cpu_map__new;
+ perf_cpu_map__read;
+ perf_cpu_map__nr;
+ perf_cpu_map__cpu;
+ perf_cpu_map__empty;
+ perf_thread_map__new_dummy;
+ perf_thread_map__set_pid;
+ perf_thread_map__comm;
+ perf_thread_map__nr;
+ perf_thread_map__pid;
+ perf_thread_map__get;
+ perf_thread_map__put;
+ perf_evsel__new;
+ perf_evsel__delete;
+ perf_evsel__enable;
+ perf_evsel__disable;
+ perf_evsel__init;
+ perf_evsel__open;
+ perf_evsel__close;
+ perf_evsel__read;
+ perf_evsel__cpus;
+ perf_evsel__threads;
+ perf_evsel__attr;
+ perf_evlist__new;
+ perf_evlist__delete;
+ perf_evlist__open;
+ perf_evlist__close;
+ perf_evlist__enable;
+ perf_evlist__disable;
+ perf_evlist__init;
+ perf_evlist__add;
+ perf_evlist__remove;
+ perf_evlist__next;
+ perf_evlist__set_maps;
+ local:
+ *;
+};
diff --git a/tools/perf/lib/libperf.pc.template b/tools/perf/lib/libperf.pc.template
new file mode 100644
index 000000000000..117e4a237b55
--- /dev/null
+++ b/tools/perf/lib/libperf.pc.template
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+prefix=@PREFIX@
+libdir=@LIBDIR@
+includedir=${prefix}/include
+
+Name: libperf
+Description: perf library
+Version: @VERSION@
+Libs: -L${libdir} -lperf
+Cflags: -I${includedir}
diff --git a/tools/perf/lib/tests/Makefile b/tools/perf/lib/tests/Makefile
new file mode 100644
index 000000000000..1ee4e9ba848b
--- /dev/null
+++ b/tools/perf/lib/tests/Makefile
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+TESTS = test-cpumap test-threadmap test-evlist test-evsel
+
+TESTS_SO := $(addsuffix -so,$(TESTS))
+TESTS_A := $(addsuffix -a,$(TESTS))
+
+# Set compile option CFLAGS
+ifdef EXTRA_CFLAGS
+ CFLAGS := $(EXTRA_CFLAGS)
+else
+ CFLAGS := -g -Wall
+endif
+
+all:
+
+include $(srctree)/tools/scripts/Makefile.include
+
+INCLUDE = -I$(srctree)/tools/perf/lib/include -I$(srctree)/tools/include
+
+$(TESTS_A): FORCE
+ $(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -o $@ $(subst -a,.c,$@) ../libperf.a
+
+$(TESTS_SO): FORCE
+ $(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -L.. -o $@ $(subst -so,.c,$@) -lperf
+
+all: $(TESTS_A) $(TESTS_SO)
+
+run:
+ @echo "running static:"
+ @for i in $(TESTS_A); do ./$$i; done
+ @echo "running dynamic:"
+ @for i in $(TESTS_SO); do LD_LIBRARY_PATH=../ ./$$i; done
+
+clean:
+ $(call QUIET_CLEAN, tests)$(RM) $(TESTS_A) $(TESTS_SO)
+
+.PHONY: all clean FORCE
diff --git a/tools/perf/lib/tests/test-cpumap.c b/tools/perf/lib/tests/test-cpumap.c
new file mode 100644
index 000000000000..76a43cfb83a1
--- /dev/null
+++ b/tools/perf/lib/tests/test-cpumap.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <perf/cpumap.h>
+#include <internal/tests.h>
+
+int main(int argc, char **argv)
+{
+ struct perf_cpu_map *cpus;
+
+ __T_START;
+
+ cpus = perf_cpu_map__dummy_new();
+ if (!cpus)
+ return -1;
+
+ perf_cpu_map__get(cpus);
+ perf_cpu_map__put(cpus);
+ perf_cpu_map__put(cpus);
+
+ __T_OK;
+ return 0;
+}
diff --git a/tools/perf/lib/tests/test-evlist.c b/tools/perf/lib/tests/test-evlist.c
new file mode 100644
index 000000000000..4e1407f20ffd
--- /dev/null
+++ b/tools/perf/lib/tests/test-evlist.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/perf_event.h>
+#include <perf/cpumap.h>
+#include <perf/threadmap.h>
+#include <perf/evlist.h>
+#include <perf/evsel.h>
+#include <internal/tests.h>
+
+static int test_stat_cpu(void)
+{
+ struct perf_cpu_map *cpus;
+ struct perf_evlist *evlist;
+ struct perf_evsel *evsel;
+ struct perf_event_attr attr1 = {
+ .type = PERF_TYPE_SOFTWARE,
+ .config = PERF_COUNT_SW_CPU_CLOCK,
+ };
+ struct perf_event_attr attr2 = {
+ .type = PERF_TYPE_SOFTWARE,
+ .config = PERF_COUNT_SW_TASK_CLOCK,
+ };
+ int err, cpu, tmp;
+
+ cpus = perf_cpu_map__new(NULL);
+ __T("failed to create cpus", cpus);
+
+ evlist = perf_evlist__new();
+ __T("failed to create evlist", evlist);
+
+ evsel = perf_evsel__new(&attr1);
+ __T("failed to create evsel1", evsel);
+
+ perf_evlist__add(evlist, evsel);
+
+ evsel = perf_evsel__new(&attr2);
+ __T("failed to create evsel2", evsel);
+
+ perf_evlist__add(evlist, evsel);
+
+ perf_evlist__set_maps(evlist, cpus, NULL);
+
+ err = perf_evlist__open(evlist);
+ __T("failed to open evsel", err == 0);
+
+ perf_evlist__for_each_evsel(evlist, evsel) {
+ cpus = perf_evsel__cpus(evsel);
+
+ perf_cpu_map__for_each_cpu(cpu, tmp, cpus) {
+ struct perf_counts_values counts = { .val = 0 };
+
+ perf_evsel__read(evsel, cpu, 0, &counts);
+ __T("failed to read value for evsel", counts.val != 0);
+ }
+ }
+
+ perf_evlist__close(evlist);
+ perf_evlist__delete(evlist);
+
+ perf_cpu_map__put(cpus);
+ return 0;
+}
+
+static int test_stat_thread(void)
+{
+ struct perf_counts_values counts = { .val = 0 };
+ struct perf_thread_map *threads;
+ struct perf_evlist *evlist;
+ struct perf_evsel *evsel;
+ struct perf_event_attr attr1 = {
+ .type = PERF_TYPE_SOFTWARE,
+ .config = PERF_COUNT_SW_CPU_CLOCK,
+ };
+ struct perf_event_attr attr2 = {
+ .type = PERF_TYPE_SOFTWARE,
+ .config = PERF_COUNT_SW_TASK_CLOCK,
+ };
+ int err;
+
+ threads = perf_thread_map__new_dummy();
+ __T("failed to create threads", threads);
+
+ perf_thread_map__set_pid(threads, 0, 0);
+
+ evlist = perf_evlist__new();
+ __T("failed to create evlist", evlist);
+
+ evsel = perf_evsel__new(&attr1);
+ __T("failed to create evsel1", evsel);
+
+ perf_evlist__add(evlist, evsel);
+
+ evsel = perf_evsel__new(&attr2);
+ __T("failed to create evsel2", evsel);
+
+ perf_evlist__add(evlist, evsel);
+
+ perf_evlist__set_maps(evlist, NULL, threads);
+
+ err = perf_evlist__open(evlist);
+ __T("failed to open evsel", err == 0);
+
+ perf_evlist__for_each_evsel(evlist, evsel) {
+ perf_evsel__read(evsel, 0, 0, &counts);
+ __T("failed to read value for evsel", counts.val != 0);
+ }
+
+ perf_evlist__close(evlist);
+ perf_evlist__delete(evlist);
+
+ perf_thread_map__put(threads);
+ return 0;
+}
+
+static int test_stat_thread_enable(void)
+{
+ struct perf_counts_values counts = { .val = 0 };
+ struct perf_thread_map *threads;
+ struct perf_evlist *evlist;
+ struct perf_evsel *evsel;
+ struct perf_event_attr attr1 = {
+ .type = PERF_TYPE_SOFTWARE,
+ .config = PERF_COUNT_SW_CPU_CLOCK,
+ .disabled = 1,
+ };
+ struct perf_event_attr attr2 = {
+ .type = PERF_TYPE_SOFTWARE,
+ .config = PERF_COUNT_SW_TASK_CLOCK,
+ .disabled = 1,
+ };
+ int err;
+
+ threads = perf_thread_map__new_dummy();
+ __T("failed to create threads", threads);
+
+ perf_thread_map__set_pid(threads, 0, 0);
+
+ evlist = perf_evlist__new();
+ __T("failed to create evlist", evlist);
+
+ evsel = perf_evsel__new(&attr1);
+ __T("failed to create evsel1", evsel);
+
+ perf_evlist__add(evlist, evsel);
+
+ evsel = perf_evsel__new(&attr2);
+ __T("failed to create evsel2", evsel);
+
+ perf_evlist__add(evlist, evsel);
+
+ perf_evlist__set_maps(evlist, NULL, threads);
+
+ err = perf_evlist__open(evlist);
+ __T("failed to open evsel", err == 0);
+
+ perf_evlist__for_each_evsel(evlist, evsel) {
+ perf_evsel__read(evsel, 0, 0, &counts);
+ __T("failed to read value for evsel", counts.val == 0);
+ }
+
+ perf_evlist__enable(evlist);
+
+ perf_evlist__for_each_evsel(evlist, evsel) {
+ perf_evsel__read(evsel, 0, 0, &counts);
+ __T("failed to read value for evsel", counts.val != 0);
+ }
+
+ perf_evlist__disable(evlist);
+
+ perf_evlist__close(evlist);
+ perf_evlist__delete(evlist);
+
+ perf_thread_map__put(threads);
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ __T_START;
+
+ test_stat_cpu();
+ test_stat_thread();
+ test_stat_thread_enable();
+
+ __T_OK;
+ return 0;
+}
diff --git a/tools/perf/lib/tests/test-evsel.c b/tools/perf/lib/tests/test-evsel.c
new file mode 100644
index 000000000000..2c648fe5617e
--- /dev/null
+++ b/tools/perf/lib/tests/test-evsel.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/perf_event.h>
+#include <perf/cpumap.h>
+#include <perf/threadmap.h>
+#include <perf/evsel.h>
+#include <internal/tests.h>
+
+static int test_stat_cpu(void)
+{
+ struct perf_cpu_map *cpus;
+ struct perf_evsel *evsel;
+ struct perf_event_attr attr = {
+ .type = PERF_TYPE_SOFTWARE,
+ .config = PERF_COUNT_SW_CPU_CLOCK,
+ };
+ int err, cpu, tmp;
+
+ cpus = perf_cpu_map__new(NULL);
+ __T("failed to create cpus", cpus);
+
+ evsel = perf_evsel__new(&attr);
+ __T("failed to create evsel", evsel);
+
+ err = perf_evsel__open(evsel, cpus, NULL);
+ __T("failed to open evsel", err == 0);
+
+ perf_cpu_map__for_each_cpu(cpu, tmp, cpus) {
+ struct perf_counts_values counts = { .val = 0 };
+
+ perf_evsel__read(evsel, cpu, 0, &counts);
+ __T("failed to read value for evsel", counts.val != 0);
+ }
+
+ perf_evsel__close(evsel);
+ perf_evsel__delete(evsel);
+
+ perf_cpu_map__put(cpus);
+ return 0;
+}
+
+static int test_stat_thread(void)
+{
+ struct perf_counts_values counts = { .val = 0 };
+ struct perf_thread_map *threads;
+ struct perf_evsel *evsel;
+ struct perf_event_attr attr = {
+ .type = PERF_TYPE_SOFTWARE,
+ .config = PERF_COUNT_SW_TASK_CLOCK,
+ };
+ int err;
+
+ threads = perf_thread_map__new_dummy();
+ __T("failed to create threads", threads);
+
+ perf_thread_map__set_pid(threads, 0, 0);
+
+ evsel = perf_evsel__new(&attr);
+ __T("failed to create evsel", evsel);
+
+ err = perf_evsel__open(evsel, NULL, threads);
+ __T("failed to open evsel", err == 0);
+
+ perf_evsel__read(evsel, 0, 0, &counts);
+ __T("failed to read value for evsel", counts.val != 0);
+
+ perf_evsel__close(evsel);
+ perf_evsel__delete(evsel);
+
+ perf_thread_map__put(threads);
+ return 0;
+}
+
+static int test_stat_thread_enable(void)
+{
+ struct perf_counts_values counts = { .val = 0 };
+ struct perf_thread_map *threads;
+ struct perf_evsel *evsel;
+ struct perf_event_attr attr = {
+ .type = PERF_TYPE_SOFTWARE,
+ .config = PERF_COUNT_SW_TASK_CLOCK,
+ .disabled = 1,
+ };
+ int err;
+
+ threads = perf_thread_map__new_dummy();
+ __T("failed to create threads", threads);
+
+ perf_thread_map__set_pid(threads, 0, 0);
+
+ evsel = perf_evsel__new(&attr);
+ __T("failed to create evsel", evsel);
+
+ err = perf_evsel__open(evsel, NULL, threads);
+ __T("failed to open evsel", err == 0);
+
+ perf_evsel__read(evsel, 0, 0, &counts);
+ __T("failed to read value for evsel", counts.val == 0);
+
+ err = perf_evsel__enable(evsel);
+ __T("failed to enable evsel", err == 0);
+
+ perf_evsel__read(evsel, 0, 0, &counts);
+ __T("failed to read value for evsel", counts.val != 0);
+
+ err = perf_evsel__disable(evsel);
+ __T("failed to enable evsel", err == 0);
+
+ perf_evsel__close(evsel);
+ perf_evsel__delete(evsel);
+
+ perf_thread_map__put(threads);
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ __T_START;
+
+ test_stat_cpu();
+ test_stat_thread();
+ test_stat_thread_enable();
+
+ __T_OK;
+ return 0;
+}
diff --git a/tools/perf/lib/tests/test-threadmap.c b/tools/perf/lib/tests/test-threadmap.c
new file mode 100644
index 000000000000..10a4f4cbbdd5
--- /dev/null
+++ b/tools/perf/lib/tests/test-threadmap.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <perf/threadmap.h>
+#include <internal/tests.h>
+
+int main(int argc, char **argv)
+{
+ struct perf_thread_map *threads;
+
+ __T_START;
+
+ threads = perf_thread_map__new_dummy();
+ if (!threads)
+ return -1;
+
+ perf_thread_map__get(threads);
+ perf_thread_map__put(threads);
+ perf_thread_map__put(threads);
+
+ __T_OK;
+ return 0;
+}
diff --git a/tools/perf/lib/threadmap.c b/tools/perf/lib/threadmap.c
new file mode 100644
index 000000000000..e92c368b0a6c
--- /dev/null
+++ b/tools/perf/lib/threadmap.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <perf/threadmap.h>
+#include <stdlib.h>
+#include <linux/refcount.h>
+#include <internal/threadmap.h>
+#include <string.h>
+#include <asm/bug.h>
+#include <stdio.h>
+
+static void perf_thread_map__reset(struct perf_thread_map *map, int start, int nr)
+{
+ size_t size = (nr - start) * sizeof(map->map[0]);
+
+ memset(&map->map[start], 0, size);
+ map->err_thread = -1;
+}
+
+struct perf_thread_map *perf_thread_map__realloc(struct perf_thread_map *map, int nr)
+{
+ size_t size = sizeof(*map) + sizeof(map->map[0]) * nr;
+ int start = map ? map->nr : 0;
+
+ map = realloc(map, size);
+ /*
+ * We only realloc to add more items, let's reset new items.
+ */
+ if (map)
+ perf_thread_map__reset(map, start, nr);
+
+ return map;
+}
+
+#define thread_map__alloc(__nr) perf_thread_map__realloc(NULL, __nr)
+
+void perf_thread_map__set_pid(struct perf_thread_map *map, int thread, pid_t pid)
+{
+ map->map[thread].pid = pid;
+}
+
+char *perf_thread_map__comm(struct perf_thread_map *map, int thread)
+{
+ return map->map[thread].comm;
+}
+
+struct perf_thread_map *perf_thread_map__new_dummy(void)
+{
+ struct perf_thread_map *threads = thread_map__alloc(1);
+
+ if (threads != NULL) {
+ perf_thread_map__set_pid(threads, 0, -1);
+ threads->nr = 1;
+ refcount_set(&threads->refcnt, 1);
+ }
+ return threads;
+}
+
+static void perf_thread_map__delete(struct perf_thread_map *threads)
+{
+ if (threads) {
+ int i;
+
+ WARN_ONCE(refcount_read(&threads->refcnt) != 0,
+ "thread map refcnt unbalanced\n");
+ for (i = 0; i < threads->nr; i++)
+ free(perf_thread_map__comm(threads, i));
+ free(threads);
+ }
+}
+
+struct perf_thread_map *perf_thread_map__get(struct perf_thread_map *map)
+{
+ if (map)
+ refcount_inc(&map->refcnt);
+ return map;
+}
+
+void perf_thread_map__put(struct perf_thread_map *map)
+{
+ if (map && refcount_dec_and_test(&map->refcnt))
+ perf_thread_map__delete(map);
+}
+
+int perf_thread_map__nr(struct perf_thread_map *threads)
+{
+ return threads ? threads->nr : 1;
+}
+
+pid_t perf_thread_map__pid(struct perf_thread_map *map, int thread)
+{
+ return map->map[thread].pid;
+}
diff --git a/tools/perf/lib/xyarray.c b/tools/perf/lib/xyarray.c
new file mode 100644
index 000000000000..dcd901d154bb
--- /dev/null
+++ b/tools/perf/lib/xyarray.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <internal/xyarray.h>
+#include <linux/zalloc.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct xyarray *xyarray__new(int xlen, int ylen, size_t entry_size)
+{
+ size_t row_size = ylen * entry_size;
+ struct xyarray *xy = zalloc(sizeof(*xy) + xlen * row_size);
+
+ if (xy != NULL) {
+ xy->entry_size = entry_size;
+ xy->row_size = row_size;
+ xy->entries = xlen * ylen;
+ xy->max_x = xlen;
+ xy->max_y = ylen;
+ }
+
+ return xy;
+}
+
+void xyarray__reset(struct xyarray *xy)
+{
+ size_t n = xy->entries * xy->entry_size;
+
+ memset(xy->contents, 0, n);
+}
+
+void xyarray__delete(struct xyarray *xy)
+{
+ free(xy);
+}
diff --git a/tools/perf/perf-sys.h b/tools/perf/perf-sys.h
index 3eb7a39169f6..63e4349a772a 100644
--- a/tools/perf/perf-sys.h
+++ b/tools/perf/perf-sys.h
@@ -5,54 +5,17 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/syscall.h>
-#include <linux/types.h>
#include <linux/compiler.h>
-#include <linux/perf_event.h>
-#include <asm/barrier.h>
-#ifdef __powerpc__
-#define CPUINFO_PROC {"cpu"}
-#endif
-
-#ifdef __s390__
-#define CPUINFO_PROC {"vendor_id"}
-#endif
-
-#ifdef __sh__
-#define CPUINFO_PROC {"cpu type"}
-#endif
+struct perf_event_attr;
-#ifdef __hppa__
-#define CPUINFO_PROC {"cpu"}
-#endif
-
-#ifdef __sparc__
-#define CPUINFO_PROC {"cpu"}
-#endif
-
-#ifdef __alpha__
-#define CPUINFO_PROC {"cpu model"}
-#endif
+extern bool test_attr__enabled;
+void test_attr__ready(void);
+void test_attr__init(void);
+void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu,
+ int fd, int group_fd, unsigned long flags);
-#ifdef __arm__
-#define CPUINFO_PROC {"model name", "Processor"}
-#endif
-
-#ifdef __mips__
-#define CPUINFO_PROC {"cpu model"}
-#endif
-
-#ifdef __arc__
-#define CPUINFO_PROC {"Processor"}
-#endif
-
-#ifdef __xtensa__
-#define CPUINFO_PROC {"core ID"}
-#endif
-
-#ifndef CPUINFO_PROC
-#define CPUINFO_PROC { "model name", }
-#endif
+#define HAVE_ATTR_TEST
static inline int
sys_perf_event_open(struct perf_event_attr *attr,
diff --git a/tools/perf/perf-with-kcore.sh b/tools/perf/perf-with-kcore.sh
index 7e47a7cbc195..0b96545c8184 100644
--- a/tools/perf/perf-with-kcore.sh
+++ b/tools/perf/perf-with-kcore.sh
@@ -1,15 +1,8 @@
#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-only
# perf-with-kcore: use perf with a copy of kcore
# Copyright (c) 2014, Intel Corporation.
#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms and conditions of the GNU General Public License,
-# version 2, as published by the Free Software Foundation.
-#
-# This program is distributed in the hope it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-# more details.
set -e
@@ -111,11 +104,6 @@ fix_buildid_cache_permissions()
USER_HOME=$(bash <<< "echo ~$SUDO_USER")
- if [ "$HOME" != "$USER_HOME" ] ; then
- echo "Fix unnecessary because root has a home: $HOME" >&2
- exit 1
- fi
-
echo "Fixing buildid cache permissions"
find "$USER_HOME/.debug" -xdev -type d ! -user "$SUDO_USER" -ls -exec chown "$SUDO_USER" \{\} \;
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 72df4b6fa36f..1193b923e801 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -1,4 +1,3 @@
-// SPDX-License-Identifier: GPL-2.0
/*
* perf.c
*
@@ -8,7 +7,10 @@
* perf top, perf record, perf report, etc.) are started.
*/
#include "builtin.h"
+#include "perf.h"
+#include "util/build-id.h"
+#include "util/cache.h"
#include "util/env.h"
#include <subcmd/exec-cmd.h>
#include "util/config.h"
@@ -18,6 +20,9 @@
#include "util/bpf-loader.h"
#include "util/debug.h"
#include "util/event.h"
+#include "util/util.h"
+#include "ui/ui.h"
+#include "perf-sys.h"
#include <api/fs/fs.h>
#include <api/fs/tracing_path.h>
#include <errno.h>
@@ -29,6 +34,8 @@
#include <sys/stat.h>
#include <unistd.h>
#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/zalloc.h>
const char perf_usage_string[] =
"perf [--version] [--help] [OPTIONS] COMMAND [ARGS]";
@@ -440,6 +447,9 @@ int main(int argc, const char **argv)
srandom(time(NULL));
+ /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */
+ config_exclusive_filename = getenv("PERF_CONFIG");
+
err = perf_config(perf_default_config, NULL);
if (err)
return err;
diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index d59dee61b64d..74014033df60 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -2,31 +2,10 @@
#ifndef _PERF_PERF_H
#define _PERF_PERF_H
-#include <time.h>
#include <stdbool.h>
-#include <linux/types.h>
-#include <linux/stddef.h>
-#include <linux/perf_event.h>
-
-extern bool test_attr__enabled;
-void test_attr__ready(void);
-void test_attr__init(void);
-void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu,
- int fd, int group_fd, unsigned long flags);
-
-#define HAVE_ATTR_TEST
-#include "perf-sys.h"
-
-static inline unsigned long long rdclock(void)
-{
- struct timespec ts;
-
- clock_gettime(CLOCK_MONOTONIC, &ts);
- return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
-}
#ifndef MAX_NR_CPUS
-#define MAX_NR_CPUS 1024
+#define MAX_NR_CPUS 2048
#endif
extern const char *input_name;
@@ -35,60 +14,6 @@ extern const char perf_version_string[];
void pthread__unblock_sigwinch(void);
-#include "util/target.h"
-
-struct record_opts {
- struct target target;
- bool group;
- bool inherit_stat;
- bool no_buffering;
- bool no_inherit;
- bool no_inherit_set;
- bool no_samples;
- bool raw_samples;
- bool sample_address;
- bool sample_phys_addr;
- bool sample_weight;
- bool sample_time;
- bool sample_time_set;
- bool sample_cpu;
- bool period;
- bool period_set;
- bool running_time;
- bool full_auxtrace;
- bool auxtrace_snapshot_mode;
- bool record_namespaces;
- bool record_switch_events;
- bool all_kernel;
- bool all_user;
- bool tail_synthesize;
- bool overwrite;
- bool ignore_missing_thread;
- bool strict_freq;
- bool sample_id;
- bool no_bpf_event;
- unsigned int freq;
- unsigned int mmap_pages;
- unsigned int auxtrace_mmap_pages;
- unsigned int user_freq;
- u64 branch_stack;
- u64 sample_intr_regs;
- u64 sample_user_regs;
- u64 default_interval;
- u64 user_interval;
- size_t auxtrace_snapshot_size;
- const char *auxtrace_snapshot_opts;
- bool sample_transaction;
- unsigned initial_delay;
- bool use_clockid;
- clockid_t clockid;
- u64 clockid_res_ns;
- int nr_cblocks;
- int affinity;
- int mmap_flush;
- unsigned int comp_level;
-};
-
enum perf_affinity {
PERF_AFFINITY_SYS = 0,
PERF_AFFINITY_NODE,
@@ -96,10 +21,5 @@ enum perf_affinity {
PERF_AFFINITY_MAX
};
-struct option;
-extern const char * const *record_usage;
-extern struct option *record_options;
extern int version_verbose;
-
-int record__parse_freq(const struct option *opt, const char *str, int unset);
#endif
diff --git a/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-ddrc.json b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-ddrc.json
new file mode 100644
index 000000000000..0d1556fcdffe
--- /dev/null
+++ b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-ddrc.json
@@ -0,0 +1,44 @@
+[
+ {
+ "EventCode": "0x02",
+ "EventName": "uncore_hisi_ddrc.flux_wcmd",
+ "BriefDescription": "DDRC write commands",
+ "PublicDescription": "DDRC write commands",
+ "Unit": "hisi_sccl,ddrc",
+ },
+ {
+ "EventCode": "0x03",
+ "EventName": "uncore_hisi_ddrc.flux_rcmd",
+ "BriefDescription": "DDRC read commands",
+ "PublicDescription": "DDRC read commands",
+ "Unit": "hisi_sccl,ddrc",
+ },
+ {
+ "EventCode": "0x04",
+ "EventName": "uncore_hisi_ddrc.flux_wr",
+ "BriefDescription": "DDRC precharge commands",
+ "PublicDescription": "DDRC precharge commands",
+ "Unit": "hisi_sccl,ddrc",
+ },
+ {
+ "EventCode": "0x05",
+ "EventName": "uncore_hisi_ddrc.act_cmd",
+ "BriefDescription": "DDRC active commands",
+ "PublicDescription": "DDRC active commands",
+ "Unit": "hisi_sccl,ddrc",
+ },
+ {
+ "EventCode": "0x06",
+ "EventName": "uncore_hisi_ddrc.rnk_chg",
+ "BriefDescription": "DDRC rank commands",
+ "PublicDescription": "DDRC rank commands",
+ "Unit": "hisi_sccl,ddrc",
+ },
+ {
+ "EventCode": "0x07",
+ "EventName": "uncore_hisi_ddrc.rw_chg",
+ "BriefDescription": "DDRC read and write changes",
+ "PublicDescription": "DDRC read and write changes",
+ "Unit": "hisi_sccl,ddrc",
+ },
+]
diff --git a/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-hha.json b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-hha.json
new file mode 100644
index 000000000000..447d3064de90
--- /dev/null
+++ b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-hha.json
@@ -0,0 +1,51 @@
+[
+ {
+ "EventCode": "0x00",
+ "EventName": "uncore_hisi_hha.rx_ops_num",
+ "BriefDescription": "The number of all operations received by the HHA",
+ "PublicDescription": "The number of all operations received by the HHA",
+ "Unit": "hisi_sccl,hha",
+ },
+ {
+ "EventCode": "0x01",
+ "EventName": "uncore_hisi_hha.rx_outer",
+ "BriefDescription": "The number of all operations received by the HHA from another socket",
+ "PublicDescription": "The number of all operations received by the HHA from another socket",
+ "Unit": "hisi_sccl,hha",
+ },
+ {
+ "EventCode": "0x02",
+ "EventName": "uncore_hisi_hha.rx_sccl",
+ "BriefDescription": "The number of all operations received by the HHA from another SCCL in this socket",
+ "PublicDescription": "The number of all operations received by the HHA from another SCCL in this socket",
+ "Unit": "hisi_sccl,hha",
+ },
+ {
+ "EventCode": "0x1c",
+ "EventName": "uncore_hisi_hha.rd_ddr_64b",
+ "BriefDescription": "The number of read operations sent by HHA to DDRC which size is 64 bytes",
+ "PublicDescription": "The number of read operations sent by HHA to DDRC which size is 64bytes",
+ "Unit": "hisi_sccl,hha",
+ },
+ {
+ "EventCode": "0x1d",
+ "EventName": "uncore_hisi_hha.wr_dr_64b",
+ "BriefDescription": "The number of write operations sent by HHA to DDRC which size is 64 bytes",
+ "PublicDescription": "The number of write operations sent by HHA to DDRC which size is 64 bytes",
+ "Unit": "hisi_sccl,hha",
+ },
+ {
+ "EventCode": "0x1e",
+ "EventName": "uncore_hisi_hha.rd_ddr_128b",
+ "BriefDescription": "The number of read operations sent by HHA to DDRC which size is 128 bytes",
+ "PublicDescription": "The number of read operations sent by HHA to DDRC which size is 128 bytes",
+ "Unit": "hisi_sccl,hha",
+ },
+ {
+ "EventCode": "0x1f",
+ "EventName": "uncore_hisi_hha.wr_ddr_128b",
+ "BriefDescription": "The number of write operations sent by HHA to DDRC which size is 128 bytes",
+ "PublicDescription": "The number of write operations sent by HHA to DDRC which size is 128 bytes",
+ "Unit": "hisi_sccl,hha",
+ },
+]
diff --git a/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-l3c.json b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-l3c.json
new file mode 100644
index 000000000000..ca48747642e1
--- /dev/null
+++ b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-l3c.json
@@ -0,0 +1,37 @@
+[
+ {
+ "EventCode": "0x00",
+ "EventName": "uncore_hisi_l3c.rd_cpipe",
+ "BriefDescription": "Total read accesses",
+ "PublicDescription": "Total read accesses",
+ "Unit": "hisi_sccl,l3c",
+ },
+ {
+ "EventCode": "0x01",
+ "EventName": "uncore_hisi_l3c.wr_cpipe",
+ "BriefDescription": "Total write accesses",
+ "PublicDescription": "Total write accesses",
+ "Unit": "hisi_sccl,l3c",
+ },
+ {
+ "EventCode": "0x02",
+ "EventName": "uncore_hisi_l3c.rd_hit_cpipe",
+ "BriefDescription": "Total read hits",
+ "PublicDescription": "Total read hits",
+ "Unit": "hisi_sccl,l3c",
+ },
+ {
+ "EventCode": "0x03",
+ "EventName": "uncore_hisi_l3c.wr_hit_cpipe",
+ "BriefDescription": "Total write hits",
+ "PublicDescription": "Total write hits",
+ "Unit": "hisi_sccl,l3c",
+ },
+ {
+ "EventCode": "0x04",
+ "EventName": "uncore_hisi_l3c.victim_num",
+ "BriefDescription": "l3c precharge commands",
+ "PublicDescription": "l3c precharge commands",
+ "Unit": "hisi_sccl,l3c",
+ },
+]
diff --git a/tools/perf/pmu-events/arch/powerpc/power9/memory.json b/tools/perf/pmu-events/arch/powerpc/power9/memory.json
index 2e2ebc700c74..c3bb283e37e9 100644
--- a/tools/perf/pmu-events/arch/powerpc/power9/memory.json
+++ b/tools/perf/pmu-events/arch/powerpc/power9/memory.json
@@ -52,7 +52,7 @@
{,
"EventCode": "0x4D02C",
"EventName": "PM_PMC1_REWIND",
- "BriefDescription": ""
+ "BriefDescription": "PMC1 rewind event"
},
{,
"EventCode": "0x15158",
diff --git a/tools/perf/pmu-events/arch/powerpc/power9/other.json b/tools/perf/pmu-events/arch/powerpc/power9/other.json
index 48cf4f920b3f..62b864269623 100644
--- a/tools/perf/pmu-events/arch/powerpc/power9/other.json
+++ b/tools/perf/pmu-events/arch/powerpc/power9/other.json
@@ -237,7 +237,7 @@
{,
"EventCode": "0xD0B0",
"EventName": "PM_HWSYNC",
- "BriefDescription": ""
+ "BriefDescription": "A hwsync instruction was decoded and transferred"
},
{,
"EventCode": "0x168B0",
@@ -1232,7 +1232,7 @@
{,
"EventCode": "0xD8AC",
"EventName": "PM_LWSYNC",
- "BriefDescription": ""
+ "BriefDescription": "An lwsync instruction was decoded and transferred"
},
{,
"EventCode": "0x2094",
@@ -1747,7 +1747,7 @@
{,
"EventCode": "0xD8B0",
"EventName": "PM_PTESYNC",
- "BriefDescription": ""
+ "BriefDescription": "A ptesync instruction was counted when the instruction is decoded and transmitted"
},
{,
"EventCode": "0x26086",
@@ -2107,7 +2107,7 @@
{,
"EventCode": "0xF080",
"EventName": "PM_LSU_STCX_FAIL",
- "BriefDescription": ""
+ "BriefDescription": "The LSU detects the condition that a stcx instruction failed. No requirement to wait for a response from the nest"
},
{,
"EventCode": "0x30038",
diff --git a/tools/perf/pmu-events/arch/s390/cf_m8561/basic.json b/tools/perf/pmu-events/arch/s390/cf_m8561/basic.json
new file mode 100644
index 000000000000..17fb5241928b
--- /dev/null
+++ b/tools/perf/pmu-events/arch/s390/cf_m8561/basic.json
@@ -0,0 +1,58 @@
+[
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "0",
+ "EventName": "CPU_CYCLES",
+ "BriefDescription": "CPU Cycles",
+ "PublicDescription": "Cycle Count"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "1",
+ "EventName": "INSTRUCTIONS",
+ "BriefDescription": "Instructions",
+ "PublicDescription": "Instruction Count"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "2",
+ "EventName": "L1I_DIR_WRITES",
+ "BriefDescription": "L1I Directory Writes",
+ "PublicDescription": "Level-1 I-Cache Directory Write Count"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "3",
+ "EventName": "L1I_PENALTY_CYCLES",
+ "BriefDescription": "L1I Penalty Cycles",
+ "PublicDescription": "Level-1 I-Cache Penalty Cycle Count"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "4",
+ "EventName": "L1D_DIR_WRITES",
+ "BriefDescription": "L1D Directory Writes",
+ "PublicDescription": "Level-1 D-Cache Directory Write Count"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "5",
+ "EventName": "L1D_PENALTY_CYCLES",
+ "BriefDescription": "L1D Penalty Cycles",
+ "PublicDescription": "Level-1 D-Cache Penalty Cycle Count"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "32",
+ "EventName": "PROBLEM_STATE_CPU_CYCLES",
+ "BriefDescription": "Problem-State CPU Cycles",
+ "PublicDescription": "Problem-State Cycle Count"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "33",
+ "EventName": "PROBLEM_STATE_INSTRUCTIONS",
+ "BriefDescription": "Problem-State Instructions",
+ "PublicDescription": "Problem-State Instruction Count"
+ },
+]
diff --git a/tools/perf/pmu-events/arch/s390/cf_m8561/crypto.json b/tools/perf/pmu-events/arch/s390/cf_m8561/crypto.json
new file mode 100644
index 000000000000..db286f19e7b6
--- /dev/null
+++ b/tools/perf/pmu-events/arch/s390/cf_m8561/crypto.json
@@ -0,0 +1,114 @@
+[
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "64",
+ "EventName": "PRNG_FUNCTIONS",
+ "BriefDescription": "PRNG Functions",
+ "PublicDescription": "Total number of the PRNG functions issued by the CPU"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "65",
+ "EventName": "PRNG_CYCLES",
+ "BriefDescription": "PRNG Cycles",
+ "PublicDescription": "Total number of CPU cycles when the DEA/AES coprocessor is busy performing PRNG functions issued by the CPU"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "66",
+ "EventName": "PRNG_BLOCKED_FUNCTIONS",
+ "BriefDescription": "PRNG Blocked Functions",
+ "PublicDescription": "Total number of the PRNG functions that are issued by the CPU and are blocked because the DEA/AES coprocessor is busy performing a function issued by another CPU"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "67",
+ "EventName": "PRNG_BLOCKED_CYCLES",
+ "BriefDescription": "PRNG Blocked Cycles",
+ "PublicDescription": "Total number of CPU cycles blocked for the PRNG functions issued by the CPU because the DEA/AES coprocessor is busy performing a function issued by another CPU"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "68",
+ "EventName": "SHA_FUNCTIONS",
+ "BriefDescription": "SHA Functions",
+ "PublicDescription": "Total number of SHA functions issued by the CPU"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "69",
+ "EventName": "SHA_CYCLES",
+ "BriefDescription": "SHA Cycles",
+ "PublicDescription": "Total number of CPU cycles when the SHA coprocessor is busy performing the SHA functions issued by the CPU"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "70",
+ "EventName": "SHA_BLOCKED_FUNCTIONS",
+ "BriefDescription": "SHA Blocked Functions",
+ "PublicDescription": "Total number of the SHA functions that are issued by the CPU and are blocked because the SHA coprocessor is busy performing a function issued by another CPU"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "71",
+ "EventName": "SHA_BLOCKED_CYCLES",
+ "BriefDescription": "SHA Bloced Cycles",
+ "PublicDescription": "Total number of CPU cycles blocked for the SHA functions issued by the CPU because the SHA coprocessor is busy performing a function issued by another CPU"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "72",
+ "EventName": "DEA_FUNCTIONS",
+ "BriefDescription": "DEA Functions",
+ "PublicDescription": "Total number of the DEA functions issued by the CPU"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "73",
+ "EventName": "DEA_CYCLES",
+ "BriefDescription": "DEA Cycles",
+ "PublicDescription": "Total number of CPU cycles when the DEA/AES coprocessor is busy performing the DEA functions issued by the CPU"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "74",
+ "EventName": "DEA_BLOCKED_FUNCTIONS",
+ "BriefDescription": "DEA Blocked Functions",
+ "PublicDescription": "Total number of the DEA functions that are issued by the CPU and are blocked because the DEA/AES coprocessor is busy performing a function issued by another CPU"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "75",
+ "EventName": "DEA_BLOCKED_CYCLES",
+ "BriefDescription": "DEA Blocked Cycles",
+ "PublicDescription": "Total number of CPU cycles blocked for the DEA functions issued by the CPU because the DEA/AES coprocessor is busy performing a function issued by another CPU"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "76",
+ "EventName": "AES_FUNCTIONS",
+ "BriefDescription": "AES Functions",
+ "PublicDescription": "Total number of AES functions issued by the CPU"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "77",
+ "EventName": "AES_CYCLES",
+ "BriefDescription": "AES Cycles",
+ "PublicDescription": "Total number of CPU cycles when the DEA/AES coprocessor is busy performing the AES functions issued by the CPU"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "78",
+ "EventName": "AES_BLOCKED_FUNCTIONS",
+ "BriefDescription": "AES Blocked Functions",
+ "PublicDescription": "Total number of AES functions that are issued by the CPU and are blocked because the DEA/AES coprocessor is busy performing a function issued by another CPU"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "79",
+ "EventName": "AES_BLOCKED_CYCLES",
+ "BriefDescription": "AES Blocked Cycles",
+ "PublicDescription": "Total number of CPU cycles blocked for the AES functions issued by the CPU because the DEA/AES coprocessor is busy performing a function issued by another CPU"
+ },
+]
diff --git a/tools/perf/pmu-events/arch/s390/cf_m8561/crypto6.json b/tools/perf/pmu-events/arch/s390/cf_m8561/crypto6.json
new file mode 100644
index 000000000000..5e36bc2468d0
--- /dev/null
+++ b/tools/perf/pmu-events/arch/s390/cf_m8561/crypto6.json
@@ -0,0 +1,30 @@
+[
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "80",
+ "EventName": "ECC_FUNCTION_COUNT",
+ "BriefDescription": "ECC Function Count",
+ "PublicDescription": "Long ECC function Count"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "81",
+ "EventName": "ECC_CYCLES_COUNT",
+ "BriefDescription": "ECC Cycles Count",
+ "PublicDescription": "Long ECC Function cycles count"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "82",
+ "EventName": "ECC_BLOCKED_FUNCTION_COUNT",
+ "BriefDescription": "Ecc Blocked Function Count",
+ "PublicDescription": "Long ECC blocked function count"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "83",
+ "EventName": "ECC_BLOCKED_CYCLES_COUNT",
+ "BriefDescription": "ECC Blocked Cycles Count",
+ "PublicDescription": "Long ECC blocked cycles count"
+ },
+]
diff --git a/tools/perf/pmu-events/arch/s390/cf_m8561/extended.json b/tools/perf/pmu-events/arch/s390/cf_m8561/extended.json
new file mode 100644
index 000000000000..89e070727e1b
--- /dev/null
+++ b/tools/perf/pmu-events/arch/s390/cf_m8561/extended.json
@@ -0,0 +1,373 @@
+[
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "128",
+ "EventName": "L1D_RO_EXCL_WRITES",
+ "BriefDescription": "L1D Read-only Exclusive Writes",
+ "PublicDescription": "A directory write to the Level-1 Data cache where the line was originally in a Read-Only state in the cache but has been updated to be in the Exclusive state that allows stores to the cache line"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "129",
+ "EventName": "DTLB2_WRITES",
+ "BriefDescription": "DTLB2 Writes",
+ "PublicDescription": "A translation has been written into The Translation Lookaside Buffer 2 (TLB2) and the request was made by the data cache"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "130",
+ "EventName": "DTLB2_MISSES",
+ "BriefDescription": "DTLB2 Misses",
+ "PublicDescription": "A TLB2 miss is in progress for a request made by the data cache. Incremented by one for every TLB2 miss in progress for the Level-1 Data cache on this cycle"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "131",
+ "EventName": "DTLB2_HPAGE_WRITES",
+ "BriefDescription": "DTLB2 One-Megabyte Page Writes",
+ "PublicDescription": "A translation entry was written into the Combined Region and Segment Table Entry array in the Level-2 TLB for a one-megabyte page or a Last Host Translation was done"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "132",
+ "EventName": "DTLB2_GPAGE_WRITES",
+ "BriefDescription": "DTLB2 Two-Gigabyte Page Writes",
+ "PublicDescription": "A translation entry for a two-gigabyte page was written into the Level-2 TLB"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "133",
+ "EventName": "L1D_L2D_SOURCED_WRITES",
+ "BriefDescription": "L1D L2D Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from the Level-2 Data cache"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "134",
+ "EventName": "ITLB2_WRITES",
+ "BriefDescription": "ITLB2 Writes",
+ "PublicDescription": "A translation entry has been written into the Translation Lookaside Buffer 2 (TLB2) and the request was made by the instruction cache"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "135",
+ "EventName": "ITLB2_MISSES",
+ "BriefDescription": "ITLB2 Misses",
+ "PublicDescription": "A TLB2 miss is in progress for a request made by the instruction cache. Incremented by one for every TLB2 miss in progress for the Level-1 Instruction cache in a cycle"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "136",
+ "EventName": "L1I_L2I_SOURCED_WRITES",
+ "BriefDescription": "L1I L2I Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from the Level-2 Instruction cache"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "137",
+ "EventName": "TLB2_PTE_WRITES",
+ "BriefDescription": "TLB2 PTE Writes",
+ "PublicDescription": "A translation entry was written into the Page Table Entry array in the Level-2 TLB"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "138",
+ "EventName": "TLB2_CRSTE_WRITES",
+ "BriefDescription": "TLB2 CRSTE Writes",
+ "PublicDescription": "Translation entries were written into the Combined Region and Segment Table Entry array and the Page Table Entry array in the Level-2 TLB"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "139",
+ "EventName": "TLB2_ENGINES_BUSY",
+ "BriefDescription": "TLB2 Engines Busy",
+ "PublicDescription": "The number of Level-2 TLB translation engines busy in a cycle"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "140",
+ "EventName": "TX_C_TEND",
+ "BriefDescription": "Completed TEND instructions in constrained TX mode",
+ "PublicDescription": "A TEND instruction has completed in a constrained transactional-execution mode"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "141",
+ "EventName": "TX_NC_TEND",
+ "BriefDescription": "Completed TEND instructions in non-constrained TX mode",
+ "PublicDescription": "A TEND instruction has completed in a non-constrained transactional-execution mode"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "143",
+ "EventName": "L1C_TLB2_MISSES",
+ "BriefDescription": "L1C TLB2 Misses",
+ "PublicDescription": "Increments by one for any cycle where a level-1 cache or level-2 TLB miss is in progress"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "144",
+ "EventName": "L1D_ONCHIP_L3_SOURCED_WRITES",
+ "BriefDescription": "L1D On-Chip L3 Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from an On-Chip Level-3 cache without intervention"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "145",
+ "EventName": "L1D_ONCHIP_MEMORY_SOURCED_WRITES",
+ "BriefDescription": "L1D On-Chip Memory Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from On-Chip memory"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "146",
+ "EventName": "L1D_ONCHIP_L3_SOURCED_WRITES_IV",
+ "BriefDescription": "L1D On-Chip L3 Sourced Writes with Intervention",
+ "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from an On-Chip Level-3 cache with intervention"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "147",
+ "EventName": "L1D_ONCLUSTER_L3_SOURCED_WRITES",
+ "BriefDescription": "L1D On-Cluster L3 Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from On-Cluster Level-3 cache withountervention"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "148",
+ "EventName": "L1D_ONCLUSTER_MEMORY_SOURCED_WRITES",
+ "BriefDescription": "L1D On-Cluster Memory Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from an On-Cluster memory"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "149",
+ "EventName": "L1D_ONCLUSTER_L3_SOURCED_WRITES_IV",
+ "BriefDescription": "L1D On-Cluster L3 Sourced Writes with Intervention",
+ "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from an On-Cluster Level-3 cache with intervention"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "150",
+ "EventName": "L1D_OFFCLUSTER_L3_SOURCED_WRITES",
+ "BriefDescription": "L1D Off-Cluster L3 Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from an Off-Cluster Level-3 cache without intervention"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "151",
+ "EventName": "L1D_OFFCLUSTER_MEMORY_SOURCED_WRITES",
+ "BriefDescription": "L1D Off-Cluster Memory Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from Off-Cluster memory"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "152",
+ "EventName": "L1D_OFFCLUSTER_L3_SOURCED_WRITES_IV",
+ "BriefDescription": "L1D Off-Cluster L3 Sourced Writes with Intervention",
+ "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from an Off-Cluster Level-3 cache with intervention"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "153",
+ "EventName": "L1D_OFFDRAWER_L3_SOURCED_WRITES",
+ "BriefDescription": "L1D Off-Drawer L3 Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from an Off-Drawer Level-3 cache without intervention"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "154",
+ "EventName": "L1D_OFFDRAWER_MEMORY_SOURCED_WRITES",
+ "BriefDescription": "L1D Off-Drawer Memory Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from Off-Drawer memory"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "155",
+ "EventName": "L1D_OFFDRAWER_L3_SOURCED_WRITES_IV",
+ "BriefDescription": "L1D Off-Drawer L3 Sourced Writes with Intervention",
+ "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from an Off-Drawer Level-3 cache with intervention"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "156",
+ "EventName": "L1D_ONDRAWER_L4_SOURCED_WRITES",
+ "BriefDescription": "L1D On-Drawer L4 Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from On-Drawer Level-4 cache"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "157",
+ "EventName": "L1D_OFFDRAWER_L4_SOURCED_WRITES",
+ "BriefDescription": "L1D Off-Drawer L4 Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from Off-Drawer Level-4 cache"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "158",
+ "EventName": "L1D_ONCHIP_L3_SOURCED_WRITES_RO",
+ "BriefDescription": "L1D On-Chip L3 Sourced Writes read-only",
+ "PublicDescription": "A directory write to the Level-1 Data cache directory where the returned cache line was sourced from On-Chip L3 but a read-only invalidate was done to remove other copies of the cache line"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "162",
+ "EventName": "L1I_ONCHIP_L3_SOURCED_WRITES",
+ "BriefDescription": "L1I On-Chip L3 Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache ine was sourced from an On-Chip Level-3 cache without intervention"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "163",
+ "EventName": "L1I_ONCHIP_MEMORY_SOURCED_WRITES",
+ "BriefDescription": "L1I On-Chip Memory Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache ine was sourced from On-Chip memory"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "164",
+ "EventName": "L1I_ONCHIP_L3_SOURCED_WRITES_IV",
+ "BriefDescription": "L1I On-Chip L3 Sourced Writes with Intervention",
+ "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache ine was sourced from an On-Chip Level-3 cache with intervention"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "165",
+ "EventName": "L1I_ONCLUSTER_L3_SOURCED_WRITES",
+ "BriefDescription": "L1I On-Cluster L3 Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from an On-Cluster Level-3 cache without intervention"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "166",
+ "EventName": "L1I_ONCLUSTER_MEMORY_SOURCED_WRITES",
+ "BriefDescription": "L1I On-Cluster Memory Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from an On-Cluster memory"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "167",
+ "EventName": "L1I_ONCLUSTER_L3_SOURCED_WRITES_IV",
+ "BriefDescription": "L1I On-Cluster L3 Sourced Writes with Intervention",
+ "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from On-Cluster Level-3 cache with intervention"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "168",
+ "EventName": "L1I_OFFCLUSTER_L3_SOURCED_WRITES",
+ "BriefDescription": "L1I Off-Cluster L3 Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from an Off-Cluster Level-3 cache without intervention"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "169",
+ "EventName": "L1I_OFFCLUSTER_MEMORY_SOURCED_WRITES",
+ "BriefDescription": "L1I Off-Cluster Memory Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from Off-Cluster memory"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "170",
+ "EventName": "L1I_OFFCLUSTER_L3_SOURCED_WRITES_IV",
+ "BriefDescription": "L1I Off-Cluster L3 Sourced Writes with Intervention",
+ "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from an Off-Cluster Level-3 cache with intervention"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "171",
+ "EventName": "L1I_OFFDRAWER_L3_SOURCED_WRITES",
+ "BriefDescription": "L1I Off-Drawer L3 Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from an Off-Drawer Level-3 cache without intervention"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "172",
+ "EventName": "L1I_OFFDRAWER_MEMORY_SOURCED_WRITES",
+ "BriefDescription": "L1I Off-Drawer Memory Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from Off-Drawer memory"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "173",
+ "EventName": "L1I_OFFDRAWER_L3_SOURCED_WRITES_IV",
+ "BriefDescription": "L1I Off-Drawer L3 Sourced Writes with Intervention",
+ "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from an Off-Drawer Level-3 cache with intervention"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "174",
+ "EventName": "L1I_ONDRAWER_L4_SOURCED_WRITES",
+ "BriefDescription": "L1I On-Drawer L4 Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from On-Drawer Level-4 cache"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "175",
+ "EventName": "L1I_OFFDRAWER_L4_SOURCED_WRITES",
+ "BriefDescription": "L1I Off-Drawer L4 Sourced Writes",
+ "PublicDescription": "A directory write to the Level-1 Instruction cache directory where the returned cache line was sourced from Off-Drawer Level-4 cache"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "224",
+ "EventName": "BCD_DFP_EXECUTION_SLOTS",
+ "BriefDescription": "BCD DFP Execution Slots",
+ "PublicDescription": "Count of floating point execution slots used for finished Binary Coded Decimal to Decimal Floating Point conversions. Instructions: CDZT, CXZT, CZDT, CZXT"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "225",
+ "EventName": "VX_BCD_EXECUTION_SLOTS",
+ "BriefDescription": "VX BCD Execution Slots",
+ "PublicDescription": "Count of floating point execution slots used for finished vector arithmetic Binary Coded Decimal instructions. Instructions: VAP, VSP, VMPVMSP, VDP, VSDP, VRP, VLIP, VSRP, VPSOPVCP, VTP, VPKZ, VUPKZ, VCVB, VCVBG, VCVDVCVDG"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "226",
+ "EventName": "DECIMAL_INSTRUCTIONS",
+ "BriefDescription": "Decimal Instructions",
+ "PublicDescription": "Decimal instructions dispatched. Instructions: CVB, CVD, AP, CP, DP, ED, EDMK, MP, SRP, SP, ZAP"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "232",
+ "EventName": "LAST_HOST_TRANSLATIONS",
+ "BriefDescription": "Last host translation done",
+ "PublicDescription": "Last Host Translation done"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "243",
+ "EventName": "TX_NC_TABORT",
+ "BriefDescription": "Aborted transactions in non-constrained TX mode",
+ "PublicDescription": "A transaction abort has occurred in a non-constrained transactional-execution mode"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "244",
+ "EventName": "TX_C_TABORT_NO_SPECIAL",
+ "BriefDescription": "Aborted transactions in constrained TX mode not using special completion logic",
+ "PublicDescription": "A transaction abort has occurred in a constrained transactional-execution mode and the CPU is not using any special logic to allow the transaction to complete"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "245",
+ "EventName": "TX_C_TABORT_SPECIAL",
+ "BriefDescription": "Aborted transactions in constrained TX mode using special completion logic",
+ "PublicDescription": "A transaction abort has occurred in a constrained transactional-execution mode and the CPU is using special logic to allow the transaction to complete"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "448",
+ "EventName": "MT_DIAG_CYCLES_ONE_THR_ACTIVE",
+ "BriefDescription": "Cycle count with one thread active",
+ "PublicDescription": "Cycle count with one thread active"
+ },
+ {
+ "Unit": "CPU-M-CF",
+ "EventCode": "449",
+ "EventName": "MT_DIAG_CYCLES_TWO_THR_ACTIVE",
+ "BriefDescription": "Cycle count with two threads active",
+ "PublicDescription": "Cycle count with two threads active"
+ },
+]
diff --git a/tools/perf/pmu-events/arch/s390/mapfile.csv b/tools/perf/pmu-events/arch/s390/mapfile.csv
index 78bcf7f8e206..bd3fc577139c 100644
--- a/tools/perf/pmu-events/arch/s390/mapfile.csv
+++ b/tools/perf/pmu-events/arch/s390/mapfile.csv
@@ -4,3 +4,4 @@ Family-model,Version,Filename,EventType
^IBM.282[78].*[13]\.[1-5].[[:xdigit:]]+$,1,cf_zec12,core
^IBM.296[45].*[13]\.[1-5].[[:xdigit:]]+$,1,cf_z13,core
^IBM.390[67].*[13]\.[1-5].[[:xdigit:]]+$,3,cf_z14,core
+^IBM.856[12].*3\.6.[[:xdigit:]]+$,3,cf_m8561,core
diff --git a/tools/perf/pmu-events/arch/x86/cascadelakex/clx-metrics.json b/tools/perf/pmu-events/arch/x86/cascadelakex/clx-metrics.json
index 1a1a3501180a..a382b115633d 100644
--- a/tools/perf/pmu-events/arch/x86/cascadelakex/clx-metrics.json
+++ b/tools/perf/pmu-events/arch/x86/cascadelakex/clx-metrics.json
@@ -314,13 +314,13 @@
"MetricName": "DRAM_BW_Use"
},
{
- "MetricExpr": "1000000000 * ( cha@event\\=0x36\\\\\\,umask\\=0x21@ / cha@event\\=0x35\\\\\\,umask\\=0x21@ ) / ( cha_0@event\\=0x0@ / duration_time )",
+ "MetricExpr": "1000000000 * ( cha@event\\=0x36\\\\\\,umask\\=0x21\\\\\\,config\\=0x40433@ / cha@event\\=0x35\\\\\\,umask\\=0x21\\\\\\,config\\=0x40433@ ) / ( cha_0@event\\=0x0@ / duration_time )",
"BriefDescription": "Average latency of data read request to external memory (in nanoseconds). Accounts for demand loads and L1/L2 prefetches",
"MetricGroup": "Memory_Lat",
"MetricName": "DRAM_Read_Latency"
},
{
- "MetricExpr": "cha@event\\=0x36\\\\\\,umask\\=0x21@ / cha@event\\=0x36\\\\\\,umask\\=0x21\\\\\\,thresh\\=1@",
+ "MetricExpr": "cha@event\\=0x36\\\\\\,umask\\=0x21\\\\\\,config\\=0x40433@ / cha@event\\=0x36\\\\\\,umask\\=0x21\\\\\\,thresh\\=1\\\\\\,config\\=0x40433@",
"BriefDescription": "Average number of parallel data read requests to external memory. Accounts for demand loads and L1/L2 prefetches",
"MetricGroup": "Memory_BW",
"MetricName": "DRAM_Parallel_Reads"
diff --git a/tools/perf/pmu-events/arch/x86/icelake/cache.json b/tools/perf/pmu-events/arch/x86/icelake/cache.json
new file mode 100644
index 000000000000..3529fc338c17
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/icelake/cache.json
@@ -0,0 +1,552 @@
+[
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of demand Data Read requests that miss L2 cache. Only not rejected loads are counted.",
+ "EventCode": "0x24",
+ "Counter": "0,1,2,3",
+ "UMask": "0x21",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L2_RQSTS.DEMAND_DATA_RD_MISS",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Demand Data Read miss L2, no rejects"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the RFO (Read-for-Ownership) requests that miss L2 cache.",
+ "EventCode": "0x24",
+ "Counter": "0,1,2,3",
+ "UMask": "0x22",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L2_RQSTS.RFO_MISS",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "RFO requests that miss L2 cache"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts L2 cache misses when fetching instructions.",
+ "EventCode": "0x24",
+ "Counter": "0,1,2,3",
+ "UMask": "0x24",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L2_RQSTS.CODE_RD_MISS",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "L2 cache misses when fetching instructions"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts demand requests that miss L2 cache.",
+ "EventCode": "0x24",
+ "Counter": "0,1,2,3",
+ "UMask": "0x27",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L2_RQSTS.ALL_DEMAND_MISS",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Demand requests that miss L2 cache"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts Software prefetch requests that miss the L2 cache. This event accounts for PREFETCHNTA and PREFETCHT0/1/2 instructions.",
+ "EventCode": "0x24",
+ "Counter": "0,1,2,3",
+ "UMask": "0x28",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L2_RQSTS.SWPF_MISS",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "SW prefetch requests that miss L2 cache."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of demand Data Read requests initiated by load instructions that hit L2 cache.",
+ "EventCode": "0x24",
+ "Counter": "0,1,2,3",
+ "UMask": "0xc1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L2_RQSTS.DEMAND_DATA_RD_HIT",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Demand Data Read requests that hit L2 cache"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the RFO (Read-for-Ownership) requests that hit L2 cache.",
+ "EventCode": "0x24",
+ "Counter": "0,1,2,3",
+ "UMask": "0xc2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L2_RQSTS.RFO_HIT",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "RFO requests that hit L2 cache"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts L2 cache hits when fetching instructions, code reads.",
+ "EventCode": "0x24",
+ "Counter": "0,1,2,3",
+ "UMask": "0xc4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L2_RQSTS.CODE_RD_HIT",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "L2 cache hits when fetching instructions, code reads."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts Software prefetch requests that hit the L2 cache. This event accounts for PREFETCHNTA and PREFETCHT0/1/2 instructions.",
+ "EventCode": "0x24",
+ "Counter": "0,1,2,3",
+ "UMask": "0xc8",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L2_RQSTS.SWPF_HIT",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "SW prefetch requests that hit L2 cache."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of demand Data Read requests (including requests from L1D hardware prefetchers). These loads may hit or miss L2 cache. Only non rejected loads are counted.",
+ "EventCode": "0x24",
+ "Counter": "0,1,2,3",
+ "UMask": "0xe1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L2_RQSTS.ALL_DEMAND_DATA_RD",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Demand Data Read requests"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the total number of RFO (read for ownership) requests to L2 cache. L2 RFO requests include both L1D demand RFO misses as well as L1D RFO prefetches.",
+ "EventCode": "0x24",
+ "Counter": "0,1,2,3",
+ "UMask": "0xe2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L2_RQSTS.ALL_RFO",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "RFO requests to L2 cache"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the total number of L2 code requests.",
+ "EventCode": "0x24",
+ "Counter": "0,1,2,3",
+ "UMask": "0xe4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L2_RQSTS.ALL_CODE_RD",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "L2 code requests"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts demand requests to L2 cache.",
+ "EventCode": "0x24",
+ "Counter": "0,1,2,3",
+ "UMask": "0xe7",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L2_RQSTS.ALL_DEMAND_REFERENCES",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Demand requests to L2 cache"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts number of L1D misses that are outstanding in each cycle, that is each cycle the number of Fill Buffers (FB) outstanding required by Demand Reads. FB either is held by demand loads, or it is held by non-demand loads and gets hit at least once by demand. The valid outstanding interval is defined until the FB deallocation by one of the following ways: from FB allocation, if FB is allocated by demand from the demand Hit FB, if it is allocated by hardware or software prefetch. Note: In the L1D, a Demand Read contains cacheable or noncacheable demand loads, including ones causing cache-line splits and reads due to page walks resulted from any request type.",
+ "EventCode": "0x48",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L1D_PEND_MISS.PENDING",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of L1D misses that are outstanding"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts duration of L1D miss outstanding in cycles.",
+ "EventCode": "0x48",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L1D_PEND_MISS.PENDING_CYCLES",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles with L1D load Misses outstanding.",
+ "CounterMask": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts number of cycles a demand request has waited due to L1D Fill Buffer (FB) unavailablability. Demand requests include cacheable/uncacheable demand load, store, lock or SW prefetch accesses.",
+ "EventCode": "0x48",
+ "Counter": "0,1,2,3",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L1D_PEND_MISS.FB_FULL",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of cycles a demand request has waited due to L1D Fill Buffer (FB) unavailablability."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts number of phases a demand request has waited due to L1D Fill Buffer (FB) unavailablability. Demand requests include cacheable/uncacheable demand load, store, lock or SW prefetch accesses.",
+ "EventCode": "0x48",
+ "Counter": "0,1,2,3",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L1D_PEND_MISS.FB_FULL_PERIODS",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of phases a demand request has waited due to L1D Fill Buffer (FB) unavailablability.",
+ "CounterMask": "1",
+ "EdgeDetect": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts number of cycles a demand request has waited due to L1D due to lack of L2 resources. Demand requests include cacheable/uncacheable demand load, store, lock or SW prefetch accesses.",
+ "EventCode": "0x48",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L1D_PEND_MISS.L2_STALL",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of cycles a demand request has waited due to L1D due to lack of L2 resources."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts L1D data line replacements including opportunistic replacements, and replacements that require stall-for-replace or block-for-replace.",
+ "EventCode": "0x51",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L1D.REPLACEMENT",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Counts the number of cache lines replaced in L1 data cache."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of offcore outstanding demand rfo Reads transactions in the super queue every cycle. The 'Offcore outstanding' state of the transaction lasts from the L2 miss until the sending transaction completion to requestor (SQ deallocation). See the corresponding Umask under OFFCORE_REQUESTS.",
+ "EventCode": "0x60",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DEMAND_RFO",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles with offcore outstanding demand rfo reads transactions in SuperQueue (SQ), queue to uncore.",
+ "CounterMask": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of offcore outstanding cacheable Core Data Read transactions in the super queue every cycle. A transaction is considered to be in the Offcore outstanding state between L2 miss and transaction completion sent to requestor (SQ de-allocation). See corresponding Umask under OFFCORE_REQUESTS.",
+ "EventCode": "0x60",
+ "Counter": "0,1,2,3",
+ "UMask": "0x8",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "OFFCORE_REQUESTS_OUTSTANDING.ALL_DATA_RD",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Offcore outstanding cacheable Core Data Read transactions in SuperQueue (SQ), queue to uncore"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles when offcore outstanding cacheable Core Data Read transactions are present in the super queue. A transaction is considered to be in the Offcore outstanding state between L2 miss and transaction completion sent to requestor (SQ de-allocation). See corresponding Umask under OFFCORE_REQUESTS.",
+ "EventCode": "0x60",
+ "Counter": "0,1,2,3",
+ "UMask": "0x8",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DATA_RD",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles when offcore outstanding cacheable Core Data Read transactions are present in SuperQueue (SQ), queue to uncore.",
+ "CounterMask": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the Demand Data Read requests sent to uncore. Use it in conjunction with OFFCORE_REQUESTS_OUTSTANDING to determine average latency in the uncore.",
+ "EventCode": "0xB0",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "OFFCORE_REQUESTS.DEMAND_DATA_RD",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Demand Data Read requests sent to uncore"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the demand RFO (read for ownership) requests including regular RFOs, locks, ItoM.",
+ "EventCode": "0xB0",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "OFFCORE_REQUESTS.DEMAND_RFO",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Demand RFO requests including regular RFOs, locks, ItoM"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the demand and prefetch data reads. All Core Data Reads include cacheable 'Demands' and L2 prefetchers (not L3 prefetchers). Counting also covers reads due to page walks resulted from any request type.",
+ "EventCode": "0xB0",
+ "Counter": "0,1,2,3",
+ "UMask": "0x8",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "OFFCORE_REQUESTS.ALL_DATA_RD",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Demand and prefetch data reads"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts memory transactions reached the super queue including requests initiated by the core, all L3 prefetches, page walks, etc..",
+ "EventCode": "0xB0",
+ "Counter": "0,1,2,3",
+ "UMask": "0x80",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "OFFCORE_REQUESTS.ALL_REQUESTS",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Any memory transaction that reached the SQ."
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired load instructions that true miss the STLB.",
+ "EventCode": "0xD0",
+ "Counter": "0,1,2,3",
+ "UMask": "0x11",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_INST_RETIRED.STLB_MISS_LOADS",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Retired load instructions that miss the STLB.",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired store instructions that true miss the STLB.",
+ "EventCode": "0xD0",
+ "Counter": "0,1,2,3",
+ "UMask": "0x12",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_INST_RETIRED.STLB_MISS_STORES",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Retired store instructions that miss the STLB.",
+ "Data_LA": "1",
+ "L1_Hit_Indication": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired load instructions with locked access.",
+ "EventCode": "0xD0",
+ "Counter": "0,1,2,3",
+ "UMask": "0x21",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_INST_RETIRED.LOCK_LOADS",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Retired load instructions with locked access.",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired load instructions that split across a cacheline boundary.",
+ "EventCode": "0xD0",
+ "Counter": "0,1,2,3",
+ "UMask": "0x41",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_INST_RETIRED.SPLIT_LOADS",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Retired load instructions that split across a cacheline boundary.",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired store instructions that split across a cacheline boundary.",
+ "EventCode": "0xD0",
+ "Counter": "0,1,2,3",
+ "UMask": "0x42",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_INST_RETIRED.SPLIT_STORES",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Retired store instructions that split across a cacheline boundary.",
+ "Data_LA": "1",
+ "L1_Hit_Indication": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts all retired load instructions. This event accounts for SW prefetch instructions for loads.",
+ "EventCode": "0xD0",
+ "Counter": "0,1,2,3",
+ "UMask": "0x81",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_INST_RETIRED.ALL_LOADS",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "All retired load instructions.",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts all retired store instructions. This event account for SW prefetch instructions and PREFETCHW instruction for stores.",
+ "EventCode": "0xD0",
+ "Counter": "0,1,2,3",
+ "UMask": "0x82",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_INST_RETIRED.ALL_STORES",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "All retired store instructions.",
+ "Data_LA": "1",
+ "L1_Hit_Indication": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired load instructions with at least one uop that hit in the L1 data cache. This event includes all SW prefetches and lock instructions regardless of the data source.",
+ "EventCode": "0xD1",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_LOAD_RETIRED.L1_HIT",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Retired load instructions with L1 cache hits as data sources",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired load instructions with L2 cache hits as data sources.",
+ "EventCode": "0xD1",
+ "Counter": "0,1,2,3",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_LOAD_RETIRED.L2_HIT",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Retired load instructions with L2 cache hits as data sources",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired load instructions with at least one uop that hit in the L3 cache.",
+ "EventCode": "0xD1",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_LOAD_RETIRED.L3_HIT",
+ "SampleAfterValue": "50021",
+ "BriefDescription": "Retired load instructions with L3 cache hits as data sources",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired load instructions with at least one uop that missed in the L1 cache.",
+ "EventCode": "0xD1",
+ "Counter": "0,1,2,3",
+ "UMask": "0x8",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_LOAD_RETIRED.L1_MISS",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Retired load instructions missed L1 cache as data sources",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired load instructions missed L2 cache as data sources.",
+ "EventCode": "0xD1",
+ "Counter": "0,1,2,3",
+ "UMask": "0x10",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_LOAD_RETIRED.L2_MISS",
+ "SampleAfterValue": "50021",
+ "BriefDescription": "Retired load instructions missed L2 cache as data sources",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired load instructions with at least one uop that missed in the L3 cache.",
+ "EventCode": "0xD1",
+ "Counter": "0,1,2,3",
+ "UMask": "0x20",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_LOAD_RETIRED.L3_MISS",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Retired load instructions missed L3 cache as data sources",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired load instructions with at least one uop was load missed in L1 but hit FB (Fill Buffers) due to preceding miss to the same cache line with data not ready.",
+ "EventCode": "0xd1",
+ "Counter": "0,1,2,3",
+ "UMask": "0x40",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_LOAD_RETIRED.FB_HIT",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Number of completed demand load requests that missed the L1, but hit the FB(fill buffer), because a preceding miss to the same cacheline initiated the line to be brought into L1, but data is not yet ready in L1.",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the retired load instructions whose data sources were L3 hit and cross-core snoop missed in on-pkg core cache.",
+ "EventCode": "0xd2",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_LOAD_L3_HIT_RETIRED.XSNP_MISS",
+ "SampleAfterValue": "20011",
+ "BriefDescription": "Retired load instructions whose data sources were L3 hit and cross-core snoop missed in on-pkg core cache.",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired load instructions whose data sources were L3 and cross-core snoop hits in on-pkg core cache.",
+ "EventCode": "0xd2",
+ "Counter": "0,1,2,3",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_LOAD_L3_HIT_RETIRED.XSNP_HIT",
+ "SampleAfterValue": "20011",
+ "BriefDescription": "Retired load instructions whose data sources were L3 and cross-core snoop hits in on-pkg core cache",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired load instructions whose data sources were HitM responses from shared L3.",
+ "EventCode": "0xd2",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_LOAD_L3_HIT_RETIRED.XSNP_HITM",
+ "SampleAfterValue": "20011",
+ "BriefDescription": "Retired load instructions whose data sources were HitM responses from shared L3",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired load instructions whose data sources were hits in L3 without snoops required.",
+ "EventCode": "0xd2",
+ "Counter": "0,1,2,3",
+ "UMask": "0x8",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_LOAD_L3_HIT_RETIRED.XSNP_NONE",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Retired load instructions whose data sources were hits in L3 without snoops required",
+ "Data_LA": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of L2 cache lines filling the L2. Counting does not cover rejects.",
+ "EventCode": "0xF1",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1f",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "L2_LINES_IN.ALL",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "L2 cache lines filling L2"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the cycles for which the thread is active and the superQ cannot take any more entries.",
+ "EventCode": "0xF4",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "SQ_MISC.SQ_FULL",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Cycles the thread is active and superQ cannot take any more entries."
+ }
+] \ No newline at end of file
diff --git a/tools/perf/pmu-events/arch/x86/icelake/floating-point.json b/tools/perf/pmu-events/arch/x86/icelake/floating-point.json
new file mode 100644
index 000000000000..594c5551f610
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/icelake/floating-point.json
@@ -0,0 +1,102 @@
+[
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts all microcode Floating Point assists.",
+ "EventCode": "0xC1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "ASSISTS.FP",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Counts all microcode FP assists.",
+ "CounterMask": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts number of SSE/AVX computational scalar double precision floating-point instructions retired; some instructions will count twice as noted below. Each count represents 1 computational operation. Applies to SSE* and AVX* scalar double precision floating-point instructions: ADD SUB MUL DIV MIN MAX SQRT FM(N)ADD/SUB. FM(N)ADD/SUB instructions count twice as they perform 2 calculations per element.",
+ "EventCode": "0xc7",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FP_ARITH_INST_RETIRED.SCALAR_DOUBLE",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of SSE/AVX computational scalar double precision floating-point instructions retired; some instructions will count twice as noted below. Each count represents 1 computation. Applies to SSE* and AVX* scalar double precision floating-point instructions: ADD SUB MUL DIV MIN MAX RCP14 RSQRT14 RANGE SQRT DPP FM(N)ADD/SUB. DPP and FM(N)ADD/SUB instructions count twice as they perform 2 calculations per element."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts number of SSE/AVX computational scalar single precision floating-point instructions retired; some instructions will count twice as noted below. Each count represents 1 computational operation. Applies to SSE* and AVX* scalar single precision floating-point instructions: ADD SUB MUL DIV MIN MAX SQRT RSQRT RCP FM(N)ADD/SUB. FM(N)ADD/SUB instructions count twice as they perform 2 calculations per element.",
+ "EventCode": "0xc7",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FP_ARITH_INST_RETIRED.SCALAR_SINGLE",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of SSE/AVX computational scalar single precision floating-point instructions retired; some instructions will count twice as noted below. Each count represents 1 computation. Applies to SSE* and AVX* scalar single precision floating-point instructions: ADD SUB MUL DIV MIN MAX RCP14 RSQRT14 RANGE SQRT DPP FM(N)ADD/SUB. DPP and FM(N)ADD/SUB instructions count twice as they perform 2 calculations per element."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts number of SSE/AVX computational 128-bit packed double precision floating-point instructions retired; some instructions will count twice as noted below. Each count represents 2 computation operations, one for each element. Applies to SSE* and AVX* packed double precision floating-point instructions: ADD SUB HADD HSUB SUBADD MUL DIV MIN MAX SQRT DPP FM(N)ADD/SUB. DPP and FM(N)ADD/SUB instructions count twice as they perform 2 calculations per element.",
+ "EventCode": "0xc7",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FP_ARITH_INST_RETIRED.128B_PACKED_DOUBLE",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of SSE/AVX computational 128-bit packed double precision floating-point instructions retired; some instructions will count twice as noted below. Each count represents 2 computation operations, one for each element. Applies to SSE* and AVX* packed double precision floating-point instructions: ADD SUB HADD HSUB SUBADD MUL DIV MIN MAX SQRT RSQRT14 RCP14 RANGE DPP FM(N)ADD/SUB. DPP and FM(N)ADD/SUB instructions count twice as they perform 2 calculations per element."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts number of SSE/AVX computational 128-bit packed single precision floating-point instructions retired; some instructions will count twice as noted below. Each count represents 4 computation operations, one for each element. Applies to SSE* and AVX* packed single precision floating-point instructions: ADD SUB HADD HSUB SUBADD MUL DIV MIN MAX SQRT RSQRT RCP DPP FM(N)ADD/SUB. DPP and FM(N)ADD/SUB instructions count twice as they perform 2 calculations per element.",
+ "EventCode": "0xc7",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x8",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FP_ARITH_INST_RETIRED.128B_PACKED_SINGLE",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of SSE/AVX computational 128-bit packed single precision floating-point instructions retired; some instructions will count twice as noted below. Each count represents 4 computation operations, one for each element. Applies to SSE* and AVX* packed single precision floating-point instructions: ADD SUB MUL DIV MIN MAX RCP14 RSQRT14 SQRT DPP FM(N)ADD/SUB. DPP and FM(N)ADD/SUB instructions count twice as they perform 2 calculations per element."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts number of SSE/AVX computational 256-bit packed double precision floating-point instructions retired; some instructions will count twice as noted below. Each count represents 4 computation operations, one for each element. Applies to SSE* and AVX* packed double precision floating-point instructions: ADD SUB HADD HSUB SUBADD MUL DIV MIN MAX SQRT FM(N)ADD/SUB. FM(N)ADD/SUB instructions count twice as they perform 2 calculations per element.",
+ "EventCode": "0xc7",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x10",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FP_ARITH_INST_RETIRED.256B_PACKED_DOUBLE",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of SSE/AVX computational 256-bit packed double precision floating-point instructions retired; some instructions will count twice as noted below. Each count represents 4 computation operations, one for each element. Applies to SSE* and AVX* packed double precision floating-point instructions: ADD SUB MUL DIV MIN MAX RCP14 RSQRT14 RANGE SQRT DPP FM(N)ADD/SUB. DPP and FM(N)ADD/SUB instructions count twice as they perform 2 calculations per element."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts number of SSE/AVX computational 256-bit packed single precision floating-point instructions retired; some instructions will count twice as noted below. Each count represents 8 computation operations, one for each element. Applies to SSE* and AVX* packed single precision floating-point instructions: ADD SUB HADD HSUB SUBADD MUL DIV MIN MAX SQRT RSQRT RCP DPP FM(N)ADD/SUB. DPP and FM(N)ADD/SUB instructions count twice as they perform 2 calculations per element.",
+ "EventCode": "0xc7",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x20",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FP_ARITH_INST_RETIRED.256B_PACKED_SINGLE",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of SSE/AVX computational 256-bit packed single precision floating-point instructions retired; some instructions will count twice as noted below. Each count represents 8 computation operations, one for each element. Applies to SSE* and AVX* packed single precision floating-point instructions: ADD SUB MUL DIV MIN MAX RCP14 RSQRT14 RANGE SQRT DPP FM(N)ADD/SUB. DPP and FM(N)ADD/SUB instructions count twice as they perform 2 calculations per element."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts number of SSE/AVX computational 512-bit packed double precision floating-point instructions retired; some instructions will count twice as noted below. Each count represents 8 computation operations, one for each element. Applies to SSE* and AVX* packed double precision floating-point instructions: ADD SUB MUL DIV MIN MAX SQRT RSQRT14 RCP14 RANGE FM(N)ADD/SUB. FM(N)ADD/SUB instructions count twice as they perform 2 calculations per element.",
+ "EventCode": "0xc7",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x40",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FP_ARITH_INST_RETIRED.512B_PACKED_DOUBLE",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of SSE/AVX computational 512-bit packed double precision floating-point instructions retired; some instructions will count twice as noted below. Each count represents 16 computation operations, one for each element. Applies to SSE* and AVX* packed double precision floating-point instructions: ADD SUB MUL DIV MIN MAX SQRT RSQRT14 RCP14 RANGE FM(N)ADD/SUB. FM(N)ADD/SUB instructions count twice as they perform 2 calculations per element."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts number of SSE/AVX computational 512-bit packed double precision floating-point instructions retired; some instructions will count twice as noted below. Each count represents 16 computation operations, one for each element. Applies to SSE* and AVX* packed double precision floating-point instructions: ADD SUB MUL DIV MIN MAX SQRT RSQRT14 RCP14 RANGE FM(N)ADD/SUB. FM(N)ADD/SUB instructions count twice as they perform 2 calculations per element.",
+ "EventCode": "0xc7",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x80",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FP_ARITH_INST_RETIRED.512B_PACKED_SINGLE",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of SSE/AVX computational 512-bit packed double precision floating-point instructions retired; some instructions will count twice as noted below. Each count represents 8 computation operations, one for each element. Applies to SSE* and AVX* packed double precision floating-point instructions: ADD SUB MUL DIV MIN MAX SQRT RSQRT14 RCP14 RANGE FM(N)ADD/SUB. FM(N)ADD/SUB instructions count twice as they perform 2 calculations per element."
+ }
+] \ No newline at end of file
diff --git a/tools/perf/pmu-events/arch/x86/icelake/frontend.json b/tools/perf/pmu-events/arch/x86/icelake/frontend.json
new file mode 100644
index 000000000000..9c3cfbfcec0f
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/icelake/frontend.json
@@ -0,0 +1,424 @@
+[
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of uops delivered to Instruction Decode Queue (IDQ) from the MITE path. This also means that uops are not being delivered from the Decode Stream Buffer (DSB).",
+ "EventCode": "0x79",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "IDQ.MITE_UOPS",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Uops delivered to Instruction Decode Queue (IDQ) from MITE path"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of cycles where optimal number of uops was delivered to the Instruction Decode Queue (IDQ) from the MITE (legacy decode pipeline) path. During these cycles uops are not being delivered from the Decode Stream Buffer (DSB).",
+ "EventCode": "0x79",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "IDQ.MITE_CYCLES_OK",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles MITE is delivering optimal number of Uops",
+ "CounterMask": "5"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of cycles uops were delivered to the Instruction Decode Queue (IDQ) from the MITE (legacy decode pipeline) path. During these cycles uops are not being delivered from the Decode Stream Buffer (DSB).",
+ "EventCode": "0x79",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "IDQ.MITE_CYCLES_ANY",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles MITE is delivering any Uop",
+ "CounterMask": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of uops delivered to Instruction Decode Queue (IDQ) from the Decode Stream Buffer (DSB) path.",
+ "EventCode": "0x79",
+ "Counter": "0,1,2,3",
+ "UMask": "0x8",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "IDQ.DSB_UOPS",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Uops delivered to Instruction Decode Queue (IDQ) from the Decode Stream Buffer (DSB) path"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of cycles where optimal number of uops was delivered to the Instruction Decode Queue (IDQ) from the MITE (legacy decode pipeline) path. During these cycles uops are not being delivered from the Decode Stream Buffer (DSB).",
+ "EventCode": "0x79",
+ "Counter": "0,1,2,3",
+ "UMask": "0x8",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "IDQ.DSB_CYCLES_OK",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles DSB is delivering optimal number of Uops",
+ "CounterMask": "5"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of cycles uops were delivered to Instruction Decode Queue (IDQ) from the Decode Stream Buffer (DSB) path.",
+ "EventCode": "0x79",
+ "Counter": "0,1,2,3",
+ "UMask": "0x8",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "IDQ.DSB_CYCLES_ANY",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles Decode Stream Buffer (DSB) is delivering any Uop",
+ "CounterMask": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Number of switches from DSB (Decode Stream Buffer) or MITE (legacy decode pipeline) to the Microcode Sequencer.",
+ "EventCode": "0x79",
+ "Counter": "0,1,2,3",
+ "UMask": "0x30",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "IDQ.MS_SWITCHES",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of switches from DSB or MITE to the MS",
+ "CounterMask": "1",
+ "EdgeDetect": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the total number of uops delivered by the Microcode Sequencer (MS). Any instruction over 4 uops will be delivered by the MS. Some instructions such as transcendentals may additionally generate uops from the MS.",
+ "EventCode": "0x79",
+ "Counter": "0,1,2,3",
+ "UMask": "0x30",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "IDQ.MS_UOPS",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Uops delivered to IDQ while MS is busy"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles during which uops are being delivered to Instruction Decode Queue (IDQ) while the Microcode Sequencer (MS) is busy. Uops maybe initiated by Decode Stream Buffer (DSB) or MITE.",
+ "EventCode": "0x79",
+ "Counter": "0,1,2,3",
+ "UMask": "0x30",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "IDQ.MS_CYCLES_ANY",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles when uops are being delivered to IDQ while MS is busy",
+ "CounterMask": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles where a code line fetch is stalled due to an L1 instruction cache miss. The legacy decode pipeline works at a 16 Byte granularity.",
+ "EventCode": "0x80",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "ICACHE_16B.IFDATA_STALL",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles where a code fetch is stalled due to L1 instruction cache miss."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts instruction fetch tag lookups that hit in the instruction cache (L1I). Counts at 64-byte cache-line granularity. Accounts for both cacheable and uncacheable accesses.",
+ "EventCode": "0x83",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "ICACHE_64B.IFTAG_HIT",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Instruction fetch tag lookups that hit in the instruction cache (L1I). Counts at 64-byte cache-line granularity."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts instruction fetch tag lookups that miss in the instruction cache (L1I). Counts at 64-byte cache-line granularity. Accounts for both cacheable and uncacheable accesses.",
+ "EventCode": "0x83",
+ "Counter": "0,1,2,3",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "ICACHE_64B.IFTAG_MISS",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Instruction fetch tag lookups that miss in the instruction cache (L1I). Counts at 64-byte cache-line granularity."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles where a code fetch is stalled due to L1 instruction cache tag miss.",
+ "EventCode": "0x83",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "ICACHE_64B.IFTAG_STALL",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Cycles where a code fetch is stalled due to L1 instruction cache tag miss."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of uops not delivered to by the Instruction Decode Queue (IDQ) to the back-end of the pipeline when there was no back-end stalls. This event counts for one SMT thread in a given cycle.",
+ "EventCode": "0x9C",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "IDQ_UOPS_NOT_DELIVERED.CORE",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Uops not delivered by IDQ when backend of the machine is not stalled"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of cycles when no uops were delivered by the Instruction Decode Queue (IDQ) to the back-end of the pipeline when there was no back-end stalls. This event counts for one SMT thread in a given cycle.",
+ "EventCode": "0x9c",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles when no uops are not delivered by the IDQ when backend of the machine is not stalled",
+ "CounterMask": "5"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of cycles when the optimal number of uops were delivered by the Instruction Decode Queue (IDQ) to the back-end of the pipeline when there was no back-end stalls. This event counts for one SMT thread in a given cycle.",
+ "EventCode": "0x9C",
+ "Invert": "1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "IDQ_UOPS_NOT_DELIVERED.CYCLES_FE_WAS_OK",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles when optimal number of uops was delivered to the back-end when the back-end is not stalled",
+ "CounterMask": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Decode Stream Buffer (DSB) is a Uop-cache that holds translations of previously fetched instructions that were decoded by the legacy x86 decode pipeline (MITE). This event counts fetch penalty cycles when a transition occurs from DSB to MITE.",
+ "EventCode": "0xAB",
+ "Counter": "0,1,2,3",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "DSB2MITE_SWITCHES.PENALTY_CYCLES",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "DSB-to-MITE switch true penalty cycles."
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired Instructions that experienced DSB (Decode stream buffer i.e. the decoded instruction-cache) miss.",
+ "EventCode": "0xC6",
+ "MSRValue": "0x11",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FRONTEND_RETIRED.DSB_MISS",
+ "MSRIndex": "0x3F7",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Retired Instructions who experienced DSB miss.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired Instructions who experienced Instruction L1 Cache true miss.",
+ "EventCode": "0xC6",
+ "MSRValue": "0x12",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FRONTEND_RETIRED.L1I_MISS",
+ "MSRIndex": "0x3F7",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Retired Instructions who experienced Instruction L1 Cache true miss.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired Instructions who experienced Instruction L2 Cache true miss.",
+ "EventCode": "0xC6",
+ "MSRValue": "0x13",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FRONTEND_RETIRED.L2_MISS",
+ "MSRIndex": "0x3F7",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Retired Instructions who experienced Instruction L2 Cache true miss.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired Instructions that experienced iTLB (Instruction TLB) true miss.",
+ "EventCode": "0xC6",
+ "MSRValue": "0x14",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FRONTEND_RETIRED.ITLB_MISS",
+ "MSRIndex": "0x3F7",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Retired Instructions who experienced iTLB true miss.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired Instructions that experienced STLB (2nd level TLB) true miss.",
+ "EventCode": "0xC6",
+ "MSRValue": "0x15",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FRONTEND_RETIRED.STLB_MISS",
+ "MSRIndex": "0x3F7",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Retired Instructions who experienced STLB (2nd level TLB) true miss.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired instructions that are fetched after an interval where the front-end delivered no uops for a period of 2 cycles which was not interrupted by a back-end stall.",
+ "EventCode": "0xC6",
+ "MSRValue": "0x500206",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FRONTEND_RETIRED.LATENCY_GE_2",
+ "MSRIndex": "0x3F7",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Retired instructions that are fetched after an interval where the front-end delivered no uops for a period of 2 cycles which was not interrupted by a back-end stall.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired instructions that are fetched after an interval where the front-end delivered no uops for a period of 4 cycles which was not interrupted by a back-end stall.",
+ "EventCode": "0xC6",
+ "MSRValue": "0x500406",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FRONTEND_RETIRED.LATENCY_GE_4",
+ "MSRIndex": "0x3F7",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Retired instructions that are fetched after an interval where the front-end delivered no uops for a period of 4 cycles which was not interrupted by a back-end stall.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired instructions that are delivered to the back-end after a front-end stall of at least 8 cycles. During this period the front-end delivered no uops.",
+ "EventCode": "0xC6",
+ "MSRValue": "0x500806",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FRONTEND_RETIRED.LATENCY_GE_8",
+ "MSRIndex": "0x3F7",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Retired instructions that are fetched after an interval where the front-end delivered no uops for a period of 8 cycles which was not interrupted by a back-end stall.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired instructions that are delivered to the back-end after a front-end stall of at least 16 cycles. During this period the front-end delivered no uops.",
+ "EventCode": "0xC6",
+ "MSRValue": "0x501006",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FRONTEND_RETIRED.LATENCY_GE_16",
+ "MSRIndex": "0x3F7",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Retired instructions that are fetched after an interval where the front-end delivered no uops for a period of 16 cycles which was not interrupted by a back-end stall.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired instructions that are delivered to the back-end after a front-end stall of at least 32 cycles. During this period the front-end delivered no uops.",
+ "EventCode": "0xC6",
+ "MSRValue": "0x502006",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FRONTEND_RETIRED.LATENCY_GE_32",
+ "MSRIndex": "0x3F7",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Retired instructions that are fetched after an interval where the front-end delivered no uops for a period of 32 cycles which was not interrupted by a back-end stall.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired instructions that are fetched after an interval where the front-end delivered no uops for a period of 64 cycles which was not interrupted by a back-end stall.",
+ "EventCode": "0xC6",
+ "MSRValue": "0x504006",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FRONTEND_RETIRED.LATENCY_GE_64",
+ "MSRIndex": "0x3F7",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Retired instructions that are fetched after an interval where the front-end delivered no uops for a period of 64 cycles which was not interrupted by a back-end stall.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired instructions that are fetched after an interval where the front-end delivered no uops for a period of 128 cycles which was not interrupted by a back-end stall.",
+ "EventCode": "0xC6",
+ "MSRValue": "0x508006",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FRONTEND_RETIRED.LATENCY_GE_128",
+ "MSRIndex": "0x3F7",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Retired instructions that are fetched after an interval where the front-end delivered no uops for a period of 128 cycles which was not interrupted by a back-end stall.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired instructions that are fetched after an interval where the front-end delivered no uops for a period of 256 cycles which was not interrupted by a back-end stall.",
+ "EventCode": "0xC6",
+ "MSRValue": "0x510006",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FRONTEND_RETIRED.LATENCY_GE_256",
+ "MSRIndex": "0x3F7",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Retired instructions that are fetched after an interval where the front-end delivered no uops for a period of 256 cycles which was not interrupted by a back-end stall.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired instructions that are fetched after an interval where the front-end delivered no uops for a period of 512 cycles which was not interrupted by a back-end stall.",
+ "EventCode": "0xC6",
+ "MSRValue": "0x520006",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FRONTEND_RETIRED.LATENCY_GE_512",
+ "MSRIndex": "0x3F7",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Retired instructions that are fetched after an interval where the front-end delivered no uops for a period of 512 cycles which was not interrupted by a back-end stall.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts retired instructions that are delivered to the back-end after the front-end had at least 1 bubble-slot for a period of 2 cycles. A bubble-slot is an empty issue-pipeline slot while there was no RAT stall.",
+ "EventCode": "0xC6",
+ "MSRValue": "0x100206",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "FRONTEND_RETIRED.LATENCY_GE_2_BUBBLES_GE_1",
+ "MSRIndex": "0x3F7",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Retired instructions that are fetched after an interval where the front-end had at least 1 bubble-slot for a period of 2 cycles which was not interrupted by a back-end stall.",
+ "TakenAlone": "1"
+ }
+] \ No newline at end of file
diff --git a/tools/perf/pmu-events/arch/x86/icelake/memory.json b/tools/perf/pmu-events/arch/x86/icelake/memory.json
new file mode 100644
index 000000000000..f158366b9dd6
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/icelake/memory.json
@@ -0,0 +1,410 @@
+[
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times a TSX line had a cache conflict.",
+ "EventCode": "0x54",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "TX_MEM.ABORT_CONFLICT",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of times a transactional abort was signaled due to a data conflict on a transactionally accessed address"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Speculatively counts the number Transactional Synchronization Extensions (TSX) Aborts due to a data capacity limitation for transactional writes.",
+ "EventCode": "0x54",
+ "Counter": "0,1,2,3",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "TX_MEM.ABORT_CAPACITY_WRITE",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Speculatively counts the number TSX Aborts due to a data capacity limitation for transactional writes."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times a TSX Abort was triggered due to a non-release/commit store to lock.",
+ "EventCode": "0x54",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "TX_MEM.ABORT_HLE_STORE_TO_ELIDED_LOCK",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Number of times a HLE transactional region aborted due to a non XRELEASE prefixed instruction writing to an elided lock in the elision buffer"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times a TSX Abort was triggered due to commit but Lock Buffer not empty.",
+ "EventCode": "0x54",
+ "Counter": "0,1,2,3",
+ "UMask": "0x8",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "TX_MEM.ABORT_HLE_ELISION_BUFFER_NOT_EMPTY",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of times an HLE transactional execution aborted due to NoAllocatedElisionBuffer being non-zero."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times a TSX Abort was triggered due to release/commit but data and address mismatch.",
+ "EventCode": "0x54",
+ "Counter": "0,1,2,3",
+ "UMask": "0x10",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "TX_MEM.ABORT_HLE_ELISION_BUFFER_MISMATCH",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of times an HLE transactional execution aborted due to XRELEASE lock not satisfying the address and value requirements in the elision buffer"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times a TSX Abort was triggered due to attempting an unsupported alignment from Lock Buffer.",
+ "EventCode": "0x54",
+ "Counter": "0,1,2,3",
+ "UMask": "0x20",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "TX_MEM.ABORT_HLE_ELISION_BUFFER_UNSUPPORTED_ALIGNMENT",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of times an HLE transactional execution aborted due to an unsupported read alignment from the elision buffer."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times we could not allocate Lock Buffer.",
+ "EventCode": "0x54",
+ "Counter": "0,1,2,3",
+ "UMask": "0x40",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "TX_MEM.HLE_ELISION_BUFFER_FULL",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of times HLE lock could not be elided due to ElisionBufferAvailable being zero."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts Unfriendly TSX abort triggered by a vzeroupper instruction.",
+ "EventCode": "0x5d",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "TX_EXEC.MISC2",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Counts the number of times a class of instructions that may cause a transactional abort was executed inside a transactional region"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts Unfriendly TSX abort triggered by a nest count that is too deep.",
+ "EventCode": "0x5d",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "TX_EXEC.MISC3",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of times an instruction execution caused the transactional nest count supported to be exceeded"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "EventCode": "0xA3",
+ "Counter": "0,1,2,3",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "CYCLE_ACTIVITY.CYCLES_L3_MISS",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles while L3 cache miss demand load is outstanding.",
+ "CounterMask": "2"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "EventCode": "0xA3",
+ "Counter": "0,1,2,3",
+ "UMask": "0x6",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "CYCLE_ACTIVITY.STALLS_L3_MISS",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Execution stalls while L3 cache miss demand load is outstanding.",
+ "CounterMask": "6"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Demand Data Read requests who miss L3 cache.",
+ "EventCode": "0xB0",
+ "Counter": "0,1,2,3",
+ "UMask": "0x10",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "OFFCORE_REQUESTS.L3_MISS_DEMAND_DATA_RD",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Demand Data Read requests who miss L3 cache"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of Machine Clears detected dye to memory ordering. Memory Ordering Machine Clears may apply when a memory read may not conform to the memory ordering rules of the x86 architecture",
+ "EventCode": "0xc3",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "MACHINE_CLEARS.MEMORY_ORDERING",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Number of machine clears due to memory ordering conflicts."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times we entered an HLE region. Does not count nested transactions.",
+ "EventCode": "0xC8",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "HLE_RETIRED.START",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of times an HLE execution started."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times HLE commit succeeded.",
+ "EventCode": "0xC8",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "HLE_RETIRED.COMMIT",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of times an HLE execution successfully committed",
+ "Data_LA": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times HLE abort was triggered.",
+ "EventCode": "0xc8",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "HLE_RETIRED.ABORTED",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of times an HLE execution aborted due to any reasons (multiple categories may count as one)."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times an HLE execution aborted due to various memory events (e.g., read/write capacity and conflicts).",
+ "EventCode": "0xC8",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x8",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "HLE_RETIRED.ABORTED_MEM",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of times an HLE execution aborted due to various memory events (e.g., read/write capacity and conflicts)."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times an HLE execution aborted due to HLE-unfriendly instructions and certain unfriendly events (such as AD assists etc.).",
+ "EventCode": "0xC8",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x20",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "HLE_RETIRED.ABORTED_UNFRIENDLY",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of times an HLE execution aborted due to HLE-unfriendly instructions and certain unfriendly events (such as AD assists etc.)."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times an HLE execution aborted due to unfriendly events (such as interrupts).",
+ "EventCode": "0xC8",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x80",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "HLE_RETIRED.ABORTED_EVENTS",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of times an HLE execution aborted due to unfriendly events (such as interrupts)."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times we entered an RTM region. Does not count nested transactions.",
+ "EventCode": "0xC9",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "RTM_RETIRED.START",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of times an RTM execution started."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times RTM commit succeeded.",
+ "EventCode": "0xC9",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "RTM_RETIRED.COMMIT",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of times an RTM execution successfully committed"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times RTM abort was triggered.",
+ "EventCode": "0xc9",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "RTM_RETIRED.ABORTED",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of times an RTM execution aborted.",
+ "Data_LA": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times an RTM execution aborted due to various memory events (e.g. read/write capacity and conflicts).",
+ "EventCode": "0xC9",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x8",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "RTM_RETIRED.ABORTED_MEM",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of times an RTM execution aborted due to various memory events (e.g. read/write capacity and conflicts)"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times an RTM execution aborted due to HLE-unfriendly instructions.",
+ "EventCode": "0xC9",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x20",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "RTM_RETIRED.ABORTED_UNFRIENDLY",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of times an RTM execution aborted due to HLE-unfriendly instructions"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times an RTM execution aborted due to incompatible memory type.",
+ "EventCode": "0xC9",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x40",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "RTM_RETIRED.ABORTED_MEMTYPE",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of times an RTM execution aborted due to incompatible memory type"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times an RTM execution aborted due to none of the previous 4 categories (e.g. interrupt).",
+ "EventCode": "0xC9",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x80",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "RTM_RETIRED.ABORTED_EVENTS",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of times an RTM execution aborted due to none of the previous 4 categories (e.g. interrupt)"
+ },
+ {
+ "PEBS": "2",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 4 cycles. Reported latency may be longer than just the memory latency.",
+ "EventCode": "0xcd",
+ "MSRValue": "0x4",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_4",
+ "MSRIndex": "0x3F6",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 4 cycles.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "2",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 8 cycles. Reported latency may be longer than just the memory latency.",
+ "EventCode": "0xcd",
+ "MSRValue": "0x8",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_8",
+ "MSRIndex": "0x3F6",
+ "SampleAfterValue": "50021",
+ "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 8 cycles.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "2",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 16 cycles. Reported latency may be longer than just the memory latency.",
+ "EventCode": "0xcd",
+ "MSRValue": "0x10",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_16",
+ "MSRIndex": "0x3F6",
+ "SampleAfterValue": "20011",
+ "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 16 cycles.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "2",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 32 cycles. Reported latency may be longer than just the memory latency.",
+ "EventCode": "0xcd",
+ "MSRValue": "0x20",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_32",
+ "MSRIndex": "0x3F6",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 32 cycles.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "2",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 64 cycles. Reported latency may be longer than just the memory latency.",
+ "EventCode": "0xcd",
+ "MSRValue": "0x40",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_64",
+ "MSRIndex": "0x3F6",
+ "SampleAfterValue": "2003",
+ "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 64 cycles.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "2",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 128 cycles. Reported latency may be longer than just the memory latency.",
+ "EventCode": "0xcd",
+ "MSRValue": "0x80",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_128",
+ "MSRIndex": "0x3F6",
+ "SampleAfterValue": "1009",
+ "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 128 cycles.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "2",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 256 cycles. Reported latency may be longer than just the memory latency.",
+ "EventCode": "0xcd",
+ "MSRValue": "0x100",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_256",
+ "MSRIndex": "0x3F6",
+ "SampleAfterValue": "503",
+ "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 256 cycles.",
+ "TakenAlone": "1"
+ },
+ {
+ "PEBS": "2",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 512 cycles. Reported latency may be longer than just the memory latency.",
+ "EventCode": "0xcd",
+ "MSRValue": "0x200",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_512",
+ "MSRIndex": "0x3F6",
+ "SampleAfterValue": "101",
+ "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 512 cycles.",
+ "TakenAlone": "1"
+ }
+] \ No newline at end of file
diff --git a/tools/perf/pmu-events/arch/x86/icelake/other.json b/tools/perf/pmu-events/arch/x86/icelake/other.json
new file mode 100644
index 000000000000..f8dfdb847224
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/icelake/other.json
@@ -0,0 +1,121 @@
+[
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of available slots for an unhalted logical processor. The event increments by machine-width of the narrowest pipeline as employed by the Top-down Microarchitecture Analysis method. The count is distributed among unhalted logical processors (hyper-threads) who share the same physical core. Software can use this event as the denominator for the top-level metrics of the Top-down Microarchitecture Analysis method. This event is counted on a designated fixed counter (Fixed Counter 3) and is an architectural event.",
+ "Counter": "35",
+ "UMask": "0x4",
+ "PEBScounters": "35",
+ "EventName": "TOPDOWN.SLOTS",
+ "SampleAfterValue": "10000003",
+ "BriefDescription": "Counts the number of available slots for an unhalted logical processor."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts Core cycles where the core was running with power-delivery for baseline license level 0. This includes non-AVX codes, SSE, AVX 128-bit, and low-current AVX 256-bit codes.",
+ "EventCode": "0x28",
+ "Counter": "0,1,2,3",
+ "UMask": "0x7",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "CORE_POWER.LVL0_TURBO_LICENSE",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Core cycles where the core was running in a manner where Turbo may be clipped to the Non-AVX turbo schedule."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts Core cycles where the core was running with power-delivery for license level 1. This includes high current AVX 256-bit instructions as well as low current AVX 512-bit instructions.",
+ "EventCode": "0x28",
+ "Counter": "0,1,2,3",
+ "UMask": "0x18",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "CORE_POWER.LVL1_TURBO_LICENSE",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Core cycles where the core was running in a manner where Turbo may be clipped to the AVX2 turbo schedule."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Core cycles where the core was running with power-delivery for license level 2 (introduced in Skylake Server microarchtecture). This includes high current AVX 512-bit instructions.",
+ "EventCode": "0x28",
+ "Counter": "0,1,2,3",
+ "UMask": "0x20",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "CORE_POWER.LVL2_TURBO_LICENSE",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Core cycles where the core was running in a manner where Turbo may be clipped to the AVX512 turbo schedule."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of PREFETCHNTA instructions executed.",
+ "EventCode": "0x32",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "SW_PREFETCH_ACCESS.NTA",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of PREFETCHNTA instructions executed."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of PREFETCHT0 instructions executed.",
+ "EventCode": "0x32",
+ "Counter": "0,1,2,3",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "SW_PREFETCH_ACCESS.T0",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of PREFETCHT0 instructions executed."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of PREFETCHT1 or PREFETCHT2 instructions executed.",
+ "EventCode": "0x32",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "SW_PREFETCH_ACCESS.T1_T2",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of PREFETCHT1 or PREFETCHT2 instructions executed."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of PREFETCHW instructions executed.",
+ "EventCode": "0x32",
+ "Counter": "0,1,2,3",
+ "UMask": "0x8",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "SW_PREFETCH_ACCESS.PREFETCHW",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of PREFETCHW instructions executed."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of available slots for an unhalted logical processor. The event increments by machine-width of the narrowest pipeline as employed by the Top-down Microarchitecture Analysis method. The count is distributed among unhalted logical processors (hyper-threads) who share the same physical core.",
+ "EventCode": "0xa4",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "TOPDOWN.SLOTS_P",
+ "SampleAfterValue": "10000003",
+ "BriefDescription": "Counts the number of available slots for an unhalted logical processor."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "EventCode": "0xA4",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "TOPDOWN.BACKEND_BOUND_SLOTS",
+ "SampleAfterValue": "10000003",
+ "BriefDescription": "Issue slots where no uops were being issued due to lack of back end resources."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of occurrences where a microcode assist is invoked by hardware Examples include AD (page Access Dirty), FP and AVX related assists.",
+ "EventCode": "0xc1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x7",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "ASSISTS.ANY",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Number of occurrences where a microcode assist is invoked by hardware."
+ }
+] \ No newline at end of file
diff --git a/tools/perf/pmu-events/arch/x86/icelake/pipeline.json b/tools/perf/pmu-events/arch/x86/icelake/pipeline.json
new file mode 100644
index 000000000000..6d8311e634aa
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/icelake/pipeline.json
@@ -0,0 +1,892 @@
+[
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of X86 instructions retired - an Architectural PerfMon event. Counting continues during hardware interrupts, traps, and inside interrupt handlers. Notes: INST_RETIRED.ANY is counted by a designated fixed counter freeing up programmable counters to count other events. INST_RETIRED.ANY_P is counted by a programmable counter.",
+ "Counter": "32",
+ "UMask": "0x1",
+ "PEBScounters": "32",
+ "EventName": "INST_RETIRED.ANY",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of instructions retired. Fixed Counter - architectural event"
+ },
+ {
+ "PEBS": "2",
+ "CollectPEBSRecord": "3",
+ "PublicDescription": "A version of INST_RETIRED that allows for a more unbiased distribution of samples across instructions retired. It utilizes the Precise Distribution of Instructions Retired (PDIR) feature to mitigate some bias in how retired instructions get sampled. Use on Fixed Counter 0.",
+ "Counter": "32",
+ "UMask": "0x1",
+ "PEBScounters": "32",
+ "EventName": "INST_RETIRED.PREC_DIST",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Precise instruction retired event with a reduced effect of PEBS shadow in IP distribution"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of core cycles while the thread is not in a halt state. The thread enters the halt state when it is running the HLT instruction. This event is a component in many key event ratios. The core frequency may change from time to time due to transitions associated with Enhanced Intel SpeedStep Technology or TM2. For this reason this event may have a changing ratio with regards to time. When the core frequency is constant, this event can approximate elapsed time while the core was not in the halt state. It is counted on a dedicated fixed counter, leaving the four (eight when Hyperthreading is disabled) programmable counters available for other events.",
+ "Counter": "33",
+ "UMask": "0x2",
+ "PEBScounters": "33",
+ "EventName": "CPU_CLK_UNHALTED.THREAD",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Core cycles when the thread is not in halt state"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of reference cycles when the core is not in a halt state. The core enters the halt state when it is running the HLT instruction or the MWAIT instruction. This event is not affected by core frequency changes (for example, P states, TM2 transitions) but has the same incrementing frequency as the time stamp counter. This event can approximate elapsed time while the core was not in a halt state. This event has a constant ratio with the CPU_CLK_UNHALTED.REF_XCLK event. It is counted on a dedicated fixed counter, leaving the four (eight when Hyperthreading is disabled) programmable counters available for other events. Note: On all current platforms this event stops counting during 'throttling (TM)' states duty off periods the processor is 'halted'. The counter update is done at a lower clock rate then the core clock the overflow status bit for this counter may appear 'sticky'. After the counter has overflowed and software clears the overflow status bit and resets the counter to less than MAX. The reset value to the counter is not clocked immediately so the overflow status bit will flip 'high (1)' and generate another PMI (if enabled) after which the reset value gets clocked into the counter. Therefore, software will get the interrupt, read the overflow status bit '1 for bit 34 while the counter value is less than MAX. Software should ignore this case.",
+ "Counter": "34",
+ "UMask": "0x3",
+ "PEBScounters": "34",
+ "EventName": "CPU_CLK_UNHALTED.REF_TSC",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Reference cycles when the core is not in halt state."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times the load operation got the true Block-on-Store blocking code preventing store forwarding. This includes cases when: a. preceding store conflicts with the load (incomplete overlap),b. store forwarding is impossible due to u-arch limitations, c. preceding lock RMW operations are not forwarded, d. store has the no-forward bit set (uncacheable/page-split/masked stores), e. all-blocking stores are used (mostly, fences and port I/O), and others. The most common case is a load blocked due to its address range overlapping with a preceding smaller uncompleted store. Note: This event does not take into account cases of out-of-SW-control (for example, SbTailHit), unknown physical STA, and cases of blocking loads on store due to being non-WB memory type or a lock. These cases are covered by other events. See the table of not supported store forwards in the Optimization Guide.",
+ "EventCode": "0x03",
+ "Counter": "0,1,2,3",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "LD_BLOCKS.STORE_FORWARD",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Loads blocked by overlapping with store buffer that cannot be forwarded."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times that split load operations are temporarily blocked because all resources for handling the split accesses are in use.",
+ "EventCode": "0x03",
+ "Counter": "0,1,2,3",
+ "UMask": "0x8",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "LD_BLOCKS.NO_SR",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "The number of times that split load operations are temporarily blocked because all resources for handling the split accesses are in use."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times a load got blocked due to false dependencies in MOB due to partial compare on address.",
+ "EventCode": "0x07",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "LD_BLOCKS_PARTIAL.ADDRESS_ALIAS",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "False dependencies in MOB due to partial compare on address."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts core cycles when the Resource allocator was stalled due to recovery from an earlier branch misprediction or machine clear event.",
+ "EventCode": "0x0D",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "INT_MISC.RECOVERY_CYCLES",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Core cycles the allocator was stalled due to recovery from earlier clear event for this thread"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles the Backend cluster is recovering after a miss-speculation or a Store Buffer or Load Buffer drain stall.",
+ "EventCode": "0x0D",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x3",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "INT_MISC.ALL_RECOVERY_CYCLES",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles the Backend cluster is recovering after a miss-speculation or a Store Buffer or Load Buffer drain stall.",
+ "CounterMask": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Cycles after recovery from a branch misprediction or machine clear till the first uop is issued from the resteered path.",
+ "EventCode": "0x0d",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x80",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "INT_MISC.CLEAR_RESTEER_CYCLES",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Counts cycles after recovery from a branch misprediction or machine clear till the first uop is issued from the resteered path."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of uops that the Resource Allocation Table (RAT) issues to the Reservation Station (RS).",
+ "EventCode": "0x0E",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_ISSUED.ANY",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Uops that RAT issues to RS"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles during which the Resource Allocation Table (RAT) does not issue any Uops to the reservation station (RS) for the current thread.",
+ "EventCode": "0x0E",
+ "Invert": "1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_ISSUED.STALL_CYCLES",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles when RAT does not issue Uops to RS for the thread",
+ "CounterMask": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles when divide unit is busy executing divide or square root operations. Accounts for integer and floating-point operations.",
+ "EventCode": "0x14",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x9",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "ARITH.DIVIDER_ACTIVE",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles when divide unit is busy executing divide or square root operations.",
+ "CounterMask": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "This is an architectural event that counts the number of thread cycles while the thread is not in a halt state. The thread enters the halt state when it is running the HLT instruction. The core frequency may change from time to time due to power or thermal throttling. For this reason, this event may have a changing ratio with regards to wall clock time.",
+ "EventCode": "0x3C",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "CPU_CLK_UNHALTED.THREAD_P",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Thread cycles when thread is not in halt state"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts core crystal clock cycles when the thread is unhalted.",
+ "EventCode": "0x3C",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "CPU_CLK_UNHALTED.REF_XCLK",
+ "SampleAfterValue": "25003",
+ "BriefDescription": "Core crystal clock cycles when the thread is unhalted."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts Core crystal clock cycles when current thread is unhalted and the other thread is halted.",
+ "EventCode": "0x3C",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE",
+ "SampleAfterValue": "25003",
+ "BriefDescription": "Core crystal clock cycles when this thread is unhalted and the other thread is halted."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts all not software-prefetch load dispatches that hit the fill buffer (FB) allocated for the software prefetch. It can also be incremented by some lock instructions. So it should only be used with profiling so that the locks can be excluded by ASM (Assembly File) inspection of the nearby instructions.",
+ "EventCode": "0x4c",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "LOAD_HIT_PREFETCH.SWPF",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Counts the number of demand load dispatches that hit L1D fill buffer (FB) allocated for software prefetch."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles during which the reservation station (RS) is empty for this logical processor. This is usually caused when the front-end pipeline runs into stravation periods (e.g. branch mispredictions or i-cache misses)",
+ "EventCode": "0x5E",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "RS_EVENTS.EMPTY_CYCLES",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles when Reservation Station (RS) is empty for the thread"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts end of periods where the Reservation Station (RS) was empty. Could be useful to closely sample on front-end latency issues (see the FRONTEND_RETIRED event of designated precise events)",
+ "EventCode": "0x5E",
+ "Invert": "1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "RS_EVENTS.EMPTY_END",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Counts end of periods where the Reservation Station (RS) was empty.",
+ "CounterMask": "1",
+ "EdgeDetect": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles that the Instruction Length decoder (ILD) stalls occurred due to dynamically changing prefix length of the decoded instruction (by operand size prefix instruction 0x66, address size prefix instruction 0x67 or REX.W for Intel64). Count is proportional to the number of prefixes in a 16B-line. This may result in a three-cycle penalty for each LCP (Length changing prefix) in a 16-byte chunk.",
+ "EventCode": "0x87",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "ILD_STALL.LCP",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Stalls caused by changing prefix length of the instruction."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts, on the per-thread basis, cycles during which at least one uop is dispatched from the Reservation Station (RS) to port 0.",
+ "EventCode": "0xa1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_DISPATCHED.PORT_0",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of uops executed on port 0"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts, on the per-thread basis, cycles during which at least one uop is dispatched from the Reservation Station (RS) to port 1.",
+ "EventCode": "0xa1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_DISPATCHED.PORT_1",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of uops executed on port 1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts, on the per-thread basis, cycles during which at least one uop is dispatched from the Reservation Station (RS) to ports 2 and 3.",
+ "EventCode": "0xa1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_DISPATCHED.PORT_2_3",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of uops executed on port 2 and 3"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts, on the per-thread basis, cycles during which at least one uop is dispatched from the Reservation Station (RS) to ports 5 and 9.",
+ "EventCode": "0xa1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x10",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_DISPATCHED.PORT_4_9",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of uops executed on port 4 and 9"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts, on the per-thread basis, cycles during which at least one uop is dispatched from the Reservation Station (RS) to port 5.",
+ "EventCode": "0xa1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x20",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_DISPATCHED.PORT_5",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of uops executed on port 5"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts, on the per-thread basis, cycles during which at least one uop is dispatched from the Reservation Station (RS) to port 6.",
+ "EventCode": "0xa1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x40",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_DISPATCHED.PORT_6",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of uops executed on port 6"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts, on the per-thread basis, cycles during which at least one uop is dispatched from the Reservation Station (RS) to ports 7 and 8.",
+ "EventCode": "0xa1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x80",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_DISPATCHED.PORT_7_8",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of uops executed on port 7 and 8"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "EventCode": "0xa2",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "RESOURCE_STALLS.SCOREBOARD",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Counts cycles where the pipeline is stalled due to serializing operations."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts allocation stall cycles caused by the store buffer (SB) being full. This counts cycles that the pipeline back-end blocked uop delivery from the front-end.",
+ "EventCode": "0xA2",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x8",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "RESOURCE_STALLS.SB",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles stalled due to no store buffers available. (not including draining form sync)."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "EventCode": "0xA3",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "CYCLE_ACTIVITY.CYCLES_L2_MISS",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles while L2 cache miss demand load is outstanding.",
+ "CounterMask": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "EventCode": "0xA3",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "CYCLE_ACTIVITY.STALLS_TOTAL",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Total execution stalls.",
+ "CounterMask": "4"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "EventCode": "0xA3",
+ "Counter": "0,1,2,3",
+ "UMask": "0x5",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "CYCLE_ACTIVITY.STALLS_L2_MISS",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Execution stalls while L2 cache miss demand load is outstanding.",
+ "CounterMask": "5"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "EventCode": "0xA3",
+ "Counter": "0,1,2,3",
+ "UMask": "0x8",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "CYCLE_ACTIVITY.CYCLES_L1D_MISS",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles while L1 cache miss demand load is outstanding.",
+ "CounterMask": "8"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "EventCode": "0xA3",
+ "Counter": "0,1,2,3",
+ "UMask": "0xc",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "CYCLE_ACTIVITY.STALLS_L1D_MISS",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Execution stalls while L1 cache miss demand load is outstanding.",
+ "CounterMask": "12"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "EventCode": "0xA3",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x10",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "CYCLE_ACTIVITY.CYCLES_MEM_ANY",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles while memory subsystem has an outstanding load.",
+ "CounterMask": "16"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "EventCode": "0xA3",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x14",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "CYCLE_ACTIVITY.STALLS_MEM_ANY",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Execution stalls while memory subsystem has an outstanding load.",
+ "CounterMask": "20"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles during which a total of 1 uop was executed on all ports and Reservation Station (RS) was not empty.",
+ "EventCode": "0xa6",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "EXE_ACTIVITY.1_PORTS_UTIL",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles total of 1 uop is executed on all ports and Reservation Station was not empty."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles during which a total of 2 uops were executed on all ports and Reservation Station (RS) was not empty.",
+ "EventCode": "0xa6",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "EXE_ACTIVITY.2_PORTS_UTIL",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles total of 2 uops are executed on all ports and Reservation Station was not empty."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles where the Store Buffer was full and no loads caused an execution stall.",
+ "EventCode": "0xA6",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x40",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "EXE_ACTIVITY.BOUND_ON_STORES",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles where the Store Buffer was full and no loads caused an execution stall.",
+ "CounterMask": "2"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles during which no uops were executed on all ports and Reservation Station (RS) was not empty.",
+ "EventCode": "0xa6",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x80",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "EXE_ACTIVITY.EXE_BOUND_0_PORTS",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles where no uops were executed, the Reservation Station was not empty, the Store Buffer was full and there was no outstanding load."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of uops delivered to the back-end by the LSD(Loop Stream Detector).",
+ "EventCode": "0xA8",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "LSD.UOPS",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of Uops delivered by the LSD."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the cycles when at least one uop is delivered by the LSD (Loop-stream detector).",
+ "EventCode": "0xA8",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "LSD.CYCLES_ACTIVE",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles Uops delivered by the LSD, but didn't come from the decoder.",
+ "CounterMask": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the cycles when optimal number of uops is delivered by the LSD (Loop-stream detector).",
+ "EventCode": "0xa8",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "LSD.CYCLES_OK",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles optimal number of Uops delivered by the LSD, but did not come from the decoder.",
+ "CounterMask": "5"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "EventCode": "0xB1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_EXECUTED.THREAD",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Counts the number of uops to be executed per-thread each cycle."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles during which no uops were dispatched from the Reservation Station (RS) per thread.",
+ "EventCode": "0xB1",
+ "Invert": "1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_EXECUTED.STALL_CYCLES",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Counts number of cycles no uops were dispatched to be executed on this thread.",
+ "CounterMask": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Cycles where at least 1 uop was executed per-thread.",
+ "EventCode": "0xb1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_EXECUTED.CYCLES_GE_1",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles where at least 1 uop was executed per-thread",
+ "CounterMask": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Cycles where at least 2 uops were executed per-thread.",
+ "EventCode": "0xb1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_EXECUTED.CYCLES_GE_2",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles where at least 2 uops were executed per-thread",
+ "CounterMask": "2"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Cycles where at least 3 uops were executed per-thread.",
+ "EventCode": "0xb1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_EXECUTED.CYCLES_GE_3",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles where at least 3 uops were executed per-thread",
+ "CounterMask": "3"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Cycles where at least 4 uops were executed per-thread.",
+ "EventCode": "0xb1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_EXECUTED.CYCLES_GE_4",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles where at least 4 uops were executed per-thread",
+ "CounterMask": "4"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of uops executed from any thread.",
+ "EventCode": "0xB1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_EXECUTED.CORE",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of uops executed on the core."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles when at least 1 micro-op is executed from any thread on physical core.",
+ "EventCode": "0xB1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_EXECUTED.CORE_CYCLES_GE_1",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles at least 1 micro-op is executed from any thread on physical core.",
+ "CounterMask": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles when at least 2 micro-ops are executed from any thread on physical core.",
+ "EventCode": "0xB1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_EXECUTED.CORE_CYCLES_GE_2",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles at least 2 micro-op is executed from any thread on physical core.",
+ "CounterMask": "2"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles when at least 3 micro-ops are executed from any thread on physical core.",
+ "EventCode": "0xB1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_EXECUTED.CORE_CYCLES_GE_3",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles at least 3 micro-op is executed from any thread on physical core.",
+ "CounterMask": "3"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles when at least 4 micro-ops are executed from any thread on physical core.",
+ "EventCode": "0xB1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_EXECUTED.CORE_CYCLES_GE_4",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles at least 4 micro-op is executed from any thread on physical core.",
+ "CounterMask": "4"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of x87 uops executed.",
+ "EventCode": "0xB1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x10",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_EXECUTED.X87",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Counts the number of x87 uops dispatched."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of X86 instructions retired - an Architectural PerfMon event. Counting continues during hardware interrupts, traps, and inside interrupt handlers. Notes: INST_RETIRED.ANY is counted by a designated fixed counter freeing up programmable counters to count other events. INST_RETIRED.ANY_P is counted by a programmable counter.",
+ "EventCode": "0xC0",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "INST_RETIRED.ANY_P",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of instructions retired. General Counter - architectural event"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of cycles using always true condition (uops_ret &amp;lt; 16) applied to non PEBS uops retired event.",
+ "EventCode": "0xC2",
+ "Invert": "1",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_RETIRED.TOTAL_CYCLES",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycles with less than 10 actually retired uops.",
+ "CounterMask": "10"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the retirement slots used each cycle.",
+ "EventCode": "0xc2",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "UOPS_RETIRED.SLOTS",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Retirement slots used."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of machine clears (nukes) of any type.",
+ "EventCode": "0xC3",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "MACHINE_CLEARS.COUNT",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Number of machine clears (nukes) of any type.",
+ "CounterMask": "1",
+ "EdgeDetect": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts self-modifying code (SMC) detected, which causes a machine clear.",
+ "EventCode": "0xC3",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "MACHINE_CLEARS.SMC",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Self-modifying code (SMC) detected."
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts all branch instructions retired.",
+ "EventCode": "0xC4",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "BR_INST_RETIRED.ALL_BRANCHES",
+ "SampleAfterValue": "400009",
+ "BriefDescription": "All branch instructions retired."
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts taken conditional branch instructions retired.",
+ "EventCode": "0xc4",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "BR_INST_RETIRED.COND_TAKEN",
+ "SampleAfterValue": "400009",
+ "BriefDescription": "Taken conditional branch instructions retired."
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts both direct and indirect near call instructions retired.",
+ "EventCode": "0xC4",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "BR_INST_RETIRED.NEAR_CALL",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Direct and indirect near call instructions retired."
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts return instructions retired.",
+ "EventCode": "0xC4",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x8",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "BR_INST_RETIRED.NEAR_RETURN",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Return instructions retired."
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts not taken branch instructions retired.",
+ "EventCode": "0xC4",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x10",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "BR_INST_RETIRED.COND_NTAKEN",
+ "SampleAfterValue": "400009",
+ "BriefDescription": "Not taken branch instructions retired."
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts conditional branch instructions retired.",
+ "EventCode": "0xc4",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x11",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "BR_INST_RETIRED.COND",
+ "SampleAfterValue": "400009",
+ "BriefDescription": "Conditional branch instructions retired."
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts taken branch instructions retired.",
+ "EventCode": "0xC4",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x20",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "BR_INST_RETIRED.NEAR_TAKEN",
+ "SampleAfterValue": "400009",
+ "BriefDescription": "Taken branch instructions retired."
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts far branch instructions retired.",
+ "EventCode": "0xC4",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x40",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "BR_INST_RETIRED.FAR_BRANCH",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Far branch instructions retired."
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts all indirect branch instructions retired (excluding RETs. TSX aborts is considered indirect branch).",
+ "EventCode": "0xc4",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x80",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "BR_INST_RETIRED.INDIRECT",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "All indirect branch instructions retired (excluding RETs. TSX aborts are considered indirect branch)."
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts all the retired branch instructions that were mispredicted by the processor. A branch misprediction occurs when the processor incorrectly predicts the destination of the branch. When the misprediction is discovered at execution, all the instructions executed in the wrong (speculative) path must be discarded, and the processor must start fetching from the correct path.",
+ "EventCode": "0xC5",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "BR_MISP_RETIRED.ALL_BRANCHES",
+ "SampleAfterValue": "400009",
+ "BriefDescription": "All mispredicted branch instructions retired.",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts taken conditional mispredicted branch instructions retired.",
+ "EventCode": "0xc5",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "BR_MISP_RETIRED.COND_TAKEN",
+ "SampleAfterValue": "400009",
+ "BriefDescription": "number of branch instructions retired that were mispredicted and taken. Non PEBS",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts mispredicted conditional branch instructions retired.",
+ "EventCode": "0xc5",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x11",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "BR_MISP_RETIRED.COND",
+ "SampleAfterValue": "400009",
+ "BriefDescription": "Mispredicted conditional branch instructions retired.",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts number of near branch instructions retired that were mispredicted and taken.",
+ "EventCode": "0xC5",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x20",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "BR_MISP_RETIRED.NEAR_TAKEN",
+ "SampleAfterValue": "400009",
+ "BriefDescription": "Number of near branch instructions retired that were mispredicted and taken.",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts all miss-predicted indirect branch instructions retired (excluding RETs. TSX aborts is considered indirect branch).",
+ "EventCode": "0xC5",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x80",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "BR_MISP_RETIRED.INDIRECT",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "All miss-predicted indirect branch instructions retired (excluding RETs. TSX aborts is considered indirect branch).",
+ "Data_LA": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Increments when an entry is added to the Last Branch Record (LBR) array (or removed from the array in case of RETURNs in call stack mode). The event requires LBR enable via IA32_DEBUGCTL MSR and branch type selection via MSR_LBR_SELECT.",
+ "EventCode": "0xcc",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x20",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "MISC_RETIRED.LBR_INSERTS",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Increments whenever there is an update to the LBR array."
+ },
+ {
+ "PublicDescription": "Counts number of retired PAUSE instructions (that do not end up with a VMExit to the VMM; TSX aborted Instructions may be counted).",
+ "EventCode": "0xcc",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x40",
+ "EventName": "MISC_RETIRED.PAUSE_INST",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of retired PAUSE instructions."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times the front-end is resteered when it finds a branch instruction in a fetch line. This occurs for the first time a branch instruction is fetched or when the branch is not tracked by the BPU (Branch Prediction Unit) anymore.",
+ "EventCode": "0xE6",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "BACLEARS.ANY",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Counts the total number when the front end is resteered, mainly when the BPU cannot provide a correct prediction and this is corrected by other branch handling mechanisms at the front end."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "This event distributes cycle counts between active hyperthreads, i.e., those in C0. A hyperthread becomes inactive when it executes the HLT or MWAIT instructions. If all other hyperthreads are inactive (or disabled or do not exist), all counts are attributed to this hyperthread. To obtain the full count when the Core is active, sum the counts from each hyperthread.",
+ "EventCode": "0xec",
+ "Counter": "0,1,2,3,4,5,6,7",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3,4,5,6,7",
+ "EventName": "CPU_CLK_UNHALTED.DISTRIBUTED",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Cycle counts are evenly distributed between active threads in the Core."
+ }
+] \ No newline at end of file
diff --git a/tools/perf/pmu-events/arch/x86/icelake/virtual-memory.json b/tools/perf/pmu-events/arch/x86/icelake/virtual-memory.json
new file mode 100644
index 000000000000..7180a900c175
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/icelake/virtual-memory.json
@@ -0,0 +1,236 @@
+[
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts page walks completed due to demand data loads whose address translations missed in the TLB and were mapped to 4K pages. The page walks can end with or without a page fault.",
+ "EventCode": "0x08",
+ "Counter": "0,1,2,3",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "DTLB_LOAD_MISSES.WALK_COMPLETED_4K",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Page walks completed due to a demand data load to a 4K page."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts page walks completed due to demand data loads whose address translations missed in the TLB and were mapped to 2M/4M pages. The page walks can end with or without a page fault.",
+ "EventCode": "0x08",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "DTLB_LOAD_MISSES.WALK_COMPLETED_2M_4M",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Page walks completed due to a demand data load to a 2M/4M page."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts demand data loads that caused a completed page walk of any page size (4K/2M/4M/1G). This implies it missed in all TLB levels. The page walk can end with or without a fault.",
+ "EventCode": "0x08",
+ "Counter": "0,1,2,3",
+ "UMask": "0xe",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "DTLB_LOAD_MISSES.WALK_COMPLETED",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Load miss in all TLB levels causes a page walk that completes. (All page sizes)"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of page walks outstanding for a demand load in the PMH (Page Miss Handler) each cycle.",
+ "EventCode": "0x08",
+ "Counter": "0,1,2,3",
+ "UMask": "0x10",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "DTLB_LOAD_MISSES.WALK_PENDING",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of page walks outstanding for a demand load in the PMH each cycle."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles when at least one PMH (Page Miss Handler) is busy with a page walk for a demand load.",
+ "EventCode": "0x08",
+ "Counter": "0,1,2,3",
+ "UMask": "0x10",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "DTLB_LOAD_MISSES.WALK_ACTIVE",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Cycles when at least one PMH is busy with a page walk for a demand load.",
+ "CounterMask": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts loads that miss the DTLB (Data TLB) and hit the STLB (Second level TLB).",
+ "EventCode": "0x08",
+ "Counter": "0,1,2,3",
+ "UMask": "0x20",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "DTLB_LOAD_MISSES.STLB_HIT",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Loads that miss the DTLB and hit the STLB."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts page walks completed due to demand data stores whose address translations missed in the TLB and were mapped to 4K pages. The page walks can end with or without a page fault.",
+ "EventCode": "0x49",
+ "Counter": "0,1,2,3",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "DTLB_STORE_MISSES.WALK_COMPLETED_4K",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Page walks completed due to a demand data store to a 4K page."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts page walks completed due to demand data stores whose address translations missed in the TLB and were mapped to 2M/4M pages. The page walks can end with or without a page fault.",
+ "EventCode": "0x49",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "DTLB_STORE_MISSES.WALK_COMPLETED_2M_4M",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Page walks completed due to a demand data store to a 2M/4M page."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts demand data stores that caused a completed page walk of any page size (4K/2M/4M/1G). This implies it missed in all TLB levels. The page walk can end with or without a fault.",
+ "EventCode": "0x49",
+ "Counter": "0,1,2,3",
+ "UMask": "0xe",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "DTLB_STORE_MISSES.WALK_COMPLETED",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Store misses in all TLB levels causes a page walk that completes. (All page sizes)"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of page walks outstanding for a store in the PMH (Page Miss Handler) each cycle.",
+ "EventCode": "0x49",
+ "Counter": "0,1,2,3",
+ "UMask": "0x10",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "DTLB_STORE_MISSES.WALK_PENDING",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Number of page walks outstanding for a store in the PMH each cycle."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles when at least one PMH (Page Miss Handler) is busy with a page walk for a store.",
+ "EventCode": "0x49",
+ "Counter": "0,1,2,3",
+ "UMask": "0x10",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "DTLB_STORE_MISSES.WALK_ACTIVE",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Cycles when at least one PMH is busy with a page walk for a store.",
+ "CounterMask": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts stores that miss the DTLB (Data TLB) and hit the STLB (2nd Level TLB).",
+ "EventCode": "0x49",
+ "Counter": "0,1,2,3",
+ "UMask": "0x20",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "DTLB_STORE_MISSES.STLB_HIT",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Stores that miss the DTLB and hit the STLB."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts completed page walks (4K page size) caused by a code fetch. This implies it missed in the ITLB and further levels of TLB. The page walk can end with or without a fault.",
+ "EventCode": "0x85",
+ "Counter": "0,1,2,3",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "ITLB_MISSES.WALK_COMPLETED_4K",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Code miss in all TLB levels causes a page walk that completes. (4K)"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts code misses in all ITLB (Instruction TLB) levels that caused a completed page walk (2M and 4M page sizes). The page walk can end with or without a fault.",
+ "EventCode": "0x85",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "ITLB_MISSES.WALK_COMPLETED_2M_4M",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Code miss in all TLB levels causes a page walk that completes. (2M/4M)"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts completed page walks (2M and 4M page sizes) caused by a code fetch. This implies it missed in the ITLB (Instruction TLB) and further levels of TLB. The page walk can end with or without a fault.",
+ "EventCode": "0x85",
+ "Counter": "0,1,2,3",
+ "UMask": "0xe",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "ITLB_MISSES.WALK_COMPLETED",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Code miss in all TLB levels causes a page walk that completes. (All page sizes)"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of page walks outstanding for an outstanding code (instruction fetch) request in the PMH (Page Miss Handler) each cycle.",
+ "EventCode": "0x85",
+ "Counter": "0,1,2,3",
+ "UMask": "0x10",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "ITLB_MISSES.WALK_PENDING",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Number of page walks outstanding for an outstanding code request in the PMH each cycle."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cycles when at least one PMH (Page Miss Handler) is busy with a page walk for a code (instruction fetch) request.",
+ "EventCode": "0x85",
+ "Counter": "0,1,2,3",
+ "UMask": "0x10",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "ITLB_MISSES.WALK_ACTIVE",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Cycles when at least one PMH is busy with a page walk for code (instruction fetch) request.",
+ "CounterMask": "1"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts instruction fetch requests that miss the ITLB (Instruction TLB) and hit the STLB (Second-level TLB).",
+ "EventCode": "0x85",
+ "Counter": "0,1,2,3",
+ "UMask": "0x20",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "ITLB_MISSES.STLB_HIT",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Instruction fetch requests that miss the ITLB and hit the STLB."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of flushes of the big or small ITLB pages. Counting include both TLB Flush (covering all sets) and TLB Set Clear (set-specific).",
+ "EventCode": "0xAE",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "ITLB.ITLB_FLUSH",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "Flushing of the Instruction TLB (ITLB) pages, includes 4k/2M/4M pages."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of DTLB flush attempts of the thread-specific entries.",
+ "EventCode": "0xBD",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "TLB_FLUSH.DTLB_THREAD",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "DTLB flush attempts of the thread-specific entries"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of any STLB flush attempts (such as entire, VPID, PCID, InvPage, CR3 write, etc.).",
+ "EventCode": "0xBD",
+ "Counter": "0,1,2,3",
+ "UMask": "0x20",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "TLB_FLUSH.STLB_ANY",
+ "SampleAfterValue": "100007",
+ "BriefDescription": "STLB flush attempts"
+ }
+] \ No newline at end of file
diff --git a/tools/perf/pmu-events/arch/x86/mapfile.csv b/tools/perf/pmu-events/arch/x86/mapfile.csv
index d6984a3017e0..745ced083844 100644
--- a/tools/perf/pmu-events/arch/x86/mapfile.csv
+++ b/tools/perf/pmu-events/arch/x86/mapfile.csv
@@ -33,4 +33,7 @@ GenuineIntel-6-25,v2,westmereep-sp,core
GenuineIntel-6-2F,v2,westmereex,core
GenuineIntel-6-55-[01234],v1,skylakex,core
GenuineIntel-6-55-[56789ABCDEF],v1,cascadelakex,core
+GenuineIntel-6-7D,v1,icelake,core
+GenuineIntel-6-7E,v1,icelake,core
+GenuineIntel-6-86,v1,tremontx,core
AuthenticAMD-23-[[:xdigit:]]+,v1,amdfam17h,core
diff --git a/tools/perf/pmu-events/arch/x86/skylakex/skx-metrics.json b/tools/perf/pmu-events/arch/x86/skylakex/skx-metrics.json
index 56e03ba771f4..35b255fa6a79 100644
--- a/tools/perf/pmu-events/arch/x86/skylakex/skx-metrics.json
+++ b/tools/perf/pmu-events/arch/x86/skylakex/skx-metrics.json
@@ -314,36 +314,18 @@
"MetricName": "DRAM_BW_Use"
},
{
- "MetricExpr": "1000000000 * ( cha@event\\=0x36\\\\\\,umask\\=0x21@ / cha@event\\=0x35\\\\\\,umask\\=0x21@ ) / ( cha_0@event\\=0x0@ / duration_time )",
+ "MetricExpr": "1000000000 * ( cha@event\\=0x36\\\\\\,umask\\=0x21\\\\\\,config\\=0x40433@ / cha@event\\=0x35\\\\\\,umask\\=0x21\\\\\\,config\\=0x40433@ ) / ( cha_0@event\\=0x0@ / duration_time )",
"BriefDescription": "Average latency of data read request to external memory (in nanoseconds). Accounts for demand loads and L1/L2 prefetches",
"MetricGroup": "Memory_Lat",
"MetricName": "DRAM_Read_Latency"
},
{
- "MetricExpr": "cha@event\\=0x36\\\\\\,umask\\=0x21@ / cha@event\\=0x36\\\\\\,umask\\=0x21\\\\\\,thresh\\=1@",
+ "MetricExpr": "cha@event\\=0x36\\\\\\,umask\\=0x21\\\\\\,config\\=0x40433@ / cha@event\\=0x36\\\\\\,umask\\=0x21\\\\\\,thresh\\=1\\\\\\,config\\=0x40433@",
"BriefDescription": "Average number of parallel data read requests to external memory. Accounts for demand loads and L1/L2 prefetches",
"MetricGroup": "Memory_BW",
"MetricName": "DRAM_Parallel_Reads"
},
{
- "MetricExpr": "( 1000000000 * ( imc@event\\=0xe0\\\\\\,umask\\=0x1@ / imc@event\\=0xe3@ ) / imc_0@event\\=0x0@ ) if 1 if 0 == 1 else 0 else 0",
- "BriefDescription": "Average latency of data read request to external 3D X-Point memory [in nanoseconds]. Accounts for demand loads and L1/L2 data-read prefetches",
- "MetricGroup": "Memory_Lat",
- "MetricName": "MEM_PMM_Read_Latency"
- },
- {
- "MetricExpr": "( ( 64 * imc@event\\=0xe3@ / 1000000000 ) / duration_time ) if 1 if 0 == 1 else 0 else 0",
- "BriefDescription": "Average 3DXP Memory Bandwidth Use for reads [GB / sec]",
- "MetricGroup": "Memory_BW",
- "MetricName": "PMM_Read_BW"
- },
- {
- "MetricExpr": "( ( 64 * imc@event\\=0xe7@ / 1000000000 ) / duration_time ) if 1 if 0 == 1 else 0 else 0",
- "BriefDescription": "Average 3DXP Memory Bandwidth Use for Writes [GB / sec]",
- "MetricGroup": "Memory_BW",
- "MetricName": "PMM_Write_BW"
- },
- {
"MetricExpr": "cha_0@event\\=0x0@",
"BriefDescription": "Socket actual clocks when any core is active on that socket",
"MetricGroup": "",
diff --git a/tools/perf/pmu-events/arch/x86/tremontx/cache.json b/tools/perf/pmu-events/arch/x86/tremontx/cache.json
new file mode 100644
index 000000000000..f88040171b4d
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/tremontx/cache.json
@@ -0,0 +1,111 @@
+[
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cacheable memory requests that miss in the the Last Level Cache. Requests include Demand Loads, Reads for Ownership(RFO), Instruction fetches and L1 HW prefetches. If the platform has an L3 cache, last level cache is the L3, otherwise it is the L2.",
+ "EventCode": "0x2e",
+ "Counter": "0,1,2,3",
+ "UMask": "0x41",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "LONGEST_LAT_CACHE.MISS",
+ "PDIR_COUNTER": "na",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Counts memory requests originating from the core that miss in the last level cache. If the platform has an L3 cache, last level cache is the L3, otherwise it is the L2."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts cacheable memory requests that access the Last Level Cache. Requests include Demand Loads, Reads for Ownership(RFO), Instruction fetches and L1 HW prefetches. If the platform has an L3 cache, last level cache is the L3, otherwise it is the L2.",
+ "EventCode": "0x2e",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4f",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "LONGEST_LAT_CACHE.REFERENCE",
+ "PDIR_COUNTER": "na",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Counts memory requests originating from the core that reference a cache line in the last level cache. If the platform has an L3 cache, last level cache is the L3, otherwise it is the L2."
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of load uops retired. This event is Precise Event capable",
+ "EventCode": "0xd0",
+ "Counter": "0,1,2,3",
+ "UMask": "0x81",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_UOPS_RETIRED.ALL_LOADS",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Counts the number of load uops retired.",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of store uops retired. This event is Precise Event capable",
+ "EventCode": "0xd0",
+ "Counter": "0,1,2,3",
+ "UMask": "0x82",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_UOPS_RETIRED.ALL_STORES",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Counts the number of store uops retired.",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "EventCode": "0xd1",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_LOAD_UOPS_RETIRED.L1_HIT",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Counts the number of load uops retired that hit the level 1 data cache",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "EventCode": "0xd1",
+ "Counter": "0,1,2,3",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_LOAD_UOPS_RETIRED.L2_HIT",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Counts the number of load uops retired that hit in the level 2 cache",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "EventCode": "0xd1",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_LOAD_UOPS_RETIRED.L3_HIT",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Counts the number of load uops retired that miss in the level 3 cache"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "EventCode": "0xd1",
+ "Counter": "0,1,2,3",
+ "UMask": "0x8",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_LOAD_UOPS_RETIRED.L1_MISS",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Counts the number of load uops retired that miss in the level 1 data cache",
+ "Data_LA": "1"
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "EventCode": "0xd1",
+ "Counter": "0,1,2,3",
+ "UMask": "0x10",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MEM_LOAD_UOPS_RETIRED.L2_MISS",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Counts the number of load uops retired that miss in the level 2 cache",
+ "Data_LA": "1"
+ }
+] \ No newline at end of file
diff --git a/tools/perf/pmu-events/arch/x86/tremontx/frontend.json b/tools/perf/pmu-events/arch/x86/tremontx/frontend.json
new file mode 100644
index 000000000000..73b0a1ed5756
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/tremontx/frontend.json
@@ -0,0 +1,26 @@
+[
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts requests to the Instruction Cache (ICache) for one or more bytes in an ICache Line and that cache line is not in the ICache (miss). The event strives to count on a cache line basis, so that multiple accesses which miss in a single cache line count as one ICACHE.MISS. Specifically, the event counts when straight line code crosses the cache line boundary, or when a branch target is to a new line, and that cache line is not in the ICache.",
+ "EventCode": "0x80",
+ "Counter": "0,1,2,3",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "ICACHE.MISSES",
+ "PDIR_COUNTER": "na",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Counts requests to the Instruction Cache (ICache) for one or more bytes in a cache line and they do not hit in the ICache (miss)."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts requests to the Instruction Cache (ICache) for one or more bytes in an ICache Line. The event strives to count on a cache line basis, so that multiple fetches to a single cache line count as one ICACHE.ACCESS. Specifically, the event counts when accesses from straight line code crosses the cache line boundary, or when a branch target is to a new line.",
+ "EventCode": "0x80",
+ "Counter": "0,1,2,3",
+ "UMask": "0x3",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "ICACHE.ACCESSES",
+ "PDIR_COUNTER": "na",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Counts requests to the Instruction Cache (ICache) for one or more bytes cache Line."
+ }
+] \ No newline at end of file
diff --git a/tools/perf/pmu-events/arch/x86/tremontx/memory.json b/tools/perf/pmu-events/arch/x86/tremontx/memory.json
new file mode 100644
index 000000000000..65469e84f35b
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/tremontx/memory.json
@@ -0,0 +1,26 @@
+[
+ {
+ "PublicDescription": "Offcore response can be programmed only with a specific pair of event select and counter MSR, and with specific event codes and predefine mask bit value in a dedicated MSR to specify attributes of the offcore transaction.",
+ "EventCode": "0XB7",
+ "MSRValue": "0x000000003F04000001",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "EventName": "OCR.DEMAND_DATA_RD.L3_MISS",
+ "MSRIndex": "0x1a6,0x1a7",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Counts demand data reads that was not supplied by the L3 cache.",
+ "Offcore": "1"
+ },
+ {
+ "PublicDescription": "Offcore response can be programmed only with a specific pair of event select and counter MSR, and with specific event codes and predefine mask bit value in a dedicated MSR to specify attributes of the offcore transaction.",
+ "EventCode": "0XB7",
+ "MSRValue": "0x000000003F04000002",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "EventName": "OCR.DEMAND_RFO.L3_MISS",
+ "MSRIndex": "0x1a6,0x1a7",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Counts all demand reads for ownership (RFO) requests and software based prefetches for exclusive ownership (PREFETCHW) that was not supplied by the L3 cache.",
+ "Offcore": "1"
+ }
+] \ No newline at end of file
diff --git a/tools/perf/pmu-events/arch/x86/tremontx/other.json b/tools/perf/pmu-events/arch/x86/tremontx/other.json
new file mode 100644
index 000000000000..85bf3c8f3914
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/tremontx/other.json
@@ -0,0 +1,26 @@
+[
+ {
+ "PublicDescription": "Offcore response can be programmed only with a specific pair of event select and counter MSR, and with specific event codes and predefine mask bit value in a dedicated MSR to specify attributes of the offcore transaction.",
+ "EventCode": "0XB7",
+ "MSRValue": "0x000000000000010001",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "EventName": "OCR.DEMAND_DATA_RD.ANY_RESPONSE",
+ "MSRIndex": "0x1a6,0x1a7",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Counts demand data reads that have any response type.",
+ "Offcore": "1"
+ },
+ {
+ "PublicDescription": "Offcore response can be programmed only with a specific pair of event select and counter MSR, and with specific event codes and predefine mask bit value in a dedicated MSR to specify attributes of the offcore transaction.",
+ "EventCode": "0XB7",
+ "MSRValue": "0x000000000000010002",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "EventName": "OCR.DEMAND_RFO.ANY_RESPONSE",
+ "MSRIndex": "0x1a6,0x1a7",
+ "SampleAfterValue": "100003",
+ "BriefDescription": "Counts all demand reads for ownership (RFO) requests and software based prefetches for exclusive ownership (PREFETCHW) that have any response type.",
+ "Offcore": "1"
+ }
+] \ No newline at end of file
diff --git a/tools/perf/pmu-events/arch/x86/tremontx/pipeline.json b/tools/perf/pmu-events/arch/x86/tremontx/pipeline.json
new file mode 100644
index 000000000000..05a8f6a7d9c0
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/tremontx/pipeline.json
@@ -0,0 +1,111 @@
+[
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of instructions that retire. For instructions that consist of multiple uops, this event counts the retirement of the last uop of the instruction. The counter continues counting during hardware interrupts, traps, and inside interrupt handlers. This event uses fixed counter 0.",
+ "Counter": "32",
+ "UMask": "0x1",
+ "PEBScounters": "32",
+ "EventName": "INST_RETIRED.ANY",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Counts the number of instructions retired. (Fixed event)"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of core cycles while the core is not in a halt state. The core enters the halt state when it is running the HLT instruction. The core frequency may change from time to time. For this reason this event may have a changing ratio with regards to time. This event uses fixed counter 1.",
+ "Counter": "33",
+ "UMask": "0x2",
+ "PEBScounters": "33",
+ "EventName": "CPU_CLK_UNHALTED.CORE",
+ "PDIR_COUNTER": "na",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Counts the number of unhalted core clock cycles. (Fixed event)"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of reference cycles that the core is not in a halt state. The core enters the halt state when it is running the HLT instruction. The core frequency may change from time. This event is not affected by core frequency changes and at a fixed frequency. This event uses fixed counter 2.",
+ "Counter": "34",
+ "UMask": "0x3",
+ "PEBScounters": "34",
+ "EventName": "CPU_CLK_UNHALTED.REF_TSC",
+ "PDIR_COUNTER": "na",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Counts the number of unhalted reference clock cycles at TSC frequency. (Fixed event)"
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of core cycles while the core is not in a halt state. The core enters the halt state when it is running the HLT instruction. The core frequency may change from time to time. For this reason this event may have a changing ratio with regards to time. This event uses a programmable general purpose performance counter.",
+ "EventCode": "0x3c",
+ "Counter": "0,1,2,3",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "CPU_CLK_UNHALTED.CORE_P",
+ "PDIR_COUNTER": "na",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Counts the number of unhalted core clock cycles."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts reference cycles (at TSC frequency) when core is not halted. This event uses a programmable general purpose perfmon counter.",
+ "EventCode": "0x3c",
+ "Counter": "0,1,2,3",
+ "UMask": "0x1",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "CPU_CLK_UNHALTED.REF",
+ "PDIR_COUNTER": "na",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Counts the number of unhalted reference clock cycles at TSC frequency."
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of instructions that retire execution. For instructions that consist of multiple uops, this event counts the retirement of the last uop of the instruction. The event continues counting during hardware interrupts, traps, and inside interrupt handlers. This is an architectural performance event. This event uses a Programmable general purpose perfmon counter. *This event is Precise Event capable: The EventingRIP field in the PEBS record is precise to the address of the instruction which caused the event.",
+ "EventCode": "0xc0",
+ "Counter": "0,1,2,3",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "INST_RETIRED.ANY_P",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Counts the number of instructions retired."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "EventCode": "0xc3",
+ "Counter": "0,1,2,3",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "MACHINE_CLEARS.ANY",
+ "PDIR_COUNTER": "na",
+ "SampleAfterValue": "20003",
+ "BriefDescription": "Counts all machine clears due to, but not limited to memory ordering, memory disambiguation, SMC, page faults and FP assist."
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts branch instructions retired for all branch types. This event is Precise Event capable. This is an architectural event.",
+ "EventCode": "0xc4",
+ "Counter": "0,1,2,3",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "BR_INST_RETIRED.ALL_BRANCHES",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Counts the number of branch instructions retired for all branch types."
+ },
+ {
+ "PEBS": "1",
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts mispredicted branch instructions retired for all branch types. This event is Precise Event capable. This is an architectural event.",
+ "EventCode": "0xc5",
+ "Counter": "0,1,2,3",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "BR_MISP_RETIRED.ALL_BRANCHES",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Counts the number of mispredicted branch instructions retired."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "EventCode": "0xcd",
+ "Counter": "0,1,2,3",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "CYCLES_DIV_BUSY.ANY",
+ "PDIR_COUNTER": "na",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Counts cycles the floating point divider or integer divider or both are busy. Does not imply a stall waiting for either divider."
+ }
+] \ No newline at end of file
diff --git a/tools/perf/pmu-events/arch/x86/tremontx/uncore-memory.json b/tools/perf/pmu-events/arch/x86/tremontx/uncore-memory.json
new file mode 100644
index 000000000000..15376f2cf052
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/tremontx/uncore-memory.json
@@ -0,0 +1,73 @@
+[
+ {
+ "BriefDescription": "read requests to memory controller. Derived from unc_m_cas_count.rd",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x04",
+ "EventName": "LLC_MISSES.MEM_READ",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x0f",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "write requests to memory controller. Derived from unc_m_cas_count.wr",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x04",
+ "EventName": "LLC_MISSES.MEM_WRITE",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0x30",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Memory controller clock ticks",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventName": "UNC_M_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Pre-charge for reads",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x02",
+ "EventName": "UNC_M_PRE_COUNT.RD",
+ "PerPkg": "1",
+ "UMask": "0x04",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Pre-charge for writes",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x02",
+ "EventName": "UNC_M_PRE_COUNT.WR",
+ "PerPkg": "1",
+ "UMask": "0x08",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "Precharge due to read on page miss, write on page miss or PGT",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x02",
+ "EventName": "UNC_M_PRE_COUNT.ALL",
+ "PerPkg": "1",
+ "UMask": "0x1c",
+ "Unit": "iMC"
+ },
+ {
+ "BriefDescription": "DRAM Precharge commands. : Precharge due to page table",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x02",
+ "EventName": "UNC_M_PRE_COUNT.PGT",
+ "PerPkg": "1",
+ "PublicDescription": "DRAM Precharge commands. : Precharge due to page table : Counts the number of DRAM Precharge commands sent on this channel.",
+ "UMask": "0x10",
+ "Unit": "iMC"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/tremontx/uncore-other.json b/tools/perf/pmu-events/arch/x86/tremontx/uncore-other.json
new file mode 100644
index 000000000000..6deff1fe89e3
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/tremontx/uncore-other.json
@@ -0,0 +1,431 @@
+[
+ {
+ "BriefDescription": "Uncore cache clock ticks",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventName": "UNC_CHA_CLOCKTICKS",
+ "PerPkg": "1",
+ "Unit": "CHA"
+ },
+ {
+ "BriefDescription": "LLC misses - Uncacheable reads (from cpu) . Derived from unc_cha_tor_inserts.ia_miss",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.UNCACHEABLE",
+ "Filter": "config1=0x40e33",
+ "PerPkg": "1",
+ "UMask": "0xC001FE01",
+ "UMaskExt": "0xC001FE",
+ "Unit": "CHA"
+ },
+ {
+ "BriefDescription": "MMIO reads. Derived from unc_cha_tor_inserts.ia_miss",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.MMIO_READ",
+ "Filter": "config1=0x40040e33",
+ "PerPkg": "1",
+ "UMask": "0xC001FE01",
+ "UMaskExt": "0xC001FE",
+ "Unit": "CHA"
+ },
+ {
+ "BriefDescription": "MMIO writes. Derived from unc_cha_tor_inserts.ia_miss",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x35",
+ "EventName": "LLC_MISSES.MMIO_WRITE",
+ "Filter": "config1=0x40041e33",
+ "PerPkg": "1",
+ "UMask": "0xC001FE01",
+ "UMaskExt": "0xC001FE",
+ "Unit": "CHA"
+ },
+ {
+ "BriefDescription": "Streaming stores (full cache line). Derived from unc_cha_tor_inserts.ia_miss",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.STREAMING_FULL",
+ "Filter": "config1=0x41833",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0xC001FE01",
+ "UMaskExt": "0xC001FE",
+ "Unit": "CHA"
+ },
+ {
+ "BriefDescription": "Streaming stores (partial cache line). Derived from unc_cha_tor_inserts.ia_miss",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x35",
+ "EventName": "LLC_REFERENCES.STREAMING_PARTIAL",
+ "Filter": "config1=0x41a33",
+ "PerPkg": "1",
+ "ScaleUnit": "64Bytes",
+ "UMask": "0xC001FE01",
+ "UMaskExt": "0xC001FE",
+ "Unit": "CHA"
+ },
+ {
+ "BriefDescription": "PCI Express bandwidth reading at IIO. Derived from unc_iio_data_req_of_cpu.mem_read.part0",
+ "Counter": "0,1",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x83",
+ "EventName": "LLC_MISSES.PCIE_READ",
+ "FCMask": "0x07",
+ "Filter": "ch_mask=0x1f",
+ "MetricExpr": "UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART0 +UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART1 +UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART2 +UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART3",
+ "MetricName": "LLC_MISSES.PCIE_READ",
+ "PerPkg": "1",
+ "PortMask": "0x01",
+ "ScaleUnit": "4Bytes",
+ "UMask": "0x04",
+ "Unit": "IIO"
+ },
+ {
+ "BriefDescription": "PCI Express bandwidth writing at IIO. Derived from unc_iio_data_req_of_cpu.mem_write.part0",
+ "Counter": "0,1",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x83",
+ "EventName": "LLC_MISSES.PCIE_WRITE",
+ "FCMask": "0x07",
+ "Filter": "ch_mask=0x1f",
+ "MetricExpr": "UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART0 +UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART1 +UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART2 +UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART3",
+ "MetricName": "LLC_MISSES.PCIE_WRITE",
+ "PerPkg": "1",
+ "PortMask": "0x01",
+ "ScaleUnit": "4Bytes",
+ "UMask": "0x01",
+ "Unit": "IIO"
+ },
+ {
+ "BriefDescription": "PCI Express bandwidth writing at IIO, part 1",
+ "Counter": "0,1",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x83",
+ "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART1",
+ "FCMask": "0x07",
+ "PerPkg": "1",
+ "PortMask": "0x02",
+ "ScaleUnit": "4Bytes",
+ "UMask": "0x01",
+ "Unit": "IIO"
+ },
+ {
+ "BriefDescription": "PCI Express bandwidth writing at IIO, part 2",
+ "Counter": "0,1",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x83",
+ "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART2",
+ "FCMask": "0x07",
+ "PerPkg": "1",
+ "PortMask": "0x04",
+ "ScaleUnit": "4Bytes",
+ "UMask": "0x01",
+ "Unit": "IIO"
+ },
+ {
+ "BriefDescription": "PCI Express bandwidth writing at IIO, part 3",
+ "Counter": "0,1",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x83",
+ "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART3",
+ "FCMask": "0x07",
+ "PerPkg": "1",
+ "PortMask": "0x08",
+ "ScaleUnit": "4Bytes",
+ "UMask": "0x01",
+ "Unit": "IIO"
+ },
+ {
+ "BriefDescription": "PCI Express bandwidth reading at IIO, part 1",
+ "Counter": "0,1",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x83",
+ "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART1",
+ "FCMask": "0x07",
+ "PerPkg": "1",
+ "PortMask": "0x02",
+ "ScaleUnit": "4Bytes",
+ "UMask": "0x04",
+ "Unit": "IIO"
+ },
+ {
+ "BriefDescription": "PCI Express bandwidth reading at IIO, part 2",
+ "Counter": "0,1",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x83",
+ "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART2",
+ "FCMask": "0x07",
+ "PerPkg": "1",
+ "PortMask": "0x04",
+ "ScaleUnit": "4Bytes",
+ "UMask": "0x04",
+ "Unit": "IIO"
+ },
+ {
+ "BriefDescription": "PCI Express bandwidth reading at IIO, part 3",
+ "Counter": "0,1",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x83",
+ "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART3",
+ "FCMask": "0x07",
+ "PerPkg": "1",
+ "PortMask": "0x08",
+ "ScaleUnit": "4Bytes",
+ "UMask": "0x04",
+ "Unit": "IIO"
+ },
+ {
+ "BriefDescription": "TOR Inserts; CRd misses from local IA",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x35",
+ "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_CRD",
+ "PerPkg": "1",
+ "PublicDescription": "TOR Inserts; Code read from local IA that misses in the snoop filter",
+ "UMask": "0xC80FFE01",
+ "UMaskExt": "0xC80FFE",
+ "Unit": "CHA"
+ },
+ {
+ "BriefDescription": "TOR Inserts; CRd Pref misses from local IA",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x35",
+ "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_CRD_PREF",
+ "PerPkg": "1",
+ "PublicDescription": "TOR Inserts; Code read prefetch from local IA that misses in the snoop filter",
+ "UMask": "0xC88FFE01",
+ "UMaskExt": "0xC88FFE",
+ "Unit": "CHA"
+ },
+ {
+ "BriefDescription": "TOR Inserts; DRd Opt misses from local IA",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x35",
+ "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT",
+ "PerPkg": "1",
+ "PublicDescription": "TOR Inserts; Data read opt from local IA that misses in the snoop filter",
+ "UMask": "0xC827FE01",
+ "UMaskExt": "0xC827FE",
+ "Unit": "CHA"
+ },
+ {
+ "BriefDescription": "TOR Inserts; DRd Opt Pref misses from local IA",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x35",
+ "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_DRD_OPT_PREF",
+ "PerPkg": "1",
+ "PublicDescription": "TOR Inserts; Data read opt prefetch from local IA that misses in the snoop filter",
+ "UMask": "0xC8A7FE01",
+ "UMaskExt": "0xC8A7FE",
+ "Unit": "CHA"
+ },
+ {
+ "BriefDescription": "TOR Inserts; RFO misses from local IA",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x35",
+ "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_RFO",
+ "PerPkg": "1",
+ "PublicDescription": "TOR Inserts; Read for ownership from local IA that misses in the snoop filter",
+ "UMask": "0xC807FE01",
+ "UMaskExt": "0xC807FE",
+ "Unit": "CHA"
+ },
+ {
+ "BriefDescription": "TOR Inserts; RFO pref misses from local IA",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x35",
+ "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_RFO_PREF",
+ "PerPkg": "1",
+ "PublicDescription": "TOR Inserts; Read for ownership prefetch from local IA that misses in the snoop filter",
+ "UMask": "0xC887FE01",
+ "UMaskExt": "0xC887FE",
+ "Unit": "CHA"
+ },
+ {
+ "BriefDescription": "TOR Inserts; WCiL misses from local IA",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x35",
+ "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_WCIL",
+ "PerPkg": "1",
+ "PublicDescription": "TOR Inserts; Data read from local IA that misses in the snoop filter",
+ "UMask": "0xC86FFE01",
+ "UMaskExt": "0xC86FFE",
+ "Unit": "CHA"
+ },
+ {
+ "BriefDescription": "TOR Inserts; WCiLF misses from local IA",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x35",
+ "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_WCILF",
+ "PerPkg": "1",
+ "PublicDescription": "TOR Inserts; Data read from local IA that misses in the snoop filter",
+ "UMask": "0xC867FE01",
+ "UMaskExt": "0xC867FE",
+ "Unit": "CHA"
+ },
+ {
+ "BriefDescription": "Clockticks of the integrated IO (IIO) traffic controller",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x01",
+ "EventName": "UNC_IIO_CLOCKTICKS",
+ "PerPkg": "1",
+ "PublicDescription": "Clockticks of the integrated IO (IIO) traffic controller",
+ "Unit": "IIO"
+ },
+ {
+ "BriefDescription": "Data requested of the CPU : Card reading from DRAM",
+ "Counter": "0,1",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x83",
+ "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART4",
+ "FCMask": "0x07",
+ "PerPkg": "1",
+ "PortMask": "0x10",
+ "PublicDescription": "Data requested of the CPU : Card reading from DRAM : Number of DWs (4 bytes) the card requests of the main die. Includes all requests initiated by the Card, including reads and writes. : x16 card plugged in to stack, Or x8 card plugged in to Lane 0/1, Or x4 card is plugged in to slot 0",
+ "UMask": "0x04",
+ "Unit": "IIO"
+ },
+ {
+ "BriefDescription": "Data requested of the CPU : Card reading from DRAM",
+ "Counter": "0,1",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x83",
+ "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART5",
+ "FCMask": "0x07",
+ "PerPkg": "1",
+ "PortMask": "0x20",
+ "PublicDescription": "Data requested of the CPU : Card reading from DRAM : Number of DWs (4 bytes) the card requests of the main die. Includes all requests initiated by the Card, including reads and writes. : x4 card is plugged in to slot 1",
+ "UMask": "0x04",
+ "Unit": "IIO"
+ },
+ {
+ "BriefDescription": "Data requested of the CPU : Card reading from DRAM",
+ "Counter": "0,1",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x83",
+ "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART6",
+ "FCMask": "0x07",
+ "PerPkg": "1",
+ "PortMask": "0x40",
+ "PublicDescription": "Data requested of the CPU : Card reading from DRAM : Number of DWs (4 bytes) the card requests of the main die. Includes all requests initiated by the Card, including reads and writes. : x8 card plugged in to Lane 2/3, Or x4 card is plugged in to slot 1",
+ "UMask": "0x04",
+ "Unit": "IIO"
+ },
+ {
+ "BriefDescription": "Data requested of the CPU : Card reading from DRAM",
+ "Counter": "0,1",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x83",
+ "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART7",
+ "FCMask": "0x07",
+ "PerPkg": "1",
+ "PortMask": "0x80",
+ "PublicDescription": "Data requested of the CPU : Card reading from DRAM : Number of DWs (4 bytes) the card requests of the main die. Includes all requests initiated by the Card, including reads and writes. : x4 card is plugged in to slot 3",
+ "UMask": "0x04",
+ "Unit": "IIO"
+ },
+ {
+ "BriefDescription": "Data requested of the CPU : Card writing to DRAM",
+ "Counter": "0,1",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x83",
+ "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART4",
+ "FCMask": "0x07",
+ "PerPkg": "1",
+ "PortMask": "0x10",
+ "PublicDescription": "Data requested of the CPU : Card writing to DRAM : Number of DWs (4 bytes) the card requests of the main die. Includes all requests initiated by the Card, including reads and writes. : x16 card plugged in to stack, Or x8 card plugged in to Lane 0/1, Or x4 card is plugged in to slot 0",
+ "UMask": "0x01",
+ "Unit": "IIO"
+ },
+ {
+ "BriefDescription": "Data requested of the CPU : Card writing to DRAM",
+ "Counter": "0,1",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x83",
+ "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART5",
+ "FCMask": "0x07",
+ "PerPkg": "1",
+ "PortMask": "0x20",
+ "PublicDescription": "Data requested of the CPU : Card writing to DRAM : Number of DWs (4 bytes) the card requests of the main die. Includes all requests initiated by the Card, including reads and writes. : x4 card is plugged in to slot 1",
+ "UMask": "0x01",
+ "Unit": "IIO"
+ },
+ {
+ "BriefDescription": "Data requested of the CPU : Card writing to DRAM",
+ "Counter": "0,1",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x83",
+ "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART6",
+ "FCMask": "0x07",
+ "PerPkg": "1",
+ "PortMask": "0x40",
+ "PublicDescription": "Data requested of the CPU : Card writing to DRAM : Number of DWs (4 bytes) the card requests of the main die. Includes all requests initiated by the Card, including reads and writes. : x8 card plugged in to Lane 2/3, Or x4 card is plugged in to slot 1",
+ "UMask": "0x01",
+ "Unit": "IIO"
+ },
+ {
+ "BriefDescription": "Data requested of the CPU : Card writing to DRAM",
+ "Counter": "0,1",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x83",
+ "EventName": "UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART7",
+ "FCMask": "0x07",
+ "PerPkg": "1",
+ "PortMask": "0x80",
+ "PublicDescription": "Data requested of the CPU : Card writing to DRAM : Number of DWs (4 bytes) the card requests of the main die. Includes all requests initiated by the Card, including reads and writes. : x4 card is plugged in to slot 3",
+ "UMask": "0x01",
+ "Unit": "IIO"
+ },
+ {
+ "BriefDescription": "Clockticks of the IO coherency tracker (IRP)",
+ "Counter": "0,1",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x01",
+ "EventName": "UNC_I_CLOCKTICKS",
+ "PerPkg": "1",
+ "PublicDescription": "Clockticks of the IO coherency tracker (IRP)",
+ "Unit": "IRP"
+ },
+ {
+ "BriefDescription": "Clockticks of the mesh to memory (M2M)",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventName": "UNC_M2M_CLOCKTICKS",
+ "PerPkg": "1",
+ "PublicDescription": "Clockticks of the mesh to memory (M2M)",
+ "Unit": "M2M"
+ },
+ {
+ "BriefDescription": "Clockticks of the mesh to PCI (M2P)",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventCode": "0x01",
+ "EventName": "UNC_M2P_CLOCKTICKS",
+ "PerPkg": "1",
+ "PublicDescription": "Clockticks of the mesh to PCI (M2P)",
+ "Unit": "M2PCIe"
+ },
+ {
+ "BriefDescription": "Clockticks in the UBOX using a dedicated 48-bit Fixed Counter",
+ "Counter": "FIXED",
+ "CounterType": "PGMABLE",
+ "EventCode": "0xff",
+ "EventName": "UNC_U_CLOCKTICKS",
+ "PerPkg": "1",
+ "PublicDescription": "Clockticks in the UBOX using a dedicated 48-bit Fixed Counter",
+ "Unit": "UBOX"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/tremontx/uncore-power.json b/tools/perf/pmu-events/arch/x86/tremontx/uncore-power.json
new file mode 100644
index 000000000000..ea62c092b43f
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/tremontx/uncore-power.json
@@ -0,0 +1,11 @@
+[
+ {
+ "BriefDescription": "Clockticks of the power control unit (PCU)",
+ "Counter": "0,1,2,3",
+ "CounterType": "PGMABLE",
+ "EventName": "UNC_P_CLOCKTICKS",
+ "PerPkg": "1",
+ "PublicDescription": "Clockticks of the power control unit (PCU)",
+ "Unit": "PCU"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/tremontx/virtual-memory.json b/tools/perf/pmu-events/arch/x86/tremontx/virtual-memory.json
new file mode 100644
index 000000000000..93e407a0f645
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/tremontx/virtual-memory.json
@@ -0,0 +1,86 @@
+[
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts page walks completed due to demand data loads (including SW prefetches) whose address translations missed in all TLB levels and were mapped to 4K pages. The page walks can end with or without a page fault.",
+ "EventCode": "0x08",
+ "Counter": "0,1,2,3",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "DTLB_LOAD_MISSES.WALK_COMPLETED_4K",
+ "PDIR_COUNTER": "na",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Page walk completed due to a demand load to a 4K page."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts page walks completed due to demand data loads (including SW prefetches) whose address translations missed in all TLB levels and were mapped to 2M or 4M pages. The page walks can end with or without a page fault.",
+ "EventCode": "0x08",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "DTLB_LOAD_MISSES.WALK_COMPLETED_2M_4M",
+ "PDIR_COUNTER": "na",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Page walk completed due to a demand load to a 2M or 4M page."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts page walks completed due to demand data stores whose address translations missed in the TLB and were mapped to 4K pages. The page walks can end with or without a page fault.",
+ "EventCode": "0x49",
+ "Counter": "0,1,2,3",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "DTLB_STORE_MISSES.WALK_COMPLETED_4K",
+ "PDIR_COUNTER": "na",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Page walk completed due to a demand data store to a 4K page."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts page walks completed due to demand data stores whose address translations missed in the TLB and were mapped to 2M or 4M pages. The page walks can end with or without a page fault.",
+ "EventCode": "0x49",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "DTLB_STORE_MISSES.WALK_COMPLETED_2M_4M",
+ "PDIR_COUNTER": "na",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Page walk completed due to a demand data store to a 2M or 4M page."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts the number of times the machine was unable to find a translation in the Instruction Translation Lookaside Buffer (ITLB) and new translation was filled into the ITLB. The event is speculative in nature, but will not count translations (page walks) that are begun and not finished, or translations that are finished but not filled into the ITLB.",
+ "EventCode": "0x81",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "ITLB.FILLS",
+ "PDIR_COUNTER": "na",
+ "SampleAfterValue": "200003",
+ "BriefDescription": "Counts the number of times there was an ITLB miss and a new translation was filled into the ITLB."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts page walks completed due to instruction fetches whose address translations missed in the TLB and were mapped to 4K pages. The page walks can end with or without a page fault.",
+ "EventCode": "0x85",
+ "Counter": "0,1,2,3",
+ "UMask": "0x2",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "ITLB_MISSES.WALK_COMPLETED_4K",
+ "PDIR_COUNTER": "na",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Page walk completed due to an instruction fetch in a 4K page."
+ },
+ {
+ "CollectPEBSRecord": "2",
+ "PublicDescription": "Counts page walks completed due to instruction fetches whose address translations missed in the TLB and were mapped to 2M or 4M pages. The page walks can end with or without a page fault.",
+ "EventCode": "0x85",
+ "Counter": "0,1,2,3",
+ "UMask": "0x4",
+ "PEBScounters": "0,1,2,3",
+ "EventName": "ITLB_MISSES.WALK_COMPLETED_2M_4M",
+ "PDIR_COUNTER": "na",
+ "SampleAfterValue": "2000003",
+ "BriefDescription": "Page walk completed due to an instruction fetch in a 2M or 4M page."
+ }
+] \ No newline at end of file
diff --git a/tools/perf/pmu-events/jevents.c b/tools/perf/pmu-events/jevents.c
index 58f77fd0f59f..d413761621b0 100644
--- a/tools/perf/pmu-events/jevents.c
+++ b/tools/perf/pmu-events/jevents.c
@@ -236,6 +236,9 @@ static struct map {
{ "CPU-M-CF", "cpum_cf" },
{ "CPU-M-SF", "cpum_sf" },
{ "UPI LL", "uncore_upi" },
+ { "hisi_sccl,ddrc", "hisi_sccl,ddrc" },
+ { "hisi_sccl,hha", "hisi_sccl,hha" },
+ { "hisi_sccl,l3c", "hisi_sccl,l3c" },
{}
};
@@ -404,7 +407,7 @@ static void free_arch_std_events(void)
list_for_each_entry_safe(es, next, &arch_std_events, list) {
FOR_ALL_EVENT_STRUCT_FIELDS(FREE_EVENT_FIELD);
- list_del(&es->list);
+ list_del_init(&es->list);
free(es);
}
}
@@ -450,6 +453,7 @@ static struct fixed {
{ "inst_retired.any_p", "event=0xc0" },
{ "cpu_clk_unhalted.ref", "event=0x0,umask=0x03" },
{ "cpu_clk_unhalted.thread", "event=0x3c" },
+ { "cpu_clk_unhalted.core", "event=0x3c" },
{ "cpu_clk_unhalted.thread_any", "event=0x3c,any=1" },
{ NULL, NULL},
};
@@ -841,7 +845,7 @@ static void create_empty_mapping(const char *output_file)
_Exit(1);
}
- fprintf(outfp, "#include \"../../pmu-events/pmu-events.h\"\n");
+ fprintf(outfp, "#include \"pmu-events/pmu-events.h\"\n");
print_mapping_table_prefix(outfp);
print_mapping_table_suffix(outfp);
fclose(outfp);
@@ -1096,7 +1100,7 @@ int main(int argc, char *argv[])
}
/* Include pmu-events.h first */
- fprintf(eventsfp, "#include \"../../pmu-events/pmu-events.h\"\n");
+ fprintf(eventsfp, "#include \"pmu-events/pmu-events.h\"\n");
/*
* The mapfile allows multiple CPUids to point to the same JSON file,
diff --git a/tools/perf/python/twatch.py b/tools/perf/python/twatch.py
index 0a29c5c3079f..ff87ccf5b708 100755
--- a/tools/perf/python/twatch.py
+++ b/tools/perf/python/twatch.py
@@ -1,17 +1,10 @@
#! /usr/bin/python
+# SPDX-License-Identifier: GPL-2.0-only
# -*- python -*-
# -*- coding: utf-8 -*-
# twatch - Experimental use of the perf python interface
# Copyright (C) 2011 Arnaldo Carvalho de Melo <acme@redhat.com>
#
-# This application is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; version 2.
-#
-# This application is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
import perf
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Context.c b/tools/perf/scripts/perl/Perf-Trace-Util/Context.c
index 28431d1bbcf5..25c47d23a130 100644
--- a/tools/perf/scripts/perl/Perf-Trace-Util/Context.c
+++ b/tools/perf/scripts/perl/Perf-Trace-Util/Context.c
@@ -1,9 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* This file was generated automatically by ExtUtils::ParseXS version 2.18_02 from the
* contents of Context.xs. Do not edit this file, edit Context.xs instead.
*
* ANY CHANGES MADE HERE WILL BE LOST!
- *
*/
#include <stdbool.h>
#ifndef HAS_BOOL
@@ -14,27 +14,11 @@
* Context.xs. XS interfaces for perf script.
*
* Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
-#include "../../../perf.h"
#include "../../../util/trace-event.h"
#ifndef PERL_UNUSED_VAR
diff --git a/tools/perf/scripts/perl/rw-by-file.pl b/tools/perf/scripts/perl/rw-by-file.pl
index 74844ee2be3e..168fa5e94b44 100644
--- a/tools/perf/scripts/perl/rw-by-file.pl
+++ b/tools/perf/scripts/perl/rw-by-file.pl
@@ -1,6 +1,6 @@
#!/usr/bin/perl -w
+# SPDX-License-Identifier: GPL-2.0-only
# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
-# Licensed under the terms of the GNU GPL License version 2
# Display r/w activity for files read/written to for a given program
diff --git a/tools/perf/scripts/perl/rw-by-pid.pl b/tools/perf/scripts/perl/rw-by-pid.pl
index 9db23c9daf55..495698250b2f 100644
--- a/tools/perf/scripts/perl/rw-by-pid.pl
+++ b/tools/perf/scripts/perl/rw-by-pid.pl
@@ -1,6 +1,6 @@
#!/usr/bin/perl -w
+# SPDX-License-Identifier: GPL-2.0-only
# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
-# Licensed under the terms of the GNU GPL License version 2
# Display r/w activity for all processes
diff --git a/tools/perf/scripts/perl/rwtop.pl b/tools/perf/scripts/perl/rwtop.pl
index 8b20787021c1..6473442568a2 100644
--- a/tools/perf/scripts/perl/rwtop.pl
+++ b/tools/perf/scripts/perl/rwtop.pl
@@ -1,6 +1,6 @@
#!/usr/bin/perl -w
+# SPDX-License-Identifier: GPL-2.0-only
# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
-# Licensed under the terms of the GNU GPL License version 2
# read/write top
#
diff --git a/tools/perf/scripts/perl/wakeup-latency.pl b/tools/perf/scripts/perl/wakeup-latency.pl
index d9143dcec6c6..efcfec5e347a 100644
--- a/tools/perf/scripts/perl/wakeup-latency.pl
+++ b/tools/perf/scripts/perl/wakeup-latency.pl
@@ -1,6 +1,6 @@
#!/usr/bin/perl -w
+# SPDX-License-Identifier: GPL-2.0-only
# (c) 2009, Tom Zanussi <tzanussi@gmail.com>
-# Licensed under the terms of the GNU GPL License version 2
# Display avg/min/max wakeup latency
diff --git a/tools/perf/scripts/python/Perf-Trace-Util/Context.c b/tools/perf/scripts/python/Perf-Trace-Util/Context.c
index 1a0d27757eec..0b7096847991 100644
--- a/tools/perf/scripts/python/Perf-Trace-Util/Context.c
+++ b/tools/perf/scripts/python/Perf-Trace-Util/Context.c
@@ -1,26 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Context.c. Python interfaces for perf script.
*
* Copyright (C) 2010 Tom Zanussi <tzanussi@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <Python.h>
-#include "../../../perf.h"
#include "../../../util/trace-event.h"
#if PY_MAJOR_VERSION < 3
diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py
index c3eae1d77d36..7bd73a904b4e 100644
--- a/tools/perf/scripts/python/export-to-postgresql.py
+++ b/tools/perf/scripts/python/export-to-postgresql.py
@@ -27,18 +27,31 @@ import datetime
#
# fedora:
#
-# $ sudo yum install postgresql postgresql-server python-pyside qt-postgresql
+# $ sudo yum install postgresql postgresql-server qt-postgresql
# $ sudo su - postgres -c initdb
# $ sudo service postgresql start
# $ sudo su - postgres
-# $ createuser <your user id here>
+# $ createuser -s <your user id here> # Older versions may not support -s, in which case answer the prompt below:
# Shall the new role be a superuser? (y/n) y
+# $ sudo yum install python-pyside
+#
+# Alternately, to use Python3 and/or pyside 2, one of the following:
+# $ sudo yum install python3-pyside
+# $ pip install --user PySide2
+# $ pip3 install --user PySide2
#
# ubuntu:
#
-# $ sudo apt-get install postgresql python-pyside.qtsql libqt4-sql-psql
+# $ sudo apt-get install postgresql
# $ sudo su - postgres
# $ createuser -s <your user id here>
+# $ sudo apt-get install python-pyside.qtsql libqt4-sql-psql
+#
+# Alternately, to use Python3 and/or pyside 2, one of the following:
+#
+# $ sudo apt-get install python3-pyside.qtsql libqt4-sql-psql
+# $ sudo apt-get install python-pyside2.qtsql libqt5sql5-psql
+# $ sudo apt-get install python3-pyside2.qtsql libqt5sql5-psql
#
# An example of using this script with Intel PT:
#
@@ -199,7 +212,16 @@ import datetime
# print "{0:>6} {1:>10} {2:>9} {3:<30} {4:>6} {5:<30}".format(query.value(0), query.value(1), query.value(2), query.value(3), query.value(4), query.value(5))
# call_path_id = query.value(6)
-from PySide.QtSql import *
+pyside_version_1 = True
+if not "pyside-version-1" in sys.argv:
+ try:
+ from PySide2.QtSql import *
+ pyside_version_1 = False
+ except:
+ pass
+
+if pyside_version_1:
+ from PySide.QtSql import *
if sys.version_info < (3, 0):
def toserverstr(str):
@@ -255,11 +277,12 @@ def printdate(*args, **kw_args):
print(datetime.datetime.today(), *args, sep=' ', **kw_args)
def usage():
- printerr("Usage is: export-to-postgresql.py <database name> [<columns>] [<calls>] [<callchains>]")
- printerr("where: columns 'all' or 'branches'")
- printerr(" calls 'calls' => create calls and call_paths table")
- printerr(" callchains 'callchains' => create call_paths table")
- raise Exception("Too few arguments")
+ printerr("Usage is: export-to-postgresql.py <database name> [<columns>] [<calls>] [<callchains>] [<pyside-version-1>]");
+ printerr("where: columns 'all' or 'branches'");
+ printerr(" calls 'calls' => create calls and call_paths table");
+ printerr(" callchains 'callchains' => create call_paths table");
+ printerr(" pyside-version-1 'pyside-version-1' => use pyside version 1");
+ raise Exception("Too few or bad arguments")
if (len(sys.argv) < 2):
usage()
@@ -281,6 +304,8 @@ for i in range(3,len(sys.argv)):
perf_db_export_calls = True
elif (sys.argv[i] == "callchains"):
perf_db_export_callchains = True
+ elif (sys.argv[i] == "pyside-version-1"):
+ pass
else:
usage()
@@ -328,7 +353,10 @@ do_query(query, 'CREATE TABLE threads ('
'tid integer)')
do_query(query, 'CREATE TABLE comms ('
'id bigint NOT NULL,'
- 'comm varchar(16))')
+ 'comm varchar(16),'
+ 'c_thread_id bigint,'
+ 'c_time bigint,'
+ 'exec_flag boolean)')
do_query(query, 'CREATE TABLE comm_threads ('
'id bigint NOT NULL,'
'comm_id bigint,'
@@ -369,7 +397,9 @@ if branches:
'to_ip bigint,'
'branch_type integer,'
'in_tx boolean,'
- 'call_path_id bigint)')
+ 'call_path_id bigint,'
+ 'insn_count bigint,'
+ 'cyc_count bigint)')
else:
do_query(query, 'CREATE TABLE samples ('
'id bigint NOT NULL,'
@@ -393,7 +423,9 @@ else:
'data_src bigint,'
'branch_type integer,'
'in_tx boolean,'
- 'call_path_id bigint)')
+ 'call_path_id bigint,'
+ 'insn_count bigint,'
+ 'cyc_count bigint)')
if perf_db_export_calls or perf_db_export_callchains:
do_query(query, 'CREATE TABLE call_paths ('
@@ -414,7 +446,52 @@ if perf_db_export_calls:
'return_id bigint,'
'parent_call_path_id bigint,'
'flags integer,'
- 'parent_id bigint)')
+ 'parent_id bigint,'
+ 'insn_count bigint,'
+ 'cyc_count bigint)')
+
+do_query(query, 'CREATE TABLE ptwrite ('
+ 'id bigint NOT NULL,'
+ 'payload bigint,'
+ 'exact_ip boolean)')
+
+do_query(query, 'CREATE TABLE cbr ('
+ 'id bigint NOT NULL,'
+ 'cbr integer,'
+ 'mhz integer,'
+ 'percent integer)')
+
+do_query(query, 'CREATE TABLE mwait ('
+ 'id bigint NOT NULL,'
+ 'hints integer,'
+ 'extensions integer)')
+
+do_query(query, 'CREATE TABLE pwre ('
+ 'id bigint NOT NULL,'
+ 'cstate integer,'
+ 'subcstate integer,'
+ 'hw boolean)')
+
+do_query(query, 'CREATE TABLE exstop ('
+ 'id bigint NOT NULL,'
+ 'exact_ip boolean)')
+
+do_query(query, 'CREATE TABLE pwrx ('
+ 'id bigint NOT NULL,'
+ 'deepest_cstate integer,'
+ 'last_cstate integer,'
+ 'wake_reason integer)')
+
+do_query(query, 'CREATE TABLE context_switches ('
+ 'id bigint NOT NULL,'
+ 'machine_id bigint,'
+ 'time bigint,'
+ 'cpu integer,'
+ 'thread_out_id bigint,'
+ 'comm_out_id bigint,'
+ 'thread_in_id bigint,'
+ 'comm_in_id bigint,'
+ 'flags integer)')
do_query(query, 'CREATE VIEW machines_view AS '
'SELECT '
@@ -496,6 +573,9 @@ if perf_db_export_calls:
'return_time,'
'return_time - call_time AS elapsed_time,'
'branch_count,'
+ 'insn_count,'
+ 'cyc_count,'
+ 'CASE WHEN cyc_count=0 THEN CAST(0 AS NUMERIC(20, 2)) ELSE CAST((CAST(insn_count AS FLOAT) / cyc_count) AS NUMERIC(20, 2)) END AS IPC,'
'call_id,'
'return_id,'
'CASE WHEN flags=0 THEN \'\' WHEN flags=1 THEN \'no call\' WHEN flags=2 THEN \'no return\' WHEN flags=3 THEN \'no call/return\' WHEN flags=6 THEN \'jump\' ELSE CAST ( flags AS VARCHAR(6) ) END AS flags,'
@@ -521,9 +601,133 @@ do_query(query, 'CREATE VIEW samples_view AS '
'to_sym_offset,'
'(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name,'
'(SELECT name FROM branch_types WHERE id = branch_type) AS branch_type_name,'
- 'in_tx'
+ 'in_tx,'
+ 'insn_count,'
+ 'cyc_count,'
+ 'CASE WHEN cyc_count=0 THEN CAST(0 AS NUMERIC(20, 2)) ELSE CAST((CAST(insn_count AS FLOAT) / cyc_count) AS NUMERIC(20, 2)) END AS IPC'
' FROM samples')
+do_query(query, 'CREATE VIEW ptwrite_view AS '
+ 'SELECT '
+ 'ptwrite.id,'
+ 'time,'
+ 'cpu,'
+ 'to_hex(payload) AS payload_hex,'
+ 'CASE WHEN exact_ip=FALSE THEN \'False\' ELSE \'True\' END AS exact_ip'
+ ' FROM ptwrite'
+ ' INNER JOIN samples ON samples.id = ptwrite.id')
+
+do_query(query, 'CREATE VIEW cbr_view AS '
+ 'SELECT '
+ 'cbr.id,'
+ 'time,'
+ 'cpu,'
+ 'cbr,'
+ 'mhz,'
+ 'percent'
+ ' FROM cbr'
+ ' INNER JOIN samples ON samples.id = cbr.id')
+
+do_query(query, 'CREATE VIEW mwait_view AS '
+ 'SELECT '
+ 'mwait.id,'
+ 'time,'
+ 'cpu,'
+ 'to_hex(hints) AS hints_hex,'
+ 'to_hex(extensions) AS extensions_hex'
+ ' FROM mwait'
+ ' INNER JOIN samples ON samples.id = mwait.id')
+
+do_query(query, 'CREATE VIEW pwre_view AS '
+ 'SELECT '
+ 'pwre.id,'
+ 'time,'
+ 'cpu,'
+ 'cstate,'
+ 'subcstate,'
+ 'CASE WHEN hw=FALSE THEN \'False\' ELSE \'True\' END AS hw'
+ ' FROM pwre'
+ ' INNER JOIN samples ON samples.id = pwre.id')
+
+do_query(query, 'CREATE VIEW exstop_view AS '
+ 'SELECT '
+ 'exstop.id,'
+ 'time,'
+ 'cpu,'
+ 'CASE WHEN exact_ip=FALSE THEN \'False\' ELSE \'True\' END AS exact_ip'
+ ' FROM exstop'
+ ' INNER JOIN samples ON samples.id = exstop.id')
+
+do_query(query, 'CREATE VIEW pwrx_view AS '
+ 'SELECT '
+ 'pwrx.id,'
+ 'time,'
+ 'cpu,'
+ 'deepest_cstate,'
+ 'last_cstate,'
+ 'CASE WHEN wake_reason=1 THEN \'Interrupt\''
+ ' WHEN wake_reason=2 THEN \'Timer Deadline\''
+ ' WHEN wake_reason=4 THEN \'Monitored Address\''
+ ' WHEN wake_reason=8 THEN \'HW\''
+ ' ELSE CAST ( wake_reason AS VARCHAR(2) )'
+ 'END AS wake_reason'
+ ' FROM pwrx'
+ ' INNER JOIN samples ON samples.id = pwrx.id')
+
+do_query(query, 'CREATE VIEW power_events_view AS '
+ 'SELECT '
+ 'samples.id,'
+ 'samples.time,'
+ 'samples.cpu,'
+ 'selected_events.name AS event,'
+ 'FORMAT(\'%6s\', cbr.cbr) AS cbr,'
+ 'FORMAT(\'%6s\', cbr.mhz) AS MHz,'
+ 'FORMAT(\'%5s\', cbr.percent) AS percent,'
+ 'to_hex(mwait.hints) AS hints_hex,'
+ 'to_hex(mwait.extensions) AS extensions_hex,'
+ 'FORMAT(\'%3s\', pwre.cstate) AS cstate,'
+ 'FORMAT(\'%3s\', pwre.subcstate) AS subcstate,'
+ 'CASE WHEN pwre.hw=FALSE THEN \'False\' WHEN pwre.hw=TRUE THEN \'True\' ELSE NULL END AS hw,'
+ 'CASE WHEN exstop.exact_ip=FALSE THEN \'False\' WHEN exstop.exact_ip=TRUE THEN \'True\' ELSE NULL END AS exact_ip,'
+ 'FORMAT(\'%3s\', pwrx.deepest_cstate) AS deepest_cstate,'
+ 'FORMAT(\'%3s\', pwrx.last_cstate) AS last_cstate,'
+ 'CASE WHEN pwrx.wake_reason=1 THEN \'Interrupt\''
+ ' WHEN pwrx.wake_reason=2 THEN \'Timer Deadline\''
+ ' WHEN pwrx.wake_reason=4 THEN \'Monitored Address\''
+ ' WHEN pwrx.wake_reason=8 THEN \'HW\''
+ ' ELSE FORMAT(\'%2s\', pwrx.wake_reason)'
+ 'END AS wake_reason'
+ ' FROM cbr'
+ ' FULL JOIN mwait ON mwait.id = cbr.id'
+ ' FULL JOIN pwre ON pwre.id = cbr.id'
+ ' FULL JOIN exstop ON exstop.id = cbr.id'
+ ' FULL JOIN pwrx ON pwrx.id = cbr.id'
+ ' INNER JOIN samples ON samples.id = coalesce(cbr.id, mwait.id, pwre.id, exstop.id, pwrx.id)'
+ ' INNER JOIN selected_events ON selected_events.id = samples.evsel_id'
+ ' ORDER BY samples.id')
+
+do_query(query, 'CREATE VIEW context_switches_view AS '
+ 'SELECT '
+ 'context_switches.id,'
+ 'context_switches.machine_id,'
+ 'context_switches.time,'
+ 'context_switches.cpu,'
+ 'th_out.pid AS pid_out,'
+ 'th_out.tid AS tid_out,'
+ 'comm_out.comm AS comm_out,'
+ 'th_in.pid AS pid_in,'
+ 'th_in.tid AS tid_in,'
+ 'comm_in.comm AS comm_in,'
+ 'CASE WHEN context_switches.flags = 0 THEN \'in\''
+ ' WHEN context_switches.flags = 1 THEN \'out\''
+ ' WHEN context_switches.flags = 3 THEN \'out preempt\''
+ ' ELSE CAST ( context_switches.flags AS VARCHAR(11) )'
+ 'END AS flags'
+ ' FROM context_switches'
+ ' INNER JOIN threads AS th_out ON th_out.id = context_switches.thread_out_id'
+ ' INNER JOIN threads AS th_in ON th_in.id = context_switches.thread_in_id'
+ ' INNER JOIN comms AS comm_out ON comm_out.id = context_switches.comm_out_id'
+ ' INNER JOIN comms AS comm_in ON comm_in.id = context_switches.comm_in_id')
file_header = struct.pack("!11sii", b"PGCOPY\n\377\r\n\0", 0, 0)
file_trailer = b"\377\377"
@@ -583,6 +787,13 @@ if perf_db_export_calls or perf_db_export_callchains:
call_path_file = open_output_file("call_path_table.bin")
if perf_db_export_calls:
call_file = open_output_file("call_table.bin")
+ptwrite_file = open_output_file("ptwrite_table.bin")
+cbr_file = open_output_file("cbr_table.bin")
+mwait_file = open_output_file("mwait_table.bin")
+pwre_file = open_output_file("pwre_table.bin")
+exstop_file = open_output_file("exstop_table.bin")
+pwrx_file = open_output_file("pwrx_table.bin")
+context_switches_file = open_output_file("context_switches_table.bin")
def trace_begin():
printdate("Writing to intermediate files...")
@@ -590,16 +801,26 @@ def trace_begin():
evsel_table(0, "unknown")
machine_table(0, 0, "unknown")
thread_table(0, 0, 0, -1, -1)
- comm_table(0, "unknown")
+ comm_table(0, "unknown", 0, 0, 0)
dso_table(0, 0, "unknown", "unknown", "")
symbol_table(0, 0, 0, 0, 0, "unknown")
- sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+ sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
if perf_db_export_calls or perf_db_export_callchains:
call_path_table(0, 0, 0, 0)
- call_return_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+ call_return_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
unhandled_count = 0
+def is_table_empty(table_name):
+ do_query(query, 'SELECT * FROM ' + table_name + ' LIMIT 1');
+ if query.next():
+ return False
+ return True
+
+def drop(table_name):
+ do_query(query, 'DROP VIEW ' + table_name + '_view');
+ do_query(query, 'DROP TABLE ' + table_name);
+
def trace_end():
printdate("Copying to database...")
copy_output_file(evsel_file, "selected_events")
@@ -615,6 +836,13 @@ def trace_end():
copy_output_file(call_path_file, "call_paths")
if perf_db_export_calls:
copy_output_file(call_file, "calls")
+ copy_output_file(ptwrite_file, "ptwrite")
+ copy_output_file(cbr_file, "cbr")
+ copy_output_file(mwait_file, "mwait")
+ copy_output_file(pwre_file, "pwre")
+ copy_output_file(exstop_file, "exstop")
+ copy_output_file(pwrx_file, "pwrx")
+ copy_output_file(context_switches_file, "context_switches")
printdate("Removing intermediate files...")
remove_output_file(evsel_file)
@@ -630,6 +858,13 @@ def trace_end():
remove_output_file(call_path_file)
if perf_db_export_calls:
remove_output_file(call_file)
+ remove_output_file(ptwrite_file)
+ remove_output_file(cbr_file)
+ remove_output_file(mwait_file)
+ remove_output_file(pwre_file)
+ remove_output_file(exstop_file)
+ remove_output_file(pwrx_file)
+ remove_output_file(context_switches_file)
os.rmdir(output_dir_name)
printdate("Adding primary keys")
do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)')
@@ -645,11 +880,20 @@ def trace_end():
do_query(query, 'ALTER TABLE call_paths ADD PRIMARY KEY (id)')
if perf_db_export_calls:
do_query(query, 'ALTER TABLE calls ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE ptwrite ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE cbr ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE mwait ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE pwre ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE exstop ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE pwrx ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE context_switches ADD PRIMARY KEY (id)')
printdate("Adding foreign keys")
do_query(query, 'ALTER TABLE threads '
'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id),'
'ADD CONSTRAINT processfk FOREIGN KEY (process_id) REFERENCES threads (id)')
+ do_query(query, 'ALTER TABLE comms '
+ 'ADD CONSTRAINT threadfk FOREIGN KEY (c_thread_id) REFERENCES threads (id)')
do_query(query, 'ALTER TABLE comm_threads '
'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),'
'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id)')
@@ -680,6 +924,40 @@ def trace_end():
'ADD CONSTRAINT parent_call_pathfk FOREIGN KEY (parent_call_path_id) REFERENCES call_paths (id)')
do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)')
do_query(query, 'CREATE INDEX pid_idx ON calls (parent_id)')
+ do_query(query, 'ALTER TABLE comms ADD has_calls boolean')
+ do_query(query, 'UPDATE comms SET has_calls = TRUE WHERE comms.id IN (SELECT DISTINCT comm_id FROM calls)')
+ do_query(query, 'ALTER TABLE ptwrite '
+ 'ADD CONSTRAINT idfk FOREIGN KEY (id) REFERENCES samples (id)')
+ do_query(query, 'ALTER TABLE cbr '
+ 'ADD CONSTRAINT idfk FOREIGN KEY (id) REFERENCES samples (id)')
+ do_query(query, 'ALTER TABLE mwait '
+ 'ADD CONSTRAINT idfk FOREIGN KEY (id) REFERENCES samples (id)')
+ do_query(query, 'ALTER TABLE pwre '
+ 'ADD CONSTRAINT idfk FOREIGN KEY (id) REFERENCES samples (id)')
+ do_query(query, 'ALTER TABLE exstop '
+ 'ADD CONSTRAINT idfk FOREIGN KEY (id) REFERENCES samples (id)')
+ do_query(query, 'ALTER TABLE pwrx '
+ 'ADD CONSTRAINT idfk FOREIGN KEY (id) REFERENCES samples (id)')
+ do_query(query, 'ALTER TABLE context_switches '
+ 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id),'
+ 'ADD CONSTRAINT toutfk FOREIGN KEY (thread_out_id) REFERENCES threads (id),'
+ 'ADD CONSTRAINT tinfk FOREIGN KEY (thread_in_id) REFERENCES threads (id),'
+ 'ADD CONSTRAINT coutfk FOREIGN KEY (comm_out_id) REFERENCES comms (id),'
+ 'ADD CONSTRAINT cinfk FOREIGN KEY (comm_in_id) REFERENCES comms (id)')
+
+ printdate("Dropping unused tables")
+ if is_table_empty("ptwrite"):
+ drop("ptwrite")
+ if is_table_empty("mwait") and is_table_empty("pwre") and is_table_empty("exstop") and is_table_empty("pwrx"):
+ do_query(query, 'DROP VIEW power_events_view');
+ drop("mwait")
+ drop("pwre")
+ drop("exstop")
+ drop("pwrx")
+ if is_table_empty("cbr"):
+ drop("cbr")
+ if is_table_empty("context_switches"):
+ drop("context_switches")
if (unhandled_count):
printdate("Warning: ", unhandled_count, " unhandled events")
@@ -710,11 +988,11 @@ def thread_table(thread_id, machine_id, process_id, pid, tid, *x):
value = struct.pack("!hiqiqiqiiii", 5, 8, thread_id, 8, machine_id, 8, process_id, 4, pid, 4, tid)
thread_file.write(value)
-def comm_table(comm_id, comm_str, *x):
+def comm_table(comm_id, comm_str, thread_id, time, exec_flag, *x):
comm_str = toserverstr(comm_str)
n = len(comm_str)
- fmt = "!hiqi" + str(n) + "s"
- value = struct.pack(fmt, 2, 8, comm_id, n, comm_str)
+ fmt = "!hiqi" + str(n) + "s" + "iqiqiB"
+ value = struct.pack(fmt, 5, 8, comm_id, n, comm_str, 8, thread_id, 8, time, 1, exec_flag)
comm_file.write(value)
def comm_thread_table(comm_thread_id, comm_id, thread_id, *x):
@@ -747,11 +1025,11 @@ def branch_type_table(branch_type, name, *x):
value = struct.pack(fmt, 2, 4, branch_type, n, name)
branch_type_file.write(value)
-def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, branch_type, in_tx, call_path_id, *x):
+def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, branch_type, in_tx, call_path_id, insn_cnt, cyc_cnt, *x):
if branches:
- value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiiiBiq", 18, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 4, branch_type, 1, in_tx, 8, call_path_id)
+ value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiiiBiqiqiq", 20, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 4, branch_type, 1, in_tx, 8, call_path_id, 8, insn_cnt, 8, cyc_cnt)
else:
- value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiqiiiBiq", 22, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src, 4, branch_type, 1, in_tx, 8, call_path_id)
+ value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiqiiiBiqiqiq", 24, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src, 4, branch_type, 1, in_tx, 8, call_path_id, 8, insn_cnt, 8, cyc_cnt)
sample_file.write(value)
def call_path_table(cp_id, parent_id, symbol_id, ip, *x):
@@ -759,7 +1037,75 @@ def call_path_table(cp_id, parent_id, symbol_id, ip, *x):
value = struct.pack(fmt, 4, 8, cp_id, 8, parent_id, 8, symbol_id, 8, ip)
call_path_file.write(value)
-def call_return_table(cr_id, thread_id, comm_id, call_path_id, call_time, return_time, branch_count, call_id, return_id, parent_call_path_id, flags, parent_id, *x):
- fmt = "!hiqiqiqiqiqiqiqiqiqiqiiiq"
- value = struct.pack(fmt, 12, 8, cr_id, 8, thread_id, 8, comm_id, 8, call_path_id, 8, call_time, 8, return_time, 8, branch_count, 8, call_id, 8, return_id, 8, parent_call_path_id, 4, flags, 8, parent_id)
+def call_return_table(cr_id, thread_id, comm_id, call_path_id, call_time, return_time, branch_count, call_id, return_id, parent_call_path_id, flags, parent_id, insn_cnt, cyc_cnt, *x):
+ fmt = "!hiqiqiqiqiqiqiqiqiqiqiiiqiqiq"
+ value = struct.pack(fmt, 14, 8, cr_id, 8, thread_id, 8, comm_id, 8, call_path_id, 8, call_time, 8, return_time, 8, branch_count, 8, call_id, 8, return_id, 8, parent_call_path_id, 4, flags, 8, parent_id, 8, insn_cnt, 8, cyc_cnt)
call_file.write(value)
+
+def ptwrite(id, raw_buf):
+ data = struct.unpack_from("<IQ", raw_buf)
+ flags = data[0]
+ payload = data[1]
+ exact_ip = flags & 1
+ value = struct.pack("!hiqiqiB", 3, 8, id, 8, payload, 1, exact_ip)
+ ptwrite_file.write(value)
+
+def cbr(id, raw_buf):
+ data = struct.unpack_from("<BBBBII", raw_buf)
+ cbr = data[0]
+ MHz = (data[4] + 500) / 1000
+ percent = ((cbr * 1000 / data[2]) + 5) / 10
+ value = struct.pack("!hiqiiiiii", 4, 8, id, 4, cbr, 4, MHz, 4, percent)
+ cbr_file.write(value)
+
+def mwait(id, raw_buf):
+ data = struct.unpack_from("<IQ", raw_buf)
+ payload = data[1]
+ hints = payload & 0xff
+ extensions = (payload >> 32) & 0x3
+ value = struct.pack("!hiqiiii", 3, 8, id, 4, hints, 4, extensions)
+ mwait_file.write(value)
+
+def pwre(id, raw_buf):
+ data = struct.unpack_from("<IQ", raw_buf)
+ payload = data[1]
+ hw = (payload >> 7) & 1
+ cstate = (payload >> 12) & 0xf
+ subcstate = (payload >> 8) & 0xf
+ value = struct.pack("!hiqiiiiiB", 4, 8, id, 4, cstate, 4, subcstate, 1, hw)
+ pwre_file.write(value)
+
+def exstop(id, raw_buf):
+ data = struct.unpack_from("<I", raw_buf)
+ flags = data[0]
+ exact_ip = flags & 1
+ value = struct.pack("!hiqiB", 2, 8, id, 1, exact_ip)
+ exstop_file.write(value)
+
+def pwrx(id, raw_buf):
+ data = struct.unpack_from("<IQ", raw_buf)
+ payload = data[1]
+ deepest_cstate = payload & 0xf
+ last_cstate = (payload >> 4) & 0xf
+ wake_reason = (payload >> 8) & 0xf
+ value = struct.pack("!hiqiiiiii", 4, 8, id, 4, deepest_cstate, 4, last_cstate, 4, wake_reason)
+ pwrx_file.write(value)
+
+def synth_data(id, config, raw_buf, *x):
+ if config == 0:
+ ptwrite(id, raw_buf)
+ elif config == 1:
+ mwait(id, raw_buf)
+ elif config == 2:
+ pwre(id, raw_buf)
+ elif config == 3:
+ exstop(id, raw_buf)
+ elif config == 4:
+ pwrx(id, raw_buf)
+ elif config == 5:
+ cbr(id, raw_buf)
+
+def context_switch_table(id, machine_id, time, cpu, thread_out_id, comm_out_id, thread_in_id, comm_in_id, flags, *x):
+ fmt = "!hiqiqiqiiiqiqiqiqii"
+ value = struct.pack(fmt, 9, 8, id, 8, machine_id, 8, time, 4, cpu, 8, thread_out_id, 8, comm_out_id, 8, thread_in_id, 8, comm_in_id, 4, flags)
+ context_switches_file.write(value)
diff --git a/tools/perf/scripts/python/export-to-sqlite.py b/tools/perf/scripts/python/export-to-sqlite.py
index bf271fbc3a88..8043a7272a56 100644
--- a/tools/perf/scripts/python/export-to-sqlite.py
+++ b/tools/perf/scripts/python/export-to-sqlite.py
@@ -21,6 +21,26 @@ import datetime
# provides LGPL-licensed Python bindings for Qt. You will also need the package
# libqt4-sql-sqlite for Qt sqlite3 support.
#
+# Examples of installing pyside:
+#
+# ubuntu:
+#
+# $ sudo apt-get install python-pyside.qtsql libqt4-sql-psql
+#
+# Alternately, to use Python3 and/or pyside 2, one of the following:
+#
+# $ sudo apt-get install python3-pyside.qtsql libqt4-sql-psql
+# $ sudo apt-get install python-pyside2.qtsql libqt5sql5-psql
+# $ sudo apt-get install python3-pyside2.qtsql libqt5sql5-psql
+# fedora:
+#
+# $ sudo yum install python-pyside
+#
+# Alternately, to use Python3 and/or pyside 2, one of the following:
+# $ sudo yum install python3-pyside
+# $ pip install --user PySide2
+# $ pip3 install --user PySide2
+#
# An example of using this script with Intel PT:
#
# $ perf record -e intel_pt//u ls
@@ -49,7 +69,16 @@ import datetime
# difference is the 'transaction' column of the 'samples' table which is
# renamed 'transaction_' in sqlite because 'transaction' is a reserved word.
-from PySide.QtSql import *
+pyside_version_1 = True
+if not "pyside-version-1" in sys.argv:
+ try:
+ from PySide2.QtSql import *
+ pyside_version_1 = False
+ except:
+ pass
+
+if pyside_version_1:
+ from PySide.QtSql import *
sys.path.append(os.environ['PERF_EXEC_PATH'] + \
'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
@@ -69,11 +98,12 @@ def printdate(*args, **kw_args):
print(datetime.datetime.today(), *args, sep=' ', **kw_args)
def usage():
- printerr("Usage is: export-to-sqlite.py <database name> [<columns>] [<calls>] [<callchains>]");
- printerr("where: columns 'all' or 'branches'");
- printerr(" calls 'calls' => create calls and call_paths table");
- printerr(" callchains 'callchains' => create call_paths table");
- raise Exception("Too few arguments")
+ printerr("Usage is: export-to-sqlite.py <database name> [<columns>] [<calls>] [<callchains>] [<pyside-version-1>]");
+ printerr("where: columns 'all' or 'branches'");
+ printerr(" calls 'calls' => create calls and call_paths table");
+ printerr(" callchains 'callchains' => create call_paths table");
+ printerr(" pyside-version-1 'pyside-version-1' => use pyside version 1");
+ raise Exception("Too few or bad arguments")
if (len(sys.argv) < 2):
usage()
@@ -95,6 +125,8 @@ for i in range(3,len(sys.argv)):
perf_db_export_calls = True
elif (sys.argv[i] == "callchains"):
perf_db_export_callchains = True
+ elif (sys.argv[i] == "pyside-version-1"):
+ pass
else:
usage()
@@ -145,7 +177,10 @@ do_query(query, 'CREATE TABLE threads ('
'tid integer)')
do_query(query, 'CREATE TABLE comms ('
'id integer NOT NULL PRIMARY KEY,'
- 'comm varchar(16))')
+ 'comm varchar(16),'
+ 'c_thread_id bigint,'
+ 'c_time bigint,'
+ 'exec_flag boolean)')
do_query(query, 'CREATE TABLE comm_threads ('
'id integer NOT NULL PRIMARY KEY,'
'comm_id bigint,'
@@ -186,7 +221,9 @@ if branches:
'to_ip bigint,'
'branch_type integer,'
'in_tx boolean,'
- 'call_path_id bigint)')
+ 'call_path_id bigint,'
+ 'insn_count bigint,'
+ 'cyc_count bigint)')
else:
do_query(query, 'CREATE TABLE samples ('
'id integer NOT NULL PRIMARY KEY,'
@@ -210,7 +247,9 @@ else:
'data_src bigint,'
'branch_type integer,'
'in_tx boolean,'
- 'call_path_id bigint)')
+ 'call_path_id bigint,'
+ 'insn_count bigint,'
+ 'cyc_count bigint)')
if perf_db_export_calls or perf_db_export_callchains:
do_query(query, 'CREATE TABLE call_paths ('
@@ -231,7 +270,52 @@ if perf_db_export_calls:
'return_id bigint,'
'parent_call_path_id bigint,'
'flags integer,'
- 'parent_id bigint)')
+ 'parent_id bigint,'
+ 'insn_count bigint,'
+ 'cyc_count bigint)')
+
+do_query(query, 'CREATE TABLE ptwrite ('
+ 'id integer NOT NULL PRIMARY KEY,'
+ 'payload bigint,'
+ 'exact_ip integer)')
+
+do_query(query, 'CREATE TABLE cbr ('
+ 'id integer NOT NULL PRIMARY KEY,'
+ 'cbr integer,'
+ 'mhz integer,'
+ 'percent integer)')
+
+do_query(query, 'CREATE TABLE mwait ('
+ 'id integer NOT NULL PRIMARY KEY,'
+ 'hints integer,'
+ 'extensions integer)')
+
+do_query(query, 'CREATE TABLE pwre ('
+ 'id integer NOT NULL PRIMARY KEY,'
+ 'cstate integer,'
+ 'subcstate integer,'
+ 'hw integer)')
+
+do_query(query, 'CREATE TABLE exstop ('
+ 'id integer NOT NULL PRIMARY KEY,'
+ 'exact_ip integer)')
+
+do_query(query, 'CREATE TABLE pwrx ('
+ 'id integer NOT NULL PRIMARY KEY,'
+ 'deepest_cstate integer,'
+ 'last_cstate integer,'
+ 'wake_reason integer)')
+
+do_query(query, 'CREATE TABLE context_switches ('
+ 'id integer NOT NULL PRIMARY KEY,'
+ 'machine_id bigint,'
+ 'time bigint,'
+ 'cpu integer,'
+ 'thread_out_id bigint,'
+ 'comm_out_id bigint,'
+ 'thread_in_id bigint,'
+ 'comm_in_id bigint,'
+ 'flags integer)')
# printf was added to sqlite in version 3.8.3
sqlite_has_printf = False
@@ -327,6 +411,9 @@ if perf_db_export_calls:
'return_time,'
'return_time - call_time AS elapsed_time,'
'branch_count,'
+ 'insn_count,'
+ 'cyc_count,'
+ 'CASE WHEN cyc_count=0 THEN CAST(0 AS FLOAT) ELSE ROUND(CAST(insn_count AS FLOAT) / cyc_count, 2) END AS IPC,'
'call_id,'
'return_id,'
'CASE WHEN flags=0 THEN \'\' WHEN flags=1 THEN \'no call\' WHEN flags=2 THEN \'no return\' WHEN flags=3 THEN \'no call/return\' WHEN flags=6 THEN \'jump\' ELSE flags END AS flags,'
@@ -352,9 +439,131 @@ do_query(query, 'CREATE VIEW samples_view AS '
'to_sym_offset,'
'(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name,'
'(SELECT name FROM branch_types WHERE id = branch_type) AS branch_type_name,'
- 'in_tx'
+ 'in_tx,'
+ 'insn_count,'
+ 'cyc_count,'
+ 'CASE WHEN cyc_count=0 THEN CAST(0 AS FLOAT) ELSE ROUND(CAST(insn_count AS FLOAT) / cyc_count, 2) END AS IPC'
' FROM samples')
+do_query(query, 'CREATE VIEW ptwrite_view AS '
+ 'SELECT '
+ 'ptwrite.id,'
+ 'time,'
+ 'cpu,'
+ + emit_to_hex('payload') + ' AS payload_hex,'
+ 'CASE WHEN exact_ip=0 THEN \'False\' ELSE \'True\' END AS exact_ip'
+ ' FROM ptwrite'
+ ' INNER JOIN samples ON samples.id = ptwrite.id')
+
+do_query(query, 'CREATE VIEW cbr_view AS '
+ 'SELECT '
+ 'cbr.id,'
+ 'time,'
+ 'cpu,'
+ 'cbr,'
+ 'mhz,'
+ 'percent'
+ ' FROM cbr'
+ ' INNER JOIN samples ON samples.id = cbr.id')
+
+do_query(query, 'CREATE VIEW mwait_view AS '
+ 'SELECT '
+ 'mwait.id,'
+ 'time,'
+ 'cpu,'
+ + emit_to_hex('hints') + ' AS hints_hex,'
+ + emit_to_hex('extensions') + ' AS extensions_hex'
+ ' FROM mwait'
+ ' INNER JOIN samples ON samples.id = mwait.id')
+
+do_query(query, 'CREATE VIEW pwre_view AS '
+ 'SELECT '
+ 'pwre.id,'
+ 'time,'
+ 'cpu,'
+ 'cstate,'
+ 'subcstate,'
+ 'CASE WHEN hw=0 THEN \'False\' ELSE \'True\' END AS hw'
+ ' FROM pwre'
+ ' INNER JOIN samples ON samples.id = pwre.id')
+
+do_query(query, 'CREATE VIEW exstop_view AS '
+ 'SELECT '
+ 'exstop.id,'
+ 'time,'
+ 'cpu,'
+ 'CASE WHEN exact_ip=0 THEN \'False\' ELSE \'True\' END AS exact_ip'
+ ' FROM exstop'
+ ' INNER JOIN samples ON samples.id = exstop.id')
+
+do_query(query, 'CREATE VIEW pwrx_view AS '
+ 'SELECT '
+ 'pwrx.id,'
+ 'time,'
+ 'cpu,'
+ 'deepest_cstate,'
+ 'last_cstate,'
+ 'CASE WHEN wake_reason=1 THEN \'Interrupt\''
+ ' WHEN wake_reason=2 THEN \'Timer Deadline\''
+ ' WHEN wake_reason=4 THEN \'Monitored Address\''
+ ' WHEN wake_reason=8 THEN \'HW\''
+ ' ELSE wake_reason '
+ 'END AS wake_reason'
+ ' FROM pwrx'
+ ' INNER JOIN samples ON samples.id = pwrx.id')
+
+do_query(query, 'CREATE VIEW power_events_view AS '
+ 'SELECT '
+ 'samples.id,'
+ 'time,'
+ 'cpu,'
+ 'selected_events.name AS event,'
+ 'CASE WHEN selected_events.name=\'cbr\' THEN (SELECT cbr FROM cbr WHERE cbr.id = samples.id) ELSE "" END AS cbr,'
+ 'CASE WHEN selected_events.name=\'cbr\' THEN (SELECT mhz FROM cbr WHERE cbr.id = samples.id) ELSE "" END AS mhz,'
+ 'CASE WHEN selected_events.name=\'cbr\' THEN (SELECT percent FROM cbr WHERE cbr.id = samples.id) ELSE "" END AS percent,'
+ 'CASE WHEN selected_events.name=\'mwait\' THEN (SELECT ' + emit_to_hex('hints') + ' FROM mwait WHERE mwait.id = samples.id) ELSE "" END AS hints_hex,'
+ 'CASE WHEN selected_events.name=\'mwait\' THEN (SELECT ' + emit_to_hex('extensions') + ' FROM mwait WHERE mwait.id = samples.id) ELSE "" END AS extensions_hex,'
+ 'CASE WHEN selected_events.name=\'pwre\' THEN (SELECT cstate FROM pwre WHERE pwre.id = samples.id) ELSE "" END AS cstate,'
+ 'CASE WHEN selected_events.name=\'pwre\' THEN (SELECT subcstate FROM pwre WHERE pwre.id = samples.id) ELSE "" END AS subcstate,'
+ 'CASE WHEN selected_events.name=\'pwre\' THEN (SELECT hw FROM pwre WHERE pwre.id = samples.id) ELSE "" END AS hw,'
+ 'CASE WHEN selected_events.name=\'exstop\' THEN (SELECT exact_ip FROM exstop WHERE exstop.id = samples.id) ELSE "" END AS exact_ip,'
+ 'CASE WHEN selected_events.name=\'pwrx\' THEN (SELECT deepest_cstate FROM pwrx WHERE pwrx.id = samples.id) ELSE "" END AS deepest_cstate,'
+ 'CASE WHEN selected_events.name=\'pwrx\' THEN (SELECT last_cstate FROM pwrx WHERE pwrx.id = samples.id) ELSE "" END AS last_cstate,'
+ 'CASE WHEN selected_events.name=\'pwrx\' THEN (SELECT '
+ 'CASE WHEN wake_reason=1 THEN \'Interrupt\''
+ ' WHEN wake_reason=2 THEN \'Timer Deadline\''
+ ' WHEN wake_reason=4 THEN \'Monitored Address\''
+ ' WHEN wake_reason=8 THEN \'HW\''
+ ' ELSE wake_reason '
+ 'END'
+ ' FROM pwrx WHERE pwrx.id = samples.id) ELSE "" END AS wake_reason'
+ ' FROM samples'
+ ' INNER JOIN selected_events ON selected_events.id = evsel_id'
+ ' WHERE selected_events.name IN (\'cbr\',\'mwait\',\'exstop\',\'pwre\',\'pwrx\')')
+
+do_query(query, 'CREATE VIEW context_switches_view AS '
+ 'SELECT '
+ 'context_switches.id,'
+ 'context_switches.machine_id,'
+ 'context_switches.time,'
+ 'context_switches.cpu,'
+ 'th_out.pid AS pid_out,'
+ 'th_out.tid AS tid_out,'
+ 'comm_out.comm AS comm_out,'
+ 'th_in.pid AS pid_in,'
+ 'th_in.tid AS tid_in,'
+ 'comm_in.comm AS comm_in,'
+ 'CASE WHEN context_switches.flags = 0 THEN \'in\''
+ ' WHEN context_switches.flags = 1 THEN \'out\''
+ ' WHEN context_switches.flags = 3 THEN \'out preempt\''
+ ' ELSE context_switches.flags '
+ 'END AS flags'
+ ' FROM context_switches'
+ ' INNER JOIN threads AS th_out ON th_out.id = context_switches.thread_out_id'
+ ' INNER JOIN threads AS th_in ON th_in.id = context_switches.thread_in_id'
+ ' INNER JOIN comms AS comm_out ON comm_out.id = context_switches.comm_out_id'
+ ' INNER JOIN comms AS comm_in ON comm_in.id = context_switches.comm_in_id')
+
do_query(query, 'END TRANSACTION')
evsel_query = QSqlQuery(db)
@@ -364,7 +573,7 @@ machine_query.prepare("INSERT INTO machines VALUES (?, ?, ?)")
thread_query = QSqlQuery(db)
thread_query.prepare("INSERT INTO threads VALUES (?, ?, ?, ?, ?)")
comm_query = QSqlQuery(db)
-comm_query.prepare("INSERT INTO comms VALUES (?, ?)")
+comm_query.prepare("INSERT INTO comms VALUES (?, ?, ?, ?, ?)")
comm_thread_query = QSqlQuery(db)
comm_thread_query.prepare("INSERT INTO comm_threads VALUES (?, ?, ?)")
dso_query = QSqlQuery(db)
@@ -375,15 +584,29 @@ branch_type_query = QSqlQuery(db)
branch_type_query.prepare("INSERT INTO branch_types VALUES (?, ?)")
sample_query = QSqlQuery(db)
if branches:
- sample_query.prepare("INSERT INTO samples VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
+ sample_query.prepare("INSERT INTO samples VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
else:
- sample_query.prepare("INSERT INTO samples VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
+ sample_query.prepare("INSERT INTO samples VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
if perf_db_export_calls or perf_db_export_callchains:
call_path_query = QSqlQuery(db)
call_path_query.prepare("INSERT INTO call_paths VALUES (?, ?, ?, ?)")
if perf_db_export_calls:
call_query = QSqlQuery(db)
- call_query.prepare("INSERT INTO calls VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
+ call_query.prepare("INSERT INTO calls VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
+ptwrite_query = QSqlQuery(db)
+ptwrite_query.prepare("INSERT INTO ptwrite VALUES (?, ?, ?)")
+cbr_query = QSqlQuery(db)
+cbr_query.prepare("INSERT INTO cbr VALUES (?, ?, ?, ?)")
+mwait_query = QSqlQuery(db)
+mwait_query.prepare("INSERT INTO mwait VALUES (?, ?, ?)")
+pwre_query = QSqlQuery(db)
+pwre_query.prepare("INSERT INTO pwre VALUES (?, ?, ?, ?)")
+exstop_query = QSqlQuery(db)
+exstop_query.prepare("INSERT INTO exstop VALUES (?, ?)")
+pwrx_query = QSqlQuery(db)
+pwrx_query.prepare("INSERT INTO pwrx VALUES (?, ?, ?, ?)")
+context_switch_query = QSqlQuery(db)
+context_switch_query.prepare("INSERT INTO context_switches VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)")
def trace_begin():
printdate("Writing records...")
@@ -392,16 +615,26 @@ def trace_begin():
evsel_table(0, "unknown")
machine_table(0, 0, "unknown")
thread_table(0, 0, 0, -1, -1)
- comm_table(0, "unknown")
+ comm_table(0, "unknown", 0, 0, 0)
dso_table(0, 0, "unknown", "unknown", "")
symbol_table(0, 0, 0, 0, 0, "unknown")
- sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+ sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
if perf_db_export_calls or perf_db_export_callchains:
call_path_table(0, 0, 0, 0)
- call_return_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+ call_return_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
unhandled_count = 0
+def is_table_empty(table_name):
+ do_query(query, 'SELECT * FROM ' + table_name + ' LIMIT 1');
+ if query.next():
+ return False
+ return True
+
+def drop(table_name):
+ do_query(query, 'DROP VIEW ' + table_name + '_view');
+ do_query(query, 'DROP TABLE ' + table_name);
+
def trace_end():
do_query(query, 'END TRANSACTION')
@@ -409,6 +642,22 @@ def trace_end():
if perf_db_export_calls:
do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)')
do_query(query, 'CREATE INDEX pid_idx ON calls (parent_id)')
+ do_query(query, 'ALTER TABLE comms ADD has_calls boolean')
+ do_query(query, 'UPDATE comms SET has_calls = 1 WHERE comms.id IN (SELECT DISTINCT comm_id FROM calls)')
+
+ printdate("Dropping unused tables")
+ if is_table_empty("ptwrite"):
+ drop("ptwrite")
+ if is_table_empty("mwait") and is_table_empty("pwre") and is_table_empty("exstop") and is_table_empty("pwrx"):
+ do_query(query, 'DROP VIEW power_events_view');
+ drop("mwait")
+ drop("pwre")
+ drop("exstop")
+ drop("pwrx")
+ if is_table_empty("cbr"):
+ drop("cbr")
+ if is_table_empty("context_switches"):
+ drop("context_switches")
if (unhandled_count):
printdate("Warning: ", unhandled_count, " unhandled events")
@@ -436,7 +685,7 @@ def thread_table(*x):
bind_exec(thread_query, 5, x)
def comm_table(*x):
- bind_exec(comm_query, 2, x)
+ bind_exec(comm_query, 5, x)
def comm_thread_table(*x):
bind_exec(comm_thread_query, 3, x)
@@ -454,14 +703,94 @@ def sample_table(*x):
if branches:
for xx in x[0:15]:
sample_query.addBindValue(str(xx))
- for xx in x[19:22]:
+ for xx in x[19:24]:
sample_query.addBindValue(str(xx))
do_query_(sample_query)
else:
- bind_exec(sample_query, 22, x)
+ bind_exec(sample_query, 24, x)
def call_path_table(*x):
bind_exec(call_path_query, 4, x)
def call_return_table(*x):
- bind_exec(call_query, 12, x)
+ bind_exec(call_query, 14, x)
+
+def ptwrite(id, raw_buf):
+ data = struct.unpack_from("<IQ", raw_buf)
+ flags = data[0]
+ payload = data[1]
+ exact_ip = flags & 1
+ ptwrite_query.addBindValue(str(id))
+ ptwrite_query.addBindValue(str(payload))
+ ptwrite_query.addBindValue(str(exact_ip))
+ do_query_(ptwrite_query)
+
+def cbr(id, raw_buf):
+ data = struct.unpack_from("<BBBBII", raw_buf)
+ cbr = data[0]
+ MHz = (data[4] + 500) / 1000
+ percent = ((cbr * 1000 / data[2]) + 5) / 10
+ cbr_query.addBindValue(str(id))
+ cbr_query.addBindValue(str(cbr))
+ cbr_query.addBindValue(str(MHz))
+ cbr_query.addBindValue(str(percent))
+ do_query_(cbr_query)
+
+def mwait(id, raw_buf):
+ data = struct.unpack_from("<IQ", raw_buf)
+ payload = data[1]
+ hints = payload & 0xff
+ extensions = (payload >> 32) & 0x3
+ mwait_query.addBindValue(str(id))
+ mwait_query.addBindValue(str(hints))
+ mwait_query.addBindValue(str(extensions))
+ do_query_(mwait_query)
+
+def pwre(id, raw_buf):
+ data = struct.unpack_from("<IQ", raw_buf)
+ payload = data[1]
+ hw = (payload >> 7) & 1
+ cstate = (payload >> 12) & 0xf
+ subcstate = (payload >> 8) & 0xf
+ pwre_query.addBindValue(str(id))
+ pwre_query.addBindValue(str(cstate))
+ pwre_query.addBindValue(str(subcstate))
+ pwre_query.addBindValue(str(hw))
+ do_query_(pwre_query)
+
+def exstop(id, raw_buf):
+ data = struct.unpack_from("<I", raw_buf)
+ flags = data[0]
+ exact_ip = flags & 1
+ exstop_query.addBindValue(str(id))
+ exstop_query.addBindValue(str(exact_ip))
+ do_query_(exstop_query)
+
+def pwrx(id, raw_buf):
+ data = struct.unpack_from("<IQ", raw_buf)
+ payload = data[1]
+ deepest_cstate = payload & 0xf
+ last_cstate = (payload >> 4) & 0xf
+ wake_reason = (payload >> 8) & 0xf
+ pwrx_query.addBindValue(str(id))
+ pwrx_query.addBindValue(str(deepest_cstate))
+ pwrx_query.addBindValue(str(last_cstate))
+ pwrx_query.addBindValue(str(wake_reason))
+ do_query_(pwrx_query)
+
+def synth_data(id, config, raw_buf, *x):
+ if config == 0:
+ ptwrite(id, raw_buf)
+ elif config == 1:
+ mwait(id, raw_buf)
+ elif config == 2:
+ pwre(id, raw_buf)
+ elif config == 3:
+ exstop(id, raw_buf)
+ elif config == 4:
+ pwrx(id, raw_buf)
+ elif config == 5:
+ cbr(id, raw_buf)
+
+def context_switch_table(*x):
+ bind_exec(context_switch_query, 9, x)
diff --git a/tools/perf/scripts/python/exported-sql-viewer.py b/tools/perf/scripts/python/exported-sql-viewer.py
index affed7d149be..61b3911d91e6 100755
--- a/tools/perf/scripts/python/exported-sql-viewer.py
+++ b/tools/perf/scripts/python/exported-sql-viewer.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python
# SPDX-License-Identifier: GPL-2.0
# exported-sql-viewer.py: view data from sql database
# Copyright (c) 2014-2018, Intel Corporation.
@@ -91,6 +91,7 @@
from __future__ import print_function
import sys
+import argparse
import weakref
import threading
import string
@@ -104,10 +105,23 @@ except ImportError:
glb_nsz = 16
import re
import os
-from PySide.QtCore import *
-from PySide.QtGui import *
-from PySide.QtSql import *
+
pyside_version_1 = True
+if not "--pyside-version-1" in sys.argv:
+ try:
+ from PySide2.QtCore import *
+ from PySide2.QtGui import *
+ from PySide2.QtSql import *
+ from PySide2.QtWidgets import *
+ pyside_version_1 = False
+ except:
+ pass
+
+if pyside_version_1:
+ from PySide.QtCore import *
+ from PySide.QtGui import *
+ from PySide.QtSql import *
+
from decimal import *
from ctypes import *
from multiprocessing import Process, Array, Value, Event
@@ -186,9 +200,10 @@ class Thread(QThread):
class TreeModel(QAbstractItemModel):
- def __init__(self, glb, parent=None):
+ def __init__(self, glb, params, parent=None):
super(TreeModel, self).__init__(parent)
self.glb = glb
+ self.params = params
self.root = self.GetRoot()
self.last_row_read = 0
@@ -377,7 +392,7 @@ class FindBar():
self.hbox.addWidget(self.close_button)
self.bar = QWidget()
- self.bar.setLayout(self.hbox);
+ self.bar.setLayout(self.hbox)
self.bar.hide()
def Widget(self):
@@ -385,6 +400,7 @@ class FindBar():
def Activate(self):
self.bar.show()
+ self.textbox.lineEdit().selectAll()
self.textbox.setFocus()
def Deactivate(self):
@@ -449,11 +465,12 @@ class FindBar():
class CallGraphLevelItemBase(object):
- def __init__(self, glb, row, parent_item):
+ def __init__(self, glb, params, row, parent_item):
self.glb = glb
+ self.params = params
self.row = row
self.parent_item = parent_item
- self.query_done = False;
+ self.query_done = False
self.child_count = 0
self.child_items = []
if parent_item:
@@ -489,18 +506,24 @@ class CallGraphLevelItemBase(object):
class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
- def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item):
- super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item)
+ def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item):
+ super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item)
self.comm_id = comm_id
self.thread_id = thread_id
self.call_path_id = call_path_id
+ self.insn_cnt = insn_cnt
+ self.cyc_cnt = cyc_cnt
self.branch_count = branch_count
self.time = time
def Select(self):
- self.query_done = True;
+ self.query_done = True
query = QSqlQuery(self.glb.db)
- QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)"
+ if self.params.have_ipc:
+ ipc_str = ", SUM(insn_count), SUM(cyc_count)"
+ else:
+ ipc_str = ""
+ QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time)" + ipc_str + ", SUM(branch_count)"
" FROM calls"
" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
@@ -511,7 +534,15 @@ class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
" GROUP BY call_path_id, name, short_name"
" ORDER BY call_path_id")
while query.next():
- child_item = CallGraphLevelThreeItem(self.glb, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), int(query.value(5)), self)
+ if self.params.have_ipc:
+ insn_cnt = int(query.value(5))
+ cyc_cnt = int(query.value(6))
+ branch_count = int(query.value(7))
+ else:
+ insn_cnt = 0
+ cyc_cnt = 0
+ branch_count = int(query.value(5))
+ child_item = CallGraphLevelThreeItem(self.glb, self.params, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), insn_cnt, cyc_cnt, branch_count, self)
self.child_items.append(child_item)
self.child_count += 1
@@ -519,48 +550,68 @@ class CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase):
class CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase):
- def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item):
- super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item)
+ def __init__(self, glb, params, row, comm_id, thread_id, call_path_id, name, dso, count, time, insn_cnt, cyc_cnt, branch_count, parent_item):
+ super(CallGraphLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, call_path_id, time, insn_cnt, cyc_cnt, branch_count, parent_item)
dso = dsoname(dso)
- self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
+ if self.params.have_ipc:
+ insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt)
+ cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt)
+ br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count)
+ ipc = CalcIPC(cyc_cnt, insn_cnt)
+ self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(insn_cnt), insn_pcnt, str(cyc_cnt), cyc_pcnt, ipc, str(branch_count), br_pcnt ]
+ else:
+ self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
self.dbid = call_path_id
# Context-sensitive call graph data model level two item
class CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase):
- def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item):
- super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item)
- self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
+ def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item):
+ super(CallGraphLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 1, 0, 0, 0, 0, parent_item)
+ if self.params.have_ipc:
+ self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""]
+ else:
+ self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
self.dbid = thread_id
def Select(self):
super(CallGraphLevelTwoItem, self).Select()
for child_item in self.child_items:
self.time += child_item.time
+ self.insn_cnt += child_item.insn_cnt
+ self.cyc_cnt += child_item.cyc_cnt
self.branch_count += child_item.branch_count
for child_item in self.child_items:
child_item.data[4] = PercentToOneDP(child_item.time, self.time)
- child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
+ if self.params.have_ipc:
+ child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt)
+ child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt)
+ child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count)
+ else:
+ child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
# Context-sensitive call graph data model level one item
class CallGraphLevelOneItem(CallGraphLevelItemBase):
- def __init__(self, glb, row, comm_id, comm, parent_item):
- super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item)
- self.data = [comm, "", "", "", "", "", ""]
+ def __init__(self, glb, params, row, comm_id, comm, parent_item):
+ super(CallGraphLevelOneItem, self).__init__(glb, params, row, parent_item)
+ if self.params.have_ipc:
+ self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""]
+ else:
+ self.data = [comm, "", "", "", "", "", ""]
self.dbid = comm_id
def Select(self):
- self.query_done = True;
+ self.query_done = True
query = QSqlQuery(self.glb.db)
QueryExec(query, "SELECT thread_id, pid, tid"
" FROM comm_threads"
" INNER JOIN threads ON thread_id = threads.id"
" WHERE comm_id = " + str(self.dbid))
while query.next():
- child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
+ child_item = CallGraphLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
self.child_items.append(child_item)
self.child_count += 1
@@ -568,25 +619,35 @@ class CallGraphLevelOneItem(CallGraphLevelItemBase):
class CallGraphRootItem(CallGraphLevelItemBase):
- def __init__(self, glb):
- super(CallGraphRootItem, self).__init__(glb, 0, None)
+ def __init__(self, glb, params):
+ super(CallGraphRootItem, self).__init__(glb, params, 0, None)
self.dbid = 0
- self.query_done = True;
+ self.query_done = True
+ if_has_calls = ""
+ if IsSelectable(glb.db, "comms", columns = "has_calls"):
+ if_has_calls = " WHERE has_calls = TRUE"
query = QSqlQuery(glb.db)
- QueryExec(query, "SELECT id, comm FROM comms")
+ QueryExec(query, "SELECT id, comm FROM comms" + if_has_calls)
while query.next():
if not query.value(0):
continue
- child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self)
+ child_item = CallGraphLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self)
self.child_items.append(child_item)
self.child_count += 1
+# Call graph model parameters
+
+class CallGraphModelParams():
+
+ def __init__(self, glb, parent=None):
+ self.have_ipc = IsSelectable(glb.db, "calls", columns = "insn_count, cyc_count")
+
# Context-sensitive call graph data model base
class CallGraphModelBase(TreeModel):
def __init__(self, glb, parent=None):
- super(CallGraphModelBase, self).__init__(glb, parent)
+ super(CallGraphModelBase, self).__init__(glb, CallGraphModelParams(glb), parent)
def FindSelect(self, value, pattern, query):
if pattern:
@@ -668,17 +729,26 @@ class CallGraphModel(CallGraphModelBase):
super(CallGraphModel, self).__init__(glb, parent)
def GetRoot(self):
- return CallGraphRootItem(self.glb)
+ return CallGraphRootItem(self.glb, self.params)
def columnCount(self, parent=None):
- return 7
+ if self.params.have_ipc:
+ return 12
+ else:
+ return 7
def columnHeader(self, column):
- headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
+ if self.params.have_ipc:
+ headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "]
+ else:
+ headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
return headers[column]
def columnAlignment(self, column):
- alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
+ if self.params.have_ipc:
+ alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
+ else:
+ alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
return alignment[column]
def DoFindSelect(self, query, match):
@@ -715,22 +785,28 @@ class CallGraphModel(CallGraphModelBase):
class CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase):
- def __init__(self, glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item):
- super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, row, parent_item)
+ def __init__(self, glb, params, row, comm_id, thread_id, calls_id, time, insn_cnt, cyc_cnt, branch_count, parent_item):
+ super(CallTreeLevelTwoPlusItemBase, self).__init__(glb, params, row, parent_item)
self.comm_id = comm_id
self.thread_id = thread_id
self.calls_id = calls_id
+ self.insn_cnt = insn_cnt
+ self.cyc_cnt = cyc_cnt
self.branch_count = branch_count
self.time = time
def Select(self):
- self.query_done = True;
+ self.query_done = True
if self.calls_id == 0:
comm_thread = " AND comm_id = " + str(self.comm_id) + " AND thread_id = " + str(self.thread_id)
else:
comm_thread = ""
+ if self.params.have_ipc:
+ ipc_str = ", insn_count, cyc_count"
+ else:
+ ipc_str = ""
query = QSqlQuery(self.glb.db)
- QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time, branch_count"
+ QueryExec(query, "SELECT calls.id, name, short_name, call_time, return_time - call_time" + ipc_str + ", branch_count"
" FROM calls"
" INNER JOIN call_paths ON calls.call_path_id = call_paths.id"
" INNER JOIN symbols ON call_paths.symbol_id = symbols.id"
@@ -738,7 +814,15 @@ class CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase):
" WHERE calls.parent_id = " + str(self.calls_id) + comm_thread +
" ORDER BY call_time, calls.id")
while query.next():
- child_item = CallTreeLevelThreeItem(self.glb, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), int(query.value(5)), self)
+ if self.params.have_ipc:
+ insn_cnt = int(query.value(5))
+ cyc_cnt = int(query.value(6))
+ branch_count = int(query.value(7))
+ else:
+ insn_cnt = 0
+ cyc_cnt = 0
+ branch_count = int(query.value(5))
+ child_item = CallTreeLevelThreeItem(self.glb, self.params, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), insn_cnt, cyc_cnt, branch_count, self)
self.child_items.append(child_item)
self.child_count += 1
@@ -746,48 +830,68 @@ class CallTreeLevelTwoPlusItemBase(CallGraphLevelItemBase):
class CallTreeLevelThreeItem(CallTreeLevelTwoPlusItemBase):
- def __init__(self, glb, row, comm_id, thread_id, calls_id, name, dso, count, time, branch_count, parent_item):
- super(CallTreeLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, calls_id, time, branch_count, parent_item)
+ def __init__(self, glb, params, row, comm_id, thread_id, calls_id, name, dso, count, time, insn_cnt, cyc_cnt, branch_count, parent_item):
+ super(CallTreeLevelThreeItem, self).__init__(glb, params, row, comm_id, thread_id, calls_id, time, insn_cnt, cyc_cnt, branch_count, parent_item)
dso = dsoname(dso)
- self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
+ if self.params.have_ipc:
+ insn_pcnt = PercentToOneDP(insn_cnt, parent_item.insn_cnt)
+ cyc_pcnt = PercentToOneDP(cyc_cnt, parent_item.cyc_cnt)
+ br_pcnt = PercentToOneDP(branch_count, parent_item.branch_count)
+ ipc = CalcIPC(cyc_cnt, insn_cnt)
+ self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(insn_cnt), insn_pcnt, str(cyc_cnt), cyc_pcnt, ipc, str(branch_count), br_pcnt ]
+ else:
+ self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ]
self.dbid = calls_id
# Call tree data model level two item
class CallTreeLevelTwoItem(CallTreeLevelTwoPlusItemBase):
- def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item):
- super(CallTreeLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 0, 0, 0, parent_item)
- self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
+ def __init__(self, glb, params, row, comm_id, thread_id, pid, tid, parent_item):
+ super(CallTreeLevelTwoItem, self).__init__(glb, params, row, comm_id, thread_id, 0, 0, 0, 0, 0, parent_item)
+ if self.params.have_ipc:
+ self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", "", "", "", "", "", ""]
+ else:
+ self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""]
self.dbid = thread_id
def Select(self):
super(CallTreeLevelTwoItem, self).Select()
for child_item in self.child_items:
self.time += child_item.time
+ self.insn_cnt += child_item.insn_cnt
+ self.cyc_cnt += child_item.cyc_cnt
self.branch_count += child_item.branch_count
for child_item in self.child_items:
child_item.data[4] = PercentToOneDP(child_item.time, self.time)
- child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
+ if self.params.have_ipc:
+ child_item.data[6] = PercentToOneDP(child_item.insn_cnt, self.insn_cnt)
+ child_item.data[8] = PercentToOneDP(child_item.cyc_cnt, self.cyc_cnt)
+ child_item.data[11] = PercentToOneDP(child_item.branch_count, self.branch_count)
+ else:
+ child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count)
# Call tree data model level one item
class CallTreeLevelOneItem(CallGraphLevelItemBase):
- def __init__(self, glb, row, comm_id, comm, parent_item):
- super(CallTreeLevelOneItem, self).__init__(glb, row, parent_item)
- self.data = [comm, "", "", "", "", "", ""]
+ def __init__(self, glb, params, row, comm_id, comm, parent_item):
+ super(CallTreeLevelOneItem, self).__init__(glb, params, row, parent_item)
+ if self.params.have_ipc:
+ self.data = [comm, "", "", "", "", "", "", "", "", "", "", ""]
+ else:
+ self.data = [comm, "", "", "", "", "", ""]
self.dbid = comm_id
def Select(self):
- self.query_done = True;
+ self.query_done = True
query = QSqlQuery(self.glb.db)
QueryExec(query, "SELECT thread_id, pid, tid"
" FROM comm_threads"
" INNER JOIN threads ON thread_id = threads.id"
" WHERE comm_id = " + str(self.dbid))
while query.next():
- child_item = CallTreeLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
+ child_item = CallTreeLevelTwoItem(self.glb, self.params, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self)
self.child_items.append(child_item)
self.child_count += 1
@@ -795,16 +899,19 @@ class CallTreeLevelOneItem(CallGraphLevelItemBase):
class CallTreeRootItem(CallGraphLevelItemBase):
- def __init__(self, glb):
- super(CallTreeRootItem, self).__init__(glb, 0, None)
+ def __init__(self, glb, params):
+ super(CallTreeRootItem, self).__init__(glb, params, 0, None)
self.dbid = 0
- self.query_done = True;
+ self.query_done = True
+ if_has_calls = ""
+ if IsSelectable(glb.db, "comms", columns = "has_calls"):
+ if_has_calls = " WHERE has_calls = TRUE"
query = QSqlQuery(glb.db)
- QueryExec(query, "SELECT id, comm FROM comms")
+ QueryExec(query, "SELECT id, comm FROM comms" + if_has_calls)
while query.next():
if not query.value(0):
continue
- child_item = CallTreeLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self)
+ child_item = CallTreeLevelOneItem(glb, params, self.child_count, query.value(0), query.value(1), self)
self.child_items.append(child_item)
self.child_count += 1
@@ -816,17 +923,26 @@ class CallTreeModel(CallGraphModelBase):
super(CallTreeModel, self).__init__(glb, parent)
def GetRoot(self):
- return CallTreeRootItem(self.glb)
+ return CallTreeRootItem(self.glb, self.params)
def columnCount(self, parent=None):
- return 7
+ if self.params.have_ipc:
+ return 12
+ else:
+ return 7
def columnHeader(self, column):
- headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
+ if self.params.have_ipc:
+ headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Insn Cnt", "Insn Cnt (%)", "Cyc Cnt", "Cyc Cnt (%)", "IPC", "Branch Count ", "Branch Count (%) "]
+ else:
+ headers = ["Call Path", "Object", "Call Time", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "]
return headers[column]
def columnAlignment(self, column):
- alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
+ if self.params.have_ipc:
+ alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
+ else:
+ alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ]
return alignment[column]
def DoFindSelect(self, query, match):
@@ -861,7 +977,7 @@ class VBox():
def __init__(self, w1, w2, w3=None):
self.vbox = QWidget()
- self.vbox.setLayout(QVBoxLayout());
+ self.vbox.setLayout(QVBoxLayout())
self.vbox.layout().setContentsMargins(0, 0, 0, 0)
@@ -1281,7 +1397,7 @@ class FetchMoreRecordsBar():
self.hbox.addWidget(self.close_button)
self.bar = QWidget()
- self.bar.setLayout(self.hbox);
+ self.bar.setLayout(self.hbox)
self.bar.show()
self.in_progress = False
@@ -1355,11 +1471,11 @@ class FetchMoreRecordsBar():
class BranchLevelTwoItem():
- def __init__(self, row, text, parent_item):
+ def __init__(self, row, col, text, parent_item):
self.row = row
self.parent_item = parent_item
- self.data = [""] * 8
- self.data[7] = text
+ self.data = [""] * (col + 1)
+ self.data[col] = text
self.level = 2
def getParentItem(self):
@@ -1391,6 +1507,7 @@ class BranchLevelOneItem():
self.dbid = data[0]
self.level = 1
self.query_done = False
+ self.br_col = len(self.data) - 1
def getChildItem(self, row):
return self.child_items[row]
@@ -1471,7 +1588,7 @@ class BranchLevelOneItem():
while k < 15:
byte_str += " "
k += 1
- self.child_items.append(BranchLevelTwoItem(0, byte_str + " " + text, self))
+ self.child_items.append(BranchLevelTwoItem(0, self.br_col, byte_str + " " + text, self))
self.child_count += 1
else:
return
@@ -1522,16 +1639,37 @@ class BranchRootItem():
def getData(self, column):
return ""
+# Calculate instructions per cycle
+
+def CalcIPC(cyc_cnt, insn_cnt):
+ if cyc_cnt and insn_cnt:
+ ipc = Decimal(float(insn_cnt) / cyc_cnt)
+ ipc = str(ipc.quantize(Decimal(".01"), rounding=ROUND_HALF_UP))
+ else:
+ ipc = "0"
+ return ipc
+
# Branch data preparation
-def BranchDataPrep(query):
- data = []
- for i in xrange(0, 8):
- data.append(query.value(i))
+def BranchDataPrepBr(query, data):
data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) +
" (" + dsoname(query.value(11)) + ")" + " -> " +
tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) +
" (" + dsoname(query.value(15)) + ")")
+
+def BranchDataPrepIPC(query, data):
+ insn_cnt = query.value(16)
+ cyc_cnt = query.value(17)
+ ipc = CalcIPC(cyc_cnt, insn_cnt)
+ data.append(insn_cnt)
+ data.append(cyc_cnt)
+ data.append(ipc)
+
+def BranchDataPrep(query):
+ data = []
+ for i in xrange(0, 8):
+ data.append(query.value(i))
+ BranchDataPrepBr(query, data)
return data
def BranchDataPrepWA(query):
@@ -1541,10 +1679,26 @@ def BranchDataPrepWA(query):
data.append("{:>19}".format(query.value(1)))
for i in xrange(2, 8):
data.append(query.value(i))
- data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) +
- " (" + dsoname(query.value(11)) + ")" + " -> " +
- tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) +
- " (" + dsoname(query.value(15)) + ")")
+ BranchDataPrepBr(query, data)
+ return data
+
+def BranchDataWithIPCPrep(query):
+ data = []
+ for i in xrange(0, 8):
+ data.append(query.value(i))
+ BranchDataPrepIPC(query, data)
+ BranchDataPrepBr(query, data)
+ return data
+
+def BranchDataWithIPCPrepWA(query):
+ data = []
+ data.append(query.value(0))
+ # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string
+ data.append("{:>19}".format(query.value(1)))
+ for i in xrange(2, 8):
+ data.append(query.value(i))
+ BranchDataPrepIPC(query, data)
+ BranchDataPrepBr(query, data)
return data
# Branch data model
@@ -1554,14 +1708,24 @@ class BranchModel(TreeModel):
progress = Signal(object)
def __init__(self, glb, event_id, where_clause, parent=None):
- super(BranchModel, self).__init__(glb, parent)
+ super(BranchModel, self).__init__(glb, None, parent)
self.event_id = event_id
self.more = True
self.populated = 0
+ self.have_ipc = IsSelectable(glb.db, "samples", columns = "insn_count, cyc_count")
+ if self.have_ipc:
+ select_ipc = ", insn_count, cyc_count"
+ prep_fn = BranchDataWithIPCPrep
+ prep_wa_fn = BranchDataWithIPCPrepWA
+ else:
+ select_ipc = ""
+ prep_fn = BranchDataPrep
+ prep_wa_fn = BranchDataPrepWA
sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name,"
" CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END,"
" ip, symbols.name, sym_offset, dsos.short_name,"
" to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name"
+ + select_ipc +
" FROM samples"
" INNER JOIN comms ON comm_id = comms.id"
" INNER JOIN threads ON thread_id = threads.id"
@@ -1575,9 +1739,9 @@ class BranchModel(TreeModel):
" ORDER BY samples.id"
" LIMIT " + str(glb_chunk_sz))
if pyside_version_1 and sys.version_info[0] == 3:
- prep = BranchDataPrepWA
+ prep = prep_fn
else:
- prep = BranchDataPrep
+ prep = prep_wa_fn
self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample)
self.fetcher.done.connect(self.Update)
self.fetcher.Fetch(glb_chunk_sz)
@@ -1586,13 +1750,23 @@ class BranchModel(TreeModel):
return BranchRootItem()
def columnCount(self, parent=None):
- return 8
+ if self.have_ipc:
+ return 11
+ else:
+ return 8
def columnHeader(self, column):
- return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column]
+ if self.have_ipc:
+ return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Insn Cnt", "Cyc Cnt", "IPC", "Branch")[column]
+ else:
+ return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column]
def columnFont(self, column):
- if column != 7:
+ if self.have_ipc:
+ br_col = 10
+ else:
+ br_col = 7
+ if column != br_col:
return None
return QFont("Monospace")
@@ -2038,7 +2212,7 @@ class ReportDialogBase(QDialog):
self.vbox.addLayout(self.grid)
self.vbox.addLayout(self.hbox)
- self.setLayout(self.vbox);
+ self.setLayout(self.vbox)
def Ok(self):
vars = self.report_vars
@@ -2100,10 +2274,10 @@ def GetEventList(db):
# Is a table selectable
-def IsSelectable(db, table, sql = ""):
+def IsSelectable(db, table, sql = "", columns = "*"):
query = QSqlQuery(db)
try:
- QueryExec(query, "SELECT * FROM " + table + " " + sql + " LIMIT 1")
+ QueryExec(query, "SELECT " + columns + " FROM " + table + " " + sql + " LIMIT 1")
except:
return False
return True
@@ -2754,7 +2928,7 @@ class WindowMenu():
action = self.window_menu.addAction(label)
action.setCheckable(True)
action.setChecked(sub_window == self.mdi_area.activeSubWindow())
- action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x))
+ action.triggered.connect(lambda a=None,x=nr: self.setActiveSubWindow(x))
self.window_menu.addAction(action)
nr += 1
@@ -2840,6 +3014,12 @@ cd xed
sudo ./mfile.py --prefix=/usr/local install
sudo ldconfig
</pre>
+<h3>Instructions per Cycle (IPC)</h3>
+If available, IPC information is displayed in columns 'insn_cnt', 'cyc_cnt' and 'IPC'.
+<p><b>Intel PT note:</b> The information applies to the blocks of code ending with, and including, that branch.
+Due to the granularity of timing information, the number of cycles for some code blocks will not be known.
+In that case, 'insn_cnt', 'cyc_cnt' and 'IPC' are zero, but when 'IPC' is displayed it covers the period
+since the previous displayed 'IPC'.
<h3>Find</h3>
Ctrl-F displays a Find bar which finds substrings by either an exact match or a regular expression match.
Refer to Python documentation for the regular expression syntax.
@@ -2965,7 +3145,7 @@ class AboutDialog(QDialog):
self.vbox = QVBoxLayout()
self.vbox.addWidget(self.text)
- self.setLayout(self.vbox);
+ self.setLayout(self.vbox)
# Font resize
@@ -3114,14 +3294,14 @@ class MainWindow(QMainWindow):
event = event.split(":")[0]
if event == "branches":
label = "All branches" if branches_events == 1 else "All branches " + "(id=" + dbid + ")"
- reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewBranchView(x), self))
+ reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewBranchView(x), self))
label = "Selected branches" if branches_events == 1 else "Selected branches " + "(id=" + dbid + ")"
- reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda x=dbid: self.NewSelectedBranchView(x), self))
+ reports_menu.addAction(CreateAction(label, "Create a new window displaying branch events", lambda a=None,x=dbid: self.NewSelectedBranchView(x), self))
def TableMenu(self, tables, menu):
table_menu = menu.addMenu("&Tables")
for table in tables:
- table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda t=table: self.NewTableView(t), self))
+ table_menu.addAction(CreateAction(table, "Create a new window containing a table view", lambda a=None,t=table: self.NewTableView(t), self))
def NewCallGraph(self):
CallGraphWindow(self.glb, self)
@@ -3361,18 +3541,27 @@ class DBRef():
# Main
def Main():
- if (len(sys.argv) < 2):
- printerr("Usage is: exported-sql-viewer.py {<database name> | --help-only}");
- raise Exception("Too few arguments")
-
- dbname = sys.argv[1]
- if dbname == "--help-only":
+ usage_str = "exported-sql-viewer.py [--pyside-version-1] <database name>\n" \
+ " or: exported-sql-viewer.py --help-only"
+ ap = argparse.ArgumentParser(usage = usage_str, add_help = False)
+ ap.add_argument("--pyside-version-1", action='store_true')
+ ap.add_argument("dbname", nargs="?")
+ ap.add_argument("--help-only", action='store_true')
+ args = ap.parse_args()
+
+ if args.help_only:
app = QApplication(sys.argv)
mainwindow = HelpOnlyWindow()
mainwindow.show()
err = app.exec_()
sys.exit(err)
+ dbname = args.dbname
+ if dbname is None:
+ ap.print_usage()
+ print("Too few arguments")
+ sys.exit(1)
+
is_sqlite3 = False
try:
f = open(dbname, "rb")
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index 0b2b8305c965..e72accefd669 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
perf-y += builtin-test.o
perf-y += parse-events.o
perf-y += dso-data.o
@@ -50,6 +52,8 @@ perf-y += perf-hooks.o
perf-y += clang.o
perf-y += unit_number__scnprintf.o
perf-y += mem2node.o
+perf-y += map_groups.o
+perf-y += time-utils-test.o
$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build
$(call rule_mkdir)
diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c
index d8426547219b..a9599ab8c471 100644
--- a/tools/perf/tests/attr.c
+++ b/tools/perf/tests/attr.c
@@ -30,8 +30,9 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
-#include "../perf.h"
+#include "../perf-sys.h"
#include <subcmd/exec-cmd.h>
+#include "event.h"
#include "tests.h"
#define ENV "PERF_TEST_ATTR"
diff --git a/tools/perf/tests/backward-ring-buffer.c b/tools/perf/tests/backward-ring-buffer.c
index 1a9c3becf5ff..a637a4a90760 100644
--- a/tools/perf/tests/backward-ring-buffer.c
+++ b/tools/perf/tests/backward-ring-buffer.c
@@ -4,12 +4,14 @@
* beginning
*/
-#include <perf.h>
#include <evlist.h>
#include <sys/prctl.h>
+#include "record.h"
#include "tests.h"
#include "debug.h"
+#include "parse-events.h"
#include <errno.h>
+#include <linux/string.h>
#define NR_ITERS 111
@@ -25,7 +27,7 @@ static void testcase(void)
}
}
-static int count_samples(struct perf_evlist *evlist, int *sample_count,
+static int count_samples(struct evlist *evlist, int *sample_count,
int *comm_count)
{
int i;
@@ -55,7 +57,7 @@ static int count_samples(struct perf_evlist *evlist, int *sample_count,
return TEST_OK;
}
-static int do_test(struct perf_evlist *evlist, int mmap_pages,
+static int do_test(struct evlist *evlist, int mmap_pages,
int *sample_count, int *comm_count)
{
int err;
@@ -68,9 +70,9 @@ static int do_test(struct perf_evlist *evlist, int mmap_pages,
return TEST_FAIL;
}
- perf_evlist__enable(evlist);
+ evlist__enable(evlist);
testcase();
- perf_evlist__disable(evlist);
+ evlist__disable(evlist);
err = count_samples(evlist, sample_count, comm_count);
perf_evlist__munmap(evlist);
@@ -82,8 +84,8 @@ int test__backward_ring_buffer(struct test *test __maybe_unused, int subtest __m
{
int ret = TEST_SKIP, err, sample_count = 0, comm_count = 0;
char pid[16], sbuf[STRERR_BUFSIZE];
- struct perf_evlist *evlist;
- struct perf_evsel *evsel __maybe_unused;
+ struct evlist *evlist;
+ struct evsel *evsel __maybe_unused;
struct parse_events_error parse_error;
struct record_opts opts = {
.target = {
@@ -99,7 +101,7 @@ int test__backward_ring_buffer(struct test *test __maybe_unused, int subtest __m
pid[sizeof(pid) - 1] = '\0';
opts.target.tid = opts.target.pid = pid;
- evlist = perf_evlist__new();
+ evlist = evlist__new();
if (!evlist) {
pr_debug("Not enough memory to create evlist\n");
return TEST_FAIL;
@@ -125,7 +127,7 @@ int test__backward_ring_buffer(struct test *test __maybe_unused, int subtest __m
perf_evlist__config(evlist, &opts, NULL);
- err = perf_evlist__open(evlist);
+ err = evlist__open(evlist);
if (err < 0) {
pr_debug("perf_evlist__open: %s\n",
str_error_r(errno, sbuf, sizeof(sbuf)));
@@ -150,6 +152,6 @@ int test__backward_ring_buffer(struct test *test __maybe_unused, int subtest __m
ret = TEST_OK;
out_delete_evlist:
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
return ret;
}
diff --git a/tools/perf/tests/bitmap.c b/tools/perf/tests/bitmap.c
index 96e7fc1ad3f9..db2aadff3708 100644
--- a/tools/perf/tests/bitmap.c
+++ b/tools/perf/tests/bitmap.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
#include <linux/bitmap.h>
+#include <perf/cpumap.h>
#include "tests.h"
#include "cpumap.h"
#include "debug.h"
@@ -9,7 +10,7 @@
static unsigned long *get_bitmap(const char *str, int nbits)
{
- struct cpu_map *map = cpu_map__new(str);
+ struct perf_cpu_map *map = perf_cpu_map__new(str);
unsigned long *bm = NULL;
int i;
@@ -21,7 +22,7 @@ static unsigned long *get_bitmap(const char *str, int nbits)
}
if (map)
- cpu_map__put(map);
+ perf_cpu_map__put(map);
return bm;
}
diff --git a/tools/perf/tests/bp_account.c b/tools/perf/tests/bp_account.c
index 57fc544aedb0..016bba2c142d 100644
--- a/tools/perf/tests/bp_account.c
+++ b/tools/perf/tests/bp_account.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Powerpc needs __SANE_USERSPACE_TYPES__ before <linux/types.h> to select
* 'int-ll64.h' and avoid compile warnings when printing __u64 with %llu.
@@ -18,7 +19,8 @@
#include "tests.h"
#include "debug.h"
-#include "perf.h"
+#include "event.h"
+#include "../perf-sys.h"
#include "cloexec.h"
volatile long the_var;
diff --git a/tools/perf/tests/bp_signal.c b/tools/perf/tests/bp_signal.c
index 910e25e64188..c1c2c13de254 100644
--- a/tools/perf/tests/bp_signal.c
+++ b/tools/perf/tests/bp_signal.c
@@ -25,7 +25,8 @@
#include "tests.h"
#include "debug.h"
-#include "perf.h"
+#include "event.h"
+#include "perf-sys.h"
#include "cloexec.h"
static int fd1;
diff --git a/tools/perf/tests/bp_signal_overflow.c b/tools/perf/tests/bp_signal_overflow.c
index ca962559e845..eb4dbbddf4ff 100644
--- a/tools/perf/tests/bp_signal_overflow.c
+++ b/tools/perf/tests/bp_signal_overflow.c
@@ -24,7 +24,8 @@
#include "tests.h"
#include "debug.h"
-#include "perf.h"
+#include "event.h"
+#include "../perf-sys.h"
#include "cloexec.h"
static int overflows;
diff --git a/tools/perf/tests/bpf-script-example.c b/tools/perf/tests/bpf-script-example.c
index 1ca5106df5f1..ab4b98b3165d 100644
--- a/tools/perf/tests/bpf-script-example.c
+++ b/tools/perf/tests/bpf-script-example.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* bpf-script-example.c
* Test basic LLVM building
diff --git a/tools/perf/tests/bpf-script-test-kbuild.c b/tools/perf/tests/bpf-script-test-kbuild.c
index ff3ec8337f0a..219673aa278f 100644
--- a/tools/perf/tests/bpf-script-test-kbuild.c
+++ b/tools/perf/tests/bpf-script-test-kbuild.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* bpf-script-test-kbuild.c
* Test include from kernel header
diff --git a/tools/perf/tests/bpf-script-test-prologue.c b/tools/perf/tests/bpf-script-test-prologue.c
index 43f1e16486f4..bd83d364cf30 100644
--- a/tools/perf/tests/bpf-script-test-prologue.c
+++ b/tools/perf/tests/bpf-script-test-prologue.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* bpf-script-test-prologue.c
* Test BPF prologue
diff --git a/tools/perf/tests/bpf-script-test-relocation.c b/tools/perf/tests/bpf-script-test-relocation.c
index 93af77421816..74006e4b2d24 100644
--- a/tools/perf/tests/bpf-script-test-relocation.c
+++ b/tools/perf/tests/bpf-script-test-relocation.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* bpf-script-test-relocation.c
* Test BPF loader checking relocation
diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
index 79b54f8ddebf..fc102e4f403e 100644
--- a/tools/perf/tests/bpf.c
+++ b/tools/perf/tests/bpf.c
@@ -1,20 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <stdio.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <util/record.h>
#include <util/util.h>
#include <util/bpf-loader.h>
#include <util/evlist.h>
#include <linux/bpf.h>
#include <linux/filter.h>
#include <linux/kernel.h>
+#include <linux/string.h>
#include <api/fs/fs.h>
#include <bpf/bpf.h>
#include "tests.h"
#include "llvm.h"
#include "debug.h"
+#include "parse-events.h"
#define NR_ITERS 111
#define PERF_TEST_BPF_PATH "/sys/fs/bpf/perf_test"
@@ -117,7 +121,7 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
char pid[16];
char sbuf[STRERR_BUFSIZE];
- struct perf_evlist *evlist;
+ struct evlist *evlist;
int i, ret = TEST_FAIL, err = 0, count = 0;
struct parse_events_state parse_state;
@@ -139,7 +143,7 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
opts.target.tid = opts.target.pid = pid;
/* Instead of perf_evlist__new_default, don't add default events */
- evlist = perf_evlist__new();
+ evlist = evlist__new();
if (!evlist) {
pr_debug("Not enough memory to create evlist\n");
return TEST_FAIL;
@@ -156,7 +160,7 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
perf_evlist__config(evlist, &opts, NULL);
- err = perf_evlist__open(evlist);
+ err = evlist__open(evlist);
if (err < 0) {
pr_debug("perf_evlist__open: %s\n",
str_error_r(errno, sbuf, sizeof(sbuf)));
@@ -170,9 +174,9 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
goto out_delete_evlist;
}
- perf_evlist__enable(evlist);
+ evlist__enable(evlist);
(*func)();
- perf_evlist__disable(evlist);
+ evlist__disable(evlist);
for (i = 0; i < evlist->nr_mmaps; i++) {
union perf_event *event;
@@ -199,7 +203,7 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
ret = TEST_OK;
out_delete_evlist:
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
return ret;
}
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 9852b5d624a5..55774baffc2a 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -8,6 +8,7 @@
#include <errno.h>
#include <unistd.h>
#include <string.h>
+#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/wait.h>
@@ -21,7 +22,9 @@
#include <subcmd/parse-options.h>
#include "string2.h"
#include "symbol.h"
+#include "util/rlimit.h"
#include <linux/kernel.h>
+#include <linux/string.h>
#include <subcmd/exec-cmd.h>
static bool dont_fork;
@@ -290,6 +293,14 @@ static struct test generic_tests[] = {
.func = test__mem2node,
},
{
+ .desc = "time utils",
+ .func = test__time_utils,
+ },
+ {
+ .desc = "map_groups__merge_in",
+ .func = test__map_groups__merge_in,
+ },
+ {
.func = NULL,
},
};
@@ -430,7 +441,7 @@ static const char *shell_test__description(char *description, size_t size,
description = fgets(description, size, fp);
fclose(fp);
- return description ? trim(description + 1) : NULL;
+ return description ? strim(description + 1) : NULL;
}
#define for_each_shell_test(dir, base, ent) \
@@ -718,6 +729,11 @@ int cmd_test(int argc, const char **argv)
if (skip != NULL)
skiplist = intlist__new(skip);
+ /*
+ * Tests that create BPF maps, for instance, need more than the 64K
+ * default:
+ */
+ rlimit__bump_memlock();
return __cmd_test(argc, argv, skiplist);
}
diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c
index 4ebd2681e760..c1c29e08e7fb 100644
--- a/tools/perf/tests/code-reading.c
+++ b/tools/perf/tests/code-reading.c
@@ -8,8 +8,14 @@
#include <stdio.h>
#include <string.h>
#include <sys/param.h>
+#include <perf/cpumap.h>
+#include <perf/evlist.h>
+#include "debug.h"
+#include "dso.h"
+#include "env.h"
#include "parse-events.h"
+#include "trace-event.h"
#include "evlist.h"
#include "evsel.h"
#include "thread_map.h"
@@ -18,11 +24,12 @@
#include "map.h"
#include "symbol.h"
#include "event.h"
+#include "record.h"
#include "thread.h"
#include "tests.h"
-#include "sane_ctype.h"
+#include <linux/ctype.h>
#define BUFSZ 1024
#define READLEN 128
@@ -362,7 +369,7 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode,
}
static int process_sample_event(struct machine *machine,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
union perf_event *event, struct state *state)
{
struct perf_sample sample;
@@ -385,7 +392,7 @@ static int process_sample_event(struct machine *machine,
return ret;
}
-static int process_event(struct machine *machine, struct perf_evlist *evlist,
+static int process_event(struct machine *machine, struct evlist *evlist,
union perf_event *event, struct state *state)
{
if (event->header.type == PERF_RECORD_SAMPLE)
@@ -408,7 +415,7 @@ static int process_event(struct machine *machine, struct perf_evlist *evlist,
return 0;
}
-static int process_events(struct machine *machine, struct perf_evlist *evlist,
+static int process_events(struct machine *machine, struct evlist *evlist,
struct state *state)
{
union perf_event *event;
@@ -491,6 +498,10 @@ static void fs_something(void)
}
}
+#ifdef __s390x__
+#include "header.h" // for get_cpuid()
+#endif
+
static const char *do_determine_event(bool excl_kernel)
{
const char *event = excl_kernel ? "cycles:u" : "cycles";
@@ -552,10 +563,10 @@ static int do_test_code_reading(bool try_kcore)
struct state state = {
.done_cnt = 0,
};
- struct thread_map *threads = NULL;
- struct cpu_map *cpus = NULL;
- struct perf_evlist *evlist = NULL;
- struct perf_evsel *evsel = NULL;
+ struct perf_thread_map *threads = NULL;
+ struct perf_cpu_map *cpus = NULL;
+ struct evlist *evlist = NULL;
+ struct evsel *evsel = NULL;
int err = -1, ret;
pid_t pid;
struct map *map;
@@ -613,22 +624,22 @@ static int do_test_code_reading(bool try_kcore)
goto out_put;
}
- cpus = cpu_map__new(NULL);
+ cpus = perf_cpu_map__new(NULL);
if (!cpus) {
- pr_debug("cpu_map__new failed\n");
+ pr_debug("perf_cpu_map__new failed\n");
goto out_put;
}
while (1) {
const char *str;
- evlist = perf_evlist__new();
+ evlist = evlist__new();
if (!evlist) {
pr_debug("perf_evlist__new failed\n");
goto out_put;
}
- perf_evlist__set_maps(evlist, cpus, threads);
+ perf_evlist__set_maps(&evlist->core, cpus, threads);
str = do_determine_event(excl_kernel);
pr_debug("Parsing event '%s'\n", str);
@@ -642,11 +653,11 @@ static int do_test_code_reading(bool try_kcore)
evsel = perf_evlist__first(evlist);
- evsel->attr.comm = 1;
- evsel->attr.disabled = 1;
- evsel->attr.enable_on_exec = 0;
+ evsel->core.attr.comm = 1;
+ evsel->core.attr.disabled = 1;
+ evsel->core.attr.enable_on_exec = 0;
- ret = perf_evlist__open(evlist);
+ ret = evlist__open(evlist);
if (ret < 0) {
if (!excl_kernel) {
excl_kernel = true;
@@ -655,10 +666,10 @@ static int do_test_code_reading(bool try_kcore)
* and will be freed by following perf_evlist__set_maps
* call. Getting refference to keep them alive.
*/
- cpu_map__get(cpus);
- thread_map__get(threads);
- perf_evlist__set_maps(evlist, NULL, NULL);
- perf_evlist__delete(evlist);
+ perf_cpu_map__get(cpus);
+ perf_thread_map__get(threads);
+ perf_evlist__set_maps(&evlist->core, NULL, NULL);
+ evlist__delete(evlist);
evlist = NULL;
continue;
}
@@ -680,11 +691,11 @@ static int do_test_code_reading(bool try_kcore)
goto out_put;
}
- perf_evlist__enable(evlist);
+ evlist__enable(evlist);
do_something();
- perf_evlist__disable(evlist);
+ evlist__disable(evlist);
ret = process_events(machine, evlist, &state);
if (ret < 0)
@@ -703,10 +714,10 @@ out_put:
out_err:
if (evlist) {
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
} else {
- cpu_map__put(cpus);
- thread_map__put(threads);
+ perf_cpu_map__put(cpus);
+ perf_thread_map__put(threads);
}
machine__delete_threads(machine);
machine__delete(machine);
diff --git a/tools/perf/tests/cpumap.c b/tools/perf/tests/cpumap.c
index e78b897677bd..39493de50117 100644
--- a/tools/perf/tests/cpumap.c
+++ b/tools/perf/tests/cpumap.c
@@ -5,6 +5,7 @@
#include "event.h"
#include <string.h>
#include <linux/bitops.h>
+#include <perf/cpumap.h>
#include "debug.h"
struct machine;
@@ -14,17 +15,17 @@ static int process_event_mask(struct perf_tool *tool __maybe_unused,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
- struct cpu_map_event *map_event = &event->cpu_map;
- struct cpu_map_mask *mask;
- struct cpu_map_data *data;
- struct cpu_map *map;
+ struct perf_record_cpu_map *map_event = &event->cpu_map;
+ struct perf_record_record_cpu_map *mask;
+ struct perf_record_cpu_map_data *data;
+ struct perf_cpu_map *map;
int i;
data = &map_event->data;
TEST_ASSERT_VAL("wrong type", data->type == PERF_CPU_MAP__MASK);
- mask = (struct cpu_map_mask *)data->data;
+ mask = (struct perf_record_record_cpu_map *)data->data;
TEST_ASSERT_VAL("wrong nr", mask->nr == 1);
@@ -39,7 +40,7 @@ static int process_event_mask(struct perf_tool *tool __maybe_unused,
TEST_ASSERT_VAL("wrong cpu", map->map[i] == i);
}
- cpu_map__put(map);
+ perf_cpu_map__put(map);
return 0;
}
@@ -48,10 +49,10 @@ static int process_event_cpus(struct perf_tool *tool __maybe_unused,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
- struct cpu_map_event *map_event = &event->cpu_map;
+ struct perf_record_cpu_map *map_event = &event->cpu_map;
struct cpu_map_entries *cpus;
- struct cpu_map_data *data;
- struct cpu_map *map;
+ struct perf_record_cpu_map_data *data;
+ struct perf_cpu_map *map;
data = &map_event->data;
@@ -68,36 +69,36 @@ static int process_event_cpus(struct perf_tool *tool __maybe_unused,
TEST_ASSERT_VAL("wrong cpu", map->map[0] == 1);
TEST_ASSERT_VAL("wrong cpu", map->map[1] == 256);
TEST_ASSERT_VAL("wrong refcnt", refcount_read(&map->refcnt) == 1);
- cpu_map__put(map);
+ perf_cpu_map__put(map);
return 0;
}
int test__cpu_map_synthesize(struct test *test __maybe_unused, int subtest __maybe_unused)
{
- struct cpu_map *cpus;
+ struct perf_cpu_map *cpus;
/* This one is better stores in mask. */
- cpus = cpu_map__new("0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19");
+ cpus = perf_cpu_map__new("0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19");
TEST_ASSERT_VAL("failed to synthesize map",
!perf_event__synthesize_cpu_map(NULL, cpus, process_event_mask, NULL));
- cpu_map__put(cpus);
+ perf_cpu_map__put(cpus);
/* This one is better stores in cpu values. */
- cpus = cpu_map__new("1,256");
+ cpus = perf_cpu_map__new("1,256");
TEST_ASSERT_VAL("failed to synthesize map",
!perf_event__synthesize_cpu_map(NULL, cpus, process_event_cpus, NULL));
- cpu_map__put(cpus);
+ perf_cpu_map__put(cpus);
return 0;
}
static int cpu_map_print(const char *str)
{
- struct cpu_map *map = cpu_map__new(str);
+ struct perf_cpu_map *map = perf_cpu_map__new(str);
char buf[100];
if (!map)
diff --git a/tools/perf/tests/dso-data.c b/tools/perf/tests/dso-data.c
index 946ab4b63acd..a4874d4ce7ef 100644
--- a/tools/perf/tests/dso-data.c
+++ b/tools/perf/tests/dso-data.c
@@ -9,6 +9,7 @@
#include <sys/time.h>
#include <sys/resource.h>
#include <api/fs/fs.h>
+#include "dso.h"
#include "util.h"
#include "machine.h"
#include "symbol.h"
diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c
index 077c306c1cae..4125255ff637 100644
--- a/tools/perf/tests/dwarf-unwind.c
+++ b/tools/perf/tests/dwarf-unwind.c
@@ -1,7 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
#include <linux/types.h>
+#include <linux/zalloc.h>
#include <inttypes.h>
+#include <limits.h>
#include <unistd.h>
#include "tests.h"
#include "debug.h"
@@ -115,8 +117,8 @@ noinline int test_dwarf_unwind__thread(struct thread *thread)
}
out:
- free(sample.user_stack.data);
- free(sample.user_regs.regs);
+ zfree(&sample.user_stack.data);
+ zfree(&sample.user_regs.regs);
return err;
}
diff --git a/tools/perf/tests/event-times.c b/tools/perf/tests/event-times.c
index 1a2686f1fcf0..d824a726906c 100644
--- a/tools/perf/tests/event-times.c
+++ b/tools/perf/tests/event-times.c
@@ -1,20 +1,23 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
+#include <linux/string.h>
#include <errno.h>
#include <inttypes.h>
#include <string.h>
#include <sys/wait.h>
+#include <perf/cpumap.h>
#include "tests.h"
#include "evlist.h"
#include "evsel.h"
#include "util.h"
#include "debug.h"
+#include "parse-events.h"
#include "thread_map.h"
#include "target.h"
-static int attach__enable_on_exec(struct perf_evlist *evlist)
+static int attach__enable_on_exec(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__last(evlist);
+ struct evsel *evsel = perf_evlist__last(evlist);
struct target target = {
.uid = UINT_MAX,
};
@@ -36,9 +39,9 @@ static int attach__enable_on_exec(struct perf_evlist *evlist)
return err;
}
- evsel->attr.enable_on_exec = 1;
+ evsel->core.attr.enable_on_exec = 1;
- err = perf_evlist__open(evlist);
+ err = evlist__open(evlist);
if (err < 0) {
pr_debug("perf_evlist__open: %s\n",
str_error_r(errno, sbuf, sizeof(sbuf)));
@@ -48,16 +51,16 @@ static int attach__enable_on_exec(struct perf_evlist *evlist)
return perf_evlist__start_workload(evlist) == 1 ? TEST_OK : TEST_FAIL;
}
-static int detach__enable_on_exec(struct perf_evlist *evlist)
+static int detach__enable_on_exec(struct evlist *evlist)
{
waitpid(evlist->workload.pid, NULL, 0);
return 0;
}
-static int attach__current_disabled(struct perf_evlist *evlist)
+static int attach__current_disabled(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__last(evlist);
- struct thread_map *threads;
+ struct evsel *evsel = perf_evlist__last(evlist);
+ struct perf_thread_map *threads;
int err;
pr_debug("attaching to current thread as disabled\n");
@@ -68,7 +71,7 @@ static int attach__current_disabled(struct perf_evlist *evlist)
return -1;
}
- evsel->attr.disabled = 1;
+ evsel->core.attr.disabled = 1;
err = perf_evsel__open_per_thread(evsel, threads);
if (err) {
@@ -76,14 +79,14 @@ static int attach__current_disabled(struct perf_evlist *evlist)
return err;
}
- thread_map__put(threads);
- return perf_evsel__enable(evsel) == 0 ? TEST_OK : TEST_FAIL;
+ perf_thread_map__put(threads);
+ return evsel__enable(evsel) == 0 ? TEST_OK : TEST_FAIL;
}
-static int attach__current_enabled(struct perf_evlist *evlist)
+static int attach__current_enabled(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__last(evlist);
- struct thread_map *threads;
+ struct evsel *evsel = perf_evlist__last(evlist);
+ struct perf_thread_map *threads;
int err;
pr_debug("attaching to current thread as enabled\n");
@@ -96,32 +99,32 @@ static int attach__current_enabled(struct perf_evlist *evlist)
err = perf_evsel__open_per_thread(evsel, threads);
- thread_map__put(threads);
+ perf_thread_map__put(threads);
return err == 0 ? TEST_OK : TEST_FAIL;
}
-static int detach__disable(struct perf_evlist *evlist)
+static int detach__disable(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__last(evlist);
+ struct evsel *evsel = perf_evlist__last(evlist);
- return perf_evsel__enable(evsel);
+ return evsel__enable(evsel);
}
-static int attach__cpu_disabled(struct perf_evlist *evlist)
+static int attach__cpu_disabled(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__last(evlist);
- struct cpu_map *cpus;
+ struct evsel *evsel = perf_evlist__last(evlist);
+ struct perf_cpu_map *cpus;
int err;
pr_debug("attaching to CPU 0 as enabled\n");
- cpus = cpu_map__new("0");
+ cpus = perf_cpu_map__new("0");
if (cpus == NULL) {
- pr_debug("failed to call cpu_map__new\n");
+ pr_debug("failed to call perf_cpu_map__new\n");
return -1;
}
- evsel->attr.disabled = 1;
+ evsel->core.attr.disabled = 1;
err = perf_evsel__open_per_cpu(evsel, cpus);
if (err) {
@@ -132,21 +135,21 @@ static int attach__cpu_disabled(struct perf_evlist *evlist)
return err;
}
- cpu_map__put(cpus);
- return perf_evsel__enable(evsel);
+ perf_cpu_map__put(cpus);
+ return evsel__enable(evsel);
}
-static int attach__cpu_enabled(struct perf_evlist *evlist)
+static int attach__cpu_enabled(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__last(evlist);
- struct cpu_map *cpus;
+ struct evsel *evsel = perf_evlist__last(evlist);
+ struct perf_cpu_map *cpus;
int err;
pr_debug("attaching to CPU 0 as enabled\n");
- cpus = cpu_map__new("0");
+ cpus = perf_cpu_map__new("0");
if (cpus == NULL) {
- pr_debug("failed to call cpu_map__new\n");
+ pr_debug("failed to call perf_cpu_map__new\n");
return -1;
}
@@ -154,19 +157,19 @@ static int attach__cpu_enabled(struct perf_evlist *evlist)
if (err == -EACCES)
return TEST_SKIP;
- cpu_map__put(cpus);
+ perf_cpu_map__put(cpus);
return err ? TEST_FAIL : TEST_OK;
}
-static int test_times(int (attach)(struct perf_evlist *),
- int (detach)(struct perf_evlist *))
+static int test_times(int (attach)(struct evlist *),
+ int (detach)(struct evlist *))
{
struct perf_counts_values count;
- struct perf_evlist *evlist = NULL;
- struct perf_evsel *evsel;
+ struct evlist *evlist = NULL;
+ struct evsel *evsel;
int err = -1, i;
- evlist = perf_evlist__new();
+ evlist = evlist__new();
if (!evlist) {
pr_debug("failed to create event list\n");
goto out_err;
@@ -179,7 +182,7 @@ static int test_times(int (attach)(struct perf_evlist *),
}
evsel = perf_evlist__last(evlist);
- evsel->attr.read_format |=
+ evsel->core.attr.read_format |=
PERF_FORMAT_TOTAL_TIME_ENABLED |
PERF_FORMAT_TOTAL_TIME_RUNNING;
@@ -195,7 +198,7 @@ static int test_times(int (attach)(struct perf_evlist *),
TEST_ASSERT_VAL("failed to detach", !detach(evlist));
- perf_evsel__read(evsel, 0, 0, &count);
+ perf_evsel__read(&evsel->core, 0, 0, &count);
err = !(count.ena == count.run);
@@ -204,7 +207,7 @@ static int test_times(int (attach)(struct perf_evlist *),
count.ena, count.run);
out_err:
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
return !err ? TEST_OK : TEST_FAIL;
}
diff --git a/tools/perf/tests/event_update.c b/tools/perf/tests/event_update.c
index f14dcd613438..cac4290e233a 100644
--- a/tools/perf/tests/event_update.c
+++ b/tools/perf/tests/event_update.c
@@ -1,8 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
+#include <perf/cpumap.h>
+#include <string.h>
#include "evlist.h"
#include "evsel.h"
+#include "header.h"
#include "machine.h"
+#include "tool.h"
#include "tests.h"
#include "debug.h"
@@ -11,7 +15,7 @@ static int process_event_unit(struct perf_tool *tool __maybe_unused,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
- struct event_update_event *ev = (struct event_update_event *) event;
+ struct perf_record_event_update *ev = (struct perf_record_event_update *)event;
TEST_ASSERT_VAL("wrong id", ev->id == 123);
TEST_ASSERT_VAL("wrong id", ev->type == PERF_EVENT_UPDATE__UNIT);
@@ -24,10 +28,10 @@ static int process_event_scale(struct perf_tool *tool __maybe_unused,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
- struct event_update_event *ev = (struct event_update_event *) event;
- struct event_update_event_scale *ev_data;
+ struct perf_record_event_update *ev = (struct perf_record_event_update *)event;
+ struct perf_record_event_update_scale *ev_data;
- ev_data = (struct event_update_event_scale *) ev->data;
+ ev_data = (struct perf_record_event_update_scale *)ev->data;
TEST_ASSERT_VAL("wrong id", ev->id == 123);
TEST_ASSERT_VAL("wrong id", ev->type == PERF_EVENT_UPDATE__SCALE);
@@ -46,7 +50,7 @@ static int process_event_name(struct perf_tool *tool,
struct machine *machine __maybe_unused)
{
struct event_name *tmp = container_of(tool, struct event_name, tool);
- struct event_update_event *ev = (struct event_update_event*) event;
+ struct perf_record_event_update *ev = (struct perf_record_event_update *)event;
TEST_ASSERT_VAL("wrong id", ev->id == 123);
TEST_ASSERT_VAL("wrong id", ev->type == PERF_EVENT_UPDATE__NAME);
@@ -59,11 +63,11 @@ static int process_event_cpus(struct perf_tool *tool __maybe_unused,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
- struct event_update_event *ev = (struct event_update_event*) event;
- struct event_update_event_cpus *ev_data;
- struct cpu_map *map;
+ struct perf_record_event_update *ev = (struct perf_record_event_update *)event;
+ struct perf_record_event_update_cpus *ev_data;
+ struct perf_cpu_map *map;
- ev_data = (struct event_update_event_cpus*) ev->data;
+ ev_data = (struct perf_record_event_update_cpus *) ev->data;
map = cpu_map__new_data(&ev_data->cpus);
@@ -73,14 +77,14 @@ static int process_event_cpus(struct perf_tool *tool __maybe_unused,
TEST_ASSERT_VAL("wrong cpus", map->map[0] == 1);
TEST_ASSERT_VAL("wrong cpus", map->map[1] == 2);
TEST_ASSERT_VAL("wrong cpus", map->map[2] == 3);
- cpu_map__put(map);
+ perf_cpu_map__put(map);
return 0;
}
int test__event_update(struct test *test __maybe_unused, int subtest __maybe_unused)
{
- struct perf_evlist *evlist;
- struct perf_evsel *evsel;
+ struct evlist *evlist;
+ struct evsel *evsel;
struct event_name tmp;
evlist = perf_evlist__new_default();
@@ -108,11 +112,11 @@ int test__event_update(struct test *test __maybe_unused, int subtest __maybe_unu
TEST_ASSERT_VAL("failed to synthesize attr update name",
!perf_event__synthesize_event_update_name(&tmp.tool, evsel, process_event_name));
- evsel->own_cpus = cpu_map__new("1,2,3");
+ evsel->core.own_cpus = perf_cpu_map__new("1,2,3");
TEST_ASSERT_VAL("failed to synthesize attr update cpus",
!perf_event__synthesize_event_update_cpus(&tmp.tool, evsel, process_event_cpus));
- cpu_map__put(evsel->own_cpus);
+ perf_cpu_map__put(evsel->core.own_cpus);
return 0;
}
diff --git a/tools/perf/tests/evsel-roundtrip-name.c b/tools/perf/tests/evsel-roundtrip-name.c
index a104728ebf25..5330f106a6ee 100644
--- a/tools/perf/tests/evsel-roundtrip-name.c
+++ b/tools/perf/tests/evsel-roundtrip-name.c
@@ -11,8 +11,8 @@ static int perf_evsel__roundtrip_cache_name_test(void)
{
char name[128];
int type, op, err = 0, ret = 0, i, idx;
- struct perf_evsel *evsel;
- struct perf_evlist *evlist = perf_evlist__new();
+ struct evsel *evsel;
+ struct evlist *evlist = evlist__new();
if (evlist == NULL)
return -ENOMEM;
@@ -60,15 +60,15 @@ static int perf_evsel__roundtrip_cache_name_test(void)
}
}
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
return ret;
}
static int __perf_evsel__name_array_test(const char *names[], int nr_names)
{
int i, err;
- struct perf_evsel *evsel;
- struct perf_evlist *evlist = perf_evlist__new();
+ struct evsel *evsel;
+ struct evlist *evlist = evlist__new();
if (evlist == NULL)
return -ENOMEM;
@@ -91,7 +91,7 @@ static int __perf_evsel__name_array_test(const char *names[], int nr_names)
}
out_delete_evlist:
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
return err;
}
diff --git a/tools/perf/tests/evsel-tp-sched.c b/tools/perf/tests/evsel-tp-sched.c
index 71f60c0f9faa..261e6eaaee99 100644
--- a/tools/perf/tests/evsel-tp-sched.c
+++ b/tools/perf/tests/evsel-tp-sched.c
@@ -5,7 +5,7 @@
#include "tests.h"
#include "debug.h"
-static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name,
+static int perf_evsel__test_field(struct evsel *evsel, const char *name,
int size, bool should_be_signed)
{
struct tep_format_field *field = perf_evsel__field(evsel, name);
@@ -35,7 +35,7 @@ static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name,
int test__perf_evsel__tp_sched_test(struct test *test __maybe_unused, int subtest __maybe_unused)
{
- struct perf_evsel *evsel = perf_evsel__newtp("sched", "sched_switch");
+ struct evsel *evsel = perf_evsel__newtp("sched", "sched_switch");
int ret = 0;
if (IS_ERR(evsel)) {
@@ -64,7 +64,7 @@ int test__perf_evsel__tp_sched_test(struct test *test __maybe_unused, int subtes
if (perf_evsel__test_field(evsel, "next_prio", 4, true))
ret = -1;
- perf_evsel__delete(evsel);
+ evsel__delete(evsel);
evsel = perf_evsel__newtp("sched", "sched_wakeup");
@@ -85,6 +85,6 @@ int test__perf_evsel__tp_sched_test(struct test *test __maybe_unused, int subtes
if (perf_evsel__test_field(evsel, "target_cpu", 4, true))
ret = -1;
- perf_evsel__delete(evsel);
+ evsel__delete(evsel);
return ret;
}
diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 9acc1e80b936..87843af4c118 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -3,6 +3,8 @@
#include "util/expr.h"
#include "tests.h"
#include <stdlib.h>
+#include <string.h>
+#include <linux/zalloc.h>
static int test(struct parse_ctx *ctx, const char *e, double val2)
{
@@ -58,7 +60,7 @@ int test__expr(struct test *t __maybe_unused, int subtest __maybe_unused)
TEST_ASSERT_VAL("find other", other[3] == NULL);
for (i = 0; i < num_other; i++)
- free((void *)other[i]);
+ zfree(&other[i]);
free((void *)other);
return 0;
diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c
index 469958cd7fe0..de110d8f169b 100644
--- a/tools/perf/tests/hists_common.c
+++ b/tools/perf/tests/hists_common.c
@@ -1,12 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
#include <inttypes.h>
-#include "perf.h"
#include "util/debug.h"
+#include "util/dso.h"
#include "util/map.h"
#include "util/symbol.h"
#include "util/sort.h"
#include "util/evsel.h"
-#include "util/evlist.h"
#include "util/machine.h"
#include "util/thread.h"
#include "tests/hists_common.h"
diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c
index 7a2eed6c783e..fa55b7bad3af 100644
--- a/tools/perf/tests/hists_cumulate.c
+++ b/tools/perf/tests/hists_cumulate.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
-#include "perf.h"
#include "util/debug.h"
+#include "util/dso.h"
#include "util/event.h"
#include "util/map.h"
#include "util/symbol.h"
@@ -80,7 +80,7 @@ static u64 fake_callchains[][10] = {
static int add_hist_entries(struct hists *hists, struct machine *machine)
{
struct addr_location al;
- struct perf_evsel *evsel = hists_to_evsel(hists);
+ struct evsel *evsel = hists_to_evsel(hists);
struct perf_sample sample = { .period = 1000, };
size_t i;
@@ -147,7 +147,7 @@ static void del_hist_entries(struct hists *hists)
}
}
-typedef int (*test_fn_t)(struct perf_evsel *, struct machine *);
+typedef int (*test_fn_t)(struct evsel *, struct machine *);
#define COMM(he) (thread__comm_str(he->thread))
#define DSO(he) (he->ms.map->dso->short_name)
@@ -247,7 +247,7 @@ static int do_test(struct hists *hists, struct result *expected, size_t nr_expec
}
/* NO callchain + NO children */
-static int test1(struct perf_evsel *evsel, struct machine *machine)
+static int test1(struct evsel *evsel, struct machine *machine)
{
int err;
struct hists *hists = evsel__hists(evsel);
@@ -298,7 +298,7 @@ out:
}
/* callcain + NO children */
-static int test2(struct perf_evsel *evsel, struct machine *machine)
+static int test2(struct evsel *evsel, struct machine *machine)
{
int err;
struct hists *hists = evsel__hists(evsel);
@@ -446,7 +446,7 @@ out:
}
/* NO callchain + children */
-static int test3(struct perf_evsel *evsel, struct machine *machine)
+static int test3(struct evsel *evsel, struct machine *machine)
{
int err;
struct hists *hists = evsel__hists(evsel);
@@ -503,7 +503,7 @@ out:
}
/* callchain + children */
-static int test4(struct perf_evsel *evsel, struct machine *machine)
+static int test4(struct evsel *evsel, struct machine *machine)
{
int err;
struct hists *hists = evsel__hists(evsel);
@@ -694,8 +694,8 @@ int test__hists_cumulate(struct test *test __maybe_unused, int subtest __maybe_u
int err = TEST_FAIL;
struct machines machines;
struct machine *machine;
- struct perf_evsel *evsel;
- struct perf_evlist *evlist = perf_evlist__new();
+ struct evsel *evsel;
+ struct evlist *evlist = evlist__new();
size_t i;
test_fn_t testcases[] = {
test1,
@@ -731,7 +731,7 @@ int test__hists_cumulate(struct test *test __maybe_unused, int subtest __maybe_u
out:
/* tear down everything */
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
machines__exit(&machines);
return err;
diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c
index 975844807fe2..618b51ffcc01 100644
--- a/tools/perf/tests/hists_filter.c
+++ b/tools/perf/tests/hists_filter.c
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
-#include "perf.h"
#include "util/debug.h"
#include "util/map.h"
#include "util/symbol.h"
@@ -8,7 +7,6 @@
#include "util/event.h"
#include "util/evlist.h"
#include "util/machine.h"
-#include "util/thread.h"
#include "util/parse-events.h"
#include "tests/tests.h"
#include "tests/hists_common.h"
@@ -47,10 +45,10 @@ static struct sample fake_samples[] = {
{ .pid = FAKE_PID_BASH, .ip = FAKE_IP_KERNEL_PAGE_FAULT, .socket = 3 },
};
-static int add_hist_entries(struct perf_evlist *evlist,
+static int add_hist_entries(struct evlist *evlist,
struct machine *machine)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct addr_location al;
struct perf_sample sample = { .period = 100, };
size_t i;
@@ -108,8 +106,8 @@ int test__hists_filter(struct test *test __maybe_unused, int subtest __maybe_unu
int err = TEST_FAIL;
struct machines machines;
struct machine *machine;
- struct perf_evsel *evsel;
- struct perf_evlist *evlist = perf_evlist__new();
+ struct evsel *evsel;
+ struct evlist *evlist = evlist__new();
TEST_ASSERT_VAL("No memory", evlist);
@@ -321,7 +319,7 @@ int test__hists_filter(struct test *test __maybe_unused, int subtest __maybe_unu
out:
/* tear down everything */
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
reset_output_field();
machines__exit(&machines);
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c
index af633db63f4d..8be4d0b61e3a 100644
--- a/tools/perf/tests/hists_link.c
+++ b/tools/perf/tests/hists_link.c
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
-#include "perf.h"
#include "tests.h"
#include "debug.h"
#include "symbol.h"
@@ -7,7 +6,6 @@
#include "evsel.h"
#include "evlist.h"
#include "machine.h"
-#include "thread.h"
#include "parse-events.h"
#include "hists_common.h"
#include <errno.h>
@@ -62,9 +60,9 @@ static struct sample fake_samples[][5] = {
},
};
-static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
+static int add_hist_entries(struct evlist *evlist, struct machine *machine)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct addr_location al;
struct hist_entry *he;
struct perf_sample sample = { .period = 1, .weight = 1, };
@@ -271,8 +269,8 @@ int test__hists_link(struct test *test __maybe_unused, int subtest __maybe_unuse
struct hists *hists, *first_hists;
struct machines machines;
struct machine *machine = NULL;
- struct perf_evsel *evsel, *first;
- struct perf_evlist *evlist = perf_evlist__new();
+ struct evsel *evsel, *first;
+ struct evlist *evlist = evlist__new();
if (evlist == NULL)
return -ENOMEM;
@@ -334,7 +332,7 @@ int test__hists_link(struct test *test __maybe_unused, int subtest __maybe_unuse
out:
/* tear down everything */
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
reset_output_field();
machines__exit(&machines);
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c
index 0a510c524a5d..3f6dfa212260 100644
--- a/tools/perf/tests/hists_output.c
+++ b/tools/perf/tests/hists_output.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
-#include "perf.h"
#include "util/debug.h"
+#include "util/dso.h"
#include "util/event.h"
#include "util/map.h"
#include "util/symbol.h"
@@ -50,7 +50,7 @@ static struct sample fake_samples[] = {
static int add_hist_entries(struct hists *hists, struct machine *machine)
{
struct addr_location al;
- struct perf_evsel *evsel = hists_to_evsel(hists);
+ struct evsel *evsel = hists_to_evsel(hists);
struct perf_sample sample = { .period = 100, };
size_t i;
@@ -113,7 +113,7 @@ static void del_hist_entries(struct hists *hists)
}
}
-typedef int (*test_fn_t)(struct perf_evsel *, struct machine *);
+typedef int (*test_fn_t)(struct evsel *, struct machine *);
#define COMM(he) (thread__comm_str(he->thread))
#define DSO(he) (he->ms.map->dso->short_name)
@@ -122,7 +122,7 @@ typedef int (*test_fn_t)(struct perf_evsel *, struct machine *);
#define PID(he) (he->thread->tid)
/* default sort keys (no field) */
-static int test1(struct perf_evsel *evsel, struct machine *machine)
+static int test1(struct evsel *evsel, struct machine *machine)
{
int err;
struct hists *hists = evsel__hists(evsel);
@@ -224,7 +224,7 @@ out:
}
/* mixed fields and sort keys */
-static int test2(struct perf_evsel *evsel, struct machine *machine)
+static int test2(struct evsel *evsel, struct machine *machine)
{
int err;
struct hists *hists = evsel__hists(evsel);
@@ -280,7 +280,7 @@ out:
}
/* fields only (no sort key) */
-static int test3(struct perf_evsel *evsel, struct machine *machine)
+static int test3(struct evsel *evsel, struct machine *machine)
{
int err;
struct hists *hists = evsel__hists(evsel);
@@ -354,7 +354,7 @@ out:
}
/* handle duplicate 'dso' field */
-static int test4(struct perf_evsel *evsel, struct machine *machine)
+static int test4(struct evsel *evsel, struct machine *machine)
{
int err;
struct hists *hists = evsel__hists(evsel);
@@ -456,7 +456,7 @@ out:
}
/* full sort keys w/o overhead field */
-static int test5(struct perf_evsel *evsel, struct machine *machine)
+static int test5(struct evsel *evsel, struct machine *machine)
{
int err;
struct hists *hists = evsel__hists(evsel);
@@ -580,8 +580,8 @@ int test__hists_output(struct test *test __maybe_unused, int subtest __maybe_unu
int err = TEST_FAIL;
struct machines machines;
struct machine *machine;
- struct perf_evsel *evsel;
- struct perf_evlist *evlist = perf_evlist__new();
+ struct evsel *evsel;
+ struct evlist *evlist = evlist__new();
size_t i;
test_fn_t testcases[] = {
test1,
@@ -618,7 +618,7 @@ int test__hists_output(struct test *test __maybe_unused, int subtest __maybe_unu
out:
/* tear down everything */
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
machines__exit(&machines);
return err;
diff --git a/tools/perf/tests/keep-tracking.c b/tools/perf/tests/keep-tracking.c
index 17c46f3e6f1e..9f0762d987fa 100644
--- a/tools/perf/tests/keep-tracking.c
+++ b/tools/perf/tests/keep-tracking.c
@@ -1,11 +1,16 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/types.h>
+#include <limits.h>
#include <unistd.h>
#include <sys/prctl.h>
+#include <perf/cpumap.h>
+#include <perf/evlist.h>
+#include "debug.h"
#include "parse-events.h"
#include "evlist.h"
#include "evsel.h"
+#include "record.h"
#include "thread_map.h"
#include "cpumap.h"
#include "tests.h"
@@ -24,7 +29,7 @@
} \
}
-static int find_comm(struct perf_evlist *evlist, const char *comm)
+static int find_comm(struct evlist *evlist, const char *comm)
{
union perf_event *event;
struct perf_mmap *md;
@@ -65,23 +70,23 @@ int test__keep_tracking(struct test *test __maybe_unused, int subtest __maybe_un
.uses_mmap = true,
},
};
- struct thread_map *threads = NULL;
- struct cpu_map *cpus = NULL;
- struct perf_evlist *evlist = NULL;
- struct perf_evsel *evsel = NULL;
+ struct perf_thread_map *threads = NULL;
+ struct perf_cpu_map *cpus = NULL;
+ struct evlist *evlist = NULL;
+ struct evsel *evsel = NULL;
int found, err = -1;
const char *comm;
threads = thread_map__new(-1, getpid(), UINT_MAX);
CHECK_NOT_NULL__(threads);
- cpus = cpu_map__new(NULL);
+ cpus = perf_cpu_map__new(NULL);
CHECK_NOT_NULL__(cpus);
- evlist = perf_evlist__new();
+ evlist = evlist__new();
CHECK_NOT_NULL__(evlist);
- perf_evlist__set_maps(evlist, cpus, threads);
+ perf_evlist__set_maps(&evlist->core, cpus, threads);
CHECK__(parse_events(evlist, "dummy:u", NULL));
CHECK__(parse_events(evlist, "cycles:u", NULL));
@@ -90,11 +95,11 @@ int test__keep_tracking(struct test *test __maybe_unused, int subtest __maybe_un
evsel = perf_evlist__first(evlist);
- evsel->attr.comm = 1;
- evsel->attr.disabled = 1;
- evsel->attr.enable_on_exec = 0;
+ evsel->core.attr.comm = 1;
+ evsel->core.attr.disabled = 1;
+ evsel->core.attr.enable_on_exec = 0;
- if (perf_evlist__open(evlist) < 0) {
+ if (evlist__open(evlist) < 0) {
pr_debug("Unable to open dummy and cycles event\n");
err = TEST_SKIP;
goto out_err;
@@ -107,12 +112,12 @@ int test__keep_tracking(struct test *test __maybe_unused, int subtest __maybe_un
* enabled.
*/
- perf_evlist__enable(evlist);
+ evlist__enable(evlist);
comm = "Test COMM 1";
CHECK__(prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0));
- perf_evlist__disable(evlist);
+ evlist__disable(evlist);
found = find_comm(evlist, comm);
if (found != 1) {
@@ -125,16 +130,16 @@ int test__keep_tracking(struct test *test __maybe_unused, int subtest __maybe_un
* disabled with the dummy event still enabled.
*/
- perf_evlist__enable(evlist);
+ evlist__enable(evlist);
evsel = perf_evlist__last(evlist);
- CHECK__(perf_evsel__disable(evsel));
+ CHECK__(evsel__disable(evsel));
comm = "Test COMM 2";
CHECK__(prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0));
- perf_evlist__disable(evlist);
+ evlist__disable(evlist);
found = find_comm(evlist, comm);
if (found != 1) {
@@ -146,11 +151,11 @@ int test__keep_tracking(struct test *test __maybe_unused, int subtest __maybe_un
out_err:
if (evlist) {
- perf_evlist__disable(evlist);
- perf_evlist__delete(evlist);
+ evlist__disable(evlist);
+ evlist__delete(evlist);
} else {
- cpu_map__put(cpus);
- thread_map__put(threads);
+ perf_cpu_map__put(cpus);
+ perf_thread_map__put(threads);
}
return err;
diff --git a/tools/perf/tests/kmod-path.c b/tools/perf/tests/kmod-path.c
index 0579a70bbbff..e483210b176b 100644
--- a/tools/perf/tests/kmod-path.c
+++ b/tools/perf/tests/kmod-path.c
@@ -1,9 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdbool.h>
#include <stdlib.h>
+#include <string.h>
#include "tests.h"
#include "dso.h"
#include "debug.h"
+#include "event.h"
static int test(const char *path, bool alloc_name, bool kmod,
int comp, const char *name)
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
index a039f93199e5..022e4c9cf092 100644
--- a/tools/perf/tests/llvm.c
+++ b/tools/perf/tests/llvm.c
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <bpf/libbpf.h>
#include <util/llvm-utils.h>
-#include <util/cache.h>
#include "llvm.h"
#include "tests.h"
#include "debug.h"
diff --git a/tools/perf/tests/make b/tools/perf/tests/make
index 5363a12a8b9b..70c48475896d 100644
--- a/tools/perf/tests/make
+++ b/tools/perf/tests/make
@@ -108,6 +108,7 @@ make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1
make_minimal += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1
make_minimal += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1 NO_LIBBPF=1
make_minimal += NO_LIBCRYPTO=1 NO_SDT=1 NO_JVMTI=1 NO_LIBZSTD=1
+make_minimal += NO_LIBCAP=1
# $(run) contains all available tests
run := make_pure
diff --git a/tools/perf/tests/map_groups.c b/tools/perf/tests/map_groups.c
new file mode 100644
index 000000000000..594fdaca4f71
--- /dev/null
+++ b/tools/perf/tests/map_groups.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include "tests.h"
+#include "map.h"
+#include "map_groups.h"
+#include "dso.h"
+#include "debug.h"
+
+struct map_def {
+ const char *name;
+ u64 start;
+ u64 end;
+};
+
+static int check_maps(struct map_def *merged, unsigned int size, struct map_groups *mg)
+{
+ struct map *map;
+ unsigned int i = 0;
+
+ map = map_groups__first(mg);
+ while (map) {
+ TEST_ASSERT_VAL("wrong map start", map->start == merged[i].start);
+ TEST_ASSERT_VAL("wrong map end", map->end == merged[i].end);
+ TEST_ASSERT_VAL("wrong map name", !strcmp(map->dso->name, merged[i].name));
+ TEST_ASSERT_VAL("wrong map refcnt", refcount_read(&map->refcnt) == 2);
+
+ i++;
+ map = map_groups__next(map);
+
+ TEST_ASSERT_VAL("less maps expected", (map && i < size) || (!map && i == size));
+ }
+
+ return TEST_OK;
+}
+
+int test__map_groups__merge_in(struct test *t __maybe_unused, int subtest __maybe_unused)
+{
+ struct map_groups mg;
+ unsigned int i;
+ struct map_def bpf_progs[] = {
+ { "bpf_prog_1", 200, 300 },
+ { "bpf_prog_2", 500, 600 },
+ { "bpf_prog_3", 800, 900 },
+ };
+ struct map_def merged12[] = {
+ { "kcore1", 100, 200 },
+ { "bpf_prog_1", 200, 300 },
+ { "kcore1", 300, 500 },
+ { "bpf_prog_2", 500, 600 },
+ { "kcore1", 600, 800 },
+ { "bpf_prog_3", 800, 900 },
+ { "kcore1", 900, 1000 },
+ };
+ struct map_def merged3[] = {
+ { "kcore1", 100, 200 },
+ { "bpf_prog_1", 200, 300 },
+ { "kcore1", 300, 500 },
+ { "bpf_prog_2", 500, 600 },
+ { "kcore1", 600, 800 },
+ { "bpf_prog_3", 800, 900 },
+ { "kcore1", 900, 1000 },
+ { "kcore3", 1000, 1100 },
+ };
+ struct map *map_kcore1, *map_kcore2, *map_kcore3;
+ int ret;
+
+ map_groups__init(&mg, NULL);
+
+ for (i = 0; i < ARRAY_SIZE(bpf_progs); i++) {
+ struct map *map;
+
+ map = dso__new_map(bpf_progs[i].name);
+ TEST_ASSERT_VAL("failed to create map", map);
+
+ map->start = bpf_progs[i].start;
+ map->end = bpf_progs[i].end;
+ map_groups__insert(&mg, map);
+ map__put(map);
+ }
+
+ map_kcore1 = dso__new_map("kcore1");
+ TEST_ASSERT_VAL("failed to create map", map_kcore1);
+
+ map_kcore2 = dso__new_map("kcore2");
+ TEST_ASSERT_VAL("failed to create map", map_kcore2);
+
+ map_kcore3 = dso__new_map("kcore3");
+ TEST_ASSERT_VAL("failed to create map", map_kcore3);
+
+ /* kcore1 map overlaps over all bpf maps */
+ map_kcore1->start = 100;
+ map_kcore1->end = 1000;
+
+ /* kcore2 map hides behind bpf_prog_2 */
+ map_kcore2->start = 550;
+ map_kcore2->end = 570;
+
+ /* kcore3 map hides behind bpf_prog_3, kcore1 and adds new map */
+ map_kcore3->start = 880;
+ map_kcore3->end = 1100;
+
+ ret = map_groups__merge_in(&mg, map_kcore1);
+ TEST_ASSERT_VAL("failed to merge map", !ret);
+
+ ret = check_maps(merged12, ARRAY_SIZE(merged12), &mg);
+ TEST_ASSERT_VAL("merge check failed", !ret);
+
+ ret = map_groups__merge_in(&mg, map_kcore2);
+ TEST_ASSERT_VAL("failed to merge map", !ret);
+
+ ret = check_maps(merged12, ARRAY_SIZE(merged12), &mg);
+ TEST_ASSERT_VAL("merge check failed", !ret);
+
+ ret = map_groups__merge_in(&mg, map_kcore3);
+ TEST_ASSERT_VAL("failed to merge map", !ret);
+
+ ret = check_maps(merged3, ARRAY_SIZE(merged3), &mg);
+ TEST_ASSERT_VAL("merge check failed", !ret);
+ return TEST_OK;
+}
diff --git a/tools/perf/tests/mem.c b/tools/perf/tests/mem.c
index 0f82ee9fd3f7..673a11a6cd1b 100644
--- a/tools/perf/tests/mem.c
+++ b/tools/perf/tests/mem.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "util/map_symbol.h"
#include "util/mem-events.h"
#include "util/symbol.h"
#include "linux/perf_event.h"
diff --git a/tools/perf/tests/mem2node.c b/tools/perf/tests/mem2node.c
index 9e9e4d37cc77..7672ade70f20 100644
--- a/tools/perf/tests/mem2node.c
+++ b/tools/perf/tests/mem2node.c
@@ -1,6 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
#include <linux/bitmap.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <perf/cpumap.h>
#include "cpumap.h"
+#include "debug.h"
+#include "env.h"
#include "mem2node.h"
#include "tests.h"
@@ -17,7 +23,7 @@ static struct node {
static unsigned long *get_bitmap(const char *str, int nbits)
{
- struct cpu_map *map = cpu_map__new(str);
+ struct perf_cpu_map *map = perf_cpu_map__new(str);
unsigned long *bm = NULL;
int i;
@@ -30,7 +36,7 @@ static unsigned long *get_bitmap(const char *str, int nbits)
}
if (map)
- cpu_map__put(map);
+ perf_cpu_map__put(map);
else
free(bm);
@@ -66,7 +72,7 @@ int test__mem2node(struct test *t __maybe_unused, int subtest __maybe_unused)
T("failed: mem2node__node", -1 == mem2node__node(&map, 0x1050));
for (i = 0; i < ARRAY_SIZE(nodes); i++)
- free(nodes[i].set);
+ zfree(&nodes[i].set);
mem2node__exit(&map);
return 0;
diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c
index 0919b0793e5b..85e1d7337dc0 100644
--- a/tools/perf/tests/mmap-basic.c
+++ b/tools/perf/tests/mmap-basic.c
@@ -3,7 +3,10 @@
#include <inttypes.h>
/* For the CLR_() macros */
#include <pthread.h>
+#include <stdlib.h>
+#include <perf/cpumap.h>
+#include "debug.h"
#include "evlist.h"
#include "evsel.h"
#include "thread_map.h"
@@ -11,6 +14,8 @@
#include "tests.h"
#include <linux/err.h>
#include <linux/kernel.h>
+#include <linux/string.h>
+#include <perf/evlist.h>
/*
* This test will generate random numbers of calls to some getpid syscalls,
@@ -27,16 +32,16 @@ int test__basic_mmap(struct test *test __maybe_unused, int subtest __maybe_unuse
{
int err = -1;
union perf_event *event;
- struct thread_map *threads;
- struct cpu_map *cpus;
- struct perf_evlist *evlist;
+ struct perf_thread_map *threads;
+ struct perf_cpu_map *cpus;
+ struct evlist *evlist;
cpu_set_t cpu_set;
const char *syscall_names[] = { "getsid", "getppid", "getpgid", };
pid_t (*syscalls[])(void) = { (void *)getsid, getppid, (void*)getpgid };
#define nsyscalls ARRAY_SIZE(syscall_names)
unsigned int nr_events[nsyscalls],
expected_nr_events[nsyscalls], i, j;
- struct perf_evsel *evsels[nsyscalls], *evsel;
+ struct evsel *evsels[nsyscalls], *evsel;
char sbuf[STRERR_BUFSIZE];
struct perf_mmap *md;
@@ -46,7 +51,7 @@ int test__basic_mmap(struct test *test __maybe_unused, int subtest __maybe_unuse
return -1;
}
- cpus = cpu_map__new(NULL);
+ cpus = perf_cpu_map__new(NULL);
if (cpus == NULL) {
pr_debug("cpu_map__new\n");
goto out_free_threads;
@@ -61,13 +66,13 @@ int test__basic_mmap(struct test *test __maybe_unused, int subtest __maybe_unuse
goto out_free_cpus;
}
- evlist = perf_evlist__new();
+ evlist = evlist__new();
if (evlist == NULL) {
pr_debug("perf_evlist__new\n");
goto out_free_cpus;
}
- perf_evlist__set_maps(evlist, cpus, threads);
+ perf_evlist__set_maps(&evlist->core, cpus, threads);
for (i = 0; i < nsyscalls; ++i) {
char name[64];
@@ -79,12 +84,12 @@ int test__basic_mmap(struct test *test __maybe_unused, int subtest __maybe_unuse
goto out_delete_evlist;
}
- evsels[i]->attr.wakeup_events = 1;
+ evsels[i]->core.attr.wakeup_events = 1;
perf_evsel__set_sample_id(evsels[i], false);
- perf_evlist__add(evlist, evsels[i]);
+ evlist__add(evlist, evsels[i]);
- if (perf_evsel__open(evsels[i], cpus, threads) < 0) {
+ if (evsel__open(evsels[i], cpus, threads) < 0) {
pr_debug("failed to open counter: %s, "
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
str_error_r(errno, sbuf, sizeof(sbuf)));
@@ -151,12 +156,12 @@ out_init:
}
out_delete_evlist:
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
cpus = NULL;
threads = NULL;
out_free_cpus:
- cpu_map__put(cpus);
+ perf_cpu_map__put(cpus);
out_free_threads:
- thread_map__put(threads);
+ perf_thread_map__put(threads);
return err;
}
diff --git a/tools/perf/tests/mmap-thread-lookup.c b/tools/perf/tests/mmap-thread-lookup.c
index ba87e6e8d18c..360d70deb855 100644
--- a/tools/perf/tests/mmap-thread-lookup.c
+++ b/tools/perf/tests/mmap-thread-lookup.c
@@ -53,7 +53,7 @@ static void *thread_fn(void *arg)
{
struct thread_data *td = arg;
ssize_t ret;
- int go;
+ int go = 0;
if (thread_init(td))
return NULL;
@@ -138,7 +138,7 @@ static int synth_all(struct machine *machine)
static int synth_process(struct machine *machine)
{
- struct thread_map *map;
+ struct perf_thread_map *map;
int err;
map = thread_map__new_by_pid(getpid());
@@ -147,7 +147,7 @@ static int synth_process(struct machine *machine)
perf_event__process,
machine, 0);
- thread_map__put(map);
+ perf_thread_map__put(map);
return err;
}
diff --git a/tools/perf/tests/openat-syscall-all-cpus.c b/tools/perf/tests/openat-syscall-all-cpus.c
index 493ecb611540..9171f77cd9cd 100644
--- a/tools/perf/tests/openat-syscall-all-cpus.c
+++ b/tools/perf/tests/openat-syscall-all-cpus.c
@@ -9,6 +9,7 @@
#include <fcntl.h>
#include <api/fs/fs.h>
#include <linux/err.h>
+#include <linux/string.h>
#include <api/fs/tracing_path.h>
#include "evsel.h"
#include "tests.h"
@@ -16,15 +17,16 @@
#include "cpumap.h"
#include "debug.h"
#include "stat.h"
+#include "util/counts.h"
int test__openat_syscall_event_on_all_cpus(struct test *test __maybe_unused, int subtest __maybe_unused)
{
int err = -1, fd, cpu;
- struct cpu_map *cpus;
- struct perf_evsel *evsel;
+ struct perf_cpu_map *cpus;
+ struct evsel *evsel;
unsigned int nr_openat_calls = 111, i;
cpu_set_t cpu_set;
- struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX);
+ struct perf_thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX);
char sbuf[STRERR_BUFSIZE];
char errbuf[BUFSIZ];
@@ -33,7 +35,7 @@ int test__openat_syscall_event_on_all_cpus(struct test *test __maybe_unused, int
return -1;
}
- cpus = cpu_map__new(NULL);
+ cpus = perf_cpu_map__new(NULL);
if (cpus == NULL) {
pr_debug("cpu_map__new\n");
goto out_thread_map_delete;
@@ -48,7 +50,7 @@ int test__openat_syscall_event_on_all_cpus(struct test *test __maybe_unused, int
goto out_cpu_map_delete;
}
- if (perf_evsel__open(evsel, cpus, threads) < 0) {
+ if (evsel__open(evsel, cpus, threads) < 0) {
pr_debug("failed to open counter: %s, "
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
str_error_r(errno, sbuf, sizeof(sbuf)));
@@ -116,12 +118,12 @@ int test__openat_syscall_event_on_all_cpus(struct test *test __maybe_unused, int
perf_evsel__free_counts(evsel);
out_close_fd:
- perf_evsel__close_fd(evsel);
+ perf_evsel__close_fd(&evsel->core);
out_evsel_delete:
- perf_evsel__delete(evsel);
+ evsel__delete(evsel);
out_cpu_map_delete:
- cpu_map__put(cpus);
+ perf_cpu_map__put(cpus);
out_thread_map_delete:
- thread_map__put(threads);
+ perf_thread_map__put(threads);
return err;
}
diff --git a/tools/perf/tests/openat-syscall-tp-fields.c b/tools/perf/tests/openat-syscall-tp-fields.c
index 344dc3ac2469..b71167b43dda 100644
--- a/tools/perf/tests/openat-syscall-tp-fields.c
+++ b/tools/perf/tests/openat-syscall-tp-fields.c
@@ -1,12 +1,14 @@
// SPDX-License-Identifier: GPL-2.0
+#include <stdbool.h>
#include <linux/err.h>
+#include <linux/string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
-#include "perf.h"
#include "evlist.h"
#include "evsel.h"
#include "thread_map.h"
+#include "record.h"
#include "tests.h"
#include "debug.h"
#include <errno.h>
@@ -32,8 +34,8 @@ int test__syscall_openat_tp_fields(struct test *test __maybe_unused, int subtest
};
const char *filename = "/etc/passwd";
int flags = O_RDONLY | O_DIRECTORY;
- struct perf_evlist *evlist = perf_evlist__new();
- struct perf_evsel *evsel;
+ struct evlist *evlist = evlist__new();
+ struct evsel *evsel;
int err = -1, i, nr_events = 0, nr_polls = 0;
char sbuf[STRERR_BUFSIZE];
@@ -48,7 +50,7 @@ int test__syscall_openat_tp_fields(struct test *test __maybe_unused, int subtest
goto out_delete_evlist;
}
- perf_evlist__add(evlist, evsel);
+ evlist__add(evlist, evsel);
err = perf_evlist__create_maps(evlist, &opts.target);
if (err < 0) {
@@ -58,9 +60,9 @@ int test__syscall_openat_tp_fields(struct test *test __maybe_unused, int subtest
perf_evsel__config(evsel, &opts, NULL);
- thread_map__set_pid(evlist->threads, 0, getpid());
+ perf_thread_map__set_pid(evlist->core.threads, 0, getpid());
- err = perf_evlist__open(evlist);
+ err = evlist__open(evlist);
if (err < 0) {
pr_debug("perf_evlist__open: %s\n",
str_error_r(errno, sbuf, sizeof(sbuf)));
@@ -74,7 +76,7 @@ int test__syscall_openat_tp_fields(struct test *test __maybe_unused, int subtest
goto out_delete_evlist;
}
- perf_evlist__enable(evlist);
+ evlist__enable(evlist);
/*
* Generate the event:
@@ -134,7 +136,7 @@ int test__syscall_openat_tp_fields(struct test *test __maybe_unused, int subtest
out_ok:
err = 0;
out_delete_evlist:
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
out:
return err;
}
diff --git a/tools/perf/tests/openat-syscall.c b/tools/perf/tests/openat-syscall.c
index 00cd63f90b92..5ebffae18605 100644
--- a/tools/perf/tests/openat-syscall.c
+++ b/tools/perf/tests/openat-syscall.c
@@ -3,6 +3,7 @@
#include <inttypes.h>
#include <api/fs/tracing_path.h>
#include <linux/err.h>
+#include <linux/string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -10,13 +11,14 @@
#include "evsel.h"
#include "debug.h"
#include "tests.h"
+#include "util/counts.h"
int test__openat_syscall_event(struct test *test __maybe_unused, int subtest __maybe_unused)
{
int err = -1, fd;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
unsigned int nr_openat_calls = 111, i;
- struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX);
+ struct perf_thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX);
char sbuf[STRERR_BUFSIZE];
char errbuf[BUFSIZ];
@@ -57,10 +59,10 @@ int test__openat_syscall_event(struct test *test __maybe_unused, int subtest __m
err = 0;
out_close_fd:
- perf_evsel__close_fd(evsel);
+ perf_evsel__close_fd(&evsel->core);
out_evsel_delete:
- perf_evsel__delete(evsel);
+ evsel__delete(evsel);
out_thread_map_delete:
- thread_map__put(threads);
+ perf_thread_map__put(threads);
return err;
}
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c
index 4a69c07f4101..02ba696fb87f 100644
--- a/tools/perf/tests/parse-events.c
+++ b/tools/perf/tests/parse-events.c
@@ -5,6 +5,7 @@
#include <api/fs/fs.h>
#include "tests.h"
#include "debug.h"
+#include "pmu.h"
#include "util.h"
#include <dirent.h>
#include <errno.h>
@@ -18,549 +19,575 @@
#define PERF_TP_SAMPLE_TYPE (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | \
PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD)
-static int test__checkevent_tracepoint(struct perf_evlist *evlist)
+#if defined(__s390x__)
+/* Return true if kvm module is available and loaded. Test this
+ * and retun success when trace point kvm_s390_create_vm
+ * exists. Otherwise this test always fails.
+ */
+static bool kvm_s390_create_vm_valid(void)
+{
+ char *eventfile;
+ bool rc = false;
+
+ eventfile = get_events_file("kvm-s390");
+
+ if (eventfile) {
+ DIR *mydir = opendir(eventfile);
+
+ if (mydir) {
+ rc = true;
+ closedir(mydir);
+ }
+ put_events_file(eventfile);
+ }
+
+ return rc;
+}
+#endif
+
+static int test__checkevent_tracepoint(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong number of groups", 0 == evlist->nr_groups);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong sample_type",
- PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type);
- TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period);
+ PERF_TP_SAMPLE_TYPE == evsel->core.attr.sample_type);
+ TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->core.attr.sample_period);
return 0;
}
-static int test__checkevent_tracepoint_multi(struct perf_evlist *evlist)
+static int test__checkevent_tracepoint_multi(struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
- TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1);
+ TEST_ASSERT_VAL("wrong number of entries", evlist->core.nr_entries > 1);
TEST_ASSERT_VAL("wrong number of groups", 0 == evlist->nr_groups);
evlist__for_each_entry(evlist, evsel) {
TEST_ASSERT_VAL("wrong type",
- PERF_TYPE_TRACEPOINT == evsel->attr.type);
+ PERF_TYPE_TRACEPOINT == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong sample_type",
- PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type);
+ PERF_TP_SAMPLE_TYPE == evsel->core.attr.sample_type);
TEST_ASSERT_VAL("wrong sample_period",
- 1 == evsel->attr.sample_period);
+ 1 == evsel->core.attr.sample_period);
}
return 0;
}
-static int test__checkevent_raw(struct perf_evlist *evlist)
+static int test__checkevent_raw(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
- TEST_ASSERT_VAL("wrong config", 0x1a == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type);
+ TEST_ASSERT_VAL("wrong config", 0x1a == evsel->core.attr.config);
return 0;
}
-static int test__checkevent_numeric(struct perf_evlist *evlist)
+static int test__checkevent_numeric(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
- TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type);
- TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
+ TEST_ASSERT_VAL("wrong type", 1 == evsel->core.attr.type);
+ TEST_ASSERT_VAL("wrong config", 1 == evsel->core.attr.config);
return 0;
}
-static int test__checkevent_symbolic_name(struct perf_evlist *evlist)
+static int test__checkevent_symbolic_name(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config);
+ PERF_COUNT_HW_INSTRUCTIONS == evsel->core.attr.config);
return 0;
}
-static int test__checkevent_symbolic_name_config(struct perf_evlist *evlist)
+static int test__checkevent_symbolic_name_config(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
+ PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
/*
* The period value gets configured within perf_evlist__config,
* while this test executes only parse events method.
*/
TEST_ASSERT_VAL("wrong period",
- 0 == evsel->attr.sample_period);
+ 0 == evsel->core.attr.sample_period);
TEST_ASSERT_VAL("wrong config1",
- 0 == evsel->attr.config1);
+ 0 == evsel->core.attr.config1);
TEST_ASSERT_VAL("wrong config2",
- 1 == evsel->attr.config2);
+ 1 == evsel->core.attr.config2);
return 0;
}
-static int test__checkevent_symbolic_alias(struct perf_evlist *evlist)
+static int test__checkevent_symbolic_alias(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_SW_PAGE_FAULTS == evsel->attr.config);
+ PERF_COUNT_SW_PAGE_FAULTS == evsel->core.attr.config);
return 0;
}
-static int test__checkevent_genhw(struct perf_evlist *evlist)
+static int test__checkevent_genhw(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE == evsel->attr.type);
- TEST_ASSERT_VAL("wrong config", (1 << 16) == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE == evsel->core.attr.type);
+ TEST_ASSERT_VAL("wrong config", (1 << 16) == evsel->core.attr.config);
return 0;
}
-static int test__checkevent_breakpoint(struct perf_evlist *evlist)
+static int test__checkevent_breakpoint(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type);
- TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->core.attr.type);
+ TEST_ASSERT_VAL("wrong config", 0 == evsel->core.attr.config);
TEST_ASSERT_VAL("wrong bp_type", (HW_BREAKPOINT_R | HW_BREAKPOINT_W) ==
- evsel->attr.bp_type);
+ evsel->core.attr.bp_type);
TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_4 ==
- evsel->attr.bp_len);
+ evsel->core.attr.bp_len);
return 0;
}
-static int test__checkevent_breakpoint_x(struct perf_evlist *evlist)
+static int test__checkevent_breakpoint_x(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type);
- TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->core.attr.type);
+ TEST_ASSERT_VAL("wrong config", 0 == evsel->core.attr.config);
TEST_ASSERT_VAL("wrong bp_type",
- HW_BREAKPOINT_X == evsel->attr.bp_type);
- TEST_ASSERT_VAL("wrong bp_len", sizeof(long) == evsel->attr.bp_len);
+ HW_BREAKPOINT_X == evsel->core.attr.bp_type);
+ TEST_ASSERT_VAL("wrong bp_len", sizeof(long) == evsel->core.attr.bp_len);
return 0;
}
-static int test__checkevent_breakpoint_r(struct perf_evlist *evlist)
+static int test__checkevent_breakpoint_r(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong type",
- PERF_TYPE_BREAKPOINT == evsel->attr.type);
- TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
+ PERF_TYPE_BREAKPOINT == evsel->core.attr.type);
+ TEST_ASSERT_VAL("wrong config", 0 == evsel->core.attr.config);
TEST_ASSERT_VAL("wrong bp_type",
- HW_BREAKPOINT_R == evsel->attr.bp_type);
+ HW_BREAKPOINT_R == evsel->core.attr.bp_type);
TEST_ASSERT_VAL("wrong bp_len",
- HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len);
+ HW_BREAKPOINT_LEN_4 == evsel->core.attr.bp_len);
return 0;
}
-static int test__checkevent_breakpoint_w(struct perf_evlist *evlist)
+static int test__checkevent_breakpoint_w(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong type",
- PERF_TYPE_BREAKPOINT == evsel->attr.type);
- TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
+ PERF_TYPE_BREAKPOINT == evsel->core.attr.type);
+ TEST_ASSERT_VAL("wrong config", 0 == evsel->core.attr.config);
TEST_ASSERT_VAL("wrong bp_type",
- HW_BREAKPOINT_W == evsel->attr.bp_type);
+ HW_BREAKPOINT_W == evsel->core.attr.bp_type);
TEST_ASSERT_VAL("wrong bp_len",
- HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len);
+ HW_BREAKPOINT_LEN_4 == evsel->core.attr.bp_len);
return 0;
}
-static int test__checkevent_breakpoint_rw(struct perf_evlist *evlist)
+static int test__checkevent_breakpoint_rw(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong type",
- PERF_TYPE_BREAKPOINT == evsel->attr.type);
- TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
+ PERF_TYPE_BREAKPOINT == evsel->core.attr.type);
+ TEST_ASSERT_VAL("wrong config", 0 == evsel->core.attr.config);
TEST_ASSERT_VAL("wrong bp_type",
- (HW_BREAKPOINT_R|HW_BREAKPOINT_W) == evsel->attr.bp_type);
+ (HW_BREAKPOINT_R|HW_BREAKPOINT_W) == evsel->core.attr.bp_type);
TEST_ASSERT_VAL("wrong bp_len",
- HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len);
+ HW_BREAKPOINT_LEN_4 == evsel->core.attr.bp_len);
return 0;
}
-static int test__checkevent_tracepoint_modifier(struct perf_evlist *evlist)
+static int test__checkevent_tracepoint_modifier(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
return test__checkevent_tracepoint(evlist);
}
static int
-test__checkevent_tracepoint_multi_modifier(struct perf_evlist *evlist)
+test__checkevent_tracepoint_multi_modifier(struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
- TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1);
+ TEST_ASSERT_VAL("wrong number of entries", evlist->core.nr_entries > 1);
evlist__for_each_entry(evlist, evsel) {
TEST_ASSERT_VAL("wrong exclude_user",
- !evsel->attr.exclude_user);
+ !evsel->core.attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel",
- evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
}
return test__checkevent_tracepoint_multi(evlist);
}
-static int test__checkevent_raw_modifier(struct perf_evlist *evlist)
+static int test__checkevent_raw_modifier(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip);
return test__checkevent_raw(evlist);
}
-static int test__checkevent_numeric_modifier(struct perf_evlist *evlist)
+static int test__checkevent_numeric_modifier(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip);
return test__checkevent_numeric(evlist);
}
-static int test__checkevent_symbolic_name_modifier(struct perf_evlist *evlist)
+static int test__checkevent_symbolic_name_modifier(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
return test__checkevent_symbolic_name(evlist);
}
-static int test__checkevent_exclude_host_modifier(struct perf_evlist *evlist)
+static int test__checkevent_exclude_host_modifier(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
return test__checkevent_symbolic_name(evlist);
}
-static int test__checkevent_exclude_guest_modifier(struct perf_evlist *evlist)
+static int test__checkevent_exclude_guest_modifier(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
+ TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
return test__checkevent_symbolic_name(evlist);
}
-static int test__checkevent_symbolic_alias_modifier(struct perf_evlist *evlist)
+static int test__checkevent_symbolic_alias_modifier(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
return test__checkevent_symbolic_alias(evlist);
}
-static int test__checkevent_genhw_modifier(struct perf_evlist *evlist)
+static int test__checkevent_genhw_modifier(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip);
return test__checkevent_genhw(evlist);
}
-static int test__checkevent_exclude_idle_modifier(struct perf_evlist *evlist)
+static int test__checkevent_exclude_idle_modifier(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong exclude idle", evsel->attr.exclude_idle);
- TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong exclude idle", evsel->core.attr.exclude_idle);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
return test__checkevent_symbolic_name(evlist);
}
-static int test__checkevent_exclude_idle_modifier_1(struct perf_evlist *evlist)
+static int test__checkevent_exclude_idle_modifier_1(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong exclude idle", evsel->attr.exclude_idle);
- TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong exclude idle", evsel->core.attr.exclude_idle);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
return test__checkevent_symbolic_name(evlist);
}
-static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist)
+static int test__checkevent_breakpoint_modifier(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong name",
!strcmp(perf_evsel__name(evsel), "mem:0:u"));
return test__checkevent_breakpoint(evlist);
}
-static int test__checkevent_breakpoint_x_modifier(struct perf_evlist *evlist)
+static int test__checkevent_breakpoint_x_modifier(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong name",
!strcmp(perf_evsel__name(evsel), "mem:0:x:k"));
return test__checkevent_breakpoint_x(evlist);
}
-static int test__checkevent_breakpoint_r_modifier(struct perf_evlist *evlist)
+static int test__checkevent_breakpoint_r_modifier(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong name",
!strcmp(perf_evsel__name(evsel), "mem:0:r:hp"));
return test__checkevent_breakpoint_r(evlist);
}
-static int test__checkevent_breakpoint_w_modifier(struct perf_evlist *evlist)
+static int test__checkevent_breakpoint_w_modifier(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong name",
!strcmp(perf_evsel__name(evsel), "mem:0:w:up"));
return test__checkevent_breakpoint_w(evlist);
}
-static int test__checkevent_breakpoint_rw_modifier(struct perf_evlist *evlist)
+static int test__checkevent_breakpoint_rw_modifier(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong name",
!strcmp(perf_evsel__name(evsel), "mem:0:rw:kp"));
return test__checkevent_breakpoint_rw(evlist);
}
-static int test__checkevent_pmu(struct perf_evlist *evlist)
+static int test__checkevent_pmu(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
- TEST_ASSERT_VAL("wrong config", 10 == evsel->attr.config);
- TEST_ASSERT_VAL("wrong config1", 1 == evsel->attr.config1);
- TEST_ASSERT_VAL("wrong config2", 3 == evsel->attr.config2);
+ TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type);
+ TEST_ASSERT_VAL("wrong config", 10 == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong config1", 1 == evsel->core.attr.config1);
+ TEST_ASSERT_VAL("wrong config2", 3 == evsel->core.attr.config2);
/*
* The period value gets configured within perf_evlist__config,
* while this test executes only parse events method.
*/
- TEST_ASSERT_VAL("wrong period", 0 == evsel->attr.sample_period);
+ TEST_ASSERT_VAL("wrong period", 0 == evsel->core.attr.sample_period);
return 0;
}
-static int test__checkevent_list(struct perf_evlist *evlist)
+static int test__checkevent_list(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->core.nr_entries);
/* r1 */
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
- TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
- TEST_ASSERT_VAL("wrong config1", 0 == evsel->attr.config1);
- TEST_ASSERT_VAL("wrong config2", 0 == evsel->attr.config2);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type);
+ TEST_ASSERT_VAL("wrong config", 1 == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong config1", 0 == evsel->core.attr.config1);
+ TEST_ASSERT_VAL("wrong config2", 0 == evsel->core.attr.config2);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
/* syscalls:sys_enter_openat:k */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong sample_type",
- PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type);
- TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period);
- TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_TP_SAMPLE_TYPE == evsel->core.attr.sample_type);
+ TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->core.attr.sample_period);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
/* 1:1:hp */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type);
- TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong type", 1 == evsel->core.attr.type);
+ TEST_ASSERT_VAL("wrong config", 1 == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip);
return 0;
}
-static int test__checkevent_pmu_name(struct perf_evlist *evlist)
+static int test__checkevent_pmu_name(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
/* cpu/config=1,name=krava/u */
- TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
- TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type);
+ TEST_ASSERT_VAL("wrong config", 1 == evsel->core.attr.config);
TEST_ASSERT_VAL("wrong name", !strcmp(perf_evsel__name(evsel), "krava"));
/* cpu/config=2/u" */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
- TEST_ASSERT_VAL("wrong config", 2 == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type);
+ TEST_ASSERT_VAL("wrong config", 2 == evsel->core.attr.config);
TEST_ASSERT_VAL("wrong name",
!strcmp(perf_evsel__name(evsel), "cpu/config=2/u"));
return 0;
}
-static int test__checkevent_pmu_partial_time_callgraph(struct perf_evlist *evlist)
+static int test__checkevent_pmu_partial_time_callgraph(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
/* cpu/config=1,call-graph=fp,time,period=100000/ */
- TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
- TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type);
+ TEST_ASSERT_VAL("wrong config", 1 == evsel->core.attr.config);
/*
* The period, time and callgraph value gets configured
* within perf_evlist__config,
* while this test executes only parse events method.
*/
- TEST_ASSERT_VAL("wrong period", 0 == evsel->attr.sample_period);
+ TEST_ASSERT_VAL("wrong period", 0 == evsel->core.attr.sample_period);
TEST_ASSERT_VAL("wrong callgraph", !evsel__has_callchain(evsel));
- TEST_ASSERT_VAL("wrong time", !(PERF_SAMPLE_TIME & evsel->attr.sample_type));
+ TEST_ASSERT_VAL("wrong time", !(PERF_SAMPLE_TIME & evsel->core.attr.sample_type));
/* cpu/config=2,call-graph=no,time=0,period=2000/ */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
- TEST_ASSERT_VAL("wrong config", 2 == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type);
+ TEST_ASSERT_VAL("wrong config", 2 == evsel->core.attr.config);
/*
* The period, time and callgraph value gets configured
* within perf_evlist__config,
* while this test executes only parse events method.
*/
- TEST_ASSERT_VAL("wrong period", 0 == evsel->attr.sample_period);
+ TEST_ASSERT_VAL("wrong period", 0 == evsel->core.attr.sample_period);
TEST_ASSERT_VAL("wrong callgraph", !evsel__has_callchain(evsel));
- TEST_ASSERT_VAL("wrong time", !(PERF_SAMPLE_TIME & evsel->attr.sample_type));
+ TEST_ASSERT_VAL("wrong time", !(PERF_SAMPLE_TIME & evsel->core.attr.sample_type));
return 0;
}
-static int test__checkevent_pmu_events(struct perf_evlist *evlist)
+static int test__checkevent_pmu_events(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong exclude_user",
- !evsel->attr.exclude_user);
+ !evsel->core.attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel",
- evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
- TEST_ASSERT_VAL("wrong pinned", !evsel->attr.pinned);
+ evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
+ TEST_ASSERT_VAL("wrong pinned", !evsel->core.attr.pinned);
return 0;
}
-static int test__checkevent_pmu_events_mix(struct perf_evlist *evlist)
+static int test__checkevent_pmu_events_mix(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
/* pmu-event:u */
- TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong exclude_user",
- !evsel->attr.exclude_user);
+ !evsel->core.attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel",
- evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
- TEST_ASSERT_VAL("wrong pinned", !evsel->attr.pinned);
+ evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
+ TEST_ASSERT_VAL("wrong pinned", !evsel->core.attr.pinned);
/* cpu/pmu-event/u*/
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong exclude_user",
- !evsel->attr.exclude_user);
+ !evsel->core.attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel",
- evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
- TEST_ASSERT_VAL("wrong pinned", !evsel->attr.pinned);
+ evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
+ TEST_ASSERT_VAL("wrong pinned", !evsel->core.attr.pinned);
return 0;
}
@@ -608,41 +635,41 @@ static int test__checkterms_simple(struct list_head *terms)
return 0;
}
-static int test__group1(struct perf_evlist *evlist)
+static int test__group1(struct evlist *evlist)
{
- struct perf_evsel *evsel, *leader;
+ struct evsel *evsel, *leader;
- TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
/* instructions:k */
evsel = leader = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_INSTRUCTIONS == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
- TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
+ TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
/* cycles:upp */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+ PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
/* use of precise requires exclude_guest */
- TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2);
+ TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 2);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
@@ -650,99 +677,99 @@ static int test__group1(struct perf_evlist *evlist)
return 0;
}
-static int test__group2(struct perf_evlist *evlist)
+static int test__group2(struct evlist *evlist)
{
- struct perf_evsel *evsel, *leader;
+ struct evsel *evsel, *leader;
- TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
/* faults + :ku modifier */
evsel = leader = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_SW_PAGE_FAULTS == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_SW_PAGE_FAULTS == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
- TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
+ TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
/* cache-references + :u modifier */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CACHE_REFERENCES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_CACHE_REFERENCES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
/* cycles:k */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
return 0;
}
-static int test__group3(struct perf_evlist *evlist __maybe_unused)
+static int test__group3(struct evlist *evlist __maybe_unused)
{
- struct perf_evsel *evsel, *leader;
+ struct evsel *evsel, *leader;
- TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong number of groups", 2 == evlist->nr_groups);
/* group1 syscalls:sys_enter_openat:H */
evsel = leader = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong sample_type",
- PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type);
- TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_TP_SAMPLE_TYPE == evsel->core.attr.sample_type);
+ TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->core.attr.sample_period);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
TEST_ASSERT_VAL("wrong group name",
!strcmp(leader->group_name, "group1"));
- TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
+ TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
/* group1 cycles:kppp */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+ PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
/* use of precise requires exclude_guest */
- TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 3);
+ TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 3);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
@@ -750,90 +777,90 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
/* group2 cycles + G modifier */
evsel = leader = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
TEST_ASSERT_VAL("wrong group name",
!strcmp(leader->group_name, "group2"));
- TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
+ TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
/* group2 1:3 + G modifier */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type);
- TEST_ASSERT_VAL("wrong config", 3 == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong type", 1 == evsel->core.attr.type);
+ TEST_ASSERT_VAL("wrong config", 3 == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
/* instructions:u */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_INSTRUCTIONS == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
return 0;
}
-static int test__group4(struct perf_evlist *evlist __maybe_unused)
+static int test__group4(struct evlist *evlist __maybe_unused)
{
- struct perf_evsel *evsel, *leader;
+ struct evsel *evsel, *leader;
- TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
/* cycles:u + p */
evsel = leader = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+ PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
/* use of precise requires exclude_guest */
- TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 1);
+ TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 1);
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
- TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
+ TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
/* instructions:kp + p */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+ PERF_COUNT_HW_INSTRUCTIONS == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
/* use of precise requires exclude_guest */
- TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2);
+ TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 2);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
@@ -841,298 +868,298 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused)
return 0;
}
-static int test__group5(struct perf_evlist *evlist __maybe_unused)
+static int test__group5(struct evlist *evlist __maybe_unused)
{
- struct perf_evsel *evsel, *leader;
+ struct evsel *evsel, *leader;
- TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong number of groups", 2 == evlist->nr_groups);
/* cycles + G */
evsel = leader = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
- TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
+ TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
/* instructions + G */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_INSTRUCTIONS == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
/* cycles:G */
evsel = leader = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
- TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
+ TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
/* instructions:G */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_INSTRUCTIONS == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
/* cycles */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
return 0;
}
-static int test__group_gh1(struct perf_evlist *evlist)
+static int test__group_gh1(struct evlist *evlist)
{
- struct perf_evsel *evsel, *leader;
+ struct evsel *evsel, *leader;
- TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
/* cycles + :H group modifier */
evsel = leader = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
- TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
+ TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
/* cache-misses:G + :H group modifier */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_CACHE_MISSES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
return 0;
}
-static int test__group_gh2(struct perf_evlist *evlist)
+static int test__group_gh2(struct evlist *evlist)
{
- struct perf_evsel *evsel, *leader;
+ struct evsel *evsel, *leader;
- TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
/* cycles + :G group modifier */
evsel = leader = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
- TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
+ TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
/* cache-misses:H + :G group modifier */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_CACHE_MISSES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
return 0;
}
-static int test__group_gh3(struct perf_evlist *evlist)
+static int test__group_gh3(struct evlist *evlist)
{
- struct perf_evsel *evsel, *leader;
+ struct evsel *evsel, *leader;
- TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
/* cycles:G + :u group modifier */
evsel = leader = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
- TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
+ TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
/* cache-misses:H + :u group modifier */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_CACHE_MISSES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
return 0;
}
-static int test__group_gh4(struct perf_evlist *evlist)
+static int test__group_gh4(struct evlist *evlist)
{
- struct perf_evsel *evsel, *leader;
+ struct evsel *evsel, *leader;
- TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
/* cycles:G + :uG group modifier */
evsel = leader = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
- TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
+ TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
/* cache-misses:H + :uG group modifier */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_CACHE_MISSES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
return 0;
}
-static int test__leader_sample1(struct perf_evlist *evlist)
+static int test__leader_sample1(struct evlist *evlist)
{
- struct perf_evsel *evsel, *leader;
+ struct evsel *evsel, *leader;
- TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->core.nr_entries);
/* cycles - sampling group leader */
evsel = leader = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read);
/* cache-misses - not sampling */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_CACHE_MISSES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read);
/* branch-misses - not sampling */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_BRANCH_MISSES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_BRANCH_MISSES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read);
@@ -1140,38 +1167,38 @@ static int test__leader_sample1(struct perf_evlist *evlist)
return 0;
}
-static int test__leader_sample2(struct perf_evlist *evlist __maybe_unused)
+static int test__leader_sample2(struct evlist *evlist __maybe_unused)
{
- struct perf_evsel *evsel, *leader;
+ struct evsel *evsel, *leader;
- TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
/* instructions - sampling group leader */
evsel = leader = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_INSTRUCTIONS == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read);
/* branch-misses - not sampling */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_BRANCH_MISSES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
- TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ PERF_COUNT_HW_BRANCH_MISSES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read);
@@ -1179,131 +1206,131 @@ static int test__leader_sample2(struct perf_evlist *evlist __maybe_unused)
return 0;
}
-static int test__checkevent_pinned_modifier(struct perf_evlist *evlist)
+static int test__checkevent_pinned_modifier(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
- TEST_ASSERT_VAL("wrong pinned", evsel->attr.pinned);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip);
+ TEST_ASSERT_VAL("wrong pinned", evsel->core.attr.pinned);
return test__checkevent_symbolic_name(evlist);
}
-static int test__pinned_group(struct perf_evlist *evlist)
+static int test__pinned_group(struct evlist *evlist)
{
- struct perf_evsel *evsel, *leader;
+ struct evsel *evsel, *leader;
- TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries);
+ TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->core.nr_entries);
/* cycles - group leader */
evsel = leader = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
+ PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
- TEST_ASSERT_VAL("wrong pinned", evsel->attr.pinned);
+ TEST_ASSERT_VAL("wrong pinned", evsel->core.attr.pinned);
/* cache-misses - can not be pinned, but will go on with the leader */
evsel = perf_evsel__next(evsel);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong pinned", !evsel->attr.pinned);
+ PERF_COUNT_HW_CACHE_MISSES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong pinned", !evsel->core.attr.pinned);
/* branch-misses - ditto */
evsel = perf_evsel__next(evsel);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_HW_BRANCH_MISSES == evsel->attr.config);
- TEST_ASSERT_VAL("wrong pinned", !evsel->attr.pinned);
+ PERF_COUNT_HW_BRANCH_MISSES == evsel->core.attr.config);
+ TEST_ASSERT_VAL("wrong pinned", !evsel->core.attr.pinned);
return 0;
}
-static int test__checkevent_breakpoint_len(struct perf_evlist *evlist)
+static int test__checkevent_breakpoint_len(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type);
- TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->core.attr.type);
+ TEST_ASSERT_VAL("wrong config", 0 == evsel->core.attr.config);
TEST_ASSERT_VAL("wrong bp_type", (HW_BREAKPOINT_R | HW_BREAKPOINT_W) ==
- evsel->attr.bp_type);
+ evsel->core.attr.bp_type);
TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_1 ==
- evsel->attr.bp_len);
+ evsel->core.attr.bp_len);
return 0;
}
-static int test__checkevent_breakpoint_len_w(struct perf_evlist *evlist)
+static int test__checkevent_breakpoint_len_w(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type);
- TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->core.attr.type);
+ TEST_ASSERT_VAL("wrong config", 0 == evsel->core.attr.config);
TEST_ASSERT_VAL("wrong bp_type", HW_BREAKPOINT_W ==
- evsel->attr.bp_type);
+ evsel->core.attr.bp_type);
TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_2 ==
- evsel->attr.bp_len);
+ evsel->core.attr.bp_len);
return 0;
}
static int
-test__checkevent_breakpoint_len_rw_modifier(struct perf_evlist *evlist)
+test__checkevent_breakpoint_len_rw_modifier(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
- TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
- TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
return test__checkevent_breakpoint_rw(evlist);
}
-static int test__checkevent_precise_max_modifier(struct perf_evlist *evlist)
+static int test__checkevent_precise_max_modifier(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
- TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config",
- PERF_COUNT_SW_TASK_CLOCK == evsel->attr.config);
+ PERF_COUNT_SW_TASK_CLOCK == evsel->core.attr.config);
return 0;
}
-static int test__checkevent_config_symbol(struct perf_evlist *evlist)
+static int test__checkevent_config_symbol(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong name setting", strcmp(evsel->name, "insn") == 0);
return 0;
}
-static int test__checkevent_config_raw(struct perf_evlist *evlist)
+static int test__checkevent_config_raw(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong name setting", strcmp(evsel->name, "rawpmu") == 0);
return 0;
}
-static int test__checkevent_config_num(struct perf_evlist *evlist)
+static int test__checkevent_config_num(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong name setting", strcmp(evsel->name, "numpmu") == 0);
return 0;
}
-static int test__checkevent_config_cache(struct perf_evlist *evlist)
+static int test__checkevent_config_cache(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong name setting", strcmp(evsel->name, "cachepmu") == 0);
return 0;
@@ -1314,39 +1341,39 @@ static bool test__intel_pt_valid(void)
return !!perf_pmu__find("intel_pt");
}
-static int test__intel_pt(struct perf_evlist *evlist)
+static int test__intel_pt(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong name setting", strcmp(evsel->name, "intel_pt//u") == 0);
return 0;
}
-static int test__checkevent_complex_name(struct perf_evlist *evlist)
+static int test__checkevent_complex_name(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong complex name parsing", strcmp(evsel->name, "COMPLEX_CYCLES_NAME:orig=cycles,desc=chip-clock-ticks") == 0);
return 0;
}
-static int test__sym_event_slash(struct perf_evlist *evlist)
+static int test__sym_event_slash(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong type", evsel->attr.type == PERF_TYPE_HARDWARE);
- TEST_ASSERT_VAL("wrong config", evsel->attr.config == PERF_COUNT_HW_CPU_CYCLES);
- TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong type", evsel->core.attr.type == PERF_TYPE_HARDWARE);
+ TEST_ASSERT_VAL("wrong config", evsel->core.attr.config == PERF_COUNT_HW_CPU_CYCLES);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
return 0;
}
-static int test__sym_event_dc(struct perf_evlist *evlist)
+static int test__sym_event_dc(struct evlist *evlist)
{
- struct perf_evsel *evsel = perf_evlist__first(evlist);
+ struct evsel *evsel = perf_evlist__first(evlist);
- TEST_ASSERT_VAL("wrong type", evsel->attr.type == PERF_TYPE_HARDWARE);
- TEST_ASSERT_VAL("wrong config", evsel->attr.config == PERF_COUNT_HW_CPU_CYCLES);
- TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong type", evsel->core.attr.type == PERF_TYPE_HARDWARE);
+ TEST_ASSERT_VAL("wrong config", evsel->core.attr.config == PERF_COUNT_HW_CPU_CYCLES);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user);
return 0;
}
@@ -1396,10 +1423,10 @@ static int count_tracepoints(void)
return cnt;
}
-static int test__all_tracepoints(struct perf_evlist *evlist)
+static int test__all_tracepoints(struct evlist *evlist)
{
TEST_ASSERT_VAL("wrong events count",
- count_tracepoints() == evlist->nr_entries);
+ count_tracepoints() == evlist->core.nr_entries);
return test__checkevent_tracepoint_multi(evlist);
}
@@ -1409,7 +1436,7 @@ struct evlist_test {
__u32 type;
const int id;
bool (*valid)(void);
- int (*check)(struct perf_evlist *evlist);
+ int (*check)(struct evlist *evlist);
};
static struct evlist_test test__events[] = {
@@ -1642,6 +1669,7 @@ static struct evlist_test test__events[] = {
{
.name = "kvm-s390:kvm_s390_create_vm",
.check = test__checkevent_tracepoint,
+ .valid = kvm_s390_create_vm_valid,
.id = 100,
},
#endif
@@ -1742,7 +1770,7 @@ static struct terms_test test__terms[] = {
static int test_event(struct evlist_test *e)
{
struct parse_events_error err = { .idx = 0, };
- struct perf_evlist *evlist;
+ struct evlist *evlist;
int ret;
if (e->valid && !e->valid()) {
@@ -1750,7 +1778,7 @@ static int test_event(struct evlist_test *e)
return 0;
}
- evlist = perf_evlist__new();
+ evlist = evlist__new();
if (evlist == NULL)
return -ENOMEM;
@@ -1763,7 +1791,7 @@ static int test_event(struct evlist_test *e)
ret = e->check(evlist);
}
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
return ret;
}
diff --git a/tools/perf/tests/parse-no-sample-id-all.c b/tools/perf/tests/parse-no-sample-id-all.c
index 2196d1497c0c..8284752a60c8 100644
--- a/tools/perf/tests/parse-no-sample-id-all.c
+++ b/tools/perf/tests/parse-no-sample-id-all.c
@@ -11,7 +11,7 @@
#include "util.h"
#include "debug.h"
-static int process_event(struct perf_evlist **pevlist, union perf_event *event)
+static int process_event(struct evlist **pevlist, union perf_event *event)
{
struct perf_sample sample;
@@ -39,14 +39,14 @@ static int process_event(struct perf_evlist **pevlist, union perf_event *event)
static int process_events(union perf_event **events, size_t count)
{
- struct perf_evlist *evlist = NULL;
+ struct evlist *evlist = NULL;
int err = 0;
size_t i;
for (i = 0; i < count && !err; i++)
err = process_event(&evlist, events[i]);
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
return err;
}
@@ -87,10 +87,10 @@ int test__parse_no_sample_id_all(struct test *test __maybe_unused, int subtest _
},
.id = 2,
};
- struct mmap_event event3 = {
+ struct perf_record_mmap event3 = {
.header = {
.type = PERF_RECORD_MMAP,
- .size = sizeof(struct mmap_event),
+ .size = sizeof(struct perf_record_mmap),
},
};
union perf_event *events[] = {
diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c
index 07f6bd8ed719..e1b42292cf7f 100644
--- a/tools/perf/tests/perf-record.c
+++ b/tools/perf/tests/perf-record.c
@@ -1,14 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <inttypes.h>
+#include <linux/string.h>
/* For the CLR_() macros */
#include <pthread.h>
#include <sched.h>
#include "evlist.h"
#include "evsel.h"
-#include "perf.h"
#include "debug.h"
+#include "record.h"
#include "tests.h"
static int sched__get_first_possible_cpu(pid_t pid, cpu_set_t *maskp)
@@ -50,8 +51,8 @@ int test__PERF_RECORD(struct test *test __maybe_unused, int subtest __maybe_unus
};
cpu_set_t cpu_mask;
size_t cpu_mask_size = sizeof(cpu_mask);
- struct perf_evlist *evlist = perf_evlist__new_dummy();
- struct perf_evsel *evsel;
+ struct evlist *evlist = perf_evlist__new_dummy();
+ struct evsel *evsel;
struct perf_sample sample;
const char *cmd = "sleep";
const char *argv[] = { cmd, "1", NULL, };
@@ -130,7 +131,7 @@ int test__PERF_RECORD(struct test *test __maybe_unused, int subtest __maybe_unus
* Call sys_perf_event_open on all the fds on all the evsels,
* grouping them if asked to.
*/
- err = perf_evlist__open(evlist);
+ err = evlist__open(evlist);
if (err < 0) {
pr_debug("perf_evlist__open: %s\n",
str_error_r(errno, sbuf, sizeof(sbuf)));
@@ -153,7 +154,7 @@ int test__PERF_RECORD(struct test *test __maybe_unused, int subtest __maybe_unus
* Now that all is properly set up, enable the events, they will
* count just on workload.pid, which will start...
*/
- perf_evlist__enable(evlist);
+ evlist__enable(evlist);
/*
* Now!
@@ -325,7 +326,7 @@ found_exit:
++errs;
}
out_delete_evlist:
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
out:
return (err < 0 || errs > 0) ? -1 : 0;
}
diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c
index 236ce0d6c826..5fcc06817076 100644
--- a/tools/perf/tests/sample-parsing.c
+++ b/tools/perf/tests/sample-parsing.c
@@ -1,10 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdbool.h>
#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/types.h>
+#include "map_symbol.h"
#include "branch.h"
#include "util.h"
#include "event.h"
@@ -152,11 +155,13 @@ static bool samples_same(const struct perf_sample *s1,
static int do_test(u64 sample_type, u64 sample_regs, u64 read_format)
{
- struct perf_evsel evsel = {
+ struct evsel evsel = {
.needs_swap = false,
- .attr = {
- .sample_type = sample_type,
- .read_format = read_format,
+ .core = {
+ . attr = {
+ .sample_type = sample_type,
+ .read_format = read_format,
+ },
},
};
union perf_event *event;
@@ -220,10 +225,10 @@ static int do_test(u64 sample_type, u64 sample_regs, u64 read_format)
int err, ret = -1;
if (sample_type & PERF_SAMPLE_REGS_USER)
- evsel.attr.sample_regs_user = sample_regs;
+ evsel.core.attr.sample_regs_user = sample_regs;
if (sample_type & PERF_SAMPLE_REGS_INTR)
- evsel.attr.sample_regs_intr = sample_regs;
+ evsel.core.attr.sample_regs_intr = sample_regs;
for (i = 0; i < sizeof(regs); i++)
*(i + (u8 *)regs) = i & 0xfe;
diff --git a/tools/perf/tests/sdt.c b/tools/perf/tests/sdt.c
index 8bfaa630389c..cf1bd57d3023 100644
--- a/tools/perf/tests/sdt.c
+++ b/tools/perf/tests/sdt.c
@@ -1,14 +1,16 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
+#include <limits.h>
#include <stdio.h>
+#include <stdlib.h>
#include <sys/epoll.h>
-#include <util/evlist.h>
#include <util/symbol.h>
#include <linux/filter.h>
#include "tests.h"
#include "debug.h"
#include "probe-file.h"
#include "build-id.h"
+#include "util.h"
/* To test SDT event, we need libelf support to scan elf binary */
#if defined(HAVE_SDT_EVENT) && defined(HAVE_LIBELF_SUPPORT)
diff --git a/tools/perf/tests/shell/lib/probe.sh b/tools/perf/tests/shell/lib/probe.sh
index e37787be672b..51e3f60baba0 100644
--- a/tools/perf/tests/shell/lib/probe.sh
+++ b/tools/perf/tests/shell/lib/probe.sh
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
# Arnaldo Carvalho de Melo <acme@kernel.org>, 2017
skip_if_no_perf_probe() {
diff --git a/tools/perf/tests/shell/probe_vfs_getname.sh b/tools/perf/tests/shell/probe_vfs_getname.sh
index 46e076e3c537..5d1b63d3f3e1 100755
--- a/tools/perf/tests/shell/probe_vfs_getname.sh
+++ b/tools/perf/tests/shell/probe_vfs_getname.sh
@@ -1,6 +1,7 @@
#!/bin/sh
# Add vfs_getname probe to get syscall args filenames
-#
+
+# SPDX-License-Identifier: GPL-2.0
# Arnaldo Carvalho de Melo <acme@kernel.org>, 2017
. $(dirname $0)/lib/probe.sh
diff --git a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh
index 61c9f8fc6fa1..f12a4e217968 100755
--- a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh
+++ b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh
@@ -7,6 +7,7 @@
# This needs no debuginfo package, all is done using the libc ELF symtab
# and the CFI info in the binaries.
+# SPDX-License-Identifier: GPL-2.0
# Arnaldo Carvalho de Melo <acme@kernel.org>, 2017
. $(dirname $0)/lib/probe.sh
@@ -44,7 +45,7 @@ trace_libc_inet_pton_backtrace() {
eventattr='max-stack=4'
echo "gaih_inet.*\+0x[[:xdigit:]]+[[:space:]]\($libc\)$" >> $expected
echo "getaddrinfo\+0x[[:xdigit:]]+[[:space:]]\($libc\)$" >> $expected
- echo ".*\+0x[[:xdigit:]]+[[:space:]]\(.*/bin/ping.*\)$" >> $expected
+ echo ".*(\+0x[[:xdigit:]]+|\[unknown\])[[:space:]]\(.*/bin/ping.*\)$" >> $expected
;;
*)
eventattr='max-stack=3'
diff --git a/tools/perf/tests/shell/record+script_probe_vfs_getname.sh b/tools/perf/tests/shell/record+script_probe_vfs_getname.sh
index 9b073e7fa88c..54030c18bfc2 100755
--- a/tools/perf/tests/shell/record+script_probe_vfs_getname.sh
+++ b/tools/perf/tests/shell/record+script_probe_vfs_getname.sh
@@ -6,6 +6,7 @@
# checks that that was captured by the vfs_getname probe in the generated
# perf.data file, with the temp file name as the pathname argument.
+# SPDX-License-Identifier: GPL-2.0
# Arnaldo Carvalho de Melo <acme@kernel.org>, 2017
. $(dirname $0)/lib/probe.sh
diff --git a/tools/perf/tests/shell/record+zstd_comp_decomp.sh b/tools/perf/tests/shell/record+zstd_comp_decomp.sh
index 5dcba800109f..63a91ec473bb 100755
--- a/tools/perf/tests/shell/record+zstd_comp_decomp.sh
+++ b/tools/perf/tests/shell/record+zstd_comp_decomp.sh
@@ -1,6 +1,8 @@
#!/bin/sh
# Zstd perf.data compression/decompression
+# SPDX-License-Identifier: GPL-2.0
+
trace_file=$(mktemp /tmp/perf.data.XXX)
perf_tool=perf
@@ -11,7 +13,7 @@ skip_if_no_z_record() {
collect_z_record() {
echo "Collecting compressed record file:"
$perf_tool record -o $trace_file -g -z -F 5000 -- \
- dd count=500 if=/dev/random of=/dev/null
+ dd count=500 if=/dev/urandom of=/dev/null
}
check_compressed_stats() {
diff --git a/tools/perf/tests/shell/trace+probe_vfs_getname.sh b/tools/perf/tests/shell/trace+probe_vfs_getname.sh
index 147efeb6b195..11cc2af13f2b 100755
--- a/tools/perf/tests/shell/trace+probe_vfs_getname.sh
+++ b/tools/perf/tests/shell/trace+probe_vfs_getname.sh
@@ -7,6 +7,7 @@
# that already handles "probe:vfs_getname" if present, and used in the
# "open" syscall "filename" argument beautifier.
+# SPDX-License-Identifier: GPL-2.0
# Arnaldo Carvalho de Melo <acme@kernel.org>, 2017
. $(dirname $0)/lib/probe.sh
@@ -31,6 +32,10 @@ if [ $err -ne 0 ] ; then
exit $err
fi
+# Do not use whatever ~/.perfconfig file, it may change the output
+# via trace.{show_timestamp,show_prefix,etc}
+export PERF_CONFIG=/dev/null
+
trace_open_vfs_getname
err=$?
rm -f ${file}
diff --git a/tools/perf/tests/stat.c b/tools/perf/tests/stat.c
index 94250024684a..cc10b4116c9f 100644
--- a/tools/perf/tests/stat.c
+++ b/tools/perf/tests/stat.c
@@ -6,7 +6,7 @@
#include "counts.h"
#include "debug.h"
-static bool has_term(struct stat_config_event *config,
+static bool has_term(struct perf_record_stat_config *config,
u64 tag, u64 val)
{
unsigned i;
@@ -25,7 +25,7 @@ static int process_stat_config_event(struct perf_tool *tool __maybe_unused,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
- struct stat_config_event *config = &event->stat_config;
+ struct perf_record_stat_config *config = &event->stat_config;
struct perf_stat_config stat_config;
#define HAS(term, val) \
@@ -65,7 +65,7 @@ static int process_stat_event(struct perf_tool *tool __maybe_unused,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
- struct stat_event *st = &event->stat;
+ struct perf_record_stat *st = &event->stat;
TEST_ASSERT_VAL("wrong cpu", st->cpu == 1);
TEST_ASSERT_VAL("wrong thread", st->thread == 2);
@@ -95,7 +95,7 @@ static int process_stat_round_event(struct perf_tool *tool __maybe_unused,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
- struct stat_round_event *stat_round = &event->stat_round;
+ struct perf_record_stat_round *stat_round = &event->stat_round;
TEST_ASSERT_VAL("wrong time", stat_round->time == 0xdeadbeef);
TEST_ASSERT_VAL("wrong type", stat_round->type == PERF_STAT_ROUND_TYPE__INTERVAL);
diff --git a/tools/perf/tests/sw-clock.c b/tools/perf/tests/sw-clock.c
index f9490b237893..97694a040986 100644
--- a/tools/perf/tests/sw-clock.c
+++ b/tools/perf/tests/sw-clock.c
@@ -5,12 +5,15 @@
#include <stdlib.h>
#include <signal.h>
#include <sys/mman.h>
+#include <linux/string.h>
#include "tests.h"
+#include "util/debug.h"
#include "util/evsel.h"
#include "util/evlist.h"
#include "util/cpumap.h"
#include "util/thread_map.h"
+#include <perf/evlist.h>
#define NR_LOOPS 10000000
@@ -27,8 +30,8 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)
int nr_samples = 0;
char sbuf[STRERR_BUFSIZE];
union perf_event *event;
- struct perf_evsel *evsel;
- struct perf_evlist *evlist;
+ struct evsel *evsel;
+ struct evlist *evlist;
struct perf_event_attr attr = {
.type = PERF_TYPE_SOFTWARE,
.config = clock_id,
@@ -37,26 +40,26 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)
.disabled = 1,
.freq = 1,
};
- struct cpu_map *cpus;
- struct thread_map *threads;
+ struct perf_cpu_map *cpus;
+ struct perf_thread_map *threads;
struct perf_mmap *md;
attr.sample_freq = 500;
- evlist = perf_evlist__new();
+ evlist = evlist__new();
if (evlist == NULL) {
- pr_debug("perf_evlist__new\n");
+ pr_debug("evlist__new\n");
return -1;
}
- evsel = perf_evsel__new(&attr);
+ evsel = evsel__new(&attr);
if (evsel == NULL) {
pr_debug("perf_evsel__new\n");
goto out_delete_evlist;
}
- perf_evlist__add(evlist, evsel);
+ evlist__add(evlist, evsel);
- cpus = cpu_map__dummy_new();
+ cpus = perf_cpu_map__dummy_new();
threads = thread_map__new_by_tid(getpid());
if (!cpus || !threads) {
err = -ENOMEM;
@@ -64,12 +67,12 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)
goto out_free_maps;
}
- perf_evlist__set_maps(evlist, cpus, threads);
+ perf_evlist__set_maps(&evlist->core, cpus, threads);
cpus = NULL;
threads = NULL;
- if (perf_evlist__open(evlist)) {
+ if (evlist__open(evlist)) {
const char *knob = "/proc/sys/kernel/perf_event_max_sample_rate";
err = -errno;
@@ -86,13 +89,13 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)
goto out_delete_evlist;
}
- perf_evlist__enable(evlist);
+ evlist__enable(evlist);
/* collect samples */
for (i = 0; i < NR_LOOPS; i++)
tmp++;
- perf_evlist__disable(evlist);
+ evlist__disable(evlist);
md = &evlist->mmap[0];
if (perf_mmap__read_init(md) < 0)
@@ -125,10 +128,10 @@ out_init:
}
out_free_maps:
- cpu_map__put(cpus);
- thread_map__put(threads);
+ perf_cpu_map__put(cpus);
+ perf_thread_map__put(threads);
out_delete_evlist:
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
return err;
}
diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c
index 9b5be51e5e7b..1a60fa1219f5 100644
--- a/tools/perf/tests/switch-tracking.c
+++ b/tools/perf/tests/switch-tracking.c
@@ -2,14 +2,20 @@
#include <sys/time.h>
#include <sys/prctl.h>
#include <errno.h>
+#include <limits.h>
#include <time.h>
#include <stdlib.h>
+#include <linux/zalloc.h>
+#include <perf/cpumap.h>
+#include <perf/evlist.h>
+#include "debug.h"
#include "parse-events.h"
#include "evlist.h"
#include "evsel.h"
#include "thread_map.h"
#include "cpumap.h"
+#include "record.h"
#include "tests.h"
static int spin_sleep(void)
@@ -51,8 +57,8 @@ static int spin_sleep(void)
}
struct switch_tracking {
- struct perf_evsel *switch_evsel;
- struct perf_evsel *cycles_evsel;
+ struct evsel *switch_evsel;
+ struct evsel *cycles_evsel;
pid_t *tids;
int nr_tids;
int comm_seen[4];
@@ -112,12 +118,12 @@ static int check_cpu(struct switch_tracking *switch_tracking, int cpu)
return 0;
}
-static int process_sample_event(struct perf_evlist *evlist,
+static int process_sample_event(struct evlist *evlist,
union perf_event *event,
struct switch_tracking *switch_tracking)
{
struct perf_sample sample;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
pid_t next_tid, prev_tid;
int cpu, err;
@@ -162,7 +168,7 @@ static int process_sample_event(struct perf_evlist *evlist,
return 0;
}
-static int process_event(struct perf_evlist *evlist, union perf_event *event,
+static int process_event(struct evlist *evlist, union perf_event *event,
struct switch_tracking *switch_tracking)
{
if (event->header.type == PERF_RECORD_SAMPLE)
@@ -202,7 +208,7 @@ struct event_node {
u64 event_time;
};
-static int add_event(struct perf_evlist *evlist, struct list_head *events,
+static int add_event(struct evlist *evlist, struct list_head *events,
union perf_event *event)
{
struct perf_sample sample;
@@ -237,7 +243,7 @@ static void free_event_nodes(struct list_head *events)
while (!list_empty(events)) {
node = list_entry(events->next, struct event_node, list);
- list_del(&node->list);
+ list_del_init(&node->list);
free(node);
}
}
@@ -251,7 +257,7 @@ static int compar(const void *a, const void *b)
return cmp;
}
-static int process_events(struct perf_evlist *evlist,
+static int process_events(struct evlist *evlist,
struct switch_tracking *switch_tracking)
{
union perf_event *event;
@@ -326,11 +332,11 @@ int test__switch_tracking(struct test *test __maybe_unused, int subtest __maybe_
.uses_mmap = true,
},
};
- struct thread_map *threads = NULL;
- struct cpu_map *cpus = NULL;
- struct perf_evlist *evlist = NULL;
- struct perf_evsel *evsel, *cpu_clocks_evsel, *cycles_evsel;
- struct perf_evsel *switch_evsel, *tracking_evsel;
+ struct perf_thread_map *threads = NULL;
+ struct perf_cpu_map *cpus = NULL;
+ struct evlist *evlist = NULL;
+ struct evsel *evsel, *cpu_clocks_evsel, *cycles_evsel;
+ struct evsel *switch_evsel, *tracking_evsel;
const char *comm;
int err = -1;
@@ -340,19 +346,19 @@ int test__switch_tracking(struct test *test __maybe_unused, int subtest __maybe_
goto out_err;
}
- cpus = cpu_map__new(NULL);
+ cpus = perf_cpu_map__new(NULL);
if (!cpus) {
- pr_debug("cpu_map__new failed!\n");
+ pr_debug("perf_cpu_map__new failed!\n");
goto out_err;
}
- evlist = perf_evlist__new();
+ evlist = evlist__new();
if (!evlist) {
- pr_debug("perf_evlist__new failed!\n");
+ pr_debug("evlist__new failed!\n");
goto out_err;
}
- perf_evlist__set_maps(evlist, cpus, threads);
+ perf_evlist__set_maps(&evlist->core, cpus, threads);
/* First event */
err = parse_events(evlist, "cpu-clock:u", NULL);
@@ -419,8 +425,8 @@ int test__switch_tracking(struct test *test __maybe_unused, int subtest __maybe_
perf_evlist__set_tracking_event(evlist, tracking_evsel);
- tracking_evsel->attr.freq = 0;
- tracking_evsel->attr.sample_period = 1;
+ tracking_evsel->core.attr.freq = 0;
+ tracking_evsel->core.attr.sample_period = 1;
perf_evsel__set_sample_bit(tracking_evsel, TIME);
@@ -434,7 +440,7 @@ int test__switch_tracking(struct test *test __maybe_unused, int subtest __maybe_
}
/* Check tracking event is tracking */
- if (!tracking_evsel->attr.mmap || !tracking_evsel->attr.comm) {
+ if (!tracking_evsel->core.attr.mmap || !tracking_evsel->core.attr.comm) {
pr_debug("Tracking event not tracking\n");
goto out_err;
}
@@ -442,14 +448,14 @@ int test__switch_tracking(struct test *test __maybe_unused, int subtest __maybe_
/* Check non-tracking events are not tracking */
evlist__for_each_entry(evlist, evsel) {
if (evsel != tracking_evsel) {
- if (evsel->attr.mmap || evsel->attr.comm) {
+ if (evsel->core.attr.mmap || evsel->core.attr.comm) {
pr_debug("Non-tracking event is tracking\n");
goto out_err;
}
}
}
- if (perf_evlist__open(evlist) < 0) {
+ if (evlist__open(evlist) < 0) {
pr_debug("Not supported\n");
err = 0;
goto out;
@@ -461,9 +467,9 @@ int test__switch_tracking(struct test *test __maybe_unused, int subtest __maybe_
goto out_err;
}
- perf_evlist__enable(evlist);
+ evlist__enable(evlist);
- err = perf_evsel__disable(cpu_clocks_evsel);
+ err = evsel__disable(cpu_clocks_evsel);
if (err) {
pr_debug("perf_evlist__disable_event failed!\n");
goto out_err;
@@ -482,7 +488,7 @@ int test__switch_tracking(struct test *test __maybe_unused, int subtest __maybe_
goto out_err;
}
- err = perf_evsel__disable(cycles_evsel);
+ err = evsel__disable(cycles_evsel);
if (err) {
pr_debug("perf_evlist__disable_event failed!\n");
goto out_err;
@@ -508,7 +514,7 @@ int test__switch_tracking(struct test *test __maybe_unused, int subtest __maybe_
goto out_err;
}
- err = perf_evsel__enable(cycles_evsel);
+ err = evsel__enable(cycles_evsel);
if (err) {
pr_debug("perf_evlist__disable_event failed!\n");
goto out_err;
@@ -527,7 +533,7 @@ int test__switch_tracking(struct test *test __maybe_unused, int subtest __maybe_
goto out_err;
}
- perf_evlist__disable(evlist);
+ evlist__disable(evlist);
switch_tracking.switch_evsel = switch_evsel;
switch_tracking.cycles_evsel = cycles_evsel;
@@ -565,11 +571,11 @@ int test__switch_tracking(struct test *test __maybe_unused, int subtest __maybe_
}
out:
if (evlist) {
- perf_evlist__disable(evlist);
- perf_evlist__delete(evlist);
+ evlist__disable(evlist);
+ evlist__delete(evlist);
} else {
- cpu_map__put(cpus);
- thread_map__put(threads);
+ perf_cpu_map__put(cpus);
+ perf_thread_map__put(threads);
}
return err;
diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c
index e92fa6029ac7..f610e8c0a083 100644
--- a/tools/perf/tests/task-exit.c
+++ b/tools/perf/tests/task-exit.c
@@ -1,12 +1,16 @@
// SPDX-License-Identifier: GPL-2.0
+#include "debug.h"
#include "evlist.h"
#include "evsel.h"
+#include "target.h"
#include "thread_map.h"
#include "cpumap.h"
#include "tests.h"
#include <errno.h>
#include <signal.h>
+#include <linux/string.h>
+#include <perf/evlist.h>
static int exited;
static int nr_exit;
@@ -37,16 +41,16 @@ int test__task_exit(struct test *test __maybe_unused, int subtest __maybe_unused
{
int err = -1;
union perf_event *event;
- struct perf_evsel *evsel;
- struct perf_evlist *evlist;
+ struct evsel *evsel;
+ struct evlist *evlist;
struct target target = {
.uid = UINT_MAX,
.uses_mmap = true,
};
const char *argv[] = { "true", NULL };
char sbuf[STRERR_BUFSIZE];
- struct cpu_map *cpus;
- struct thread_map *threads;
+ struct perf_cpu_map *cpus;
+ struct perf_thread_map *threads;
struct perf_mmap *md;
signal(SIGCHLD, sig_handler);
@@ -63,7 +67,7 @@ int test__task_exit(struct test *test __maybe_unused, int subtest __maybe_unused
* perf_evlist__prepare_workload we'll fill in the only thread
* we're monitoring, the one forked there.
*/
- cpus = cpu_map__dummy_new();
+ cpus = perf_cpu_map__dummy_new();
threads = thread_map__new_by_tid(-1);
if (!cpus || !threads) {
err = -ENOMEM;
@@ -71,7 +75,7 @@ int test__task_exit(struct test *test __maybe_unused, int subtest __maybe_unused
goto out_free_maps;
}
- perf_evlist__set_maps(evlist, cpus, threads);
+ perf_evlist__set_maps(&evlist->core, cpus, threads);
cpus = NULL;
threads = NULL;
@@ -84,18 +88,18 @@ int test__task_exit(struct test *test __maybe_unused, int subtest __maybe_unused
}
evsel = perf_evlist__first(evlist);
- evsel->attr.task = 1;
+ evsel->core.attr.task = 1;
#ifdef __s390x__
- evsel->attr.sample_freq = 1000000;
+ evsel->core.attr.sample_freq = 1000000;
#else
- evsel->attr.sample_freq = 1;
+ evsel->core.attr.sample_freq = 1;
#endif
- evsel->attr.inherit = 0;
- evsel->attr.watermark = 0;
- evsel->attr.wakeup_events = 1;
- evsel->attr.exclude_kernel = 1;
+ evsel->core.attr.inherit = 0;
+ evsel->core.attr.watermark = 0;
+ evsel->core.attr.wakeup_events = 1;
+ evsel->core.attr.exclude_kernel = 1;
- err = perf_evlist__open(evlist);
+ err = evlist__open(evlist);
if (err < 0) {
pr_debug("Couldn't open the evlist: %s\n",
str_error_r(-err, sbuf, sizeof(sbuf)));
@@ -135,9 +139,9 @@ out_init:
}
out_free_maps:
- cpu_map__put(cpus);
- thread_map__put(threads);
+ perf_cpu_map__put(cpus);
+ perf_thread_map__put(threads);
out_delete_evlist:
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
return err;
}
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index 399f18ca71a3..72912eb473cb 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -107,6 +107,8 @@ const char *test__clang_subtest_get_desc(int subtest);
int test__clang_subtest_get_nr(void);
int test__unit_number__scnprint(struct test *test, int subtest);
int test__mem2node(struct test *t, int subtest);
+int test__map_groups__merge_in(struct test *t, int subtest);
+int test__time_utils(struct test *t, int subtest);
bool test__bp_signal_is_supported(void);
bool test__wp_is_supported(void);
diff --git a/tools/perf/tests/thread-map.c b/tools/perf/tests/thread-map.c
index 4de1939b58ba..39168c57943b 100644
--- a/tools/perf/tests/thread-map.c
+++ b/tools/perf/tests/thread-map.c
@@ -1,18 +1,26 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdlib.h>
+#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/prctl.h>
#include "tests.h"
#include "thread_map.h"
#include "debug.h"
+#include "event.h"
+#include <linux/zalloc.h>
+#include <perf/event.h>
+
+struct perf_sample;
+struct perf_tool;
+struct machine;
#define NAME (const char *) "perf"
#define NAMEUL (unsigned long) NAME
int test__thread_map(struct test *test __maybe_unused, int subtest __maybe_unused)
{
- struct thread_map *map;
+ struct perf_thread_map *map;
TEST_ASSERT_VAL("failed to set process name",
!prctl(PR_SET_NAME, NAMEUL, 0, 0, 0));
@@ -25,28 +33,28 @@ int test__thread_map(struct test *test __maybe_unused, int subtest __maybe_unuse
TEST_ASSERT_VAL("wrong nr", map->nr == 1);
TEST_ASSERT_VAL("wrong pid",
- thread_map__pid(map, 0) == getpid());
+ perf_thread_map__pid(map, 0) == getpid());
TEST_ASSERT_VAL("wrong comm",
- thread_map__comm(map, 0) &&
- !strcmp(thread_map__comm(map, 0), NAME));
+ perf_thread_map__comm(map, 0) &&
+ !strcmp(perf_thread_map__comm(map, 0), NAME));
TEST_ASSERT_VAL("wrong refcnt",
refcount_read(&map->refcnt) == 1);
- thread_map__put(map);
+ perf_thread_map__put(map);
/* test dummy pid */
- map = thread_map__new_dummy();
+ map = perf_thread_map__new_dummy();
TEST_ASSERT_VAL("failed to alloc map", map);
thread_map__read_comms(map);
TEST_ASSERT_VAL("wrong nr", map->nr == 1);
- TEST_ASSERT_VAL("wrong pid", thread_map__pid(map, 0) == -1);
+ TEST_ASSERT_VAL("wrong pid", perf_thread_map__pid(map, 0) == -1);
TEST_ASSERT_VAL("wrong comm",
- thread_map__comm(map, 0) &&
- !strcmp(thread_map__comm(map, 0), "dummy"));
+ perf_thread_map__comm(map, 0) &&
+ !strcmp(perf_thread_map__comm(map, 0), "dummy"));
TEST_ASSERT_VAL("wrong refcnt",
refcount_read(&map->refcnt) == 1);
- thread_map__put(map);
+ perf_thread_map__put(map);
return 0;
}
@@ -55,8 +63,8 @@ static int process_event(struct perf_tool *tool __maybe_unused,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
- struct thread_map_event *map = &event->thread_map;
- struct thread_map *threads;
+ struct perf_record_thread_map *map = &event->thread_map;
+ struct perf_thread_map *threads;
TEST_ASSERT_VAL("wrong nr", map->nr == 1);
TEST_ASSERT_VAL("wrong pid", map->entries[0].pid == (u64) getpid());
@@ -67,19 +75,19 @@ static int process_event(struct perf_tool *tool __maybe_unused,
TEST_ASSERT_VAL("wrong nr", threads->nr == 1);
TEST_ASSERT_VAL("wrong pid",
- thread_map__pid(threads, 0) == getpid());
+ perf_thread_map__pid(threads, 0) == getpid());
TEST_ASSERT_VAL("wrong comm",
- thread_map__comm(threads, 0) &&
- !strcmp(thread_map__comm(threads, 0), NAME));
+ perf_thread_map__comm(threads, 0) &&
+ !strcmp(perf_thread_map__comm(threads, 0), NAME));
TEST_ASSERT_VAL("wrong refcnt",
refcount_read(&threads->refcnt) == 1);
- thread_map__put(threads);
+ perf_thread_map__put(threads);
return 0;
}
int test__thread_map_synthesize(struct test *test __maybe_unused, int subtest __maybe_unused)
{
- struct thread_map *threads;
+ struct perf_thread_map *threads;
TEST_ASSERT_VAL("failed to set process name",
!prctl(PR_SET_NAME, NAMEUL, 0, 0, 0));
@@ -98,7 +106,7 @@ int test__thread_map_synthesize(struct test *test __maybe_unused, int subtest __
int test__thread_map_remove(struct test *test __maybe_unused, int subtest __maybe_unused)
{
- struct thread_map *threads;
+ struct perf_thread_map *threads;
char *str;
int i;
@@ -133,7 +141,7 @@ int test__thread_map_remove(struct test *test __maybe_unused, int subtest __mayb
thread_map__remove(threads, 0));
for (i = 0; i < threads->nr; i++)
- free(threads->map[i].comm);
+ zfree(&threads->map[i].comm);
free(threads);
return 0;
diff --git a/tools/perf/tests/thread-mg-share.c b/tools/perf/tests/thread-mg-share.c
index b1d1bbafe7ae..cbac71716dec 100644
--- a/tools/perf/tests/thread-mg-share.c
+++ b/tools/perf/tests/thread-mg-share.c
@@ -2,7 +2,6 @@
#include "tests.h"
#include "machine.h"
#include "thread.h"
-#include "map.h"
#include "debug.h"
int test__thread_mg_share(struct test *test __maybe_unused, int subtest __maybe_unused)
diff --git a/tools/perf/tests/time-utils-test.c b/tools/perf/tests/time-utils-test.c
new file mode 100644
index 000000000000..fe57ca3b6e54
--- /dev/null
+++ b/tools/perf/tests/time-utils-test.c
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/compiler.h>
+#include <linux/time64.h>
+#include <inttypes.h>
+#include <string.h>
+#include "time-utils.h"
+#include "evlist.h"
+#include "session.h"
+#include "debug.h"
+#include "tests.h"
+
+static bool test__parse_nsec_time(const char *str, u64 expected)
+{
+ u64 ptime;
+ int err;
+
+ pr_debug("\nparse_nsec_time(\"%s\")\n", str);
+
+ err = parse_nsec_time(str, &ptime);
+ if (err) {
+ pr_debug("error %d\n", err);
+ return false;
+ }
+
+ if (ptime != expected) {
+ pr_debug("Failed. ptime %" PRIu64 " expected %" PRIu64 "\n",
+ ptime, expected);
+ return false;
+ }
+
+ pr_debug("%" PRIu64 "\n", ptime);
+
+ return true;
+}
+
+static bool test__perf_time__parse_str(const char *ostr, u64 start, u64 end)
+{
+ struct perf_time_interval ptime;
+ int err;
+
+ pr_debug("\nperf_time__parse_str(\"%s\")\n", ostr);
+
+ err = perf_time__parse_str(&ptime, ostr);
+ if (err) {
+ pr_debug("Error %d\n", err);
+ return false;
+ }
+
+ if (ptime.start != start || ptime.end != end) {
+ pr_debug("Failed. Expected %" PRIu64 " to %" PRIu64 "\n",
+ start, end);
+ return false;
+ }
+
+ return true;
+}
+
+#define TEST_MAX 64
+
+struct test_data {
+ const char *str;
+ u64 first;
+ u64 last;
+ struct perf_time_interval ptime[TEST_MAX];
+ int num;
+ u64 skip[TEST_MAX];
+ u64 noskip[TEST_MAX];
+};
+
+static bool test__perf_time__parse_for_ranges(struct test_data *d)
+{
+ struct evlist evlist = {
+ .first_sample_time = d->first,
+ .last_sample_time = d->last,
+ };
+ struct perf_session session = { .evlist = &evlist };
+ struct perf_time_interval *ptime = NULL;
+ int range_size, range_num;
+ bool pass = false;
+ int i, err;
+
+ pr_debug("\nperf_time__parse_for_ranges(\"%s\")\n", d->str);
+
+ if (strchr(d->str, '%'))
+ pr_debug("first_sample_time %" PRIu64 " last_sample_time %" PRIu64 "\n",
+ d->first, d->last);
+
+ err = perf_time__parse_for_ranges(d->str, &session, &ptime, &range_size,
+ &range_num);
+ if (err) {
+ pr_debug("error %d\n", err);
+ goto out;
+ }
+
+ if (range_size < d->num || range_num != d->num) {
+ pr_debug("bad size: range_size %d range_num %d expected num %d\n",
+ range_size, range_num, d->num);
+ goto out;
+ }
+
+ for (i = 0; i < d->num; i++) {
+ if (ptime[i].start != d->ptime[i].start ||
+ ptime[i].end != d->ptime[i].end) {
+ pr_debug("bad range %d expected %" PRIu64 " to %" PRIu64 "\n",
+ i, d->ptime[i].start, d->ptime[i].end);
+ goto out;
+ }
+ }
+
+ if (perf_time__ranges_skip_sample(ptime, d->num, 0)) {
+ pr_debug("failed to keep 0\n");
+ goto out;
+ }
+
+ for (i = 0; i < TEST_MAX; i++) {
+ if (d->skip[i] &&
+ !perf_time__ranges_skip_sample(ptime, d->num, d->skip[i])) {
+ pr_debug("failed to skip %" PRIu64 "\n", d->skip[i]);
+ goto out;
+ }
+ if (d->noskip[i] &&
+ perf_time__ranges_skip_sample(ptime, d->num, d->noskip[i])) {
+ pr_debug("failed to keep %" PRIu64 "\n", d->noskip[i]);
+ goto out;
+ }
+ }
+
+ pass = true;
+out:
+ free(ptime);
+ return pass;
+}
+
+int test__time_utils(struct test *t __maybe_unused, int subtest __maybe_unused)
+{
+ bool pass = true;
+
+ pass &= test__parse_nsec_time("0", 0);
+ pass &= test__parse_nsec_time("1", 1000000000ULL);
+ pass &= test__parse_nsec_time("0.000000001", 1);
+ pass &= test__parse_nsec_time("1.000000001", 1000000001ULL);
+ pass &= test__parse_nsec_time("123456.123456", 123456123456000ULL);
+ pass &= test__parse_nsec_time("1234567.123456789", 1234567123456789ULL);
+ pass &= test__parse_nsec_time("18446744073.709551615",
+ 0xFFFFFFFFFFFFFFFFULL);
+
+ pass &= test__perf_time__parse_str("1234567.123456789,1234567.123456789",
+ 1234567123456789ULL, 1234567123456789ULL);
+ pass &= test__perf_time__parse_str("1234567.123456789,1234567.123456790",
+ 1234567123456789ULL, 1234567123456790ULL);
+ pass &= test__perf_time__parse_str("1234567.123456789,",
+ 1234567123456789ULL, 0);
+ pass &= test__perf_time__parse_str(",1234567.123456789",
+ 0, 1234567123456789ULL);
+ pass &= test__perf_time__parse_str("0,1234567.123456789",
+ 0, 1234567123456789ULL);
+
+ {
+ u64 b = 1234567123456789ULL;
+ struct test_data d = {
+ .str = "1234567.123456789,1234567.123456790",
+ .ptime = { {b, b + 1}, },
+ .num = 1,
+ .skip = { b - 1, b + 2, },
+ .noskip = { b, b + 1, },
+ };
+
+ pass &= test__perf_time__parse_for_ranges(&d);
+ }
+
+ {
+ u64 b = 1234567123456789ULL;
+ u64 c = 7654321987654321ULL;
+ u64 e = 8000000000000000ULL;
+ struct test_data d = {
+ .str = "1234567.123456789,1234567.123456790 "
+ "7654321.987654321,7654321.987654444 "
+ "8000000,8000000.000000005",
+ .ptime = { {b, b + 1}, {c, c + 123}, {e, e + 5}, },
+ .num = 3,
+ .skip = { b - 1, b + 2, c - 1, c + 124, e - 1, e + 6 },
+ .noskip = { b, b + 1, c, c + 123, e, e + 5 },
+ };
+
+ pass &= test__perf_time__parse_for_ranges(&d);
+ }
+
+ {
+ u64 b = 7654321ULL * NSEC_PER_SEC;
+ struct test_data d = {
+ .str = "10%/1",
+ .first = b,
+ .last = b + 100,
+ .ptime = { {b, b + 9}, },
+ .num = 1,
+ .skip = { b - 1, b + 10, },
+ .noskip = { b, b + 9, },
+ };
+
+ pass &= test__perf_time__parse_for_ranges(&d);
+ }
+
+ {
+ u64 b = 7654321ULL * NSEC_PER_SEC;
+ struct test_data d = {
+ .str = "10%/2",
+ .first = b,
+ .last = b + 100,
+ .ptime = { {b + 10, b + 19}, },
+ .num = 1,
+ .skip = { b + 9, b + 20, },
+ .noskip = { b + 10, b + 19, },
+ };
+
+ pass &= test__perf_time__parse_for_ranges(&d);
+ }
+
+ {
+ u64 b = 11223344ULL * NSEC_PER_SEC;
+ struct test_data d = {
+ .str = "10%/1,10%/2",
+ .first = b,
+ .last = b + 100,
+ .ptime = { {b, b + 9}, {b + 10, b + 19}, },
+ .num = 2,
+ .skip = { b - 1, b + 20, },
+ .noskip = { b, b + 8, b + 9, b + 10, b + 11, b + 12, b + 19, },
+ };
+
+ pass &= test__perf_time__parse_for_ranges(&d);
+ }
+
+ {
+ u64 b = 11223344ULL * NSEC_PER_SEC;
+ struct test_data d = {
+ .str = "10%/1,10%/3,10%/10",
+ .first = b,
+ .last = b + 100,
+ .ptime = { {b, b + 9}, {b + 20, b + 29}, { b + 90, b + 100}, },
+ .num = 3,
+ .skip = { b - 1, b + 10, b + 19, b + 30, b + 89, b + 101 },
+ .noskip = { b, b + 9, b + 20, b + 29, b + 90, b + 100},
+ };
+
+ pass &= test__perf_time__parse_for_ranges(&d);
+ }
+
+ pr_debug("\n");
+
+ return pass ? 0 : TEST_FAIL;
+}
diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c
index 9497d02f69e6..a4f9f5182b47 100644
--- a/tools/perf/tests/topology.c
+++ b/tools/perf/tests/topology.c
@@ -2,6 +2,7 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
+#include <perf/cpumap.h>
#include "tests.h"
#include "util.h"
#include "session.h"
@@ -57,7 +58,7 @@ static int session_write_header(char *path)
return 0;
}
-static int check_cpu_topology(char *path, struct cpu_map *map)
+static int check_cpu_topology(char *path, struct perf_cpu_map *map)
{
struct perf_session *session;
struct perf_data data = {
@@ -116,7 +117,7 @@ static int check_cpu_topology(char *path, struct cpu_map *map)
int test__session_topology(struct test *test __maybe_unused, int subtest __maybe_unused)
{
char path[PATH_MAX];
- struct cpu_map *map;
+ struct perf_cpu_map *map;
int ret = TEST_FAIL;
TEST_ASSERT_VAL("can't get templ file", !get_temp(path));
@@ -126,14 +127,14 @@ int test__session_topology(struct test *test __maybe_unused, int subtest __maybe
if (session_write_header(path))
goto free_path;
- map = cpu_map__new(NULL);
+ map = perf_cpu_map__new(NULL);
if (map == NULL) {
pr_debug("failed to get system cpumap\n");
goto free_path;
}
ret = check_cpu_topology(path, map);
- cpu_map__put(map);
+ perf_cpu_map__put(map);
free_path:
unlink(path);
diff --git a/tools/perf/tests/unit_number__scnprintf.c b/tools/perf/tests/unit_number__scnprintf.c
index 2bb8cb0039c1..3721757435da 100644
--- a/tools/perf/tests/unit_number__scnprintf.c
+++ b/tools/perf/tests/unit_number__scnprintf.c
@@ -2,6 +2,7 @@
#include <inttypes.h>
#include <linux/compiler.h>
#include <linux/types.h>
+#include <string.h>
#include "tests.h"
#include "units.h"
#include "debug.h"
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c
index 7691980b7df1..01f434c067c6 100644
--- a/tools/perf/tests/vmlinux-kallsyms.c
+++ b/tools/perf/tests/vmlinux-kallsyms.c
@@ -3,6 +3,8 @@
#include <linux/rbtree.h>
#include <inttypes.h>
#include <string.h>
+#include <stdlib.h>
+#include "dso.h"
#include "map.h"
#include "symbol.h"
#include "util.h"
@@ -161,9 +163,16 @@ next_pair:
continue;
}
- } else
+ } else if (mem_start == kallsyms.vmlinux_map->end) {
+ /*
+ * Ignore aliases to _etext, i.e. to the end of the kernel text area,
+ * such as __indirect_thunk_end.
+ */
+ continue;
+ } else {
pr_debug("ERR : %#" PRIx64 ": %s not on kallsyms\n",
mem_start, sym->name);
+ }
err = -1;
}
diff --git a/tools/perf/tests/wp.c b/tools/perf/tests/wp.c
index f89e6806557b..d262d6639829 100644
--- a/tools/perf/tests/wp.c
+++ b/tools/perf/tests/wp.c
@@ -1,10 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/hw_breakpoint.h>
+#include <linux/kernel.h>
#include "tests.h"
#include "debug.h"
+#include "event.h"
#include "cloexec.h"
+#include "../perf-sys.h"
#define WP_TEST_ASSERT_VAL(fd, text, val) \
do { \
diff --git a/tools/perf/trace/beauty/Build b/tools/perf/trace/beauty/Build
index 85f328ddf897..afa75a76f6b8 100644
--- a/tools/perf/trace/beauty/Build
+++ b/tools/perf/trace/beauty/Build
@@ -1,11 +1,14 @@
perf-y += clone.o
perf-y += fcntl.o
perf-y += flock.o
+perf-y += fsmount.o
+perf-y += fspick.o
ifeq ($(SRCARCH),$(filter $(SRCARCH),x86))
perf-y += ioctl.o
endif
perf-y += kcmp.o
perf-y += mount_flags.o
+perf-y += move_mount.o
perf-y += pkey_alloc.o
perf-y += arch_prctl.o
perf-y += prctl.o
@@ -13,3 +16,4 @@ perf-y += renameat.o
perf-y += sockaddr.o
perf-y += socket.o
perf-y += statx.o
+perf-y += sync_file_range.o
diff --git a/tools/perf/trace/beauty/beauty.h b/tools/perf/trace/beauty/beauty.h
index 139d485a6f16..7e06605f7c76 100644
--- a/tools/perf/trace/beauty/beauty.h
+++ b/tools/perf/trace/beauty/beauty.h
@@ -108,6 +108,9 @@ struct syscall_arg {
unsigned long syscall_arg__val(struct syscall_arg *arg, u8 idx);
+size_t syscall_arg__scnprintf_strarray_flags(char *bf, size_t size, struct syscall_arg *arg);
+#define SCA_STRARRAY_FLAGS syscall_arg__scnprintf_strarray_flags
+
size_t syscall_arg__scnprintf_strarrays(char *bf, size_t size, struct syscall_arg *arg);
#define SCA_STRARRAYS syscall_arg__scnprintf_strarrays
@@ -141,6 +144,12 @@ size_t syscall_arg__scnprintf_fcntl_arg(char *bf, size_t size, struct syscall_ar
size_t syscall_arg__scnprintf_flock(char *bf, size_t size, struct syscall_arg *arg);
#define SCA_FLOCK syscall_arg__scnprintf_flock
+size_t syscall_arg__scnprintf_fsmount_attr_flags(char *bf, size_t size, struct syscall_arg *arg);
+#define SCA_FSMOUNT_ATTR_FLAGS syscall_arg__scnprintf_fsmount_attr_flags
+
+size_t syscall_arg__scnprintf_fspick_flags(char *bf, size_t size, struct syscall_arg *arg);
+#define SCA_FSPICK_FLAGS syscall_arg__scnprintf_fspick_flags
+
size_t syscall_arg__scnprintf_ioctl_cmd(char *bf, size_t size, struct syscall_arg *arg);
#define SCA_IOCTL_CMD syscall_arg__scnprintf_ioctl_cmd
@@ -156,6 +165,9 @@ unsigned long syscall_arg__mask_val_mount_flags(struct syscall_arg *arg, unsigne
size_t syscall_arg__scnprintf_mount_flags(char *bf, size_t size, struct syscall_arg *arg);
#define SCA_MOUNT_FLAGS syscall_arg__scnprintf_mount_flags
+size_t syscall_arg__scnprintf_move_mount_flags(char *bf, size_t size, struct syscall_arg *arg);
+#define SCA_MOVE_MOUNT_FLAGS syscall_arg__scnprintf_move_mount_flags
+
size_t syscall_arg__scnprintf_pkey_alloc_access_rights(char *bf, size_t size, struct syscall_arg *arg);
#define SCA_PKEY_ALLOC_ACCESS_RIGHTS syscall_arg__scnprintf_pkey_alloc_access_rights
@@ -189,6 +201,9 @@ size_t syscall_arg__scnprintf_statx_flags(char *bf, size_t size, struct syscall_
size_t syscall_arg__scnprintf_statx_mask(char *bf, size_t size, struct syscall_arg *arg);
#define SCA_STATX_MASK syscall_arg__scnprintf_statx_mask
+size_t syscall_arg__scnprintf_sync_file_range_flags(char *bf, size_t size, struct syscall_arg *arg);
+#define SCA_SYNC_FILE_RANGE_FLAGS syscall_arg__scnprintf_sync_file_range_flags
+
size_t open__scnprintf_flags(unsigned long flags, char *bf, size_t size, bool show_prefix);
void syscall_arg__set_ret_scnprintf(struct syscall_arg *arg,
diff --git a/tools/perf/trace/beauty/clone.c b/tools/perf/trace/beauty/clone.c
index 6eb9a6636171..1a8d3be2030e 100644
--- a/tools/perf/trace/beauty/clone.c
+++ b/tools/perf/trace/beauty/clone.c
@@ -25,6 +25,7 @@ static size_t clone__scnprintf_flags(unsigned long flags, char *bf, size_t size,
P_FLAG(FS);
P_FLAG(FILES);
P_FLAG(SIGHAND);
+ P_FLAG(PIDFD);
P_FLAG(PTRACE);
P_FLAG(VFORK);
P_FLAG(PARENT);
diff --git a/tools/perf/trace/beauty/fsconfig.sh b/tools/perf/trace/beauty/fsconfig.sh
new file mode 100755
index 000000000000..83fb24df05c9
--- /dev/null
+++ b/tools/perf/trace/beauty/fsconfig.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+# SPDX-License-Identifier: LGPL-2.1
+
+if [ $# -ne 1 ] ; then
+ linux_header_dir=tools/include/uapi/linux
+else
+ linux_header_dir=$1
+fi
+
+linux_mount=${linux_header_dir}/mount.h
+
+printf "static const char *fsconfig_cmds[] = {\n"
+regex='^[[:space:]]*+FSCONFIG_([[:alnum:]_]+)[[:space:]]*=[[:space:]]*([[:digit:]]+)[[:space:]]*,[[:space:]]*.*'
+egrep $regex ${linux_mount} | \
+ sed -r "s/$regex/\2 \1/g" | \
+ xargs printf "\t[%s] = \"%s\",\n"
+printf "};\n"
diff --git a/tools/perf/trace/beauty/fsmount.c b/tools/perf/trace/beauty/fsmount.c
new file mode 100644
index 000000000000..30c8c082a3c3
--- /dev/null
+++ b/tools/perf/trace/beauty/fsmount.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * trace/beauty/fsmount.c
+ *
+ * Copyright (C) 2019, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
+ */
+
+#include "trace/beauty/beauty.h"
+#include <linux/log2.h>
+#include <uapi/linux/mount.h>
+
+static size_t fsmount__scnprintf_attr_flags(unsigned long flags, char *bf, size_t size, bool show_prefix)
+{
+#include "trace/beauty/generated/fsmount_arrays.c"
+ static DEFINE_STRARRAY(fsmount_attr_flags, "MOUNT_ATTR_");
+ size_t printed = 0;
+
+ if ((flags & ~MOUNT_ATTR__ATIME) != 0)
+ printed += strarray__scnprintf_flags(&strarray__fsmount_attr_flags, bf, size, show_prefix, flags);
+
+ if ((flags & MOUNT_ATTR__ATIME) == MOUNT_ATTR_RELATIME) {
+ printed += scnprintf(bf + printed, size - printed, "%s%s%s",
+ printed ? "|" : "", show_prefix ? "MOUNT_ATTR_" : "", "RELATIME");
+ }
+
+ return printed;
+}
+
+size_t syscall_arg__scnprintf_fsmount_attr_flags(char *bf, size_t size, struct syscall_arg *arg)
+{
+ unsigned long flags = arg->val;
+
+ return fsmount__scnprintf_attr_flags(flags, bf, size, arg->show_string_prefix);
+}
diff --git a/tools/perf/trace/beauty/fsmount.sh b/tools/perf/trace/beauty/fsmount.sh
new file mode 100755
index 000000000000..615cc0fcf4f9
--- /dev/null
+++ b/tools/perf/trace/beauty/fsmount.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+# SPDX-License-Identifier: LGPL-2.1
+
+if [ $# -ne 1 ] ; then
+ linux_header_dir=tools/include/uapi/linux
+else
+ linux_header_dir=$1
+fi
+
+linux_mount=${linux_header_dir}/mount.h
+
+# Remove MOUNT_ATTR_RELATIME as it is zeros, handle it a special way in the beautifier
+# Only handle MOUNT_ATTR_ followed by a capital letter/num as __ is special case
+# for things like MOUNT_ATTR__ATIME that is a mask for the possible ATIME handling
+# bits. Special case it as well in the beautifier
+
+printf "static const char *fsmount_attr_flags[] = {\n"
+regex='^[[:space:]]*#[[:space:]]*define[[:space:]]+MOUNT_ATTR_([[:alnum:]][[:alnum:]_]+)[[:space:]]+(0x[[:xdigit:]]+)[[:space:]]*.*'
+egrep $regex ${linux_mount} | grep -v MOUNT_ATTR_RELATIME | \
+ sed -r "s/$regex/\2 \1/g" | \
+ xargs printf "\t[ilog2(%s) + 1] = \"%s\",\n"
+printf "};\n"
diff --git a/tools/perf/trace/beauty/fspick.c b/tools/perf/trace/beauty/fspick.c
new file mode 100644
index 000000000000..c402479c96f0
--- /dev/null
+++ b/tools/perf/trace/beauty/fspick.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * trace/beauty/fspick.c
+ *
+ * Copyright (C) 2019, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
+ */
+
+#include "trace/beauty/beauty.h"
+#include <linux/log2.h>
+
+static size_t fspick__scnprintf_flags(unsigned long flags, char *bf, size_t size, bool show_prefix)
+{
+#include "trace/beauty/generated/fspick_arrays.c"
+ static DEFINE_STRARRAY(fspick_flags, "FSPICK_");
+
+ return strarray__scnprintf_flags(&strarray__fspick_flags, bf, size, show_prefix, flags);
+}
+
+size_t syscall_arg__scnprintf_fspick_flags(char *bf, size_t size, struct syscall_arg *arg)
+{
+ unsigned long flags = arg->val;
+
+ return fspick__scnprintf_flags(flags, bf, size, arg->show_string_prefix);
+}
diff --git a/tools/perf/trace/beauty/fspick.sh b/tools/perf/trace/beauty/fspick.sh
new file mode 100755
index 000000000000..b220e07ef452
--- /dev/null
+++ b/tools/perf/trace/beauty/fspick.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+# SPDX-License-Identifier: LGPL-2.1
+
+if [ $# -ne 1 ] ; then
+ linux_header_dir=tools/include/uapi/linux
+else
+ linux_header_dir=$1
+fi
+
+linux_mount=${linux_header_dir}/mount.h
+
+printf "static const char *fspick_flags[] = {\n"
+regex='^[[:space:]]*#[[:space:]]*define[[:space:]]+FSPICK_([[:alnum:]_]+)[[:space:]]+(0x[[:xdigit:]]+)[[:space:]]*.*'
+egrep $regex ${linux_mount} | \
+ sed -r "s/$regex/\2 \1/g" | \
+ xargs printf "\t[ilog2(%s) + 1] = \"%s\",\n"
+printf "};\n"
diff --git a/tools/perf/trace/beauty/ioctl.c b/tools/perf/trace/beauty/ioctl.c
index 52242fa4072b..e19eb6ea361d 100644
--- a/tools/perf/trace/beauty/ioctl.c
+++ b/tools/perf/trace/beauty/ioctl.c
@@ -21,7 +21,7 @@
static size_t ioctl__scnprintf_tty_cmd(int nr, int dir, char *bf, size_t size)
{
static const char *ioctl_tty_cmd[] = {
- "TCGETS", "TCSETS", "TCSETSW", "TCSETSF", "TCGETA", "TCSETA", "TCSETAW",
+ [_IOC_NR(TCGETS)] = "TCGETS", "TCSETS", "TCSETSW", "TCSETSF", "TCGETA", "TCSETA", "TCSETAW",
"TCSETAF", "TCSBRK", "TCXONC", "TCFLSH", "TIOCEXCL", "TIOCNXCL", "TIOCSCTTY",
"TIOCGPGRP", "TIOCSPGRP", "TIOCOUTQ", "TIOCSTI", "TIOCGWINSZ", "TIOCSWINSZ",
"TIOCMGET", "TIOCMBIS", "TIOCMBIC", "TIOCMSET", "TIOCGSOFTCAR", "TIOCSSOFTCAR",
diff --git a/tools/perf/trace/beauty/move_mount.c b/tools/perf/trace/beauty/move_mount.c
new file mode 100644
index 000000000000..78ed80395406
--- /dev/null
+++ b/tools/perf/trace/beauty/move_mount.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * trace/beauty/move_mount.c
+ *
+ * Copyright (C) 2019, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
+ */
+
+#include "trace/beauty/beauty.h"
+#include <linux/log2.h>
+
+static size_t move_mount__scnprintf_flags(unsigned long flags, char *bf, size_t size, bool show_prefix)
+{
+#include "trace/beauty/generated/move_mount_flags_array.c"
+ static DEFINE_STRARRAY(move_mount_flags, "MOVE_MOUNT_");
+
+ return strarray__scnprintf_flags(&strarray__move_mount_flags, bf, size, show_prefix, flags);
+}
+
+size_t syscall_arg__scnprintf_move_mount_flags(char *bf, size_t size, struct syscall_arg *arg)
+{
+ unsigned long flags = arg->val;
+
+ return move_mount__scnprintf_flags(flags, bf, size, arg->show_string_prefix);
+}
diff --git a/tools/perf/trace/beauty/move_mount_flags.sh b/tools/perf/trace/beauty/move_mount_flags.sh
new file mode 100755
index 000000000000..55e59241daa4
--- /dev/null
+++ b/tools/perf/trace/beauty/move_mount_flags.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+# SPDX-License-Identifier: LGPL-2.1
+
+if [ $# -ne 1 ] ; then
+ linux_header_dir=tools/include/uapi/linux
+else
+ linux_header_dir=$1
+fi
+
+linux_mount=${linux_header_dir}/mount.h
+
+printf "static const char *move_mount_flags[] = {\n"
+regex='^[[:space:]]*#[[:space:]]*define[[:space:]]+MOVE_MOUNT_([FT]_[[:alnum:]_]+)[[:space:]]+(0x[[:xdigit:]]+)[[:space:]]*.*'
+egrep $regex ${linux_mount} | \
+ sed -r "s/$regex/\2 \1/g" | \
+ xargs printf "\t[ilog2(%s) + 1] = \"%s\",\n"
+printf "};\n"
diff --git a/tools/perf/trace/beauty/sync_file_range.c b/tools/perf/trace/beauty/sync_file_range.c
new file mode 100644
index 000000000000..1c425f04047d
--- /dev/null
+++ b/tools/perf/trace/beauty/sync_file_range.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * trace/beauty/sync_file_range.c
+ *
+ * Copyright (C) 2019, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
+ */
+
+#include "trace/beauty/beauty.h"
+#include <linux/log2.h>
+#include <uapi/linux/fs.h>
+
+static size_t sync_file_range__scnprintf_flags(unsigned long flags, char *bf, size_t size, bool show_prefix)
+{
+#include "trace/beauty/generated/sync_file_range_arrays.c"
+ static DEFINE_STRARRAY(sync_file_range_flags, "SYNC_FILE_RANGE_");
+ size_t printed = 0;
+
+ if ((flags & SYNC_FILE_RANGE_WRITE_AND_WAIT) == SYNC_FILE_RANGE_WRITE_AND_WAIT) {
+ printed += scnprintf(bf + printed, size - printed, "%s%s", show_prefix ? "SYNC_FILE_RANGE_" : "", "WRITE_AND_WAIT");
+ flags &= ~SYNC_FILE_RANGE_WRITE_AND_WAIT;
+ }
+
+ return printed + strarray__scnprintf_flags(&strarray__sync_file_range_flags, bf + printed, size - printed, show_prefix, flags);
+}
+
+size_t syscall_arg__scnprintf_sync_file_range_flags(char *bf, size_t size, struct syscall_arg *arg)
+{
+ unsigned long flags = arg->val;
+
+ return sync_file_range__scnprintf_flags(flags, bf, size, arg->show_string_prefix);
+}
diff --git a/tools/perf/trace/beauty/sync_file_range.sh b/tools/perf/trace/beauty/sync_file_range.sh
new file mode 100755
index 000000000000..7a9282d04e44
--- /dev/null
+++ b/tools/perf/trace/beauty/sync_file_range.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+# SPDX-License-Identifier: LGPL-2.1
+
+if [ $# -ne 1 ] ; then
+ linux_header_dir=tools/include/uapi/linux
+else
+ linux_header_dir=$1
+fi
+
+linux_fs=${linux_header_dir}/fs.h
+
+printf "static const char *sync_file_range_flags[] = {\n"
+regex='^[[:space:]]*#[[:space:]]*define[[:space:]]+SYNC_FILE_RANGE_([[:alnum:]_]+)[[:space:]]+([[:xdigit:]]+)[[:space:]]*.*'
+egrep $regex ${linux_fs} | \
+ sed -r "s/$regex/\2 \1/g" | \
+ xargs printf "\t[ilog2(%s) + 1] = \"%s\",\n"
+printf "};\n"
diff --git a/tools/perf/trace/beauty/usbdevfs_ioctl.sh b/tools/perf/trace/beauty/usbdevfs_ioctl.sh
index 930b80f422e8..aa597ae53747 100755
--- a/tools/perf/trace/beauty/usbdevfs_ioctl.sh
+++ b/tools/perf/trace/beauty/usbdevfs_ioctl.sh
@@ -3,10 +3,13 @@
[ $# -eq 1 ] && header_dir=$1 || header_dir=tools/include/uapi/linux/
+# also as:
+# #define USBDEVFS_CONNINFO_EX(len) _IOC(_IOC_READ, 'U', 32, len)
+
printf "static const char *usbdevfs_ioctl_cmds[] = {\n"
-regex="^#[[:space:]]*define[[:space:]]+USBDEVFS_(\w+)[[:space:]]+_IO[WR]{0,2}\([[:space:]]*'U'[[:space:]]*,[[:space:]]*([[:digit:]]+).*"
-egrep $regex ${header_dir}/usbdevice_fs.h | egrep -v 'USBDEVFS_\w+32[[:space:]]' | \
- sed -r "s/$regex/\2 \1/g" | \
+regex="^#[[:space:]]*define[[:space:]]+USBDEVFS_(\w+)(\(\w+\))?[[:space:]]+_IO[CWR]{0,2}\([[:space:]]*(_IOC_\w+,[[:space:]]*)?'U'[[:space:]]*,[[:space:]]*([[:digit:]]+).*"
+egrep "$regex" ${header_dir}/usbdevice_fs.h | egrep -v 'USBDEVFS_\w+32[[:space:]]' | \
+ sed -r "s/$regex/\4 \1/g" | \
sort | xargs printf "\t[%s] = \"%s\",\n"
printf "};\n\n"
printf "#if 0\n"
diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c
index 4ad37d8c7d6a..f93d40b1c203 100644
--- a/tools/perf/ui/browser.c
+++ b/tools/perf/ui/browser.c
@@ -1,8 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
-#include "../util.h"
-#include "../string2.h"
-#include "../config.h"
-#include "../../perf.h"
+#include "../util/util.h"
+#include "../util/string2.h"
+#include "../util/config.h"
#include "libslang.h"
#include "ui.h"
#include "util.h"
@@ -15,8 +14,9 @@
#include "browser.h"
#include "helpline.h"
#include "keysyms.h"
-#include "../color.h"
-#include "sane_ctype.h"
+#include "../util/color.h"
+#include <linux/ctype.h>
+#include <linux/zalloc.h>
static int ui_browser__percent_color(struct ui_browser *browser,
double percent, bool current)
@@ -346,6 +346,8 @@ static int __ui_browser__refresh(struct ui_browser *browser)
SLsmg_fill_region(browser->y + row + browser->extra_title_lines, browser->x,
browser->rows - row, width, ' ');
+ if (browser->nr_entries == 0 && browser->no_samples_msg)
+ __ui__info_window(NULL, browser->no_samples_msg, NULL);
return 0;
}
@@ -594,7 +596,7 @@ static int ui_browser__color_config(const char *var, const char *value,
break;
*bg = '\0';
- bg = ltrim(++bg);
+ bg = skip_spaces(bg + 1);
ui_browser__colorsets[i].bg = bg;
ui_browser__colorsets[i].fg = fg;
return 0;
diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h
index aa5932e1d62e..3678eb88f119 100644
--- a/tools/perf/ui/browser.h
+++ b/tools/perf/ui/browser.h
@@ -4,6 +4,7 @@
#include <linux/types.h>
#include <stdarg.h>
+#include <sys/types.h>
#define HE_COLORSET_TOP 50
#define HE_COLORSET_MEDIUM 51
@@ -22,6 +23,7 @@ struct ui_browser {
void *priv;
const char *title;
char *helpline;
+ const char *no_samples_msg;
void (*refresh_dimensions)(struct ui_browser *browser);
unsigned int (*refresh)(struct ui_browser *browser);
void (*write)(struct ui_browser *browser, void *entry, int row);
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index 98d934a36d86..ac74ed2c23a0 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -1,10 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
-#include "../../util/util.h"
#include "../browser.h"
#include "../helpline.h"
#include "../ui.h"
#include "../util.h"
#include "../../util/annotate.h"
+#include "../../util/debug.h"
+#include "../../util/dso.h"
#include "../../util/hist.h"
#include "../../util/sort.h"
#include "../../util/map.h"
@@ -15,6 +16,7 @@
#include <pthread.h>
#include <linux/kernel.h>
#include <linux/string.h>
+#include <linux/zalloc.h>
#include <sys/ttydefaults.h>
#include <asm/bug.h>
@@ -97,11 +99,12 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
struct annotation *notes = browser__annotation(browser);
struct annotation_line *al = list_entry(entry, struct annotation_line, node);
+ const bool is_current_entry = ui_browser__is_current_entry(browser, row);
struct annotation_write_ops ops = {
.first_line = row == 0,
- .current_entry = ui_browser__is_current_entry(browser, row),
+ .current_entry = is_current_entry,
.change_color = (!notes->options->hide_src_code &&
- (!ops.current_entry ||
+ (!is_current_entry ||
(browser->use_navkeypressed &&
!browser->navkeypressed))),
.width = browser->width,
@@ -298,7 +301,7 @@ static void annotate_browser__set_rb_top(struct annotate_browser *browser,
}
static void annotate_browser__calc_percent(struct annotate_browser *browser,
- struct perf_evsel *evsel)
+ struct evsel *evsel)
{
struct map_symbol *ms = browser->b.priv;
struct symbol *sym = ms->sym;
@@ -405,7 +408,7 @@ static int sym_title(struct symbol *sym, struct map *map, char *title,
* to the calling function.
*/
static bool annotate_browser__callq(struct annotate_browser *browser,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct hist_browser_timer *hbt)
{
struct map_symbol *ms = browser->b.priv;
@@ -421,7 +424,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
notes = symbol__annotation(dl->ops.target.sym);
pthread_mutex_lock(&notes->lock);
- if (!symbol__hists(dl->ops.target.sym, evsel->evlist->nr_entries)) {
+ if (!symbol__hists(dl->ops.target.sym, evsel->evlist->core.nr_entries)) {
pthread_mutex_unlock(&notes->lock);
ui__warning("Not enough memory for annotating '%s' symbol!\n",
dl->ops.target.sym->name);
@@ -454,7 +457,7 @@ struct disasm_line *annotate_browser__find_offset(struct annotate_browser *brows
}
static bool annotate_browser__jump(struct annotate_browser *browser,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct hist_browser_timer *hbt)
{
struct disasm_line *dl = disasm_line(browser->selection);
@@ -655,7 +658,7 @@ switch_percent_type(struct annotation_options *opts, bool base)
}
static int annotate_browser__run(struct annotate_browser *browser,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct hist_browser_timer *hbt)
{
struct rb_node *nd = NULL;
@@ -868,14 +871,14 @@ out:
return key;
}
-int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
+int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
struct hist_browser_timer *hbt,
struct annotation_options *opts)
{
return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt, opts);
}
-int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
+int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
struct hist_browser_timer *hbt,
struct annotation_options *opts)
{
@@ -887,7 +890,7 @@ int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
}
int symbol__tui_annotate(struct symbol *sym, struct map *map,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct hist_browser_timer *hbt,
struct annotation_options *opts)
{
diff --git a/tools/perf/ui/browsers/header.c b/tools/perf/ui/browsers/header.c
index 5aeb663dd184..0f59a7001479 100644
--- a/tools/perf/ui/browsers/header.c
+++ b/tools/perf/ui/browsers/header.c
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
-#include "util/cache.h"
#include "util/debug.h"
#include "ui/browser.h"
#include "ui/keysyms.h"
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 3421ecbdd3f0..589168ca9f62 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -6,21 +6,28 @@
#include <stdlib.h>
#include <string.h>
#include <linux/rbtree.h>
+#include <linux/string.h>
#include <sys/ttydefaults.h>
#include <linux/time64.h>
+#include <linux/zalloc.h>
+#include "../../util/debug.h"
+#include "../../util/dso.h"
#include "../../util/callchain.h"
#include "../../util/evsel.h"
#include "../../util/evlist.h"
+#include "../../util/header.h"
#include "../../util/hist.h"
#include "../../util/map.h"
#include "../../util/symbol.h"
+#include "../../util/map_symbol.h"
+#include "../../util/branch.h"
#include "../../util/pstack.h"
#include "../../util/sort.h"
-#include "../../util/util.h"
#include "../../util/top.h"
#include "../../util/thread.h"
#include "../../arch/common.h"
+#include "../../perf.h"
#include "../browsers/hists.h"
#include "../helpline.h"
@@ -33,7 +40,7 @@
#include "units.h"
#include "time-utils.h"
-#include "sane_ctype.h"
+#include <linux/ctype.h>
extern void hist_browser__init_hpp(void);
@@ -638,7 +645,11 @@ int hist_browser__run(struct hist_browser *browser, const char *help,
switch (key) {
case K_TIMER: {
u64 nr_entries;
- hbt->timer(hbt->arg);
+
+ WARN_ON_ONCE(!hbt);
+
+ if (hbt)
+ hbt->timer(hbt->arg);
if (hist_browser__has_filter(browser) ||
symbol_conf.report_hierarchy)
@@ -1470,7 +1481,7 @@ static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
int i = 0;
width -= fmt->entry(fmt, &hpp, entry);
- ui_browser__printf(&browser->b, "%s", ltrim(s));
+ ui_browser__printf(&browser->b, "%s", skip_spaces(s));
while (isspace(s[i++]))
width++;
@@ -1686,7 +1697,7 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows
ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
dummy_hpp.buf[ret] = '\0';
- start = trim(dummy_hpp.buf);
+ start = strim(dummy_hpp.buf);
ret = strlen(start);
if (start != dummy_hpp.buf)
@@ -2070,7 +2081,8 @@ static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
advance_hpp(&hpp, ret);
}
- printed += fprintf(fp, "%s\n", rtrim(s));
+ strim(s);
+ printed += fprintf(fp, "%s\n", s);
if (he->leaf && folded_sign == '-') {
printed += hist_browser__fprintf_callchain(browser, he, fp,
@@ -2181,7 +2193,7 @@ struct hist_browser *hist_browser__new(struct hists *hists)
}
static struct hist_browser *
-perf_evsel_browser__new(struct perf_evsel *evsel,
+perf_evsel_browser__new(struct evsel *evsel,
struct hist_browser_timer *hbt,
struct perf_env *env,
struct annotation_options *annotation_opts)
@@ -2346,7 +2358,7 @@ struct popup_action {
struct thread *thread;
struct map_symbol ms;
int socket;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
enum rstype rstype;
int (*fn)(struct hist_browser *browser, struct popup_action *act);
@@ -2355,7 +2367,7 @@ struct popup_action {
static int
do_annotate(struct hist_browser *browser, struct popup_action *act)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct annotation *notes;
struct hist_entry *he;
int err;
@@ -2590,7 +2602,7 @@ static int
add_script_opt_2(struct hist_browser *browser __maybe_unused,
struct popup_action *act, char **optstr,
struct thread *thread, struct symbol *sym,
- struct perf_evsel *evsel, const char *tstr)
+ struct evsel *evsel, const char *tstr)
{
if (thread) {
@@ -2617,7 +2629,7 @@ static int
add_script_opt(struct hist_browser *browser,
struct popup_action *act, char **optstr,
struct thread *thread, struct symbol *sym,
- struct perf_evsel *evsel)
+ struct evsel *evsel)
{
int n, j;
struct hist_entry *he;
@@ -2647,7 +2659,7 @@ static int
add_res_sample_opt(struct hist_browser *browser __maybe_unused,
struct popup_action *act, char **optstr,
struct res_sample *res_sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
enum rstype type)
{
if (!res_sample)
@@ -2808,7 +2820,7 @@ next:
}
}
-static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
+static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events,
const char *helpline,
bool left_exits,
struct hist_browser_timer *hbt,
@@ -2819,7 +2831,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
{
struct hists *hists = evsel__hists(evsel);
struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts);
- struct branch_info *bi;
+ struct branch_info *bi = NULL;
#define MAX_OPTIONS 16
char *options[MAX_OPTIONS];
struct popup_action actions[MAX_OPTIONS];
@@ -2888,6 +2900,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
if (symbol_conf.col_width_list_str)
perf_hpp__set_user_width(symbol_conf.col_width_list_str);
+ if (!is_report_browser(hbt))
+ browser->b.no_samples_msg = "Collecting samples...";
+
while (1) {
struct thread *thread = NULL;
struct map *map = NULL;
@@ -3085,7 +3100,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
goto skip_annotation;
if (sort__mode == SORT_MODE__BRANCH) {
- bi = browser->he_selection->branch_info;
+
+ if (browser->he_selection)
+ bi = browser->he_selection->branch_info;
if (bi == NULL)
goto skip_annotation;
@@ -3190,9 +3207,9 @@ out:
return key;
}
-struct perf_evsel_menu {
+struct evsel_menu {
struct ui_browser b;
- struct perf_evsel *selection;
+ struct evsel *selection;
struct annotation_options *annotation_opts;
bool lost_events, lost_events_warned;
float min_pcnt;
@@ -3202,9 +3219,9 @@ struct perf_evsel_menu {
static void perf_evsel_menu__write(struct ui_browser *browser,
void *entry, int row)
{
- struct perf_evsel_menu *menu = container_of(browser,
- struct perf_evsel_menu, b);
- struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
+ struct evsel_menu *menu = container_of(browser,
+ struct evsel_menu, b);
+ struct evsel *evsel = list_entry(entry, struct evsel, core.node);
struct hists *hists = evsel__hists(evsel);
bool current_entry = ui_browser__is_current_entry(browser, row);
unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
@@ -3217,7 +3234,7 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
HE_COLORSET_NORMAL);
if (perf_evsel__is_group_event(evsel)) {
- struct perf_evsel *pos;
+ struct evsel *pos;
ev_name = perf_evsel__group_name(evsel);
@@ -3249,13 +3266,13 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
menu->selection = evsel;
}
-static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
+static int perf_evsel_menu__run(struct evsel_menu *menu,
int nr_events, const char *help,
struct hist_browser_timer *hbt,
bool warn_lost_event)
{
- struct perf_evlist *evlist = menu->b.priv;
- struct perf_evsel *pos;
+ struct evlist *evlist = menu->b.priv;
+ struct evsel *pos;
const char *title = "Available samples";
int delay_secs = hbt ? hbt->refresh : 0;
int key;
@@ -3269,7 +3286,8 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
switch (key) {
case K_TIMER:
- hbt->timer(hbt->arg);
+ if (hbt)
+ hbt->timer(hbt->arg);
if (!menu->lost_events_warned &&
menu->lost_events &&
@@ -3300,13 +3318,13 @@ browse_hists:
ui_browser__show_title(&menu->b, title);
switch (key) {
case K_TAB:
- if (pos->node.next == &evlist->entries)
+ if (pos->core.node.next == &evlist->core.entries)
pos = perf_evlist__first(evlist);
else
pos = perf_evsel__next(pos);
goto browse_hists;
case K_UNTAB:
- if (pos->node.prev == &evlist->entries)
+ if (pos->core.node.prev == &evlist->core.entries)
pos = perf_evlist__last(evlist);
else
pos = perf_evsel__prev(pos);
@@ -3342,7 +3360,7 @@ out:
static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
void *entry)
{
- struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
+ struct evsel *evsel = list_entry(entry, struct evsel, core.node);
if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
return true;
@@ -3350,7 +3368,7 @@ static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
return false;
}
-static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
+static int __perf_evlist__tui_browse_hists(struct evlist *evlist,
int nr_entries, const char *help,
struct hist_browser_timer *hbt,
float min_pcnt,
@@ -3358,10 +3376,10 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
bool warn_lost_event,
struct annotation_options *annotation_opts)
{
- struct perf_evsel *pos;
- struct perf_evsel_menu menu = {
+ struct evsel *pos;
+ struct evsel_menu menu = {
.b = {
- .entries = &evlist->entries,
+ .entries = &evlist->core.entries,
.refresh = ui_browser__list_head_refresh,
.seek = ui_browser__list_head_seek,
.write = perf_evsel_menu__write,
@@ -3388,18 +3406,18 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
hbt, warn_lost_event);
}
-int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
+int perf_evlist__tui_browse_hists(struct evlist *evlist, const char *help,
struct hist_browser_timer *hbt,
float min_pcnt,
struct perf_env *env,
bool warn_lost_event,
struct annotation_options *annotation_opts)
{
- int nr_entries = evlist->nr_entries;
+ int nr_entries = evlist->core.nr_entries;
single_entry:
if (nr_entries == 1) {
- struct perf_evsel *first = perf_evlist__first(evlist);
+ struct evsel *first = perf_evlist__first(evlist);
return perf_evsel__hists_browse(first, nr_entries, help,
false, hbt, min_pcnt,
@@ -3408,7 +3426,7 @@ single_entry:
}
if (symbol_conf.event_group) {
- struct perf_evsel *pos;
+ struct evsel *pos;
nr_entries = 0;
evlist__for_each_entry(evlist, pos) {
diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c
index c70d9337405b..893b065971f6 100644
--- a/tools/perf/ui/browsers/map.c
+++ b/tools/perf/ui/browsers/map.c
@@ -2,18 +2,20 @@
#include <elf.h>
#include <inttypes.h>
#include <sys/ttydefaults.h>
+#include <stdlib.h>
#include <string.h>
#include <linux/bitops.h>
#include "../../util/util.h"
#include "../../util/debug.h"
#include "../../util/map.h"
+#include "../../util/dso.h"
#include "../../util/symbol.h"
#include "../browser.h"
#include "../helpline.h"
#include "../keysyms.h"
#include "map.h"
-#include "sane_ctype.h"
+#include <linux/ctype.h>
struct map_browser {
struct ui_browser b;
diff --git a/tools/perf/ui/browsers/res_sample.c b/tools/perf/ui/browsers/res_sample.c
index c0dd73176d42..f16a38fea45e 100644
--- a/tools/perf/ui/browsers/res_sample.c
+++ b/tools/perf/ui/browsers/res_sample.c
@@ -1,13 +1,18 @@
// SPDX-License-Identifier: GPL-2.0
/* Display a menu with individual samples to browse with perf script */
-#include "util.h"
#include "hist.h"
#include "evsel.h"
#include "hists.h"
#include "sort.h"
#include "config.h"
#include "time-utils.h"
+#include "../util.h"
+#include "../../util/util.h"
+#include "../../perf.h"
+#include <stdlib.h>
+#include <string.h>
#include <linux/time64.h>
+#include <linux/zalloc.h>
static u64 context_len = 10 * NSEC_PER_MSEC;
@@ -24,7 +29,7 @@ void res_sample_init(void)
}
int res_sample_browse(struct res_sample *res_samples, int num_res,
- struct perf_evsel *evsel, enum rstype rstype)
+ struct evsel *evsel, enum rstype rstype)
{
char **names;
int i, n;
@@ -46,14 +51,14 @@ int res_sample_browse(struct res_sample *res_samples, int num_res,
if (asprintf(&names[i], "%s: CPU %d tid %d", tbuf,
res_samples[i].cpu, res_samples[i].tid) < 0) {
while (--i >= 0)
- free(names[i]);
+ zfree(&names[i]);
free(names);
return -1;
}
}
choice = ui__popup_menu(num_res, names);
for (i = 0; i < num_res; i++)
- free(names[i]);
+ zfree(&names[i]);
free(names);
if (choice < 0 || choice >= num_res)
@@ -66,7 +71,7 @@ int res_sample_browse(struct res_sample *res_samples, int num_res,
timestamp__scnprintf_nsec(r->time, tsample, sizeof tsample);
- attr_to_script(extra_format, &evsel->attr);
+ attr_to_script(extra_format, &evsel->core.attr);
if (asprintf(&cmd, "%s script %s%s --time %s %s%s %s%s --ns %s %s %s %s %s | less +/%s",
perf,
diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scripts.c
index 27cf3ab88d13..586a21acc13d 100644
--- a/tools/perf/ui/browsers/scripts.c
+++ b/tools/perf/ui/browsers/scripts.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
-#include "../../util/sort.h"
+#include "../../builtin.h"
+#include "../../perf.h"
#include "../../util/util.h"
#include "../../util/hist.h"
#include "../../util/debug.h"
@@ -7,6 +8,9 @@
#include "../browser.h"
#include "../libslang.h"
#include "config.h"
+#include <linux/string.h>
+#include <linux/zalloc.h>
+#include <stdlib.h>
#define SCRIPT_NAMELEN 128
#define SCRIPT_MAX_NO 64
@@ -78,7 +82,7 @@ static int scripts_config(const char *var, const char *value, void *data)
* Return -1 on failure.
*/
static int list_scripts(char *script_name, bool *custom,
- struct perf_evsel *evsel)
+ struct evsel *evsel)
{
char *buf, *paths[SCRIPT_MAX_NO], *names[SCRIPT_MAX_NO];
int i, num, choice;
@@ -100,7 +104,7 @@ static int list_scripts(char *script_name, bool *custom,
return -1;
if (evsel)
- attr_to_script(scriptc.extra_format, &evsel->attr);
+ attr_to_script(scriptc.extra_format, &evsel->core.attr);
add_script_option("Show individual samples", "", &scriptc);
add_script_option("Show individual samples with assembler", "-F +insn --xed",
&scriptc);
@@ -131,8 +135,10 @@ static int list_scripts(char *script_name, bool *custom,
int key = ui_browser__input_window("perf script command",
"Enter perf script command line (without perf script prefix)",
script_args, "", 0);
- if (key != K_ENTER)
- return -1;
+ if (key != K_ENTER) {
+ ret = -1;
+ goto out;
+ }
sprintf(script_name, "%s script %s", perf, script_args);
} else if (choice < num + max_std) {
strcpy(script_name, paths[choice]);
@@ -142,7 +148,7 @@ static int list_scripts(char *script_name, bool *custom,
out:
free(buf);
for (i = 0; i < max_std; i++)
- free(paths[i]);
+ zfree(&paths[i]);
return ret;
}
@@ -162,7 +168,7 @@ void run_script(char *cmd)
SLsmg_refresh();
}
-int script_browse(const char *script_opt, struct perf_evsel *evsel)
+int script_browse(const char *script_opt, struct evsel *evsel)
{
char *cmd, script_name[SCRIPT_FULLPATH_LEN];
bool custom = false;
diff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c
index df49c9ba1785..8e744af24f7c 100644
--- a/tools/perf/ui/gtk/annotate.c
+++ b/tools/perf/ui/gtk/annotate.c
@@ -5,6 +5,7 @@
#include "util/annotate.h"
#include "util/evsel.h"
#include "util/map.h"
+#include "util/dso.h"
#include "util/symbol.h"
#include "ui/helpline.h"
#include <inttypes.h>
@@ -91,7 +92,7 @@ static int perf_gtk__get_line(char *buf, size_t size, struct disasm_line *dl)
}
static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym,
- struct map *map, struct perf_evsel *evsel,
+ struct map *map, struct evsel *evsel,
struct hist_browser_timer *hbt __maybe_unused)
{
struct disasm_line *pos, *n;
@@ -129,7 +130,7 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym,
gtk_list_store_append(store, &iter);
if (perf_evsel__is_group_event(evsel)) {
- for (i = 0; i < evsel->nr_members; i++) {
+ for (i = 0; i < evsel->core.nr_members; i++) {
ret += perf_gtk__get_percent(s + ret,
sizeof(s) - ret,
sym, pos,
@@ -152,7 +153,7 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym,
gtk_container_add(GTK_CONTAINER(window), view);
list_for_each_entry_safe(pos, n, &notes->src->source, al.node) {
- list_del(&pos->al.node);
+ list_del_init(&pos->al.node);
disasm_line__free(pos);
}
@@ -160,7 +161,7 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym,
}
static int symbol__gtk_annotate(struct symbol *sym, struct map *map,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct hist_browser_timer *hbt)
{
GtkWidget *window;
@@ -238,7 +239,7 @@ static int symbol__gtk_annotate(struct symbol *sym, struct map *map,
}
int hist_entry__gtk_annotate(struct hist_entry *he,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct hist_browser_timer *hbt)
{
return symbol__gtk_annotate(he->ms.sym, he->ms.map, evsel, hbt);
diff --git a/tools/perf/ui/gtk/browser.c b/tools/perf/ui/gtk/browser.c
index 4820e25ac68d..8f3e43d148a8 100644
--- a/tools/perf/ui/gtk/browser.c
+++ b/tools/perf/ui/gtk/browser.c
@@ -1,6 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
-#include "../evlist.h"
-#include "../cache.h"
#include "../evsel.h"
#include "../sort.h"
#include "../hist.h"
diff --git a/tools/perf/ui/gtk/gtk.h b/tools/perf/ui/gtk/gtk.h
index 9846ea5c831b..a9563932fa04 100644
--- a/tools/perf/ui/gtk/gtk.h
+++ b/tools/perf/ui/gtk/gtk.h
@@ -52,16 +52,16 @@ static inline GtkWidget *perf_gtk__setup_info_bar(void)
}
#endif
-struct perf_evsel;
-struct perf_evlist;
+struct evsel;
+struct evlist;
struct hist_entry;
struct hist_browser_timer;
-int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help,
+int perf_evlist__gtk_browse_hists(struct evlist *evlist, const char *help,
struct hist_browser_timer *hbt,
float min_pcnt);
int hist_entry__gtk_annotate(struct hist_entry *he,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct hist_browser_timer *hbt);
void perf_gtk__show_annotations(void);
diff --git a/tools/perf/ui/gtk/helpline.c b/tools/perf/ui/gtk/helpline.c
index fbf1ea9ce9a2..e166da9ec767 100644
--- a/tools/perf/ui/gtk/helpline.c
+++ b/tools/perf/ui/gtk/helpline.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
#include <string.h>
+#include <linux/kernel.h>
#include "gtk.h"
#include "../ui.h"
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c
index 0c08890f006a..6c2efc10bf5c 100644
--- a/tools/perf/ui/gtk/hists.c
+++ b/tools/perf/ui/gtk/hists.c
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
#include "../evlist.h"
-#include "../cache.h"
#include "../callchain.h"
#include "../evsel.h"
#include "../sort.h"
@@ -9,6 +8,7 @@
#include "../string2.h"
#include "gtk.h"
#include <signal.h>
+#include <linux/string.h>
#define MAX_COLUMNS 32
@@ -459,7 +459,7 @@ static void perf_gtk__add_hierarchy_entries(struct hists *hists,
advance_hpp(hpp, ret + 2);
}
- gtk_tree_store_set(store, &iter, col_idx, ltrim(rtrim(bf)), -1);
+ gtk_tree_store_set(store, &iter, col_idx, strim(bf), -1);
if (!he->leaf) {
hpp->buf = bf;
@@ -555,7 +555,7 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,
first_col = false;
fmt->header(fmt, &hpp, hists, 0, NULL);
- strcat(buf, ltrim(rtrim(hpp.buf)));
+ strcat(buf, strim(hpp.buf));
}
}
@@ -589,12 +589,12 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,
gtk_container_add(GTK_CONTAINER(window), view);
}
-int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
+int perf_evlist__gtk_browse_hists(struct evlist *evlist,
const char *help,
struct hist_browser_timer *hbt __maybe_unused,
float min_pcnt)
{
- struct perf_evsel *pos;
+ struct evsel *pos;
GtkWidget *vbox;
GtkWidget *notebook;
GtkWidget *info_bar;
@@ -644,7 +644,7 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
if (!perf_evsel__is_group_leader(pos))
continue;
- if (pos->nr_members > 1) {
+ if (pos->core.nr_members > 1) {
perf_evsel__group_desc(pos, buf, size);
evname = buf;
}
diff --git a/tools/perf/ui/gtk/setup.c b/tools/perf/ui/gtk/setup.c
index 506e73b3834c..1a2616b97b5c 100644
--- a/tools/perf/ui/gtk/setup.c
+++ b/tools/perf/ui/gtk/setup.c
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
#include "gtk.h"
-#include "../../util/cache.h"
#include "../../util/debug.h"
extern struct perf_error_ops perf_gtk_eops;
diff --git a/tools/perf/ui/gtk/util.c b/tools/perf/ui/gtk/util.c
index 7250d8101c8f..c2c558958b9c 100644
--- a/tools/perf/ui/gtk/util.c
+++ b/tools/perf/ui/gtk/util.c
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
#include "../util.h"
-#include "../../util/util.h"
#include "../../util/debug.h"
#include "gtk.h"
+#include <stdlib.h>
#include <string.h>
-
+#include <linux/zalloc.h>
struct perf_gtk_context *pgctx;
diff --git a/tools/perf/ui/helpline.c b/tools/perf/ui/helpline.c
index b3c421429ed4..54bcd08df87e 100644
--- a/tools/perf/ui/helpline.c
+++ b/tools/perf/ui/helpline.c
@@ -3,10 +3,10 @@
#include <stdlib.h>
#include <string.h>
-#include "../debug.h"
+#include "../util/debug.h"
#include "helpline.h"
#include "ui.h"
-#include "../util.h"
+#include "../util/util.h"
char ui_helpline__current[512];
diff --git a/tools/perf/ui/helpline.h b/tools/perf/ui/helpline.h
index 8f775a053ca3..2165a098dee8 100644
--- a/tools/perf/ui/helpline.h
+++ b/tools/perf/ui/helpline.h
@@ -5,8 +5,6 @@
#include <stdio.h>
#include <stdarg.h>
-#include "../util/cache.h"
-
struct ui_helpline {
void (*pop)(void);
void (*push)(const char *msg);
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c
index 412d6f1626e3..3e533de7d852 100644
--- a/tools/perf/ui/hist.c
+++ b/tools/perf/ui/hist.c
@@ -1,14 +1,18 @@
// SPDX-License-Identifier: GPL-2.0
#include <inttypes.h>
#include <math.h>
+#include <stdlib.h>
+#include <string.h>
#include <linux/compiler.h>
#include "../util/callchain.h"
+#include "../util/debug.h"
#include "../util/hist.h"
#include "../util/util.h"
#include "../util/sort.h"
#include "../util/evsel.h"
#include "../util/evlist.h"
+#include "../perf.h"
/* hist period print (hpp) functions */
@@ -25,7 +29,7 @@ static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
{
int ret;
struct hists *hists = he->hists;
- struct perf_evsel *evsel = hists_to_evsel(hists);
+ struct evsel *evsel = hists_to_evsel(hists);
char *buf = hpp->buf;
size_t size = hpp->size;
@@ -43,7 +47,7 @@ static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
if (perf_evsel__is_group_event(evsel)) {
int prev_idx, idx_delta;
struct hist_entry *pair;
- int nr_members = evsel->nr_members;
+ int nr_members = evsel->core.nr_members;
prev_idx = perf_evsel__group_idx(evsel);
@@ -153,7 +157,7 @@ static int __hpp__sort(struct hist_entry *a, struct hist_entry *b,
{
s64 ret;
int i, nr_members;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct hist_entry *pair;
u64 *fields_a, *fields_b;
@@ -165,7 +169,7 @@ static int __hpp__sort(struct hist_entry *a, struct hist_entry *b,
if (!perf_evsel__is_group_event(evsel))
return ret;
- nr_members = evsel->nr_members;
+ nr_members = evsel->core.nr_members;
fields_a = calloc(nr_members, sizeof(*fields_a));
fields_b = calloc(nr_members, sizeof(*fields_b));
@@ -223,10 +227,10 @@ static int hpp__width_fn(struct perf_hpp_fmt *fmt,
struct hists *hists)
{
int len = fmt->user_len ?: fmt->len;
- struct perf_evsel *evsel = hists_to_evsel(hists);
+ struct evsel *evsel = hists_to_evsel(hists);
if (symbol_conf.event_group)
- len = max(len, evsel->nr_members * fmt->len);
+ len = max(len, evsel->core.nr_members * fmt->len);
if (len < (int)strlen(fmt->name))
len = strlen(fmt->name);
@@ -795,9 +799,9 @@ static int add_hierarchy_fmt(struct hists *hists, struct perf_hpp_fmt *fmt)
}
int perf_hpp__setup_hists_formats(struct perf_hpp_list *list,
- struct perf_evlist *evlist)
+ struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct perf_hpp_fmt *fmt;
struct hists *hists;
int ret;
diff --git a/tools/perf/ui/libslang.h b/tools/perf/ui/libslang.h
index c0686cda39a5..991e692b9b46 100644
--- a/tools/perf/ui/libslang.h
+++ b/tools/perf/ui/libslang.h
@@ -10,7 +10,12 @@
#ifndef HAVE_LONG_LONG
#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
#endif
+
+#ifdef HAVE_SLANG_INCLUDE_SUBDIR
+#include <slang/slang.h>
+#else
#include <slang.h>
+#endif
#if SLANG_VERSION < 20104
#define slsmg_printf(msg, args...) \
diff --git a/tools/perf/ui/progress.c b/tools/perf/ui/progress.c
index bbfbc91a0fa4..99d60223c74b 100644
--- a/tools/perf/ui/progress.c
+++ b/tools/perf/ui/progress.c
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
-#include "../cache.h"
#include "progress.h"
static void null_progress__update(struct ui_progress *p __maybe_unused)
diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c
index 44fe824e96cd..c7a86b4be9f5 100644
--- a/tools/perf/ui/setup.c
+++ b/tools/perf/ui/setup.c
@@ -2,10 +2,11 @@
#include <pthread.h>
#include <dlfcn.h>
-#include "../util/cache.h"
+#include <subcmd/pager.h>
#include "../util/debug.h"
#include "../util/hist.h"
#include "../util/util.h"
+#include "ui.h"
pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;
void *perf_gtk_handle;
@@ -89,9 +90,9 @@ void setup_browser(bool fallback_to_pager)
printf("GTK browser requested but could not find %s\n",
PERF_GTK_DSO);
sleep(1);
+ use_browser = 1;
/* fall through */
case 1:
- use_browser = 1;
if (ui__init() == 0)
break;
/* fall through */
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
index a60f2993d390..832ca6cfbe30 100644
--- a/tools/perf/ui/stdio/hist.c
+++ b/tools/perf/ui/stdio/hist.c
@@ -1,9 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
+#include <stdlib.h>
#include <linux/string.h>
#include "../../util/callchain.h"
-#include "../../util/util.h"
+#include "../../util/debug.h"
#include "../../util/hist.h"
#include "../../util/map.h"
#include "../../util/map_groups.h"
@@ -13,7 +14,8 @@
#include "../../util/srcline.h"
#include "../../util/string2.h"
#include "../../util/thread.h"
-#include "../../util/sane_ctype.h"
+#include <linux/ctype.h>
+#include <linux/zalloc.h>
static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
{
@@ -516,7 +518,7 @@ static int hist_entry__hierarchy_fprintf(struct hist_entry *he,
* dynamic entries are right-aligned but we want left-aligned
* in the hierarchy mode
*/
- printed += fprintf(fp, "%s%s", sep ?: " ", ltrim(buf));
+ printed += fprintf(fp, "%s%s", sep ?: " ", skip_spaces(buf));
}
printed += putc('\n', fp);
@@ -531,6 +533,30 @@ out:
return printed;
}
+static int hist_entry__block_fprintf(struct hist_entry *he,
+ char *bf, size_t size,
+ FILE *fp)
+{
+ struct block_hist *bh = container_of(he, struct block_hist, he);
+ int ret = 0;
+
+ for (unsigned int i = 0; i < bh->block_hists.nr_entries; i++) {
+ struct perf_hpp hpp = {
+ .buf = bf,
+ .size = size,
+ .skip = false,
+ };
+
+ bh->block_idx = i;
+ hist_entry__snprintf(he, &hpp);
+
+ if (!hpp.skip)
+ ret += fprintf(fp, "%s\n", bf);
+ }
+
+ return ret;
+}
+
static int hist_entry__fprintf(struct hist_entry *he, size_t size,
char *bf, size_t bfsz, FILE *fp,
bool ignore_callchains)
@@ -550,6 +576,9 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
if (symbol_conf.report_hierarchy)
return hist_entry__hierarchy_fprintf(he, &hpp, hists, fp);
+ if (symbol_conf.report_block)
+ return hist_entry__block_fprintf(he, bf, size, fp);
+
hist_entry__snprintf(he, &hpp);
ret = fprintf(fp, "%s\n", bf);
@@ -566,10 +595,14 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
static int print_hierarchy_indent(const char *sep, int indent,
const char *line, FILE *fp)
{
+ int width;
+
if (sep != NULL || indent < 2)
return 0;
- return fprintf(fp, "%-.*s", (indent - 2) * HIERARCHY_INDENT, line);
+ width = (indent - 2) * HIERARCHY_INDENT;
+
+ return fprintf(fp, "%-*.*s", width, width, line);
}
static int hists__fprintf_hierarchy_headers(struct hists *hists,
@@ -587,7 +620,7 @@ static int hists__fprintf_hierarchy_headers(struct hists *hists,
indent = hists->nr_hpp_node;
/* preserve max indent depth for column headers */
- print_hierarchy_indent(sep, indent, spaces, fp);
+ print_hierarchy_indent(sep, indent, " ", fp);
/* the first hpp_list_node is for overhead columns */
fmt_node = list_first_entry(&hists->hpp_formats,
@@ -616,7 +649,7 @@ static int hists__fprintf_hierarchy_headers(struct hists *hists,
fmt->header(fmt, hpp, hists, 0, NULL);
- header_width += fprintf(fp, "%s", trim(hpp->buf));
+ header_width += fprintf(fp, "%s", strim(hpp->buf));
}
}
@@ -816,7 +849,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
if (!h->leaf && !hist_entry__has_hierarchy_children(h, min_pcnt)) {
int depth = hists->nr_hpp_node + h->depth + 1;
- print_hierarchy_indent(sep, depth, spaces, fp);
+ print_hierarchy_indent(sep, depth, " ", fp);
fprintf(fp, "%*sno entry >= %.2f%%\n", indent, "", min_pcnt);
if (max_rows && ++nr_rows >= max_rows)
diff --git a/tools/perf/ui/tui/helpline.c b/tools/perf/ui/tui/helpline.c
index 93d6b7240285..5f188f678c55 100644
--- a/tools/perf/ui/tui/helpline.c
+++ b/tools/perf/ui/tui/helpline.c
@@ -3,6 +3,8 @@
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
#include "../../util/debug.h"
#include "../helpline.h"
diff --git a/tools/perf/ui/tui/progress.c b/tools/perf/ui/tui/progress.c
index bc134b82829d..3d74af5a7ece 100644
--- a/tools/perf/ui/tui/progress.c
+++ b/tools/perf/ui/tui/progress.c
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
-#include "../cache.h"
#include "../progress.h"
#include "../libslang.h"
#include "../ui.h"
diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c
index d4ac41679721..56651a4f5aa0 100644
--- a/tools/perf/ui/tui/setup.c
+++ b/tools/perf/ui/tui/setup.c
@@ -1,15 +1,15 @@
-// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
+#include <stdlib.h>
#include <linux/kernel.h>
#ifdef HAVE_BACKTRACE_SUPPORT
#include <execinfo.h>
#endif
-#include "../../util/cache.h"
#include "../../util/debug.h"
#include "../../util/util.h"
+#include "../../perf.h"
#include "../browser.h"
#include "../helpline.h"
#include "../ui.h"
diff --git a/tools/perf/ui/tui/util.c b/tools/perf/ui/tui/util.c
index b9794d6185af..087d9ab054c8 100644
--- a/tools/perf/ui/tui/util.c
+++ b/tools/perf/ui/tui/util.c
@@ -1,11 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
-#include "../../util/util.h"
#include <signal.h>
#include <stdbool.h>
#include <string.h>
+#include <stdlib.h>
#include <sys/ttydefaults.h>
-#include "../../util/cache.h"
#include "../../util/debug.h"
#include "../browser.h"
#include "../keysyms.h"
@@ -162,8 +161,7 @@ next_key:
return key;
}
-int ui__question_window(const char *title, const char *text,
- const char *exit_msg, int delay_secs)
+void __ui__info_window(const char *title, const char *text, const char *exit_msg)
{
int x, y;
int max_len = 0, nr_lines = 0;
@@ -185,10 +183,10 @@ int ui__question_window(const char *title, const char *text,
t = sep + 1;
}
- pthread_mutex_lock(&ui__lock);
-
max_len += 2;
- nr_lines += 4;
+ nr_lines += 2;
+ if (exit_msg)
+ nr_lines += 2;
y = SLtt_Screen_Rows / 2 - nr_lines / 2,
x = SLtt_Screen_Cols / 2 - max_len / 2;
@@ -199,18 +197,34 @@ int ui__question_window(const char *title, const char *text,
SLsmg_write_string((char *)title);
}
SLsmg_gotorc(++y, x);
- nr_lines -= 2;
+ if (exit_msg)
+ nr_lines -= 2;
max_len -= 2;
SLsmg_write_wrapped_string((unsigned char *)text, y, x,
nr_lines, max_len, 1);
- SLsmg_gotorc(y + nr_lines - 2, x);
- SLsmg_write_nstring((char *)" ", max_len);
- SLsmg_gotorc(y + nr_lines - 1, x);
- SLsmg_write_nstring((char *)exit_msg, max_len);
- SLsmg_refresh();
+ if (exit_msg) {
+ SLsmg_gotorc(y + nr_lines - 2, x);
+ SLsmg_write_nstring((char *)" ", max_len);
+ SLsmg_gotorc(y + nr_lines - 1, x);
+ SLsmg_write_nstring((char *)exit_msg, max_len);
+ }
+}
+void ui__info_window(const char *title, const char *text)
+{
+ pthread_mutex_lock(&ui__lock);
+ __ui__info_window(title, text, NULL);
+ SLsmg_refresh();
pthread_mutex_unlock(&ui__lock);
+}
+int ui__question_window(const char *title, const char *text,
+ const char *exit_msg, int delay_secs)
+{
+ pthread_mutex_lock(&ui__lock);
+ __ui__info_window(title, text, exit_msg);
+ SLsmg_refresh();
+ pthread_mutex_unlock(&ui__lock);
return ui__getch(delay_secs);
}
diff --git a/tools/perf/ui/util.c b/tools/perf/ui/util.c
index 63bf06e80ab9..689b27c34246 100644
--- a/tools/perf/ui/util.c
+++ b/tools/perf/ui/util.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "util.h"
-#include "../debug.h"
-
+#include "../util/debug.h"
+#include <stdio.h>
/*
* Default error logging functions
diff --git a/tools/perf/ui/util.h b/tools/perf/ui/util.h
index 5e44223b56fa..40891942f465 100644
--- a/tools/perf/ui/util.h
+++ b/tools/perf/ui/util.h
@@ -8,6 +8,8 @@ int ui__getch(int delay_secs);
int ui__popup_menu(int argc, char * const argv[]);
int ui__help_window(const char *text);
int ui__dialog_yesno(const char *msg);
+void __ui__info_window(const char *title, const char *text, const char *exit_msg);
+void ui__info_window(const char *title, const char *text);
int ui__question_window(const char *title, const char *text,
const char *exit_msg, int delay_secs);
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 6d5bbc8b589b..0b4d8e0d474c 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -1,6 +1,7 @@
perf-y += annotate.o
perf-y += block-range.o
perf-y += build-id.o
+perf-y += cacheline.o
perf-y += config.o
perf-y += ctype.o
perf-y += db-export.o
@@ -9,6 +10,7 @@ perf-y += event.o
perf-y += evlist.o
perf-y += evsel.o
perf-y += evsel_fprintf.o
+perf-y += evswitch.o
perf-y += find_bit.o
perf-y += get_current_dir_name.o
perf-y += kallsyms.o
@@ -20,6 +22,8 @@ perf-y += parse-events.o
perf-y += perf_regs.o
perf-y += path.o
perf-y += print_binary.o
+perf-y += rlimit.o
+perf-y += argv_split.o
perf-y += rbtree.o
perf-y += libstring.o
perf-y += bitmap.o
@@ -32,6 +36,7 @@ perf-y += strfilter.o
perf-y += top.o
perf-y += usage.o
perf-y += dso.o
+perf-y += dsos.o
perf-y += symbol.o
perf-y += symbol_fprintf.o
perf-y += color.o
@@ -67,7 +72,6 @@ perf-y += svghelper.o
perf-y += sort.o
perf-y += hist.o
perf-y += util.o
-perf-y += xyarray.o
perf-y += cpumap.o
perf-y += cputopo.o
perf-y += cgroup.o
@@ -147,6 +151,8 @@ perf-$(CONFIG_ZLIB) += zlib.o
perf-$(CONFIG_LZMA) += lzma.o
perf-$(CONFIG_ZSTD) += zstd.o
+perf-$(CONFIG_LIBCAP) += cap.o
+
perf-y += demangle-java.o
perf-y += demangle-rust.o
@@ -209,10 +215,18 @@ $(OUTPUT)util/kallsyms.o: ../lib/symbol/kallsyms.c FORCE
$(call rule_mkdir)
$(call if_changed_dep,cc_o_c)
+$(OUTPUT)util/argv_split.o: ../lib/argv_split.c FORCE
+ $(call rule_mkdir)
+ $(call if_changed_dep,cc_o_c)
+
$(OUTPUT)util/bitmap.o: ../lib/bitmap.c FORCE
$(call rule_mkdir)
$(call if_changed_dep,cc_o_c)
+$(OUTPUT)util/ctype.o: ../lib/ctype.c FORCE
+ $(call rule_mkdir)
+ $(call if_changed_dep,cc_o_c)
+
$(OUTPUT)util/find_bit.o: ../lib/find_bit.c FORCE
$(call rule_mkdir)
$(call if_changed_dep,cc_o_c)
diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN
index 3802cee5e188..59241ff342be 100755
--- a/tools/perf/util/PERF-VERSION-GEN
+++ b/tools/perf/util/PERF-VERSION-GEN
@@ -19,7 +19,7 @@ TAG=
if test -d ../../.git -o -f ../../.git
then
TAG=$(git describe --abbrev=0 --match "v[0-9].[0-9]*" 2>/dev/null )
- CID=$(git log -1 --abbrev=4 --pretty=format:"%h" 2>/dev/null) && CID="-g$CID"
+ CID=$(git log -1 --abbrev=12 --pretty=format:"%h" 2>/dev/null) && CID="-g$CID"
elif test -f ../../PERF-VERSION-FILE
then
TAG=$(cut -d' ' -f3 ../../PERF-VERSION-FILE | sed -e 's/\"//g')
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 0b8573fd9b05..1748f528b6e9 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -1,15 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
*
* Parts came from builtin-annotate.c, see those files for further
* copyright notes.
- *
- * Released under the GPL v2. (and only v2, not any later version)
*/
#include <errno.h>
#include <inttypes.h>
#include <libgen.h>
+#include <stdlib.h>
#include <bpf/bpf.h>
#include <bpf/btf.h>
#include <bpf/libbpf.h>
@@ -20,9 +20,12 @@
#include "build-id.h"
#include "color.h"
#include "config.h"
-#include "cache.h"
+#include "dso.h"
+#include "env.h"
#include "map.h"
+#include "map_groups.h"
#include "symbol.h"
+#include "srcline.h"
#include "units.h"
#include "debug.h"
#include "annotate.h"
@@ -36,7 +39,9 @@
#include <pthread.h>
#include <linux/bitops.h>
#include <linux/kernel.h>
+#include <linux/string.h>
#include <bpf/libbpf.h>
+#include <subcmd/parse-options.h>
/* FIXME: For the HE_COLORSET */
#include "ui/browser.h"
@@ -50,7 +55,7 @@
#define DARROW_CHAR ((unsigned char)'.')
#define UARROW_CHAR ((unsigned char)'-')
-#include "sane_ctype.h"
+#include <linux/ctype.h>
struct annotation_options annotation__default_options = {
.use_offset = true,
@@ -145,6 +150,7 @@ static int arch__associate_ins_ops(struct arch* arch, const char *name, struct i
#include "arch/arc/annotate/instructions.c"
#include "arch/arm/annotate/instructions.c"
#include "arch/arm64/annotate/instructions.c"
+#include "arch/csky/annotate/instructions.c"
#include "arch/x86/annotate/instructions.c"
#include "arch/powerpc/annotate/instructions.c"
#include "arch/s390/annotate/instructions.c"
@@ -164,6 +170,10 @@ static struct arch architectures[] = {
.init = arm64__annotate_init,
},
{
+ .name = "csky",
+ .init = csky__annotate_init,
+ },
+ {
.name = "x86",
.init = x86__annotate_init,
.instructions = x86__instructions,
@@ -558,7 +568,7 @@ static int mov__parse(struct arch *arch, struct ins_operands *ops, struct map_sy
if (comment == NULL)
return 0;
- comment = ltrim(comment);
+ comment = skip_spaces(comment);
comment__symbol(ops->source.raw, comment + 1, &ops->source.addr, &ops->source.name);
comment__symbol(ops->target.raw, comment + 1, &ops->target.addr, &ops->target.name);
@@ -603,7 +613,7 @@ static int dec__parse(struct arch *arch __maybe_unused, struct ins_operands *ops
if (comment == NULL)
return 0;
- comment = ltrim(comment);
+ comment = skip_spaces(comment);
comment__symbol(ops->target.raw, comment + 1, &ops->target.addr, &ops->target.name);
return 0;
@@ -924,17 +934,16 @@ alloc_histograms:
}
static int symbol__inc_addr_samples(struct symbol *sym, struct map *map,
- struct perf_evsel *evsel, u64 addr,
+ struct evsel *evsel, u64 addr,
struct perf_sample *sample)
{
struct annotated_source *src;
if (sym == NULL)
return 0;
- src = symbol__hists(sym, evsel->evlist->nr_entries);
- if (src == NULL)
- return -ENOMEM;
- return __symbol__inc_addr_samples(sym, map, src, evsel->idx, addr, sample);
+ src = symbol__hists(sym, evsel->evlist->core.nr_entries);
+ return (src) ? __symbol__inc_addr_samples(sym, map, src, evsel->idx,
+ addr, sample) : 0;
}
static int symbol__account_cycles(u64 addr, u64 start,
@@ -1076,13 +1085,13 @@ void annotation__compute_ipc(struct annotation *notes, size_t size)
}
int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample,
- struct perf_evsel *evsel)
+ struct evsel *evsel)
{
return symbol__inc_addr_samples(ams->sym, ams->map, evsel, ams->al_addr, sample);
}
int hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *sample,
- struct perf_evsel *evsel, u64 ip)
+ struct evsel *evsel, u64 ip)
{
return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evsel, ip, sample);
}
@@ -1100,7 +1109,7 @@ static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, str
static int disasm_line__parse(char *line, const char **namep, char **rawp)
{
- char tmp, *name = ltrim(line);
+ char tmp, *name = skip_spaces(line);
if (name[0] == '\0')
return -1;
@@ -1115,16 +1124,14 @@ static int disasm_line__parse(char *line, const char **namep, char **rawp)
*namep = strdup(name);
if (*namep == NULL)
- goto out_free_name;
+ goto out;
(*rawp)[0] = tmp;
- *rawp = ltrim(*rawp);
+ *rawp = strim(*rawp);
return 0;
-out_free_name:
- free((void *)namep);
- *namep = NULL;
+out:
return -1;
}
@@ -1132,7 +1139,7 @@ struct annotate_args {
size_t privsize;
struct arch *arch;
struct map_symbol ms;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct annotation_options *options;
s64 offset;
char *line;
@@ -1163,12 +1170,12 @@ static struct annotation_line *
annotation_line__new(struct annotate_args *args, size_t privsize)
{
struct annotation_line *al;
- struct perf_evsel *evsel = args->evsel;
+ struct evsel *evsel = args->evsel;
size_t size = privsize + sizeof(*al);
int nr = 1;
if (perf_evsel__is_group_event(evsel))
- nr = evsel->nr_members;
+ nr = evsel->core.nr_members;
size += sizeof(al->data[0]) * nr;
@@ -1233,8 +1240,7 @@ void disasm_line__free(struct disasm_line *dl)
dl->ins.ops->free(&dl->ops);
else
ins__delete(&dl->ops);
- free((void *)dl->ins.name);
- dl->ins.name = NULL;
+ zfree(&dl->ins.name);
annotation_line__delete(&dl->al);
}
@@ -1358,7 +1364,7 @@ static int disasm_line__print(struct disasm_line *dl, u64 start, int addr_fmt_wi
static int
annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start,
- struct perf_evsel *evsel, u64 len, int min_pcnt, int printed,
+ struct evsel *evsel, u64 len, int min_pcnt, int printed,
int max_lines, struct annotation_line *queue, int addr_fmt_width,
int percent_type)
{
@@ -1447,7 +1453,7 @@ annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start
return -1;
if (perf_evsel__is_group_event(evsel))
- width *= evsel->nr_members;
+ width *= evsel->core.nr_members;
if (!*al->line)
printf(" %*s:\n", width, " ");
@@ -1497,7 +1503,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file,
return -1;
line_ip = -1;
- parsed_line = rtrim(line);
+ parsed_line = strim(line);
/* /filename:linenr ? Save line number and ignore. */
if (regexec(&file_lineno, parsed_line, 2, match, 0) == 0) {
@@ -1505,7 +1511,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file,
return 0;
}
- tmp = ltrim(parsed_line);
+ tmp = skip_spaces(parsed_line);
if (*tmp) {
/*
* Parse hexa addresses followed by ':'
@@ -1585,7 +1591,7 @@ static void delete_last_nop(struct symbol *sym)
return;
}
- list_del(&dl->al.node);
+ list_del_init(&dl->al.node);
disasm_line__free(dl);
}
}
@@ -2010,10 +2016,10 @@ static void calc_percent(struct sym_hist *sym_hist,
}
static void annotation__calc_percent(struct annotation *notes,
- struct perf_evsel *leader, s64 len)
+ struct evsel *leader, s64 len)
{
struct annotation_line *al, *next;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
list_for_each_entry(al, &notes->src->source, node) {
s64 end;
@@ -2040,7 +2046,7 @@ static void annotation__calc_percent(struct annotation *notes,
}
}
-void symbol__calc_percent(struct symbol *sym, struct perf_evsel *evsel)
+void symbol__calc_percent(struct symbol *sym, struct evsel *evsel)
{
struct annotation *notes = symbol__annotation(sym);
@@ -2048,7 +2054,7 @@ void symbol__calc_percent(struct symbol *sym, struct perf_evsel *evsel)
}
int symbol__annotate(struct symbol *sym, struct map *map,
- struct perf_evsel *evsel, size_t privsize,
+ struct evsel *evsel, size_t privsize,
struct annotation_options *options,
struct arch **parch)
{
@@ -2213,7 +2219,7 @@ static void print_summary(struct rb_root *root, const char *filename)
}
}
-static void symbol__annotate_hits(struct symbol *sym, struct perf_evsel *evsel)
+static void symbol__annotate_hits(struct symbol *sym, struct evsel *evsel)
{
struct annotation *notes = symbol__annotation(sym);
struct sym_hist *h = annotation__histogram(notes, evsel->idx);
@@ -2240,7 +2246,7 @@ static int annotated_source__addr_fmt_width(struct list_head *lines, u64 start)
}
int symbol__annotate_printf(struct symbol *sym, struct map *map,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct annotation_options *opts)
{
struct dso *dso = map->dso;
@@ -2271,7 +2277,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map,
len = symbol__size(sym);
if (perf_evsel__is_group_event(evsel)) {
- width *= evsel->nr_members;
+ width *= evsel->core.nr_members;
perf_evsel__group_desc(evsel, buf, sizeof(buf));
evsel_name = buf;
}
@@ -2404,7 +2410,7 @@ static int symbol__annotate_fprintf2(struct symbol *sym, FILE *fp,
return 0;
}
-int map_symbol__annotation_dump(struct map_symbol *ms, struct perf_evsel *evsel,
+int map_symbol__annotation_dump(struct map_symbol *ms, struct evsel *evsel,
struct annotation_options *opts)
{
const char *ev_name = perf_evsel__name(evsel);
@@ -2462,7 +2468,7 @@ void annotated_source__purge(struct annotated_source *as)
struct annotation_line *al, *n;
list_for_each_entry_safe(al, n, &as->source, node) {
- list_del(&al->node);
+ list_del_init(&al->node);
disasm_line__free(disasm_line(al));
}
}
@@ -2656,7 +2662,7 @@ static void symbol__calc_lines(struct symbol *sym, struct map *map,
}
int symbol__tty_annotate2(struct symbol *sym, struct map *map,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct annotation_options *opts)
{
struct dso *dso = map->dso;
@@ -2684,7 +2690,7 @@ int symbol__tty_annotate2(struct symbol *sym, struct map *map,
}
int symbol__tty_annotate(struct symbol *sym, struct map *map,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct annotation_options *opts)
{
struct dso *dso = map->dso;
@@ -2955,7 +2961,7 @@ void annotation_line__write(struct annotation_line *al, struct annotation *notes
wops->write_graph);
}
-int symbol__annotate2(struct symbol *sym, struct map *map, struct perf_evsel *evsel,
+int symbol__annotate2(struct symbol *sym, struct map *map, struct evsel *evsel,
struct annotation_options *options, struct arch **parch)
{
struct annotation *notes = symbol__annotation(sym);
@@ -2967,7 +2973,7 @@ int symbol__annotate2(struct symbol *sym, struct map *map, struct perf_evsel *ev
return -1;
if (perf_evsel__is_group_event(evsel))
- nr_pcnt = evsel->nr_members;
+ nr_pcnt = evsel->core.nr_members;
err = symbol__annotate(sym, map, evsel, 0, options, parch);
if (err)
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index 5bc0cf655d37..d94be9140e31 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -20,7 +20,7 @@ struct map_symbol;
struct addr_map_symbol;
struct option;
struct perf_sample;
-struct perf_evsel;
+struct evsel;
struct symbol;
struct ins {
@@ -216,12 +216,12 @@ void annotation_line__write(struct annotation_line *al, struct annotation *notes
int __annotation__scnprintf_samples_period(struct annotation *notes,
char *bf, size_t size,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
bool show_freq);
int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw, int max_ins_name);
size_t disasm__fprintf(struct list_head *head, FILE *fp);
-void symbol__calc_percent(struct symbol *sym, struct perf_evsel *evsel);
+void symbol__calc_percent(struct symbol *sym, struct evsel *evsel);
struct sym_hist {
u64 nr_samples;
@@ -245,7 +245,7 @@ struct cyc_hist {
/** struct annotated_source - symbols with hits have this attached as in sannotation
*
* @histograms: Array of addr hit histograms per event being monitored
- * nr_histograms: This may not be the same as evsel->evlist->nr_entries if
+ * nr_histograms: This may not be the same as evsel->evlist->core.nr_entries if
* we have more than a group in a evlist, where we will want
* to see each group separately, that is why symbol__annotate2()
* sets src->nr_histograms to evsel->nr_members.
@@ -335,24 +335,24 @@ static inline struct annotation *symbol__annotation(struct symbol *sym)
}
int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample,
- struct perf_evsel *evsel);
+ struct evsel *evsel);
int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,
struct addr_map_symbol *start,
unsigned cycles);
int hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *sample,
- struct perf_evsel *evsel, u64 addr);
+ struct evsel *evsel, u64 addr);
struct annotated_source *symbol__hists(struct symbol *sym, int nr_hists);
void symbol__annotate_zero_histograms(struct symbol *sym);
int symbol__annotate(struct symbol *sym, struct map *map,
- struct perf_evsel *evsel, size_t privsize,
+ struct evsel *evsel, size_t privsize,
struct annotation_options *options,
struct arch **parch);
int symbol__annotate2(struct symbol *sym, struct map *map,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct annotation_options *options,
struct arch **parch);
@@ -378,32 +378,32 @@ int symbol__strerror_disassemble(struct symbol *sym, struct map *map,
int errnum, char *buf, size_t buflen);
int symbol__annotate_printf(struct symbol *sym, struct map *map,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct annotation_options *options);
void symbol__annotate_zero_histogram(struct symbol *sym, int evidx);
void symbol__annotate_decay_histogram(struct symbol *sym, int evidx);
void annotated_source__purge(struct annotated_source *as);
-int map_symbol__annotation_dump(struct map_symbol *ms, struct perf_evsel *evsel,
+int map_symbol__annotation_dump(struct map_symbol *ms, struct evsel *evsel,
struct annotation_options *opts);
bool ui__has_annotation(void);
int symbol__tty_annotate(struct symbol *sym, struct map *map,
- struct perf_evsel *evsel, struct annotation_options *opts);
+ struct evsel *evsel, struct annotation_options *opts);
int symbol__tty_annotate2(struct symbol *sym, struct map *map,
- struct perf_evsel *evsel, struct annotation_options *opts);
+ struct evsel *evsel, struct annotation_options *opts);
#ifdef HAVE_SLANG_SUPPORT
int symbol__tui_annotate(struct symbol *sym, struct map *map,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct hist_browser_timer *hbt,
struct annotation_options *opts);
#else
static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused,
struct map *map __maybe_unused,
- struct perf_evsel *evsel __maybe_unused,
+ struct evsel *evsel __maybe_unused,
struct hist_browser_timer *hbt __maybe_unused,
struct annotation_options *opts __maybe_unused)
{
diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c
index 6067267cc76c..8a7340f6a2a2 100644
--- a/tools/perf/util/arm-spe.c
+++ b/tools/perf/util/arm-spe.c
@@ -8,19 +8,19 @@
#include <errno.h>
#include <byteswap.h>
#include <inttypes.h>
+#include <unistd.h>
+#include <stdlib.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/log2.h>
+#include <linux/zalloc.h>
#include "cpumap.h"
#include "color.h"
#include "evsel.h"
-#include "evlist.h"
#include "machine.h"
#include "session.h"
-#include "util.h"
-#include "thread.h"
#include "debug.h"
#include "auxtrace.h"
#include "arm-spe.h"
@@ -181,7 +181,7 @@ static const char * const arm_spe_info_fmts[] = {
[ARM_SPE_PMU_TYPE] = " PMU Type %"PRId64"\n",
};
-static void arm_spe_print_info(u64 *arr)
+static void arm_spe_print_info(__u64 *arr)
{
if (!dump_trace)
return;
@@ -192,12 +192,12 @@ static void arm_spe_print_info(u64 *arr)
int arm_spe_process_auxtrace_info(union perf_event *event,
struct perf_session *session)
{
- struct auxtrace_info_event *auxtrace_info = &event->auxtrace_info;
+ struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
size_t min_sz = sizeof(u64) * ARM_SPE_PMU_TYPE;
struct arm_spe *spe;
int err;
- if (auxtrace_info->header.size < sizeof(struct auxtrace_info_event) +
+ if (auxtrace_info->header.size < sizeof(struct perf_record_auxtrace_info) +
min_sz)
return -EINVAL;
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index fb76b6b232d4..6f25224a3def 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -1,16 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* auxtrace.c: AUX area trace support
* Copyright (c) 2013-2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#include <inttypes.h>
@@ -33,9 +24,8 @@
#include <stdlib.h>
#include <stdio.h>
#include <linux/list.h>
+#include <linux/zalloc.h>
-#include "../perf.h"
-#include "util.h"
#include "evlist.h"
#include "dso.h"
#include "map.h"
@@ -50,6 +40,7 @@
#include <linux/hash.h>
#include "event.h"
+#include "record.h"
#include "session.h"
#include "debug.h"
#include <subcmd/parse-options.h>
@@ -59,8 +50,9 @@
#include "intel-bts.h"
#include "arm-spe.h"
#include "s390-cpumsf.h"
+#include "util.h"
-#include "sane_ctype.h"
+#include <linux/ctype.h>
#include "symbol/kallsyms.h"
static bool auxtrace__dont_decode(struct perf_session *session)
@@ -133,20 +125,20 @@ void auxtrace_mmap_params__init(struct auxtrace_mmap_params *mp,
}
void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp,
- struct perf_evlist *evlist, int idx,
+ struct evlist *evlist, int idx,
bool per_cpu)
{
mp->idx = idx;
if (per_cpu) {
- mp->cpu = evlist->cpus->map[idx];
- if (evlist->threads)
- mp->tid = thread_map__pid(evlist->threads, 0);
+ mp->cpu = evlist->core.cpus->map[idx];
+ if (evlist->core.threads)
+ mp->tid = perf_thread_map__pid(evlist->core.threads, 0);
else
mp->tid = -1;
} else {
mp->cpu = -1;
- mp->tid = thread_map__pid(evlist->threads, idx);
+ mp->tid = perf_thread_map__pid(evlist->core.threads, idx);
}
}
@@ -394,7 +386,7 @@ static int auxtrace_queues__add_indexed_event(struct auxtrace_queues *queues,
return err;
if (event->header.type == PERF_RECORD_AUXTRACE) {
- if (event->header.size < sizeof(struct auxtrace_event) ||
+ if (event->header.size < sizeof(struct perf_record_auxtrace) ||
event->header.size != sz) {
err = -EINVAL;
goto out;
@@ -417,7 +409,7 @@ void auxtrace_queues__free(struct auxtrace_queues *queues)
buffer = list_entry(queues->queue_array[i].head.next,
struct auxtrace_buffer, list);
- list_del(&buffer->list);
+ list_del_init(&buffer->list);
auxtrace_buffer__free(buffer);
}
}
@@ -512,7 +504,7 @@ void auxtrace_heap__pop(struct auxtrace_heap *heap)
}
size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
- struct perf_evlist *evlist)
+ struct evlist *evlist)
{
if (itr)
return itr->info_priv_size(itr, evlist);
@@ -527,7 +519,7 @@ static int auxtrace_not_supported(void)
int auxtrace_record__info_fill(struct auxtrace_record *itr,
struct perf_session *session,
- struct auxtrace_info_event *auxtrace_info,
+ struct perf_record_auxtrace_info *auxtrace_info,
size_t priv_size)
{
if (itr)
@@ -548,9 +540,9 @@ int auxtrace_record__snapshot_start(struct auxtrace_record *itr)
return 0;
}
-int auxtrace_record__snapshot_finish(struct auxtrace_record *itr)
+int auxtrace_record__snapshot_finish(struct auxtrace_record *itr, bool on_exit)
{
- if (itr && itr->snapshot_finish)
+ if (!on_exit && itr && itr->snapshot_finish)
return itr->snapshot_finish(itr);
return 0;
}
@@ -565,7 +557,7 @@ int auxtrace_record__find_snapshot(struct auxtrace_record *itr, int idx,
}
int auxtrace_record__options(struct auxtrace_record *itr,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
struct record_opts *opts)
{
if (itr)
@@ -586,6 +578,16 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
if (!str)
return 0;
+ /* PMU-agnostic options */
+ switch (*str) {
+ case 'e':
+ opts->auxtrace_snapshot_on_exit = true;
+ str++;
+ break;
+ default:
+ break;
+ }
+
if (itr)
return itr->parse_snapshot_options(itr, opts, str);
@@ -594,7 +596,7 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
}
struct auxtrace_record *__weak
-auxtrace_record__init(struct perf_evlist *evlist __maybe_unused, int *err)
+auxtrace_record__init(struct evlist *evlist __maybe_unused, int *err)
{
*err = 0;
return NULL;
@@ -621,7 +623,7 @@ void auxtrace_index__free(struct list_head *head)
struct auxtrace_index *auxtrace_index, *n;
list_for_each_entry_safe(auxtrace_index, n, head, list) {
- list_del(&auxtrace_index->list);
+ list_del_init(&auxtrace_index->list);
free(auxtrace_index);
}
}
@@ -857,13 +859,13 @@ void auxtrace_buffer__free(struct auxtrace_buffer *buffer)
free(buffer);
}
-void auxtrace_synth_error(struct auxtrace_error_event *auxtrace_error, int type,
+void auxtrace_synth_error(struct perf_record_auxtrace_error *auxtrace_error, int type,
int code, int cpu, pid_t pid, pid_t tid, u64 ip,
const char *msg, u64 timestamp)
{
size_t size;
- memset(auxtrace_error, 0, sizeof(struct auxtrace_error_event));
+ memset(auxtrace_error, 0, sizeof(struct perf_record_auxtrace_error));
auxtrace_error->header.type = PERF_RECORD_AUXTRACE_ERROR;
auxtrace_error->type = type;
@@ -892,12 +894,12 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr,
pr_debug2("Synthesizing auxtrace information\n");
priv_size = auxtrace_record__info_priv_size(itr, session->evlist);
- ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size);
+ ev = zalloc(sizeof(struct perf_record_auxtrace_info) + priv_size);
if (!ev)
return -ENOMEM;
ev->auxtrace_info.header.type = PERF_RECORD_AUXTRACE_INFO;
- ev->auxtrace_info.header.size = sizeof(struct auxtrace_info_event) +
+ ev->auxtrace_info.header.size = sizeof(struct perf_record_auxtrace_info) +
priv_size;
err = auxtrace_record__info_fill(itr, session, &ev->auxtrace_info,
priv_size);
@@ -941,7 +943,7 @@ s64 perf_event__process_auxtrace(struct perf_session *session,
s64 err;
if (dump_trace)
- fprintf(stdout, " size: %#"PRIx64" offset: %#"PRIx64" ref: %#"PRIx64" idx: %u tid: %d cpu: %d\n",
+ fprintf(stdout, " size: %#"PRI_lx64" offset: %#"PRI_lx64" ref: %#"PRI_lx64" idx: %u tid: %d cpu: %d\n",
event->auxtrace.size, event->auxtrace.offset,
event->auxtrace.reference, event->auxtrace.idx,
event->auxtrace.tid, event->auxtrace.cpu);
@@ -973,6 +975,7 @@ void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts,
synth_opts->transactions = true;
synth_opts->ptwrites = true;
synth_opts->pwr_events = true;
+ synth_opts->other_events = true;
synth_opts->errors = true;
if (no_sample) {
synth_opts->period_type = PERF_ITRACE_PERIOD_INSTRUCTIONS;
@@ -1010,7 +1013,8 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str,
}
if (!str) {
- itrace_synth_opts__set_default(synth_opts, false);
+ itrace_synth_opts__set_default(synth_opts,
+ synth_opts->default_no_sample);
return 0;
}
@@ -1069,6 +1073,9 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str,
case 'p':
synth_opts->pwr_events = true;
break;
+ case 'o':
+ synth_opts->other_events = true;
+ break;
case 'e':
synth_opts->errors = true;
break;
@@ -1162,7 +1169,7 @@ static const char *auxtrace_error_name(int type)
size_t perf_event__fprintf_auxtrace_error(union perf_event *event, FILE *fp)
{
- struct auxtrace_error_event *e = &event->auxtrace_error;
+ struct perf_record_auxtrace_error *e = &event->auxtrace_error;
unsigned long long nsecs = e->time;
const char *msg = e->msg;
int ret;
@@ -1182,7 +1189,7 @@ size_t perf_event__fprintf_auxtrace_error(union perf_event *event, FILE *fp)
if (!e->fmt)
msg = (const char *)&e->time;
- ret += fprintf(fp, " cpu %d pid %d tid %d ip %#"PRIx64" code %u: %s\n",
+ ret += fprintf(fp, " cpu %d pid %d tid %d ip %#"PRI_lx64" code %u: %s\n",
e->cpu, e->pid, e->tid, e->ip, e->code, msg);
return ret;
}
@@ -1190,7 +1197,7 @@ size_t perf_event__fprintf_auxtrace_error(union perf_event *event, FILE *fp)
void perf_session__auxtrace_error_inc(struct perf_session *session,
union perf_event *event)
{
- struct auxtrace_error_event *e = &event->auxtrace_error;
+ struct perf_record_auxtrace_error *e = &event->auxtrace_error;
if (e->type < PERF_AUXTRACE_ERROR_MAX)
session->evlist->stats.nr_auxtrace_errors[e->type] += 1;
@@ -1421,7 +1428,7 @@ void auxtrace_cache__free(struct auxtrace_cache *c)
return;
auxtrace_cache__drop(c);
- free(c->hashtable);
+ zfree(&c->hashtable);
free(c);
}
@@ -1467,12 +1474,11 @@ void *auxtrace_cache__lookup(struct auxtrace_cache *c, u32 key)
static void addr_filter__free_str(struct addr_filter *filt)
{
- free(filt->str);
+ zfree(&filt->str);
filt->action = NULL;
filt->sym_from = NULL;
filt->sym_to = NULL;
filt->filename = NULL;
- filt->str = NULL;
}
static struct addr_filter *addr_filter__new(void)
@@ -2093,7 +2099,7 @@ static char *addr_filter__to_str(struct addr_filter *filt)
return err < 0 ? NULL : filter;
}
-static int parse_addr_filter(struct perf_evsel *evsel, const char *filter,
+static int parse_addr_filter(struct evsel *evsel, const char *filter,
int max_nr)
{
struct addr_filters filts;
@@ -2144,19 +2150,19 @@ out_exit:
return err;
}
-static struct perf_pmu *perf_evsel__find_pmu(struct perf_evsel *evsel)
+static struct perf_pmu *perf_evsel__find_pmu(struct evsel *evsel)
{
struct perf_pmu *pmu = NULL;
while ((pmu = perf_pmu__scan(pmu)) != NULL) {
- if (pmu->type == evsel->attr.type)
+ if (pmu->type == evsel->core.attr.type)
break;
}
return pmu;
}
-static int perf_evsel__nr_addr_filter(struct perf_evsel *evsel)
+static int perf_evsel__nr_addr_filter(struct evsel *evsel)
{
struct perf_pmu *pmu = perf_evsel__find_pmu(evsel);
int nr_addr_filters = 0;
@@ -2169,9 +2175,9 @@ static int perf_evsel__nr_addr_filter(struct perf_evsel *evsel)
return nr_addr_filters;
}
-int auxtrace_parse_filters(struct perf_evlist *evlist)
+int auxtrace_parse_filters(struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
char *filter;
int err, max_nr;
@@ -2190,3 +2196,36 @@ int auxtrace_parse_filters(struct perf_evlist *evlist)
return 0;
}
+
+int auxtrace__process_event(struct perf_session *session, union perf_event *event,
+ struct perf_sample *sample, struct perf_tool *tool)
+{
+ if (!session->auxtrace)
+ return 0;
+
+ return session->auxtrace->process_event(session, event, sample, tool);
+}
+
+int auxtrace__flush_events(struct perf_session *session, struct perf_tool *tool)
+{
+ if (!session->auxtrace)
+ return 0;
+
+ return session->auxtrace->flush_events(session, tool);
+}
+
+void auxtrace__free_events(struct perf_session *session)
+{
+ if (!session->auxtrace)
+ return;
+
+ return session->auxtrace->free_events(session);
+}
+
+void auxtrace__free(struct perf_session *session)
+{
+ if (!session->auxtrace)
+ return;
+
+ return session->auxtrace->free(session);
+}
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
index c69bcd9a3091..37e70dc01436 100644
--- a/tools/perf/util/auxtrace.h
+++ b/tools/perf/util/auxtrace.h
@@ -1,16 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* auxtrace.h: AUX area trace support
* Copyright (c) 2013-2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#ifndef __PERF_AUXTRACE_H
@@ -24,22 +15,25 @@
#include <linux/perf_event.h>
#include <linux/types.h>
#include <asm/bitsperlong.h>
+#include <asm/barrier.h>
-#include "../perf.h"
#include "event.h"
-#include "session.h"
-#include "debug.h"
union perf_event;
struct perf_session;
-struct perf_evlist;
+struct evlist;
struct perf_tool;
struct perf_mmap;
struct option;
struct record_opts;
-struct auxtrace_info_event;
+struct perf_record_auxtrace_info;
struct events_stats;
+enum auxtrace_error_type {
+ PERF_AUXTRACE_ERROR_ITRACE = 1,
+ PERF_AUXTRACE_ERROR_MAX
+};
+
/* Auxtrace records must have the same alignment as perf event records */
#define PERF_AUXTRACE_RECORD_ALIGNMENT 8
@@ -69,6 +63,8 @@ enum itrace_period_type {
* @transactions: whether to synthesize events for transactions
* @ptwrites: whether to synthesize events for ptwrites
* @pwr_events: whether to synthesize power events
+ * @other_events: whether to synthesize other events recorded due to the use of
+ * aux_output
* @errors: whether to synthesize decoder error events
* @dont_decode: whether to skip decoding entirely
* @log: write a decoding log
@@ -83,6 +79,8 @@ enum itrace_period_type {
* @period_type: 'instructions' events period type
* @initial_skip: skip N events at the beginning.
* @cpu_bitmap: CPUs for which to synthesize events, or NULL for all
+ * @ptime_range: time intervals to trace or NULL
+ * @range_num: number of time intervals to trace
*/
struct itrace_synth_opts {
bool set;
@@ -93,6 +91,7 @@ struct itrace_synth_opts {
bool transactions;
bool ptwrites;
bool pwr_events;
+ bool other_events;
bool errors;
bool dont_decode;
bool log;
@@ -107,6 +106,8 @@ struct itrace_synth_opts {
enum itrace_period_type period_type;
unsigned long initial_skip;
unsigned long *cpu_bitmap;
+ struct perf_time_interval *ptime_range;
+ int range_num;
};
/**
@@ -314,13 +315,13 @@ struct auxtrace_mmap_params {
*/
struct auxtrace_record {
int (*recording_options)(struct auxtrace_record *itr,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
struct record_opts *opts);
size_t (*info_priv_size)(struct auxtrace_record *itr,
- struct perf_evlist *evlist);
+ struct evlist *evlist);
int (*info_fill)(struct auxtrace_record *itr,
struct perf_session *session,
- struct auxtrace_info_event *auxtrace_info,
+ struct perf_record_auxtrace_info *auxtrace_info,
size_t priv_size);
void (*free)(struct auxtrace_record *itr);
int (*snapshot_start)(struct auxtrace_record *itr);
@@ -378,6 +379,8 @@ struct addr_filters {
int cnt;
};
+struct auxtrace_cache;
+
#ifdef HAVE_AUXTRACE_SUPPORT
/*
@@ -437,7 +440,7 @@ void auxtrace_mmap_params__init(struct auxtrace_mmap_params *mp,
unsigned int auxtrace_pages,
bool auxtrace_overwrite);
void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp,
- struct perf_evlist *evlist, int idx,
+ struct evlist *evlist, int idx,
bool per_cpu);
typedef int (*process_auxtrace_t)(struct perf_tool *tool,
@@ -487,24 +490,24 @@ int auxtrace_cache__add(struct auxtrace_cache *c, u32 key,
struct auxtrace_cache_entry *entry);
void *auxtrace_cache__lookup(struct auxtrace_cache *c, u32 key);
-struct auxtrace_record *auxtrace_record__init(struct perf_evlist *evlist,
+struct auxtrace_record *auxtrace_record__init(struct evlist *evlist,
int *err);
int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
struct record_opts *opts,
const char *str);
int auxtrace_record__options(struct auxtrace_record *itr,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
struct record_opts *opts);
size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr,
- struct perf_evlist *evlist);
+ struct evlist *evlist);
int auxtrace_record__info_fill(struct auxtrace_record *itr,
struct perf_session *session,
- struct auxtrace_info_event *auxtrace_info,
+ struct perf_record_auxtrace_info *auxtrace_info,
size_t priv_size);
void auxtrace_record__free(struct auxtrace_record *itr);
int auxtrace_record__snapshot_start(struct auxtrace_record *itr);
-int auxtrace_record__snapshot_finish(struct auxtrace_record *itr);
+int auxtrace_record__snapshot_finish(struct auxtrace_record *itr, bool on_exit);
int auxtrace_record__find_snapshot(struct auxtrace_record *itr, int idx,
struct auxtrace_mmap *mm,
unsigned char *data, u64 *head, u64 *old);
@@ -517,7 +520,7 @@ int auxtrace_index__process(int fd, u64 size, struct perf_session *session,
bool needs_swap);
void auxtrace_index__free(struct list_head *head);
-void auxtrace_synth_error(struct auxtrace_error_event *auxtrace_error, int type,
+void auxtrace_synth_error(struct perf_record_auxtrace_error *auxtrace_error, int type,
int code, int cpu, pid_t pid, pid_t tid, u64 ip,
const char *msg, u64 timestamp);
@@ -545,43 +548,13 @@ void addr_filters__init(struct addr_filters *filts);
void addr_filters__exit(struct addr_filters *filts);
int addr_filters__parse_bare_filter(struct addr_filters *filts,
const char *filter);
-int auxtrace_parse_filters(struct perf_evlist *evlist);
-
-static inline int auxtrace__process_event(struct perf_session *session,
- union perf_event *event,
- struct perf_sample *sample,
- struct perf_tool *tool)
-{
- if (!session->auxtrace)
- return 0;
+int auxtrace_parse_filters(struct evlist *evlist);
- return session->auxtrace->process_event(session, event, sample, tool);
-}
-
-static inline int auxtrace__flush_events(struct perf_session *session,
- struct perf_tool *tool)
-{
- if (!session->auxtrace)
- return 0;
-
- return session->auxtrace->flush_events(session, tool);
-}
-
-static inline void auxtrace__free_events(struct perf_session *session)
-{
- if (!session->auxtrace)
- return;
-
- return session->auxtrace->free_events(session);
-}
-
-static inline void auxtrace__free(struct perf_session *session)
-{
- if (!session->auxtrace)
- return;
-
- return session->auxtrace->free(session);
-}
+int auxtrace__process_event(struct perf_session *session, union perf_event *event,
+ struct perf_sample *sample, struct perf_tool *tool);
+int auxtrace__flush_events(struct perf_session *session, struct perf_tool *tool);
+void auxtrace__free_events(struct perf_session *session);
+void auxtrace__free(struct perf_session *session);
#define ITRACE_HELP \
" i: synthesize instructions events\n" \
@@ -599,11 +572,27 @@ static inline void auxtrace__free(struct perf_session *session)
" PERIOD[ns|us|ms|i|t]: specify period to sample stream\n" \
" concatenate multiple options. Default is ibxwpe or cewp\n"
+static inline
+void itrace_synth_opts__set_time_range(struct itrace_synth_opts *opts,
+ struct perf_time_interval *ptime_range,
+ int range_num)
+{
+ opts->ptime_range = ptime_range;
+ opts->range_num = range_num;
+}
+
+static inline
+void itrace_synth_opts__clear_time_range(struct itrace_synth_opts *opts)
+{
+ opts->ptime_range = NULL;
+ opts->range_num = 0;
+}
#else
+#include "debug.h"
static inline struct auxtrace_record *
-auxtrace_record__init(struct perf_evlist *evlist __maybe_unused,
+auxtrace_record__init(struct evlist *evlist __maybe_unused,
int *err)
{
*err = 0;
@@ -626,7 +615,7 @@ perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr __maybe_unused,
static inline
int auxtrace_record__options(struct auxtrace_record *itr __maybe_unused,
- struct perf_evlist *evlist __maybe_unused,
+ struct evlist *evlist __maybe_unused,
struct record_opts *opts __maybe_unused)
{
return 0;
@@ -723,7 +712,7 @@ void auxtrace_index__free(struct list_head *head __maybe_unused)
}
static inline
-int auxtrace_parse_filters(struct perf_evlist *evlist __maybe_unused)
+int auxtrace_parse_filters(struct evlist *evlist __maybe_unused)
{
return 0;
}
@@ -737,11 +726,26 @@ void auxtrace_mmap_params__init(struct auxtrace_mmap_params *mp,
unsigned int auxtrace_pages,
bool auxtrace_overwrite);
void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp,
- struct perf_evlist *evlist, int idx,
+ struct evlist *evlist, int idx,
bool per_cpu);
#define ITRACE_HELP ""
+static inline
+void itrace_synth_opts__set_time_range(struct itrace_synth_opts *opts
+ __maybe_unused,
+ struct perf_time_interval *ptime_range
+ __maybe_unused,
+ int range_num __maybe_unused)
+{
+}
+
+static inline
+void itrace_synth_opts__clear_time_range(struct itrace_synth_opts *opts
+ __maybe_unused)
+{
+}
+
#endif
#endif
diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c
index 2a4a0da35632..7a3d4b125323 100644
--- a/tools/perf/util/bpf-event.c
+++ b/tools/perf/util/bpf-event.c
@@ -8,12 +8,14 @@
#include <linux/err.h>
#include "bpf-event.h"
#include "debug.h"
+#include "dso.h"
#include "symbol.h"
#include "machine.h"
#include "env.h"
#include "session.h"
#include "map.h"
#include "evlist.h"
+#include "record.h"
#define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr))
@@ -34,7 +36,7 @@ static int machine__process_bpf_event_load(struct machine *machine,
struct bpf_prog_info_linear *info_linear;
struct bpf_prog_info_node *info_node;
struct perf_env *env = machine->env;
- int id = event->bpf_event.id;
+ int id = event->bpf.id;
unsigned int i;
/* perf-record, no need to handle bpf-event */
@@ -63,14 +65,13 @@ static int machine__process_bpf_event_load(struct machine *machine,
return 0;
}
-int machine__process_bpf_event(struct machine *machine __maybe_unused,
- union perf_event *event,
- struct perf_sample *sample __maybe_unused)
+int machine__process_bpf(struct machine *machine, union perf_event *event,
+ struct perf_sample *sample)
{
if (dump_trace)
- perf_event__fprintf_bpf_event(event, stdout);
+ perf_event__fprintf_bpf(event, stdout);
- switch (event->bpf_event.type) {
+ switch (event->bpf.type) {
case PERF_BPF_EVENT_PROG_LOAD:
return machine__process_bpf_event_load(machine, event, sample);
@@ -82,8 +83,7 @@ int machine__process_bpf_event(struct machine *machine __maybe_unused,
*/
break;
default:
- pr_debug("unexpected bpf_event type of %d\n",
- event->bpf_event.type);
+ pr_debug("unexpected bpf event type of %d\n", event->bpf.type);
break;
}
return 0;
@@ -160,8 +160,8 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
union perf_event *event,
struct record_opts *opts)
{
- struct ksymbol_event *ksymbol_event = &event->ksymbol_event;
- struct bpf_event *bpf_event = &event->bpf_event;
+ struct perf_record_ksymbol *ksymbol_event = &event->ksymbol;
+ struct perf_record_bpf_event *bpf_event = &event->bpf;
struct bpf_prog_info_linear *info_linear;
struct perf_tool *tool = session->tool;
struct bpf_prog_info_node *info_node;
@@ -229,10 +229,10 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
__u64 *prog_addrs = (__u64 *)(uintptr_t)(info->jited_ksyms);
int name_len;
- *ksymbol_event = (struct ksymbol_event){
+ *ksymbol_event = (struct perf_record_ksymbol) {
.header = {
.type = PERF_RECORD_KSYMBOL,
- .size = offsetof(struct ksymbol_event, name),
+ .size = offsetof(struct perf_record_ksymbol, name),
},
.addr = prog_addrs[i],
.len = prog_lens[i],
@@ -253,10 +253,10 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
if (!opts->no_bpf_event) {
/* Synthesize PERF_RECORD_BPF_EVENT */
- *bpf_event = (struct bpf_event){
+ *bpf_event = (struct perf_record_bpf_event) {
.header = {
.type = PERF_RECORD_BPF_EVENT,
- .size = sizeof(struct bpf_event),
+ .size = sizeof(struct perf_record_bpf_event),
},
.type = PERF_BPF_EVENT_PROG_LOAD,
.flags = 0,
@@ -301,7 +301,7 @@ int perf_event__synthesize_bpf_events(struct perf_session *session,
int err;
int fd;
- event = malloc(sizeof(event->bpf_event) + KSYM_NAME_LEN + machine->id_hdr_size);
+ event = malloc(sizeof(event->bpf) + KSYM_NAME_LEN + machine->id_hdr_size);
if (!event)
return -1;
while (true) {
@@ -398,9 +398,9 @@ static int bpf_event__sb_cb(union perf_event *event, void *data)
if (event->header.type != PERF_RECORD_BPF_EVENT)
return -1;
- switch (event->bpf_event.type) {
+ switch (event->bpf.type) {
case PERF_BPF_EVENT_PROG_LOAD:
- perf_env__add_bpf_info(env, event->bpf_event.id);
+ perf_env__add_bpf_info(env, event->bpf.id);
case PERF_BPF_EVENT_PROG_UNLOAD:
/*
@@ -410,15 +410,14 @@ static int bpf_event__sb_cb(union perf_event *event, void *data)
*/
break;
default:
- pr_debug("unexpected bpf_event type of %d\n",
- event->bpf_event.type);
+ pr_debug("unexpected bpf event type of %d\n", event->bpf.type);
break;
}
return 0;
}
-int bpf_event__add_sb_event(struct perf_evlist **evlist,
+int bpf_event__add_sb_event(struct evlist **evlist,
struct perf_env *env)
{
struct perf_event_attr attr = {
diff --git a/tools/perf/util/bpf-event.h b/tools/perf/util/bpf-event.h
index 04c33b3bfe28..a01c2fd68c03 100644
--- a/tools/perf/util/bpf-event.h
+++ b/tools/perf/util/bpf-event.h
@@ -13,6 +13,7 @@ struct machine;
union perf_event;
struct perf_env;
struct perf_sample;
+struct perf_session;
struct record_opts;
struct evlist;
struct target;
@@ -30,22 +31,22 @@ struct btf_node {
};
#ifdef HAVE_LIBBPF_SUPPORT
-int machine__process_bpf_event(struct machine *machine, union perf_event *event,
- struct perf_sample *sample);
+int machine__process_bpf(struct machine *machine, union perf_event *event,
+ struct perf_sample *sample);
int perf_event__synthesize_bpf_events(struct perf_session *session,
perf_event__handler_t process,
struct machine *machine,
struct record_opts *opts);
-int bpf_event__add_sb_event(struct perf_evlist **evlist,
+int bpf_event__add_sb_event(struct evlist **evlist,
struct perf_env *env);
void bpf_event__print_bpf_prog_info(struct bpf_prog_info *info,
struct perf_env *env,
FILE *fp);
#else
-static inline int machine__process_bpf_event(struct machine *machine __maybe_unused,
- union perf_event *event __maybe_unused,
- struct perf_sample *sample __maybe_unused)
+static inline int machine__process_bpf(struct machine *machine __maybe_unused,
+ union perf_event *event __maybe_unused,
+ struct perf_sample *sample __maybe_unused)
{
return 0;
}
@@ -58,7 +59,7 @@ static inline int perf_event__synthesize_bpf_events(struct perf_session *session
return 0;
}
-static inline int bpf_event__add_sb_event(struct perf_evlist **evlist __maybe_unused,
+static inline int bpf_event__add_sb_event(struct evlist **evlist __maybe_unused,
struct perf_env *env __maybe_unused)
{
return 0;
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 251d9ea6252f..37283e865352 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -12,8 +12,9 @@
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/string.h>
+#include <linux/zalloc.h>
#include <errno.h>
-#include "perf.h"
+#include <stdlib.h>
#include "debug.h"
#include "evlist.h"
#include "bpf-loader.h"
@@ -22,9 +23,12 @@
#include "probe-finder.h" // for MAX_PROBES
#include "parse-events.h"
#include "strfilter.h"
+#include "util.h"
#include "llvm-utils.h"
#include "c++/clang-c.h"
+#include <internal/xyarray.h>
+
static int libbpf_perf_print(enum libbpf_print_level level __attribute__((unused)),
const char *fmt, va_list args)
{
@@ -762,7 +766,7 @@ int bpf__foreach_event(struct bpf_object *obj,
if (priv->is_tp) {
fd = bpf_program__fd(prog);
- err = (*func)(priv->sys_name, priv->evt_name, fd, arg);
+ err = (*func)(priv->sys_name, priv->evt_name, fd, obj, arg);
if (err) {
pr_debug("bpf: tracepoint call back failed, stop iterate\n");
return err;
@@ -787,7 +791,7 @@ int bpf__foreach_event(struct bpf_object *obj,
return fd;
}
- err = (*func)(tev->group, tev->event, fd, arg);
+ err = (*func)(tev->group, tev->event, fd, obj, arg);
if (err) {
pr_debug("bpf: call back failed, stop iterate\n");
return err;
@@ -816,7 +820,7 @@ struct bpf_map_op {
} k;
union {
u64 value;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
} v;
};
@@ -828,7 +832,7 @@ static void
bpf_map_op__delete(struct bpf_map_op *op)
{
if (!list_empty(&op->list))
- list_del(&op->list);
+ list_del_init(&op->list);
if (op->key_type == BPF_MAP_KEY_RANGES)
parse_events__clear_array(&op->k.array);
free(op);
@@ -1042,7 +1046,7 @@ __bpf_map__config_value(struct bpf_map *map,
static int
bpf_map__config_value(struct bpf_map *map,
struct parse_events_term *term,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
if (!term->err_val) {
pr_debug("Config value not set\n");
@@ -1060,9 +1064,9 @@ bpf_map__config_value(struct bpf_map *map,
static int
__bpf_map__config_event(struct bpf_map *map,
struct parse_events_term *term,
- struct perf_evlist *evlist)
+ struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
const struct bpf_map_def *def;
struct bpf_map_op *op;
const char *map_name = bpf_map__name(map);
@@ -1102,7 +1106,7 @@ __bpf_map__config_event(struct bpf_map *map,
static int
bpf_map__config_event(struct bpf_map *map,
struct parse_events_term *term,
- struct perf_evlist *evlist)
+ struct evlist *evlist)
{
if (!term->err_val) {
pr_debug("Config value not set\n");
@@ -1120,7 +1124,7 @@ bpf_map__config_event(struct bpf_map *map,
struct bpf_obj_config__map_func {
const char *config_opt;
int (*config_func)(struct bpf_map *, struct parse_events_term *,
- struct perf_evlist *);
+ struct evlist *);
};
struct bpf_obj_config__map_func bpf_obj_config__map_funcs[] = {
@@ -1168,7 +1172,7 @@ config_map_indices_range_check(struct parse_events_term *term,
static int
bpf__obj_config_map(struct bpf_object *obj,
struct parse_events_term *term,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
int *key_scan_pos)
{
/* key is "map:<mapname>.<config opt>" */
@@ -1227,7 +1231,7 @@ out:
int bpf__config_obj(struct bpf_object *obj,
struct parse_events_term *term,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
int *error_pos)
{
int key_scan_pos = 0;
@@ -1400,9 +1404,9 @@ apply_config_value_for_key(int map_fd, void *pkey,
static int
apply_config_evsel_for_key(const char *name, int map_fd, void *pkey,
- struct perf_evsel *evsel)
+ struct evsel *evsel)
{
- struct xyarray *xy = evsel->fd;
+ struct xyarray *xy = evsel->core.fd;
struct perf_event_attr *attr;
unsigned int key, events;
bool check_pass = false;
@@ -1420,7 +1424,7 @@ apply_config_evsel_for_key(const char *name, int map_fd, void *pkey,
return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM;
}
- attr = &evsel->attr;
+ attr = &evsel->core.attr;
if (attr->inherit) {
pr_debug("ERROR: Can't put inherit event into map %s\n", name);
return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH;
@@ -1522,11 +1526,11 @@ int bpf__apply_obj_config(void)
(strcmp(name, \
bpf_map__name(pos)) == 0))
-struct perf_evsel *bpf__setup_output_event(struct perf_evlist *evlist, const char *name)
+struct evsel *bpf__setup_output_event(struct evlist *evlist, const char *name)
{
struct bpf_map_priv *tmpl_priv = NULL;
struct bpf_object *obj, *tmp;
- struct perf_evsel *evsel = NULL;
+ struct evsel *evsel = NULL;
struct bpf_map *map;
int err;
bool need_init = false;
@@ -1599,9 +1603,9 @@ struct perf_evsel *bpf__setup_output_event(struct perf_evlist *evlist, const cha
return evsel;
}
-int bpf__setup_stdout(struct perf_evlist *evlist)
+int bpf__setup_stdout(struct evlist *evlist)
{
- struct perf_evsel *evsel = bpf__setup_output_event(evlist, "__bpf_stdout__");
+ struct evsel *evsel = bpf__setup_output_event(evlist, "__bpf_stdout__");
return PTR_ERR_OR_ZERO(evsel);
}
@@ -1755,7 +1759,7 @@ int bpf__strerror_load(struct bpf_object *obj,
int bpf__strerror_config_obj(struct bpf_object *obj __maybe_unused,
struct parse_events_term *term __maybe_unused,
- struct perf_evlist *evlist __maybe_unused,
+ struct evlist *evlist __maybe_unused,
int *error_pos __maybe_unused, int err,
char *buf, size_t size)
{
@@ -1779,7 +1783,7 @@ int bpf__strerror_apply_obj_config(int err, char *buf, size_t size)
return 0;
}
-int bpf__strerror_setup_output_event(struct perf_evlist *evlist __maybe_unused,
+int bpf__strerror_setup_output_event(struct evlist *evlist __maybe_unused,
int err, char *buf, size_t size)
{
bpf__strerror_head(err, buf, size);
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index 3f46856e3330..25251d63164c 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -39,14 +39,14 @@ enum bpf_loader_errno {
__BPF_LOADER_ERRNO__END,
};
-struct perf_evsel;
-struct perf_evlist;
+struct evsel;
+struct evlist;
struct bpf_object;
struct parse_events_term;
#define PERF_BPF_PROBE_GROUP "perf_bpf_probe"
typedef int (*bpf_prog_iter_callback_t)(const char *group, const char *event,
- int fd, void *arg);
+ int fd, struct bpf_object *obj, void *arg);
#ifdef HAVE_LIBBPF_SUPPORT
struct bpf_object *bpf__prepare_load(const char *filename, bool source);
@@ -70,18 +70,18 @@ int bpf__foreach_event(struct bpf_object *obj,
bpf_prog_iter_callback_t func, void *arg);
int bpf__config_obj(struct bpf_object *obj, struct parse_events_term *term,
- struct perf_evlist *evlist, int *error_pos);
+ struct evlist *evlist, int *error_pos);
int bpf__strerror_config_obj(struct bpf_object *obj,
struct parse_events_term *term,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
int *error_pos, int err, char *buf,
size_t size);
int bpf__apply_obj_config(void);
int bpf__strerror_apply_obj_config(int err, char *buf, size_t size);
-int bpf__setup_stdout(struct perf_evlist *evlist);
-struct perf_evsel *bpf__setup_output_event(struct perf_evlist *evlist, const char *name);
-int bpf__strerror_setup_output_event(struct perf_evlist *evlist, int err, char *buf, size_t size);
+int bpf__setup_stdout(struct evlist *evlist);
+struct evsel *bpf__setup_output_event(struct evlist *evlist, const char *name);
+int bpf__strerror_setup_output_event(struct evlist *evlist, int err, char *buf, size_t size);
#else
#include <errno.h>
#include <string.h>
@@ -119,7 +119,7 @@ bpf__foreach_event(struct bpf_object *obj __maybe_unused,
static inline int
bpf__config_obj(struct bpf_object *obj __maybe_unused,
struct parse_events_term *term __maybe_unused,
- struct perf_evlist *evlist __maybe_unused,
+ struct evlist *evlist __maybe_unused,
int *error_pos __maybe_unused)
{
return 0;
@@ -132,13 +132,13 @@ bpf__apply_obj_config(void)
}
static inline int
-bpf__setup_stdout(struct perf_evlist *evlist __maybe_unused)
+bpf__setup_stdout(struct evlist *evlist __maybe_unused)
{
return 0;
}
-static inline struct perf_evsel *
-bpf__setup_output_event(struct perf_evlist *evlist __maybe_unused, const char *name __maybe_unused)
+static inline struct evsel *
+bpf__setup_output_event(struct evlist *evlist __maybe_unused, const char *name __maybe_unused)
{
return NULL;
}
@@ -182,7 +182,7 @@ static inline int bpf__strerror_load(struct bpf_object *obj __maybe_unused,
static inline int
bpf__strerror_config_obj(struct bpf_object *obj __maybe_unused,
struct parse_events_term *term __maybe_unused,
- struct perf_evlist *evlist __maybe_unused,
+ struct evlist *evlist __maybe_unused,
int *error_pos __maybe_unused,
int err __maybe_unused,
char *buf, size_t size)
@@ -198,7 +198,7 @@ bpf__strerror_apply_obj_config(int err __maybe_unused,
}
static inline int
-bpf__strerror_setup_output_event(struct perf_evlist *evlist __maybe_unused,
+bpf__strerror_setup_output_event(struct evlist *evlist __maybe_unused,
int err __maybe_unused, char *buf, size_t size)
{
return __bpf_strerror(buf, size);
@@ -206,7 +206,7 @@ bpf__strerror_setup_output_event(struct perf_evlist *evlist __maybe_unused,
#endif
-static inline int bpf__strerror_setup_stdout(struct perf_evlist *evlist, int err, char *buf, size_t size)
+static inline int bpf__strerror_setup_stdout(struct evlist *evlist, int err, char *buf, size_t size)
{
return bpf__strerror_setup_output_event(evlist, err, buf, size);
}
diff --git a/tools/perf/util/bpf-prologue.c b/tools/perf/util/bpf-prologue.c
index 77e4891e17b0..b020a8678eb9 100644
--- a/tools/perf/util/bpf-prologue.c
+++ b/tools/perf/util/bpf-prologue.c
@@ -8,12 +8,12 @@
*/
#include <bpf/libbpf.h>
-#include "perf.h"
#include "debug.h"
#include "bpf-loader.h"
#include "bpf-prologue.h"
#include "probe-finder.h"
#include <errno.h>
+#include <stdlib.h>
#include <dwarf-regs.h>
#include <linux/filter.h>
diff --git a/tools/perf/util/branch.c b/tools/perf/util/branch.c
index a4fce2729e50..9d1e090084a2 100644
--- a/tools/perf/util/branch.c
+++ b/tools/perf/util/branch.c
@@ -1,7 +1,8 @@
-#include "perf.h"
#include "util/util.h"
#include "util/debug.h"
+#include "util/map_symbol.h"
#include "util/branch.h"
+#include <linux/kernel.h>
static bool cross_area(u64 addr1, u64 addr2, int size)
{
diff --git a/tools/perf/util/branch.h b/tools/perf/util/branch.h
index 64f96b79f1d7..06f66dad0b79 100644
--- a/tools/perf/util/branch.h
+++ b/tools/perf/util/branch.h
@@ -16,6 +16,14 @@ struct branch_flags {
u64 reserved:40;
};
+struct branch_info {
+ struct addr_map_symbol from;
+ struct addr_map_symbol to;
+ struct branch_flags flags;
+ char *srcline_from;
+ char *srcline_to;
+};
+
struct branch_entry {
u64 from;
u64 to;
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index 0c5517a8d0b7..e5fb77755d9e 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -13,6 +13,7 @@
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include "dso.h"
#include "build-id.h"
#include "event.h"
#include "namespaces.h"
@@ -29,14 +30,15 @@
#include "probe-file.h"
#include "strlist.h"
-#include "sane_ctype.h"
+#include <linux/ctype.h>
+#include <linux/zalloc.h>
static bool no_buildid_cache;
int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel __maybe_unused,
+ struct evsel *evsel __maybe_unused,
struct machine *machine)
{
struct addr_location al;
@@ -294,7 +296,7 @@ static int write_buildid(const char *name, size_t name_len, u8 *build_id,
pid_t pid, u16 misc, struct feat_fd *fd)
{
int err;
- struct build_id_event b;
+ struct perf_record_header_build_id b;
size_t len;
len = name_len + 1;
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h
index 93668f38f1ed..aad419bb165c 100644
--- a/tools/perf/util/build-id.h
+++ b/tools/perf/util/build-id.h
@@ -24,7 +24,7 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size,
bool is_debug);
int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event,
- struct perf_sample *sample, struct perf_evsel *evsel,
+ struct perf_sample *sample, struct evsel *evsel,
struct machine *machine);
int dsos__hit_all(struct perf_session *session);
diff --git a/tools/perf/util/c++/clang-c.h b/tools/perf/util/c++/clang-c.h
index e513366f2ee0..2df8a45bd088 100644
--- a/tools/perf/util/c++/clang-c.h
+++ b/tools/perf/util/c++/clang-c.h
@@ -3,7 +3,6 @@
#define PERF_UTIL_CLANG_C_H
#include <stddef.h> /* for size_t */
-#include <util-cxx.h> /* for __maybe_unused */
#ifdef __cplusplus
extern "C" {
@@ -22,6 +21,7 @@ extern int perf_clang__compile_bpf(const char *filename,
#else
#include <errno.h>
+#include <linux/compiler.h> /* for __maybe_unused */
static inline void perf_clang__init(void) { }
static inline void perf_clang__cleanup(void) { }
diff --git a/tools/perf/util/c++/clang-test.cpp b/tools/perf/util/c++/clang-test.cpp
index 7b042a5ebc68..21b23605f78b 100644
--- a/tools/perf/util/c++/clang-test.cpp
+++ b/tools/perf/util/c++/clang-test.cpp
@@ -1,10 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
#include "clang.h"
#include "clang-c.h"
+extern "C" {
+#include "../util.h"
+}
#include "llvm/IR/Function.h"
#include "llvm/IR/LLVMContext.h"
-#include <util-cxx.h>
#include <tests/llvm.h>
#include <string>
diff --git a/tools/perf/util/cacheline.c b/tools/perf/util/cacheline.c
new file mode 100644
index 000000000000..e98b5250a517
--- /dev/null
+++ b/tools/perf/util/cacheline.c
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "cacheline.h"
+#include <unistd.h>
+
+#ifdef _SC_LEVEL1_DCACHE_LINESIZE
+#define cache_line_size(cacheline_sizep) *cacheline_sizep = sysconf(_SC_LEVEL1_DCACHE_LINESIZE)
+#else
+#include <api/fs/fs.h>
+#include "debug.h"
+static void cache_line_size(int *cacheline_sizep)
+{
+ if (sysfs__read_int("devices/system/cpu/cpu0/cache/index0/coherency_line_size", cacheline_sizep))
+ pr_debug("cannot determine cache line size");
+}
+#endif
+
+int cacheline_size(void)
+{
+ static int size;
+
+ if (!size)
+ cache_line_size(&size);
+
+ return size;
+}
diff --git a/tools/perf/util/cacheline.h b/tools/perf/util/cacheline.h
new file mode 100644
index 000000000000..dec8c0fb1f4a
--- /dev/null
+++ b/tools/perf/util/cacheline.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef PERF_CACHELINE_H
+#define PERF_CACHELINE_H
+
+#include <linux/compiler.h>
+
+int __pure cacheline_size(void);
+
+static inline u64 cl_address(u64 address)
+{
+ /* return the cacheline of the address */
+ return (address & ~(cacheline_size() - 1));
+}
+
+static inline u64 cl_offset(u64 address)
+{
+ /* return the cacheline of the address */
+ return (address & (cacheline_size() - 1));
+}
+
+#endif // PERF_CACHELINE_H
diff --git a/tools/perf/util/call-path.c b/tools/perf/util/call-path.c
index 904a17052e38..5c60b8be1cf6 100644
--- a/tools/perf/util/call-path.c
+++ b/tools/perf/util/call-path.c
@@ -1,22 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* call-path.h: Manipulate a tree data structure containing function call paths
* Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#include <linux/rbtree.h>
#include <linux/list.h>
+#include <linux/zalloc.h>
+#include <stdlib.h>
-#include "util.h"
#include "call-path.h"
static void call_path__init(struct call_path *cp, struct call_path *parent,
@@ -48,7 +40,7 @@ void call_path_root__free(struct call_path_root *cpr)
struct call_path_block *pos, *n;
list_for_each_entry_safe(pos, n, &cpr->blocks, node) {
- list_del(&pos->node);
+ list_del_init(&pos->node);
free(pos);
}
free(cpr);
diff --git a/tools/perf/util/call-path.h b/tools/perf/util/call-path.h
index 477f6d03b659..6b3229106f16 100644
--- a/tools/perf/util/call-path.h
+++ b/tools/perf/util/call-path.h
@@ -1,16 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* call-path.h: Manipulate a tree data structure containing function call paths
* Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#ifndef __PERF_CALL_PATH_H
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index abb608b09269..c14646c1f2eb 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -16,17 +16,21 @@
#include <stdbool.h>
#include <errno.h>
#include <math.h>
+#include <linux/string.h>
+#include <linux/zalloc.h>
#include "asm/bug.h"
+#include "debug.h"
+#include "dso.h"
#include "hist.h"
-#include "util.h"
#include "sort.h"
#include "machine.h"
#include "map.h"
#include "callchain.h"
#include "branch.h"
#include "symbol.h"
+#include "../perf.h"
#define CALLCHAIN_PARAM_DEFAULT \
.mode = CHAIN_GRAPH_ABS, \
@@ -636,7 +640,7 @@ add_child(struct callchain_node *parent,
struct callchain_list *call, *tmp;
list_for_each_entry_safe(call, tmp, &new->val, list) {
- list_del(&call->list);
+ list_del_init(&call->list);
map__zput(call->ms.map);
free(call);
}
@@ -1002,7 +1006,7 @@ merge_chain_branch(struct callchain_cursor *cursor,
callchain_cursor_append(cursor, list->ip,
list->ms.map, list->ms.sym,
false, NULL, 0, 0, 0, list->srcline);
- list_del(&list->list);
+ list_del_init(&list->list);
map__zput(list->ms.map);
free(list);
}
@@ -1077,7 +1081,7 @@ int callchain_cursor_append(struct callchain_cursor *cursor,
int sample__resolve_callchain(struct perf_sample *sample,
struct callchain_cursor *cursor, struct symbol **parent,
- struct perf_evsel *evsel, struct addr_location *al,
+ struct evsel *evsel, struct addr_location *al,
int max_stack)
{
if (sample->callchain == NULL && !symbol_conf.show_branchflag_count)
@@ -1453,13 +1457,13 @@ static void free_callchain_node(struct callchain_node *node)
struct rb_node *n;
list_for_each_entry_safe(list, tmp, &node->parent_val, list) {
- list_del(&list->list);
+ list_del_init(&list->list);
map__zput(list->ms.map);
free(list);
}
list_for_each_entry_safe(list, tmp, &node->val, list) {
- list_del(&list->list);
+ list_del_init(&list->list);
map__zput(list->ms.map);
free(list);
}
@@ -1544,7 +1548,7 @@ int callchain_node__make_parent_list(struct callchain_node *node)
out:
list_for_each_entry_safe(chain, new, &head, list) {
- list_del(&chain->list);
+ list_del_init(&chain->list);
map__zput(chain->ms.map);
free(chain);
}
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index 80e056a3d882..b042ceef4114 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -8,6 +8,7 @@
#include "map_symbol.h"
#include "branch.h"
+struct evsel;
struct map;
#define HELP_PAD "\t\t\t\t"
@@ -236,7 +237,7 @@ int record_opts__parse_callchain(struct record_opts *record,
int sample__resolve_callchain(struct perf_sample *sample,
struct callchain_cursor *cursor, struct symbol **parent,
- struct perf_evsel *evsel, struct addr_location *al,
+ struct evsel *evsel, struct addr_location *al,
int max_stack);
int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample);
int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node,
diff --git a/tools/perf/util/cap.c b/tools/perf/util/cap.c
new file mode 100644
index 000000000000..c3ba841bbf37
--- /dev/null
+++ b/tools/perf/util/cap.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Capability utilities
+ */
+
+#ifdef HAVE_LIBCAP_SUPPORT
+
+#include "cap.h"
+#include <stdbool.h>
+#include <sys/capability.h>
+
+bool perf_cap__capable(cap_value_t cap)
+{
+ cap_flag_value_t val;
+ cap_t caps = cap_get_proc();
+
+ if (!caps)
+ return false;
+
+ if (cap_get_flag(caps, cap, CAP_EFFECTIVE, &val) != 0)
+ val = CAP_CLEAR;
+
+ if (cap_free(caps) != 0)
+ return false;
+
+ return val == CAP_SET;
+}
+
+#endif /* HAVE_LIBCAP_SUPPORT */
diff --git a/tools/perf/util/cap.h b/tools/perf/util/cap.h
new file mode 100644
index 000000000000..051dc590ceee
--- /dev/null
+++ b/tools/perf/util/cap.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PERF_CAP_H
+#define __PERF_CAP_H
+
+#include <stdbool.h>
+#include <linux/capability.h>
+#include <linux/compiler.h>
+
+#ifdef HAVE_LIBCAP_SUPPORT
+
+#include <sys/capability.h>
+
+bool perf_cap__capable(cap_value_t cap);
+
+#else
+
+#include <unistd.h>
+#include <sys/types.h>
+
+static inline bool perf_cap__capable(int cap __maybe_unused)
+{
+ return geteuid() == 0;
+}
+
+#endif /* HAVE_LIBCAP_SUPPORT */
+
+/* For older systems */
+#ifndef CAP_SYSLOG
+#define CAP_SYSLOG 34
+#endif
+
+#endif /* __PERF_CAP_H */
diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c
index ccd02634a616..4881d4af3381 100644
--- a/tools/perf/util/cgroup.c
+++ b/tools/perf/util/cgroup.c
@@ -1,14 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
-#include "util.h"
-#include "../perf.h"
#include <subcmd/parse-options.h>
#include "evsel.h"
#include "cgroup.h"
#include "evlist.h"
#include <linux/stringify.h>
+#include <linux/zalloc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
int nr_cgroups;
@@ -90,9 +91,9 @@ static int open_cgroup(const char *name)
return fd;
}
-static struct cgroup *evlist__find_cgroup(struct perf_evlist *evlist, const char *str)
+static struct cgroup *evlist__find_cgroup(struct evlist *evlist, const char *str)
{
- struct perf_evsel *counter;
+ struct evsel *counter;
/*
* check if cgrp is already defined, if so we reuse it
*/
@@ -124,22 +125,22 @@ static struct cgroup *cgroup__new(const char *name)
return cgroup;
out_free_name:
- free(cgroup->name);
+ zfree(&cgroup->name);
out_err:
free(cgroup);
return NULL;
}
-struct cgroup *evlist__findnew_cgroup(struct perf_evlist *evlist, const char *name)
+struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name)
{
struct cgroup *cgroup = evlist__find_cgroup(evlist, name);
return cgroup ?: cgroup__new(name);
}
-static int add_cgroup(struct perf_evlist *evlist, const char *str)
+static int add_cgroup(struct evlist *evlist, const char *str)
{
- struct perf_evsel *counter;
+ struct evsel *counter;
struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str);
int n;
@@ -184,15 +185,15 @@ struct cgroup *cgroup__get(struct cgroup *cgroup)
return cgroup;
}
-static void evsel__set_default_cgroup(struct perf_evsel *evsel, struct cgroup *cgroup)
+static void evsel__set_default_cgroup(struct evsel *evsel, struct cgroup *cgroup)
{
if (evsel->cgrp == NULL)
evsel->cgrp = cgroup__get(cgroup);
}
-void evlist__set_default_cgroup(struct perf_evlist *evlist, struct cgroup *cgroup)
+void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel)
evsel__set_default_cgroup(evsel, cgroup);
@@ -201,14 +202,14 @@ void evlist__set_default_cgroup(struct perf_evlist *evlist, struct cgroup *cgrou
int parse_cgroups(const struct option *opt, const char *str,
int unset __maybe_unused)
{
- struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
- struct perf_evsel *counter;
+ struct evlist *evlist = *(struct evlist **)opt->value;
+ struct evsel *counter;
struct cgroup *cgrp = NULL;
const char *p, *e, *eos = str + strlen(str);
char *s;
int ret, i;
- if (list_empty(&evlist->entries)) {
+ if (list_empty(&evlist->core.entries)) {
fprintf(stderr, "must define events before cgroups\n");
return -1;
}
diff --git a/tools/perf/util/cgroup.h b/tools/perf/util/cgroup.h
index f033a80c1b14..2ec11f01090d 100644
--- a/tools/perf/util/cgroup.h
+++ b/tools/perf/util/cgroup.h
@@ -18,11 +18,11 @@ extern int nr_cgroups; /* number of explicit cgroups defined */
struct cgroup *cgroup__get(struct cgroup *cgroup);
void cgroup__put(struct cgroup *cgroup);
-struct perf_evlist;
+struct evlist;
-struct cgroup *evlist__findnew_cgroup(struct perf_evlist *evlist, const char *name);
+struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name);
-void evlist__set_default_cgroup(struct perf_evlist *evlist, struct cgroup *cgroup);
+void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup);
int parse_cgroups(const struct option *opt, const char *str, int unset);
diff --git a/tools/perf/util/cloexec.c b/tools/perf/util/cloexec.c
index 06f48312c5ed..4e904fcb2783 100644
--- a/tools/perf/util/cloexec.c
+++ b/tools/perf/util/cloexec.c
@@ -2,12 +2,14 @@
#include <errno.h>
#include <sched.h>
#include "util.h"
-#include "../perf.h"
+#include "../perf-sys.h"
#include "cloexec.h"
+#include "event.h"
#include "asm/bug.h"
#include "debug.h"
#include <unistd.h>
#include <sys/syscall.h>
+#include <linux/string.h>
static unsigned long flag = PERF_FLAG_FD_CLOEXEC;
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c
index 39b8c4ec4e2e..bffbdd216a6a 100644
--- a/tools/perf/util/color.c
+++ b/tools/perf/util/color.c
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
-#include "cache.h"
+#include <subcmd/pager.h>
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
#include "color.h"
#include <math.h>
#include <unistd.h>
diff --git a/tools/perf/util/color_config.c b/tools/perf/util/color_config.c
index 817dc56e7e95..dc09ba7cb31e 100644
--- a/tools/perf/util/color_config.c
+++ b/tools/perf/util/color_config.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
-#include "cache.h"
+#include <subcmd/pager.h>
+#include <string.h>
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c
index 1066de92af12..afb8d4fd2644 100644
--- a/tools/perf/util/comm.c
+++ b/tools/perf/util/comm.c
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
#include "comm.h"
-#include "util.h"
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <linux/refcount.h>
#include <linux/rbtree.h>
+#include <linux/zalloc.h>
#include "rwsem.h"
struct comm_str {
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index 7e3c1b60120c..0bc9c4d7fdc5 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -11,20 +11,23 @@
*/
#include <errno.h>
#include <sys/param.h>
-#include "util.h"
#include "cache.h"
#include "callchain.h"
#include <subcmd/exec-cmd.h>
#include "util/event.h" /* proc_map_timeout */
#include "util/hist.h" /* perf_hist_config */
#include "util/llvm-utils.h" /* perf_llvm_config */
+#include "build-id.h"
+#include "debug.h"
#include "config.h"
+#include "debug.h"
#include <sys/types.h>
#include <sys/stat.h>
+#include <stdlib.h>
#include <unistd.h>
#include <linux/string.h>
-
-#include "sane_ctype.h"
+#include <linux/zalloc.h>
+#include <linux/ctype.h>
#define MAXNAME (256)
@@ -739,11 +742,15 @@ int perf_config(config_fn_t fn, void *data)
if (ret < 0) {
pr_err("Error: wrong config key-value pair %s=%s\n",
key, value);
- break;
+ /*
+ * Can't be just a 'break', as perf_config_set__for_each_entry()
+ * expands to two nested for() loops.
+ */
+ goto out;
}
}
}
-
+out:
return ret;
}
diff --git a/tools/perf/util/counts.c b/tools/perf/util/counts.c
index 03032b410c29..f94e1a23dad6 100644
--- a/tools/perf/util/counts.c
+++ b/tools/perf/util/counts.c
@@ -3,7 +3,7 @@
#include <stdlib.h>
#include "evsel.h"
#include "counts.h"
-#include "util.h"
+#include <linux/zalloc.h>
struct perf_counts *perf_counts__new(int ncpus, int nthreads)
{
@@ -19,6 +19,15 @@ struct perf_counts *perf_counts__new(int ncpus, int nthreads)
}
counts->values = values;
+
+ values = xyarray__new(ncpus, nthreads, sizeof(bool));
+ if (!values) {
+ xyarray__delete(counts->values);
+ free(counts);
+ return NULL;
+ }
+
+ counts->loaded = values;
}
return counts;
@@ -27,6 +36,7 @@ struct perf_counts *perf_counts__new(int ncpus, int nthreads)
void perf_counts__delete(struct perf_counts *counts)
{
if (counts) {
+ xyarray__delete(counts->loaded);
xyarray__delete(counts->values);
free(counts);
}
@@ -34,21 +44,22 @@ void perf_counts__delete(struct perf_counts *counts)
static void perf_counts__reset(struct perf_counts *counts)
{
+ xyarray__reset(counts->loaded);
xyarray__reset(counts->values);
}
-void perf_evsel__reset_counts(struct perf_evsel *evsel)
+void perf_evsel__reset_counts(struct evsel *evsel)
{
perf_counts__reset(evsel->counts);
}
-int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus, int nthreads)
+int perf_evsel__alloc_counts(struct evsel *evsel, int ncpus, int nthreads)
{
evsel->counts = perf_counts__new(ncpus, nthreads);
return evsel->counts != NULL ? 0 : -ENOMEM;
}
-void perf_evsel__free_counts(struct perf_evsel *evsel)
+void perf_evsel__free_counts(struct evsel *evsel)
{
perf_counts__delete(evsel->counts);
evsel->counts = NULL;
diff --git a/tools/perf/util/counts.h b/tools/perf/util/counts.h
index 0d1050ccc586..92196df4945f 100644
--- a/tools/perf/util/counts.h
+++ b/tools/perf/util/counts.h
@@ -2,24 +2,18 @@
#ifndef __PERF_COUNTS_H
#define __PERF_COUNTS_H
-#include "xyarray.h"
-
-struct perf_counts_values {
- union {
- struct {
- u64 val;
- u64 ena;
- u64 run;
- };
- u64 values[3];
- };
- bool loaded;
-};
+#include <linux/types.h>
+#include <internal/xyarray.h>
+#include <perf/evsel.h>
+#include <stdbool.h>
+
+struct evsel;
struct perf_counts {
s8 scaled;
struct perf_counts_values aggr;
struct xyarray *values;
+ struct xyarray *loaded;
};
@@ -29,11 +23,23 @@ perf_counts(struct perf_counts *counts, int cpu, int thread)
return xyarray__entry(counts->values, cpu, thread);
}
+static inline bool
+perf_counts__is_loaded(struct perf_counts *counts, int cpu, int thread)
+{
+ return *((bool *) xyarray__entry(counts->loaded, cpu, thread));
+}
+
+static inline void
+perf_counts__set_loaded(struct perf_counts *counts, int cpu, int thread, bool loaded)
+{
+ *((bool *) xyarray__entry(counts->loaded, cpu, thread)) = loaded;
+}
+
struct perf_counts *perf_counts__new(int ncpus, int nthreads);
void perf_counts__delete(struct perf_counts *counts);
-void perf_evsel__reset_counts(struct perf_evsel *evsel);
-int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus, int nthreads);
-void perf_evsel__free_counts(struct perf_evsel *evsel);
+void perf_evsel__reset_counts(struct evsel *evsel);
+int perf_evsel__alloc_counts(struct evsel *evsel, int ncpus, int nthreads);
+void perf_evsel__free_counts(struct evsel *evsel);
#endif /* __PERF_COUNTS_H */
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 0b599229bc7e..a22c1114e880 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
-#include "util.h"
#include <api/fs/fs.h>
-#include "../perf.h"
#include "cpumap.h"
+#include "debug.h"
+#include "event.h"
#include <assert.h>
#include <dirent.h>
#include <stdio.h>
@@ -10,197 +10,19 @@
#include <linux/bitmap.h>
#include "asm/bug.h"
-#include "sane_ctype.h"
+#include <linux/ctype.h>
+#include <linux/zalloc.h>
static int max_cpu_num;
static int max_present_cpu_num;
static int max_node_num;
static int *cpunode_map;
-static struct cpu_map *cpu_map__default_new(void)
+static struct perf_cpu_map *cpu_map__from_entries(struct cpu_map_entries *cpus)
{
- struct cpu_map *cpus;
- int nr_cpus;
+ struct perf_cpu_map *map;
- nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
- if (nr_cpus < 0)
- return NULL;
-
- cpus = malloc(sizeof(*cpus) + nr_cpus * sizeof(int));
- if (cpus != NULL) {
- int i;
- for (i = 0; i < nr_cpus; ++i)
- cpus->map[i] = i;
-
- cpus->nr = nr_cpus;
- refcount_set(&cpus->refcnt, 1);
- }
-
- return cpus;
-}
-
-static struct cpu_map *cpu_map__trim_new(int nr_cpus, int *tmp_cpus)
-{
- size_t payload_size = nr_cpus * sizeof(int);
- struct cpu_map *cpus = malloc(sizeof(*cpus) + payload_size);
-
- if (cpus != NULL) {
- cpus->nr = nr_cpus;
- memcpy(cpus->map, tmp_cpus, payload_size);
- refcount_set(&cpus->refcnt, 1);
- }
-
- return cpus;
-}
-
-struct cpu_map *cpu_map__read(FILE *file)
-{
- struct cpu_map *cpus = NULL;
- int nr_cpus = 0;
- int *tmp_cpus = NULL, *tmp;
- int max_entries = 0;
- int n, cpu, prev;
- char sep;
-
- sep = 0;
- prev = -1;
- for (;;) {
- n = fscanf(file, "%u%c", &cpu, &sep);
- if (n <= 0)
- break;
- if (prev >= 0) {
- int new_max = nr_cpus + cpu - prev - 1;
-
- if (new_max >= max_entries) {
- max_entries = new_max + MAX_NR_CPUS / 2;
- tmp = realloc(tmp_cpus, max_entries * sizeof(int));
- if (tmp == NULL)
- goto out_free_tmp;
- tmp_cpus = tmp;
- }
-
- while (++prev < cpu)
- tmp_cpus[nr_cpus++] = prev;
- }
- if (nr_cpus == max_entries) {
- max_entries += MAX_NR_CPUS;
- tmp = realloc(tmp_cpus, max_entries * sizeof(int));
- if (tmp == NULL)
- goto out_free_tmp;
- tmp_cpus = tmp;
- }
-
- tmp_cpus[nr_cpus++] = cpu;
- if (n == 2 && sep == '-')
- prev = cpu;
- else
- prev = -1;
- if (n == 1 || sep == '\n')
- break;
- }
-
- if (nr_cpus > 0)
- cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
- else
- cpus = cpu_map__default_new();
-out_free_tmp:
- free(tmp_cpus);
- return cpus;
-}
-
-static struct cpu_map *cpu_map__read_all_cpu_map(void)
-{
- struct cpu_map *cpus = NULL;
- FILE *onlnf;
-
- onlnf = fopen("/sys/devices/system/cpu/online", "r");
- if (!onlnf)
- return cpu_map__default_new();
-
- cpus = cpu_map__read(onlnf);
- fclose(onlnf);
- return cpus;
-}
-
-struct cpu_map *cpu_map__new(const char *cpu_list)
-{
- struct cpu_map *cpus = NULL;
- unsigned long start_cpu, end_cpu = 0;
- char *p = NULL;
- int i, nr_cpus = 0;
- int *tmp_cpus = NULL, *tmp;
- int max_entries = 0;
-
- if (!cpu_list)
- return cpu_map__read_all_cpu_map();
-
- /*
- * must handle the case of empty cpumap to cover
- * TOPOLOGY header for NUMA nodes with no CPU
- * ( e.g., because of CPU hotplug)
- */
- if (!isdigit(*cpu_list) && *cpu_list != '\0')
- goto out;
-
- while (isdigit(*cpu_list)) {
- p = NULL;
- start_cpu = strtoul(cpu_list, &p, 0);
- if (start_cpu >= INT_MAX
- || (*p != '\0' && *p != ',' && *p != '-'))
- goto invalid;
-
- if (*p == '-') {
- cpu_list = ++p;
- p = NULL;
- end_cpu = strtoul(cpu_list, &p, 0);
-
- if (end_cpu >= INT_MAX || (*p != '\0' && *p != ','))
- goto invalid;
-
- if (end_cpu < start_cpu)
- goto invalid;
- } else {
- end_cpu = start_cpu;
- }
-
- for (; start_cpu <= end_cpu; start_cpu++) {
- /* check for duplicates */
- for (i = 0; i < nr_cpus; i++)
- if (tmp_cpus[i] == (int)start_cpu)
- goto invalid;
-
- if (nr_cpus == max_entries) {
- max_entries += MAX_NR_CPUS;
- tmp = realloc(tmp_cpus, max_entries * sizeof(int));
- if (tmp == NULL)
- goto invalid;
- tmp_cpus = tmp;
- }
- tmp_cpus[nr_cpus++] = (int)start_cpu;
- }
- if (*p)
- ++p;
-
- cpu_list = p;
- }
-
- if (nr_cpus > 0)
- cpus = cpu_map__trim_new(nr_cpus, tmp_cpus);
- else if (*cpu_list != '\0')
- cpus = cpu_map__default_new();
- else
- cpus = cpu_map__dummy_new();
-invalid:
- free(tmp_cpus);
-out:
- return cpus;
-}
-
-static struct cpu_map *cpu_map__from_entries(struct cpu_map_entries *cpus)
-{
- struct cpu_map *map;
-
- map = cpu_map__empty_new(cpus->nr);
+ map = perf_cpu_map__empty_new(cpus->nr);
if (map) {
unsigned i;
@@ -220,14 +42,14 @@ static struct cpu_map *cpu_map__from_entries(struct cpu_map_entries *cpus)
return map;
}
-static struct cpu_map *cpu_map__from_mask(struct cpu_map_mask *mask)
+static struct perf_cpu_map *cpu_map__from_mask(struct perf_record_record_cpu_map *mask)
{
- struct cpu_map *map;
+ struct perf_cpu_map *map;
int nr, nbits = mask->nr * mask->long_size * BITS_PER_BYTE;
nr = bitmap_weight(mask->mask, nbits);
- map = cpu_map__empty_new(nr);
+ map = perf_cpu_map__empty_new(nr);
if (map) {
int cpu, i = 0;
@@ -238,15 +60,15 @@ static struct cpu_map *cpu_map__from_mask(struct cpu_map_mask *mask)
}
-struct cpu_map *cpu_map__new_data(struct cpu_map_data *data)
+struct perf_cpu_map *cpu_map__new_data(struct perf_record_cpu_map_data *data)
{
if (data->type == PERF_CPU_MAP__CPUS)
return cpu_map__from_entries((struct cpu_map_entries *)data->data);
else
- return cpu_map__from_mask((struct cpu_map_mask *)data->data);
+ return cpu_map__from_mask((struct perf_record_record_cpu_map *)data->data);
}
-size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp)
+size_t cpu_map__fprintf(struct perf_cpu_map *map, FILE *fp)
{
#define BUFSIZE 1024
char buf[BUFSIZE];
@@ -256,22 +78,9 @@ size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp)
#undef BUFSIZE
}
-struct cpu_map *cpu_map__dummy_new(void)
+struct perf_cpu_map *perf_cpu_map__empty_new(int nr)
{
- struct cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int));
-
- if (cpus != NULL) {
- cpus->nr = 1;
- cpus->map[0] = -1;
- refcount_set(&cpus->refcnt, 1);
- }
-
- return cpus;
-}
-
-struct cpu_map *cpu_map__empty_new(int nr)
-{
- struct cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int) * nr);
+ struct perf_cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int) * nr);
if (cpus != NULL) {
int i;
@@ -286,28 +95,6 @@ struct cpu_map *cpu_map__empty_new(int nr)
return cpus;
}
-static void cpu_map__delete(struct cpu_map *map)
-{
- if (map) {
- WARN_ONCE(refcount_read(&map->refcnt) != 0,
- "cpu_map refcnt unbalanced\n");
- free(map);
- }
-}
-
-struct cpu_map *cpu_map__get(struct cpu_map *map)
-{
- if (map)
- refcount_inc(&map->refcnt);
- return map;
-}
-
-void cpu_map__put(struct cpu_map *map)
-{
- if (map && refcount_dec_and_test(&map->refcnt))
- cpu_map__delete(map);
-}
-
static int cpu__get_topology_int(int cpu, const char *name, int *value)
{
char path[PATH_MAX];
@@ -324,7 +111,7 @@ int cpu_map__get_socket_id(int cpu)
return ret ?: value;
}
-int cpu_map__get_socket(struct cpu_map *map, int idx, void *data __maybe_unused)
+int cpu_map__get_socket(struct perf_cpu_map *map, int idx, void *data __maybe_unused)
{
int cpu;
@@ -341,11 +128,11 @@ static int cmp_ids(const void *a, const void *b)
return *(int *)a - *(int *)b;
}
-int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
- int (*f)(struct cpu_map *map, int cpu, void *data),
+int cpu_map__build_map(struct perf_cpu_map *cpus, struct perf_cpu_map **res,
+ int (*f)(struct perf_cpu_map *map, int cpu, void *data),
void *data)
{
- struct cpu_map *c;
+ struct perf_cpu_map *c;
int nr = cpus->nr;
int cpu, s1, s2;
@@ -373,15 +160,55 @@ int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
return 0;
}
+int cpu_map__get_die_id(int cpu)
+{
+ int value, ret = cpu__get_topology_int(cpu, "die_id", &value);
+
+ return ret ?: value;
+}
+
+int cpu_map__get_die(struct perf_cpu_map *map, int idx, void *data)
+{
+ int cpu, die_id, s;
+
+ if (idx > map->nr)
+ return -1;
+
+ cpu = map->map[idx];
+
+ die_id = cpu_map__get_die_id(cpu);
+ /* There is no die_id on legacy system. */
+ if (die_id == -1)
+ die_id = 0;
+
+ s = cpu_map__get_socket(map, idx, data);
+ if (s == -1)
+ return -1;
+
+ /*
+ * Encode socket in bit range 15:8
+ * die_id is relative to socket, and
+ * we need a global id. So we combine
+ * socket + die id
+ */
+ if (WARN_ONCE(die_id >> 8, "The die id number is too big.\n"))
+ return -1;
+
+ if (WARN_ONCE(s >> 8, "The socket id number is too big.\n"))
+ return -1;
+
+ return (s << 8) | (die_id & 0xff);
+}
+
int cpu_map__get_core_id(int cpu)
{
int value, ret = cpu__get_topology_int(cpu, "core_id", &value);
return ret ?: value;
}
-int cpu_map__get_core(struct cpu_map *map, int idx, void *data)
+int cpu_map__get_core(struct perf_cpu_map *map, int idx, void *data)
{
- int cpu, s;
+ int cpu, s_die;
if (idx > map->nr)
return -1;
@@ -390,25 +217,35 @@ int cpu_map__get_core(struct cpu_map *map, int idx, void *data)
cpu = cpu_map__get_core_id(cpu);
- s = cpu_map__get_socket(map, idx, data);
- if (s == -1)
+ /* s_die is the combination of socket + die id */
+ s_die = cpu_map__get_die(map, idx, data);
+ if (s_die == -1)
return -1;
/*
- * encode socket in upper 16 bits
- * core_id is relative to socket, and
+ * encode socket in bit range 31:24
+ * encode die id in bit range 23:16
+ * core_id is relative to socket and die,
* we need a global id. So we combine
- * socket+ core id
+ * socket + die id + core id
*/
- return (s << 16) | (cpu & 0xffff);
+ if (WARN_ONCE(cpu >> 16, "The core id number is too big.\n"))
+ return -1;
+
+ return (s_die << 16) | (cpu & 0xffff);
}
-int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp)
+int cpu_map__build_socket_map(struct perf_cpu_map *cpus, struct perf_cpu_map **sockp)
{
return cpu_map__build_map(cpus, sockp, cpu_map__get_socket, NULL);
}
-int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep)
+int cpu_map__build_die_map(struct perf_cpu_map *cpus, struct perf_cpu_map **diep)
+{
+ return cpu_map__build_map(cpus, diep, cpu_map__get_die, NULL);
+}
+
+int cpu_map__build_core_map(struct perf_cpu_map *cpus, struct perf_cpu_map **corep)
{
return cpu_map__build_map(cpus, corep, cpu_map__get_core, NULL);
}
@@ -620,29 +457,17 @@ int cpu__setup_cpunode_map(void)
return 0;
}
-bool cpu_map__has(struct cpu_map *cpus, int cpu)
+bool cpu_map__has(struct perf_cpu_map *cpus, int cpu)
{
- return cpu_map__idx(cpus, cpu) != -1;
+ return perf_cpu_map__idx(cpus, cpu) != -1;
}
-int cpu_map__idx(struct cpu_map *cpus, int cpu)
-{
- int i;
-
- for (i = 0; i < cpus->nr; ++i) {
- if (cpus->map[i] == cpu)
- return i;
- }
-
- return -1;
-}
-
-int cpu_map__cpu(struct cpu_map *cpus, int idx)
+int cpu_map__cpu(struct perf_cpu_map *cpus, int idx)
{
return cpus->map[idx];
}
-size_t cpu_map__snprint(struct cpu_map *map, char *buf, size_t size)
+size_t cpu_map__snprint(struct perf_cpu_map *map, char *buf, size_t size)
{
int i, cpu, start = -1;
bool first = true;
@@ -694,14 +519,17 @@ static char hex_char(unsigned char val)
return '?';
}
-size_t cpu_map__snprint_mask(struct cpu_map *map, char *buf, size_t size)
+size_t cpu_map__snprint_mask(struct perf_cpu_map *map, char *buf, size_t size)
{
int i, cpu;
char *ptr = buf;
unsigned char *bitmap;
int last_cpu = cpu_map__cpu(map, map->nr - 1);
- bitmap = zalloc((last_cpu + 7) / 8);
+ if (buf == NULL)
+ return 0;
+
+ bitmap = zalloc(last_cpu / 8 + 1);
if (bitmap == NULL) {
buf[0] = '\0';
return 0;
@@ -731,12 +559,12 @@ size_t cpu_map__snprint_mask(struct cpu_map *map, char *buf, size_t size)
return ptr - buf;
}
-const struct cpu_map *cpu_map__online(void) /* thread unsafe */
+const struct perf_cpu_map *cpu_map__online(void) /* thread unsafe */
{
- static const struct cpu_map *online = NULL;
+ static const struct perf_cpu_map *online = NULL;
if (!online)
- online = cpu_map__new(NULL); /* from /sys/devices/system/cpu/online */
+ online = perf_cpu_map__new(NULL); /* from /sys/devices/system/cpu/online */
return online;
}
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index f00ce624b9f7..2553bef1279d 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -4,37 +4,28 @@
#include <stdio.h>
#include <stdbool.h>
-#include <linux/refcount.h>
+#include <internal/cpumap.h>
+#include <perf/cpumap.h>
-#include "perf.h"
-#include "util/debug.h"
+struct perf_record_cpu_map_data;
-struct cpu_map {
- refcount_t refcnt;
- int nr;
- int map[];
-};
-
-struct cpu_map *cpu_map__new(const char *cpu_list);
-struct cpu_map *cpu_map__empty_new(int nr);
-struct cpu_map *cpu_map__dummy_new(void);
-struct cpu_map *cpu_map__new_data(struct cpu_map_data *data);
-struct cpu_map *cpu_map__read(FILE *file);
-size_t cpu_map__snprint(struct cpu_map *map, char *buf, size_t size);
-size_t cpu_map__snprint_mask(struct cpu_map *map, char *buf, size_t size);
-size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp);
+struct perf_cpu_map *perf_cpu_map__empty_new(int nr);
+struct perf_cpu_map *cpu_map__new_data(struct perf_record_cpu_map_data *data);
+size_t cpu_map__snprint(struct perf_cpu_map *map, char *buf, size_t size);
+size_t cpu_map__snprint_mask(struct perf_cpu_map *map, char *buf, size_t size);
+size_t cpu_map__fprintf(struct perf_cpu_map *map, FILE *fp);
int cpu_map__get_socket_id(int cpu);
-int cpu_map__get_socket(struct cpu_map *map, int idx, void *data);
+int cpu_map__get_socket(struct perf_cpu_map *map, int idx, void *data);
+int cpu_map__get_die_id(int cpu);
+int cpu_map__get_die(struct perf_cpu_map *map, int idx, void *data);
int cpu_map__get_core_id(int cpu);
-int cpu_map__get_core(struct cpu_map *map, int idx, void *data);
-int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp);
-int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep);
-const struct cpu_map *cpu_map__online(void); /* thread unsafe */
-
-struct cpu_map *cpu_map__get(struct cpu_map *map);
-void cpu_map__put(struct cpu_map *map);
+int cpu_map__get_core(struct perf_cpu_map *map, int idx, void *data);
+int cpu_map__build_socket_map(struct perf_cpu_map *cpus, struct perf_cpu_map **sockp);
+int cpu_map__build_die_map(struct perf_cpu_map *cpus, struct perf_cpu_map **diep);
+int cpu_map__build_core_map(struct perf_cpu_map *cpus, struct perf_cpu_map **corep);
+const struct perf_cpu_map *cpu_map__online(void); /* thread unsafe */
-static inline int cpu_map__socket(struct cpu_map *sock, int s)
+static inline int cpu_map__socket(struct perf_cpu_map *sock, int s)
{
if (!sock || s > sock->nr || s < 0)
return 0;
@@ -43,22 +34,17 @@ static inline int cpu_map__socket(struct cpu_map *sock, int s)
static inline int cpu_map__id_to_socket(int id)
{
- return id >> 16;
+ return id >> 24;
}
-static inline int cpu_map__id_to_cpu(int id)
+static inline int cpu_map__id_to_die(int id)
{
- return id & 0xffff;
+ return (id >> 16) & 0xff;
}
-static inline int cpu_map__nr(const struct cpu_map *map)
-{
- return map ? map->nr : 1;
-}
-
-static inline bool cpu_map__empty(const struct cpu_map *map)
+static inline int cpu_map__id_to_cpu(int id)
{
- return map ? map->map[0] == -1 : true;
+ return id & 0xffff;
}
int cpu__setup_cpunode_map(void);
@@ -68,11 +54,10 @@ int cpu__max_cpu(void);
int cpu__max_present_cpu(void);
int cpu__get_node(int cpu);
-int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
- int (*f)(struct cpu_map *map, int cpu, void *data),
+int cpu_map__build_map(struct perf_cpu_map *cpus, struct perf_cpu_map **res,
+ int (*f)(struct perf_cpu_map *map, int cpu, void *data),
void *data);
-int cpu_map__cpu(struct cpu_map *cpus, int idx);
-bool cpu_map__has(struct cpu_map *cpus, int cpu);
-int cpu_map__idx(struct cpu_map *cpus, int cpu);
+int cpu_map__cpu(struct perf_cpu_map *cpus, int idx);
+bool cpu_map__has(struct perf_cpu_map *cpus, int cpu);
#endif /* __PERF_CPUMAP_H */
diff --git a/tools/perf/util/cputopo.c b/tools/perf/util/cputopo.c
index ece0710249d4..1b52402a8923 100644
--- a/tools/perf/util/cputopo.c
+++ b/tools/perf/util/cputopo.c
@@ -1,18 +1,26 @@
// SPDX-License-Identifier: GPL-2.0
#include <sys/param.h>
+#include <sys/utsname.h>
#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
#include <api/fs/fs.h>
+#include <linux/zalloc.h>
+#include <perf/cpumap.h>
#include "cputopo.h"
#include "cpumap.h"
-#include "util.h"
+#include "debug.h"
#include "env.h"
-
#define CORE_SIB_FMT \
"%s/devices/system/cpu/cpu%d/topology/core_siblings_list"
+#define DIE_SIB_FMT \
+ "%s/devices/system/cpu/cpu%d/topology/die_cpus_list"
#define THRD_SIB_FMT \
"%s/devices/system/cpu/cpu%d/topology/thread_siblings_list"
+#define THRD_SIB_FMT_NEW \
+ "%s/devices/system/cpu/cpu%d/topology/core_cpus_list"
#define NODE_ONLINE_FMT \
"%s/devices/system/node/online"
#define NODE_MEMINFO_FMT \
@@ -34,12 +42,12 @@ static int build_cpu_topology(struct cpu_topology *tp, int cpu)
sysfs__mountpoint(), cpu);
fp = fopen(filename, "r");
if (!fp)
- goto try_threads;
+ goto try_dies;
sret = getline(&buf, &len, fp);
fclose(fp);
if (sret <= 0)
- goto try_threads;
+ goto try_dies;
p = strchr(buf, '\n');
if (p)
@@ -57,9 +65,44 @@ static int build_cpu_topology(struct cpu_topology *tp, int cpu)
}
ret = 0;
+try_dies:
+ if (!tp->die_siblings)
+ goto try_threads;
+
+ scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT,
+ sysfs__mountpoint(), cpu);
+ fp = fopen(filename, "r");
+ if (!fp)
+ goto try_threads;
+
+ sret = getline(&buf, &len, fp);
+ fclose(fp);
+ if (sret <= 0)
+ goto try_threads;
+
+ p = strchr(buf, '\n');
+ if (p)
+ *p = '\0';
+
+ for (i = 0; i < tp->die_sib; i++) {
+ if (!strcmp(buf, tp->die_siblings[i]))
+ break;
+ }
+ if (i == tp->die_sib) {
+ tp->die_siblings[i] = buf;
+ tp->die_sib++;
+ buf = NULL;
+ len = 0;
+ }
+ ret = 0;
+
try_threads:
- scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT,
+ scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT_NEW,
sysfs__mountpoint(), cpu);
+ if (access(filename, F_OK) == -1) {
+ scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT,
+ sysfs__mountpoint(), cpu);
+ }
fp = fopen(filename, "r");
if (!fp)
goto done;
@@ -98,26 +141,51 @@ void cpu_topology__delete(struct cpu_topology *tp)
for (i = 0 ; i < tp->core_sib; i++)
zfree(&tp->core_siblings[i]);
+ if (tp->die_sib) {
+ for (i = 0 ; i < tp->die_sib; i++)
+ zfree(&tp->die_siblings[i]);
+ }
+
for (i = 0 ; i < tp->thread_sib; i++)
zfree(&tp->thread_siblings[i]);
free(tp);
}
+static bool has_die_topology(void)
+{
+ char filename[MAXPATHLEN];
+ struct utsname uts;
+
+ if (uname(&uts) < 0)
+ return false;
+
+ if (strncmp(uts.machine, "x86_64", 6))
+ return false;
+
+ scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT,
+ sysfs__mountpoint(), 0);
+ if (access(filename, F_OK) == -1)
+ return false;
+
+ return true;
+}
+
struct cpu_topology *cpu_topology__new(void)
{
struct cpu_topology *tp = NULL;
void *addr;
- u32 nr, i;
+ u32 nr, i, nr_addr;
size_t sz;
long ncpus;
int ret = -1;
- struct cpu_map *map;
+ struct perf_cpu_map *map;
+ bool has_die = has_die_topology();
ncpus = cpu__max_present_cpu();
/* build online CPU map */
- map = cpu_map__new(NULL);
+ map = perf_cpu_map__new(NULL);
if (map == NULL) {
pr_debug("failed to get system cpumap\n");
return NULL;
@@ -126,7 +194,11 @@ struct cpu_topology *cpu_topology__new(void)
nr = (u32)(ncpus & UINT_MAX);
sz = nr * sizeof(char *);
- addr = calloc(1, sizeof(*tp) + 2 * sz);
+ if (has_die)
+ nr_addr = 3;
+ else
+ nr_addr = 2;
+ addr = calloc(1, sizeof(*tp) + nr_addr * sz);
if (!addr)
goto out_free;
@@ -134,6 +206,10 @@ struct cpu_topology *cpu_topology__new(void)
addr += sizeof(*tp);
tp->core_siblings = addr;
addr += sz;
+ if (has_die) {
+ tp->die_siblings = addr;
+ addr += sz;
+ }
tp->thread_siblings = addr;
for (i = 0; i < nr; i++) {
@@ -146,7 +222,7 @@ struct cpu_topology *cpu_topology__new(void)
}
out_free:
- cpu_map__put(map);
+ perf_cpu_map__put(map);
if (ret) {
cpu_topology__delete(tp);
tp = NULL;
@@ -216,7 +292,7 @@ err:
struct numa_topology *numa_topology__new(void)
{
- struct cpu_map *node_map = NULL;
+ struct perf_cpu_map *node_map = NULL;
struct numa_topology *tp = NULL;
char path[MAXPATHLEN];
char *buf = NULL;
@@ -239,7 +315,7 @@ struct numa_topology *numa_topology__new(void)
if (c)
*c = '\0';
- node_map = cpu_map__new(buf);
+ node_map = perf_cpu_map__new(buf);
if (!node_map)
goto out;
@@ -262,7 +338,7 @@ struct numa_topology *numa_topology__new(void)
out:
free(buf);
fclose(fp);
- cpu_map__put(node_map);
+ perf_cpu_map__put(node_map);
return tp;
}
@@ -271,7 +347,7 @@ void numa_topology__delete(struct numa_topology *tp)
u32 i;
for (i = 0; i < tp->nr; i++)
- free(tp->nodes[i].cpus);
+ zfree(&tp->nodes[i].cpus);
free(tp);
}
diff --git a/tools/perf/util/cputopo.h b/tools/perf/util/cputopo.h
index 47a97e71acdf..7bf6b811f715 100644
--- a/tools/perf/util/cputopo.h
+++ b/tools/perf/util/cputopo.h
@@ -3,12 +3,13 @@
#define __PERF_CPUTOPO_H
#include <linux/types.h>
-#include "env.h"
struct cpu_topology {
u32 core_sib;
+ u32 die_sib;
u32 thread_sib;
char **core_siblings;
+ char **die_siblings;
char **thread_siblings;
};
diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
index 39fe21e1cf93..37d7c492b155 100644
--- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
+++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
@@ -8,6 +8,7 @@
#include <linux/err.h>
#include <linux/list.h>
+#include <linux/zalloc.h>
#include <stdlib.h>
#include <opencsd/c_api/opencsd_c_api.h>
#include <opencsd/etmv4/trc_pkt_types_etmv4.h>
@@ -18,8 +19,6 @@
#include "intlist.h"
#include "util.h"
-#define MAX_BUFFER 1024
-
/* use raw logging */
#ifdef CS_DEBUG_RAW
#define CS_LOG_RAW_FRAMES
@@ -31,33 +30,26 @@
#endif
#endif
-#define CS_ETM_INVAL_ADDR 0xdeadbeefdeadbeefUL
-
struct cs_etm_decoder {
void *data;
void (*packet_printer)(const char *msg);
dcd_tree_handle_t dcd_tree;
cs_etm_mem_cb_type mem_access;
ocsd_datapath_resp_t prev_return;
- u32 packet_count;
- u32 head;
- u32 tail;
- struct cs_etm_packet packet_buffer[MAX_BUFFER];
};
static u32
cs_etm_decoder__mem_access(const void *context,
const ocsd_vaddr_t address,
const ocsd_mem_space_acc_t mem_space __maybe_unused,
+ const u8 trace_chan_id,
const u32 req_size,
u8 *buffer)
{
struct cs_etm_decoder *decoder = (struct cs_etm_decoder *) context;
- return decoder->mem_access(decoder->data,
- address,
- req_size,
- buffer);
+ return decoder->mem_access(decoder->data, trace_chan_id,
+ address, req_size, buffer);
}
int cs_etm_decoder__add_mem_access_cb(struct cs_etm_decoder *decoder,
@@ -66,9 +58,10 @@ int cs_etm_decoder__add_mem_access_cb(struct cs_etm_decoder *decoder,
{
decoder->mem_access = cb_func;
- if (ocsd_dt_add_callback_mem_acc(decoder->dcd_tree, start, end,
- OCSD_MEM_SPACE_ANY,
- cs_etm_decoder__mem_access, decoder))
+ if (ocsd_dt_add_callback_trcid_mem_acc(decoder->dcd_tree, start, end,
+ OCSD_MEM_SPACE_ANY,
+ cs_etm_decoder__mem_access,
+ decoder))
return -1;
return 0;
@@ -88,14 +81,14 @@ int cs_etm_decoder__reset(struct cs_etm_decoder *decoder)
return 0;
}
-int cs_etm_decoder__get_packet(struct cs_etm_decoder *decoder,
+int cs_etm_decoder__get_packet(struct cs_etm_packet_queue *packet_queue,
struct cs_etm_packet *packet)
{
- if (!decoder || !packet)
+ if (!packet_queue || !packet)
return -EINVAL;
/* Nothing to do, might as well just return */
- if (decoder->packet_count == 0)
+ if (packet_queue->packet_count == 0)
return 0;
/*
* The queueing process in function cs_etm_decoder__buffer_packet()
@@ -106,11 +99,12 @@ int cs_etm_decoder__get_packet(struct cs_etm_decoder *decoder,
* value. Otherwise the first element of the packet queue is not
* used.
*/
- decoder->head = (decoder->head + 1) & (MAX_BUFFER - 1);
+ packet_queue->head = (packet_queue->head + 1) &
+ (CS_ETM_PACKET_MAX_BUFFER - 1);
- *packet = decoder->packet_buffer[decoder->head];
+ *packet = packet_queue->packet_buffer[packet_queue->head];
- decoder->packet_count--;
+ packet_queue->packet_count--;
return 1;
}
@@ -276,84 +270,130 @@ cs_etm_decoder__create_etm_packet_printer(struct cs_etm_trace_params *t_params,
trace_config);
}
-static void cs_etm_decoder__clear_buffer(struct cs_etm_decoder *decoder)
+static ocsd_datapath_resp_t
+cs_etm_decoder__do_soft_timestamp(struct cs_etm_queue *etmq,
+ struct cs_etm_packet_queue *packet_queue,
+ const uint8_t trace_chan_id)
{
- int i;
-
- decoder->head = 0;
- decoder->tail = 0;
- decoder->packet_count = 0;
- for (i = 0; i < MAX_BUFFER; i++) {
- decoder->packet_buffer[i].isa = CS_ETM_ISA_UNKNOWN;
- decoder->packet_buffer[i].start_addr = CS_ETM_INVAL_ADDR;
- decoder->packet_buffer[i].end_addr = CS_ETM_INVAL_ADDR;
- decoder->packet_buffer[i].instr_count = 0;
- decoder->packet_buffer[i].last_instr_taken_branch = false;
- decoder->packet_buffer[i].last_instr_size = 0;
- decoder->packet_buffer[i].last_instr_type = 0;
- decoder->packet_buffer[i].last_instr_subtype = 0;
- decoder->packet_buffer[i].last_instr_cond = 0;
- decoder->packet_buffer[i].flags = 0;
- decoder->packet_buffer[i].exception_number = UINT32_MAX;
- decoder->packet_buffer[i].trace_chan_id = UINT8_MAX;
- decoder->packet_buffer[i].cpu = INT_MIN;
+ /* No timestamp packet has been received, nothing to do */
+ if (!packet_queue->timestamp)
+ return OCSD_RESP_CONT;
+
+ packet_queue->timestamp = packet_queue->next_timestamp;
+
+ /* Estimate the timestamp for the next range packet */
+ packet_queue->next_timestamp += packet_queue->instr_count;
+ packet_queue->instr_count = 0;
+
+ /* Tell the front end which traceid_queue needs attention */
+ cs_etm__etmq_set_traceid_queue_timestamp(etmq, trace_chan_id);
+
+ return OCSD_RESP_WAIT;
+}
+
+static ocsd_datapath_resp_t
+cs_etm_decoder__do_hard_timestamp(struct cs_etm_queue *etmq,
+ const ocsd_generic_trace_elem *elem,
+ const uint8_t trace_chan_id)
+{
+ struct cs_etm_packet_queue *packet_queue;
+
+ /* First get the packet queue for this traceID */
+ packet_queue = cs_etm__etmq_get_packet_queue(etmq, trace_chan_id);
+ if (!packet_queue)
+ return OCSD_RESP_FATAL_SYS_ERR;
+
+ /*
+ * We've seen a timestamp packet before - simply record the new value.
+ * Function do_soft_timestamp() will report the value to the front end,
+ * hence asking the decoder to keep decoding rather than stopping.
+ */
+ if (packet_queue->timestamp) {
+ packet_queue->next_timestamp = elem->timestamp;
+ return OCSD_RESP_CONT;
}
+
+ /*
+ * This is the first timestamp we've seen since the beginning of traces
+ * or a discontinuity. Since timestamps packets are generated *after*
+ * range packets have been generated, we need to estimate the time at
+ * which instructions started by substracting the number of instructions
+ * executed to the timestamp.
+ */
+ packet_queue->timestamp = elem->timestamp - packet_queue->instr_count;
+ packet_queue->next_timestamp = elem->timestamp;
+ packet_queue->instr_count = 0;
+
+ /* Tell the front end which traceid_queue needs attention */
+ cs_etm__etmq_set_traceid_queue_timestamp(etmq, trace_chan_id);
+
+ /* Halt processing until we are being told to proceed */
+ return OCSD_RESP_WAIT;
+}
+
+static void
+cs_etm_decoder__reset_timestamp(struct cs_etm_packet_queue *packet_queue)
+{
+ packet_queue->timestamp = 0;
+ packet_queue->next_timestamp = 0;
+ packet_queue->instr_count = 0;
}
static ocsd_datapath_resp_t
-cs_etm_decoder__buffer_packet(struct cs_etm_decoder *decoder,
+cs_etm_decoder__buffer_packet(struct cs_etm_packet_queue *packet_queue,
const u8 trace_chan_id,
enum cs_etm_sample_type sample_type)
{
u32 et = 0;
int cpu;
- if (decoder->packet_count >= MAX_BUFFER - 1)
+ if (packet_queue->packet_count >= CS_ETM_PACKET_MAX_BUFFER - 1)
return OCSD_RESP_FATAL_SYS_ERR;
if (cs_etm__get_cpu(trace_chan_id, &cpu) < 0)
return OCSD_RESP_FATAL_SYS_ERR;
- et = decoder->tail;
- et = (et + 1) & (MAX_BUFFER - 1);
- decoder->tail = et;
- decoder->packet_count++;
-
- decoder->packet_buffer[et].sample_type = sample_type;
- decoder->packet_buffer[et].isa = CS_ETM_ISA_UNKNOWN;
- decoder->packet_buffer[et].cpu = cpu;
- decoder->packet_buffer[et].start_addr = CS_ETM_INVAL_ADDR;
- decoder->packet_buffer[et].end_addr = CS_ETM_INVAL_ADDR;
- decoder->packet_buffer[et].instr_count = 0;
- decoder->packet_buffer[et].last_instr_taken_branch = false;
- decoder->packet_buffer[et].last_instr_size = 0;
- decoder->packet_buffer[et].last_instr_type = 0;
- decoder->packet_buffer[et].last_instr_subtype = 0;
- decoder->packet_buffer[et].last_instr_cond = 0;
- decoder->packet_buffer[et].flags = 0;
- decoder->packet_buffer[et].exception_number = UINT32_MAX;
- decoder->packet_buffer[et].trace_chan_id = trace_chan_id;
-
- if (decoder->packet_count == MAX_BUFFER - 1)
+ et = packet_queue->tail;
+ et = (et + 1) & (CS_ETM_PACKET_MAX_BUFFER - 1);
+ packet_queue->tail = et;
+ packet_queue->packet_count++;
+
+ packet_queue->packet_buffer[et].sample_type = sample_type;
+ packet_queue->packet_buffer[et].isa = CS_ETM_ISA_UNKNOWN;
+ packet_queue->packet_buffer[et].cpu = cpu;
+ packet_queue->packet_buffer[et].start_addr = CS_ETM_INVAL_ADDR;
+ packet_queue->packet_buffer[et].end_addr = CS_ETM_INVAL_ADDR;
+ packet_queue->packet_buffer[et].instr_count = 0;
+ packet_queue->packet_buffer[et].last_instr_taken_branch = false;
+ packet_queue->packet_buffer[et].last_instr_size = 0;
+ packet_queue->packet_buffer[et].last_instr_type = 0;
+ packet_queue->packet_buffer[et].last_instr_subtype = 0;
+ packet_queue->packet_buffer[et].last_instr_cond = 0;
+ packet_queue->packet_buffer[et].flags = 0;
+ packet_queue->packet_buffer[et].exception_number = UINT32_MAX;
+ packet_queue->packet_buffer[et].trace_chan_id = trace_chan_id;
+
+ if (packet_queue->packet_count == CS_ETM_PACKET_MAX_BUFFER - 1)
return OCSD_RESP_WAIT;
return OCSD_RESP_CONT;
}
static ocsd_datapath_resp_t
-cs_etm_decoder__buffer_range(struct cs_etm_decoder *decoder,
+cs_etm_decoder__buffer_range(struct cs_etm_queue *etmq,
+ struct cs_etm_packet_queue *packet_queue,
const ocsd_generic_trace_elem *elem,
const uint8_t trace_chan_id)
{
int ret = 0;
struct cs_etm_packet *packet;
- ret = cs_etm_decoder__buffer_packet(decoder, trace_chan_id,
+ ret = cs_etm_decoder__buffer_packet(packet_queue, trace_chan_id,
CS_ETM_RANGE);
if (ret != OCSD_RESP_CONT && ret != OCSD_RESP_WAIT)
return ret;
- packet = &decoder->packet_buffer[decoder->tail];
+ packet = &packet_queue->packet_buffer[packet_queue->tail];
switch (elem->isa) {
case ocsd_isa_aarch64:
@@ -396,43 +436,90 @@ cs_etm_decoder__buffer_range(struct cs_etm_decoder *decoder,
packet->last_instr_size = elem->last_instr_sz;
+ /* per-thread scenario, no need to generate a timestamp */
+ if (cs_etm__etmq_is_timeless(etmq))
+ goto out;
+
+ /*
+ * The packet queue is full and we haven't seen a timestamp (had we
+ * seen one the packet queue wouldn't be full). Let the front end
+ * deal with it.
+ */
+ if (ret == OCSD_RESP_WAIT)
+ goto out;
+
+ packet_queue->instr_count += elem->num_instr_range;
+ /* Tell the front end we have a new timestamp to process */
+ ret = cs_etm_decoder__do_soft_timestamp(etmq, packet_queue,
+ trace_chan_id);
+out:
return ret;
}
static ocsd_datapath_resp_t
-cs_etm_decoder__buffer_discontinuity(struct cs_etm_decoder *decoder,
- const uint8_t trace_chan_id)
+cs_etm_decoder__buffer_discontinuity(struct cs_etm_packet_queue *queue,
+ const uint8_t trace_chan_id)
{
- return cs_etm_decoder__buffer_packet(decoder, trace_chan_id,
+ /*
+ * Something happened and who knows when we'll get new traces so
+ * reset time statistics.
+ */
+ cs_etm_decoder__reset_timestamp(queue);
+ return cs_etm_decoder__buffer_packet(queue, trace_chan_id,
CS_ETM_DISCONTINUITY);
}
static ocsd_datapath_resp_t
-cs_etm_decoder__buffer_exception(struct cs_etm_decoder *decoder,
+cs_etm_decoder__buffer_exception(struct cs_etm_packet_queue *queue,
const ocsd_generic_trace_elem *elem,
const uint8_t trace_chan_id)
{ int ret = 0;
struct cs_etm_packet *packet;
- ret = cs_etm_decoder__buffer_packet(decoder, trace_chan_id,
+ ret = cs_etm_decoder__buffer_packet(queue, trace_chan_id,
CS_ETM_EXCEPTION);
if (ret != OCSD_RESP_CONT && ret != OCSD_RESP_WAIT)
return ret;
- packet = &decoder->packet_buffer[decoder->tail];
+ packet = &queue->packet_buffer[queue->tail];
packet->exception_number = elem->exception_number;
return ret;
}
static ocsd_datapath_resp_t
-cs_etm_decoder__buffer_exception_ret(struct cs_etm_decoder *decoder,
+cs_etm_decoder__buffer_exception_ret(struct cs_etm_packet_queue *queue,
const uint8_t trace_chan_id)
{
- return cs_etm_decoder__buffer_packet(decoder, trace_chan_id,
+ return cs_etm_decoder__buffer_packet(queue, trace_chan_id,
CS_ETM_EXCEPTION_RET);
}
+static ocsd_datapath_resp_t
+cs_etm_decoder__set_tid(struct cs_etm_queue *etmq,
+ struct cs_etm_packet_queue *packet_queue,
+ const ocsd_generic_trace_elem *elem,
+ const uint8_t trace_chan_id)
+{
+ pid_t tid;
+
+ /* Ignore PE_CONTEXT packets that don't have a valid contextID */
+ if (!elem->context.ctxt_id_valid)
+ return OCSD_RESP_CONT;
+
+ tid = elem->context.context_id;
+ if (cs_etm__etmq_set_tid(etmq, tid, trace_chan_id))
+ return OCSD_RESP_FATAL_SYS_ERR;
+
+ /*
+ * A timestamp is generated after a PE_CONTEXT element so make sure
+ * to rely on that coming one.
+ */
+ cs_etm_decoder__reset_timestamp(packet_queue);
+
+ return OCSD_RESP_CONT;
+}
+
static ocsd_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer(
const void *context,
const ocsd_trc_index_t indx __maybe_unused,
@@ -441,6 +528,13 @@ static ocsd_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer(
{
ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
struct cs_etm_decoder *decoder = (struct cs_etm_decoder *) context;
+ struct cs_etm_queue *etmq = decoder->data;
+ struct cs_etm_packet_queue *packet_queue;
+
+ /* First get the packet queue for this traceID */
+ packet_queue = cs_etm__etmq_get_packet_queue(etmq, trace_chan_id);
+ if (!packet_queue)
+ return OCSD_RESP_FATAL_SYS_ERR;
switch (elem->elem_type) {
case OCSD_GEN_TRC_ELEM_UNKNOWN:
@@ -448,24 +542,30 @@ static ocsd_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer(
case OCSD_GEN_TRC_ELEM_EO_TRACE:
case OCSD_GEN_TRC_ELEM_NO_SYNC:
case OCSD_GEN_TRC_ELEM_TRACE_ON:
- resp = cs_etm_decoder__buffer_discontinuity(decoder,
+ resp = cs_etm_decoder__buffer_discontinuity(packet_queue,
trace_chan_id);
break;
case OCSD_GEN_TRC_ELEM_INSTR_RANGE:
- resp = cs_etm_decoder__buffer_range(decoder, elem,
+ resp = cs_etm_decoder__buffer_range(etmq, packet_queue, elem,
trace_chan_id);
break;
case OCSD_GEN_TRC_ELEM_EXCEPTION:
- resp = cs_etm_decoder__buffer_exception(decoder, elem,
+ resp = cs_etm_decoder__buffer_exception(packet_queue, elem,
trace_chan_id);
break;
case OCSD_GEN_TRC_ELEM_EXCEPTION_RET:
- resp = cs_etm_decoder__buffer_exception_ret(decoder,
+ resp = cs_etm_decoder__buffer_exception_ret(packet_queue,
trace_chan_id);
break;
+ case OCSD_GEN_TRC_ELEM_TIMESTAMP:
+ resp = cs_etm_decoder__do_hard_timestamp(etmq, elem,
+ trace_chan_id);
+ break;
case OCSD_GEN_TRC_ELEM_PE_CONTEXT:
+ resp = cs_etm_decoder__set_tid(etmq, packet_queue,
+ elem, trace_chan_id);
+ break;
case OCSD_GEN_TRC_ELEM_ADDR_NACC:
- case OCSD_GEN_TRC_ELEM_TIMESTAMP:
case OCSD_GEN_TRC_ELEM_CYCLE_COUNT:
case OCSD_GEN_TRC_ELEM_ADDR_UNKNOWN:
case OCSD_GEN_TRC_ELEM_EVENT:
@@ -554,7 +654,6 @@ cs_etm_decoder__new(int num_cpu, struct cs_etm_decoder_params *d_params,
decoder->data = d_params->data;
decoder->prev_return = OCSD_RESP_CONT;
- cs_etm_decoder__clear_buffer(decoder);
format = (d_params->formatted ? OCSD_TRC_SRC_FRAME_FORMATTED :
OCSD_TRC_SRC_SINGLE);
flags = 0;
@@ -577,7 +676,7 @@ cs_etm_decoder__new(int num_cpu, struct cs_etm_decoder_params *d_params,
/* init library print logging support */
ret = cs_etm_decoder__init_def_logger_printing(d_params, decoder);
if (ret != 0)
- goto err_free_decoder_tree;
+ goto err_free_decoder;
/* init raw frame logging if required */
cs_etm_decoder__init_raw_frame_logging(d_params, decoder);
@@ -587,15 +686,13 @@ cs_etm_decoder__new(int num_cpu, struct cs_etm_decoder_params *d_params,
&t_params[i],
decoder);
if (ret != 0)
- goto err_free_decoder_tree;
+ goto err_free_decoder;
}
return decoder;
-err_free_decoder_tree:
- ocsd_destroy_dcd_tree(decoder->dcd_tree);
err_free_decoder:
- free(decoder);
+ cs_etm_decoder__free(decoder);
return NULL;
}
diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h
index 3ab11dfa92ae..11f3391d06f2 100644
--- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h
+++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h
@@ -14,43 +14,12 @@
#include <stdio.h>
struct cs_etm_decoder;
-
-enum cs_etm_sample_type {
- CS_ETM_EMPTY,
- CS_ETM_RANGE,
- CS_ETM_DISCONTINUITY,
- CS_ETM_EXCEPTION,
- CS_ETM_EXCEPTION_RET,
-};
-
-enum cs_etm_isa {
- CS_ETM_ISA_UNKNOWN,
- CS_ETM_ISA_A64,
- CS_ETM_ISA_A32,
- CS_ETM_ISA_T32,
-};
-
-struct cs_etm_packet {
- enum cs_etm_sample_type sample_type;
- enum cs_etm_isa isa;
- u64 start_addr;
- u64 end_addr;
- u32 instr_count;
- u32 last_instr_type;
- u32 last_instr_subtype;
- u32 flags;
- u32 exception_number;
- u8 last_instr_cond;
- u8 last_instr_taken_branch;
- u8 last_instr_size;
- u8 trace_chan_id;
- int cpu;
-};
+struct cs_etm_packet;
+struct cs_etm_packet_queue;
struct cs_etm_queue;
-typedef u32 (*cs_etm_mem_cb_type)(struct cs_etm_queue *, u64,
- size_t, u8 *);
+typedef u32 (*cs_etm_mem_cb_type)(struct cs_etm_queue *, u8, u64, size_t, u8 *);
struct cs_etmv3_trace_params {
u32 reg_ctrl;
@@ -119,7 +88,7 @@ int cs_etm_decoder__add_mem_access_cb(struct cs_etm_decoder *decoder,
u64 start, u64 end,
cs_etm_mem_cb_type cb_func);
-int cs_etm_decoder__get_packet(struct cs_etm_decoder *decoder,
+int cs_etm_decoder__get_packet(struct cs_etm_packet_queue *packet_queue,
struct cs_etm_packet *packet);
int cs_etm_decoder__reset(struct cs_etm_decoder *decoder);
diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
index de488b43f440..707afdbd9529 100644
--- a/tools/perf/util/cs-etm.c
+++ b/tools/perf/util/cs-etm.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/log2.h>
#include <linux/types.h>
+#include <linux/zalloc.h>
#include <opencsd/ocsd_if_types.h>
#include <stdlib.h>
@@ -20,15 +21,20 @@
#include "cs-etm.h"
#include "cs-etm-decoder/cs-etm-decoder.h"
#include "debug.h"
+#include "dso.h"
#include "evlist.h"
#include "intlist.h"
#include "machine.h"
#include "map.h"
#include "perf.h"
+#include "session.h"
+#include "map_symbol.h"
+#include "branch.h"
#include "symbol.h"
+#include "tool.h"
#include "thread.h"
-#include "thread_map.h"
#include "thread-stack.h"
+#include <tools/libc_compat.h>
#include "util.h"
#define MAX_TIMESTAMP (~0ULL)
@@ -60,33 +66,55 @@ struct cs_etm_auxtrace {
unsigned int pmu_type;
};
-struct cs_etm_queue {
- struct cs_etm_auxtrace *etm;
- struct thread *thread;
- struct cs_etm_decoder *decoder;
- struct auxtrace_buffer *buffer;
- union perf_event *event_buf;
- unsigned int queue_nr;
+struct cs_etm_traceid_queue {
+ u8 trace_chan_id;
pid_t pid, tid;
- int cpu;
- u64 offset;
u64 period_instructions;
+ size_t last_branch_pos;
+ union perf_event *event_buf;
+ struct thread *thread;
struct branch_stack *last_branch;
struct branch_stack *last_branch_rb;
- size_t last_branch_pos;
struct cs_etm_packet *prev_packet;
struct cs_etm_packet *packet;
+ struct cs_etm_packet_queue packet_queue;
+};
+
+struct cs_etm_queue {
+ struct cs_etm_auxtrace *etm;
+ struct cs_etm_decoder *decoder;
+ struct auxtrace_buffer *buffer;
+ unsigned int queue_nr;
+ u8 pending_timestamp;
+ u64 offset;
const unsigned char *buf;
size_t buf_len, buf_used;
+ /* Conversion between traceID and index in traceid_queues array */
+ struct intlist *traceid_queues_list;
+ struct cs_etm_traceid_queue **traceid_queues;
};
static int cs_etm__update_queues(struct cs_etm_auxtrace *etm);
+static int cs_etm__process_queues(struct cs_etm_auxtrace *etm);
static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm,
pid_t tid);
+static int cs_etm__get_data_block(struct cs_etm_queue *etmq);
+static int cs_etm__decode_data_block(struct cs_etm_queue *etmq);
/* PTMs ETMIDR [11:8] set to b0011 */
#define ETMIDR_PTM_VERSION 0x00000300
+/*
+ * A struct auxtrace_heap_item only has a queue_nr and a timestamp to
+ * work with. One option is to modify to auxtrace_heap_XYZ() API or simply
+ * encode the etm queue number as the upper 16 bit and the channel as
+ * the lower 16 bit.
+ */
+#define TO_CS_QUEUE_NR(queue_nr, trace_id_chan) \
+ (queue_nr << 16 | trace_chan_id)
+#define TO_QUEUE_NR(cs_queue_nr) (cs_queue_nr >> 16)
+#define TO_TRACE_CHAN_ID(cs_queue_nr) (cs_queue_nr & 0x0000ffff)
+
static u32 cs_etm__get_v7_protocol_version(u32 etmidr)
{
etmidr &= ETMIDR_PTM_VERSION;
@@ -125,6 +153,216 @@ int cs_etm__get_cpu(u8 trace_chan_id, int *cpu)
return 0;
}
+void cs_etm__etmq_set_traceid_queue_timestamp(struct cs_etm_queue *etmq,
+ u8 trace_chan_id)
+{
+ /*
+ * Wnen a timestamp packet is encountered the backend code
+ * is stopped so that the front end has time to process packets
+ * that were accumulated in the traceID queue. Since there can
+ * be more than one channel per cs_etm_queue, we need to specify
+ * what traceID queue needs servicing.
+ */
+ etmq->pending_timestamp = trace_chan_id;
+}
+
+static u64 cs_etm__etmq_get_timestamp(struct cs_etm_queue *etmq,
+ u8 *trace_chan_id)
+{
+ struct cs_etm_packet_queue *packet_queue;
+
+ if (!etmq->pending_timestamp)
+ return 0;
+
+ if (trace_chan_id)
+ *trace_chan_id = etmq->pending_timestamp;
+
+ packet_queue = cs_etm__etmq_get_packet_queue(etmq,
+ etmq->pending_timestamp);
+ if (!packet_queue)
+ return 0;
+
+ /* Acknowledge pending status */
+ etmq->pending_timestamp = 0;
+
+ /* See function cs_etm_decoder__do_{hard|soft}_timestamp() */
+ return packet_queue->timestamp;
+}
+
+static void cs_etm__clear_packet_queue(struct cs_etm_packet_queue *queue)
+{
+ int i;
+
+ queue->head = 0;
+ queue->tail = 0;
+ queue->packet_count = 0;
+ for (i = 0; i < CS_ETM_PACKET_MAX_BUFFER; i++) {
+ queue->packet_buffer[i].isa = CS_ETM_ISA_UNKNOWN;
+ queue->packet_buffer[i].start_addr = CS_ETM_INVAL_ADDR;
+ queue->packet_buffer[i].end_addr = CS_ETM_INVAL_ADDR;
+ queue->packet_buffer[i].instr_count = 0;
+ queue->packet_buffer[i].last_instr_taken_branch = false;
+ queue->packet_buffer[i].last_instr_size = 0;
+ queue->packet_buffer[i].last_instr_type = 0;
+ queue->packet_buffer[i].last_instr_subtype = 0;
+ queue->packet_buffer[i].last_instr_cond = 0;
+ queue->packet_buffer[i].flags = 0;
+ queue->packet_buffer[i].exception_number = UINT32_MAX;
+ queue->packet_buffer[i].trace_chan_id = UINT8_MAX;
+ queue->packet_buffer[i].cpu = INT_MIN;
+ }
+}
+
+static void cs_etm__clear_all_packet_queues(struct cs_etm_queue *etmq)
+{
+ int idx;
+ struct int_node *inode;
+ struct cs_etm_traceid_queue *tidq;
+ struct intlist *traceid_queues_list = etmq->traceid_queues_list;
+
+ intlist__for_each_entry(inode, traceid_queues_list) {
+ idx = (int)(intptr_t)inode->priv;
+ tidq = etmq->traceid_queues[idx];
+ cs_etm__clear_packet_queue(&tidq->packet_queue);
+ }
+}
+
+static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq,
+ struct cs_etm_traceid_queue *tidq,
+ u8 trace_chan_id)
+{
+ int rc = -ENOMEM;
+ struct auxtrace_queue *queue;
+ struct cs_etm_auxtrace *etm = etmq->etm;
+
+ cs_etm__clear_packet_queue(&tidq->packet_queue);
+
+ queue = &etmq->etm->queues.queue_array[etmq->queue_nr];
+ tidq->tid = queue->tid;
+ tidq->pid = -1;
+ tidq->trace_chan_id = trace_chan_id;
+
+ tidq->packet = zalloc(sizeof(struct cs_etm_packet));
+ if (!tidq->packet)
+ goto out;
+
+ tidq->prev_packet = zalloc(sizeof(struct cs_etm_packet));
+ if (!tidq->prev_packet)
+ goto out_free;
+
+ if (etm->synth_opts.last_branch) {
+ size_t sz = sizeof(struct branch_stack);
+
+ sz += etm->synth_opts.last_branch_sz *
+ sizeof(struct branch_entry);
+ tidq->last_branch = zalloc(sz);
+ if (!tidq->last_branch)
+ goto out_free;
+ tidq->last_branch_rb = zalloc(sz);
+ if (!tidq->last_branch_rb)
+ goto out_free;
+ }
+
+ tidq->event_buf = malloc(PERF_SAMPLE_MAX_SIZE);
+ if (!tidq->event_buf)
+ goto out_free;
+
+ return 0;
+
+out_free:
+ zfree(&tidq->last_branch_rb);
+ zfree(&tidq->last_branch);
+ zfree(&tidq->prev_packet);
+ zfree(&tidq->packet);
+out:
+ return rc;
+}
+
+static struct cs_etm_traceid_queue
+*cs_etm__etmq_get_traceid_queue(struct cs_etm_queue *etmq, u8 trace_chan_id)
+{
+ int idx;
+ struct int_node *inode;
+ struct intlist *traceid_queues_list;
+ struct cs_etm_traceid_queue *tidq, **traceid_queues;
+ struct cs_etm_auxtrace *etm = etmq->etm;
+
+ if (etm->timeless_decoding)
+ trace_chan_id = CS_ETM_PER_THREAD_TRACEID;
+
+ traceid_queues_list = etmq->traceid_queues_list;
+
+ /*
+ * Check if the traceid_queue exist for this traceID by looking
+ * in the queue list.
+ */
+ inode = intlist__find(traceid_queues_list, trace_chan_id);
+ if (inode) {
+ idx = (int)(intptr_t)inode->priv;
+ return etmq->traceid_queues[idx];
+ }
+
+ /* We couldn't find a traceid_queue for this traceID, allocate one */
+ tidq = malloc(sizeof(*tidq));
+ if (!tidq)
+ return NULL;
+
+ memset(tidq, 0, sizeof(*tidq));
+
+ /* Get a valid index for the new traceid_queue */
+ idx = intlist__nr_entries(traceid_queues_list);
+ /* Memory for the inode is free'ed in cs_etm_free_traceid_queues () */
+ inode = intlist__findnew(traceid_queues_list, trace_chan_id);
+ if (!inode)
+ goto out_free;
+
+ /* Associate this traceID with this index */
+ inode->priv = (void *)(intptr_t)idx;
+
+ if (cs_etm__init_traceid_queue(etmq, tidq, trace_chan_id))
+ goto out_free;
+
+ /* Grow the traceid_queues array by one unit */
+ traceid_queues = etmq->traceid_queues;
+ traceid_queues = reallocarray(traceid_queues,
+ idx + 1,
+ sizeof(*traceid_queues));
+
+ /*
+ * On failure reallocarray() returns NULL and the original block of
+ * memory is left untouched.
+ */
+ if (!traceid_queues)
+ goto out_free;
+
+ traceid_queues[idx] = tidq;
+ etmq->traceid_queues = traceid_queues;
+
+ return etmq->traceid_queues[idx];
+
+out_free:
+ /*
+ * Function intlist__remove() removes the inode from the list
+ * and delete the memory associated to it.
+ */
+ intlist__remove(traceid_queues_list, inode);
+ free(tidq);
+
+ return NULL;
+}
+
+struct cs_etm_packet_queue
+*cs_etm__etmq_get_packet_queue(struct cs_etm_queue *etmq, u8 trace_chan_id)
+{
+ struct cs_etm_traceid_queue *tidq;
+
+ tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id);
+ if (tidq)
+ return &tidq->packet_queue;
+
+ return NULL;
+}
+
static void cs_etm__packet_dump(const char *pkt_string)
{
const char *color = PERF_COLOR_BLUE;
@@ -276,15 +514,52 @@ static int cs_etm__flush_events(struct perf_session *session,
if (!tool->ordered_events)
return -EINVAL;
- if (!etm->timeless_decoding)
- return -EINVAL;
-
ret = cs_etm__update_queues(etm);
if (ret < 0)
return ret;
- return cs_etm__process_timeless_queues(etm, -1);
+ if (etm->timeless_decoding)
+ return cs_etm__process_timeless_queues(etm, -1);
+
+ return cs_etm__process_queues(etm);
+}
+
+static void cs_etm__free_traceid_queues(struct cs_etm_queue *etmq)
+{
+ int idx;
+ uintptr_t priv;
+ struct int_node *inode, *tmp;
+ struct cs_etm_traceid_queue *tidq;
+ struct intlist *traceid_queues_list = etmq->traceid_queues_list;
+
+ intlist__for_each_entry_safe(inode, tmp, traceid_queues_list) {
+ priv = (uintptr_t)inode->priv;
+ idx = priv;
+
+ /* Free this traceid_queue from the array */
+ tidq = etmq->traceid_queues[idx];
+ thread__zput(tidq->thread);
+ zfree(&tidq->event_buf);
+ zfree(&tidq->last_branch);
+ zfree(&tidq->last_branch_rb);
+ zfree(&tidq->prev_packet);
+ zfree(&tidq->packet);
+ zfree(&tidq);
+
+ /*
+ * Function intlist__remove() removes the inode from the list
+ * and delete the memory associated to it.
+ */
+ intlist__remove(traceid_queues_list, inode);
+ }
+
+ /* Then the RB tree itself */
+ intlist__delete(traceid_queues_list);
+ etmq->traceid_queues_list = NULL;
+
+ /* finally free the traceid_queues array */
+ zfree(&etmq->traceid_queues);
}
static void cs_etm__free_queue(void *priv)
@@ -294,13 +569,8 @@ static void cs_etm__free_queue(void *priv)
if (!etmq)
return;
- thread__zput(etmq->thread);
cs_etm_decoder__free(etmq->decoder);
- zfree(&etmq->event_buf);
- zfree(&etmq->last_branch);
- zfree(&etmq->last_branch_rb);
- zfree(&etmq->prev_packet);
- zfree(&etmq->packet);
+ cs_etm__free_traceid_queues(etmq);
free(etmq);
}
@@ -365,23 +635,27 @@ static u8 cs_etm__cpu_mode(struct cs_etm_queue *etmq, u64 address)
}
}
-static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u64 address,
- size_t size, u8 *buffer)
+static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id,
+ u64 address, size_t size, u8 *buffer)
{
u8 cpumode;
u64 offset;
int len;
- struct thread *thread;
- struct machine *machine;
- struct addr_location al;
+ struct thread *thread;
+ struct machine *machine;
+ struct addr_location al;
+ struct cs_etm_traceid_queue *tidq;
if (!etmq)
return 0;
machine = etmq->etm->machine;
cpumode = cs_etm__cpu_mode(etmq, address);
+ tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id);
+ if (!tidq)
+ return 0;
- thread = etmq->thread;
+ thread = tidq->thread;
if (!thread) {
if (cpumode != PERF_RECORD_MISC_KERNEL)
return 0;
@@ -412,35 +686,13 @@ static struct cs_etm_queue *cs_etm__alloc_queue(struct cs_etm_auxtrace *etm)
struct cs_etm_decoder_params d_params;
struct cs_etm_trace_params *t_params = NULL;
struct cs_etm_queue *etmq;
- size_t szp = sizeof(struct cs_etm_packet);
etmq = zalloc(sizeof(*etmq));
if (!etmq)
return NULL;
- etmq->packet = zalloc(szp);
- if (!etmq->packet)
- goto out_free;
-
- etmq->prev_packet = zalloc(szp);
- if (!etmq->prev_packet)
- goto out_free;
-
- if (etm->synth_opts.last_branch) {
- size_t sz = sizeof(struct branch_stack);
-
- sz += etm->synth_opts.last_branch_sz *
- sizeof(struct branch_entry);
- etmq->last_branch = zalloc(sz);
- if (!etmq->last_branch)
- goto out_free;
- etmq->last_branch_rb = zalloc(sz);
- if (!etmq->last_branch_rb)
- goto out_free;
- }
-
- etmq->event_buf = malloc(PERF_SAMPLE_MAX_SIZE);
- if (!etmq->event_buf)
+ etmq->traceid_queues_list = intlist__new(NULL);
+ if (!etmq->traceid_queues_list)
goto out_free;
/* Use metadata to fill in trace parameters for trace decoder */
@@ -477,12 +729,7 @@ static struct cs_etm_queue *cs_etm__alloc_queue(struct cs_etm_auxtrace *etm)
out_free_decoder:
cs_etm_decoder__free(etmq->decoder);
out_free:
- zfree(&t_params);
- zfree(&etmq->event_buf);
- zfree(&etmq->last_branch);
- zfree(&etmq->last_branch_rb);
- zfree(&etmq->prev_packet);
- zfree(&etmq->packet);
+ intlist__delete(etmq->traceid_queues_list);
free(etmq);
return NULL;
@@ -493,6 +740,9 @@ static int cs_etm__setup_queue(struct cs_etm_auxtrace *etm,
unsigned int queue_nr)
{
int ret = 0;
+ unsigned int cs_queue_nr;
+ u8 trace_chan_id;
+ u64 timestamp;
struct cs_etm_queue *etmq = queue->priv;
if (list_empty(&queue->head) || etmq)
@@ -508,12 +758,69 @@ static int cs_etm__setup_queue(struct cs_etm_auxtrace *etm,
queue->priv = etmq;
etmq->etm = etm;
etmq->queue_nr = queue_nr;
- etmq->cpu = queue->cpu;
- etmq->tid = queue->tid;
- etmq->pid = -1;
etmq->offset = 0;
- etmq->period_instructions = 0;
+ if (etm->timeless_decoding)
+ goto out;
+
+ /*
+ * We are under a CPU-wide trace scenario. As such we need to know
+ * when the code that generated the traces started to execute so that
+ * it can be correlated with execution on other CPUs. So we get a
+ * handle on the beginning of traces and decode until we find a
+ * timestamp. The timestamp is then added to the auxtrace min heap
+ * in order to know what nibble (of all the etmqs) to decode first.
+ */
+ while (1) {
+ /*
+ * Fetch an aux_buffer from this etmq. Bail if no more
+ * blocks or an error has been encountered.
+ */
+ ret = cs_etm__get_data_block(etmq);
+ if (ret <= 0)
+ goto out;
+
+ /*
+ * Run decoder on the trace block. The decoder will stop when
+ * encountering a timestamp, a full packet queue or the end of
+ * trace for that block.
+ */
+ ret = cs_etm__decode_data_block(etmq);
+ if (ret)
+ goto out;
+
+ /*
+ * Function cs_etm_decoder__do_{hard|soft}_timestamp() does all
+ * the timestamp calculation for us.
+ */
+ timestamp = cs_etm__etmq_get_timestamp(etmq, &trace_chan_id);
+
+ /* We found a timestamp, no need to continue. */
+ if (timestamp)
+ break;
+
+ /*
+ * We didn't find a timestamp so empty all the traceid packet
+ * queues before looking for another timestamp packet, either
+ * in the current data block or a new one. Packets that were
+ * just decoded are useless since no timestamp has been
+ * associated with them. As such simply discard them.
+ */
+ cs_etm__clear_all_packet_queues(etmq);
+ }
+
+ /*
+ * We have a timestamp. Add it to the min heap to reflect when
+ * instructions conveyed by the range packets of this traceID queue
+ * started to execute. Once the same has been done for all the traceID
+ * queues of each etmq, redenring and decoding can start in
+ * chronological order.
+ *
+ * Note that packets decoded above are still in the traceID's packet
+ * queue and will be processed in cs_etm__process_queues().
+ */
+ cs_queue_nr = TO_CS_QUEUE_NR(queue_nr, trace_id_chan);
+ ret = auxtrace_heap__add(&etm->heap, cs_queue_nr, timestamp);
out:
return ret;
}
@@ -545,10 +852,12 @@ static int cs_etm__update_queues(struct cs_etm_auxtrace *etm)
return 0;
}
-static inline void cs_etm__copy_last_branch_rb(struct cs_etm_queue *etmq)
+static inline
+void cs_etm__copy_last_branch_rb(struct cs_etm_queue *etmq,
+ struct cs_etm_traceid_queue *tidq)
{
- struct branch_stack *bs_src = etmq->last_branch_rb;
- struct branch_stack *bs_dst = etmq->last_branch;
+ struct branch_stack *bs_src = tidq->last_branch_rb;
+ struct branch_stack *bs_dst = tidq->last_branch;
size_t nr = 0;
/*
@@ -568,9 +877,9 @@ static inline void cs_etm__copy_last_branch_rb(struct cs_etm_queue *etmq)
* two steps. First, copy the branches from the most recently inserted
* branch ->last_branch_pos until the end of bs_src->entries buffer.
*/
- nr = etmq->etm->synth_opts.last_branch_sz - etmq->last_branch_pos;
+ nr = etmq->etm->synth_opts.last_branch_sz - tidq->last_branch_pos;
memcpy(&bs_dst->entries[0],
- &bs_src->entries[etmq->last_branch_pos],
+ &bs_src->entries[tidq->last_branch_pos],
sizeof(struct branch_entry) * nr);
/*
@@ -583,21 +892,24 @@ static inline void cs_etm__copy_last_branch_rb(struct cs_etm_queue *etmq)
if (bs_src->nr >= etmq->etm->synth_opts.last_branch_sz) {
memcpy(&bs_dst->entries[nr],
&bs_src->entries[0],
- sizeof(struct branch_entry) * etmq->last_branch_pos);
+ sizeof(struct branch_entry) * tidq->last_branch_pos);
}
}
-static inline void cs_etm__reset_last_branch_rb(struct cs_etm_queue *etmq)
+static inline
+void cs_etm__reset_last_branch_rb(struct cs_etm_traceid_queue *tidq)
{
- etmq->last_branch_pos = 0;
- etmq->last_branch_rb->nr = 0;
+ tidq->last_branch_pos = 0;
+ tidq->last_branch_rb->nr = 0;
}
static inline int cs_etm__t32_instr_size(struct cs_etm_queue *etmq,
- u64 addr) {
+ u8 trace_chan_id, u64 addr)
+{
u8 instrBytes[2];
- cs_etm__mem_access(etmq, addr, ARRAY_SIZE(instrBytes), instrBytes);
+ cs_etm__mem_access(etmq, trace_chan_id, addr,
+ ARRAY_SIZE(instrBytes), instrBytes);
/*
* T32 instruction size is indicated by bits[15:11] of the first
* 16-bit word of the instruction: 0b11101, 0b11110 and 0b11111
@@ -626,6 +938,7 @@ u64 cs_etm__last_executed_instr(const struct cs_etm_packet *packet)
}
static inline u64 cs_etm__instr_addr(struct cs_etm_queue *etmq,
+ u64 trace_chan_id,
const struct cs_etm_packet *packet,
u64 offset)
{
@@ -633,7 +946,8 @@ static inline u64 cs_etm__instr_addr(struct cs_etm_queue *etmq,
u64 addr = packet->start_addr;
while (offset > 0) {
- addr += cs_etm__t32_instr_size(etmq, addr);
+ addr += cs_etm__t32_instr_size(etmq,
+ trace_chan_id, addr);
offset--;
}
return addr;
@@ -643,9 +957,10 @@ static inline u64 cs_etm__instr_addr(struct cs_etm_queue *etmq,
return packet->start_addr + offset * 4;
}
-static void cs_etm__update_last_branch_rb(struct cs_etm_queue *etmq)
+static void cs_etm__update_last_branch_rb(struct cs_etm_queue *etmq,
+ struct cs_etm_traceid_queue *tidq)
{
- struct branch_stack *bs = etmq->last_branch_rb;
+ struct branch_stack *bs = tidq->last_branch_rb;
struct branch_entry *be;
/*
@@ -654,14 +969,14 @@ static void cs_etm__update_last_branch_rb(struct cs_etm_queue *etmq)
* buffer down. After writing the first element of the stack, move the
* insert position back to the end of the buffer.
*/
- if (!etmq->last_branch_pos)
- etmq->last_branch_pos = etmq->etm->synth_opts.last_branch_sz;
+ if (!tidq->last_branch_pos)
+ tidq->last_branch_pos = etmq->etm->synth_opts.last_branch_sz;
- etmq->last_branch_pos -= 1;
+ tidq->last_branch_pos -= 1;
- be = &bs->entries[etmq->last_branch_pos];
- be->from = cs_etm__last_executed_instr(etmq->prev_packet);
- be->to = cs_etm__first_executed_instr(etmq->packet);
+ be = &bs->entries[tidq->last_branch_pos];
+ be->from = cs_etm__last_executed_instr(tidq->prev_packet);
+ be->to = cs_etm__first_executed_instr(tidq->packet);
/* No support for mispredict */
be->flags.mispred = 0;
be->flags.predicted = 1;
@@ -725,31 +1040,82 @@ cs_etm__get_trace(struct cs_etm_queue *etmq)
}
static void cs_etm__set_pid_tid_cpu(struct cs_etm_auxtrace *etm,
- struct auxtrace_queue *queue)
+ struct cs_etm_traceid_queue *tidq)
{
- struct cs_etm_queue *etmq = queue->priv;
+ if ((!tidq->thread) && (tidq->tid != -1))
+ tidq->thread = machine__find_thread(etm->machine, -1,
+ tidq->tid);
- /* CPU-wide tracing isn't supported yet */
- if (queue->tid == -1)
- return;
+ if (tidq->thread)
+ tidq->pid = tidq->thread->pid_;
+}
- if ((!etmq->thread) && (etmq->tid != -1))
- etmq->thread = machine__find_thread(etm->machine, -1,
- etmq->tid);
+int cs_etm__etmq_set_tid(struct cs_etm_queue *etmq,
+ pid_t tid, u8 trace_chan_id)
+{
+ int cpu, err = -EINVAL;
+ struct cs_etm_auxtrace *etm = etmq->etm;
+ struct cs_etm_traceid_queue *tidq;
- if (etmq->thread) {
- etmq->pid = etmq->thread->pid_;
- if (queue->cpu == -1)
- etmq->cpu = etmq->thread->cpu;
+ tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id);
+ if (!tidq)
+ return err;
+
+ if (cs_etm__get_cpu(trace_chan_id, &cpu) < 0)
+ return err;
+
+ err = machine__set_current_tid(etm->machine, cpu, tid, tid);
+ if (err)
+ return err;
+
+ tidq->tid = tid;
+ thread__zput(tidq->thread);
+
+ cs_etm__set_pid_tid_cpu(etm, tidq);
+ return 0;
+}
+
+bool cs_etm__etmq_is_timeless(struct cs_etm_queue *etmq)
+{
+ return !!etmq->etm->timeless_decoding;
+}
+
+static void cs_etm__copy_insn(struct cs_etm_queue *etmq,
+ u64 trace_chan_id,
+ const struct cs_etm_packet *packet,
+ struct perf_sample *sample)
+{
+ /*
+ * It's pointless to read instructions for the CS_ETM_DISCONTINUITY
+ * packet, so directly bail out with 'insn_len' = 0.
+ */
+ if (packet->sample_type == CS_ETM_DISCONTINUITY) {
+ sample->insn_len = 0;
+ return;
}
+
+ /*
+ * T32 instruction size might be 32-bit or 16-bit, decide by calling
+ * cs_etm__t32_instr_size().
+ */
+ if (packet->isa == CS_ETM_ISA_T32)
+ sample->insn_len = cs_etm__t32_instr_size(etmq, trace_chan_id,
+ sample->ip);
+ /* Otherwise, A64 and A32 instruction size are always 32-bit. */
+ else
+ sample->insn_len = 4;
+
+ cs_etm__mem_access(etmq, trace_chan_id, sample->ip,
+ sample->insn_len, (void *)sample->insn);
}
static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
+ struct cs_etm_traceid_queue *tidq,
u64 addr, u64 period)
{
int ret = 0;
struct cs_etm_auxtrace *etm = etmq->etm;
- union perf_event *event = etmq->event_buf;
+ union perf_event *event = tidq->event_buf;
struct perf_sample sample = {.ip = 0,};
event->sample.header.type = PERF_RECORD_SAMPLE;
@@ -757,19 +1123,20 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
event->sample.header.size = sizeof(struct perf_event_header);
sample.ip = addr;
- sample.pid = etmq->pid;
- sample.tid = etmq->tid;
+ sample.pid = tidq->pid;
+ sample.tid = tidq->tid;
sample.id = etmq->etm->instructions_id;
sample.stream_id = etmq->etm->instructions_id;
sample.period = period;
- sample.cpu = etmq->packet->cpu;
- sample.flags = etmq->prev_packet->flags;
- sample.insn_len = 1;
+ sample.cpu = tidq->packet->cpu;
+ sample.flags = tidq->prev_packet->flags;
sample.cpumode = event->sample.header.misc;
+ cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->packet, &sample);
+
if (etm->synth_opts.last_branch) {
- cs_etm__copy_last_branch_rb(etmq);
- sample.branch_stack = etmq->last_branch;
+ cs_etm__copy_last_branch_rb(etmq, tidq);
+ sample.branch_stack = tidq->last_branch;
}
if (etm->synth_opts.inject) {
@@ -787,7 +1154,7 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
ret);
if (etm->synth_opts.last_branch)
- cs_etm__reset_last_branch_rb(etmq);
+ cs_etm__reset_last_branch_rb(tidq);
return ret;
}
@@ -796,35 +1163,39 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
* The cs etm packet encodes an instruction range between a branch target
* and the next taken branch. Generate sample accordingly.
*/
-static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq)
+static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq,
+ struct cs_etm_traceid_queue *tidq)
{
int ret = 0;
struct cs_etm_auxtrace *etm = etmq->etm;
struct perf_sample sample = {.ip = 0,};
- union perf_event *event = etmq->event_buf;
+ union perf_event *event = tidq->event_buf;
struct dummy_branch_stack {
u64 nr;
struct branch_entry entries;
} dummy_bs;
u64 ip;
- ip = cs_etm__last_executed_instr(etmq->prev_packet);
+ ip = cs_etm__last_executed_instr(tidq->prev_packet);
event->sample.header.type = PERF_RECORD_SAMPLE;
event->sample.header.misc = cs_etm__cpu_mode(etmq, ip);
event->sample.header.size = sizeof(struct perf_event_header);
sample.ip = ip;
- sample.pid = etmq->pid;
- sample.tid = etmq->tid;
- sample.addr = cs_etm__first_executed_instr(etmq->packet);
+ sample.pid = tidq->pid;
+ sample.tid = tidq->tid;
+ sample.addr = cs_etm__first_executed_instr(tidq->packet);
sample.id = etmq->etm->branches_id;
sample.stream_id = etmq->etm->branches_id;
sample.period = 1;
- sample.cpu = etmq->packet->cpu;
- sample.flags = etmq->prev_packet->flags;
+ sample.cpu = tidq->packet->cpu;
+ sample.flags = tidq->prev_packet->flags;
sample.cpumode = event->sample.header.misc;
+ cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->prev_packet,
+ &sample);
+
/*
* perf report cannot handle events without a branch stack
*/
@@ -888,15 +1259,15 @@ static int cs_etm__synth_event(struct perf_session *session,
static int cs_etm__synth_events(struct cs_etm_auxtrace *etm,
struct perf_session *session)
{
- struct perf_evlist *evlist = session->evlist;
- struct perf_evsel *evsel;
+ struct evlist *evlist = session->evlist;
+ struct evsel *evsel;
struct perf_event_attr attr;
bool found = false;
u64 id;
int err;
evlist__for_each_entry(evlist, evsel) {
- if (evsel->attr.type == etm->pmu_type) {
+ if (evsel->core.attr.type == etm->pmu_type) {
found = true;
break;
}
@@ -910,7 +1281,7 @@ static int cs_etm__synth_events(struct cs_etm_auxtrace *etm,
memset(&attr, 0, sizeof(struct perf_event_attr));
attr.size = sizeof(struct perf_event_attr);
attr.type = PERF_TYPE_HARDWARE;
- attr.sample_type = evsel->attr.sample_type & PERF_SAMPLE_MASK;
+ attr.sample_type = evsel->core.attr.sample_type & PERF_SAMPLE_MASK;
attr.sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID |
PERF_SAMPLE_PERIOD;
if (etm->timeless_decoding)
@@ -918,13 +1289,13 @@ static int cs_etm__synth_events(struct cs_etm_auxtrace *etm,
else
attr.sample_type |= PERF_SAMPLE_TIME;
- attr.exclude_user = evsel->attr.exclude_user;
- attr.exclude_kernel = evsel->attr.exclude_kernel;
- attr.exclude_hv = evsel->attr.exclude_hv;
- attr.exclude_host = evsel->attr.exclude_host;
- attr.exclude_guest = evsel->attr.exclude_guest;
- attr.sample_id_all = evsel->attr.sample_id_all;
- attr.read_format = evsel->attr.read_format;
+ attr.exclude_user = evsel->core.attr.exclude_user;
+ attr.exclude_kernel = evsel->core.attr.exclude_kernel;
+ attr.exclude_hv = evsel->core.attr.exclude_hv;
+ attr.exclude_host = evsel->core.attr.exclude_host;
+ attr.exclude_guest = evsel->core.attr.exclude_guest;
+ attr.sample_id_all = evsel->core.attr.sample_id_all;
+ attr.read_format = evsel->core.attr.read_format;
/* create new id val to be a fixed offset from evsel id */
id = evsel->id[0] + 1000000000;
@@ -965,33 +1336,35 @@ static int cs_etm__synth_events(struct cs_etm_auxtrace *etm,
return 0;
}
-static int cs_etm__sample(struct cs_etm_queue *etmq)
+static int cs_etm__sample(struct cs_etm_queue *etmq,
+ struct cs_etm_traceid_queue *tidq)
{
struct cs_etm_auxtrace *etm = etmq->etm;
struct cs_etm_packet *tmp;
int ret;
- u64 instrs_executed = etmq->packet->instr_count;
+ u8 trace_chan_id = tidq->trace_chan_id;
+ u64 instrs_executed = tidq->packet->instr_count;
- etmq->period_instructions += instrs_executed;
+ tidq->period_instructions += instrs_executed;
/*
* Record a branch when the last instruction in
* PREV_PACKET is a branch.
*/
if (etm->synth_opts.last_branch &&
- etmq->prev_packet->sample_type == CS_ETM_RANGE &&
- etmq->prev_packet->last_instr_taken_branch)
- cs_etm__update_last_branch_rb(etmq);
+ tidq->prev_packet->sample_type == CS_ETM_RANGE &&
+ tidq->prev_packet->last_instr_taken_branch)
+ cs_etm__update_last_branch_rb(etmq, tidq);
if (etm->sample_instructions &&
- etmq->period_instructions >= etm->instructions_sample_period) {
+ tidq->period_instructions >= etm->instructions_sample_period) {
/*
* Emit instruction sample periodically
* TODO: allow period to be defined in cycles and clock time
*/
/* Get number of instructions executed after the sample point */
- u64 instrs_over = etmq->period_instructions -
+ u64 instrs_over = tidq->period_instructions -
etm->instructions_sample_period;
/*
@@ -1000,31 +1373,32 @@ static int cs_etm__sample(struct cs_etm_queue *etmq)
* executed, but PC has not advanced to next instruction)
*/
u64 offset = (instrs_executed - instrs_over - 1);
- u64 addr = cs_etm__instr_addr(etmq, etmq->packet, offset);
+ u64 addr = cs_etm__instr_addr(etmq, trace_chan_id,
+ tidq->packet, offset);
ret = cs_etm__synth_instruction_sample(
- etmq, addr, etm->instructions_sample_period);
+ etmq, tidq, addr, etm->instructions_sample_period);
if (ret)
return ret;
/* Carry remaining instructions into next sample period */
- etmq->period_instructions = instrs_over;
+ tidq->period_instructions = instrs_over;
}
if (etm->sample_branches) {
bool generate_sample = false;
/* Generate sample for tracing on packet */
- if (etmq->prev_packet->sample_type == CS_ETM_DISCONTINUITY)
+ if (tidq->prev_packet->sample_type == CS_ETM_DISCONTINUITY)
generate_sample = true;
/* Generate sample for branch taken packet */
- if (etmq->prev_packet->sample_type == CS_ETM_RANGE &&
- etmq->prev_packet->last_instr_taken_branch)
+ if (tidq->prev_packet->sample_type == CS_ETM_RANGE &&
+ tidq->prev_packet->last_instr_taken_branch)
generate_sample = true;
if (generate_sample) {
- ret = cs_etm__synth_branch_sample(etmq);
+ ret = cs_etm__synth_branch_sample(etmq, tidq);
if (ret)
return ret;
}
@@ -1035,15 +1409,15 @@ static int cs_etm__sample(struct cs_etm_queue *etmq)
* Swap PACKET with PREV_PACKET: PACKET becomes PREV_PACKET for
* the next incoming packet.
*/
- tmp = etmq->packet;
- etmq->packet = etmq->prev_packet;
- etmq->prev_packet = tmp;
+ tmp = tidq->packet;
+ tidq->packet = tidq->prev_packet;
+ tidq->prev_packet = tmp;
}
return 0;
}
-static int cs_etm__exception(struct cs_etm_queue *etmq)
+static int cs_etm__exception(struct cs_etm_traceid_queue *tidq)
{
/*
* When the exception packet is inserted, whether the last instruction
@@ -1056,24 +1430,25 @@ static int cs_etm__exception(struct cs_etm_queue *etmq)
* swap PACKET with PREV_PACKET. This keeps PREV_PACKET to be useful
* for generating instruction and branch samples.
*/
- if (etmq->prev_packet->sample_type == CS_ETM_RANGE)
- etmq->prev_packet->last_instr_taken_branch = true;
+ if (tidq->prev_packet->sample_type == CS_ETM_RANGE)
+ tidq->prev_packet->last_instr_taken_branch = true;
return 0;
}
-static int cs_etm__flush(struct cs_etm_queue *etmq)
+static int cs_etm__flush(struct cs_etm_queue *etmq,
+ struct cs_etm_traceid_queue *tidq)
{
int err = 0;
struct cs_etm_auxtrace *etm = etmq->etm;
struct cs_etm_packet *tmp;
/* Handle start tracing packet */
- if (etmq->prev_packet->sample_type == CS_ETM_EMPTY)
+ if (tidq->prev_packet->sample_type == CS_ETM_EMPTY)
goto swap_packet;
if (etmq->etm->synth_opts.last_branch &&
- etmq->prev_packet->sample_type == CS_ETM_RANGE) {
+ tidq->prev_packet->sample_type == CS_ETM_RANGE) {
/*
* Generate a last branch event for the branches left in the
* circular buffer at the end of the trace.
@@ -1081,21 +1456,21 @@ static int cs_etm__flush(struct cs_etm_queue *etmq)
* Use the address of the end of the last reported execution
* range
*/
- u64 addr = cs_etm__last_executed_instr(etmq->prev_packet);
+ u64 addr = cs_etm__last_executed_instr(tidq->prev_packet);
err = cs_etm__synth_instruction_sample(
- etmq, addr,
- etmq->period_instructions);
+ etmq, tidq, addr,
+ tidq->period_instructions);
if (err)
return err;
- etmq->period_instructions = 0;
+ tidq->period_instructions = 0;
}
if (etm->sample_branches &&
- etmq->prev_packet->sample_type == CS_ETM_RANGE) {
- err = cs_etm__synth_branch_sample(etmq);
+ tidq->prev_packet->sample_type == CS_ETM_RANGE) {
+ err = cs_etm__synth_branch_sample(etmq, tidq);
if (err)
return err;
}
@@ -1106,15 +1481,16 @@ swap_packet:
* Swap PACKET with PREV_PACKET: PACKET becomes PREV_PACKET for
* the next incoming packet.
*/
- tmp = etmq->packet;
- etmq->packet = etmq->prev_packet;
- etmq->prev_packet = tmp;
+ tmp = tidq->packet;
+ tidq->packet = tidq->prev_packet;
+ tidq->prev_packet = tmp;
}
return err;
}
-static int cs_etm__end_block(struct cs_etm_queue *etmq)
+static int cs_etm__end_block(struct cs_etm_queue *etmq,
+ struct cs_etm_traceid_queue *tidq)
{
int err;
@@ -1128,20 +1504,20 @@ static int cs_etm__end_block(struct cs_etm_queue *etmq)
* the trace.
*/
if (etmq->etm->synth_opts.last_branch &&
- etmq->prev_packet->sample_type == CS_ETM_RANGE) {
+ tidq->prev_packet->sample_type == CS_ETM_RANGE) {
/*
* Use the address of the end of the last reported execution
* range.
*/
- u64 addr = cs_etm__last_executed_instr(etmq->prev_packet);
+ u64 addr = cs_etm__last_executed_instr(tidq->prev_packet);
err = cs_etm__synth_instruction_sample(
- etmq, addr,
- etmq->period_instructions);
+ etmq, tidq, addr,
+ tidq->period_instructions);
if (err)
return err;
- etmq->period_instructions = 0;
+ tidq->period_instructions = 0;
}
return 0;
@@ -1173,12 +1549,13 @@ static int cs_etm__get_data_block(struct cs_etm_queue *etmq)
return etmq->buf_len;
}
-static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq,
+static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq, u8 trace_chan_id,
struct cs_etm_packet *packet,
u64 end_addr)
{
- u16 instr16;
- u32 instr32;
+ /* Initialise to keep compiler happy */
+ u16 instr16 = 0;
+ u32 instr32 = 0;
u64 addr;
switch (packet->isa) {
@@ -1196,7 +1573,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq,
* so below only read 2 bytes as instruction size for T32.
*/
addr = end_addr - 2;
- cs_etm__mem_access(etmq, addr, sizeof(instr16), (u8 *)&instr16);
+ cs_etm__mem_access(etmq, trace_chan_id, addr,
+ sizeof(instr16), (u8 *)&instr16);
if ((instr16 & 0xFF00) == 0xDF00)
return true;
@@ -1211,7 +1589,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq,
* +---------+---------+-------------------------+
*/
addr = end_addr - 4;
- cs_etm__mem_access(etmq, addr, sizeof(instr32), (u8 *)&instr32);
+ cs_etm__mem_access(etmq, trace_chan_id, addr,
+ sizeof(instr32), (u8 *)&instr32);
if ((instr32 & 0x0F000000) == 0x0F000000 &&
(instr32 & 0xF0000000) != 0xF0000000)
return true;
@@ -1227,7 +1606,8 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq,
* +-----------------------+---------+-----------+
*/
addr = end_addr - 4;
- cs_etm__mem_access(etmq, addr, sizeof(instr32), (u8 *)&instr32);
+ cs_etm__mem_access(etmq, trace_chan_id, addr,
+ sizeof(instr32), (u8 *)&instr32);
if ((instr32 & 0xFFE0001F) == 0xd4000001)
return true;
@@ -1240,10 +1620,12 @@ static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq,
return false;
}
-static bool cs_etm__is_syscall(struct cs_etm_queue *etmq, u64 magic)
+static bool cs_etm__is_syscall(struct cs_etm_queue *etmq,
+ struct cs_etm_traceid_queue *tidq, u64 magic)
{
- struct cs_etm_packet *packet = etmq->packet;
- struct cs_etm_packet *prev_packet = etmq->prev_packet;
+ u8 trace_chan_id = tidq->trace_chan_id;
+ struct cs_etm_packet *packet = tidq->packet;
+ struct cs_etm_packet *prev_packet = tidq->prev_packet;
if (magic == __perf_cs_etmv3_magic)
if (packet->exception_number == CS_ETMV3_EXC_SVC)
@@ -1256,7 +1638,7 @@ static bool cs_etm__is_syscall(struct cs_etm_queue *etmq, u64 magic)
*/
if (magic == __perf_cs_etmv4_magic) {
if (packet->exception_number == CS_ETMV4_EXC_CALL &&
- cs_etm__is_svc_instr(etmq, prev_packet,
+ cs_etm__is_svc_instr(etmq, trace_chan_id, prev_packet,
prev_packet->end_addr))
return true;
}
@@ -1264,9 +1646,10 @@ static bool cs_etm__is_syscall(struct cs_etm_queue *etmq, u64 magic)
return false;
}
-static bool cs_etm__is_async_exception(struct cs_etm_queue *etmq, u64 magic)
+static bool cs_etm__is_async_exception(struct cs_etm_traceid_queue *tidq,
+ u64 magic)
{
- struct cs_etm_packet *packet = etmq->packet;
+ struct cs_etm_packet *packet = tidq->packet;
if (magic == __perf_cs_etmv3_magic)
if (packet->exception_number == CS_ETMV3_EXC_DEBUG_HALT ||
@@ -1289,10 +1672,13 @@ static bool cs_etm__is_async_exception(struct cs_etm_queue *etmq, u64 magic)
return false;
}
-static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq, u64 magic)
+static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq,
+ struct cs_etm_traceid_queue *tidq,
+ u64 magic)
{
- struct cs_etm_packet *packet = etmq->packet;
- struct cs_etm_packet *prev_packet = etmq->prev_packet;
+ u8 trace_chan_id = tidq->trace_chan_id;
+ struct cs_etm_packet *packet = tidq->packet;
+ struct cs_etm_packet *prev_packet = tidq->prev_packet;
if (magic == __perf_cs_etmv3_magic)
if (packet->exception_number == CS_ETMV3_EXC_SMC ||
@@ -1316,7 +1702,7 @@ static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq, u64 magic)
* (SMC, HVC) are taken as sync exceptions.
*/
if (packet->exception_number == CS_ETMV4_EXC_CALL &&
- !cs_etm__is_svc_instr(etmq, prev_packet,
+ !cs_etm__is_svc_instr(etmq, trace_chan_id, prev_packet,
prev_packet->end_addr))
return true;
@@ -1335,10 +1721,12 @@ static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq, u64 magic)
return false;
}
-static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq)
+static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq,
+ struct cs_etm_traceid_queue *tidq)
{
- struct cs_etm_packet *packet = etmq->packet;
- struct cs_etm_packet *prev_packet = etmq->prev_packet;
+ struct cs_etm_packet *packet = tidq->packet;
+ struct cs_etm_packet *prev_packet = tidq->prev_packet;
+ u8 trace_chan_id = tidq->trace_chan_id;
u64 magic;
int ret;
@@ -1419,7 +1807,8 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq)
if (prev_packet->flags == (PERF_IP_FLAG_BRANCH |
PERF_IP_FLAG_RETURN |
PERF_IP_FLAG_INTERRUPT) &&
- cs_etm__is_svc_instr(etmq, packet, packet->start_addr))
+ cs_etm__is_svc_instr(etmq, trace_chan_id,
+ packet, packet->start_addr))
prev_packet->flags = PERF_IP_FLAG_BRANCH |
PERF_IP_FLAG_RETURN |
PERF_IP_FLAG_SYSCALLRET;
@@ -1440,7 +1829,7 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq)
return ret;
/* The exception is for system call. */
- if (cs_etm__is_syscall(etmq, magic))
+ if (cs_etm__is_syscall(etmq, tidq, magic))
packet->flags = PERF_IP_FLAG_BRANCH |
PERF_IP_FLAG_CALL |
PERF_IP_FLAG_SYSCALLRET;
@@ -1448,7 +1837,7 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq)
* The exceptions are triggered by external signals from bus,
* interrupt controller, debug module, PE reset or halt.
*/
- else if (cs_etm__is_async_exception(etmq, magic))
+ else if (cs_etm__is_async_exception(tidq, magic))
packet->flags = PERF_IP_FLAG_BRANCH |
PERF_IP_FLAG_CALL |
PERF_IP_FLAG_ASYNC |
@@ -1457,7 +1846,7 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq)
* Otherwise, exception is caused by trap, instruction &
* data fault, or alignment errors.
*/
- else if (cs_etm__is_sync_exception(etmq, magic))
+ else if (cs_etm__is_sync_exception(etmq, tidq, magic))
packet->flags = PERF_IP_FLAG_BRANCH |
PERF_IP_FLAG_CALL |
PERF_IP_FLAG_INTERRUPT;
@@ -1539,75 +1928,106 @@ out:
return ret;
}
-static int cs_etm__process_decoder_queue(struct cs_etm_queue *etmq)
+static int cs_etm__process_traceid_queue(struct cs_etm_queue *etmq,
+ struct cs_etm_traceid_queue *tidq)
{
int ret;
+ struct cs_etm_packet_queue *packet_queue;
- /* Process each packet in this chunk */
- while (1) {
- ret = cs_etm_decoder__get_packet(etmq->decoder,
- etmq->packet);
- if (ret <= 0)
- /*
- * Stop processing this chunk on
- * end of data or error
- */
- break;
+ packet_queue = &tidq->packet_queue;
+ /* Process each packet in this chunk */
+ while (1) {
+ ret = cs_etm_decoder__get_packet(packet_queue,
+ tidq->packet);
+ if (ret <= 0)
/*
- * Since packet addresses are swapped in packet
- * handling within below switch() statements,
- * thus setting sample flags must be called
- * prior to switch() statement to use address
- * information before packets swapping.
+ * Stop processing this chunk on
+ * end of data or error
*/
- ret = cs_etm__set_sample_flags(etmq);
- if (ret < 0)
- break;
-
- switch (etmq->packet->sample_type) {
- case CS_ETM_RANGE:
- /*
- * If the packet contains an instruction
- * range, generate instruction sequence
- * events.
- */
- cs_etm__sample(etmq);
- break;
- case CS_ETM_EXCEPTION:
- case CS_ETM_EXCEPTION_RET:
- /*
- * If the exception packet is coming,
- * make sure the previous instruction
- * range packet to be handled properly.
- */
- cs_etm__exception(etmq);
- break;
- case CS_ETM_DISCONTINUITY:
- /*
- * Discontinuity in trace, flush
- * previous branch stack
- */
- cs_etm__flush(etmq);
- break;
- case CS_ETM_EMPTY:
- /*
- * Should not receive empty packet,
- * report error.
- */
- pr_err("CS ETM Trace: empty packet\n");
- return -EINVAL;
- default:
- break;
- }
+ break;
+
+ /*
+ * Since packet addresses are swapped in packet
+ * handling within below switch() statements,
+ * thus setting sample flags must be called
+ * prior to switch() statement to use address
+ * information before packets swapping.
+ */
+ ret = cs_etm__set_sample_flags(etmq, tidq);
+ if (ret < 0)
+ break;
+
+ switch (tidq->packet->sample_type) {
+ case CS_ETM_RANGE:
+ /*
+ * If the packet contains an instruction
+ * range, generate instruction sequence
+ * events.
+ */
+ cs_etm__sample(etmq, tidq);
+ break;
+ case CS_ETM_EXCEPTION:
+ case CS_ETM_EXCEPTION_RET:
+ /*
+ * If the exception packet is coming,
+ * make sure the previous instruction
+ * range packet to be handled properly.
+ */
+ cs_etm__exception(tidq);
+ break;
+ case CS_ETM_DISCONTINUITY:
+ /*
+ * Discontinuity in trace, flush
+ * previous branch stack
+ */
+ cs_etm__flush(etmq, tidq);
+ break;
+ case CS_ETM_EMPTY:
+ /*
+ * Should not receive empty packet,
+ * report error.
+ */
+ pr_err("CS ETM Trace: empty packet\n");
+ return -EINVAL;
+ default:
+ break;
}
+ }
return ret;
}
+static void cs_etm__clear_all_traceid_queues(struct cs_etm_queue *etmq)
+{
+ int idx;
+ struct int_node *inode;
+ struct cs_etm_traceid_queue *tidq;
+ struct intlist *traceid_queues_list = etmq->traceid_queues_list;
+
+ intlist__for_each_entry(inode, traceid_queues_list) {
+ idx = (int)(intptr_t)inode->priv;
+ tidq = etmq->traceid_queues[idx];
+
+ /* Ignore return value */
+ cs_etm__process_traceid_queue(etmq, tidq);
+
+ /*
+ * Generate an instruction sample with the remaining
+ * branchstack entries.
+ */
+ cs_etm__flush(etmq, tidq);
+ }
+}
+
static int cs_etm__run_decoder(struct cs_etm_queue *etmq)
{
int err = 0;
+ struct cs_etm_traceid_queue *tidq;
+
+ tidq = cs_etm__etmq_get_traceid_queue(etmq, CS_ETM_PER_THREAD_TRACEID);
+ if (!tidq)
+ return -EINVAL;
/* Go through each buffer in the queue and decode them one by one */
while (1) {
@@ -1626,13 +2046,13 @@ static int cs_etm__run_decoder(struct cs_etm_queue *etmq)
* an error occurs other than hoping the next one will
* be better.
*/
- err = cs_etm__process_decoder_queue(etmq);
+ err = cs_etm__process_traceid_queue(etmq, tidq);
} while (etmq->buf_len);
if (err == 0)
/* Flush any remaining branch stack entries */
- err = cs_etm__end_block(etmq);
+ err = cs_etm__end_block(etmq, tidq);
}
return err;
@@ -1647,9 +2067,19 @@ static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm,
for (i = 0; i < queues->nr_queues; i++) {
struct auxtrace_queue *queue = &etm->queues.queue_array[i];
struct cs_etm_queue *etmq = queue->priv;
+ struct cs_etm_traceid_queue *tidq;
+
+ if (!etmq)
+ continue;
+
+ tidq = cs_etm__etmq_get_traceid_queue(etmq,
+ CS_ETM_PER_THREAD_TRACEID);
+
+ if (!tidq)
+ continue;
- if (etmq && ((tid == -1) || (etmq->tid == tid))) {
- cs_etm__set_pid_tid_cpu(etm, queue);
+ if ((tid == -1) || (tidq->tid == tid)) {
+ cs_etm__set_pid_tid_cpu(etm, tidq);
cs_etm__run_decoder(etmq);
}
}
@@ -1657,6 +2087,164 @@ static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm,
return 0;
}
+static int cs_etm__process_queues(struct cs_etm_auxtrace *etm)
+{
+ int ret = 0;
+ unsigned int cs_queue_nr, queue_nr;
+ u8 trace_chan_id;
+ u64 timestamp;
+ struct auxtrace_queue *queue;
+ struct cs_etm_queue *etmq;
+ struct cs_etm_traceid_queue *tidq;
+
+ while (1) {
+ if (!etm->heap.heap_cnt)
+ goto out;
+
+ /* Take the entry at the top of the min heap */
+ cs_queue_nr = etm->heap.heap_array[0].queue_nr;
+ queue_nr = TO_QUEUE_NR(cs_queue_nr);
+ trace_chan_id = TO_TRACE_CHAN_ID(cs_queue_nr);
+ queue = &etm->queues.queue_array[queue_nr];
+ etmq = queue->priv;
+
+ /*
+ * Remove the top entry from the heap since we are about
+ * to process it.
+ */
+ auxtrace_heap__pop(&etm->heap);
+
+ tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id);
+ if (!tidq) {
+ /*
+ * No traceID queue has been allocated for this traceID,
+ * which means something somewhere went very wrong. No
+ * other choice than simply exit.
+ */
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Packets associated with this timestamp are already in
+ * the etmq's traceID queue, so process them.
+ */
+ ret = cs_etm__process_traceid_queue(etmq, tidq);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Packets for this timestamp have been processed, time to
+ * move on to the next timestamp, fetching a new auxtrace_buffer
+ * if need be.
+ */
+refetch:
+ ret = cs_etm__get_data_block(etmq);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * No more auxtrace_buffers to process in this etmq, simply
+ * move on to another entry in the auxtrace_heap.
+ */
+ if (!ret)
+ continue;
+
+ ret = cs_etm__decode_data_block(etmq);
+ if (ret)
+ goto out;
+
+ timestamp = cs_etm__etmq_get_timestamp(etmq, &trace_chan_id);
+
+ if (!timestamp) {
+ /*
+ * Function cs_etm__decode_data_block() returns when
+ * there is no more traces to decode in the current
+ * auxtrace_buffer OR when a timestamp has been
+ * encountered on any of the traceID queues. Since we
+ * did not get a timestamp, there is no more traces to
+ * process in this auxtrace_buffer. As such empty and
+ * flush all traceID queues.
+ */
+ cs_etm__clear_all_traceid_queues(etmq);
+
+ /* Fetch another auxtrace_buffer for this etmq */
+ goto refetch;
+ }
+
+ /*
+ * Add to the min heap the timestamp for packets that have
+ * just been decoded. They will be processed and synthesized
+ * during the next call to cs_etm__process_traceid_queue() for
+ * this queue/traceID.
+ */
+ cs_queue_nr = TO_CS_QUEUE_NR(queue_nr, trace_chan_id);
+ ret = auxtrace_heap__add(&etm->heap, cs_queue_nr, timestamp);
+ }
+
+out:
+ return ret;
+}
+
+static int cs_etm__process_itrace_start(struct cs_etm_auxtrace *etm,
+ union perf_event *event)
+{
+ struct thread *th;
+
+ if (etm->timeless_decoding)
+ return 0;
+
+ /*
+ * Add the tid/pid to the log so that we can get a match when
+ * we get a contextID from the decoder.
+ */
+ th = machine__findnew_thread(etm->machine,
+ event->itrace_start.pid,
+ event->itrace_start.tid);
+ if (!th)
+ return -ENOMEM;
+
+ thread__put(th);
+
+ return 0;
+}
+
+static int cs_etm__process_switch_cpu_wide(struct cs_etm_auxtrace *etm,
+ union perf_event *event)
+{
+ struct thread *th;
+ bool out = event->header.misc & PERF_RECORD_MISC_SWITCH_OUT;
+
+ /*
+ * Context switch in per-thread mode are irrelevant since perf
+ * will start/stop tracing as the process is scheduled.
+ */
+ if (etm->timeless_decoding)
+ return 0;
+
+ /*
+ * SWITCH_IN events carry the next process to be switched out while
+ * SWITCH_OUT events carry the process to be switched in. As such
+ * we don't care about IN events.
+ */
+ if (!out)
+ return 0;
+
+ /*
+ * Add the tid/pid to the log so that we can get a match when
+ * we get a contextID from the decoder.
+ */
+ th = machine__findnew_thread(etm->machine,
+ event->context_switch.next_prev_pid,
+ event->context_switch.next_prev_tid);
+ if (!th)
+ return -ENOMEM;
+
+ thread__put(th);
+
+ return 0;
+}
+
static int cs_etm__process_event(struct perf_session *session,
union perf_event *event,
struct perf_sample *sample,
@@ -1676,9 +2264,6 @@ static int cs_etm__process_event(struct perf_session *session,
return -EINVAL;
}
- if (!etm->timeless_decoding)
- return -EINVAL;
-
if (sample->time && (sample->time != (u64) -1))
timestamp = sample->time;
else
@@ -1690,10 +2275,20 @@ static int cs_etm__process_event(struct perf_session *session,
return err;
}
- if (event->header.type == PERF_RECORD_EXIT)
+ if (etm->timeless_decoding &&
+ event->header.type == PERF_RECORD_EXIT)
return cs_etm__process_timeless_queues(etm,
event->fork.tid);
+ if (event->header.type == PERF_RECORD_ITRACE_START)
+ return cs_etm__process_itrace_start(etm, event);
+ else if (event->header.type == PERF_RECORD_SWITCH_CPU_WIDE)
+ return cs_etm__process_switch_cpu_wide(etm, event);
+
+ if (!etm->timeless_decoding &&
+ event->header.type == PERF_RECORD_AUX)
+ return cs_etm__process_queues(etm);
+
return 0;
}
@@ -1736,8 +2331,8 @@ static int cs_etm__process_auxtrace_event(struct perf_session *session,
static bool cs_etm__is_timeless_decoding(struct cs_etm_auxtrace *etm)
{
- struct perf_evsel *evsel;
- struct perf_evlist *evlist = etm->session->evlist;
+ struct evsel *evsel;
+ struct evlist *evlist = etm->session->evlist;
bool timeless_decoding = true;
/*
@@ -1745,7 +2340,7 @@ static bool cs_etm__is_timeless_decoding(struct cs_etm_auxtrace *etm)
* with the time bit set.
*/
evlist__for_each_entry(evlist, evsel) {
- if ((evsel->attr.sample_type & PERF_SAMPLE_TIME))
+ if ((evsel->core.attr.sample_type & PERF_SAMPLE_TIME))
timeless_decoding = false;
}
@@ -1779,7 +2374,7 @@ static const char * const cs_etmv4_priv_fmts[] = {
[CS_ETMV4_TRCAUTHSTATUS] = " TRCAUTHSTATUS %llx\n",
};
-static void cs_etm__print_auxtrace_info(u64 *val, int num)
+static void cs_etm__print_auxtrace_info(__u64 *val, int num)
{
int i, j, cpu = 0;
@@ -1802,7 +2397,7 @@ static void cs_etm__print_auxtrace_info(u64 *val, int num)
int cs_etm__process_auxtrace_info(union perf_event *event,
struct perf_session *session)
{
- struct auxtrace_info_event *auxtrace_info = &event->auxtrace_info;
+ struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
struct cs_etm_auxtrace *etm = NULL;
struct int_node *inode;
unsigned int pmu_type;
@@ -1902,7 +2497,7 @@ int cs_etm__process_auxtrace_info(union perf_event *event,
/* Something went wrong, no need to continue */
if (!inode) {
- err = PTR_ERR(inode);
+ err = -ENOMEM;
goto err_free_metadata;
}
@@ -1959,8 +2554,10 @@ int cs_etm__process_auxtrace_info(union perf_event *event,
session->auxtrace = &etm->auxtrace;
etm->unknown_thread = thread__new(999999999, 999999999);
- if (!etm->unknown_thread)
+ if (!etm->unknown_thread) {
+ err = -ENOMEM;
goto err_free_queues;
+ }
/*
* Initialize list node so that at thread__zput() we can avoid
@@ -1972,15 +2569,17 @@ int cs_etm__process_auxtrace_info(union perf_event *event,
if (err)
goto err_delete_thread;
- if (thread__init_map_groups(etm->unknown_thread, etm->machine))
+ if (thread__init_map_groups(etm->unknown_thread, etm->machine)) {
+ err = -ENOMEM;
goto err_delete_thread;
+ }
if (dump_trace) {
cs_etm__print_auxtrace_info(auxtrace_info->priv, num_cpu);
return 0;
}
- if (session->itrace_synth_opts && session->itrace_synth_opts->set) {
+ if (session->itrace_synth_opts->set) {
etm->synth_opts = *session->itrace_synth_opts;
} else {
itrace_synth_opts__set_default(&etm->synth_opts,
@@ -2010,12 +2609,12 @@ err_free_etm:
err_free_metadata:
/* No need to check @metadata[j], free(NULL) is supported */
for (j = 0; j < num_cpu; j++)
- free(metadata[j]);
+ zfree(&metadata[j]);
zfree(&metadata);
err_free_traceid_list:
intlist__delete(traceid_list);
err_free_hdr:
zfree(&hdr);
- return -EINVAL;
+ return err;
}
diff --git a/tools/perf/util/cs-etm.h b/tools/perf/util/cs-etm.h
index 0e97c196147a..650ecc2a6349 100644
--- a/tools/perf/util/cs-etm.h
+++ b/tools/perf/util/cs-etm.h
@@ -8,7 +8,9 @@
#define INCLUDE__UTIL_PERF_CS_ETM_H__
#include "util/event.h"
-#include "util/session.h"
+#include <linux/bits.h>
+
+struct perf_session;
/* Versionning header in case things need tro change in the future. That way
* decoding of old snapshot is still possible.
@@ -97,12 +99,72 @@ enum {
CS_ETMV4_EXC_END = 31,
};
+enum cs_etm_sample_type {
+ CS_ETM_EMPTY,
+ CS_ETM_RANGE,
+ CS_ETM_DISCONTINUITY,
+ CS_ETM_EXCEPTION,
+ CS_ETM_EXCEPTION_RET,
+};
+
+enum cs_etm_isa {
+ CS_ETM_ISA_UNKNOWN,
+ CS_ETM_ISA_A64,
+ CS_ETM_ISA_A32,
+ CS_ETM_ISA_T32,
+};
+
/* RB tree for quick conversion between traceID and metadata pointers */
struct intlist *traceid_list;
+struct cs_etm_queue;
+
+struct cs_etm_packet {
+ enum cs_etm_sample_type sample_type;
+ enum cs_etm_isa isa;
+ u64 start_addr;
+ u64 end_addr;
+ u32 instr_count;
+ u32 last_instr_type;
+ u32 last_instr_subtype;
+ u32 flags;
+ u32 exception_number;
+ u8 last_instr_cond;
+ u8 last_instr_taken_branch;
+ u8 last_instr_size;
+ u8 trace_chan_id;
+ int cpu;
+};
+
+#define CS_ETM_PACKET_MAX_BUFFER 1024
+
+/*
+ * When working with per-thread scenarios the process under trace can
+ * be scheduled on any CPU and as such, more than one traceID may be
+ * associated with the same process. Since a traceID of '0' is illegal
+ * as per the CoreSight architecture, use that specific value to
+ * identify the queue where all packets (with any traceID) are
+ * aggregated.
+ */
+#define CS_ETM_PER_THREAD_TRACEID 0
+
+struct cs_etm_packet_queue {
+ u32 packet_count;
+ u32 head;
+ u32 tail;
+ u32 instr_count;
+ u64 timestamp;
+ u64 next_timestamp;
+ struct cs_etm_packet packet_buffer[CS_ETM_PACKET_MAX_BUFFER];
+};
+
#define KiB(x) ((x) * 1024)
#define MiB(x) ((x) * 1024 * 1024)
+#define CS_ETM_INVAL_ADDR 0xdeadbeefdeadbeefUL
+
+#define BMVAL(val, lsb, msb) ((val & GENMASK(msb, lsb)) >> lsb)
+
#define CS_ETM_HEADER_SIZE (CS_HEADER_VERSION_0_MAX * sizeof(u64))
#define __perf_cs_etmv3_magic 0x3030303030303030ULL
@@ -114,6 +176,13 @@ struct intlist *traceid_list;
int cs_etm__process_auxtrace_info(union perf_event *event,
struct perf_session *session);
int cs_etm__get_cpu(u8 trace_chan_id, int *cpu);
+int cs_etm__etmq_set_tid(struct cs_etm_queue *etmq,
+ pid_t tid, u8 trace_chan_id);
+bool cs_etm__etmq_is_timeless(struct cs_etm_queue *etmq);
+void cs_etm__etmq_set_traceid_queue_timestamp(struct cs_etm_queue *etmq,
+ u8 trace_chan_id);
+struct cs_etm_packet_queue
+*cs_etm__etmq_get_packet_queue(struct cs_etm_queue *etmq, u8 trace_chan_id);
#else
static inline int
cs_etm__process_auxtrace_info(union perf_event *event __maybe_unused,
@@ -127,6 +196,32 @@ static inline int cs_etm__get_cpu(u8 trace_chan_id __maybe_unused,
{
return -1;
}
+
+static inline int cs_etm__etmq_set_tid(
+ struct cs_etm_queue *etmq __maybe_unused,
+ pid_t tid __maybe_unused,
+ u8 trace_chan_id __maybe_unused)
+{
+ return -1;
+}
+
+static inline bool cs_etm__etmq_is_timeless(
+ struct cs_etm_queue *etmq __maybe_unused)
+{
+ /* What else to return? */
+ return true;
+}
+
+static inline void cs_etm__etmq_set_traceid_queue_timestamp(
+ struct cs_etm_queue *etmq __maybe_unused,
+ u8 trace_chan_id __maybe_unused) {}
+
+static inline struct cs_etm_packet_queue *cs_etm__etmq_get_packet_queue(
+ struct cs_etm_queue *etmq __maybe_unused,
+ u8 trace_chan_id __maybe_unused)
+{
+ return NULL;
+}
#endif
#endif
diff --git a/tools/perf/util/ctype.c b/tools/perf/util/ctype.c
deleted file mode 100644
index ee4c1e8ed54b..000000000000
--- a/tools/perf/util/ctype.c
+++ /dev/null
@@ -1,49 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Sane locale-independent, ASCII ctype.
- *
- * No surprises, and works with signed and unsigned chars.
- */
-#include "sane_ctype.h"
-
-enum {
- S = GIT_SPACE,
- A = GIT_ALPHA,
- D = GIT_DIGIT,
- G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */
- R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | * */
- P = GIT_PRINT_EXTRA, /* printable - alpha - digit - glob - regex */
-
- PS = GIT_SPACE | GIT_PRINT_EXTRA,
-};
-
-unsigned char sane_ctype[256] = {
-/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
-
- 0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0, /* 0.. 15 */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16.. 31 */
- PS,P, P, P, R, P, P, P, R, R, G, R, P, P, R, P, /* 32.. 47 */
- D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G, /* 48.. 63 */
- P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */
- A, A, A, A, A, A, A, A, A, A, A, G, G, P, R, P, /* 80.. 95 */
- P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */
- A, A, A, A, A, A, A, A, A, A, A, R, R, P, P, 0, /* 112..127 */
- /* Nothing in the 128.. range */
-};
-
-const char *graph_line =
- "_____________________________________________________________________"
- "_____________________________________________________________________"
- "_____________________________________________________________________";
-const char *graph_dotted_line =
- "---------------------------------------------------------------------"
- "---------------------------------------------------------------------"
- "---------------------------------------------------------------------";
-const char *spaces =
- " "
- " "
- " ";
-const char *dots =
- "....................................................................."
- "....................................................................."
- ".....................................................................";
diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c
index e0311c9750ad..0c268449959c 100644
--- a/tools/perf/util/data-convert-bt.c
+++ b/tools/perf/util/data-convert-bt.c
@@ -1,16 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* CTF writing support via babeltrace.
*
* Copyright (C) 2014, Jiri Olsa <jolsa@redhat.com>
* Copyright (C) 2014, Sebastian Andrzej Siewior <bigeasy@linutronix.de>
- *
- * Released under the GPL v2. (and only v2, not any later version)
*/
#include <errno.h>
#include <inttypes.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
+#include <linux/zalloc.h>
#include <babeltrace/ctf-writer/writer.h>
#include <babeltrace/ctf-writer/clock.h>
#include <babeltrace/ctf-writer/stream.h>
@@ -23,14 +23,13 @@
#include "asm/bug.h"
#include "data-convert-bt.h"
#include "session.h"
-#include "util.h"
#include "debug.h"
#include "tool.h"
#include "evlist.h"
#include "evsel.h"
#include "machine.h"
#include "config.h"
-#include "sane_ctype.h"
+#include <linux/ctype.h>
#define pr_N(n, fmt, ...) \
eprintf(n, debug_data_convert, fmt, ##__VA_ARGS__)
@@ -271,7 +270,7 @@ static int string_set_value(struct bt_ctf_field *field, const char *string)
if (i > 0)
strncpy(buffer, string, i);
}
- strncat(buffer + p, numstr, 4);
+ memcpy(buffer + p, numstr, 4);
p += 3;
}
}
@@ -414,7 +413,7 @@ static int add_tracepoint_fields_values(struct ctf_writer *cw,
static int add_tracepoint_values(struct ctf_writer *cw,
struct bt_ctf_event_class *event_class,
struct bt_ctf_event *event,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample)
{
struct tep_format_field *common_fields = evsel->tp_format->format.common_fields;
@@ -585,10 +584,10 @@ put_len_type:
static int add_generic_values(struct ctf_writer *cw,
struct bt_ctf_event *event,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample)
{
- u64 type = evsel->attr.sample_type;
+ u64 type = evsel->core.attr.sample_type;
int ret;
/*
@@ -754,11 +753,11 @@ static struct ctf_stream *ctf_stream(struct ctf_writer *cw, int cpu)
}
static int get_sample_cpu(struct ctf_writer *cw, struct perf_sample *sample,
- struct perf_evsel *evsel)
+ struct evsel *evsel)
{
int cpu = 0;
- if (evsel->attr.sample_type & PERF_SAMPLE_CPU)
+ if (evsel->core.attr.sample_type & PERF_SAMPLE_CPU)
cpu = sample->cpu;
if (cpu > cw->stream_cnt) {
@@ -786,7 +785,7 @@ static bool is_flush_needed(struct ctf_stream *cs)
static int process_sample_event(struct perf_tool *tool,
union perf_event *_event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine __maybe_unused)
{
struct convert *c = container_of(tool, struct convert, tool);
@@ -796,7 +795,7 @@ static int process_sample_event(struct perf_tool *tool,
struct bt_ctf_event_class *event_class;
struct bt_ctf_event *event;
int ret;
- unsigned long type = evsel->attr.sample_type;
+ unsigned long type = evsel->core.attr.sample_type;
if (WARN_ONCE(!priv, "Failed to setup all events.\n"))
return 0;
@@ -821,7 +820,7 @@ static int process_sample_event(struct perf_tool *tool,
if (ret)
return -1;
- if (evsel->attr.type == PERF_TYPE_TRACEPOINT) {
+ if (evsel->core.attr.type == PERF_TYPE_TRACEPOINT) {
ret = add_tracepoint_values(cw, event_class, event,
evsel, sample);
if (ret)
@@ -1052,7 +1051,7 @@ static int add_tracepoint_fields_types(struct ctf_writer *cw,
}
static int add_tracepoint_types(struct ctf_writer *cw,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct bt_ctf_event_class *class)
{
struct tep_format_field *common_fields = evsel->tp_format->format.common_fields;
@@ -1085,10 +1084,10 @@ static int add_bpf_output_types(struct ctf_writer *cw,
return bt_ctf_event_class_add_field(class, seq_type, "raw_data");
}
-static int add_generic_types(struct ctf_writer *cw, struct perf_evsel *evsel,
+static int add_generic_types(struct ctf_writer *cw, struct evsel *evsel,
struct bt_ctf_event_class *event_class)
{
- u64 type = evsel->attr.sample_type;
+ u64 type = evsel->core.attr.sample_type;
/*
* missing:
@@ -1151,14 +1150,14 @@ static int add_generic_types(struct ctf_writer *cw, struct perf_evsel *evsel,
return 0;
}
-static int add_event(struct ctf_writer *cw, struct perf_evsel *evsel)
+static int add_event(struct ctf_writer *cw, struct evsel *evsel)
{
struct bt_ctf_event_class *event_class;
struct evsel_priv *priv;
const char *name = perf_evsel__name(evsel);
int ret;
- pr("Adding event '%s' (type %d)\n", name, evsel->attr.type);
+ pr("Adding event '%s' (type %d)\n", name, evsel->core.attr.type);
event_class = bt_ctf_event_class_create(name);
if (!event_class)
@@ -1168,7 +1167,7 @@ static int add_event(struct ctf_writer *cw, struct perf_evsel *evsel)
if (ret)
goto err;
- if (evsel->attr.type == PERF_TYPE_TRACEPOINT) {
+ if (evsel->core.attr.type == PERF_TYPE_TRACEPOINT) {
ret = add_tracepoint_types(cw, evsel, event_class);
if (ret)
goto err;
@@ -1202,8 +1201,8 @@ err:
static int setup_events(struct ctf_writer *cw, struct perf_session *session)
{
- struct perf_evlist *evlist = session->evlist;
- struct perf_evsel *evsel;
+ struct evlist *evlist = session->evlist;
+ struct evsel *evsel;
int ret;
evlist__for_each_entry(evlist, evsel) {
@@ -1309,8 +1308,8 @@ static int setup_non_sample_events(struct ctf_writer *cw,
static void cleanup_events(struct perf_session *session)
{
- struct perf_evlist *evlist = session->evlist;
- struct perf_evsel *evsel;
+ struct evlist *evlist = session->evlist;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) {
struct evsel_priv *priv;
@@ -1320,7 +1319,7 @@ static void cleanup_events(struct perf_session *session)
zfree(&evsel->priv);
}
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
session->evlist = NULL;
}
@@ -1354,7 +1353,7 @@ static void free_streams(struct ctf_writer *cw)
for (cpu = 0; cpu < cw->stream_cnt; cpu++)
ctf_stream__delete(cw->stream[cpu]);
- free(cw->stream);
+ zfree(&cw->stream);
}
static int ctf_writer__setup_env(struct ctf_writer *cw,
diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c
index 6a64f713710d..e75c3a279fe8 100644
--- a/tools/perf/util/data.c
+++ b/tools/perf/util/data.c
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/zalloc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
@@ -8,7 +10,6 @@
#include <unistd.h>
#include <string.h>
#include <asm/bug.h>
-#include <sys/types.h>
#include <dirent.h>
#include "data.h"
@@ -20,7 +21,7 @@ static void close_dir(struct perf_data_file *files, int nr)
{
while (--nr >= 1) {
close(files[nr].fd);
- free(files[nr].path);
+ zfree(&files[nr].path);
}
free(files);
}
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index d7315a00c731..752227b265e7 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -1,20 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* db-export.c: Support for exporting data suitable for import to a database
* Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#include <errno.h>
+#include <stdlib.h>
+#include "dso.h"
#include "evsel.h"
#include "machine.h"
#include "thread.h"
@@ -22,81 +15,25 @@
#include "symbol.h"
#include "map.h"
#include "event.h"
-#include "util.h"
#include "thread-stack.h"
#include "callchain.h"
#include "call-path.h"
#include "db-export.h"
-
-struct deferred_export {
- struct list_head node;
- struct comm *comm;
-};
-
-static int db_export__deferred(struct db_export *dbe)
-{
- struct deferred_export *de;
- int err;
-
- while (!list_empty(&dbe->deferred)) {
- de = list_entry(dbe->deferred.next, struct deferred_export,
- node);
- err = dbe->export_comm(dbe, de->comm);
- list_del(&de->node);
- free(de);
- if (err)
- return err;
- }
-
- return 0;
-}
-
-static void db_export__free_deferred(struct db_export *dbe)
-{
- struct deferred_export *de;
-
- while (!list_empty(&dbe->deferred)) {
- de = list_entry(dbe->deferred.next, struct deferred_export,
- node);
- list_del(&de->node);
- free(de);
- }
-}
-
-static int db_export__defer_comm(struct db_export *dbe, struct comm *comm)
-{
- struct deferred_export *de;
-
- de = zalloc(sizeof(struct deferred_export));
- if (!de)
- return -ENOMEM;
-
- de->comm = comm;
- list_add_tail(&de->node, &dbe->deferred);
-
- return 0;
-}
+#include <linux/zalloc.h>
int db_export__init(struct db_export *dbe)
{
memset(dbe, 0, sizeof(struct db_export));
- INIT_LIST_HEAD(&dbe->deferred);
return 0;
}
-int db_export__flush(struct db_export *dbe)
-{
- return db_export__deferred(dbe);
-}
-
void db_export__exit(struct db_export *dbe)
{
- db_export__free_deferred(dbe);
call_return_processor__free(dbe->crp);
dbe->crp = NULL;
}
-int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel)
+int db_export__evsel(struct db_export *dbe, struct evsel *evsel)
{
if (evsel->db_id)
return 0;
@@ -123,71 +60,73 @@ int db_export__machine(struct db_export *dbe, struct machine *machine)
}
int db_export__thread(struct db_export *dbe, struct thread *thread,
- struct machine *machine, struct comm *comm)
+ struct machine *machine, struct thread *main_thread)
{
- struct thread *main_thread;
u64 main_thread_db_id = 0;
- int err;
if (thread->db_id)
return 0;
thread->db_id = ++dbe->thread_last_db_id;
- if (thread->pid_ != -1) {
- if (thread->pid_ == thread->tid) {
- main_thread = thread;
- } else {
- main_thread = machine__findnew_thread(machine,
- thread->pid_,
- thread->pid_);
- if (!main_thread)
- return -ENOMEM;
- err = db_export__thread(dbe, main_thread, machine,
- comm);
- if (err)
- goto out_put;
- if (comm) {
- err = db_export__comm_thread(dbe, comm, thread);
- if (err)
- goto out_put;
- }
- }
+ if (main_thread)
main_thread_db_id = main_thread->db_id;
- if (main_thread != thread)
- thread__put(main_thread);
- }
if (dbe->export_thread)
return dbe->export_thread(dbe, thread, main_thread_db_id,
machine);
return 0;
+}
-out_put:
- thread__put(main_thread);
- return err;
+static int __db_export__comm(struct db_export *dbe, struct comm *comm,
+ struct thread *thread)
+{
+ comm->db_id = ++dbe->comm_last_db_id;
+
+ if (dbe->export_comm)
+ return dbe->export_comm(dbe, comm, thread);
+
+ return 0;
}
int db_export__comm(struct db_export *dbe, struct comm *comm,
- struct thread *main_thread)
+ struct thread *thread)
+{
+ if (comm->db_id)
+ return 0;
+
+ return __db_export__comm(dbe, comm, thread);
+}
+
+/*
+ * Export the "exec" comm. The "exec" comm is the program / application command
+ * name at the time it first executes. It is used to group threads for the same
+ * program. Note that the main thread pid (or thread group id tgid) cannot be
+ * used because it does not change when a new program is exec'ed.
+ */
+int db_export__exec_comm(struct db_export *dbe, struct comm *comm,
+ struct thread *main_thread)
{
int err;
if (comm->db_id)
return 0;
- comm->db_id = ++dbe->comm_last_db_id;
-
- if (dbe->export_comm) {
- if (main_thread->comm_set)
- err = dbe->export_comm(dbe, comm);
- else
- err = db_export__defer_comm(dbe, comm);
- if (err)
- return err;
- }
+ err = __db_export__comm(dbe, comm, main_thread);
+ if (err)
+ return err;
+ /*
+ * Record the main thread for this comm. Note that the main thread can
+ * have many "exec" comms because there will be a new one every time it
+ * exec's. An "exec" comm however will only ever have 1 main thread.
+ * That is different to any other threads for that same program because
+ * exec() will effectively kill them, so the relationship between the
+ * "exec" comm and non-main threads is 1-to-1. That is why
+ * db_export__comm_thread() is called here for the main thread, but it
+ * is called for non-main threads when they are exported.
+ */
return db_export__comm_thread(dbe, comm, main_thread);
}
@@ -271,7 +210,7 @@ static struct call_path *call_path_from_sample(struct db_export *dbe,
struct machine *machine,
struct thread *thread,
struct perf_sample *sample,
- struct perf_evsel *evsel)
+ struct evsel *evsel)
{
u64 kernel_start = machine__kernel_start(machine);
struct call_path *current = &dbe->cpr->call_path;
@@ -348,11 +287,65 @@ int db_export__branch_type(struct db_export *dbe, u32 branch_type,
return 0;
}
+static int db_export__threads(struct db_export *dbe, struct thread *thread,
+ struct thread *main_thread,
+ struct machine *machine, struct comm **comm_ptr)
+{
+ struct comm *comm = NULL;
+ struct comm *curr_comm;
+ int err;
+
+ if (main_thread) {
+ /*
+ * A thread has a reference to the main thread, so export the
+ * main thread first.
+ */
+ err = db_export__thread(dbe, main_thread, machine, main_thread);
+ if (err)
+ return err;
+ /*
+ * Export comm before exporting the non-main thread because
+ * db_export__comm_thread() can be called further below.
+ */
+ comm = machine__thread_exec_comm(machine, main_thread);
+ if (comm) {
+ err = db_export__exec_comm(dbe, comm, main_thread);
+ if (err)
+ return err;
+ *comm_ptr = comm;
+ }
+ }
+
+ if (thread != main_thread) {
+ /*
+ * For a non-main thread, db_export__comm_thread() must be
+ * called only if thread has not previously been exported.
+ */
+ bool export_comm_thread = comm && !thread->db_id;
+
+ err = db_export__thread(dbe, thread, machine, main_thread);
+ if (err)
+ return err;
+
+ if (export_comm_thread) {
+ err = db_export__comm_thread(dbe, comm, thread);
+ if (err)
+ return err;
+ }
+ }
+
+ curr_comm = thread__comm(thread);
+ if (curr_comm)
+ return db_export__comm(dbe, curr_comm, thread);
+
+ return 0;
+}
+
int db_export__sample(struct db_export *dbe, union perf_event *event,
- struct perf_sample *sample, struct perf_evsel *evsel,
+ struct perf_sample *sample, struct evsel *evsel,
struct addr_location *al)
{
- struct thread* thread = al->thread;
+ struct thread *thread = al->thread;
struct export_sample es = {
.event = event,
.sample = sample,
@@ -372,19 +365,13 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
return err;
main_thread = thread__main_thread(al->machine, thread);
- if (main_thread)
- comm = machine__thread_exec_comm(al->machine, main_thread);
- err = db_export__thread(dbe, thread, al->machine, comm);
+ err = db_export__threads(dbe, thread, main_thread, al->machine, &comm);
if (err)
goto out_put;
- if (comm) {
- err = db_export__comm(dbe, comm, main_thread);
- if (err)
- goto out_put;
+ if (comm)
es.comm_db_id = comm->db_id;
- }
es.db_id = ++dbe->sample_last_db_id;
@@ -402,8 +389,8 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
}
}
- if ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) &&
- sample_addr_correlates_sym(&evsel->attr)) {
+ if ((evsel->core.attr.sample_type & PERF_SAMPLE_ADDR) &&
+ sample_addr_correlates_sym(&evsel->core.attr)) {
struct addr_location addr_al;
thread__resolve(thread, &addr_al, sample);
@@ -533,3 +520,92 @@ int db_export__call_return(struct db_export *dbe, struct call_return *cr,
return 0;
}
+
+static int db_export__pid_tid(struct db_export *dbe, struct machine *machine,
+ pid_t pid, pid_t tid, u64 *db_id,
+ struct comm **comm_ptr, bool *is_idle)
+{
+ struct thread *thread = machine__find_thread(machine, pid, tid);
+ struct thread *main_thread;
+ int err = 0;
+
+ if (!thread || !thread->comm_set)
+ goto out_put;
+
+ *is_idle = !thread->pid_ && !thread->tid;
+
+ main_thread = thread__main_thread(machine, thread);
+
+ err = db_export__threads(dbe, thread, main_thread, machine, comm_ptr);
+
+ *db_id = thread->db_id;
+
+ thread__put(main_thread);
+out_put:
+ thread__put(thread);
+
+ return err;
+}
+
+int db_export__switch(struct db_export *dbe, union perf_event *event,
+ struct perf_sample *sample, struct machine *machine)
+{
+ bool out = event->header.misc & PERF_RECORD_MISC_SWITCH_OUT;
+ bool out_preempt = out &&
+ (event->header.misc & PERF_RECORD_MISC_SWITCH_OUT_PREEMPT);
+ int flags = out | (out_preempt << 1);
+ bool is_idle_a = false, is_idle_b = false;
+ u64 th_a_id = 0, th_b_id = 0;
+ u64 comm_out_id, comm_in_id;
+ struct comm *comm_a = NULL;
+ struct comm *comm_b = NULL;
+ u64 th_out_id, th_in_id;
+ u64 db_id;
+ int err;
+
+ err = db_export__machine(dbe, machine);
+ if (err)
+ return err;
+
+ err = db_export__pid_tid(dbe, machine, sample->pid, sample->tid,
+ &th_a_id, &comm_a, &is_idle_a);
+ if (err)
+ return err;
+
+ if (event->header.type == PERF_RECORD_SWITCH_CPU_WIDE) {
+ pid_t pid = event->context_switch.next_prev_pid;
+ pid_t tid = event->context_switch.next_prev_tid;
+
+ err = db_export__pid_tid(dbe, machine, pid, tid, &th_b_id,
+ &comm_b, &is_idle_b);
+ if (err)
+ return err;
+ }
+
+ /*
+ * Do not export if both threads are unknown (i.e. not being traced),
+ * or one is unknown and the other is the idle task.
+ */
+ if ((!th_a_id || is_idle_a) && (!th_b_id || is_idle_b))
+ return 0;
+
+ db_id = ++dbe->context_switch_last_db_id;
+
+ if (out) {
+ th_out_id = th_a_id;
+ th_in_id = th_b_id;
+ comm_out_id = comm_a ? comm_a->db_id : 0;
+ comm_in_id = comm_b ? comm_b->db_id : 0;
+ } else {
+ th_out_id = th_b_id;
+ th_in_id = th_a_id;
+ comm_out_id = comm_b ? comm_b->db_id : 0;
+ comm_in_id = comm_a ? comm_a->db_id : 0;
+ }
+
+ if (dbe->export_context_switch)
+ return dbe->export_context_switch(dbe, db_id, machine, sample,
+ th_out_id, comm_out_id,
+ th_in_id, comm_in_id, flags);
+ return 0;
+}
diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h
index 4e2424c89df9..9c3d38f5a40d 100644
--- a/tools/perf/util/db-export.h
+++ b/tools/perf/util/db-export.h
@@ -1,16 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* db-export.h: Support for exporting data suitable for import to a database
* Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#ifndef __PERF_DB_EXPORT_H
@@ -19,7 +10,7 @@
#include <linux/types.h>
#include <linux/list.h>
-struct perf_evsel;
+struct evsel;
struct machine;
struct thread;
struct comm;
@@ -34,7 +25,7 @@ struct call_return;
struct export_sample {
union perf_event *event;
struct perf_sample *sample;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct addr_location *al;
u64 db_id;
u64 comm_db_id;
@@ -48,11 +39,12 @@ struct export_sample {
};
struct db_export {
- int (*export_evsel)(struct db_export *dbe, struct perf_evsel *evsel);
+ int (*export_evsel)(struct db_export *dbe, struct evsel *evsel);
int (*export_machine)(struct db_export *dbe, struct machine *machine);
int (*export_thread)(struct db_export *dbe, struct thread *thread,
u64 main_thread_db_id, struct machine *machine);
- int (*export_comm)(struct db_export *dbe, struct comm *comm);
+ int (*export_comm)(struct db_export *dbe, struct comm *comm,
+ struct thread *thread);
int (*export_comm_thread)(struct db_export *dbe, u64 db_id,
struct comm *comm, struct thread *thread);
int (*export_dso)(struct db_export *dbe, struct dso *dso,
@@ -65,6 +57,11 @@ struct db_export {
int (*export_call_path)(struct db_export *dbe, struct call_path *cp);
int (*export_call_return)(struct db_export *dbe,
struct call_return *cr);
+ int (*export_context_switch)(struct db_export *dbe, u64 db_id,
+ struct machine *machine,
+ struct perf_sample *sample,
+ u64 th_out_id, u64 comm_out_id,
+ u64 th_in_id, u64 comm_in_id, int flags);
struct call_return_processor *crp;
struct call_path_root *cpr;
u64 evsel_last_db_id;
@@ -77,18 +74,19 @@ struct db_export {
u64 sample_last_db_id;
u64 call_path_last_db_id;
u64 call_return_last_db_id;
- struct list_head deferred;
+ u64 context_switch_last_db_id;
};
int db_export__init(struct db_export *dbe);
-int db_export__flush(struct db_export *dbe);
void db_export__exit(struct db_export *dbe);
-int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel);
+int db_export__evsel(struct db_export *dbe, struct evsel *evsel);
int db_export__machine(struct db_export *dbe, struct machine *machine);
int db_export__thread(struct db_export *dbe, struct thread *thread,
- struct machine *machine, struct comm *comm);
+ struct machine *machine, struct thread *main_thread);
int db_export__comm(struct db_export *dbe, struct comm *comm,
- struct thread *main_thread);
+ struct thread *thread);
+int db_export__exec_comm(struct db_export *dbe, struct comm *comm,
+ struct thread *main_thread);
int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
struct thread *thread);
int db_export__dso(struct db_export *dbe, struct dso *dso,
@@ -98,7 +96,7 @@ int db_export__symbol(struct db_export *dbe, struct symbol *sym,
int db_export__branch_type(struct db_export *dbe, u32 branch_type,
const char *name);
int db_export__sample(struct db_export *dbe, union perf_event *event,
- struct perf_sample *sample, struct perf_evsel *evsel,
+ struct perf_sample *sample, struct evsel *evsel,
struct addr_location *al);
int db_export__branch_types(struct db_export *dbe);
@@ -106,5 +104,7 @@ int db_export__branch_types(struct db_export *dbe);
int db_export__call_path(struct db_export *dbe, struct call_path *cp);
int db_export__call_return(struct db_export *dbe, struct call_return *cr,
u64 *parent_db_id);
+int db_export__switch(struct db_export *dbe, union perf_event *event,
+ struct perf_sample *sample, struct machine *machine);
#endif
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index 3d6459626c2a..a1b59bd35519 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -1,27 +1,28 @@
// SPDX-License-Identifier: GPL-2.0
/* For general debugging purposes */
-#include "../perf.h"
-
#include <inttypes.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
+#include <stdlib.h>
#include <sys/wait.h>
#include <api/debug.h>
+#include <linux/kernel.h>
#include <linux/time64.h>
#ifdef HAVE_BACKTRACE_SUPPORT
#include <execinfo.h>
#endif
-#include "cache.h"
#include "color.h"
#include "event.h"
#include "debug.h"
#include "print_binary.h"
#include "util.h"
#include "target.h"
+#include "ui/helpline.h"
+#include "ui/ui.h"
-#include "sane_ctype.h"
+#include <linux/ctype.h>
int verbose;
bool dump_trace = false, quiet = false;
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
index 77445dfc5c7d..b2deee987ffa 100644
--- a/tools/perf/util/debug.h
+++ b/tools/perf/util/debug.h
@@ -4,11 +4,7 @@
#define __PERF_DEBUG_H
#include <stdbool.h>
-#include <string.h>
#include <linux/compiler.h>
-#include "event.h"
-#include "../ui/helpline.h"
-#include "../ui/progress.h"
#include "../ui/util.h"
extern int verbose;
@@ -42,6 +38,8 @@ extern int debug_data_convert;
#define STRERR_BUFSIZE 128 /* For the buffer size of str_error_r */
+union perf_event;
+
int dump_printf(const char *fmt, ...) __printf(1, 2);
void trace_event(union perf_event *event);
diff --git a/tools/perf/util/demangle-java.c b/tools/perf/util/demangle-java.c
index e4c486756053..763328c151e9 100644
--- a/tools/perf/util/demangle-java.c
+++ b/tools/perf/util/demangle-java.c
@@ -1,14 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
#include <sys/types.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include "util.h"
#include "debug.h"
#include "symbol.h"
#include "demangle-java.h"
-#include "sane_ctype.h"
+#include <linux/ctype.h>
+#include <linux/kernel.h>
enum {
MODE_PREFIX = 0,
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index e059976d9d93..e11ddf86f2b3 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
#include <asm/bug.h>
#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/zalloc.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/types.h>
@@ -8,17 +10,21 @@
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
-#include <libgen.h>
+#include <stdlib.h>
+#include <bpf/libbpf.h>
+#include "bpf-event.h"
#include "compress.h"
+#include "env.h"
#include "namespaces.h"
#include "path.h"
#include "map.h"
#include "symbol.h"
#include "srcline.h"
#include "dso.h"
+#include "dsos.h"
#include "machine.h"
#include "auxtrace.h"
-#include "util.h"
+#include "util.h" /* O_CLOEXEC for older systems */
#include "debug.h"
#include "string2.h"
#include "vdso.h"
@@ -392,7 +398,7 @@ int __kmod_path__parse(struct kmod_path *m, const char *path,
return -ENOMEM;
}
- strxfrchar(m->name, '-', '_');
+ strreplace(m->name, '-', '_');
}
return 0;
@@ -430,7 +436,7 @@ static void dso__list_add(struct dso *dso)
static void dso__list_del(struct dso *dso)
{
- list_del(&dso->data.open_entry);
+ list_del_init(&dso->data.open_entry);
WARN_ONCE(dso__data_open_cnt <= 0,
"DSO data fd counter out of bounds.");
dso__data_open_cnt--;
@@ -706,6 +712,44 @@ bool dso__data_status_seen(struct dso *dso, enum dso_data_status_seen by)
return false;
}
+static ssize_t bpf_read(struct dso *dso, u64 offset, char *data)
+{
+ struct bpf_prog_info_node *node;
+ ssize_t size = DSO__DATA_CACHE_SIZE;
+ u64 len;
+ u8 *buf;
+
+ node = perf_env__find_bpf_prog_info(dso->bpf_prog.env, dso->bpf_prog.id);
+ if (!node || !node->info_linear) {
+ dso->data.status = DSO_DATA_STATUS_ERROR;
+ return -1;
+ }
+
+ len = node->info_linear->info.jited_prog_len;
+ buf = (u8 *)(uintptr_t)node->info_linear->info.jited_prog_insns;
+
+ if (offset >= len)
+ return -1;
+
+ size = (ssize_t)min(len - offset, (u64)size);
+ memcpy(data, buf + offset, size);
+ return size;
+}
+
+static int bpf_size(struct dso *dso)
+{
+ struct bpf_prog_info_node *node;
+
+ node = perf_env__find_bpf_prog_info(dso->bpf_prog.env, dso->bpf_prog.id);
+ if (!node || !node->info_linear) {
+ dso->data.status = DSO_DATA_STATUS_ERROR;
+ return -1;
+ }
+
+ dso->data.file_size = node->info_linear->info.jited_prog_len;
+ return 0;
+}
+
static void
dso_cache__free(struct dso *dso)
{
@@ -794,48 +838,53 @@ dso_cache__memcpy(struct dso_cache *cache, u64 offset,
return cache_size;
}
-static ssize_t
-dso_cache__read(struct dso *dso, struct machine *machine,
- u64 offset, u8 *data, ssize_t size)
+static ssize_t file_read(struct dso *dso, struct machine *machine,
+ u64 offset, char *data)
{
- struct dso_cache *cache;
- struct dso_cache *old;
ssize_t ret;
- do {
- u64 cache_offset;
+ pthread_mutex_lock(&dso__data_open_lock);
- cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE);
- if (!cache)
- return -ENOMEM;
+ /*
+ * dso->data.fd might be closed if other thread opened another
+ * file (dso) due to open file limit (RLIMIT_NOFILE).
+ */
+ try_to_open_dso(dso, machine);
- pthread_mutex_lock(&dso__data_open_lock);
+ if (dso->data.fd < 0) {
+ dso->data.status = DSO_DATA_STATUS_ERROR;
+ ret = -errno;
+ goto out;
+ }
- /*
- * dso->data.fd might be closed if other thread opened another
- * file (dso) due to open file limit (RLIMIT_NOFILE).
- */
- try_to_open_dso(dso, machine);
+ ret = pread(dso->data.fd, data, DSO__DATA_CACHE_SIZE, offset);
+out:
+ pthread_mutex_unlock(&dso__data_open_lock);
+ return ret;
+}
- if (dso->data.fd < 0) {
- ret = -errno;
- dso->data.status = DSO_DATA_STATUS_ERROR;
- break;
- }
+static ssize_t
+dso_cache__read(struct dso *dso, struct machine *machine,
+ u64 offset, u8 *data, ssize_t size)
+{
+ u64 cache_offset = offset & DSO__DATA_CACHE_MASK;
+ struct dso_cache *cache;
+ struct dso_cache *old;
+ ssize_t ret;
- cache_offset = offset & DSO__DATA_CACHE_MASK;
+ cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE);
+ if (!cache)
+ return -ENOMEM;
- ret = pread(dso->data.fd, cache->data, DSO__DATA_CACHE_SIZE, cache_offset);
- if (ret <= 0)
- break;
+ if (dso->binary_type == DSO_BINARY_TYPE__BPF_PROG_INFO)
+ ret = bpf_read(dso, cache_offset, cache->data);
+ else
+ ret = file_read(dso, machine, cache_offset, cache->data);
+ if (ret > 0) {
cache->offset = cache_offset;
cache->size = ret;
- } while (0);
-
- pthread_mutex_unlock(&dso__data_open_lock);
- if (ret > 0) {
old = dso_cache__insert(dso, cache);
if (old) {
/* we lose the race */
@@ -898,18 +947,12 @@ static ssize_t cached_read(struct dso *dso, struct machine *machine,
return r;
}
-int dso__data_file_size(struct dso *dso, struct machine *machine)
+static int file_size(struct dso *dso, struct machine *machine)
{
int ret = 0;
struct stat st;
char sbuf[STRERR_BUFSIZE];
- if (dso->data.file_size)
- return 0;
-
- if (dso->data.status == DSO_DATA_STATUS_ERROR)
- return -1;
-
pthread_mutex_lock(&dso__data_open_lock);
/*
@@ -938,6 +981,20 @@ out:
return ret;
}
+int dso__data_file_size(struct dso *dso, struct machine *machine)
+{
+ if (dso->data.file_size)
+ return 0;
+
+ if (dso->data.status == DSO_DATA_STATUS_ERROR)
+ return -1;
+
+ if (dso->binary_type == DSO_BINARY_TYPE__BPF_PROG_INFO)
+ return bpf_size(dso);
+
+ return file_size(dso, machine);
+}
+
/**
* dso__data_size - Return dso data size
* @dso: dso object
@@ -1039,66 +1096,6 @@ struct dso *machine__findnew_kernel(struct machine *machine, const char *name,
return dso;
}
-/*
- * Find a matching entry and/or link current entry to RB tree.
- * Either one of the dso or name parameter must be non-NULL or the
- * function will not work.
- */
-static struct dso *__dso__findlink_by_longname(struct rb_root *root,
- struct dso *dso, const char *name)
-{
- struct rb_node **p = &root->rb_node;
- struct rb_node *parent = NULL;
-
- if (!name)
- name = dso->long_name;
- /*
- * Find node with the matching name
- */
- while (*p) {
- struct dso *this = rb_entry(*p, struct dso, rb_node);
- int rc = strcmp(name, this->long_name);
-
- parent = *p;
- if (rc == 0) {
- /*
- * In case the new DSO is a duplicate of an existing
- * one, print a one-time warning & put the new entry
- * at the end of the list of duplicates.
- */
- if (!dso || (dso == this))
- return this; /* Find matching dso */
- /*
- * The core kernel DSOs may have duplicated long name.
- * In this case, the short name should be different.
- * Comparing the short names to differentiate the DSOs.
- */
- rc = strcmp(dso->short_name, this->short_name);
- if (rc == 0) {
- pr_err("Duplicated dso name: %s\n", name);
- return NULL;
- }
- }
- if (rc < 0)
- p = &parent->rb_left;
- else
- p = &parent->rb_right;
- }
- if (dso) {
- /* Add new node and rebalance tree */
- rb_link_node(&dso->rb_node, parent, p);
- rb_insert_color(&dso->rb_node, root);
- dso->root = root;
- }
- return NULL;
-}
-
-static inline struct dso *__dso__find_by_longname(struct rb_root *root,
- const char *name)
-{
- return __dso__findlink_by_longname(root, NULL, name);
-}
-
void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated)
{
struct rb_root *root = dso->root;
@@ -1112,7 +1109,7 @@ void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated)
if (root) {
rb_erase(&dso->rb_node, root);
/*
- * __dso__findlink_by_longname() isn't guaranteed to add it
+ * __dsos__findnew_link_by_longname() isn't guaranteed to add it
* back, so a clean removal is required here.
*/
RB_CLEAR_NODE(&dso->rb_node);
@@ -1124,7 +1121,7 @@ void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated)
dso->long_name_allocated = name_allocated;
if (root)
- __dso__findlink_by_longname(root, dso, NULL);
+ __dsos__findnew_link_by_longname(root, dso, NULL);
}
void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated)
@@ -1140,38 +1137,6 @@ void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated)
dso->short_name_allocated = name_allocated;
}
-static void dso__set_basename(struct dso *dso)
-{
- char *base, *lname;
- int tid;
-
- if (sscanf(dso->long_name, "/tmp/perf-%d.map", &tid) == 1) {
- if (asprintf(&base, "[JIT] tid %d", tid) < 0)
- return;
- } else {
- /*
- * basename() may modify path buffer, so we must pass
- * a copy.
- */
- lname = strdup(dso->long_name);
- if (!lname)
- return;
-
- /*
- * basename() may return a pointer to internal
- * storage which is reused in subsequent calls
- * so copy the result.
- */
- base = strdup(basename(lname));
-
- free(lname);
-
- if (!base)
- return;
- }
- dso__set_short_name(dso, base, true);
-}
-
int dso__name_len(const struct dso *dso)
{
if (!dso)
@@ -1322,143 +1287,6 @@ int dso__kernel_module_get_build_id(struct dso *dso,
return 0;
}
-bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
-{
- bool have_build_id = false;
- struct dso *pos;
- struct nscookie nsc;
-
- list_for_each_entry(pos, head, node) {
- if (with_hits && !pos->hit && !dso__is_vdso(pos))
- continue;
- if (pos->has_build_id) {
- have_build_id = true;
- continue;
- }
- nsinfo__mountns_enter(pos->nsinfo, &nsc);
- if (filename__read_build_id(pos->long_name, pos->build_id,
- sizeof(pos->build_id)) > 0) {
- have_build_id = true;
- pos->has_build_id = true;
- }
- nsinfo__mountns_exit(&nsc);
- }
-
- return have_build_id;
-}
-
-void __dsos__add(struct dsos *dsos, struct dso *dso)
-{
- list_add_tail(&dso->node, &dsos->head);
- __dso__findlink_by_longname(&dsos->root, dso, NULL);
- /*
- * It is now in the linked list, grab a reference, then garbage collect
- * this when needing memory, by looking at LRU dso instances in the
- * list with atomic_read(&dso->refcnt) == 1, i.e. no references
- * anywhere besides the one for the list, do, under a lock for the
- * list: remove it from the list, then a dso__put(), that probably will
- * be the last and will then call dso__delete(), end of life.
- *
- * That, or at the end of the 'struct machine' lifetime, when all
- * 'struct dso' instances will be removed from the list, in
- * dsos__exit(), if they have no other reference from some other data
- * structure.
- *
- * E.g.: after processing a 'perf.data' file and storing references
- * to objects instantiated while processing events, we will have
- * references to the 'thread', 'map', 'dso' structs all from 'struct
- * hist_entry' instances, but we may not need anything not referenced,
- * so we might as well call machines__exit()/machines__delete() and
- * garbage collect it.
- */
- dso__get(dso);
-}
-
-void dsos__add(struct dsos *dsos, struct dso *dso)
-{
- down_write(&dsos->lock);
- __dsos__add(dsos, dso);
- up_write(&dsos->lock);
-}
-
-struct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short)
-{
- struct dso *pos;
-
- if (cmp_short) {
- list_for_each_entry(pos, &dsos->head, node)
- if (strcmp(pos->short_name, name) == 0)
- return pos;
- return NULL;
- }
- return __dso__find_by_longname(&dsos->root, name);
-}
-
-struct dso *dsos__find(struct dsos *dsos, const char *name, bool cmp_short)
-{
- struct dso *dso;
- down_read(&dsos->lock);
- dso = __dsos__find(dsos, name, cmp_short);
- up_read(&dsos->lock);
- return dso;
-}
-
-struct dso *__dsos__addnew(struct dsos *dsos, const char *name)
-{
- struct dso *dso = dso__new(name);
-
- if (dso != NULL) {
- __dsos__add(dsos, dso);
- dso__set_basename(dso);
- /* Put dso here because __dsos_add already got it */
- dso__put(dso);
- }
- return dso;
-}
-
-struct dso *__dsos__findnew(struct dsos *dsos, const char *name)
-{
- struct dso *dso = __dsos__find(dsos, name, false);
-
- return dso ? dso : __dsos__addnew(dsos, name);
-}
-
-struct dso *dsos__findnew(struct dsos *dsos, const char *name)
-{
- struct dso *dso;
- down_write(&dsos->lock);
- dso = dso__get(__dsos__findnew(dsos, name));
- up_write(&dsos->lock);
- return dso;
-}
-
-size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
- bool (skip)(struct dso *dso, int parm), int parm)
-{
- struct dso *pos;
- size_t ret = 0;
-
- list_for_each_entry(pos, head, node) {
- if (skip && skip(pos, parm))
- continue;
- ret += dso__fprintf_buildid(pos, fp);
- ret += fprintf(fp, " %s\n", pos->long_name);
- }
- return ret;
-}
-
-size_t __dsos__fprintf(struct list_head *head, FILE *fp)
-{
- struct dso *pos;
- size_t ret = 0;
-
- list_for_each_entry(pos, head, node) {
- ret += dso__fprintf(pos, fp);
- }
-
- return ret;
-}
-
size_t dso__fprintf_buildid(struct dso *dso, FILE *fp)
{
char sbuild_id[SBUILD_ID_SIZE];
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 6e3f63781e51..e4dddb76770d 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -2,13 +2,13 @@
#ifndef __PERF_DSO
#define __PERF_DSO
+#include <pthread.h>
#include <linux/refcount.h>
#include <linux/types.h>
#include <linux/rbtree.h>
#include <sys/types.h>
#include <stdbool.h>
#include <stdio.h>
-#include "rwsem.h"
#include <linux/bitops.h>
#include "build-id.h"
@@ -16,6 +16,9 @@ struct machine;
struct map;
struct perf_env;
+#define DSO__NAME_KALLSYMS "[kernel.kallsyms]"
+#define DSO__NAME_KCORE "[kernel.kcore]"
+
enum dso_binary_type {
DSO_BINARY_TYPE__KALLSYMS = 0,
DSO_BINARY_TYPE__GUEST_KALLSYMS,
@@ -126,16 +129,6 @@ struct dso_cache {
char data[0];
};
-/*
- * DSOs are put into both a list for fast iteration and rbtree for fast
- * long name lookup.
- */
-struct dsos {
- struct list_head head;
- struct rb_root root; /* rbtree root sorted by long name */
- struct rw_semaphore lock;
-};
-
struct auxtrace_cache;
struct dso {
@@ -344,21 +337,8 @@ struct map *dso__new_map(const char *name);
struct dso *machine__findnew_kernel(struct machine *machine, const char *name,
const char *short_name, int dso_type);
-void __dsos__add(struct dsos *dsos, struct dso *dso);
-void dsos__add(struct dsos *dsos, struct dso *dso);
-struct dso *__dsos__addnew(struct dsos *dsos, const char *name);
-struct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short);
-struct dso *dsos__find(struct dsos *dsos, const char *name, bool cmp_short);
-struct dso *__dsos__findnew(struct dsos *dsos, const char *name);
-struct dso *dsos__findnew(struct dsos *dsos, const char *name);
-bool __dsos__read_build_ids(struct list_head *head, bool with_hits);
-
void dso__reset_find_symbol_cache(struct dso *dso);
-size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
- bool (skip)(struct dso *dso, int parm), int parm);
-size_t __dsos__fprintf(struct list_head *head, FILE *fp);
-
size_t dso__fprintf_buildid(struct dso *dso, FILE *fp);
size_t dso__fprintf_symbols_by_name(struct dso *dso, FILE *fp);
size_t dso__fprintf(struct dso *dso, FILE *fp);
diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c
new file mode 100644
index 000000000000..3ea80d203587
--- /dev/null
+++ b/tools/perf/util/dsos.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "debug.h"
+#include "dsos.h"
+#include "dso.h"
+#include "vdso.h"
+#include "namespaces.h"
+#include <libgen.h>
+#include <stdlib.h>
+#include <string.h>
+#include <symbol.h> // filename__read_build_id
+
+bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
+{
+ bool have_build_id = false;
+ struct dso *pos;
+ struct nscookie nsc;
+
+ list_for_each_entry(pos, head, node) {
+ if (with_hits && !pos->hit && !dso__is_vdso(pos))
+ continue;
+ if (pos->has_build_id) {
+ have_build_id = true;
+ continue;
+ }
+ nsinfo__mountns_enter(pos->nsinfo, &nsc);
+ if (filename__read_build_id(pos->long_name, pos->build_id,
+ sizeof(pos->build_id)) > 0) {
+ have_build_id = true;
+ pos->has_build_id = true;
+ }
+ nsinfo__mountns_exit(&nsc);
+ }
+
+ return have_build_id;
+}
+
+/*
+ * Find a matching entry and/or link current entry to RB tree.
+ * Either one of the dso or name parameter must be non-NULL or the
+ * function will not work.
+ */
+struct dso *__dsos__findnew_link_by_longname(struct rb_root *root, struct dso *dso, const char *name)
+{
+ struct rb_node **p = &root->rb_node;
+ struct rb_node *parent = NULL;
+
+ if (!name)
+ name = dso->long_name;
+ /*
+ * Find node with the matching name
+ */
+ while (*p) {
+ struct dso *this = rb_entry(*p, struct dso, rb_node);
+ int rc = strcmp(name, this->long_name);
+
+ parent = *p;
+ if (rc == 0) {
+ /*
+ * In case the new DSO is a duplicate of an existing
+ * one, print a one-time warning & put the new entry
+ * at the end of the list of duplicates.
+ */
+ if (!dso || (dso == this))
+ return this; /* Find matching dso */
+ /*
+ * The core kernel DSOs may have duplicated long name.
+ * In this case, the short name should be different.
+ * Comparing the short names to differentiate the DSOs.
+ */
+ rc = strcmp(dso->short_name, this->short_name);
+ if (rc == 0) {
+ pr_err("Duplicated dso name: %s\n", name);
+ return NULL;
+ }
+ }
+ if (rc < 0)
+ p = &parent->rb_left;
+ else
+ p = &parent->rb_right;
+ }
+ if (dso) {
+ /* Add new node and rebalance tree */
+ rb_link_node(&dso->rb_node, parent, p);
+ rb_insert_color(&dso->rb_node, root);
+ dso->root = root;
+ }
+ return NULL;
+}
+
+void __dsos__add(struct dsos *dsos, struct dso *dso)
+{
+ list_add_tail(&dso->node, &dsos->head);
+ __dsos__findnew_link_by_longname(&dsos->root, dso, NULL);
+ /*
+ * It is now in the linked list, grab a reference, then garbage collect
+ * this when needing memory, by looking at LRU dso instances in the
+ * list with atomic_read(&dso->refcnt) == 1, i.e. no references
+ * anywhere besides the one for the list, do, under a lock for the
+ * list: remove it from the list, then a dso__put(), that probably will
+ * be the last and will then call dso__delete(), end of life.
+ *
+ * That, or at the end of the 'struct machine' lifetime, when all
+ * 'struct dso' instances will be removed from the list, in
+ * dsos__exit(), if they have no other reference from some other data
+ * structure.
+ *
+ * E.g.: after processing a 'perf.data' file and storing references
+ * to objects instantiated while processing events, we will have
+ * references to the 'thread', 'map', 'dso' structs all from 'struct
+ * hist_entry' instances, but we may not need anything not referenced,
+ * so we might as well call machines__exit()/machines__delete() and
+ * garbage collect it.
+ */
+ dso__get(dso);
+}
+
+void dsos__add(struct dsos *dsos, struct dso *dso)
+{
+ down_write(&dsos->lock);
+ __dsos__add(dsos, dso);
+ up_write(&dsos->lock);
+}
+
+struct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short)
+{
+ struct dso *pos;
+
+ if (cmp_short) {
+ list_for_each_entry(pos, &dsos->head, node)
+ if (strcmp(pos->short_name, name) == 0)
+ return pos;
+ return NULL;
+ }
+ return __dsos__findnew_by_longname(&dsos->root, name);
+}
+
+struct dso *dsos__find(struct dsos *dsos, const char *name, bool cmp_short)
+{
+ struct dso *dso;
+ down_read(&dsos->lock);
+ dso = __dsos__find(dsos, name, cmp_short);
+ up_read(&dsos->lock);
+ return dso;
+}
+
+static void dso__set_basename(struct dso *dso)
+{
+ char *base, *lname;
+ int tid;
+
+ if (sscanf(dso->long_name, "/tmp/perf-%d.map", &tid) == 1) {
+ if (asprintf(&base, "[JIT] tid %d", tid) < 0)
+ return;
+ } else {
+ /*
+ * basename() may modify path buffer, so we must pass
+ * a copy.
+ */
+ lname = strdup(dso->long_name);
+ if (!lname)
+ return;
+
+ /*
+ * basename() may return a pointer to internal
+ * storage which is reused in subsequent calls
+ * so copy the result.
+ */
+ base = strdup(basename(lname));
+
+ free(lname);
+
+ if (!base)
+ return;
+ }
+ dso__set_short_name(dso, base, true);
+}
+
+struct dso *__dsos__addnew(struct dsos *dsos, const char *name)
+{
+ struct dso *dso = dso__new(name);
+
+ if (dso != NULL) {
+ __dsos__add(dsos, dso);
+ dso__set_basename(dso);
+ /* Put dso here because __dsos_add already got it */
+ dso__put(dso);
+ }
+ return dso;
+}
+
+struct dso *__dsos__findnew(struct dsos *dsos, const char *name)
+{
+ struct dso *dso = __dsos__find(dsos, name, false);
+
+ return dso ? dso : __dsos__addnew(dsos, name);
+}
+
+struct dso *dsos__findnew(struct dsos *dsos, const char *name)
+{
+ struct dso *dso;
+ down_write(&dsos->lock);
+ dso = dso__get(__dsos__findnew(dsos, name));
+ up_write(&dsos->lock);
+ return dso;
+}
+
+size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
+ bool (skip)(struct dso *dso, int parm), int parm)
+{
+ struct dso *pos;
+ size_t ret = 0;
+
+ list_for_each_entry(pos, head, node) {
+ if (skip && skip(pos, parm))
+ continue;
+ ret += dso__fprintf_buildid(pos, fp);
+ ret += fprintf(fp, " %s\n", pos->long_name);
+ }
+ return ret;
+}
+
+size_t __dsos__fprintf(struct list_head *head, FILE *fp)
+{
+ struct dso *pos;
+ size_t ret = 0;
+
+ list_for_each_entry(pos, head, node) {
+ ret += dso__fprintf(pos, fp);
+ }
+
+ return ret;
+}
diff --git a/tools/perf/util/dsos.h b/tools/perf/util/dsos.h
new file mode 100644
index 000000000000..32f1fbee0feb
--- /dev/null
+++ b/tools/perf/util/dsos.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PERF_DSOS
+#define __PERF_DSOS
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include "rwsem.h"
+
+struct dso;
+
+/*
+ * DSOs are put into both a list for fast iteration and rbtree for fast
+ * long name lookup.
+ */
+struct dsos {
+ struct list_head head;
+ struct rb_root root; /* rbtree root sorted by long name */
+ struct rw_semaphore lock;
+};
+
+void __dsos__add(struct dsos *dsos, struct dso *dso);
+void dsos__add(struct dsos *dsos, struct dso *dso);
+struct dso *__dsos__addnew(struct dsos *dsos, const char *name);
+struct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short);
+struct dso *dsos__find(struct dsos *dsos, const char *name, bool cmp_short);
+struct dso *__dsos__findnew(struct dsos *dsos, const char *name);
+struct dso *dsos__findnew(struct dsos *dsos, const char *name);
+
+struct dso *__dsos__findnew_link_by_longname(struct rb_root *root, struct dso *dso, const char *name);
+
+static inline struct dso *__dsos__findnew_by_longname(struct rb_root *root, const char *name)
+{
+ return __dsos__findnew_link_by_longname(root, NULL, name);
+}
+
+bool __dsos__read_build_ids(struct list_head *head, bool with_hits);
+
+size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
+ bool (skip)(struct dso *dso, int parm), int parm);
+size_t __dsos__fprintf(struct list_head *head, FILE *fp);
+
+#endif /* __PERF_DSOS */
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
index 7eb7de5aee44..df6cee5c071f 100644
--- a/tools/perf/util/dwarf-aux.c
+++ b/tools/perf/util/dwarf-aux.c
@@ -1,28 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* dwarf-aux.c : libdw auxiliary interfaces
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
*/
#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
-#include "util.h"
+#include <stdlib.h>
#include "debug.h"
#include "dwarf-aux.h"
+#include "strbuf.h"
#include "string2.h"
/**
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h
index 8ac53bf1ec4e..f204e5892403 100644
--- a/tools/perf/util/dwarf-aux.h
+++ b/tools/perf/util/dwarf-aux.h
@@ -1,22 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _DWARF_AUX_H
#define _DWARF_AUX_H
/*
* dwarf-aux.h : libdw auxiliary interfaces
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
*/
#include <dwarf.h>
@@ -24,6 +10,8 @@
#include <elfutils/libdwfl.h>
#include <elfutils/version.h>
+struct strbuf;
+
/* Find the realpath of the target file */
const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname);
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index 6a3eaf7d9353..3baca06786fb 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -1,12 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
#include "cpumap.h"
+#include "debug.h"
#include "env.h"
-#include "sane_ctype.h"
-#include "util.h"
+#include <linux/ctype.h>
+#include <linux/zalloc.h>
#include "bpf-event.h"
#include <errno.h>
#include <sys/utsname.h>
#include <bpf/libbpf.h>
+#include <stdlib.h>
+#include <string.h>
struct perf_env perf_env;
@@ -178,7 +181,7 @@ void perf_env__exit(struct perf_env *env)
zfree(&env->cpu);
for (i = 0; i < env->nr_numa_nodes; i++)
- cpu_map__put(env->numa_nodes[i].map);
+ perf_cpu_map__put(env->numa_nodes[i].map);
zfree(&env->numa_nodes);
for (i = 0; i < env->caches_cnt; i++)
@@ -186,7 +189,7 @@ void perf_env__exit(struct perf_env *env)
zfree(&env->caches);
for (i = 0; i < env->nr_memory_nodes; i++)
- free(env->memory_nodes[i].set);
+ zfree(&env->memory_nodes[i].set);
zfree(&env->memory_nodes);
}
@@ -246,6 +249,7 @@ int perf_env__read_cpu_topology_map(struct perf_env *env)
for (cpu = 0; cpu < nr_cpus; ++cpu) {
env->cpu[cpu].core_id = cpu_map__get_core_id(cpu);
env->cpu[cpu].socket_id = cpu_map__get_socket_id(cpu);
+ env->cpu[cpu].die_id = cpu_map__get_die_id(cpu);
}
env->nr_cpus_avail = nr_cpus;
@@ -285,9 +289,9 @@ int perf_env__nr_cpus_avail(struct perf_env *env)
void cpu_cache_level__free(struct cpu_cache_level *cache)
{
- free(cache->type);
- free(cache->map);
- free(cache->size);
+ zfree(&cache->type);
+ zfree(&cache->map);
+ zfree(&cache->size);
}
/*
diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h
index 271a90b326c4..d8e083d42610 100644
--- a/tools/perf/util/env.h
+++ b/tools/perf/util/env.h
@@ -9,6 +9,7 @@
struct cpu_topology_map {
int socket_id;
+ int die_id;
int core_id;
};
@@ -26,7 +27,7 @@ struct numa_node {
u32 node;
u64 mem_total;
u64 mem_free;
- struct cpu_map *map;
+ struct perf_cpu_map *map;
};
struct memory_node {
@@ -49,6 +50,7 @@ struct perf_env {
int nr_cmdline;
int nr_sibling_cores;
+ int nr_sibling_dies;
int nr_sibling_threads;
int nr_numa_nodes;
int nr_memory_nodes;
@@ -57,6 +59,7 @@ struct perf_env {
char *cmdline;
const char **cmdline_argv;
char *sibling_cores;
+ char *sibling_dies;
char *sibling_threads;
char *pmu_mappings;
struct cpu_topology_map *cpu;
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index d1ad6c419724..f4afbb858ebb 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -1,4 +1,3 @@
-// SPDX-License-Identifier: GPL-2.0
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -11,6 +10,8 @@
#include <uapi/linux/mman.h> /* To get things like MAP_HUGETLB even on older libc headers */
#include <api/fs/fs.h>
#include <linux/perf_event.h>
+#include <linux/zalloc.h>
+#include "dso.h"
#include "event.h"
#include "debug.h"
#include "hist.h"
@@ -20,7 +21,8 @@
#include "strlist.h"
#include "thread.h"
#include "thread_map.h"
-#include "sane_ctype.h"
+#include "time-utils.h"
+#include <linux/ctype.h>
#include "map.h"
#include "symbol.h"
#include "symbol/kallsyms.h"
@@ -28,6 +30,8 @@
#include "stat.h"
#include "session.h"
#include "bpf-event.h"
+#include "tool.h"
+#include "../perf.h"
#define DEFAULT_PROC_MAP_PARSE_TIMEOUT 500
@@ -158,9 +162,7 @@ static int perf_event__get_comm_ids(pid_t pid, char *comm, size_t len,
if (name) {
char *nl;
- name += 5; /* strlen("Name:") */
- name = ltrim(name);
-
+ name = skip_spaces(name + 5); /* strlen("Name:") */
nl = strchr(name, '\n');
if (nl)
*nl = '\0';
@@ -388,7 +390,7 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
strcpy(execname, "");
/* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
- n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %x:%x %u %[^\n]\n",
+ n = sscanf(bf, "%"PRI_lx64"-%"PRI_lx64" %s %"PRI_lx64" %x:%x %u %[^\n]\n",
&event->mmap2.start, &event->mmap2.len, prot,
&event->mmap2.pgoff, &event->mmap2.maj,
&event->mmap2.min,
@@ -617,7 +619,7 @@ static int __event__synthesize_thread(union perf_event *comm_event,
}
int perf_event__synthesize_thread_map(struct perf_tool *tool,
- struct thread_map *threads,
+ struct perf_thread_map *threads,
perf_event__handler_t process,
struct machine *machine,
bool mmap_data)
@@ -648,7 +650,7 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool,
for (thread = 0; thread < threads->nr; ++thread) {
if (__event__synthesize_thread(comm_event, mmap_event,
fork_event, namespaces_event,
- thread_map__pid(threads, thread), 0,
+ perf_thread_map__pid(threads, thread), 0,
process, tool, machine,
mmap_data)) {
err = -1;
@@ -659,12 +661,12 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool,
* comm.pid is set to thread group id by
* perf_event__synthesize_comm
*/
- if ((int) comm_event->comm.pid != thread_map__pid(threads, thread)) {
+ if ((int) comm_event->comm.pid != perf_thread_map__pid(threads, thread)) {
bool need_leader = true;
/* is thread group leader in thread_map? */
for (j = 0; j < threads->nr; ++j) {
- if ((int) comm_event->comm.pid == thread_map__pid(threads, j)) {
+ if ((int) comm_event->comm.pid == perf_thread_map__pid(threads, j)) {
need_leader = false;
break;
}
@@ -857,7 +859,7 @@ free_threads:
free(synthesize_threads);
free_dirent:
for (i = 0; i < n; i++)
- free(dirent[i]);
+ zfree(&dirent[i]);
free(dirent);
return err;
@@ -914,11 +916,13 @@ static int __perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
int err;
union perf_event *event;
- if (symbol_conf.kptr_restrict)
- return -1;
if (map == NULL)
return -1;
+ kmap = map__kmap(map);
+ if (!kmap->ref_reloc_sym)
+ return -1;
+
/*
* We should get this from /sys/kernel/sections/.text, but till that is
* available use this, and after it is use this as a fallback for older
@@ -941,7 +945,6 @@ static int __perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL;
}
- kmap = map__kmap(map);
size = snprintf(event->mmap.filename, sizeof(event->mmap.filename),
"%s%s", machine->mmap_name, kmap->ref_reloc_sym->name) + 1;
size = PERF_ALIGN(size, sizeof(u64));
@@ -973,7 +976,7 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
}
int perf_event__synthesize_thread_map2(struct perf_tool *tool,
- struct thread_map *threads,
+ struct perf_thread_map *threads,
perf_event__handler_t process,
struct machine *machine)
{
@@ -992,13 +995,13 @@ int perf_event__synthesize_thread_map2(struct perf_tool *tool,
event->thread_map.nr = threads->nr;
for (i = 0; i < threads->nr; i++) {
- struct thread_map_event_entry *entry = &event->thread_map.entries[i];
- char *comm = thread_map__comm(threads, i);
+ struct perf_record_thread_map_entry *entry = &event->thread_map.entries[i];
+ char *comm = perf_thread_map__comm(threads, i);
if (!comm)
comm = (char *) "";
- entry->pid = thread_map__pid(threads, i);
+ entry->pid = perf_thread_map__pid(threads, i);
strncpy((char *) &entry->comm, comm, sizeof(entry->comm));
}
@@ -1009,7 +1012,7 @@ int perf_event__synthesize_thread_map2(struct perf_tool *tool,
}
static void synthesize_cpus(struct cpu_map_entries *cpus,
- struct cpu_map *map)
+ struct perf_cpu_map *map)
{
int i;
@@ -1019,8 +1022,8 @@ static void synthesize_cpus(struct cpu_map_entries *cpus,
cpus->cpu[i] = map->map[i];
}
-static void synthesize_mask(struct cpu_map_mask *mask,
- struct cpu_map *map, int max)
+static void synthesize_mask(struct perf_record_record_cpu_map *mask,
+ struct perf_cpu_map *map, int max)
{
int i;
@@ -1031,12 +1034,12 @@ static void synthesize_mask(struct cpu_map_mask *mask,
set_bit(map->map[i], mask->mask);
}
-static size_t cpus_size(struct cpu_map *map)
+static size_t cpus_size(struct perf_cpu_map *map)
{
return sizeof(struct cpu_map_entries) + map->nr * sizeof(u16);
}
-static size_t mask_size(struct cpu_map *map, int *max)
+static size_t mask_size(struct perf_cpu_map *map, int *max)
{
int i;
@@ -1050,26 +1053,26 @@ static size_t mask_size(struct cpu_map *map, int *max)
*max = bit;
}
- return sizeof(struct cpu_map_mask) + BITS_TO_LONGS(*max) * sizeof(long);
+ return sizeof(struct perf_record_record_cpu_map) + BITS_TO_LONGS(*max) * sizeof(long);
}
-void *cpu_map_data__alloc(struct cpu_map *map, size_t *size, u16 *type, int *max)
+void *cpu_map_data__alloc(struct perf_cpu_map *map, size_t *size, u16 *type, int *max)
{
size_t size_cpus, size_mask;
- bool is_dummy = cpu_map__empty(map);
+ bool is_dummy = perf_cpu_map__empty(map);
/*
* Both array and mask data have variable size based
* on the number of cpus and their actual values.
- * The size of the 'struct cpu_map_data' is:
+ * The size of the 'struct perf_record_cpu_map_data' is:
*
* array = size of 'struct cpu_map_entries' +
* number of cpus * sizeof(u64)
*
- * mask = size of 'struct cpu_map_mask' +
+ * mask = size of 'struct perf_record_record_cpu_map' +
* maximum cpu bit converted to size of longs
*
- * and finaly + the size of 'struct cpu_map_data'.
+ * and finaly + the size of 'struct perf_record_cpu_map_data'.
*/
size_cpus = cpus_size(map);
size_mask = mask_size(map, max);
@@ -1082,12 +1085,12 @@ void *cpu_map_data__alloc(struct cpu_map *map, size_t *size, u16 *type, int *max
*type = PERF_CPU_MAP__MASK;
}
- *size += sizeof(struct cpu_map_data);
+ *size += sizeof(struct perf_record_cpu_map_data);
*size = PERF_ALIGN(*size, sizeof(u64));
return zalloc(*size);
}
-void cpu_map_data__synthesize(struct cpu_map_data *data, struct cpu_map *map,
+void cpu_map_data__synthesize(struct perf_record_cpu_map_data *data, struct perf_cpu_map *map,
u16 type, int max)
{
data->type = type;
@@ -1097,16 +1100,16 @@ void cpu_map_data__synthesize(struct cpu_map_data *data, struct cpu_map *map,
synthesize_cpus((struct cpu_map_entries *) data->data, map);
break;
case PERF_CPU_MAP__MASK:
- synthesize_mask((struct cpu_map_mask *) data->data, map, max);
+ synthesize_mask((struct perf_record_record_cpu_map *)data->data, map, max);
default:
break;
};
}
-static struct cpu_map_event* cpu_map_event__new(struct cpu_map *map)
+static struct perf_record_cpu_map *cpu_map_event__new(struct perf_cpu_map *map)
{
- size_t size = sizeof(struct cpu_map_event);
- struct cpu_map_event *event;
+ size_t size = sizeof(struct perf_record_cpu_map);
+ struct perf_record_cpu_map *event;
int max;
u16 type;
@@ -1123,11 +1126,11 @@ static struct cpu_map_event* cpu_map_event__new(struct cpu_map *map)
}
int perf_event__synthesize_cpu_map(struct perf_tool *tool,
- struct cpu_map *map,
+ struct perf_cpu_map *map,
perf_event__handler_t process,
struct machine *machine)
{
- struct cpu_map_event *event;
+ struct perf_record_cpu_map *event;
int err;
event = cpu_map_event__new(map);
@@ -1145,7 +1148,7 @@ int perf_event__synthesize_stat_config(struct perf_tool *tool,
perf_event__handler_t process,
struct machine *machine)
{
- struct stat_config_event *event;
+ struct perf_record_stat_config *event;
int size, i = 0, err;
size = sizeof(*event);
@@ -1184,7 +1187,7 @@ int perf_event__synthesize_stat(struct perf_tool *tool,
perf_event__handler_t process,
struct machine *machine)
{
- struct stat_event event;
+ struct perf_record_stat event;
event.header.type = PERF_RECORD_STAT;
event.header.size = sizeof(event);
@@ -1205,7 +1208,7 @@ int perf_event__synthesize_stat_round(struct perf_tool *tool,
perf_event__handler_t process,
struct machine *machine)
{
- struct stat_round_event event;
+ struct perf_record_stat_round event;
event.header.type = PERF_RECORD_STAT_ROUND;
event.header.size = sizeof(event);
@@ -1218,7 +1221,7 @@ int perf_event__synthesize_stat_round(struct perf_tool *tool,
}
void perf_event__read_stat_config(struct perf_stat_config *config,
- struct stat_config_event *event)
+ struct perf_record_stat_config *event)
{
unsigned i;
@@ -1235,7 +1238,7 @@ void perf_event__read_stat_config(struct perf_stat_config *config,
CASE(INTERVAL, interval)
#undef CASE
default:
- pr_warning("unknown stat config term %" PRIu64 "\n",
+ pr_warning("unknown stat config term %" PRI_lu64 "\n",
event->data[i].tag);
}
}
@@ -1344,17 +1347,17 @@ int perf_event__process_ksymbol(struct perf_tool *tool __maybe_unused,
return machine__process_ksymbol(machine, event, sample);
}
-int perf_event__process_bpf_event(struct perf_tool *tool __maybe_unused,
- union perf_event *event,
- struct perf_sample *sample __maybe_unused,
- struct machine *machine)
+int perf_event__process_bpf(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct machine *machine)
{
- return machine__process_bpf_event(machine, event, sample);
+ return machine__process_bpf(machine, event, sample);
}
size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp)
{
- return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %c %s\n",
+ return fprintf(fp, " %d/%d: [%#" PRI_lx64 "(%#" PRI_lx64 ") @ %#" PRI_lx64 "]: %c %s\n",
event->mmap.pid, event->mmap.tid, event->mmap.start,
event->mmap.len, event->mmap.pgoff,
(event->header.misc & PERF_RECORD_MISC_MMAP_DATA) ? 'r' : 'x',
@@ -1363,8 +1366,8 @@ size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp)
size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp)
{
- return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64
- " %02x:%02x %"PRIu64" %"PRIu64"]: %c%c%c%c %s\n",
+ return fprintf(fp, " %d/%d: [%#" PRI_lx64 "(%#" PRI_lx64 ") @ %#" PRI_lx64
+ " %02x:%02x %"PRI_lu64" %"PRI_lu64"]: %c%c%c%c %s\n",
event->mmap2.pid, event->mmap2.tid, event->mmap2.start,
event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj,
event->mmap2.min, event->mmap2.ino,
@@ -1378,7 +1381,7 @@ size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp)
size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp)
{
- struct thread_map *threads = thread_map__new_event(&event->thread_map);
+ struct perf_thread_map *threads = thread_map__new_event(&event->thread_map);
size_t ret;
ret = fprintf(fp, " nr: ");
@@ -1388,13 +1391,13 @@ size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp)
else
ret += fprintf(fp, "failed to get threads from event\n");
- thread_map__put(threads);
+ perf_thread_map__put(threads);
return ret;
}
size_t perf_event__fprintf_cpu_map(union perf_event *event, FILE *fp)
{
- struct cpu_map *cpus = cpu_map__new_data(&event->cpu_map.data);
+ struct perf_cpu_map *cpus = cpu_map__new_data(&event->cpu_map.data);
size_t ret;
ret = fprintf(fp, ": ");
@@ -1404,7 +1407,7 @@ size_t perf_event__fprintf_cpu_map(union perf_event *event, FILE *fp)
else
ret += fprintf(fp, "failed to get cpumap from event\n");
- cpu_map__put(cpus);
+ perf_cpu_map__put(cpus);
return ret;
}
@@ -1449,7 +1452,7 @@ int perf_event__process_exit(struct perf_tool *tool __maybe_unused,
size_t perf_event__fprintf_aux(union perf_event *event, FILE *fp)
{
- return fprintf(fp, " offset: %#"PRIx64" size: %#"PRIx64" flags: %#"PRIx64" [%s%s%s]\n",
+ return fprintf(fp, " offset: %#"PRI_lx64" size: %#"PRI_lx64" flags: %#"PRI_lx64" [%s%s%s]\n",
event->aux.aux_offset, event->aux.aux_size,
event->aux.flags,
event->aux.flags & PERF_AUX_FLAG_TRUNCATED ? "T" : "",
@@ -1481,22 +1484,21 @@ size_t perf_event__fprintf_switch(union perf_event *event, FILE *fp)
static size_t perf_event__fprintf_lost(union perf_event *event, FILE *fp)
{
- return fprintf(fp, " lost %" PRIu64 "\n", event->lost.lost);
+ return fprintf(fp, " lost %" PRI_lu64 "\n", event->lost.lost);
}
size_t perf_event__fprintf_ksymbol(union perf_event *event, FILE *fp)
{
- return fprintf(fp, " ksymbol event with addr %" PRIx64 " len %u type %u flags 0x%x name %s\n",
- event->ksymbol_event.addr, event->ksymbol_event.len,
- event->ksymbol_event.ksym_type,
- event->ksymbol_event.flags, event->ksymbol_event.name);
+ return fprintf(fp, " addr %" PRI_lx64 " len %u type %u flags 0x%x name %s\n",
+ event->ksymbol.addr, event->ksymbol.len,
+ event->ksymbol.ksym_type,
+ event->ksymbol.flags, event->ksymbol.name);
}
-size_t perf_event__fprintf_bpf_event(union perf_event *event, FILE *fp)
+size_t perf_event__fprintf_bpf(union perf_event *event, FILE *fp)
{
- return fprintf(fp, " bpf event with type %u, flags %u, id %u\n",
- event->bpf_event.type, event->bpf_event.flags,
- event->bpf_event.id);
+ return fprintf(fp, " type %u, flags %u, id %u\n",
+ event->bpf.type, event->bpf.flags, event->bpf.id);
}
size_t perf_event__fprintf(union perf_event *event, FILE *fp)
@@ -1538,7 +1540,7 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp)
ret += perf_event__fprintf_ksymbol(event, fp);
break;
case PERF_RECORD_BPF_EVENT:
- ret += perf_event__fprintf_bpf_event(event, fp);
+ ret += perf_event__fprintf_bpf(event, fp);
break;
default:
ret += fprintf(fp, "\n");
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 9e999550f247..47ad81d47b1a 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -1,114 +1,39 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __PERF_RECORD_H
#define __PERF_RECORD_H
-
-#include <limits.h>
+/*
+ * The linux/stddef.h isn't need here, but is needed for __always_inline used
+ * in files included from uapi/linux/perf_event.h such as
+ * /usr/include/linux/swab.h and /usr/include/linux/byteorder/little_endian.h,
+ * detected in at least musl libc, used in Alpine Linux. -acme
+ */
#include <stdio.h>
-#include <linux/kernel.h>
-#include <linux/bpf.h>
-#include <linux/perf_event.h>
+#include <linux/stddef.h>
+#include <perf/event.h>
+#include <linux/types.h>
-#include "../perf.h"
-#include "build-id.h"
#include "perf_regs.h"
-struct mmap_event {
- struct perf_event_header header;
- u32 pid, tid;
- u64 start;
- u64 len;
- u64 pgoff;
- char filename[PATH_MAX];
-};
-
-struct mmap2_event {
- struct perf_event_header header;
- u32 pid, tid;
- u64 start;
- u64 len;
- u64 pgoff;
- u32 maj;
- u32 min;
- u64 ino;
- u64 ino_generation;
- u32 prot;
- u32 flags;
- char filename[PATH_MAX];
-};
-
-struct comm_event {
- struct perf_event_header header;
- u32 pid, tid;
- char comm[16];
-};
-
-struct namespaces_event {
- struct perf_event_header header;
- u32 pid, tid;
- u64 nr_namespaces;
- struct perf_ns_link_info link_info[];
-};
-
-struct fork_event {
- struct perf_event_header header;
- u32 pid, ppid;
- u32 tid, ptid;
- u64 time;
-};
-
-struct lost_event {
- struct perf_event_header header;
- u64 id;
- u64 lost;
-};
-
-struct lost_samples_event {
- struct perf_event_header header;
- u64 lost;
-};
+struct dso;
+struct machine;
+struct perf_event_attr;
+#ifdef __LP64__
/*
- * PERF_FORMAT_ENABLED | PERF_FORMAT_RUNNING | PERF_FORMAT_ID
+ * /usr/include/inttypes.h uses just 'lu' for PRIu64, but we end up defining
+ * __u64 as long long unsigned int, and then -Werror=format= kicks in and
+ * complains of the mismatched types, so use these two special extra PRI
+ * macros to overcome that.
*/
-struct read_event {
- struct perf_event_header header;
- u32 pid, tid;
- u64 value;
- u64 time_enabled;
- u64 time_running;
- u64 id;
-};
-
-struct throttle_event {
- struct perf_event_header header;
- u64 time;
- u64 id;
- u64 stream_id;
-};
-
-#ifndef KSYM_NAME_LEN
-#define KSYM_NAME_LEN 256
+#define PRI_lu64 "l" PRIu64
+#define PRI_lx64 "l" PRIx64
+#define PRI_ld64 "l" PRId64
+#else
+#define PRI_lu64 PRIu64
+#define PRI_lx64 PRIx64
+#define PRI_ld64 PRId64
#endif
-struct ksymbol_event {
- struct perf_event_header header;
- u64 addr;
- u32 len;
- u16 ksym_type;
- u16 flags;
- char name[KSYM_NAME_LEN];
-};
-
-struct bpf_event {
- struct perf_event_header header;
- u16 type;
- u16 flags;
- u32 id;
-
- /* for bpf_prog types */
- u8 tag[BPF_TAG_SIZE]; // prog tag
-};
-
#define PERF_SAMPLE_MASK \
(PERF_SAMPLE_IP | PERF_SAMPLE_TID | \
PERF_SAMPLE_TIME | PERF_SAMPLE_ADDR | \
@@ -119,11 +44,6 @@ struct bpf_event {
/* perf sample has 16 bits size limit */
#define PERF_SAMPLE_MAX_SIZE (1 << 16)
-struct sample_event {
- struct perf_event_header header;
- u64 array[];
-};
-
struct regs_dump {
u64 abi;
u64 mask;
@@ -204,6 +124,8 @@ struct perf_sample {
u64 period;
u64 weight;
u64 transaction;
+ u64 insn_cnt;
+ u64 cyc_cnt;
u32 cpu;
u32 raw_size;
u64 data_src;
@@ -229,41 +151,6 @@ struct perf_sample {
PERF_MEM_S(LOCK, NA) |\
PERF_MEM_S(TLB, NA))
-struct build_id_event {
- struct perf_event_header header;
- pid_t pid;
- u8 build_id[PERF_ALIGN(BUILD_ID_SIZE, sizeof(u64))];
- char filename[];
-};
-
-enum perf_user_event_type { /* above any possible kernel type */
- PERF_RECORD_USER_TYPE_START = 64,
- PERF_RECORD_HEADER_ATTR = 64,
- PERF_RECORD_HEADER_EVENT_TYPE = 65, /* deprecated */
- PERF_RECORD_HEADER_TRACING_DATA = 66,
- PERF_RECORD_HEADER_BUILD_ID = 67,
- PERF_RECORD_FINISHED_ROUND = 68,
- PERF_RECORD_ID_INDEX = 69,
- PERF_RECORD_AUXTRACE_INFO = 70,
- PERF_RECORD_AUXTRACE = 71,
- PERF_RECORD_AUXTRACE_ERROR = 72,
- PERF_RECORD_THREAD_MAP = 73,
- PERF_RECORD_CPU_MAP = 74,
- PERF_RECORD_STAT_CONFIG = 75,
- PERF_RECORD_STAT = 76,
- PERF_RECORD_STAT_ROUND = 77,
- PERF_RECORD_EVENT_UPDATE = 78,
- PERF_RECORD_TIME_CONV = 79,
- PERF_RECORD_HEADER_FEATURE = 80,
- PERF_RECORD_COMPRESSED = 81,
- PERF_RECORD_HEADER_MAX
-};
-
-enum auxtrace_error_type {
- PERF_AUXTRACE_ERROR_ITRACE = 1,
- PERF_AUXTRACE_ERROR_MAX
-};
-
/* Attribute type for custom synthesized events */
#define PERF_TYPE_SYNTH (INT_MAX + 1U)
@@ -385,295 +272,16 @@ static inline void *perf_synth__raw_data(void *p)
#define perf_sample__bad_synth_size(s, d) ((s)->raw_size < sizeof(d) - 4)
-/*
- * The kernel collects the number of events it couldn't send in a stretch and
- * when possible sends this number in a PERF_RECORD_LOST event. The number of
- * such "chunks" of lost events is stored in .nr_events[PERF_EVENT_LOST] while
- * total_lost tells exactly how many events the kernel in fact lost, i.e. it is
- * the sum of all struct lost_event.lost fields reported.
- *
- * The kernel discards mixed up samples and sends the number in a
- * PERF_RECORD_LOST_SAMPLES event. The number of lost-samples events is stored
- * in .nr_events[PERF_RECORD_LOST_SAMPLES] while total_lost_samples tells
- * exactly how many samples the kernel in fact dropped, i.e. it is the sum of
- * all struct lost_samples_event.lost fields reported.
- *
- * The total_period is needed because by default auto-freq is used, so
- * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get
- * the total number of low level events, it is necessary to to sum all struct
- * sample_event.period and stash the result in total_period.
- */
-struct events_stats {
- u64 total_period;
- u64 total_non_filtered_period;
- u64 total_lost;
- u64 total_lost_samples;
- u64 total_aux_lost;
- u64 total_aux_partial;
- u64 total_invalid_chains;
- u32 nr_events[PERF_RECORD_HEADER_MAX];
- u32 nr_non_filtered_samples;
- u32 nr_lost_warned;
- u32 nr_unknown_events;
- u32 nr_invalid_chains;
- u32 nr_unknown_id;
- u32 nr_unprocessable_samples;
- u32 nr_auxtrace_errors[PERF_AUXTRACE_ERROR_MAX];
- u32 nr_proc_map_timeout;
-};
-
-enum {
- PERF_CPU_MAP__CPUS = 0,
- PERF_CPU_MAP__MASK = 1,
-};
-
-struct cpu_map_entries {
- u16 nr;
- u16 cpu[];
-};
-
-struct cpu_map_mask {
- u16 nr;
- u16 long_size;
- unsigned long mask[];
-};
-
-struct cpu_map_data {
- u16 type;
- char data[];
-};
-
-struct cpu_map_event {
- struct perf_event_header header;
- struct cpu_map_data data;
-};
-
-struct attr_event {
- struct perf_event_header header;
- struct perf_event_attr attr;
- u64 id[];
-};
-
-enum {
- PERF_EVENT_UPDATE__UNIT = 0,
- PERF_EVENT_UPDATE__SCALE = 1,
- PERF_EVENT_UPDATE__NAME = 2,
- PERF_EVENT_UPDATE__CPUS = 3,
-};
-
-struct event_update_event_cpus {
- struct cpu_map_data cpus;
-};
-
-struct event_update_event_scale {
- double scale;
-};
-
-struct event_update_event {
- struct perf_event_header header;
- u64 type;
- u64 id;
-
- char data[];
-};
-
-#define MAX_EVENT_NAME 64
-
-struct perf_trace_event_type {
- u64 event_id;
- char name[MAX_EVENT_NAME];
-};
-
-struct event_type_event {
- struct perf_event_header header;
- struct perf_trace_event_type event_type;
-};
-
-struct tracing_data_event {
- struct perf_event_header header;
- u32 size;
-};
-
-struct id_index_entry {
- u64 id;
- u64 idx;
- u64 cpu;
- u64 tid;
-};
-
-struct id_index_event {
- struct perf_event_header header;
- u64 nr;
- struct id_index_entry entries[0];
-};
-
-struct auxtrace_info_event {
- struct perf_event_header header;
- u32 type;
- u32 reserved__; /* For alignment */
- u64 priv[];
-};
-
-struct auxtrace_event {
- struct perf_event_header header;
- u64 size;
- u64 offset;
- u64 reference;
- u32 idx;
- u32 tid;
- u32 cpu;
- u32 reserved__; /* For alignment */
-};
-
-#define MAX_AUXTRACE_ERROR_MSG 64
-
-struct auxtrace_error_event {
- struct perf_event_header header;
- u32 type;
- u32 code;
- u32 cpu;
- u32 pid;
- u32 tid;
- u32 fmt;
- u64 ip;
- u64 time;
- char msg[MAX_AUXTRACE_ERROR_MSG];
-};
-
-struct aux_event {
- struct perf_event_header header;
- u64 aux_offset;
- u64 aux_size;
- u64 flags;
-};
-
-struct itrace_start_event {
- struct perf_event_header header;
- u32 pid, tid;
-};
-
-struct context_switch_event {
- struct perf_event_header header;
- u32 next_prev_pid;
- u32 next_prev_tid;
-};
-
-struct thread_map_event_entry {
- u64 pid;
- char comm[16];
-};
-
-struct thread_map_event {
- struct perf_event_header header;
- u64 nr;
- struct thread_map_event_entry entries[];
-};
-
-enum {
- PERF_STAT_CONFIG_TERM__AGGR_MODE = 0,
- PERF_STAT_CONFIG_TERM__INTERVAL = 1,
- PERF_STAT_CONFIG_TERM__SCALE = 2,
- PERF_STAT_CONFIG_TERM__MAX = 3,
-};
-
-struct stat_config_event_entry {
- u64 tag;
- u64 val;
-};
-
-struct stat_config_event {
- struct perf_event_header header;
- u64 nr;
- struct stat_config_event_entry data[];
-};
-
-struct stat_event {
- struct perf_event_header header;
-
- u64 id;
- u32 cpu;
- u32 thread;
-
- union {
- struct {
- u64 val;
- u64 ena;
- u64 run;
- };
- u64 values[3];
- };
-};
-
enum {
PERF_STAT_ROUND_TYPE__INTERVAL = 0,
PERF_STAT_ROUND_TYPE__FINAL = 1,
};
-struct stat_round_event {
- struct perf_event_header header;
- u64 type;
- u64 time;
-};
-
-struct time_conv_event {
- struct perf_event_header header;
- u64 time_shift;
- u64 time_mult;
- u64 time_zero;
-};
-
-struct feature_event {
- struct perf_event_header header;
- u64 feat_id;
- char data[];
-};
-
-struct compressed_event {
- struct perf_event_header header;
- char data[];
-};
-
-union perf_event {
- struct perf_event_header header;
- struct mmap_event mmap;
- struct mmap2_event mmap2;
- struct comm_event comm;
- struct namespaces_event namespaces;
- struct fork_event fork;
- struct lost_event lost;
- struct lost_samples_event lost_samples;
- struct read_event read;
- struct throttle_event throttle;
- struct sample_event sample;
- struct attr_event attr;
- struct event_update_event event_update;
- struct event_type_event event_type;
- struct tracing_data_event tracing_data;
- struct build_id_event build_id;
- struct id_index_event id_index;
- struct auxtrace_info_event auxtrace_info;
- struct auxtrace_event auxtrace;
- struct auxtrace_error_event auxtrace_error;
- struct aux_event aux;
- struct itrace_start_event itrace_start;
- struct context_switch_event context_switch;
- struct thread_map_event thread_map;
- struct cpu_map_event cpu_map;
- struct stat_config_event stat_config;
- struct stat_event stat;
- struct stat_round_event stat_round;
- struct time_conv_event time_conv;
- struct feature_event feat;
- struct ksymbol_event ksymbol_event;
- struct bpf_event bpf_event;
- struct compressed_event pack;
-};
-
void perf_event__print_totals(void);
struct perf_tool;
-struct thread_map;
-struct cpu_map;
+struct perf_thread_map;
+struct perf_cpu_map;
struct perf_stat_config;
struct perf_counts_values;
@@ -683,15 +291,15 @@ typedef int (*perf_event__handler_t)(struct perf_tool *tool,
struct machine *machine);
int perf_event__synthesize_thread_map(struct perf_tool *tool,
- struct thread_map *threads,
+ struct perf_thread_map *threads,
perf_event__handler_t process,
struct machine *machine, bool mmap_data);
int perf_event__synthesize_thread_map2(struct perf_tool *tool,
- struct thread_map *threads,
+ struct perf_thread_map *threads,
perf_event__handler_t process,
struct machine *machine);
int perf_event__synthesize_cpu_map(struct perf_tool *tool,
- struct cpu_map *cpus,
+ struct perf_cpu_map *cpus,
perf_event__handler_t process,
struct machine *machine);
int perf_event__synthesize_threads(struct perf_tool *tool,
@@ -706,7 +314,7 @@ int perf_event__synthesize_stat_config(struct perf_tool *tool,
perf_event__handler_t process,
struct machine *machine);
void perf_event__read_stat_config(struct perf_stat_config *config,
- struct stat_config_event *event);
+ struct perf_record_stat_config *event);
int perf_event__synthesize_stat(struct perf_tool *tool,
u32 cpu, u32 thread, u64 id,
struct perf_counts_values *count,
@@ -768,10 +376,10 @@ int perf_event__process_ksymbol(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
struct machine *machine);
-int perf_event__process_bpf_event(struct perf_tool *tool,
- union perf_event *event,
- struct perf_sample *sample,
- struct machine *machine);
+int perf_event__process_bpf(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct machine *machine);
int perf_tool__process_synth_event(struct perf_tool *tool,
union perf_event *event,
struct machine *machine,
@@ -836,19 +444,20 @@ size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp);
size_t perf_event__fprintf_cpu_map(union perf_event *event, FILE *fp);
size_t perf_event__fprintf_namespaces(union perf_event *event, FILE *fp);
size_t perf_event__fprintf_ksymbol(union perf_event *event, FILE *fp);
-size_t perf_event__fprintf_bpf_event(union perf_event *event, FILE *fp);
+size_t perf_event__fprintf_bpf(union perf_event *event, FILE *fp);
size_t perf_event__fprintf(union perf_event *event, FILE *fp);
int kallsyms__get_function_start(const char *kallsyms_filename,
const char *symbol_name, u64 *addr);
-void *cpu_map_data__alloc(struct cpu_map *map, size_t *size, u16 *type, int *max);
-void cpu_map_data__synthesize(struct cpu_map_data *data, struct cpu_map *map,
+void *cpu_map_data__alloc(struct perf_cpu_map *map, size_t *size, u16 *type, int *max);
+void cpu_map_data__synthesize(struct perf_record_cpu_map_data *data, struct perf_cpu_map *map,
u16 type, int max);
void event_attr_init(struct perf_event_attr *attr);
int perf_event_paranoid(void);
+bool perf_event_paranoid_check(int max_level);
extern int sysctl_perf_event_max_stack;
extern int sysctl_perf_event_max_contexts_per_stack;
diff --git a/tools/perf/util/events_stats.h b/tools/perf/util/events_stats.h
new file mode 100644
index 000000000000..859cb34fcff2
--- /dev/null
+++ b/tools/perf/util/events_stats.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PERF_EVENTS_STATS_
+#define __PERF_EVENTS_STATS_
+
+#include <stdio.h>
+#include <perf/event.h>
+#include <linux/types.h>
+#include "auxtrace.h"
+
+/*
+ * The kernel collects the number of events it couldn't send in a stretch and
+ * when possible sends this number in a PERF_RECORD_LOST event. The number of
+ * such "chunks" of lost events is stored in .nr_events[PERF_EVENT_LOST] while
+ * total_lost tells exactly how many events the kernel in fact lost, i.e. it is
+ * the sum of all struct perf_record_lost.lost fields reported.
+ *
+ * The kernel discards mixed up samples and sends the number in a
+ * PERF_RECORD_LOST_SAMPLES event. The number of lost-samples events is stored
+ * in .nr_events[PERF_RECORD_LOST_SAMPLES] while total_lost_samples tells
+ * exactly how many samples the kernel in fact dropped, i.e. it is the sum of
+ * all struct perf_record_lost_samples.lost fields reported.
+ *
+ * The total_period is needed because by default auto-freq is used, so
+ * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get
+ * the total number of low level events, it is necessary to to sum all struct
+ * perf_record_sample.period and stash the result in total_period.
+ */
+struct events_stats {
+ u64 total_period;
+ u64 total_non_filtered_period;
+ u64 total_lost;
+ u64 total_lost_samples;
+ u64 total_aux_lost;
+ u64 total_aux_partial;
+ u64 total_invalid_chains;
+ u32 nr_events[PERF_RECORD_HEADER_MAX];
+ u32 nr_non_filtered_samples;
+ u32 nr_lost_warned;
+ u32 nr_unknown_events;
+ u32 nr_invalid_chains;
+ u32 nr_unknown_id;
+ u32 nr_unprocessable_samples;
+ u32 nr_auxtrace_errors[PERF_AUXTRACE_ERROR_MAX];
+ u32 nr_proc_map_timeout;
+};
+
+void events_stats__inc(struct events_stats *stats, u32 type);
+
+size_t events_stats__fprintf(struct events_stats *stats, FILE *fp);
+
+#endif /* __PERF_EVENTS_STATS_ */
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index 69d0fa8ab16f..095924aa186b 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -1,12 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
*
* Parts came from builtin-{top,stat,record}.c, see those files for further
* copyright notes.
- *
- * Released under the GPL v2. (and only v2, not any later version)
*/
-#include "util.h"
#include <api/fs/fs.h>
#include <errno.h>
#include <inttypes.h>
@@ -18,10 +16,14 @@
#include "evsel.h"
#include "debug.h"
#include "units.h"
+#include "util.h"
+#include "../perf.h"
#include "asm/bug.h"
#include "bpf-event.h"
#include <signal.h>
#include <unistd.h>
+#include <sched.h>
+#include <stdlib.h>
#include "parse-events.h"
#include <subcmd/parse-options.h>
@@ -34,56 +36,63 @@
#include <linux/hash.h>
#include <linux/log2.h>
#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/zalloc.h>
+#include <perf/evlist.h>
+#include <perf/evsel.h>
+#include <perf/cpumap.h>
+
+#include <internal/xyarray.h>
#ifdef LACKS_SIGQUEUE_PROTOTYPE
int sigqueue(pid_t pid, int sig, const union sigval value);
#endif
-#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
+#define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y))
#define SID(e, x, y) xyarray__entry(e->sample_id, x, y)
-void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus,
- struct thread_map *threads)
+void evlist__init(struct evlist *evlist, struct perf_cpu_map *cpus,
+ struct perf_thread_map *threads)
{
int i;
for (i = 0; i < PERF_EVLIST__HLIST_SIZE; ++i)
INIT_HLIST_HEAD(&evlist->heads[i]);
- INIT_LIST_HEAD(&evlist->entries);
- perf_evlist__set_maps(evlist, cpus, threads);
+ perf_evlist__init(&evlist->core);
+ perf_evlist__set_maps(&evlist->core, cpus, threads);
fdarray__init(&evlist->pollfd, 64);
evlist->workload.pid = -1;
evlist->bkw_mmap_state = BKW_MMAP_NOTREADY;
}
-struct perf_evlist *perf_evlist__new(void)
+struct evlist *evlist__new(void)
{
- struct perf_evlist *evlist = zalloc(sizeof(*evlist));
+ struct evlist *evlist = zalloc(sizeof(*evlist));
if (evlist != NULL)
- perf_evlist__init(evlist, NULL, NULL);
+ evlist__init(evlist, NULL, NULL);
return evlist;
}
-struct perf_evlist *perf_evlist__new_default(void)
+struct evlist *perf_evlist__new_default(void)
{
- struct perf_evlist *evlist = perf_evlist__new();
+ struct evlist *evlist = evlist__new();
if (evlist && perf_evlist__add_default(evlist)) {
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
evlist = NULL;
}
return evlist;
}
-struct perf_evlist *perf_evlist__new_dummy(void)
+struct evlist *perf_evlist__new_dummy(void)
{
- struct perf_evlist *evlist = perf_evlist__new();
+ struct evlist *evlist = evlist__new();
if (evlist && perf_evlist__add_dummy(evlist)) {
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
evlist = NULL;
}
@@ -97,17 +106,17 @@ struct perf_evlist *perf_evlist__new_dummy(void)
* Events with compatible sample types all have the same id_pos
* and is_pos. For convenience, put a copy on evlist.
*/
-void perf_evlist__set_id_pos(struct perf_evlist *evlist)
+void perf_evlist__set_id_pos(struct evlist *evlist)
{
- struct perf_evsel *first = perf_evlist__first(evlist);
+ struct evsel *first = perf_evlist__first(evlist);
evlist->id_pos = first->id_pos;
evlist->is_pos = first->is_pos;
}
-static void perf_evlist__update_id_pos(struct perf_evlist *evlist)
+static void perf_evlist__update_id_pos(struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel)
perf_evsel__calc_id_pos(evsel);
@@ -115,161 +124,132 @@ static void perf_evlist__update_id_pos(struct perf_evlist *evlist)
perf_evlist__set_id_pos(evlist);
}
-static void perf_evlist__purge(struct perf_evlist *evlist)
+static void perf_evlist__purge(struct evlist *evlist)
{
- struct perf_evsel *pos, *n;
+ struct evsel *pos, *n;
evlist__for_each_entry_safe(evlist, n, pos) {
- list_del_init(&pos->node);
+ list_del_init(&pos->core.node);
pos->evlist = NULL;
- perf_evsel__delete(pos);
+ evsel__delete(pos);
}
- evlist->nr_entries = 0;
+ evlist->core.nr_entries = 0;
}
-void perf_evlist__exit(struct perf_evlist *evlist)
+void perf_evlist__exit(struct evlist *evlist)
{
zfree(&evlist->mmap);
zfree(&evlist->overwrite_mmap);
fdarray__exit(&evlist->pollfd);
}
-void perf_evlist__delete(struct perf_evlist *evlist)
+void evlist__delete(struct evlist *evlist)
{
if (evlist == NULL)
return;
perf_evlist__munmap(evlist);
- perf_evlist__close(evlist);
- cpu_map__put(evlist->cpus);
- thread_map__put(evlist->threads);
- evlist->cpus = NULL;
- evlist->threads = NULL;
+ evlist__close(evlist);
+ perf_cpu_map__put(evlist->core.cpus);
+ perf_thread_map__put(evlist->core.threads);
+ evlist->core.cpus = NULL;
+ evlist->core.threads = NULL;
perf_evlist__purge(evlist);
perf_evlist__exit(evlist);
free(evlist);
}
-static void __perf_evlist__propagate_maps(struct perf_evlist *evlist,
- struct perf_evsel *evsel)
-{
- /*
- * We already have cpus for evsel (via PMU sysfs) so
- * keep it, if there's no target cpu list defined.
- */
- if (!evsel->own_cpus || evlist->has_user_cpus) {
- cpu_map__put(evsel->cpus);
- evsel->cpus = cpu_map__get(evlist->cpus);
- } else if (evsel->cpus != evsel->own_cpus) {
- cpu_map__put(evsel->cpus);
- evsel->cpus = cpu_map__get(evsel->own_cpus);
- }
-
- thread_map__put(evsel->threads);
- evsel->threads = thread_map__get(evlist->threads);
-}
-
-static void perf_evlist__propagate_maps(struct perf_evlist *evlist)
-{
- struct perf_evsel *evsel;
-
- evlist__for_each_entry(evlist, evsel)
- __perf_evlist__propagate_maps(evlist, evsel);
-}
-
-void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry)
+void evlist__add(struct evlist *evlist, struct evsel *entry)
{
entry->evlist = evlist;
- list_add_tail(&entry->node, &evlist->entries);
- entry->idx = evlist->nr_entries;
+ entry->idx = evlist->core.nr_entries;
entry->tracking = !entry->idx;
- if (!evlist->nr_entries++)
- perf_evlist__set_id_pos(evlist);
+ perf_evlist__add(&evlist->core, &entry->core);
- __perf_evlist__propagate_maps(evlist, entry);
+ if (evlist->core.nr_entries == 1)
+ perf_evlist__set_id_pos(evlist);
}
-void perf_evlist__remove(struct perf_evlist *evlist, struct perf_evsel *evsel)
+void evlist__remove(struct evlist *evlist, struct evsel *evsel)
{
evsel->evlist = NULL;
- list_del_init(&evsel->node);
- evlist->nr_entries -= 1;
+ perf_evlist__remove(&evlist->core, &evsel->core);
}
-void perf_evlist__splice_list_tail(struct perf_evlist *evlist,
+void perf_evlist__splice_list_tail(struct evlist *evlist,
struct list_head *list)
{
- struct perf_evsel *evsel, *temp;
+ struct evsel *evsel, *temp;
__evlist__for_each_entry_safe(list, temp, evsel) {
- list_del_init(&evsel->node);
- perf_evlist__add(evlist, evsel);
+ list_del_init(&evsel->core.node);
+ evlist__add(evlist, evsel);
}
}
void __perf_evlist__set_leader(struct list_head *list)
{
- struct perf_evsel *evsel, *leader;
+ struct evsel *evsel, *leader;
- leader = list_entry(list->next, struct perf_evsel, node);
- evsel = list_entry(list->prev, struct perf_evsel, node);
+ leader = list_entry(list->next, struct evsel, core.node);
+ evsel = list_entry(list->prev, struct evsel, core.node);
- leader->nr_members = evsel->idx - leader->idx + 1;
+ leader->core.nr_members = evsel->idx - leader->idx + 1;
__evlist__for_each_entry(list, evsel) {
evsel->leader = leader;
}
}
-void perf_evlist__set_leader(struct perf_evlist *evlist)
+void perf_evlist__set_leader(struct evlist *evlist)
{
- if (evlist->nr_entries) {
- evlist->nr_groups = evlist->nr_entries > 1 ? 1 : 0;
- __perf_evlist__set_leader(&evlist->entries);
+ if (evlist->core.nr_entries) {
+ evlist->nr_groups = evlist->core.nr_entries > 1 ? 1 : 0;
+ __perf_evlist__set_leader(&evlist->core.entries);
}
}
-int __perf_evlist__add_default(struct perf_evlist *evlist, bool precise)
+int __perf_evlist__add_default(struct evlist *evlist, bool precise)
{
- struct perf_evsel *evsel = perf_evsel__new_cycles(precise);
+ struct evsel *evsel = perf_evsel__new_cycles(precise);
if (evsel == NULL)
return -ENOMEM;
- perf_evlist__add(evlist, evsel);
+ evlist__add(evlist, evsel);
return 0;
}
-int perf_evlist__add_dummy(struct perf_evlist *evlist)
+int perf_evlist__add_dummy(struct evlist *evlist)
{
struct perf_event_attr attr = {
.type = PERF_TYPE_SOFTWARE,
.config = PERF_COUNT_SW_DUMMY,
.size = sizeof(attr), /* to capture ABI version */
};
- struct perf_evsel *evsel = perf_evsel__new_idx(&attr, evlist->nr_entries);
+ struct evsel *evsel = perf_evsel__new_idx(&attr, evlist->core.nr_entries);
if (evsel == NULL)
return -ENOMEM;
- perf_evlist__add(evlist, evsel);
+ evlist__add(evlist, evsel);
return 0;
}
-static int perf_evlist__add_attrs(struct perf_evlist *evlist,
+static int evlist__add_attrs(struct evlist *evlist,
struct perf_event_attr *attrs, size_t nr_attrs)
{
- struct perf_evsel *evsel, *n;
+ struct evsel *evsel, *n;
LIST_HEAD(head);
size_t i;
for (i = 0; i < nr_attrs; i++) {
- evsel = perf_evsel__new_idx(attrs + i, evlist->nr_entries + i);
+ evsel = perf_evsel__new_idx(attrs + i, evlist->core.nr_entries + i);
if (evsel == NULL)
goto out_delete_partial_list;
- list_add_tail(&evsel->node, &head);
+ list_add_tail(&evsel->core.node, &head);
}
perf_evlist__splice_list_tail(evlist, &head);
@@ -278,11 +258,11 @@ static int perf_evlist__add_attrs(struct perf_evlist *evlist,
out_delete_partial_list:
__evlist__for_each_entry_safe(&head, n, evsel)
- perf_evsel__delete(evsel);
+ evsel__delete(evsel);
return -1;
}
-int __perf_evlist__add_default_attrs(struct perf_evlist *evlist,
+int __perf_evlist__add_default_attrs(struct evlist *evlist,
struct perf_event_attr *attrs, size_t nr_attrs)
{
size_t i;
@@ -290,31 +270,31 @@ int __perf_evlist__add_default_attrs(struct perf_evlist *evlist,
for (i = 0; i < nr_attrs; i++)
event_attr_init(attrs + i);
- return perf_evlist__add_attrs(evlist, attrs, nr_attrs);
+ return evlist__add_attrs(evlist, attrs, nr_attrs);
}
-struct perf_evsel *
-perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id)
+struct evsel *
+perf_evlist__find_tracepoint_by_id(struct evlist *evlist, int id)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) {
- if (evsel->attr.type == PERF_TYPE_TRACEPOINT &&
- (int)evsel->attr.config == id)
+ if (evsel->core.attr.type == PERF_TYPE_TRACEPOINT &&
+ (int)evsel->core.attr.config == id)
return evsel;
}
return NULL;
}
-struct perf_evsel *
-perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist,
+struct evsel *
+perf_evlist__find_tracepoint_by_name(struct evlist *evlist,
const char *name)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) {
- if ((evsel->attr.type == PERF_TYPE_TRACEPOINT) &&
+ if ((evsel->core.attr.type == PERF_TYPE_TRACEPOINT) &&
(strcmp(evsel->name, name) == 0))
return evsel;
}
@@ -322,66 +302,66 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist,
return NULL;
}
-int perf_evlist__add_newtp(struct perf_evlist *evlist,
+int perf_evlist__add_newtp(struct evlist *evlist,
const char *sys, const char *name, void *handler)
{
- struct perf_evsel *evsel = perf_evsel__newtp(sys, name);
+ struct evsel *evsel = perf_evsel__newtp(sys, name);
if (IS_ERR(evsel))
return -1;
evsel->handler = handler;
- perf_evlist__add(evlist, evsel);
+ evlist__add(evlist, evsel);
return 0;
}
-static int perf_evlist__nr_threads(struct perf_evlist *evlist,
- struct perf_evsel *evsel)
+static int perf_evlist__nr_threads(struct evlist *evlist,
+ struct evsel *evsel)
{
if (evsel->system_wide)
return 1;
else
- return thread_map__nr(evlist->threads);
+ return perf_thread_map__nr(evlist->core.threads);
}
-void perf_evlist__disable(struct perf_evlist *evlist)
+void evlist__disable(struct evlist *evlist)
{
- struct perf_evsel *pos;
+ struct evsel *pos;
evlist__for_each_entry(evlist, pos) {
- if (pos->disabled || !perf_evsel__is_group_leader(pos) || !pos->fd)
+ if (pos->disabled || !perf_evsel__is_group_leader(pos) || !pos->core.fd)
continue;
- perf_evsel__disable(pos);
+ evsel__disable(pos);
}
evlist->enabled = false;
}
-void perf_evlist__enable(struct perf_evlist *evlist)
+void evlist__enable(struct evlist *evlist)
{
- struct perf_evsel *pos;
+ struct evsel *pos;
evlist__for_each_entry(evlist, pos) {
- if (!perf_evsel__is_group_leader(pos) || !pos->fd)
+ if (!perf_evsel__is_group_leader(pos) || !pos->core.fd)
continue;
- perf_evsel__enable(pos);
+ evsel__enable(pos);
}
evlist->enabled = true;
}
-void perf_evlist__toggle_enable(struct perf_evlist *evlist)
+void perf_evlist__toggle_enable(struct evlist *evlist)
{
- (evlist->enabled ? perf_evlist__disable : perf_evlist__enable)(evlist);
+ (evlist->enabled ? evlist__disable : evlist__enable)(evlist);
}
-static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist,
- struct perf_evsel *evsel, int cpu)
+static int perf_evlist__enable_event_cpu(struct evlist *evlist,
+ struct evsel *evsel, int cpu)
{
int thread;
int nr_threads = perf_evlist__nr_threads(evlist, evsel);
- if (!evsel->fd)
+ if (!evsel->core.fd)
return -EINVAL;
for (thread = 0; thread < nr_threads; thread++) {
@@ -392,14 +372,14 @@ static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist,
return 0;
}
-static int perf_evlist__enable_event_thread(struct perf_evlist *evlist,
- struct perf_evsel *evsel,
+static int perf_evlist__enable_event_thread(struct evlist *evlist,
+ struct evsel *evsel,
int thread)
{
int cpu;
- int nr_cpus = cpu_map__nr(evlist->cpus);
+ int nr_cpus = perf_cpu_map__nr(evlist->core.cpus);
- if (!evsel->fd)
+ if (!evsel->core.fd)
return -EINVAL;
for (cpu = 0; cpu < nr_cpus; cpu++) {
@@ -410,10 +390,10 @@ static int perf_evlist__enable_event_thread(struct perf_evlist *evlist,
return 0;
}
-int perf_evlist__enable_event_idx(struct perf_evlist *evlist,
- struct perf_evsel *evsel, int idx)
+int perf_evlist__enable_event_idx(struct evlist *evlist,
+ struct evsel *evsel, int idx)
{
- bool per_cpu_mmaps = !cpu_map__empty(evlist->cpus);
+ bool per_cpu_mmaps = !perf_cpu_map__empty(evlist->core.cpus);
if (per_cpu_mmaps)
return perf_evlist__enable_event_cpu(evlist, evsel, idx);
@@ -421,12 +401,12 @@ int perf_evlist__enable_event_idx(struct perf_evlist *evlist,
return perf_evlist__enable_event_thread(evlist, evsel, idx);
}
-int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
+int perf_evlist__alloc_pollfd(struct evlist *evlist)
{
- int nr_cpus = cpu_map__nr(evlist->cpus);
- int nr_threads = thread_map__nr(evlist->threads);
+ int nr_cpus = perf_cpu_map__nr(evlist->core.cpus);
+ int nr_threads = perf_thread_map__nr(evlist->core.threads);
int nfds = 0;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) {
if (evsel->system_wide)
@@ -442,7 +422,7 @@ int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
return 0;
}
-static int __perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd,
+static int __perf_evlist__add_pollfd(struct evlist *evlist, int fd,
struct perf_mmap *map, short revent)
{
int pos = fdarray__add(&evlist->pollfd, fd, revent | POLLERR | POLLHUP);
@@ -459,7 +439,7 @@ static int __perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd,
return pos;
}
-int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd)
+int perf_evlist__add_pollfd(struct evlist *evlist, int fd)
{
return __perf_evlist__add_pollfd(evlist, fd, NULL, POLLIN);
}
@@ -473,19 +453,19 @@ static void perf_evlist__munmap_filtered(struct fdarray *fda, int fd,
perf_mmap__put(map);
}
-int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask)
+int perf_evlist__filter_pollfd(struct evlist *evlist, short revents_and_mask)
{
return fdarray__filter(&evlist->pollfd, revents_and_mask,
perf_evlist__munmap_filtered, NULL);
}
-int perf_evlist__poll(struct perf_evlist *evlist, int timeout)
+int perf_evlist__poll(struct evlist *evlist, int timeout)
{
return fdarray__poll(&evlist->pollfd, timeout);
}
-static void perf_evlist__id_hash(struct perf_evlist *evlist,
- struct perf_evsel *evsel,
+static void perf_evlist__id_hash(struct evlist *evlist,
+ struct evsel *evsel,
int cpu, int thread, u64 id)
{
int hash;
@@ -497,15 +477,15 @@ static void perf_evlist__id_hash(struct perf_evlist *evlist,
hlist_add_head(&sid->node, &evlist->heads[hash]);
}
-void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel,
+void perf_evlist__id_add(struct evlist *evlist, struct evsel *evsel,
int cpu, int thread, u64 id)
{
perf_evlist__id_hash(evlist, evsel, cpu, thread, id);
evsel->id[evsel->ids++] = id;
}
-int perf_evlist__id_add_fd(struct perf_evlist *evlist,
- struct perf_evsel *evsel,
+int perf_evlist__id_add_fd(struct evlist *evlist,
+ struct evsel *evsel,
int cpu, int thread, int fd)
{
u64 read_data[4] = { 0, };
@@ -529,13 +509,13 @@ int perf_evlist__id_add_fd(struct perf_evlist *evlist,
if (perf_evlist__read_format(evlist) & PERF_FORMAT_GROUP)
return -1;
- if (!(evsel->attr.read_format & PERF_FORMAT_ID) ||
+ if (!(evsel->core.attr.read_format & PERF_FORMAT_ID) ||
read(fd, &read_data, sizeof(read_data)) == -1)
return -1;
- if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
+ if (evsel->core.attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
++id_idx;
- if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
+ if (evsel->core.attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
++id_idx;
id = read_data[id_idx];
@@ -545,23 +525,23 @@ int perf_evlist__id_add_fd(struct perf_evlist *evlist,
return 0;
}
-static void perf_evlist__set_sid_idx(struct perf_evlist *evlist,
- struct perf_evsel *evsel, int idx, int cpu,
+static void perf_evlist__set_sid_idx(struct evlist *evlist,
+ struct evsel *evsel, int idx, int cpu,
int thread)
{
struct perf_sample_id *sid = SID(evsel, cpu, thread);
sid->idx = idx;
- if (evlist->cpus && cpu >= 0)
- sid->cpu = evlist->cpus->map[cpu];
+ if (evlist->core.cpus && cpu >= 0)
+ sid->cpu = evlist->core.cpus->map[cpu];
else
sid->cpu = -1;
- if (!evsel->system_wide && evlist->threads && thread >= 0)
- sid->tid = thread_map__pid(evlist->threads, thread);
+ if (!evsel->system_wide && evlist->core.threads && thread >= 0)
+ sid->tid = perf_thread_map__pid(evlist->core.threads, thread);
else
sid->tid = -1;
}
-struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id)
+struct perf_sample_id *perf_evlist__id2sid(struct evlist *evlist, u64 id)
{
struct hlist_head *head;
struct perf_sample_id *sid;
@@ -577,11 +557,11 @@ struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id)
return NULL;
}
-struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id)
+struct evsel *perf_evlist__id2evsel(struct evlist *evlist, u64 id)
{
struct perf_sample_id *sid;
- if (evlist->nr_entries == 1 || !id)
+ if (evlist->core.nr_entries == 1 || !id)
return perf_evlist__first(evlist);
sid = perf_evlist__id2sid(evlist, id);
@@ -594,7 +574,7 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id)
return NULL;
}
-struct perf_evsel *perf_evlist__id2evsel_strict(struct perf_evlist *evlist,
+struct evsel *perf_evlist__id2evsel_strict(struct evlist *evlist,
u64 id)
{
struct perf_sample_id *sid;
@@ -609,10 +589,10 @@ struct perf_evsel *perf_evlist__id2evsel_strict(struct perf_evlist *evlist,
return NULL;
}
-static int perf_evlist__event2id(struct perf_evlist *evlist,
+static int perf_evlist__event2id(struct evlist *evlist,
union perf_event *event, u64 *id)
{
- const u64 *array = event->sample.array;
+ const __u64 *array = event->sample.array;
ssize_t n;
n = (event->header.size - sizeof(event->header)) >> 3;
@@ -630,19 +610,19 @@ static int perf_evlist__event2id(struct perf_evlist *evlist,
return 0;
}
-struct perf_evsel *perf_evlist__event2evsel(struct perf_evlist *evlist,
+struct evsel *perf_evlist__event2evsel(struct evlist *evlist,
union perf_event *event)
{
- struct perf_evsel *first = perf_evlist__first(evlist);
+ struct evsel *first = perf_evlist__first(evlist);
struct hlist_head *head;
struct perf_sample_id *sid;
int hash;
u64 id;
- if (evlist->nr_entries == 1)
+ if (evlist->core.nr_entries == 1)
return first;
- if (!first->attr.sample_id_all &&
+ if (!first->core.attr.sample_id_all &&
event->header.type != PERF_RECORD_SAMPLE)
return first;
@@ -663,7 +643,7 @@ struct perf_evsel *perf_evlist__event2evsel(struct perf_evlist *evlist,
return NULL;
}
-static int perf_evlist__set_paused(struct perf_evlist *evlist, bool value)
+static int perf_evlist__set_paused(struct evlist *evlist, bool value)
{
int i;
@@ -683,17 +663,17 @@ static int perf_evlist__set_paused(struct perf_evlist *evlist, bool value)
return 0;
}
-static int perf_evlist__pause(struct perf_evlist *evlist)
+static int perf_evlist__pause(struct evlist *evlist)
{
return perf_evlist__set_paused(evlist, true);
}
-static int perf_evlist__resume(struct perf_evlist *evlist)
+static int perf_evlist__resume(struct evlist *evlist)
{
return perf_evlist__set_paused(evlist, false);
}
-static void perf_evlist__munmap_nofree(struct perf_evlist *evlist)
+static void perf_evlist__munmap_nofree(struct evlist *evlist)
{
int i;
@@ -706,22 +686,22 @@ static void perf_evlist__munmap_nofree(struct perf_evlist *evlist)
perf_mmap__munmap(&evlist->overwrite_mmap[i]);
}
-void perf_evlist__munmap(struct perf_evlist *evlist)
+void perf_evlist__munmap(struct evlist *evlist)
{
perf_evlist__munmap_nofree(evlist);
zfree(&evlist->mmap);
zfree(&evlist->overwrite_mmap);
}
-static struct perf_mmap *perf_evlist__alloc_mmap(struct perf_evlist *evlist,
+static struct perf_mmap *perf_evlist__alloc_mmap(struct evlist *evlist,
bool overwrite)
{
int i;
struct perf_mmap *map;
- evlist->nr_mmaps = cpu_map__nr(evlist->cpus);
- if (cpu_map__empty(evlist->cpus))
- evlist->nr_mmaps = thread_map__nr(evlist->threads);
+ evlist->nr_mmaps = perf_cpu_map__nr(evlist->core.cpus);
+ if (perf_cpu_map__empty(evlist->core.cpus))
+ evlist->nr_mmaps = perf_thread_map__nr(evlist->core.threads);
map = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap));
if (!map)
return NULL;
@@ -744,21 +724,21 @@ static struct perf_mmap *perf_evlist__alloc_mmap(struct perf_evlist *evlist,
}
static bool
-perf_evlist__should_poll(struct perf_evlist *evlist __maybe_unused,
- struct perf_evsel *evsel)
+perf_evlist__should_poll(struct evlist *evlist __maybe_unused,
+ struct evsel *evsel)
{
- if (evsel->attr.write_backward)
+ if (evsel->core.attr.write_backward)
return false;
return true;
}
-static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,
+static int perf_evlist__mmap_per_evsel(struct evlist *evlist, int idx,
struct mmap_params *mp, int cpu_idx,
int thread, int *_output, int *_output_overwrite)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
int revent;
- int evlist_cpu = cpu_map__cpu(evlist->cpus, cpu_idx);
+ int evlist_cpu = cpu_map__cpu(evlist->core.cpus, cpu_idx);
evlist__for_each_entry(evlist, evsel) {
struct perf_mmap *maps = evlist->mmap;
@@ -767,7 +747,7 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,
int cpu;
mp->prot = PROT_READ | PROT_WRITE;
- if (evsel->attr.write_backward) {
+ if (evsel->core.attr.write_backward) {
output = _output_overwrite;
maps = evlist->overwrite_mmap;
@@ -785,7 +765,7 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,
if (evsel->system_wide && thread)
continue;
- cpu = cpu_map__idx(evsel->cpus, evlist_cpu);
+ cpu = perf_cpu_map__idx(evsel->core.cpus, evlist_cpu);
if (cpu == -1)
continue;
@@ -818,7 +798,7 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,
return -1;
}
- if (evsel->attr.read_format & PERF_FORMAT_ID) {
+ if (evsel->core.attr.read_format & PERF_FORMAT_ID) {
if (perf_evlist__id_add_fd(evlist, evsel, cpu, thread,
fd) < 0)
return -1;
@@ -830,12 +810,12 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,
return 0;
}
-static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist,
+static int perf_evlist__mmap_per_cpu(struct evlist *evlist,
struct mmap_params *mp)
{
int cpu, thread;
- int nr_cpus = cpu_map__nr(evlist->cpus);
- int nr_threads = thread_map__nr(evlist->threads);
+ int nr_cpus = perf_cpu_map__nr(evlist->core.cpus);
+ int nr_threads = perf_thread_map__nr(evlist->core.threads);
pr_debug2("perf event ring buffer mmapped per cpu\n");
for (cpu = 0; cpu < nr_cpus; cpu++) {
@@ -859,11 +839,11 @@ out_unmap:
return -1;
}
-static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist,
+static int perf_evlist__mmap_per_thread(struct evlist *evlist,
struct mmap_params *mp)
{
int thread;
- int nr_threads = thread_map__nr(evlist->threads);
+ int nr_threads = perf_thread_map__nr(evlist->core.threads);
pr_debug2("perf event ring buffer mmapped per thread\n");
for (thread = 0; thread < nr_threads; thread++) {
@@ -1007,14 +987,14 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str,
*
* Return: %0 on success, negative error code otherwise.
*/
-int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
+int perf_evlist__mmap_ex(struct evlist *evlist, unsigned int pages,
unsigned int auxtrace_pages,
bool auxtrace_overwrite, int nr_cblocks, int affinity, int flush,
int comp_level)
{
- struct perf_evsel *evsel;
- const struct cpu_map *cpus = evlist->cpus;
- const struct thread_map *threads = evlist->threads;
+ struct evsel *evsel;
+ const struct perf_cpu_map *cpus = evlist->core.cpus;
+ const struct perf_thread_map *threads = evlist->core.threads;
/*
* Delay setting mp.prot: set it before calling perf_mmap__mmap.
* Its value is decided by evsel's write_backward.
@@ -1039,28 +1019,28 @@ int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
auxtrace_pages, auxtrace_overwrite);
evlist__for_each_entry(evlist, evsel) {
- if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
+ if ((evsel->core.attr.read_format & PERF_FORMAT_ID) &&
evsel->sample_id == NULL &&
- perf_evsel__alloc_id(evsel, cpu_map__nr(cpus), threads->nr) < 0)
+ perf_evsel__alloc_id(evsel, perf_cpu_map__nr(cpus), threads->nr) < 0)
return -ENOMEM;
}
- if (cpu_map__empty(cpus))
+ if (perf_cpu_map__empty(cpus))
return perf_evlist__mmap_per_thread(evlist, &mp);
return perf_evlist__mmap_per_cpu(evlist, &mp);
}
-int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages)
+int perf_evlist__mmap(struct evlist *evlist, unsigned int pages)
{
return perf_evlist__mmap_ex(evlist, pages, 0, false, 0, PERF_AFFINITY_SYS, 1, 0);
}
-int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target)
+int perf_evlist__create_maps(struct evlist *evlist, struct target *target)
{
bool all_threads = (target->per_thread && target->system_wide);
- struct cpu_map *cpus;
- struct thread_map *threads;
+ struct perf_cpu_map *cpus;
+ struct perf_thread_map *threads;
/*
* If specify '-a' and '--per-thread' to perf record, perf record
@@ -1087,68 +1067,45 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target)
return -1;
if (target__uses_dummy_map(target))
- cpus = cpu_map__dummy_new();
+ cpus = perf_cpu_map__dummy_new();
else
- cpus = cpu_map__new(target->cpu_list);
+ cpus = perf_cpu_map__new(target->cpu_list);
if (!cpus)
goto out_delete_threads;
- evlist->has_user_cpus = !!target->cpu_list;
+ evlist->core.has_user_cpus = !!target->cpu_list;
- perf_evlist__set_maps(evlist, cpus, threads);
+ perf_evlist__set_maps(&evlist->core, cpus, threads);
return 0;
out_delete_threads:
- thread_map__put(threads);
+ perf_thread_map__put(threads);
return -1;
}
-void perf_evlist__set_maps(struct perf_evlist *evlist, struct cpu_map *cpus,
- struct thread_map *threads)
-{
- /*
- * Allow for the possibility that one or another of the maps isn't being
- * changed i.e. don't put it. Note we are assuming the maps that are
- * being applied are brand new and evlist is taking ownership of the
- * original reference count of 1. If that is not the case it is up to
- * the caller to increase the reference count.
- */
- if (cpus != evlist->cpus) {
- cpu_map__put(evlist->cpus);
- evlist->cpus = cpu_map__get(cpus);
- }
-
- if (threads != evlist->threads) {
- thread_map__put(evlist->threads);
- evlist->threads = thread_map__get(threads);
- }
-
- perf_evlist__propagate_maps(evlist);
-}
-
-void __perf_evlist__set_sample_bit(struct perf_evlist *evlist,
+void __perf_evlist__set_sample_bit(struct evlist *evlist,
enum perf_event_sample_format bit)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel)
__perf_evsel__set_sample_bit(evsel, bit);
}
-void __perf_evlist__reset_sample_bit(struct perf_evlist *evlist,
+void __perf_evlist__reset_sample_bit(struct evlist *evlist,
enum perf_event_sample_format bit)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel)
__perf_evsel__reset_sample_bit(evsel, bit);
}
-int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel **err_evsel)
+int perf_evlist__apply_filters(struct evlist *evlist, struct evsel **err_evsel)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
int err = 0;
evlist__for_each_entry(evlist, evsel) {
@@ -1159,7 +1116,7 @@ int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel **e
* filters only work for tracepoint event, which doesn't have cpu limit.
* So evlist and evsel should always be same.
*/
- err = perf_evsel__apply_filter(evsel, evsel->filter);
+ err = perf_evsel__apply_filter(&evsel->core, evsel->filter);
if (err) {
*err_evsel = evsel;
break;
@@ -1169,13 +1126,13 @@ int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel **e
return err;
}
-int perf_evlist__set_tp_filter(struct perf_evlist *evlist, const char *filter)
+int perf_evlist__set_tp_filter(struct evlist *evlist, const char *filter)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
int err = 0;
evlist__for_each_entry(evlist, evsel) {
- if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
+ if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
continue;
err = perf_evsel__set_filter(evsel, filter);
@@ -1186,7 +1143,7 @@ int perf_evlist__set_tp_filter(struct perf_evlist *evlist, const char *filter)
return err;
}
-int perf_evlist__set_tp_filter_pids(struct perf_evlist *evlist, size_t npids, pid_t *pids)
+int perf_evlist__set_tp_filter_pids(struct evlist *evlist, size_t npids, pid_t *pids)
{
char *filter;
int ret = -1;
@@ -1213,16 +1170,16 @@ out_free:
return ret;
}
-int perf_evlist__set_tp_filter_pid(struct perf_evlist *evlist, pid_t pid)
+int perf_evlist__set_tp_filter_pid(struct evlist *evlist, pid_t pid)
{
return perf_evlist__set_tp_filter_pids(evlist, 1, &pid);
}
-bool perf_evlist__valid_sample_type(struct perf_evlist *evlist)
+bool perf_evlist__valid_sample_type(struct evlist *evlist)
{
- struct perf_evsel *pos;
+ struct evsel *pos;
- if (evlist->nr_entries == 1)
+ if (evlist->core.nr_entries == 1)
return true;
if (evlist->id_pos < 0 || evlist->is_pos < 0)
@@ -1237,43 +1194,43 @@ bool perf_evlist__valid_sample_type(struct perf_evlist *evlist)
return true;
}
-u64 __perf_evlist__combined_sample_type(struct perf_evlist *evlist)
+u64 __perf_evlist__combined_sample_type(struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
if (evlist->combined_sample_type)
return evlist->combined_sample_type;
evlist__for_each_entry(evlist, evsel)
- evlist->combined_sample_type |= evsel->attr.sample_type;
+ evlist->combined_sample_type |= evsel->core.attr.sample_type;
return evlist->combined_sample_type;
}
-u64 perf_evlist__combined_sample_type(struct perf_evlist *evlist)
+u64 perf_evlist__combined_sample_type(struct evlist *evlist)
{
evlist->combined_sample_type = 0;
return __perf_evlist__combined_sample_type(evlist);
}
-u64 perf_evlist__combined_branch_type(struct perf_evlist *evlist)
+u64 perf_evlist__combined_branch_type(struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
u64 branch_type = 0;
evlist__for_each_entry(evlist, evsel)
- branch_type |= evsel->attr.branch_sample_type;
+ branch_type |= evsel->core.attr.branch_sample_type;
return branch_type;
}
-bool perf_evlist__valid_read_format(struct perf_evlist *evlist)
+bool perf_evlist__valid_read_format(struct evlist *evlist)
{
- struct perf_evsel *first = perf_evlist__first(evlist), *pos = first;
- u64 read_format = first->attr.read_format;
- u64 sample_type = first->attr.sample_type;
+ struct evsel *first = perf_evlist__first(evlist), *pos = first;
+ u64 read_format = first->core.attr.read_format;
+ u64 sample_type = first->core.attr.sample_type;
evlist__for_each_entry(evlist, pos) {
- if (read_format != pos->attr.read_format)
+ if (read_format != pos->core.attr.read_format)
return false;
}
@@ -1286,23 +1243,23 @@ bool perf_evlist__valid_read_format(struct perf_evlist *evlist)
return true;
}
-u64 perf_evlist__read_format(struct perf_evlist *evlist)
+u64 perf_evlist__read_format(struct evlist *evlist)
{
- struct perf_evsel *first = perf_evlist__first(evlist);
- return first->attr.read_format;
+ struct evsel *first = perf_evlist__first(evlist);
+ return first->core.attr.read_format;
}
-u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist)
+u16 perf_evlist__id_hdr_size(struct evlist *evlist)
{
- struct perf_evsel *first = perf_evlist__first(evlist);
+ struct evsel *first = perf_evlist__first(evlist);
struct perf_sample *data;
u64 sample_type;
u16 size = 0;
- if (!first->attr.sample_id_all)
+ if (!first->core.attr.sample_id_all)
goto out;
- sample_type = first->attr.sample_type;
+ sample_type = first->core.attr.sample_type;
if (sample_type & PERF_SAMPLE_TID)
size += sizeof(data->tid) * 2;
@@ -1325,42 +1282,42 @@ out:
return size;
}
-bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist)
+bool perf_evlist__valid_sample_id_all(struct evlist *evlist)
{
- struct perf_evsel *first = perf_evlist__first(evlist), *pos = first;
+ struct evsel *first = perf_evlist__first(evlist), *pos = first;
evlist__for_each_entry_continue(evlist, pos) {
- if (first->attr.sample_id_all != pos->attr.sample_id_all)
+ if (first->core.attr.sample_id_all != pos->core.attr.sample_id_all)
return false;
}
return true;
}
-bool perf_evlist__sample_id_all(struct perf_evlist *evlist)
+bool perf_evlist__sample_id_all(struct evlist *evlist)
{
- struct perf_evsel *first = perf_evlist__first(evlist);
- return first->attr.sample_id_all;
+ struct evsel *first = perf_evlist__first(evlist);
+ return first->core.attr.sample_id_all;
}
-void perf_evlist__set_selected(struct perf_evlist *evlist,
- struct perf_evsel *evsel)
+void perf_evlist__set_selected(struct evlist *evlist,
+ struct evsel *evsel)
{
evlist->selected = evsel;
}
-void perf_evlist__close(struct perf_evlist *evlist)
+void evlist__close(struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry_reverse(evlist, evsel)
- perf_evsel__close(evsel);
+ evsel__close(evsel);
}
-static int perf_evlist__create_syswide_maps(struct perf_evlist *evlist)
+static int perf_evlist__create_syswide_maps(struct evlist *evlist)
{
- struct cpu_map *cpus;
- struct thread_map *threads;
+ struct perf_cpu_map *cpus;
+ struct perf_thread_map *threads;
int err = -ENOMEM;
/*
@@ -1372,32 +1329,32 @@ static int perf_evlist__create_syswide_maps(struct perf_evlist *evlist)
* error, and we may not want to do that fallback to a
* default cpu identity map :-\
*/
- cpus = cpu_map__new(NULL);
+ cpus = perf_cpu_map__new(NULL);
if (!cpus)
goto out;
- threads = thread_map__new_dummy();
+ threads = perf_thread_map__new_dummy();
if (!threads)
goto out_put;
- perf_evlist__set_maps(evlist, cpus, threads);
+ perf_evlist__set_maps(&evlist->core, cpus, threads);
out:
return err;
out_put:
- cpu_map__put(cpus);
+ perf_cpu_map__put(cpus);
goto out;
}
-int perf_evlist__open(struct perf_evlist *evlist)
+int evlist__open(struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
int err;
/*
* Default: one fd per CPU, all threads, aka systemwide
* as sys_perf_event_open(cpu = -1, thread = -1) is EINVAL
*/
- if (evlist->threads == NULL && evlist->cpus == NULL) {
+ if (evlist->core.threads == NULL && evlist->core.cpus == NULL) {
err = perf_evlist__create_syswide_maps(evlist);
if (err < 0)
goto out_err;
@@ -1406,19 +1363,19 @@ int perf_evlist__open(struct perf_evlist *evlist)
perf_evlist__update_id_pos(evlist);
evlist__for_each_entry(evlist, evsel) {
- err = perf_evsel__open(evsel, evsel->cpus, evsel->threads);
+ err = evsel__open(evsel, evsel->core.cpus, evsel->core.threads);
if (err < 0)
goto out_err;
}
return 0;
out_err:
- perf_evlist__close(evlist);
+ evlist__close(evlist);
errno = -err;
return err;
}
-int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *target,
+int perf_evlist__prepare_workload(struct evlist *evlist, struct target *target,
const char *argv[], bool pipe_output,
void (*exec_error)(int signo, siginfo_t *info, void *ucontext))
{
@@ -1500,12 +1457,12 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar
}
if (target__none(target)) {
- if (evlist->threads == NULL) {
+ if (evlist->core.threads == NULL) {
fprintf(stderr, "FATAL: evlist->threads need to be set at this point (%s:%d).\n",
__func__, __LINE__);
goto out_close_pipes;
}
- thread_map__set_pid(evlist->threads, 0, evlist->workload.pid);
+ perf_thread_map__set_pid(evlist->core.threads, 0, evlist->workload.pid);
}
close(child_ready_pipe[1]);
@@ -1532,7 +1489,7 @@ out_close_ready_pipe:
return -1;
}
-int perf_evlist__start_workload(struct perf_evlist *evlist)
+int perf_evlist__start_workload(struct evlist *evlist)
{
if (evlist->workload.cork_fd > 0) {
char bf = 0;
@@ -1551,30 +1508,30 @@ int perf_evlist__start_workload(struct perf_evlist *evlist)
return 0;
}
-int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event,
+int perf_evlist__parse_sample(struct evlist *evlist, union perf_event *event,
struct perf_sample *sample)
{
- struct perf_evsel *evsel = perf_evlist__event2evsel(evlist, event);
+ struct evsel *evsel = perf_evlist__event2evsel(evlist, event);
if (!evsel)
return -EFAULT;
return perf_evsel__parse_sample(evsel, event, sample);
}
-int perf_evlist__parse_sample_timestamp(struct perf_evlist *evlist,
+int perf_evlist__parse_sample_timestamp(struct evlist *evlist,
union perf_event *event,
u64 *timestamp)
{
- struct perf_evsel *evsel = perf_evlist__event2evsel(evlist, event);
+ struct evsel *evsel = perf_evlist__event2evsel(evlist, event);
if (!evsel)
return -EFAULT;
return perf_evsel__parse_sample_timestamp(evsel, event, timestamp);
}
-size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp)
+size_t perf_evlist__fprintf(struct evlist *evlist, FILE *fp)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
size_t printed = 0;
evlist__for_each_entry(evlist, evsel) {
@@ -1585,7 +1542,7 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp)
return printed + fprintf(fp, "\n");
}
-int perf_evlist__strerror_open(struct perf_evlist *evlist,
+int perf_evlist__strerror_open(struct evlist *evlist,
int err, char *buf, size_t size)
{
int printed, value;
@@ -1614,20 +1571,20 @@ int perf_evlist__strerror_open(struct perf_evlist *evlist,
"Hint:\tThe current value is %d.", value);
break;
case EINVAL: {
- struct perf_evsel *first = perf_evlist__first(evlist);
+ struct evsel *first = perf_evlist__first(evlist);
int max_freq;
if (sysctl__read_int("kernel/perf_event_max_sample_rate", &max_freq) < 0)
goto out_default;
- if (first->attr.sample_freq < (u64)max_freq)
+ if (first->core.attr.sample_freq < (u64)max_freq)
goto out_default;
printed = scnprintf(buf, size,
"Error:\t%s.\n"
"Hint:\tCheck /proc/sys/kernel/perf_event_max_sample_rate.\n"
"Hint:\tThe current value is %d and %" PRIu64 " is being requested.",
- emsg, max_freq, first->attr.sample_freq);
+ emsg, max_freq, first->core.attr.sample_freq);
break;
}
default:
@@ -1639,7 +1596,7 @@ out_default:
return 0;
}
-int perf_evlist__strerror_mmap(struct perf_evlist *evlist, int err, char *buf, size_t size)
+int perf_evlist__strerror_mmap(struct evlist *evlist, int err, char *buf, size_t size)
{
char sbuf[STRERR_BUFSIZE], *emsg = str_error_r(err, sbuf, sizeof(sbuf));
int pages_attempted = evlist->mmap_len / 1024, pages_max_per_user, printed = 0;
@@ -1670,10 +1627,10 @@ int perf_evlist__strerror_mmap(struct perf_evlist *evlist, int err, char *buf, s
return 0;
}
-void perf_evlist__to_front(struct perf_evlist *evlist,
- struct perf_evsel *move_evsel)
+void perf_evlist__to_front(struct evlist *evlist,
+ struct evsel *move_evsel)
{
- struct perf_evsel *evsel, *n;
+ struct evsel *evsel, *n;
LIST_HEAD(move);
if (move_evsel == perf_evlist__first(evlist))
@@ -1681,16 +1638,16 @@ void perf_evlist__to_front(struct perf_evlist *evlist,
evlist__for_each_entry_safe(evlist, n, evsel) {
if (evsel->leader == move_evsel->leader)
- list_move_tail(&evsel->node, &move);
+ list_move_tail(&evsel->core.node, &move);
}
- list_splice(&move, &evlist->entries);
+ list_splice(&move, &evlist->core.entries);
}
-void perf_evlist__set_tracking_event(struct perf_evlist *evlist,
- struct perf_evsel *tracking_evsel)
+void perf_evlist__set_tracking_event(struct evlist *evlist,
+ struct evsel *tracking_evsel)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
if (tracking_evsel->tracking)
return;
@@ -1703,11 +1660,11 @@ void perf_evlist__set_tracking_event(struct perf_evlist *evlist,
tracking_evsel->tracking = true;
}
-struct perf_evsel *
-perf_evlist__find_evsel_by_str(struct perf_evlist *evlist,
+struct evsel *
+perf_evlist__find_evsel_by_str(struct evlist *evlist,
const char *str)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) {
if (!evsel->name)
@@ -1719,7 +1676,7 @@ perf_evlist__find_evsel_by_str(struct perf_evlist *evlist,
return NULL;
}
-void perf_evlist__toggle_bkw_mmap(struct perf_evlist *evlist,
+void perf_evlist__toggle_bkw_mmap(struct evlist *evlist,
enum bkw_mmap_state state)
{
enum bkw_mmap_state old_state = evlist->bkw_mmap_state;
@@ -1777,12 +1734,12 @@ state_err:
return;
}
-bool perf_evlist__exclude_kernel(struct perf_evlist *evlist)
+bool perf_evlist__exclude_kernel(struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) {
- if (!evsel->attr.exclude_kernel)
+ if (!evsel->core.attr.exclude_kernel)
return false;
}
@@ -1794,25 +1751,25 @@ bool perf_evlist__exclude_kernel(struct perf_evlist *evlist)
* the group display. Set the artificial group and set the leader's
* forced_leader flag to notify the display code.
*/
-void perf_evlist__force_leader(struct perf_evlist *evlist)
+void perf_evlist__force_leader(struct evlist *evlist)
{
if (!evlist->nr_groups) {
- struct perf_evsel *leader = perf_evlist__first(evlist);
+ struct evsel *leader = perf_evlist__first(evlist);
perf_evlist__set_leader(evlist);
leader->forced_leader = true;
}
}
-struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evsel_list,
- struct perf_evsel *evsel)
+struct evsel *perf_evlist__reset_weak_group(struct evlist *evsel_list,
+ struct evsel *evsel)
{
- struct perf_evsel *c2, *leader;
+ struct evsel *c2, *leader;
bool is_open = true;
leader = evsel->leader;
pr_debug("Weak group for %s/%d failed\n",
- leader->name, leader->nr_members);
+ leader->name, leader->core.nr_members);
/*
* for_each_group_member doesn't work here because it doesn't
@@ -1823,24 +1780,24 @@ struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evsel_list,
is_open = false;
if (c2->leader == leader) {
if (is_open)
- perf_evsel__close(c2);
+ evsel__close(c2);
c2->leader = c2;
- c2->nr_members = 0;
+ c2->core.nr_members = 0;
}
}
return leader;
}
-int perf_evlist__add_sb_event(struct perf_evlist **evlist,
+int perf_evlist__add_sb_event(struct evlist **evlist,
struct perf_event_attr *attr,
perf_evsel__sb_cb_t cb,
void *data)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
bool new_evlist = (*evlist) == NULL;
if (*evlist == NULL)
- *evlist = perf_evlist__new();
+ *evlist = evlist__new();
if (*evlist == NULL)
return -1;
@@ -1849,18 +1806,18 @@ int perf_evlist__add_sb_event(struct perf_evlist **evlist,
attr->sample_id_all = 1;
}
- evsel = perf_evsel__new_idx(attr, (*evlist)->nr_entries);
+ evsel = perf_evsel__new_idx(attr, (*evlist)->core.nr_entries);
if (!evsel)
goto out_err;
evsel->side_band.cb = cb;
evsel->side_band.data = data;
- perf_evlist__add(*evlist, evsel);
+ evlist__add(*evlist, evsel);
return 0;
out_err:
if (new_evlist) {
- perf_evlist__delete(*evlist);
+ evlist__delete(*evlist);
*evlist = NULL;
}
return -1;
@@ -1868,9 +1825,17 @@ out_err:
static void *perf_evlist__poll_thread(void *arg)
{
- struct perf_evlist *evlist = arg;
+ struct evlist *evlist = arg;
bool draining = false;
int i, done = 0;
+ /*
+ * In order to read symbols from other namespaces perf to needs to call
+ * setns(2). This isn't permitted if the struct_fs has multiple users.
+ * unshare(2) the fs so that we may continue to setns into namespaces
+ * that we're observing when, for instance, reading the build-ids at
+ * the end of a 'perf record' session.
+ */
+ unshare(CLONE_FS);
while (!done) {
bool got_data = false;
@@ -1888,7 +1853,7 @@ static void *perf_evlist__poll_thread(void *arg)
if (perf_mmap__read_init(map))
continue;
while ((event = perf_mmap__read_event(map)) != NULL) {
- struct perf_evsel *evsel = perf_evlist__event2evsel(evlist, event);
+ struct evsel *evsel = perf_evlist__event2evsel(evlist, event);
if (evsel && evsel->side_band.cb)
evsel->side_band.cb(event, evsel->side_band.data);
@@ -1907,10 +1872,10 @@ static void *perf_evlist__poll_thread(void *arg)
return NULL;
}
-int perf_evlist__start_sb_thread(struct perf_evlist *evlist,
+int perf_evlist__start_sb_thread(struct evlist *evlist,
struct target *target)
{
- struct perf_evsel *counter;
+ struct evsel *counter;
if (!evlist)
return 0;
@@ -1919,8 +1884,8 @@ int perf_evlist__start_sb_thread(struct perf_evlist *evlist,
goto out_delete_evlist;
evlist__for_each_entry(evlist, counter) {
- if (perf_evsel__open(counter, evlist->cpus,
- evlist->threads) < 0)
+ if (evsel__open(counter, evlist->core.cpus,
+ evlist->core.threads) < 0)
goto out_delete_evlist;
}
@@ -1928,7 +1893,7 @@ int perf_evlist__start_sb_thread(struct perf_evlist *evlist,
goto out_delete_evlist;
evlist__for_each_entry(evlist, counter) {
- if (perf_evsel__enable(counter))
+ if (evsel__enable(counter))
goto out_delete_evlist;
}
@@ -1939,16 +1904,16 @@ int perf_evlist__start_sb_thread(struct perf_evlist *evlist,
return 0;
out_delete_evlist:
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
evlist = NULL;
return -1;
}
-void perf_evlist__stop_sb_thread(struct perf_evlist *evlist)
+void perf_evlist__stop_sb_thread(struct evlist *evlist)
{
if (!evlist)
return;
evlist->thread.done = 1;
pthread_join(evlist->thread.th, NULL);
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
}
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index 49354fe24d5f..a55f0f2546e5 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -8,30 +8,27 @@
#include <linux/list.h>
#include <api/fd/array.h>
#include <stdio.h>
-#include "../perf.h"
-#include "event.h"
+#include <internal/evlist.h>
+#include "events_stats.h"
#include "evsel.h"
#include "mmap.h"
-#include "util.h"
#include <signal.h>
#include <unistd.h>
struct pollfd;
struct thread_map;
-struct cpu_map;
+struct perf_cpu_map;
struct record_opts;
#define PERF_EVLIST__HLIST_BITS 8
#define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS)
-struct perf_evlist {
- struct list_head entries;
+struct evlist {
+ struct perf_evlist core;
struct hlist_head heads[PERF_EVLIST__HLIST_SIZE];
- int nr_entries;
int nr_groups;
int nr_mmaps;
bool enabled;
- bool has_user_cpus;
size_t mmap_len;
int id_pos;
int is_pos;
@@ -44,12 +41,10 @@ struct perf_evlist {
struct fdarray pollfd;
struct perf_mmap *mmap;
struct perf_mmap *overwrite_mmap;
- struct thread_map *threads;
- struct cpu_map *cpus;
- struct perf_evsel *selected;
+ struct evsel *selected;
struct events_stats stats;
struct perf_env *env;
- void (*trace_event_sample_raw)(struct perf_evlist *evlist,
+ void (*trace_event_sample_raw)(struct evlist *evlist,
union perf_event *event,
struct perf_sample *sample);
u64 first_sample_time;
@@ -60,51 +55,51 @@ struct perf_evlist {
} thread;
};
-struct perf_evsel_str_handler {
+struct evsel_str_handler {
const char *name;
void *handler;
};
-struct perf_evlist *perf_evlist__new(void);
-struct perf_evlist *perf_evlist__new_default(void);
-struct perf_evlist *perf_evlist__new_dummy(void);
-void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus,
- struct thread_map *threads);
-void perf_evlist__exit(struct perf_evlist *evlist);
-void perf_evlist__delete(struct perf_evlist *evlist);
+struct evlist *evlist__new(void);
+struct evlist *perf_evlist__new_default(void);
+struct evlist *perf_evlist__new_dummy(void);
+void evlist__init(struct evlist *evlist, struct perf_cpu_map *cpus,
+ struct perf_thread_map *threads);
+void perf_evlist__exit(struct evlist *evlist);
+void evlist__delete(struct evlist *evlist);
-void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry);
-void perf_evlist__remove(struct perf_evlist *evlist, struct perf_evsel *evsel);
+void evlist__add(struct evlist *evlist, struct evsel *entry);
+void evlist__remove(struct evlist *evlist, struct evsel *evsel);
-int __perf_evlist__add_default(struct perf_evlist *evlist, bool precise);
+int __perf_evlist__add_default(struct evlist *evlist, bool precise);
-static inline int perf_evlist__add_default(struct perf_evlist *evlist)
+static inline int perf_evlist__add_default(struct evlist *evlist)
{
return __perf_evlist__add_default(evlist, true);
}
-int __perf_evlist__add_default_attrs(struct perf_evlist *evlist,
+int __perf_evlist__add_default_attrs(struct evlist *evlist,
struct perf_event_attr *attrs, size_t nr_attrs);
#define perf_evlist__add_default_attrs(evlist, array) \
__perf_evlist__add_default_attrs(evlist, array, ARRAY_SIZE(array))
-int perf_evlist__add_dummy(struct perf_evlist *evlist);
+int perf_evlist__add_dummy(struct evlist *evlist);
-int perf_evlist__add_sb_event(struct perf_evlist **evlist,
+int perf_evlist__add_sb_event(struct evlist **evlist,
struct perf_event_attr *attr,
perf_evsel__sb_cb_t cb,
void *data);
-int perf_evlist__start_sb_thread(struct perf_evlist *evlist,
+int perf_evlist__start_sb_thread(struct evlist *evlist,
struct target *target);
-void perf_evlist__stop_sb_thread(struct perf_evlist *evlist);
+void perf_evlist__stop_sb_thread(struct evlist *evlist);
-int perf_evlist__add_newtp(struct perf_evlist *evlist,
+int perf_evlist__add_newtp(struct evlist *evlist,
const char *sys, const char *name, void *handler);
-void __perf_evlist__set_sample_bit(struct perf_evlist *evlist,
+void __perf_evlist__set_sample_bit(struct evlist *evlist,
enum perf_event_sample_format bit);
-void __perf_evlist__reset_sample_bit(struct perf_evlist *evlist,
+void __perf_evlist__reset_sample_bit(struct evlist *evlist,
enum perf_event_sample_format bit);
#define perf_evlist__set_sample_bit(evlist, bit) \
@@ -113,58 +108,58 @@ void __perf_evlist__reset_sample_bit(struct perf_evlist *evlist,
#define perf_evlist__reset_sample_bit(evlist, bit) \
__perf_evlist__reset_sample_bit(evlist, PERF_SAMPLE_##bit)
-int perf_evlist__set_tp_filter(struct perf_evlist *evlist, const char *filter);
-int perf_evlist__set_tp_filter_pid(struct perf_evlist *evlist, pid_t pid);
-int perf_evlist__set_tp_filter_pids(struct perf_evlist *evlist, size_t npids, pid_t *pids);
+int perf_evlist__set_tp_filter(struct evlist *evlist, const char *filter);
+int perf_evlist__set_tp_filter_pid(struct evlist *evlist, pid_t pid);
+int perf_evlist__set_tp_filter_pids(struct evlist *evlist, size_t npids, pid_t *pids);
-struct perf_evsel *
-perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id);
+struct evsel *
+perf_evlist__find_tracepoint_by_id(struct evlist *evlist, int id);
-struct perf_evsel *
-perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist,
+struct evsel *
+perf_evlist__find_tracepoint_by_name(struct evlist *evlist,
const char *name);
-void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel,
+void perf_evlist__id_add(struct evlist *evlist, struct evsel *evsel,
int cpu, int thread, u64 id);
-int perf_evlist__id_add_fd(struct perf_evlist *evlist,
- struct perf_evsel *evsel,
+int perf_evlist__id_add_fd(struct evlist *evlist,
+ struct evsel *evsel,
int cpu, int thread, int fd);
-int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd);
-int perf_evlist__alloc_pollfd(struct perf_evlist *evlist);
-int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask);
+int perf_evlist__add_pollfd(struct evlist *evlist, int fd);
+int perf_evlist__alloc_pollfd(struct evlist *evlist);
+int perf_evlist__filter_pollfd(struct evlist *evlist, short revents_and_mask);
-int perf_evlist__poll(struct perf_evlist *evlist, int timeout);
+int perf_evlist__poll(struct evlist *evlist, int timeout);
-struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id);
-struct perf_evsel *perf_evlist__id2evsel_strict(struct perf_evlist *evlist,
+struct evsel *perf_evlist__id2evsel(struct evlist *evlist, u64 id);
+struct evsel *perf_evlist__id2evsel_strict(struct evlist *evlist,
u64 id);
-struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id);
+struct perf_sample_id *perf_evlist__id2sid(struct evlist *evlist, u64 id);
-void perf_evlist__toggle_bkw_mmap(struct perf_evlist *evlist, enum bkw_mmap_state state);
+void perf_evlist__toggle_bkw_mmap(struct evlist *evlist, enum bkw_mmap_state state);
-void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx);
+void perf_evlist__mmap_consume(struct evlist *evlist, int idx);
-int perf_evlist__open(struct perf_evlist *evlist);
-void perf_evlist__close(struct perf_evlist *evlist);
+int evlist__open(struct evlist *evlist);
+void evlist__close(struct evlist *evlist);
struct callchain_param;
-void perf_evlist__set_id_pos(struct perf_evlist *evlist);
+void perf_evlist__set_id_pos(struct evlist *evlist);
bool perf_can_sample_identifier(void);
bool perf_can_record_switch_events(void);
bool perf_can_record_cpu_wide(void);
-void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts,
+void perf_evlist__config(struct evlist *evlist, struct record_opts *opts,
struct callchain_param *callchain);
int record_opts__config(struct record_opts *opts);
-int perf_evlist__prepare_workload(struct perf_evlist *evlist,
+int perf_evlist__prepare_workload(struct evlist *evlist,
struct target *target,
const char *argv[], bool pipe_output,
void (*exec_error)(int signo, siginfo_t *info,
void *ucontext));
-int perf_evlist__start_workload(struct perf_evlist *evlist);
+int perf_evlist__start_workload(struct evlist *evlist);
struct option;
@@ -175,77 +170,75 @@ int perf_evlist__parse_mmap_pages(const struct option *opt,
unsigned long perf_event_mlock_kb_in_pages(void);
-int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
+int perf_evlist__mmap_ex(struct evlist *evlist, unsigned int pages,
unsigned int auxtrace_pages,
bool auxtrace_overwrite, int nr_cblocks,
int affinity, int flush, int comp_level);
-int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages);
-void perf_evlist__munmap(struct perf_evlist *evlist);
+int perf_evlist__mmap(struct evlist *evlist, unsigned int pages);
+void perf_evlist__munmap(struct evlist *evlist);
size_t perf_evlist__mmap_size(unsigned long pages);
-void perf_evlist__disable(struct perf_evlist *evlist);
-void perf_evlist__enable(struct perf_evlist *evlist);
-void perf_evlist__toggle_enable(struct perf_evlist *evlist);
+void evlist__disable(struct evlist *evlist);
+void evlist__enable(struct evlist *evlist);
+void perf_evlist__toggle_enable(struct evlist *evlist);
-int perf_evlist__enable_event_idx(struct perf_evlist *evlist,
- struct perf_evsel *evsel, int idx);
+int perf_evlist__enable_event_idx(struct evlist *evlist,
+ struct evsel *evsel, int idx);
-void perf_evlist__set_selected(struct perf_evlist *evlist,
- struct perf_evsel *evsel);
+void perf_evlist__set_selected(struct evlist *evlist,
+ struct evsel *evsel);
-void perf_evlist__set_maps(struct perf_evlist *evlist, struct cpu_map *cpus,
- struct thread_map *threads);
-int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target);
-int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel **err_evsel);
+int perf_evlist__create_maps(struct evlist *evlist, struct target *target);
+int perf_evlist__apply_filters(struct evlist *evlist, struct evsel **err_evsel);
void __perf_evlist__set_leader(struct list_head *list);
-void perf_evlist__set_leader(struct perf_evlist *evlist);
+void perf_evlist__set_leader(struct evlist *evlist);
-u64 perf_evlist__read_format(struct perf_evlist *evlist);
-u64 __perf_evlist__combined_sample_type(struct perf_evlist *evlist);
-u64 perf_evlist__combined_sample_type(struct perf_evlist *evlist);
-u64 perf_evlist__combined_branch_type(struct perf_evlist *evlist);
-bool perf_evlist__sample_id_all(struct perf_evlist *evlist);
-u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist);
+u64 perf_evlist__read_format(struct evlist *evlist);
+u64 __perf_evlist__combined_sample_type(struct evlist *evlist);
+u64 perf_evlist__combined_sample_type(struct evlist *evlist);
+u64 perf_evlist__combined_branch_type(struct evlist *evlist);
+bool perf_evlist__sample_id_all(struct evlist *evlist);
+u16 perf_evlist__id_hdr_size(struct evlist *evlist);
-int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event,
+int perf_evlist__parse_sample(struct evlist *evlist, union perf_event *event,
struct perf_sample *sample);
-int perf_evlist__parse_sample_timestamp(struct perf_evlist *evlist,
+int perf_evlist__parse_sample_timestamp(struct evlist *evlist,
union perf_event *event,
u64 *timestamp);
-bool perf_evlist__valid_sample_type(struct perf_evlist *evlist);
-bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist);
-bool perf_evlist__valid_read_format(struct perf_evlist *evlist);
+bool perf_evlist__valid_sample_type(struct evlist *evlist);
+bool perf_evlist__valid_sample_id_all(struct evlist *evlist);
+bool perf_evlist__valid_read_format(struct evlist *evlist);
-void perf_evlist__splice_list_tail(struct perf_evlist *evlist,
+void perf_evlist__splice_list_tail(struct evlist *evlist,
struct list_head *list);
-static inline bool perf_evlist__empty(struct perf_evlist *evlist)
+static inline bool perf_evlist__empty(struct evlist *evlist)
{
- return list_empty(&evlist->entries);
+ return list_empty(&evlist->core.entries);
}
-static inline struct perf_evsel *perf_evlist__first(struct perf_evlist *evlist)
+static inline struct evsel *perf_evlist__first(struct evlist *evlist)
{
- return list_entry(evlist->entries.next, struct perf_evsel, node);
+ return list_entry(evlist->core.entries.next, struct evsel, core.node);
}
-static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist)
+static inline struct evsel *perf_evlist__last(struct evlist *evlist)
{
- return list_entry(evlist->entries.prev, struct perf_evsel, node);
+ return list_entry(evlist->core.entries.prev, struct evsel, core.node);
}
-size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp);
+size_t perf_evlist__fprintf(struct evlist *evlist, FILE *fp);
-int perf_evlist__strerror_open(struct perf_evlist *evlist, int err, char *buf, size_t size);
-int perf_evlist__strerror_mmap(struct perf_evlist *evlist, int err, char *buf, size_t size);
+int perf_evlist__strerror_open(struct evlist *evlist, int err, char *buf, size_t size);
+int perf_evlist__strerror_mmap(struct evlist *evlist, int err, char *buf, size_t size);
-bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str);
-void perf_evlist__to_front(struct perf_evlist *evlist,
- struct perf_evsel *move_evsel);
+bool perf_evlist__can_select_event(struct evlist *evlist, const char *str);
+void perf_evlist__to_front(struct evlist *evlist,
+ struct evsel *move_evsel);
/**
* __evlist__for_each_entry - iterate thru all the evsels
@@ -253,7 +246,7 @@ void perf_evlist__to_front(struct perf_evlist *evlist,
* @evsel: struct evsel iterator
*/
#define __evlist__for_each_entry(list, evsel) \
- list_for_each_entry(evsel, list, node)
+ list_for_each_entry(evsel, list, core.node)
/**
* evlist__for_each_entry - iterate thru all the evsels
@@ -261,7 +254,7 @@ void perf_evlist__to_front(struct perf_evlist *evlist,
* @evsel: struct evsel iterator
*/
#define evlist__for_each_entry(evlist, evsel) \
- __evlist__for_each_entry(&(evlist)->entries, evsel)
+ __evlist__for_each_entry(&(evlist)->core.entries, evsel)
/**
* __evlist__for_each_entry_continue - continue iteration thru all the evsels
@@ -269,7 +262,7 @@ void perf_evlist__to_front(struct perf_evlist *evlist,
* @evsel: struct evsel iterator
*/
#define __evlist__for_each_entry_continue(list, evsel) \
- list_for_each_entry_continue(evsel, list, node)
+ list_for_each_entry_continue(evsel, list, core.node)
/**
* evlist__for_each_entry_continue - continue iteration thru all the evsels
@@ -277,7 +270,7 @@ void perf_evlist__to_front(struct perf_evlist *evlist,
* @evsel: struct evsel iterator
*/
#define evlist__for_each_entry_continue(evlist, evsel) \
- __evlist__for_each_entry_continue(&(evlist)->entries, evsel)
+ __evlist__for_each_entry_continue(&(evlist)->core.entries, evsel)
/**
* __evlist__for_each_entry_reverse - iterate thru all the evsels in reverse order
@@ -285,7 +278,7 @@ void perf_evlist__to_front(struct perf_evlist *evlist,
* @evsel: struct evsel iterator
*/
#define __evlist__for_each_entry_reverse(list, evsel) \
- list_for_each_entry_reverse(evsel, list, node)
+ list_for_each_entry_reverse(evsel, list, core.node)
/**
* evlist__for_each_entry_reverse - iterate thru all the evsels in reverse order
@@ -293,7 +286,7 @@ void perf_evlist__to_front(struct perf_evlist *evlist,
* @evsel: struct evsel iterator
*/
#define evlist__for_each_entry_reverse(evlist, evsel) \
- __evlist__for_each_entry_reverse(&(evlist)->entries, evsel)
+ __evlist__for_each_entry_reverse(&(evlist)->core.entries, evsel)
/**
* __evlist__for_each_entry_safe - safely iterate thru all the evsels
@@ -302,7 +295,7 @@ void perf_evlist__to_front(struct perf_evlist *evlist,
* @evsel: struct evsel iterator
*/
#define __evlist__for_each_entry_safe(list, tmp, evsel) \
- list_for_each_entry_safe(evsel, tmp, list, node)
+ list_for_each_entry_safe(evsel, tmp, list, core.node)
/**
* evlist__for_each_entry_safe - safely iterate thru all the evsels
@@ -311,21 +304,21 @@ void perf_evlist__to_front(struct perf_evlist *evlist,
* @tmp: struct evsel temp iterator
*/
#define evlist__for_each_entry_safe(evlist, tmp, evsel) \
- __evlist__for_each_entry_safe(&(evlist)->entries, tmp, evsel)
+ __evlist__for_each_entry_safe(&(evlist)->core.entries, tmp, evsel)
-void perf_evlist__set_tracking_event(struct perf_evlist *evlist,
- struct perf_evsel *tracking_evsel);
+void perf_evlist__set_tracking_event(struct evlist *evlist,
+ struct evsel *tracking_evsel);
-struct perf_evsel *
-perf_evlist__find_evsel_by_str(struct perf_evlist *evlist, const char *str);
+struct evsel *
+perf_evlist__find_evsel_by_str(struct evlist *evlist, const char *str);
-struct perf_evsel *perf_evlist__event2evsel(struct perf_evlist *evlist,
+struct evsel *perf_evlist__event2evsel(struct evlist *evlist,
union perf_event *event);
-bool perf_evlist__exclude_kernel(struct perf_evlist *evlist);
+bool perf_evlist__exclude_kernel(struct evlist *evlist);
-void perf_evlist__force_leader(struct perf_evlist *evlist);
+void perf_evlist__force_leader(struct evlist *evlist);
-struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evlist,
- struct perf_evsel *evsel);
+struct evsel *perf_evlist__reset_weak_group(struct evlist *evlist,
+ struct evsel *evsel);
#endif /* __PERF_EVLIST_H */
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index a6f572a40deb..85825384f9e8 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
*
* Parts came from builtin-{top,stat,record}.c, see those files for further
* copyright notes.
- *
- * Released under the GPL v2. (and only v2, not any later version)
*/
#include <byteswap.h>
@@ -18,57 +17,65 @@
#include <linux/perf_event.h>
#include <linux/compiler.h>
#include <linux/err.h>
+#include <linux/zalloc.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <dirent.h>
+#include <stdlib.h>
+#include <perf/evsel.h>
#include "asm/bug.h"
#include "callchain.h"
#include "cgroup.h"
+#include "counts.h"
#include "event.h"
#include "evsel.h"
#include "evlist.h"
-#include "util.h"
#include "cpumap.h"
#include "thread_map.h"
#include "target.h"
#include "perf_regs.h"
+#include "record.h"
#include "debug.h"
#include "trace-event.h"
#include "stat.h"
+#include "string2.h"
#include "memswap.h"
+#include "util.h"
+#include "../perf-sys.h"
#include "util/parse-branch-options.h"
+#include <internal/xyarray.h>
-#include "sane_ctype.h"
+#include <linux/ctype.h>
struct perf_missing_features perf_missing_features;
static clockid_t clockid;
-static int perf_evsel__no_extra_init(struct perf_evsel *evsel __maybe_unused)
+static int perf_evsel__no_extra_init(struct evsel *evsel __maybe_unused)
{
return 0;
}
void __weak test_attr__ready(void) { }
-static void perf_evsel__no_extra_fini(struct perf_evsel *evsel __maybe_unused)
+static void perf_evsel__no_extra_fini(struct evsel *evsel __maybe_unused)
{
}
static struct {
size_t size;
- int (*init)(struct perf_evsel *evsel);
- void (*fini)(struct perf_evsel *evsel);
+ int (*init)(struct evsel *evsel);
+ void (*fini)(struct evsel *evsel);
} perf_evsel__object = {
- .size = sizeof(struct perf_evsel),
+ .size = sizeof(struct evsel),
.init = perf_evsel__no_extra_init,
.fini = perf_evsel__no_extra_fini,
};
int perf_evsel__object_config(size_t object_size,
- int (*init)(struct perf_evsel *evsel),
- void (*fini)(struct perf_evsel *evsel))
+ int (*init)(struct evsel *evsel),
+ void (*fini)(struct evsel *evsel))
{
if (object_size == 0)
@@ -89,7 +96,7 @@ set_methods:
return 0;
}
-#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
+#define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y))
int __perf_evsel__sample_size(u64 sample_type)
{
@@ -113,7 +120,7 @@ int __perf_evsel__sample_size(u64 sample_type)
*
* This function returns the position of the event id (PERF_SAMPLE_ID or
* PERF_SAMPLE_IDENTIFIER) in a sample event i.e. in the array of struct
- * sample_event.
+ * perf_record_sample.
*/
static int __perf_evsel__calc_id_pos(u64 sample_type)
{
@@ -167,33 +174,33 @@ static int __perf_evsel__calc_is_pos(u64 sample_type)
return idx;
}
-void perf_evsel__calc_id_pos(struct perf_evsel *evsel)
+void perf_evsel__calc_id_pos(struct evsel *evsel)
{
- evsel->id_pos = __perf_evsel__calc_id_pos(evsel->attr.sample_type);
- evsel->is_pos = __perf_evsel__calc_is_pos(evsel->attr.sample_type);
+ evsel->id_pos = __perf_evsel__calc_id_pos(evsel->core.attr.sample_type);
+ evsel->is_pos = __perf_evsel__calc_is_pos(evsel->core.attr.sample_type);
}
-void __perf_evsel__set_sample_bit(struct perf_evsel *evsel,
+void __perf_evsel__set_sample_bit(struct evsel *evsel,
enum perf_event_sample_format bit)
{
- if (!(evsel->attr.sample_type & bit)) {
- evsel->attr.sample_type |= bit;
+ if (!(evsel->core.attr.sample_type & bit)) {
+ evsel->core.attr.sample_type |= bit;
evsel->sample_size += sizeof(u64);
perf_evsel__calc_id_pos(evsel);
}
}
-void __perf_evsel__reset_sample_bit(struct perf_evsel *evsel,
+void __perf_evsel__reset_sample_bit(struct evsel *evsel,
enum perf_event_sample_format bit)
{
- if (evsel->attr.sample_type & bit) {
- evsel->attr.sample_type &= ~bit;
+ if (evsel->core.attr.sample_type & bit) {
+ evsel->core.attr.sample_type &= ~bit;
evsel->sample_size -= sizeof(u64);
perf_evsel__calc_id_pos(evsel);
}
}
-void perf_evsel__set_sample_id(struct perf_evsel *evsel,
+void perf_evsel__set_sample_id(struct evsel *evsel,
bool can_sample_identifier)
{
if (can_sample_identifier) {
@@ -202,7 +209,7 @@ void perf_evsel__set_sample_id(struct perf_evsel *evsel,
} else {
perf_evsel__set_sample_bit(evsel, ID);
}
- evsel->attr.read_format |= PERF_FORMAT_ID;
+ evsel->core.attr.read_format |= PERF_FORMAT_ID;
}
/**
@@ -213,7 +220,7 @@ void perf_evsel__set_sample_id(struct perf_evsel *evsel,
*
* Return %true if event is function trace event
*/
-bool perf_evsel__is_function_event(struct perf_evsel *evsel)
+bool perf_evsel__is_function_event(struct evsel *evsel)
{
#define FUNCTION_EVENT "ftrace:function"
@@ -223,19 +230,19 @@ bool perf_evsel__is_function_event(struct perf_evsel *evsel)
#undef FUNCTION_EVENT
}
-void perf_evsel__init(struct perf_evsel *evsel,
- struct perf_event_attr *attr, int idx)
+void evsel__init(struct evsel *evsel,
+ struct perf_event_attr *attr, int idx)
{
+ perf_evsel__init(&evsel->core, attr);
evsel->idx = idx;
evsel->tracking = !idx;
- evsel->attr = *attr;
evsel->leader = evsel;
evsel->unit = "";
evsel->scale = 1.0;
evsel->max_events = ULONG_MAX;
evsel->evlist = NULL;
+ evsel->bpf_obj = NULL;
evsel->bpf_fd = -1;
- INIT_LIST_HEAD(&evsel->node);
INIT_LIST_HEAD(&evsel->config_terms);
perf_evsel__object.init(evsel);
evsel->sample_size = __perf_evsel__sample_size(attr->sample_type);
@@ -248,18 +255,18 @@ void perf_evsel__init(struct perf_evsel *evsel,
evsel->pmu_name = NULL;
}
-struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)
+struct evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)
{
- struct perf_evsel *evsel = zalloc(perf_evsel__object.size);
+ struct evsel *evsel = zalloc(perf_evsel__object.size);
if (!evsel)
return NULL;
- perf_evsel__init(evsel, attr, idx);
+ evsel__init(evsel, attr, idx);
if (perf_evsel__is_bpf_output(evsel)) {
- evsel->attr.sample_type |= (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
+ evsel->core.attr.sample_type |= (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD),
- evsel->attr.sample_period = 1;
+ evsel->core.attr.sample_period = 1;
}
if (perf_evsel__is_clock(evsel)) {
@@ -278,17 +285,17 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)
static bool perf_event_can_profile_kernel(void)
{
- return geteuid() == 0 || perf_event_paranoid() == -1;
+ return perf_event_paranoid_check(1);
}
-struct perf_evsel *perf_evsel__new_cycles(bool precise)
+struct evsel *perf_evsel__new_cycles(bool precise)
{
struct perf_event_attr attr = {
.type = PERF_TYPE_HARDWARE,
.config = PERF_COUNT_HW_CPU_CYCLES,
.exclude_kernel = !perf_event_can_profile_kernel(),
};
- struct perf_evsel *evsel;
+ struct evsel *evsel;
event_attr_init(&attr);
@@ -300,7 +307,7 @@ struct perf_evsel *perf_evsel__new_cycles(bool precise)
* to kick in when we return and before perf_evsel__open() is called.
*/
new_event:
- evsel = perf_evsel__new(&attr);
+ evsel = evsel__new(&attr);
if (evsel == NULL)
goto out;
@@ -315,7 +322,7 @@ new_event:
out:
return evsel;
error_free:
- perf_evsel__delete(evsel);
+ evsel__delete(evsel);
evsel = NULL;
goto out;
}
@@ -323,9 +330,9 @@ error_free:
/*
* Returns pointer with encoded error via <linux/err.h> interface.
*/
-struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx)
+struct evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx)
{
- struct perf_evsel *evsel = zalloc(perf_evsel__object.size);
+ struct evsel *evsel = zalloc(perf_evsel__object.size);
int err = -ENOMEM;
if (evsel == NULL) {
@@ -349,7 +356,7 @@ struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int
event_attr_init(&attr);
attr.config = evsel->tp_format->id;
attr.sample_period = 1;
- perf_evsel__init(evsel, &attr, idx);
+ evsel__init(evsel, &attr, idx);
}
return evsel;
@@ -382,10 +389,10 @@ static const char *__perf_evsel__hw_name(u64 config)
return "unknown-hardware";
}
-static int perf_evsel__add_modifiers(struct perf_evsel *evsel, char *bf, size_t size)
+static int perf_evsel__add_modifiers(struct evsel *evsel, char *bf, size_t size)
{
int colon = 0, r = 0;
- struct perf_event_attr *attr = &evsel->attr;
+ struct perf_event_attr *attr = &evsel->core.attr;
bool exclude_guest_default = false;
#define MOD_PRINT(context, mod) do { \
@@ -418,9 +425,9 @@ static int perf_evsel__add_modifiers(struct perf_evsel *evsel, char *bf, size_t
return r;
}
-static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size)
+static int perf_evsel__hw_name(struct evsel *evsel, char *bf, size_t size)
{
- int r = scnprintf(bf, size, "%s", __perf_evsel__hw_name(evsel->attr.config));
+ int r = scnprintf(bf, size, "%s", __perf_evsel__hw_name(evsel->core.attr.config));
return r + perf_evsel__add_modifiers(evsel, bf + r, size - r);
}
@@ -444,9 +451,9 @@ static const char *__perf_evsel__sw_name(u64 config)
return "unknown-software";
}
-static int perf_evsel__sw_name(struct perf_evsel *evsel, char *bf, size_t size)
+static int perf_evsel__sw_name(struct evsel *evsel, char *bf, size_t size)
{
- int r = scnprintf(bf, size, "%s", __perf_evsel__sw_name(evsel->attr.config));
+ int r = scnprintf(bf, size, "%s", __perf_evsel__sw_name(evsel->core.attr.config));
return r + perf_evsel__add_modifiers(evsel, bf + r, size - r);
}
@@ -468,9 +475,9 @@ static int __perf_evsel__bp_name(char *bf, size_t size, u64 addr, u64 type)
return r;
}
-static int perf_evsel__bp_name(struct perf_evsel *evsel, char *bf, size_t size)
+static int perf_evsel__bp_name(struct evsel *evsel, char *bf, size_t size)
{
- struct perf_event_attr *attr = &evsel->attr;
+ struct perf_event_attr *attr = &evsel->core.attr;
int r = __perf_evsel__bp_name(bf, size, attr->bp_addr, attr->bp_type);
return r + perf_evsel__add_modifiers(evsel, bf + r, size - r);
}
@@ -568,15 +575,15 @@ out_err:
return scnprintf(bf, size, "%s", err);
}
-static int perf_evsel__hw_cache_name(struct perf_evsel *evsel, char *bf, size_t size)
+static int perf_evsel__hw_cache_name(struct evsel *evsel, char *bf, size_t size)
{
- int ret = __perf_evsel__hw_cache_name(evsel->attr.config, bf, size);
+ int ret = __perf_evsel__hw_cache_name(evsel->core.attr.config, bf, size);
return ret + perf_evsel__add_modifiers(evsel, bf + ret, size - ret);
}
-static int perf_evsel__raw_name(struct perf_evsel *evsel, char *bf, size_t size)
+static int perf_evsel__raw_name(struct evsel *evsel, char *bf, size_t size)
{
- int ret = scnprintf(bf, size, "raw 0x%" PRIx64, evsel->attr.config);
+ int ret = scnprintf(bf, size, "raw 0x%" PRIx64, evsel->core.attr.config);
return ret + perf_evsel__add_modifiers(evsel, bf + ret, size - ret);
}
@@ -586,14 +593,17 @@ static int perf_evsel__tool_name(char *bf, size_t size)
return ret;
}
-const char *perf_evsel__name(struct perf_evsel *evsel)
+const char *perf_evsel__name(struct evsel *evsel)
{
char bf[128];
+ if (!evsel)
+ goto out_unknown;
+
if (evsel->name)
return evsel->name;
- switch (evsel->attr.type) {
+ switch (evsel->core.attr.type) {
case PERF_TYPE_RAW:
perf_evsel__raw_name(evsel, bf, sizeof(bf));
break;
@@ -623,16 +633,19 @@ const char *perf_evsel__name(struct perf_evsel *evsel)
default:
scnprintf(bf, sizeof(bf), "unknown attr type: %d",
- evsel->attr.type);
+ evsel->core.attr.type);
break;
}
evsel->name = strdup(bf);
- return evsel->name ?: "unknown";
+ if (evsel->name)
+ return evsel->name;
+out_unknown:
+ return "unknown";
}
-const char *perf_evsel__group_name(struct perf_evsel *evsel)
+const char *perf_evsel__group_name(struct evsel *evsel)
{
return evsel->group_name ?: "anon group";
}
@@ -647,10 +660,10 @@ const char *perf_evsel__group_name(struct perf_evsel *evsel)
* For record -e 'cycles,instructions' and report --group
* 'cycles:u, instructions:u'
*/
-int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size)
+int perf_evsel__group_desc(struct evsel *evsel, char *buf, size_t size)
{
int ret = 0;
- struct perf_evsel *pos;
+ struct evsel *pos;
const char *group_name = perf_evsel__group_name(evsel);
if (!evsel->forced_leader)
@@ -669,17 +682,21 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size)
return ret;
}
-static void __perf_evsel__config_callchain(struct perf_evsel *evsel,
+static void __perf_evsel__config_callchain(struct evsel *evsel,
struct record_opts *opts,
struct callchain_param *param)
{
bool function = perf_evsel__is_function_event(evsel);
- struct perf_event_attr *attr = &evsel->attr;
+ struct perf_event_attr *attr = &evsel->core.attr;
perf_evsel__set_sample_bit(evsel, CALLCHAIN);
attr->sample_max_stack = param->max_stack;
+ if (opts->kernel_callchains)
+ attr->exclude_callchain_user = 1;
+ if (opts->user_callchains)
+ attr->exclude_callchain_kernel = 1;
if (param->record_mode == CALLCHAIN_LBR) {
if (!opts->branch_stack) {
if (attr->exclude_user) {
@@ -702,7 +719,14 @@ static void __perf_evsel__config_callchain(struct perf_evsel *evsel,
if (!function) {
perf_evsel__set_sample_bit(evsel, REGS_USER);
perf_evsel__set_sample_bit(evsel, STACK_USER);
- attr->sample_regs_user |= PERF_REGS_MASK;
+ if (opts->sample_user_regs && DWARF_MINIMAL_REGS != PERF_REGS_MASK) {
+ attr->sample_regs_user |= DWARF_MINIMAL_REGS;
+ pr_warning("WARNING: The use of --call-graph=dwarf may require all the user registers, "
+ "specifying a subset with --user-regs may render DWARF unwinding unreliable, "
+ "so the minimal registers set (IP, SP) is explicitly forced.\n");
+ } else {
+ attr->sample_regs_user |= PERF_REGS_MASK;
+ }
attr->sample_stack_user = param->dump_size;
attr->exclude_callchain_user = 1;
} else {
@@ -717,7 +741,7 @@ static void __perf_evsel__config_callchain(struct perf_evsel *evsel,
}
}
-void perf_evsel__config_callchain(struct perf_evsel *evsel,
+void perf_evsel__config_callchain(struct evsel *evsel,
struct record_opts *opts,
struct callchain_param *param)
{
@@ -726,10 +750,10 @@ void perf_evsel__config_callchain(struct perf_evsel *evsel,
}
static void
-perf_evsel__reset_callgraph(struct perf_evsel *evsel,
+perf_evsel__reset_callgraph(struct evsel *evsel,
struct callchain_param *param)
{
- struct perf_event_attr *attr = &evsel->attr;
+ struct perf_event_attr *attr = &evsel->core.attr;
perf_evsel__reset_sample_bit(evsel, CALLCHAIN);
if (param->record_mode == CALLCHAIN_LBR) {
@@ -743,12 +767,12 @@ perf_evsel__reset_callgraph(struct perf_evsel *evsel,
}
}
-static void apply_config_terms(struct perf_evsel *evsel,
+static void apply_config_terms(struct evsel *evsel,
struct record_opts *opts, bool track)
{
struct perf_evsel_config_term *term;
struct list_head *config_terms = &evsel->config_terms;
- struct perf_event_attr *attr = &evsel->attr;
+ struct perf_event_attr *attr = &evsel->core.attr;
/* callgraph default */
struct callchain_param param = {
.record_mode = callchain_param.record_mode,
@@ -815,6 +839,9 @@ static void apply_config_terms(struct perf_evsel *evsel,
break;
case PERF_EVSEL__CONFIG_TERM_PERCORE:
break;
+ case PERF_EVSEL__CONFIG_TERM_AUX_OUTPUT:
+ attr->aux_output = term->val.aux_output ? 1 : 0;
+ break;
default:
break;
}
@@ -861,17 +888,17 @@ static void apply_config_terms(struct perf_evsel *evsel,
if (sample_address) {
perf_evsel__set_sample_bit(evsel, ADDR);
perf_evsel__set_sample_bit(evsel, DATA_SRC);
- evsel->attr.mmap_data = track;
+ evsel->core.attr.mmap_data = track;
}
perf_evsel__config_callchain(evsel, opts, &param);
}
}
}
-static bool is_dummy_event(struct perf_evsel *evsel)
+static bool is_dummy_event(struct evsel *evsel)
{
- return (evsel->attr.type == PERF_TYPE_SOFTWARE) &&
- (evsel->attr.config == PERF_COUNT_SW_DUMMY);
+ return (evsel->core.attr.type == PERF_TYPE_SOFTWARE) &&
+ (evsel->core.attr.config == PERF_COUNT_SW_DUMMY);
}
/*
@@ -902,11 +929,11 @@ static bool is_dummy_event(struct perf_evsel *evsel)
* enable/disable events specifically, as there's no
* initial traced exec call.
*/
-void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts,
+void perf_evsel__config(struct evsel *evsel, struct record_opts *opts,
struct callchain_param *callchain)
{
- struct perf_evsel *leader = evsel->leader;
- struct perf_event_attr *attr = &evsel->attr;
+ struct evsel *leader = evsel->leader;
+ struct perf_event_attr *attr = &evsel->core.attr;
int track = evsel->tracking;
bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread;
@@ -930,7 +957,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts,
* Apply group format only if we belong to group
* with more than one members.
*/
- if (leader->nr_members > 1) {
+ if (leader->core.nr_members > 1) {
attr->read_format |= PERF_FORMAT_GROUP;
attr->inherit = 0;
}
@@ -967,14 +994,14 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts,
* event to follow the master sample_type to ease up
* report.
*/
- attr->sample_type = leader->attr.sample_type;
+ attr->sample_type = leader->core.attr.sample_type;
}
if (opts->no_samples)
attr->sample_freq = 0;
if (opts->inherit_stat) {
- evsel->attr.read_format |=
+ evsel->core.attr.read_format |=
PERF_FORMAT_TOTAL_TIME_ENABLED |
PERF_FORMAT_TOTAL_TIME_RUNNING |
PERF_FORMAT_ID;
@@ -992,7 +1019,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts,
* fault handler and its overall trickiness nature.
*/
if (perf_evsel__is_function_event(evsel))
- evsel->attr.exclude_callchain_user = 1;
+ evsel->core.attr.exclude_callchain_user = 1;
if (callchain && callchain->enabled && !evsel->no_aux_samples)
perf_evsel__config_callchain(evsel, opts, callchain);
@@ -1048,8 +1075,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts,
attr->mmap2 = track && !perf_missing_features.mmap2;
attr->comm = track;
attr->ksymbol = track && !perf_missing_features.ksymbol;
- attr->bpf_event = track && !opts->no_bpf_event &&
- !perf_missing_features.bpf_event;
+ attr->bpf_event = track && !opts->no_bpf_event && !perf_missing_features.bpf;
if (opts->record_namespaces)
attr->namespaces = track;
@@ -1061,7 +1087,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts,
perf_evsel__set_sample_bit(evsel, TRANSACTION);
if (opts->running_time) {
- evsel->attr.read_format |=
+ evsel->core.attr.read_format |=
PERF_FORMAT_TOTAL_TIME_ENABLED |
PERF_FORMAT_TOTAL_TIME_RUNNING;
}
@@ -1107,8 +1133,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts,
attr->exclude_user = 1;
}
- if (evsel->own_cpus || evsel->unit)
- evsel->attr.read_format |= PERF_FORMAT_ID;
+ if (evsel->core.own_cpus || evsel->unit)
+ evsel->core.attr.read_format |= PERF_FORMAT_ID;
/*
* Apply event specific term settings,
@@ -1135,51 +1161,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts,
perf_evsel__reset_sample_bit(evsel, BRANCH_STACK);
}
-static int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
-{
- if (evsel->system_wide)
- nthreads = 1;
-
- evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int));
-
- if (evsel->fd) {
- int cpu, thread;
- for (cpu = 0; cpu < ncpus; cpu++) {
- for (thread = 0; thread < nthreads; thread++) {
- FD(evsel, cpu, thread) = -1;
- }
- }
- }
-
- return evsel->fd != NULL ? 0 : -ENOMEM;
-}
-
-static int perf_evsel__run_ioctl(struct perf_evsel *evsel,
- int ioc, void *arg)
-{
- int cpu, thread;
-
- for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++) {
- for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) {
- int fd = FD(evsel, cpu, thread),
- err = ioctl(fd, ioc, arg);
-
- if (err)
- return err;
- }
- }
-
- return 0;
-}
-
-int perf_evsel__apply_filter(struct perf_evsel *evsel, const char *filter)
-{
- return perf_evsel__run_ioctl(evsel,
- PERF_EVENT_IOC_SET_FILTER,
- (void *)filter);
-}
-
-int perf_evsel__set_filter(struct perf_evsel *evsel, const char *filter)
+int perf_evsel__set_filter(struct evsel *evsel, const char *filter)
{
char *new_filter = strdup(filter);
@@ -1192,7 +1174,7 @@ int perf_evsel__set_filter(struct perf_evsel *evsel, const char *filter)
return -1;
}
-static int perf_evsel__append_filter(struct perf_evsel *evsel,
+static int perf_evsel__append_filter(struct evsel *evsel,
const char *fmt, const char *filter)
{
char *new_filter;
@@ -1209,19 +1191,19 @@ static int perf_evsel__append_filter(struct perf_evsel *evsel,
return -1;
}
-int perf_evsel__append_tp_filter(struct perf_evsel *evsel, const char *filter)
+int perf_evsel__append_tp_filter(struct evsel *evsel, const char *filter)
{
return perf_evsel__append_filter(evsel, "(%s) && (%s)", filter);
}
-int perf_evsel__append_addr_filter(struct perf_evsel *evsel, const char *filter)
+int perf_evsel__append_addr_filter(struct evsel *evsel, const char *filter)
{
return perf_evsel__append_filter(evsel, "%s,%s", filter);
}
-int perf_evsel__enable(struct perf_evsel *evsel)
+int evsel__enable(struct evsel *evsel)
{
- int err = perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_ENABLE, 0);
+ int err = perf_evsel__enable(&evsel->core);
if (!err)
evsel->disabled = false;
@@ -1229,9 +1211,9 @@ int perf_evsel__enable(struct perf_evsel *evsel)
return err;
}
-int perf_evsel__disable(struct perf_evsel *evsel)
+int evsel__disable(struct evsel *evsel)
{
- int err = perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_DISABLE, 0);
+ int err = perf_evsel__disable(&evsel->core);
/*
* We mark it disabled here so that tools that disable a event can
* ignore events after they disable it. I.e. the ring buffer may have
@@ -1244,7 +1226,7 @@ int perf_evsel__disable(struct perf_evsel *evsel)
return err;
}
-int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads)
+int perf_evsel__alloc_id(struct evsel *evsel, int ncpus, int nthreads)
{
if (ncpus == 0 || nthreads == 0)
return 0;
@@ -1266,64 +1248,48 @@ int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads)
return 0;
}
-static void perf_evsel__free_fd(struct perf_evsel *evsel)
-{
- xyarray__delete(evsel->fd);
- evsel->fd = NULL;
-}
-
-static void perf_evsel__free_id(struct perf_evsel *evsel)
+static void perf_evsel__free_id(struct evsel *evsel)
{
xyarray__delete(evsel->sample_id);
evsel->sample_id = NULL;
zfree(&evsel->id);
+ evsel->ids = 0;
}
-static void perf_evsel__free_config_terms(struct perf_evsel *evsel)
+static void perf_evsel__free_config_terms(struct evsel *evsel)
{
struct perf_evsel_config_term *term, *h;
list_for_each_entry_safe(term, h, &evsel->config_terms, list) {
- list_del(&term->list);
+ list_del_init(&term->list);
free(term);
}
}
-void perf_evsel__close_fd(struct perf_evsel *evsel)
+void perf_evsel__exit(struct evsel *evsel)
{
- int cpu, thread;
-
- for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++)
- for (thread = 0; thread < xyarray__max_y(evsel->fd); ++thread) {
- close(FD(evsel, cpu, thread));
- FD(evsel, cpu, thread) = -1;
- }
-}
-
-void perf_evsel__exit(struct perf_evsel *evsel)
-{
- assert(list_empty(&evsel->node));
+ assert(list_empty(&evsel->core.node));
assert(evsel->evlist == NULL);
perf_evsel__free_counts(evsel);
- perf_evsel__free_fd(evsel);
+ perf_evsel__free_fd(&evsel->core);
perf_evsel__free_id(evsel);
perf_evsel__free_config_terms(evsel);
cgroup__put(evsel->cgrp);
- cpu_map__put(evsel->cpus);
- cpu_map__put(evsel->own_cpus);
- thread_map__put(evsel->threads);
+ perf_cpu_map__put(evsel->core.cpus);
+ perf_cpu_map__put(evsel->core.own_cpus);
+ perf_thread_map__put(evsel->core.threads);
zfree(&evsel->group_name);
zfree(&evsel->name);
perf_evsel__object.fini(evsel);
}
-void perf_evsel__delete(struct perf_evsel *evsel)
+void evsel__delete(struct evsel *evsel)
{
perf_evsel__exit(evsel);
free(evsel);
}
-void perf_evsel__compute_deltas(struct perf_evsel *evsel, int cpu, int thread,
+void perf_evsel__compute_deltas(struct evsel *evsel, int cpu, int thread,
struct perf_counts_values *count)
{
struct perf_counts_values tmp;
@@ -1363,57 +1329,16 @@ void perf_counts_values__scale(struct perf_counts_values *count,
*pscaled = scaled;
}
-static int perf_evsel__read_size(struct perf_evsel *evsel)
-{
- u64 read_format = evsel->attr.read_format;
- int entry = sizeof(u64); /* value */
- int size = 0;
- int nr = 1;
-
- if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
- size += sizeof(u64);
-
- if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
- size += sizeof(u64);
-
- if (read_format & PERF_FORMAT_ID)
- entry += sizeof(u64);
-
- if (read_format & PERF_FORMAT_GROUP) {
- nr = evsel->nr_members;
- size += sizeof(u64);
- }
-
- size += entry * nr;
- return size;
-}
-
-int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread,
- struct perf_counts_values *count)
-{
- size_t size = perf_evsel__read_size(evsel);
-
- memset(count, 0, sizeof(*count));
-
- if (FD(evsel, cpu, thread) < 0)
- return -EINVAL;
-
- if (readn(FD(evsel, cpu, thread), count->values, size) <= 0)
- return -errno;
-
- return 0;
-}
-
static int
-perf_evsel__read_one(struct perf_evsel *evsel, int cpu, int thread)
+perf_evsel__read_one(struct evsel *evsel, int cpu, int thread)
{
struct perf_counts_values *count = perf_counts(evsel->counts, cpu, thread);
- return perf_evsel__read(evsel, cpu, thread, count);
+ return perf_evsel__read(&evsel->core, cpu, thread, count);
}
static void
-perf_evsel__set_count(struct perf_evsel *counter, int cpu, int thread,
+perf_evsel__set_count(struct evsel *counter, int cpu, int thread,
u64 val, u64 ena, u64 run)
{
struct perf_counts_values *count;
@@ -1423,20 +1348,21 @@ perf_evsel__set_count(struct perf_evsel *counter, int cpu, int thread,
count->val = val;
count->ena = ena;
count->run = run;
- count->loaded = true;
+
+ perf_counts__set_loaded(counter->counts, cpu, thread, true);
}
static int
-perf_evsel__process_group_data(struct perf_evsel *leader,
+perf_evsel__process_group_data(struct evsel *leader,
int cpu, int thread, u64 *data)
{
- u64 read_format = leader->attr.read_format;
+ u64 read_format = leader->core.attr.read_format;
struct sample_read_value *v;
u64 nr, ena = 0, run = 0, i;
nr = *data++;
- if (nr != (u64) leader->nr_members)
+ if (nr != (u64) leader->core.nr_members)
return -EINVAL;
if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
@@ -1451,7 +1377,7 @@ perf_evsel__process_group_data(struct perf_evsel *leader,
v[0].value, ena, run);
for (i = 1; i < nr; i++) {
- struct perf_evsel *counter;
+ struct evsel *counter;
counter = perf_evlist__id2evsel(leader->evlist, v[i].id);
if (!counter)
@@ -1465,11 +1391,11 @@ perf_evsel__process_group_data(struct perf_evsel *leader,
}
static int
-perf_evsel__read_group(struct perf_evsel *leader, int cpu, int thread)
+perf_evsel__read_group(struct evsel *leader, int cpu, int thread)
{
struct perf_stat_evsel *ps = leader->stats;
- u64 read_format = leader->attr.read_format;
- int size = perf_evsel__read_size(leader);
+ u64 read_format = leader->core.attr.read_format;
+ int size = perf_evsel__read_size(&leader->core);
u64 *data = ps->group_data;
if (!(read_format & PERF_FORMAT_ID))
@@ -1495,9 +1421,9 @@ perf_evsel__read_group(struct perf_evsel *leader, int cpu, int thread)
return perf_evsel__process_group_data(leader, cpu, thread, data);
}
-int perf_evsel__read_counter(struct perf_evsel *evsel, int cpu, int thread)
+int perf_evsel__read_counter(struct evsel *evsel, int cpu, int thread)
{
- u64 read_format = evsel->attr.read_format;
+ u64 read_format = evsel->core.attr.read_format;
if (read_format & PERF_FORMAT_GROUP)
return perf_evsel__read_group(evsel, cpu, thread);
@@ -1505,7 +1431,7 @@ int perf_evsel__read_counter(struct perf_evsel *evsel, int cpu, int thread)
return perf_evsel__read_one(evsel, cpu, thread);
}
-int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
+int __perf_evsel__read_on_cpu(struct evsel *evsel,
int cpu, int thread, bool scale)
{
struct perf_counts_values count;
@@ -1526,9 +1452,9 @@ int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
return 0;
}
-static int get_group_fd(struct perf_evsel *evsel, int cpu, int thread)
+static int get_group_fd(struct evsel *evsel, int cpu, int thread)
{
- struct perf_evsel *leader = evsel->leader;
+ struct evsel *leader = evsel->leader;
int fd;
if (perf_evsel__is_group_leader(evsel))
@@ -1538,7 +1464,7 @@ static int get_group_fd(struct perf_evsel *evsel, int cpu, int thread)
* Leader must be already processed/open,
* if not it's a bug.
*/
- BUG_ON(!leader->fd);
+ BUG_ON(!leader->core.fd);
fd = FD(leader, cpu, thread);
BUG_ON(fd == -1);
@@ -1669,6 +1595,7 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,
PRINT_ATTRf(namespaces, p_unsigned);
PRINT_ATTRf(ksymbol, p_unsigned);
PRINT_ATTRf(bpf_event, p_unsigned);
+ PRINT_ATTRf(aux_output, p_unsigned);
PRINT_ATTRn("{ wakeup_events, wakeup_watermark }", wakeup_events, p_unsigned);
PRINT_ATTRf(bp_type, p_unsigned);
@@ -1691,7 +1618,7 @@ static int __open_attr__fprintf(FILE *fp, const char *name, const char *val,
return fprintf(fp, " %-32s %s\n", name, val);
}
-static void perf_evsel__remove_fd(struct perf_evsel *pos,
+static void perf_evsel__remove_fd(struct evsel *pos,
int nr_cpus, int nr_threads,
int thread_idx)
{
@@ -1700,11 +1627,11 @@ static void perf_evsel__remove_fd(struct perf_evsel *pos,
FD(pos, cpu, thread) = FD(pos, cpu, thread + 1);
}
-static int update_fds(struct perf_evsel *evsel,
+static int update_fds(struct evsel *evsel,
int nr_cpus, int cpu_idx,
int nr_threads, int thread_idx)
{
- struct perf_evsel *pos;
+ struct evsel *pos;
if (cpu_idx >= nr_cpus || thread_idx >= nr_threads)
return -EINVAL;
@@ -1724,12 +1651,12 @@ static int update_fds(struct perf_evsel *evsel,
return 0;
}
-static bool ignore_missing_thread(struct perf_evsel *evsel,
+static bool ignore_missing_thread(struct evsel *evsel,
int nr_cpus, int cpu,
- struct thread_map *threads,
+ struct perf_thread_map *threads,
int thread, int err)
{
- pid_t ignore_pid = thread_map__pid(threads, thread);
+ pid_t ignore_pid = perf_thread_map__pid(threads, thread);
if (!evsel->ignore_missing_thread)
return false;
@@ -1771,65 +1698,60 @@ static void display_attr(struct perf_event_attr *attr)
}
}
-static int perf_event_open(struct perf_evsel *evsel,
+static int perf_event_open(struct evsel *evsel,
pid_t pid, int cpu, int group_fd,
unsigned long flags)
{
- int precise_ip = evsel->attr.precise_ip;
+ int precise_ip = evsel->core.attr.precise_ip;
int fd;
while (1) {
pr_debug2("sys_perf_event_open: pid %d cpu %d group_fd %d flags %#lx",
pid, cpu, group_fd, flags);
- fd = sys_perf_event_open(&evsel->attr, pid, cpu, group_fd, flags);
+ fd = sys_perf_event_open(&evsel->core.attr, pid, cpu, group_fd, flags);
if (fd >= 0)
break;
- /*
- * Do quick precise_ip fallback if:
- * - there is precise_ip set in perf_event_attr
- * - maximum precise is requested
- * - sys_perf_event_open failed with ENOTSUP error,
- * which is associated with wrong precise_ip
- */
- if (!precise_ip || !evsel->precise_max || (errno != ENOTSUP))
+ /* Do not try less precise if not requested. */
+ if (!evsel->precise_max)
break;
/*
* We tried all the precise_ip values, and it's
* still failing, so leave it to standard fallback.
*/
- if (!evsel->attr.precise_ip) {
- evsel->attr.precise_ip = precise_ip;
+ if (!evsel->core.attr.precise_ip) {
+ evsel->core.attr.precise_ip = precise_ip;
break;
}
pr_debug2("\nsys_perf_event_open failed, error %d\n", -ENOTSUP);
- evsel->attr.precise_ip--;
- pr_debug2("decreasing precise_ip by one (%d)\n", evsel->attr.precise_ip);
- display_attr(&evsel->attr);
+ evsel->core.attr.precise_ip--;
+ pr_debug2("decreasing precise_ip by one (%d)\n", evsel->core.attr.precise_ip);
+ display_attr(&evsel->core.attr);
}
return fd;
}
-int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
- struct thread_map *threads)
+int evsel__open(struct evsel *evsel, struct perf_cpu_map *cpus,
+ struct perf_thread_map *threads)
{
int cpu, thread, nthreads;
unsigned long flags = PERF_FLAG_FD_CLOEXEC;
int pid = -1, err;
enum { NO_CHANGE, SET_TO_MAX, INCREASED_MAX } set_rlimit = NO_CHANGE;
- if (perf_missing_features.write_backward && evsel->attr.write_backward)
+ if ((perf_missing_features.write_backward && evsel->core.attr.write_backward) ||
+ (perf_missing_features.aux_output && evsel->core.attr.aux_output))
return -EINVAL;
if (cpus == NULL) {
- static struct cpu_map *empty_cpu_map;
+ static struct perf_cpu_map *empty_cpu_map;
if (empty_cpu_map == NULL) {
- empty_cpu_map = cpu_map__dummy_new();
+ empty_cpu_map = perf_cpu_map__dummy_new();
if (empty_cpu_map == NULL)
return -ENOMEM;
}
@@ -1838,7 +1760,7 @@ int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
}
if (threads == NULL) {
- static struct thread_map *empty_thread_map;
+ static struct perf_thread_map *empty_thread_map;
if (empty_thread_map == NULL) {
empty_thread_map = thread_map__new_by_tid(-1);
@@ -1854,8 +1776,8 @@ int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
else
nthreads = threads->nr;
- if (evsel->fd == NULL &&
- perf_evsel__alloc_fd(evsel, cpus->nr, nthreads) < 0)
+ if (evsel->core.fd == NULL &&
+ perf_evsel__alloc_fd(&evsel->core, cpus->nr, nthreads) < 0)
return -ENOMEM;
if (evsel->cgrp) {
@@ -1865,31 +1787,31 @@ int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
fallback_missing_features:
if (perf_missing_features.clockid_wrong)
- evsel->attr.clockid = CLOCK_MONOTONIC; /* should always work */
+ evsel->core.attr.clockid = CLOCK_MONOTONIC; /* should always work */
if (perf_missing_features.clockid) {
- evsel->attr.use_clockid = 0;
- evsel->attr.clockid = 0;
+ evsel->core.attr.use_clockid = 0;
+ evsel->core.attr.clockid = 0;
}
if (perf_missing_features.cloexec)
flags &= ~(unsigned long)PERF_FLAG_FD_CLOEXEC;
if (perf_missing_features.mmap2)
- evsel->attr.mmap2 = 0;
+ evsel->core.attr.mmap2 = 0;
if (perf_missing_features.exclude_guest)
- evsel->attr.exclude_guest = evsel->attr.exclude_host = 0;
+ evsel->core.attr.exclude_guest = evsel->core.attr.exclude_host = 0;
if (perf_missing_features.lbr_flags)
- evsel->attr.branch_sample_type &= ~(PERF_SAMPLE_BRANCH_NO_FLAGS |
+ evsel->core.attr.branch_sample_type &= ~(PERF_SAMPLE_BRANCH_NO_FLAGS |
PERF_SAMPLE_BRANCH_NO_CYCLES);
- if (perf_missing_features.group_read && evsel->attr.inherit)
- evsel->attr.read_format &= ~(PERF_FORMAT_GROUP|PERF_FORMAT_ID);
+ if (perf_missing_features.group_read && evsel->core.attr.inherit)
+ evsel->core.attr.read_format &= ~(PERF_FORMAT_GROUP|PERF_FORMAT_ID);
if (perf_missing_features.ksymbol)
- evsel->attr.ksymbol = 0;
- if (perf_missing_features.bpf_event)
- evsel->attr.bpf_event = 0;
+ evsel->core.attr.ksymbol = 0;
+ if (perf_missing_features.bpf)
+ evsel->core.attr.bpf_event = 0;
retry_sample_id:
if (perf_missing_features.sample_id_all)
- evsel->attr.sample_id_all = 0;
+ evsel->core.attr.sample_id_all = 0;
- display_attr(&evsel->attr);
+ display_attr(&evsel->core.attr);
for (cpu = 0; cpu < cpus->nr; cpu++) {
@@ -1897,7 +1819,7 @@ retry_sample_id:
int fd, group_fd;
if (!evsel->cgrp && !evsel->system_wide)
- pid = thread_map__pid(threads, thread);
+ pid = perf_thread_map__pid(threads, thread);
group_fd = get_group_fd(evsel, cpu, thread);
retry_open:
@@ -1996,23 +1918,27 @@ try_fallback:
* Must probe features in the order they were added to the
* perf_event_attr interface.
*/
- if (!perf_missing_features.bpf_event && evsel->attr.bpf_event) {
- perf_missing_features.bpf_event = true;
+ if (!perf_missing_features.aux_output && evsel->core.attr.aux_output) {
+ perf_missing_features.aux_output = true;
+ pr_debug2("Kernel has no attr.aux_output support, bailing out\n");
+ goto out_close;
+ } else if (!perf_missing_features.bpf && evsel->core.attr.bpf_event) {
+ perf_missing_features.bpf = true;
pr_debug2("switching off bpf_event\n");
goto fallback_missing_features;
- } else if (!perf_missing_features.ksymbol && evsel->attr.ksymbol) {
+ } else if (!perf_missing_features.ksymbol && evsel->core.attr.ksymbol) {
perf_missing_features.ksymbol = true;
pr_debug2("switching off ksymbol\n");
goto fallback_missing_features;
- } else if (!perf_missing_features.write_backward && evsel->attr.write_backward) {
+ } else if (!perf_missing_features.write_backward && evsel->core.attr.write_backward) {
perf_missing_features.write_backward = true;
pr_debug2("switching off write_backward\n");
goto out_close;
- } else if (!perf_missing_features.clockid_wrong && evsel->attr.use_clockid) {
+ } else if (!perf_missing_features.clockid_wrong && evsel->core.attr.use_clockid) {
perf_missing_features.clockid_wrong = true;
pr_debug2("switching off clockid\n");
goto fallback_missing_features;
- } else if (!perf_missing_features.clockid && evsel->attr.use_clockid) {
+ } else if (!perf_missing_features.clockid && evsel->core.attr.use_clockid) {
perf_missing_features.clockid = true;
pr_debug2("switching off use_clockid\n");
goto fallback_missing_features;
@@ -2020,12 +1946,12 @@ try_fallback:
perf_missing_features.cloexec = true;
pr_debug2("switching off cloexec flag\n");
goto fallback_missing_features;
- } else if (!perf_missing_features.mmap2 && evsel->attr.mmap2) {
+ } else if (!perf_missing_features.mmap2 && evsel->core.attr.mmap2) {
perf_missing_features.mmap2 = true;
pr_debug2("switching off mmap2\n");
goto fallback_missing_features;
} else if (!perf_missing_features.exclude_guest &&
- (evsel->attr.exclude_guest || evsel->attr.exclude_host)) {
+ (evsel->core.attr.exclude_guest || evsel->core.attr.exclude_host)) {
perf_missing_features.exclude_guest = true;
pr_debug2("switching off exclude_guest, exclude_host\n");
goto fallback_missing_features;
@@ -2034,15 +1960,15 @@ try_fallback:
pr_debug2("switching off sample_id_all\n");
goto retry_sample_id;
} else if (!perf_missing_features.lbr_flags &&
- (evsel->attr.branch_sample_type &
+ (evsel->core.attr.branch_sample_type &
(PERF_SAMPLE_BRANCH_NO_CYCLES |
PERF_SAMPLE_BRANCH_NO_FLAGS))) {
perf_missing_features.lbr_flags = true;
pr_debug2("switching off branch sample type no (cycles/flags)\n");
goto fallback_missing_features;
} else if (!perf_missing_features.group_read &&
- evsel->attr.inherit &&
- (evsel->attr.read_format & PERF_FORMAT_GROUP) &&
+ evsel->core.attr.inherit &&
+ (evsel->core.attr.read_format & PERF_FORMAT_GROUP) &&
perf_evsel__is_group_leader(evsel)) {
perf_missing_features.group_read = true;
pr_debug2("switching off group read\n");
@@ -2062,33 +1988,30 @@ out_close:
return err;
}
-void perf_evsel__close(struct perf_evsel *evsel)
+void evsel__close(struct evsel *evsel)
{
- if (evsel->fd == NULL)
- return;
-
- perf_evsel__close_fd(evsel);
- perf_evsel__free_fd(evsel);
+ perf_evsel__close(&evsel->core);
+ perf_evsel__free_id(evsel);
}
-int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
- struct cpu_map *cpus)
+int perf_evsel__open_per_cpu(struct evsel *evsel,
+ struct perf_cpu_map *cpus)
{
- return perf_evsel__open(evsel, cpus, NULL);
+ return evsel__open(evsel, cpus, NULL);
}
-int perf_evsel__open_per_thread(struct perf_evsel *evsel,
- struct thread_map *threads)
+int perf_evsel__open_per_thread(struct evsel *evsel,
+ struct perf_thread_map *threads)
{
- return perf_evsel__open(evsel, NULL, threads);
+ return evsel__open(evsel, NULL, threads);
}
-static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel,
+static int perf_evsel__parse_id_sample(const struct evsel *evsel,
const union perf_event *event,
struct perf_sample *sample)
{
- u64 type = evsel->attr.sample_type;
- const u64 *array = event->sample.array;
+ u64 type = evsel->core.attr.sample_type;
+ const __u64 *array = event->sample.array;
bool swapped = evsel->needs_swap;
union u64_swap u;
@@ -2173,12 +2096,12 @@ perf_event__check_size(union perf_event *event, unsigned int sample_size)
return 0;
}
-int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
+int perf_evsel__parse_sample(struct evsel *evsel, union perf_event *event,
struct perf_sample *data)
{
- u64 type = evsel->attr.sample_type;
+ u64 type = evsel->core.attr.sample_type;
bool swapped = evsel->needs_swap;
- const u64 *array;
+ const __u64 *array;
u16 max_size = event->header.size;
const void *endp = (void *)event + max_size;
u64 sz;
@@ -2192,14 +2115,14 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
memset(data, 0, sizeof(*data));
data->cpu = data->pid = data->tid = -1;
data->stream_id = data->id = data->time = -1ULL;
- data->period = evsel->attr.sample_period;
+ data->period = evsel->core.attr.sample_period;
data->cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
data->misc = event->header.misc;
data->id = -1ULL;
data->data_src = PERF_MEM_DATA_SRC_NONE;
if (event->header.type != PERF_RECORD_SAMPLE) {
- if (!evsel->attr.sample_id_all)
+ if (!evsel->core.attr.sample_id_all)
return 0;
return perf_evsel__parse_id_sample(evsel, event, data);
}
@@ -2272,7 +2195,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
}
if (type & PERF_SAMPLE_READ) {
- u64 read_format = evsel->attr.read_format;
+ u64 read_format = evsel->core.attr.read_format;
OVERFLOW_CHECK_u64(array);
if (read_format & PERF_FORMAT_GROUP)
@@ -2377,7 +2300,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
array++;
if (data->user_regs.abi) {
- u64 mask = evsel->attr.sample_regs_user;
+ u64 mask = evsel->core.attr.sample_regs_user;
sz = hweight64(mask) * sizeof(u64);
OVERFLOW_CHECK(array, sz, max_size);
@@ -2433,7 +2356,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
array++;
if (data->intr_regs.abi != PERF_SAMPLE_REGS_ABI_NONE) {
- u64 mask = evsel->attr.sample_regs_intr;
+ u64 mask = evsel->core.attr.sample_regs_intr;
sz = hweight64(mask) * sizeof(u64);
OVERFLOW_CHECK(array, sz, max_size);
@@ -2452,12 +2375,12 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
return 0;
}
-int perf_evsel__parse_sample_timestamp(struct perf_evsel *evsel,
+int perf_evsel__parse_sample_timestamp(struct evsel *evsel,
union perf_event *event,
u64 *timestamp)
{
- u64 type = evsel->attr.sample_type;
- const u64 *array;
+ u64 type = evsel->core.attr.sample_type;
+ const __u64 *array;
if (!(type & PERF_SAMPLE_TIME))
return -1;
@@ -2467,7 +2390,7 @@ int perf_evsel__parse_sample_timestamp(struct perf_evsel *evsel,
.time = -1ULL,
};
- if (!evsel->attr.sample_id_all)
+ if (!evsel->core.attr.sample_id_all)
return -1;
if (perf_evsel__parse_id_sample(evsel, event, &data))
return -1;
@@ -2499,7 +2422,7 @@ int perf_evsel__parse_sample_timestamp(struct perf_evsel *evsel,
size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
u64 read_format)
{
- size_t sz, result = sizeof(struct sample_event);
+ size_t sz, result = sizeof(struct perf_record_sample);
if (type & PERF_SAMPLE_IDENTIFIER)
result += sizeof(u64);
@@ -2608,7 +2531,7 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type,
u64 read_format,
const struct perf_sample *sample)
{
- u64 *array;
+ __u64 *array;
size_t sz;
/*
* used for cross-endian analysis. See git commit 65014ab3
@@ -2773,12 +2696,12 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type,
return 0;
}
-struct tep_format_field *perf_evsel__field(struct perf_evsel *evsel, const char *name)
+struct tep_format_field *perf_evsel__field(struct evsel *evsel, const char *name)
{
return tep_find_field(evsel->tp_format, name);
}
-void *perf_evsel__rawptr(struct perf_evsel *evsel, struct perf_sample *sample,
+void *perf_evsel__rawptr(struct evsel *evsel, struct perf_sample *sample,
const char *name)
{
struct tep_format_field *field = perf_evsel__field(evsel, name);
@@ -2836,7 +2759,7 @@ u64 format_field__intval(struct tep_format_field *field, struct perf_sample *sam
return 0;
}
-u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample,
+u64 perf_evsel__intval(struct evsel *evsel, struct perf_sample *sample,
const char *name)
{
struct tep_format_field *field = perf_evsel__field(evsel, name);
@@ -2847,14 +2770,14 @@ u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample,
return field ? format_field__intval(field, sample, evsel->needs_swap) : 0;
}
-bool perf_evsel__fallback(struct perf_evsel *evsel, int err,
+bool perf_evsel__fallback(struct evsel *evsel, int err,
char *msg, size_t msgsize)
{
int paranoid;
if ((err == ENOENT || err == ENXIO || err == ENODEV) &&
- evsel->attr.type == PERF_TYPE_HARDWARE &&
- evsel->attr.config == PERF_COUNT_HW_CPU_CYCLES) {
+ evsel->core.attr.type == PERF_TYPE_HARDWARE &&
+ evsel->core.attr.config == PERF_COUNT_HW_CPU_CYCLES) {
/*
* If it's cycles then fall back to hrtimer based
* cpu-clock-tick sw counter, which is always available even if
@@ -2866,12 +2789,12 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err,
scnprintf(msg, msgsize, "%s",
"The cycles event is not supported, trying to fall back to cpu-clock-ticks");
- evsel->attr.type = PERF_TYPE_SOFTWARE;
- evsel->attr.config = PERF_COUNT_SW_CPU_CLOCK;
+ evsel->core.attr.type = PERF_TYPE_SOFTWARE;
+ evsel->core.attr.config = PERF_COUNT_SW_CPU_CLOCK;
zfree(&evsel->name);
return true;
- } else if (err == EACCES && !evsel->attr.exclude_kernel &&
+ } else if (err == EACCES && !evsel->core.attr.exclude_kernel &&
(paranoid = perf_event_paranoid()) > 1) {
const char *name = perf_evsel__name(evsel);
char *new_name;
@@ -2890,7 +2813,7 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err,
evsel->name = new_name;
scnprintf(msg, msgsize,
"kernel.perf_event_paranoid=%d, trying to fall back to excluding kernel samples", paranoid);
- evsel->attr.exclude_kernel = 1;
+ evsel->core.attr.exclude_kernel = 1;
return true;
}
@@ -2934,7 +2857,7 @@ static bool find_process(const char *name)
return ret ? false : true;
}
-int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,
+int perf_evsel__open_strerror(struct evsel *evsel, struct target *target,
int err, char *msg, size_t size)
{
char sbuf[STRERR_BUFSIZE];
@@ -2987,15 +2910,15 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,
"No such device - did you specify an out-of-range profile CPU?");
break;
case EOPNOTSUPP:
- if (evsel->attr.sample_period != 0)
+ if (evsel->core.attr.sample_period != 0)
return scnprintf(msg, size,
"%s: PMU Hardware doesn't support sampling/overflow-interrupts. Try 'perf stat'",
perf_evsel__name(evsel));
- if (evsel->attr.precise_ip)
+ if (evsel->core.attr.precise_ip)
return scnprintf(msg, size, "%s",
"\'precise\' request may not be supported. Try removing 'p' modifier.");
#if defined(__i386__) || defined(__x86_64__)
- if (evsel->attr.type == PERF_TYPE_HARDWARE)
+ if (evsel->core.attr.type == PERF_TYPE_HARDWARE)
return scnprintf(msg, size, "%s",
"No hardware sampling interrupt available.\n");
#endif
@@ -3007,12 +2930,14 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,
"We found oprofile daemon running, please stop it and try again.");
break;
case EINVAL:
- if (evsel->attr.write_backward && perf_missing_features.write_backward)
+ if (evsel->core.attr.write_backward && perf_missing_features.write_backward)
return scnprintf(msg, size, "Reading from overwrite event is not supported by this kernel.");
if (perf_missing_features.clockid)
return scnprintf(msg, size, "clockid feature not supported.");
if (perf_missing_features.clockid_wrong)
return scnprintf(msg, size, "wrong clockid (%d).", clockid);
+ if (perf_missing_features.aux_output)
+ return scnprintf(msg, size, "The 'aux_output' feature is not supported, update the kernel.");
break;
default:
break;
@@ -3025,19 +2950,19 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,
perf_evsel__name(evsel));
}
-struct perf_env *perf_evsel__env(struct perf_evsel *evsel)
+struct perf_env *perf_evsel__env(struct evsel *evsel)
{
if (evsel && evsel->evlist)
return evsel->evlist->env;
return NULL;
}
-static int store_evsel_ids(struct perf_evsel *evsel, struct perf_evlist *evlist)
+static int store_evsel_ids(struct evsel *evsel, struct evlist *evlist)
{
int cpu, thread;
- for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++) {
- for (thread = 0; thread < xyarray__max_y(evsel->fd);
+ for (cpu = 0; cpu < xyarray__max_x(evsel->core.fd); cpu++) {
+ for (thread = 0; thread < xyarray__max_y(evsel->core.fd);
thread++) {
int fd = FD(evsel, cpu, thread);
@@ -3050,10 +2975,10 @@ static int store_evsel_ids(struct perf_evsel *evsel, struct perf_evlist *evlist)
return 0;
}
-int perf_evsel__store_ids(struct perf_evsel *evsel, struct perf_evlist *evlist)
+int perf_evsel__store_ids(struct evsel *evsel, struct evlist *evlist)
{
- struct cpu_map *cpus = evsel->cpus;
- struct thread_map *threads = evsel->threads;
+ struct perf_cpu_map *cpus = evsel->core.cpus;
+ struct perf_thread_map *threads = evsel->core.threads;
if (perf_evsel__alloc_id(evsel, cpus->nr, threads->nr))
return -ENOMEM;
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index cad54e8ba522..68321d10eb2d 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -4,15 +4,18 @@
#include <linux/list.h>
#include <stdbool.h>
-#include <stddef.h>
+#include <stdio.h>
+#include <sys/types.h>
#include <linux/perf_event.h>
#include <linux/types.h>
-#include "xyarray.h"
+#include <internal/evsel.h>
+#include <perf/evsel.h>
#include "symbol_conf.h"
-#include "cpumap.h"
-#include "counts.h"
+#include <internal/cpumap.h>
-struct perf_evsel;
+struct addr_location;
+struct evsel;
+union perf_event;
/*
* Per fd, to map back from PERF_SAMPLE_ID to evsel, only used when there are
@@ -21,7 +24,14 @@ struct perf_evsel;
struct perf_sample_id {
struct hlist_node node;
u64 id;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
+ /*
+ * 'idx' will be used for AUX area sampling. A sample will have AUX area
+ * data that will be queued for decoding, where there are separate
+ * queues for each CPU (per-cpu tracing) or task (per-thread tracing).
+ * The sample ID can be used to lookup 'idx' which is effectively the
+ * queue number.
+ */
int idx;
int cpu;
pid_t tid;
@@ -51,6 +61,7 @@ enum term_type {
PERF_EVSEL__CONFIG_TERM_DRV_CFG,
PERF_EVSEL__CONFIG_TERM_BRANCH,
PERF_EVSEL__CONFIG_TERM_PERCORE,
+ PERF_EVSEL__CONFIG_TERM_AUX_OUTPUT,
};
struct perf_evsel_config_term {
@@ -69,6 +80,7 @@ struct perf_evsel_config_term {
char *branch;
unsigned long max_events;
bool percore;
+ bool aux_output;
} val;
bool weak;
};
@@ -82,28 +94,29 @@ enum perf_tool_event {
PERF_TOOL_DURATION_TIME = 1,
};
-/** struct perf_evsel - event selector
+struct bpf_object;
+struct perf_counts;
+struct xyarray;
+
+/** struct evsel - event selector
*
* @evlist - evlist this evsel is in, if it is in one.
- * @node - To insert it into evlist->entries or in other list_heads, say in
- * the event parsing routines.
+ * @core - libperf evsel object
* @name - Can be set to retain the original event name passed by the user,
* so that when showing results in tools such as 'perf stat', we
* show the name used, not some alias.
* @id_pos: the position of the event id (PERF_SAMPLE_ID or
* PERF_SAMPLE_IDENTIFIER) in a sample event i.e. in the array of
- * struct sample_event
+ * struct perf_record_sample
* @is_pos: the position (counting backwards) of the event id (PERF_SAMPLE_ID or
* PERF_SAMPLE_IDENTIFIER) in a non-sample event i.e. if sample_id_all
* is used there is an id sample appended to non-sample events
* @priv: And what is in its containing unnamed union are tool specific
*/
-struct perf_evsel {
- struct list_head node;
- struct perf_evlist *evlist;
- struct perf_event_attr attr;
+struct evsel {
+ struct perf_evsel core;
+ struct evlist *evlist;
char *filter;
- struct xyarray *fd;
struct xyarray *sample_id;
u64 *id;
struct perf_counts *counts;
@@ -122,9 +135,6 @@ struct perf_evsel {
u64 db_id;
struct cgroup *cgrp;
void *handler;
- struct cpu_map *cpus;
- struct cpu_map *own_cpus;
- struct thread_map *threads;
unsigned int sample_size;
int id_pos;
int is_pos;
@@ -145,19 +155,20 @@ struct perf_evsel {
bool use_uncore_alias;
/* parse modifier helper */
int exclude_GH;
- int nr_members;
int sample_read;
unsigned long *per_pkg_mask;
- struct perf_evsel *leader;
+ struct evsel *leader;
char *group_name;
bool cmdline_group_boundary;
struct list_head config_terms;
+ struct bpf_object *bpf_obj;
int bpf_fd;
bool auto_merge_stats;
bool merged_stat;
const char * metric_expr;
const char * metric_name;
- struct perf_evsel **metric_events;
+ struct evsel **metric_events;
+ struct evsel *metric_leader;
bool collect_stat;
bool weak_group;
bool percore;
@@ -184,73 +195,73 @@ struct perf_missing_features {
bool write_backward;
bool group_read;
bool ksymbol;
- bool bpf_event;
+ bool bpf;
+ bool aux_output;
};
extern struct perf_missing_features perf_missing_features;
-struct cpu_map;
+struct perf_cpu_map;
struct target;
struct thread_map;
struct record_opts;
-static inline struct cpu_map *perf_evsel__cpus(struct perf_evsel *evsel)
+static inline struct perf_cpu_map *evsel__cpus(struct evsel *evsel)
{
- return evsel->cpus;
+ return perf_evsel__cpus(&evsel->core);
}
-static inline int perf_evsel__nr_cpus(struct perf_evsel *evsel)
+static inline int perf_evsel__nr_cpus(struct evsel *evsel)
{
- return perf_evsel__cpus(evsel)->nr;
+ return evsel__cpus(evsel)->nr;
}
void perf_counts_values__scale(struct perf_counts_values *count,
bool scale, s8 *pscaled);
-void perf_evsel__compute_deltas(struct perf_evsel *evsel, int cpu, int thread,
+void perf_evsel__compute_deltas(struct evsel *evsel, int cpu, int thread,
struct perf_counts_values *count);
int perf_evsel__object_config(size_t object_size,
- int (*init)(struct perf_evsel *evsel),
- void (*fini)(struct perf_evsel *evsel));
+ int (*init)(struct evsel *evsel),
+ void (*fini)(struct evsel *evsel));
-struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx);
+struct evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx);
-static inline struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr)
+static inline struct evsel *evsel__new(struct perf_event_attr *attr)
{
return perf_evsel__new_idx(attr, 0);
}
-struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx);
+struct evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx);
/*
* Returns pointer with encoded error via <linux/err.h> interface.
*/
-static inline struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name)
+static inline struct evsel *perf_evsel__newtp(const char *sys, const char *name)
{
return perf_evsel__newtp_idx(sys, name, 0);
}
-struct perf_evsel *perf_evsel__new_cycles(bool precise);
+struct evsel *perf_evsel__new_cycles(bool precise);
struct tep_event *event_format__new(const char *sys, const char *name);
-void perf_evsel__init(struct perf_evsel *evsel,
- struct perf_event_attr *attr, int idx);
-void perf_evsel__exit(struct perf_evsel *evsel);
-void perf_evsel__delete(struct perf_evsel *evsel);
+void evsel__init(struct evsel *evsel, struct perf_event_attr *attr, int idx);
+void perf_evsel__exit(struct evsel *evsel);
+void evsel__delete(struct evsel *evsel);
struct callchain_param;
-void perf_evsel__config(struct perf_evsel *evsel,
+void perf_evsel__config(struct evsel *evsel,
struct record_opts *opts,
struct callchain_param *callchain);
-void perf_evsel__config_callchain(struct perf_evsel *evsel,
+void perf_evsel__config_callchain(struct evsel *evsel,
struct record_opts *opts,
struct callchain_param *callchain);
int __perf_evsel__sample_size(u64 sample_type);
-void perf_evsel__calc_id_pos(struct perf_evsel *evsel);
+void perf_evsel__calc_id_pos(struct evsel *evsel);
bool perf_evsel__is_cache_op_valid(u8 type, u8 op);
@@ -266,17 +277,16 @@ extern const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX];
extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX];
int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,
char *bf, size_t size);
-const char *perf_evsel__name(struct perf_evsel *evsel);
+const char *perf_evsel__name(struct evsel *evsel);
-const char *perf_evsel__group_name(struct perf_evsel *evsel);
-int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size);
+const char *perf_evsel__group_name(struct evsel *evsel);
+int perf_evsel__group_desc(struct evsel *evsel, char *buf, size_t size);
-int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads);
-void perf_evsel__close_fd(struct perf_evsel *evsel);
+int perf_evsel__alloc_id(struct evsel *evsel, int ncpus, int nthreads);
-void __perf_evsel__set_sample_bit(struct perf_evsel *evsel,
+void __perf_evsel__set_sample_bit(struct evsel *evsel,
enum perf_event_sample_format bit);
-void __perf_evsel__reset_sample_bit(struct perf_evsel *evsel,
+void __perf_evsel__reset_sample_bit(struct evsel *evsel,
enum perf_event_sample_format bit);
#define perf_evsel__set_sample_bit(evsel, bit) \
@@ -285,33 +295,32 @@ void __perf_evsel__reset_sample_bit(struct perf_evsel *evsel,
#define perf_evsel__reset_sample_bit(evsel, bit) \
__perf_evsel__reset_sample_bit(evsel, PERF_SAMPLE_##bit)
-void perf_evsel__set_sample_id(struct perf_evsel *evsel,
+void perf_evsel__set_sample_id(struct evsel *evsel,
bool use_sample_identifier);
-int perf_evsel__set_filter(struct perf_evsel *evsel, const char *filter);
-int perf_evsel__append_tp_filter(struct perf_evsel *evsel, const char *filter);
-int perf_evsel__append_addr_filter(struct perf_evsel *evsel,
+int perf_evsel__set_filter(struct evsel *evsel, const char *filter);
+int perf_evsel__append_tp_filter(struct evsel *evsel, const char *filter);
+int perf_evsel__append_addr_filter(struct evsel *evsel,
const char *filter);
-int perf_evsel__apply_filter(struct perf_evsel *evsel, const char *filter);
-int perf_evsel__enable(struct perf_evsel *evsel);
-int perf_evsel__disable(struct perf_evsel *evsel);
-
-int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
- struct cpu_map *cpus);
-int perf_evsel__open_per_thread(struct perf_evsel *evsel,
- struct thread_map *threads);
-int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
- struct thread_map *threads);
-void perf_evsel__close(struct perf_evsel *evsel);
+int evsel__enable(struct evsel *evsel);
+int evsel__disable(struct evsel *evsel);
+
+int perf_evsel__open_per_cpu(struct evsel *evsel,
+ struct perf_cpu_map *cpus);
+int perf_evsel__open_per_thread(struct evsel *evsel,
+ struct perf_thread_map *threads);
+int evsel__open(struct evsel *evsel, struct perf_cpu_map *cpus,
+ struct perf_thread_map *threads);
+void evsel__close(struct evsel *evsel);
struct perf_sample;
-void *perf_evsel__rawptr(struct perf_evsel *evsel, struct perf_sample *sample,
+void *perf_evsel__rawptr(struct evsel *evsel, struct perf_sample *sample,
const char *name);
-u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample,
+u64 perf_evsel__intval(struct evsel *evsel, struct perf_sample *sample,
const char *name);
-static inline char *perf_evsel__strval(struct perf_evsel *evsel,
+static inline char *perf_evsel__strval(struct evsel *evsel,
struct perf_sample *sample,
const char *name)
{
@@ -322,31 +331,28 @@ struct tep_format_field;
u64 format_field__intval(struct tep_format_field *field, struct perf_sample *sample, bool needs_swap);
-struct tep_format_field *perf_evsel__field(struct perf_evsel *evsel, const char *name);
+struct tep_format_field *perf_evsel__field(struct evsel *evsel, const char *name);
#define perf_evsel__match(evsel, t, c) \
- (evsel->attr.type == PERF_TYPE_##t && \
- evsel->attr.config == PERF_COUNT_##c)
+ (evsel->core.attr.type == PERF_TYPE_##t && \
+ evsel->core.attr.config == PERF_COUNT_##c)
-static inline bool perf_evsel__match2(struct perf_evsel *e1,
- struct perf_evsel *e2)
+static inline bool perf_evsel__match2(struct evsel *e1,
+ struct evsel *e2)
{
- return (e1->attr.type == e2->attr.type) &&
- (e1->attr.config == e2->attr.config);
+ return (e1->core.attr.type == e2->core.attr.type) &&
+ (e1->core.attr.config == e2->core.attr.config);
}
#define perf_evsel__cmp(a, b) \
((a) && \
(b) && \
- (a)->attr.type == (b)->attr.type && \
- (a)->attr.config == (b)->attr.config)
-
-int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread,
- struct perf_counts_values *count);
+ (a)->core.attr.type == (b)->core.attr.type && \
+ (a)->core.attr.config == (b)->core.attr.config)
-int perf_evsel__read_counter(struct perf_evsel *evsel, int cpu, int thread);
+int perf_evsel__read_counter(struct evsel *evsel, int cpu, int thread);
-int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
+int __perf_evsel__read_on_cpu(struct evsel *evsel,
int cpu, int thread, bool scale);
/**
@@ -356,7 +362,7 @@ int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
* @cpu - CPU of interest
* @thread - thread of interest
*/
-static inline int perf_evsel__read_on_cpu(struct perf_evsel *evsel,
+static inline int perf_evsel__read_on_cpu(struct evsel *evsel,
int cpu, int thread)
{
return __perf_evsel__read_on_cpu(evsel, cpu, thread, false);
@@ -369,27 +375,27 @@ static inline int perf_evsel__read_on_cpu(struct perf_evsel *evsel,
* @cpu - CPU of interest
* @thread - thread of interest
*/
-static inline int perf_evsel__read_on_cpu_scaled(struct perf_evsel *evsel,
+static inline int perf_evsel__read_on_cpu_scaled(struct evsel *evsel,
int cpu, int thread)
{
return __perf_evsel__read_on_cpu(evsel, cpu, thread, true);
}
-int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
+int perf_evsel__parse_sample(struct evsel *evsel, union perf_event *event,
struct perf_sample *sample);
-int perf_evsel__parse_sample_timestamp(struct perf_evsel *evsel,
+int perf_evsel__parse_sample_timestamp(struct evsel *evsel,
union perf_event *event,
u64 *timestamp);
-static inline struct perf_evsel *perf_evsel__next(struct perf_evsel *evsel)
+static inline struct evsel *perf_evsel__next(struct evsel *evsel)
{
- return list_entry(evsel->node.next, struct perf_evsel, node);
+ return list_entry(evsel->core.node.next, struct evsel, core.node);
}
-static inline struct perf_evsel *perf_evsel__prev(struct perf_evsel *evsel)
+static inline struct evsel *perf_evsel__prev(struct evsel *evsel)
{
- return list_entry(evsel->node.prev, struct perf_evsel, node);
+ return list_entry(evsel->core.node.prev, struct evsel, core.node);
}
/**
@@ -399,7 +405,7 @@ static inline struct perf_evsel *perf_evsel__prev(struct perf_evsel *evsel)
*
* Return %true if @evsel is a group leader or a stand-alone event
*/
-static inline bool perf_evsel__is_group_leader(const struct perf_evsel *evsel)
+static inline bool perf_evsel__is_group_leader(const struct evsel *evsel)
{
return evsel->leader == evsel;
}
@@ -412,22 +418,22 @@ static inline bool perf_evsel__is_group_leader(const struct perf_evsel *evsel)
* Return %true iff event group view is enabled and @evsel is a actual group
* leader which has other members in the group
*/
-static inline bool perf_evsel__is_group_event(struct perf_evsel *evsel)
+static inline bool perf_evsel__is_group_event(struct evsel *evsel)
{
if (!symbol_conf.event_group)
return false;
- return perf_evsel__is_group_leader(evsel) && evsel->nr_members > 1;
+ return perf_evsel__is_group_leader(evsel) && evsel->core.nr_members > 1;
}
-bool perf_evsel__is_function_event(struct perf_evsel *evsel);
+bool perf_evsel__is_function_event(struct evsel *evsel);
-static inline bool perf_evsel__is_bpf_output(struct perf_evsel *evsel)
+static inline bool perf_evsel__is_bpf_output(struct evsel *evsel)
{
return perf_evsel__match(evsel, SOFTWARE, SW_BPF_OUTPUT);
}
-static inline bool perf_evsel__is_clock(struct perf_evsel *evsel)
+static inline bool perf_evsel__is_clock(struct evsel *evsel)
{
return perf_evsel__match(evsel, SOFTWARE, SW_CPU_CLOCK) ||
perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK);
@@ -441,7 +447,7 @@ struct perf_attr_details {
bool trace_fields;
};
-int perf_evsel__fprintf(struct perf_evsel *evsel,
+int perf_evsel__fprintf(struct evsel *evsel,
struct perf_attr_details *details, FILE *fp);
#define EVSEL__PRINT_IP (1<<0)
@@ -464,36 +470,36 @@ int sample__fprintf_sym(struct perf_sample *sample, struct addr_location *al,
int left_alignment, unsigned int print_opts,
struct callchain_cursor *cursor, FILE *fp);
-bool perf_evsel__fallback(struct perf_evsel *evsel, int err,
+bool perf_evsel__fallback(struct evsel *evsel, int err,
char *msg, size_t msgsize);
-int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,
+int perf_evsel__open_strerror(struct evsel *evsel, struct target *target,
int err, char *msg, size_t size);
-static inline int perf_evsel__group_idx(struct perf_evsel *evsel)
+static inline int perf_evsel__group_idx(struct evsel *evsel)
{
return evsel->idx - evsel->leader->idx;
}
/* Iterates group WITHOUT the leader. */
#define for_each_group_member(_evsel, _leader) \
-for ((_evsel) = list_entry((_leader)->node.next, struct perf_evsel, node); \
+for ((_evsel) = list_entry((_leader)->core.node.next, struct evsel, core.node); \
(_evsel) && (_evsel)->leader == (_leader); \
- (_evsel) = list_entry((_evsel)->node.next, struct perf_evsel, node))
+ (_evsel) = list_entry((_evsel)->core.node.next, struct evsel, core.node))
/* Iterates group WITH the leader. */
#define for_each_group_evsel(_evsel, _leader) \
for ((_evsel) = _leader; \
(_evsel) && (_evsel)->leader == (_leader); \
- (_evsel) = list_entry((_evsel)->node.next, struct perf_evsel, node))
+ (_evsel) = list_entry((_evsel)->core.node.next, struct evsel, core.node))
-static inline bool perf_evsel__has_branch_callstack(const struct perf_evsel *evsel)
+static inline bool perf_evsel__has_branch_callstack(const struct evsel *evsel)
{
- return evsel->attr.branch_sample_type & PERF_SAMPLE_BRANCH_CALL_STACK;
+ return evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_CALL_STACK;
}
-static inline bool evsel__has_callchain(const struct perf_evsel *evsel)
+static inline bool evsel__has_callchain(const struct evsel *evsel)
{
- return (evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN) != 0;
+ return (evsel->core.attr.sample_type & PERF_SAMPLE_CALLCHAIN) != 0;
}
typedef int (*attr__fprintf_f)(FILE *, const char *, const char *, void *);
@@ -501,7 +507,7 @@ typedef int (*attr__fprintf_f)(FILE *, const char *, const char *, void *);
int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,
attr__fprintf_f attr__fprintf, void *priv);
-struct perf_env *perf_evsel__env(struct perf_evsel *evsel);
+struct perf_env *perf_evsel__env(struct evsel *evsel);
-int perf_evsel__store_ids(struct perf_evsel *evsel, struct perf_evlist *evlist);
+int perf_evsel__store_ids(struct evsel *evsel, struct evlist *evlist);
#endif /* __PERF_EVSEL_H */
diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c
index 95ea147f9e18..496fec01f5d1 100644
--- a/tools/perf/util/evsel_fprintf.c
+++ b/tools/perf/util/evsel_fprintf.c
@@ -33,26 +33,26 @@ static int __print_attr__fprintf(FILE *fp, const char *name, const char *val, vo
return comma_fprintf(fp, (bool *)priv, " %s: %s", name, val);
}
-int perf_evsel__fprintf(struct perf_evsel *evsel,
+int perf_evsel__fprintf(struct evsel *evsel,
struct perf_attr_details *details, FILE *fp)
{
bool first = true;
int printed = 0;
if (details->event_group) {
- struct perf_evsel *pos;
+ struct evsel *pos;
if (!perf_evsel__is_group_leader(evsel))
return 0;
- if (evsel->nr_members > 1)
+ if (evsel->core.nr_members > 1)
printed += fprintf(fp, "%s{", evsel->group_name ?: "");
printed += fprintf(fp, "%s", perf_evsel__name(evsel));
for_each_group_member(pos, evsel)
printed += fprintf(fp, ",%s", perf_evsel__name(pos));
- if (evsel->nr_members > 1)
+ if (evsel->core.nr_members > 1)
printed += fprintf(fp, "}");
goto out;
}
@@ -60,22 +60,22 @@ int perf_evsel__fprintf(struct perf_evsel *evsel,
printed += fprintf(fp, "%s", perf_evsel__name(evsel));
if (details->verbose) {
- printed += perf_event_attr__fprintf(fp, &evsel->attr,
+ printed += perf_event_attr__fprintf(fp, &evsel->core.attr,
__print_attr__fprintf, &first);
} else if (details->freq) {
const char *term = "sample_freq";
- if (!evsel->attr.freq)
+ if (!evsel->core.attr.freq)
term = "sample_period";
printed += comma_fprintf(fp, &first, " %s=%" PRIu64,
- term, (u64)evsel->attr.sample_freq);
+ term, (u64)evsel->core.attr.sample_freq);
}
if (details->trace_fields) {
struct tep_format_field *field;
- if (evsel->attr.type != PERF_TYPE_TRACEPOINT) {
+ if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT) {
printed += comma_fprintf(fp, &first, " (not a tracepoint)");
goto out;
}
diff --git a/tools/perf/util/evswitch.c b/tools/perf/util/evswitch.c
new file mode 100644
index 000000000000..3ba72f743d3c
--- /dev/null
+++ b/tools/perf/util/evswitch.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2019, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
+
+#include "evswitch.h"
+#include "evlist.h"
+
+bool evswitch__discard(struct evswitch *evswitch, struct evsel *evsel)
+{
+ if (evswitch->on && evswitch->discarding) {
+ if (evswitch->on != evsel)
+ return true;
+
+ evswitch->discarding = false;
+
+ if (!evswitch->show_on_off_events)
+ return true;
+
+ return false;
+ }
+
+ if (evswitch->off && !evswitch->discarding) {
+ if (evswitch->off != evsel)
+ return false;
+
+ evswitch->discarding = true;
+
+ if (!evswitch->show_on_off_events)
+ return true;
+ }
+
+ return false;
+}
+
+static int evswitch__fprintf_enoent(FILE *fp, const char *evtype, const char *evname)
+{
+ int printed = fprintf(fp, "ERROR: switch-%s event not found (%s)\n", evtype, evname);
+
+ return printed += fprintf(fp, "HINT: use 'perf evlist' to see the available event names\n");
+}
+
+int evswitch__init(struct evswitch *evswitch, struct evlist *evlist, FILE *fp)
+{
+ if (evswitch->on_name) {
+ evswitch->on = perf_evlist__find_evsel_by_str(evlist, evswitch->on_name);
+ if (evswitch->on == NULL) {
+ evswitch__fprintf_enoent(fp, "on", evswitch->on_name);
+ return -ENOENT;
+ }
+ evswitch->discarding = true;
+ }
+
+ if (evswitch->off_name) {
+ evswitch->off = perf_evlist__find_evsel_by_str(evlist, evswitch->off_name);
+ if (evswitch->off == NULL) {
+ evswitch__fprintf_enoent(fp, "off", evswitch->off_name);
+ return -ENOENT;
+ }
+ }
+
+ return 0;
+}
diff --git a/tools/perf/util/evswitch.h b/tools/perf/util/evswitch.h
new file mode 100644
index 000000000000..fd30460b6218
--- /dev/null
+++ b/tools/perf/util/evswitch.h
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2019, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
+#ifndef __PERF_EVSWITCH_H
+#define __PERF_EVSWITCH_H 1
+
+#include <stdbool.h>
+#include <stdio.h>
+
+struct evsel;
+struct evlist;
+
+struct evswitch {
+ struct evsel *on, *off;
+ const char *on_name, *off_name;
+ bool discarding;
+ bool show_on_off_events;
+};
+
+int evswitch__init(struct evswitch *evswitch, struct evlist *evlist, FILE *fp);
+
+bool evswitch__discard(struct evswitch *evswitch, struct evsel *evsel);
+
+#define OPTS_EVSWITCH(evswitch) \
+ OPT_STRING(0, "switch-on", &(evswitch)->on_name, \
+ "event", "Consider events after the ocurrence of this event"), \
+ OPT_STRING(0, "switch-off", &(evswitch)->off_name, \
+ "event", "Stop considering events after the ocurrence of this event"), \
+ OPT_BOOLEAN(0, "show-on-off-events", &(evswitch)->show_on_off_events, \
+ "Show the on/off switch events, used with --switch-on and --switch-off")
+
+#endif /* __PERF_EVSWITCH_H */
diff --git a/tools/perf/util/expr.y b/tools/perf/util/expr.y
index 432b8560cf51..f9a20a39b64a 100644
--- a/tools/perf/util/expr.y
+++ b/tools/perf/util/expr.y
@@ -2,9 +2,11 @@
%{
#include "util.h"
#include "util/debug.h"
+#include <stdlib.h> // strtod()
#define IN_EXPR_Y 1
#include "expr.h"
#include "smt.h"
+#include <assert.h>
#include <string.h>
#define MAXIDLEN 256
diff --git a/tools/perf/util/genelf.c b/tools/perf/util/genelf.c
index aafbe54fd3fa..f9f18b8b1df9 100644
--- a/tools/perf/util/genelf.c
+++ b/tools/perf/util/genelf.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* genelf.c
* Copyright (C) 2014, Google, Inc
*
* Contributed by:
* Stephane Eranian <eranian@gmail.com>
- *
- * Released under the GPL v2. (and only v2, not any later version)
*/
#include <sys/types.h>
@@ -15,6 +14,7 @@
#include <libelf.h>
#include <string.h>
#include <stdlib.h>
+#include <unistd.h>
#include <inttypes.h>
#include <limits.h>
#include <fcntl.h>
@@ -23,9 +23,9 @@
#include <dwarf.h>
#endif
-#include "perf.h"
#include "genelf.h"
#include "../util/jitdump.h"
+#include <linux/compiler.h>
#ifndef NT_GNU_BUILD_ID
#define NT_GNU_BUILD_ID 3
diff --git a/tools/perf/util/genelf_debug.c b/tools/perf/util/genelf_debug.c
index 40789d8603d0..30e9f618f6cd 100644
--- a/tools/perf/util/genelf_debug.c
+++ b/tools/perf/util/genelf_debug.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* genelf_debug.c
* Copyright (C) 2015, Google, Inc
@@ -5,8 +6,6 @@
* Contributed by:
* Stephane Eranian <eranian@google.com>
*
- * Released under the GPL v2.
- *
* based on GPLv2 source code from Oprofile
* @remark Copyright 2007 OProfile authors
* @author Philippe Elie
@@ -25,7 +24,6 @@
#include <err.h>
#include <dwarf.h>
-#include "perf.h"
#include "genelf.h"
#include "../util/jitdump.h"
diff --git a/tools/perf/util/get_current_dir_name.c b/tools/perf/util/get_current_dir_name.c
index 267aa609a582..b205d929245f 100644
--- a/tools/perf/util/get_current_dir_name.c
+++ b/tools/perf/util/get_current_dir_name.c
@@ -1,11 +1,10 @@
-// SPDX-License-Identifier: GPL-2.0
-// Copyright (C) 2018, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
+// SPDX-License-Identifier: LGPL-2.1
+// Copyright (C) 2018, 2019 Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
//
#ifndef HAVE_GET_CURRENT_DIR_NAME
-#include "util.h"
+#include "get_current_dir_name.h"
#include <unistd.h>
#include <stdlib.h>
-#include <stdlib.h>
/* Android's 'bionic' library, for one, doesn't have this */
diff --git a/tools/perf/util/get_current_dir_name.h b/tools/perf/util/get_current_dir_name.h
new file mode 100644
index 000000000000..69f7d5537d32
--- /dev/null
+++ b/tools/perf/util/get_current_dir_name.h
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: LGPL-2.1
+// Copyright (C) 2018, 2019 Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
+//
+#ifndef __PERF_GET_CURRENT_DIR_NAME_H
+#ifndef HAVE_GET_CURRENT_DIR_NAME
+char *get_current_dir_name(void);
+#endif // HAVE_GET_CURRENT_DIR_NAME
+#endif // __PERF_GET_CURRENT_DIR_NAME_H
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 847ae51a524b..b0c34dda30a0 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <inttypes.h>
-#include "util.h"
#include "string2.h"
#include <sys/param.h>
#include <sys/types.h>
@@ -13,18 +12,21 @@
#include <linux/list.h>
#include <linux/kernel.h>
#include <linux/bitops.h>
+#include <linux/string.h>
#include <linux/stringify.h>
+#include <linux/zalloc.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <linux/time64.h>
#include <dirent.h>
#include <bpf/libbpf.h>
+#include <perf/cpumap.h>
+#include "dso.h"
#include "evlist.h"
#include "evsel.h"
#include "header.h"
#include "memswap.h"
-#include "../perf.h"
#include "trace-event.h"
#include "session.h"
#include "symbol.h"
@@ -40,10 +42,11 @@
#include "tool.h"
#include "time-utils.h"
#include "units.h"
+#include "util.h"
#include "cputopo.h"
#include "bpf-event.h"
-#include "sane_ctype.h"
+#include <linux/ctype.h>
/*
* magic2 = "PERFILE2"
@@ -73,7 +76,7 @@ struct feat_fd {
void *buf; /* Either buf != NULL or fd >= 0 */
ssize_t offset;
size_t size;
- struct perf_evsel *events;
+ struct evsel *events;
};
void perf_header__set_feat(struct perf_header *header, int feat)
@@ -298,16 +301,16 @@ static int do_read_bitmap(struct feat_fd *ff, unsigned long **pset, u64 *psize)
}
static int write_tracing_data(struct feat_fd *ff,
- struct perf_evlist *evlist)
+ struct evlist *evlist)
{
if (WARN(ff->buf, "Error: calling %s in pipe-mode.\n", __func__))
return -1;
- return read_tracing_data(ff->fd, &evlist->entries);
+ return read_tracing_data(ff->fd, &evlist->core.entries);
}
static int write_build_id(struct feat_fd *ff,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
struct perf_session *session;
int err;
@@ -331,7 +334,7 @@ static int write_build_id(struct feat_fd *ff,
}
static int write_hostname(struct feat_fd *ff,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
struct utsname uts;
int ret;
@@ -344,7 +347,7 @@ static int write_hostname(struct feat_fd *ff,
}
static int write_osrelease(struct feat_fd *ff,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
struct utsname uts;
int ret;
@@ -357,7 +360,7 @@ static int write_osrelease(struct feat_fd *ff,
}
static int write_arch(struct feat_fd *ff,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
struct utsname uts;
int ret;
@@ -370,7 +373,7 @@ static int write_arch(struct feat_fd *ff,
}
static int write_version(struct feat_fd *ff,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
return do_write_string(ff, perf_version_string);
}
@@ -416,10 +419,8 @@ static int __write_cpudesc(struct feat_fd *ff, const char *cpuinfo_proc)
while (*p) {
if (isspace(*p)) {
char *r = p + 1;
- char *q = r;
+ char *q = skip_spaces(r);
*p = ' ';
- while (*q && isspace(*q))
- q++;
if (q != (p+1))
while ((*r++ = *q++));
}
@@ -433,9 +434,27 @@ done:
}
static int write_cpudesc(struct feat_fd *ff,
- struct perf_evlist *evlist __maybe_unused)
-{
+ struct evlist *evlist __maybe_unused)
+{
+#if defined(__powerpc__) || defined(__hppa__) || defined(__sparc__)
+#define CPUINFO_PROC { "cpu", }
+#elif defined(__s390__)
+#define CPUINFO_PROC { "vendor_id", }
+#elif defined(__sh__)
+#define CPUINFO_PROC { "cpu type", }
+#elif defined(__alpha__) || defined(__mips__)
+#define CPUINFO_PROC { "cpu model", }
+#elif defined(__arm__)
+#define CPUINFO_PROC { "model name", "Processor", }
+#elif defined(__arc__)
+#define CPUINFO_PROC { "Processor", }
+#elif defined(__xtensa__)
+#define CPUINFO_PROC { "core ID", }
+#else
+#define CPUINFO_PROC { "model name", }
+#endif
const char *cpuinfo_procs[] = CPUINFO_PROC;
+#undef CPUINFO_PROC
unsigned int i;
for (i = 0; i < ARRAY_SIZE(cpuinfo_procs); i++) {
@@ -449,7 +468,7 @@ static int write_cpudesc(struct feat_fd *ff,
static int write_nrcpus(struct feat_fd *ff,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
long nr;
u32 nrc, nra;
@@ -471,13 +490,13 @@ static int write_nrcpus(struct feat_fd *ff,
}
static int write_event_desc(struct feat_fd *ff,
- struct perf_evlist *evlist)
+ struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
u32 nre, nri, sz;
int ret;
- nre = evlist->nr_entries;
+ nre = evlist->core.nr_entries;
/*
* write number of events
@@ -489,13 +508,13 @@ static int write_event_desc(struct feat_fd *ff,
/*
* size of perf_event_attr struct
*/
- sz = (u32)sizeof(evsel->attr);
+ sz = (u32)sizeof(evsel->core.attr);
ret = do_write(ff, &sz, sizeof(sz));
if (ret < 0)
return ret;
evlist__for_each_entry(evlist, evsel) {
- ret = do_write(ff, &evsel->attr, sz);
+ ret = do_write(ff, &evsel->core.attr, sz);
if (ret < 0)
return ret;
/*
@@ -527,7 +546,7 @@ static int write_event_desc(struct feat_fd *ff,
}
static int write_cmdline(struct feat_fd *ff,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
char pbuf[MAXPATHLEN], *buf;
int i, ret, n;
@@ -556,7 +575,7 @@ static int write_cmdline(struct feat_fd *ff,
static int write_cpu_topology(struct feat_fd *ff,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
struct cpu_topology *tp;
u32 i;
@@ -599,6 +618,27 @@ static int write_cpu_topology(struct feat_fd *ff,
if (ret < 0)
return ret;
}
+
+ if (!tp->die_sib)
+ goto done;
+
+ ret = do_write(ff, &tp->die_sib, sizeof(tp->die_sib));
+ if (ret < 0)
+ goto done;
+
+ for (i = 0; i < tp->die_sib; i++) {
+ ret = do_write_string(ff, tp->die_siblings[i]);
+ if (ret < 0)
+ goto done;
+ }
+
+ for (j = 0; j < perf_env.nr_cpus_avail; j++) {
+ ret = do_write(ff, &perf_env.cpu[j].die_id,
+ sizeof(perf_env.cpu[j].die_id));
+ if (ret < 0)
+ return ret;
+ }
+
done:
cpu_topology__delete(tp);
return ret;
@@ -607,7 +647,7 @@ done:
static int write_total_mem(struct feat_fd *ff,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
char *buf = NULL;
FILE *fp;
@@ -636,7 +676,7 @@ static int write_total_mem(struct feat_fd *ff,
}
static int write_numa_topology(struct feat_fd *ff,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
struct numa_topology *tp;
int ret = -1;
@@ -690,7 +730,7 @@ err:
*/
static int write_pmu_mappings(struct feat_fd *ff,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
struct perf_pmu *pmu = NULL;
u32 pmu_num = 0;
@@ -739,10 +779,10 @@ static int write_pmu_mappings(struct feat_fd *ff,
* };
*/
static int write_group_desc(struct feat_fd *ff,
- struct perf_evlist *evlist)
+ struct evlist *evlist)
{
u32 nr_groups = evlist->nr_groups;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
int ret;
ret = do_write(ff, &nr_groups, sizeof(nr_groups));
@@ -751,10 +791,10 @@ static int write_group_desc(struct feat_fd *ff,
evlist__for_each_entry(evlist, evsel) {
if (perf_evsel__is_group_leader(evsel) &&
- evsel->nr_members > 1) {
+ evsel->core.nr_members > 1) {
const char *name = evsel->group_name ?: "{anon_group}";
u32 leader_idx = evsel->idx;
- u32 nr_members = evsel->nr_members;
+ u32 nr_members = evsel->core.nr_members;
ret = do_write_string(ff, name);
if (ret < 0)
@@ -821,7 +861,7 @@ int __weak get_cpuid(char *buffer __maybe_unused, size_t sz __maybe_unused)
}
static int write_cpuid(struct feat_fd *ff,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
char buffer[64];
int ret;
@@ -834,13 +874,13 @@ static int write_cpuid(struct feat_fd *ff,
}
static int write_branch_stack(struct feat_fd *ff __maybe_unused,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
return 0;
}
static int write_auxtrace(struct feat_fd *ff,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
struct perf_session *session;
int err;
@@ -857,14 +897,14 @@ static int write_auxtrace(struct feat_fd *ff,
}
static int write_clockid(struct feat_fd *ff,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
return do_write(ff, &ff->ph->env.clockid_res_ns,
sizeof(ff->ph->env.clockid_res_ns));
}
static int write_dir_format(struct feat_fd *ff,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
struct perf_session *session;
struct perf_data *data;
@@ -880,7 +920,7 @@ static int write_dir_format(struct feat_fd *ff,
#ifdef HAVE_LIBBPF_SUPPORT
static int write_bpf_prog_info(struct feat_fd *ff,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
struct perf_env *env = &ff->ph->env;
struct rb_root *root;
@@ -922,14 +962,14 @@ out:
}
#else // HAVE_LIBBPF_SUPPORT
static int write_bpf_prog_info(struct feat_fd *ff __maybe_unused,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
return 0;
}
#endif // HAVE_LIBBPF_SUPPORT
static int write_bpf_btf(struct feat_fd *ff,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
struct perf_env *env = &ff->ph->env;
struct rb_root *root;
@@ -1028,26 +1068,26 @@ static int cpu_cache_level__read(struct cpu_cache_level *cache, u32 cpu, u16 lev
return -1;
cache->type[len] = 0;
- cache->type = rtrim(cache->type);
+ cache->type = strim(cache->type);
scnprintf(file, PATH_MAX, "%s/size", path);
if (sysfs__read_str(file, &cache->size, &len)) {
- free(cache->type);
+ zfree(&cache->type);
return -1;
}
cache->size[len] = 0;
- cache->size = rtrim(cache->size);
+ cache->size = strim(cache->size);
scnprintf(file, PATH_MAX, "%s/shared_cpu_list", path);
if (sysfs__read_str(file, &cache->map, &len)) {
- free(cache->map);
- free(cache->type);
+ zfree(&cache->map);
+ zfree(&cache->type);
return -1;
}
cache->map[len] = 0;
- cache->map = rtrim(cache->map);
+ cache->map = strim(cache->map);
return 0;
}
@@ -1100,16 +1140,17 @@ static int build_caches(struct cpu_cache_level caches[], u32 size, u32 *cntp)
return 0;
}
-#define MAX_CACHES 2000
+#define MAX_CACHE_LVL 4
static int write_cache(struct feat_fd *ff,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
- struct cpu_cache_level caches[MAX_CACHES];
+ u32 max_caches = cpu__max_cpu() * MAX_CACHE_LVL;
+ struct cpu_cache_level caches[max_caches];
u32 cnt = 0, i, version = 1;
int ret;
- ret = build_caches(caches, MAX_CACHES, &cnt);
+ ret = build_caches(caches, max_caches, &cnt);
if (ret)
goto out;
@@ -1155,13 +1196,13 @@ out:
}
static int write_stat(struct feat_fd *ff __maybe_unused,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
return 0;
}
static int write_sample_time(struct feat_fd *ff,
- struct perf_evlist *evlist)
+ struct evlist *evlist)
{
int ret;
@@ -1295,7 +1336,7 @@ static int build_mem_topology(struct memory_node *nodes, u64 size, u64 *cntp)
* 48 - bitmap | bitmap of memory indexes that belongs to node
*/
static int write_mem_topology(struct feat_fd *ff __maybe_unused,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
static struct memory_node nodes[MAX_MEMORY_NODES];
u64 bsize, version = 1, i, nr;
@@ -1345,7 +1386,7 @@ out:
}
static int write_compressed(struct feat_fd *ff __maybe_unused,
- struct perf_evlist *evlist __maybe_unused)
+ struct evlist *evlist __maybe_unused)
{
int ret;
@@ -1439,10 +1480,20 @@ static void print_cpu_topology(struct feat_fd *ff, FILE *fp)
str = ph->env.sibling_cores;
for (i = 0; i < nr; i++) {
- fprintf(fp, "# sibling cores : %s\n", str);
+ fprintf(fp, "# sibling sockets : %s\n", str);
str += strlen(str) + 1;
}
+ if (ph->env.nr_sibling_dies) {
+ nr = ph->env.nr_sibling_dies;
+ str = ph->env.sibling_dies;
+
+ for (i = 0; i < nr; i++) {
+ fprintf(fp, "# sibling dies : %s\n", str);
+ str += strlen(str) + 1;
+ }
+ }
+
nr = ph->env.nr_sibling_threads;
str = ph->env.sibling_threads;
@@ -1451,12 +1502,28 @@ static void print_cpu_topology(struct feat_fd *ff, FILE *fp)
str += strlen(str) + 1;
}
- if (ph->env.cpu != NULL) {
- for (i = 0; i < cpu_nr; i++)
- fprintf(fp, "# CPU %d: Core ID %d, Socket ID %d\n", i,
- ph->env.cpu[i].core_id, ph->env.cpu[i].socket_id);
- } else
- fprintf(fp, "# Core ID and Socket ID information is not available\n");
+ if (ph->env.nr_sibling_dies) {
+ if (ph->env.cpu != NULL) {
+ for (i = 0; i < cpu_nr; i++)
+ fprintf(fp, "# CPU %d: Core ID %d, "
+ "Die ID %d, Socket ID %d\n",
+ i, ph->env.cpu[i].core_id,
+ ph->env.cpu[i].die_id,
+ ph->env.cpu[i].socket_id);
+ } else
+ fprintf(fp, "# Core ID, Die ID and Socket ID "
+ "information is not available\n");
+ } else {
+ if (ph->env.cpu != NULL) {
+ for (i = 0; i < cpu_nr; i++)
+ fprintf(fp, "# CPU %d: Core ID %d, "
+ "Socket ID %d\n",
+ i, ph->env.cpu[i].core_id,
+ ph->env.cpu[i].socket_id);
+ } else
+ fprintf(fp, "# Core ID and Socket ID "
+ "information is not available\n");
+ }
}
static void print_clockid(struct feat_fd *ff, FILE *fp)
@@ -1522,14 +1589,14 @@ static void print_bpf_btf(struct feat_fd *ff, FILE *fp)
up_read(&env->bpf_progs.lock);
}
-static void free_event_desc(struct perf_evsel *events)
+static void free_event_desc(struct evsel *events)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
if (!events)
return;
- for (evsel = events; evsel->attr.size; evsel++) {
+ for (evsel = events; evsel->core.attr.size; evsel++) {
zfree(&evsel->name);
zfree(&evsel->id);
}
@@ -1537,9 +1604,9 @@ static void free_event_desc(struct perf_evsel *events)
free(events);
}
-static struct perf_evsel *read_event_desc(struct feat_fd *ff)
+static struct evsel *read_event_desc(struct feat_fd *ff)
{
- struct perf_evsel *evsel, *events = NULL;
+ struct evsel *evsel, *events = NULL;
u64 *id;
void *buf = NULL;
u32 nre, sz, nr, i, j;
@@ -1557,12 +1624,12 @@ static struct perf_evsel *read_event_desc(struct feat_fd *ff)
if (!buf)
goto error;
- /* the last event terminates with evsel->attr.size == 0: */
+ /* the last event terminates with evsel->core.attr.size == 0: */
events = calloc(nre + 1, sizeof(*events));
if (!events)
goto error;
- msz = sizeof(evsel->attr);
+ msz = sizeof(evsel->core.attr);
if (sz < msz)
msz = sz;
@@ -1579,7 +1646,7 @@ static struct perf_evsel *read_event_desc(struct feat_fd *ff)
if (ff->ph->needs_swap)
perf_event__attr_swap(buf);
- memcpy(&evsel->attr, buf, msz);
+ memcpy(&evsel->core.attr, buf, msz);
if (do_read_u32(ff, &nr))
goto error;
@@ -1623,7 +1690,7 @@ static int __desc_attr__fprintf(FILE *fp, const char *name, const char *val,
static void print_event_desc(struct feat_fd *ff, FILE *fp)
{
- struct perf_evsel *evsel, *events;
+ struct evsel *evsel, *events;
u32 j;
u64 *id;
@@ -1637,7 +1704,7 @@ static void print_event_desc(struct feat_fd *ff, FILE *fp)
return;
}
- for (evsel = events; evsel->attr.size; evsel++) {
+ for (evsel = events; evsel->core.attr.size; evsel++) {
fprintf(fp, "# event : name = %s, ", evsel->name);
if (evsel->ids) {
@@ -1650,7 +1717,7 @@ static void print_event_desc(struct feat_fd *ff, FILE *fp)
fprintf(fp, " }");
}
- perf_event_attr__fprintf(fp, &evsel->attr, __desc_attr__fprintf, NULL);
+ perf_event_attr__fprintf(fp, &evsel->core.attr, __desc_attr__fprintf, NULL);
fputc('\n', fp);
}
@@ -1758,18 +1825,18 @@ error:
static void print_group_desc(struct feat_fd *ff, FILE *fp)
{
struct perf_session *session;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
u32 nr = 0;
session = container_of(ff->ph, struct perf_session, header);
evlist__for_each_entry(session->evlist, evsel) {
if (perf_evsel__is_group_leader(evsel) &&
- evsel->nr_members > 1) {
+ evsel->core.nr_members > 1) {
fprintf(fp, "# group: %s{%s", evsel->group_name ?: "",
perf_evsel__name(evsel));
- nr = evsel->nr_members - 1;
+ nr = evsel->core.nr_members - 1;
} else if (nr) {
fprintf(fp, ",%s", perf_evsel__name(evsel));
@@ -1830,7 +1897,7 @@ static void print_mem_topology(struct feat_fd *ff, FILE *fp)
}
}
-static int __event_process_build_id(struct build_id_event *bev,
+static int __event_process_build_id(struct perf_record_header_build_id *bev,
char *filename,
struct perf_session *session)
{
@@ -1899,7 +1966,7 @@ static int perf_header__read_build_ids_abi_quirk(struct perf_header *header,
u8 build_id[PERF_ALIGN(BUILD_ID_SIZE, sizeof(u64))];
char filename[0];
} old_bev;
- struct build_id_event bev;
+ struct perf_record_header_build_id bev;
char filename[PATH_MAX];
u64 limit = offset + size;
@@ -1940,7 +2007,7 @@ static int perf_header__read_build_ids(struct perf_header *header,
int input, u64 offset, u64 size)
{
struct perf_session *session = container_of(header, struct perf_session, header);
- struct build_id_event bev;
+ struct perf_record_header_build_id bev;
char filename[PATH_MAX];
u64 limit = offset + size, orig_offset = offset;
int err = -1;
@@ -1962,7 +2029,7 @@ static int perf_header__read_build_ids(struct perf_header *header,
*
* "perf: 'perf kvm' tool for monitoring guest performance from host"
*
- * Added a field to struct build_id_event that broke the file
+ * Added a field to struct perf_record_header_build_id that broke the file
* format.
*
* Since the kernel build-id is the first entry, process the
@@ -2043,10 +2110,10 @@ static int process_total_mem(struct feat_fd *ff, void *data __maybe_unused)
return 0;
}
-static struct perf_evsel *
-perf_evlist__find_by_index(struct perf_evlist *evlist, int idx)
+static struct evsel *
+perf_evlist__find_by_index(struct evlist *evlist, int idx)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) {
if (evsel->idx == idx)
@@ -2057,10 +2124,10 @@ perf_evlist__find_by_index(struct perf_evlist *evlist, int idx)
}
static void
-perf_evlist__set_event_name(struct perf_evlist *evlist,
- struct perf_evsel *event)
+perf_evlist__set_event_name(struct evlist *evlist,
+ struct evsel *event)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
if (!event->name)
return;
@@ -2079,7 +2146,7 @@ static int
process_event_desc(struct feat_fd *ff, void *data __maybe_unused)
{
struct perf_session *session;
- struct perf_evsel *evsel, *events = read_event_desc(ff);
+ struct evsel *evsel, *events = read_event_desc(ff);
if (!events)
return 0;
@@ -2092,7 +2159,7 @@ process_event_desc(struct feat_fd *ff, void *data __maybe_unused)
ff->events = events;
}
- for (evsel = events; evsel->attr.size; evsel++)
+ for (evsel = events; evsel->core.attr.size; evsel++)
perf_evlist__set_event_name(session->evlist, evsel);
if (!session->data->is_pipe)
@@ -2205,8 +2272,10 @@ static int process_cpu_topology(struct feat_fd *ff, void *data __maybe_unused)
/* On s390 the socket_id number is not related to the numbers of cpus.
* The socket_id number might be higher than the numbers of cpus.
* This depends on the configuration.
+ * AArch64 is the same.
*/
- if (ph->env.arch && !strncmp(ph->env.arch, "s390", 4))
+ if (ph->env.arch && (!strncmp(ph->env.arch, "s390", 4)
+ || !strncmp(ph->env.arch, "aarch64", 7)))
do_core_id_test = false;
for (i = 0; i < (u32)cpu_nr; i++) {
@@ -2214,6 +2283,7 @@ static int process_cpu_topology(struct feat_fd *ff, void *data __maybe_unused)
goto free_cpu;
ph->env.cpu[i].core_id = nr;
+ size += sizeof(u32);
if (do_read_u32(ff, &nr))
goto free_cpu;
@@ -2225,6 +2295,40 @@ static int process_cpu_topology(struct feat_fd *ff, void *data __maybe_unused)
}
ph->env.cpu[i].socket_id = nr;
+ size += sizeof(u32);
+ }
+
+ /*
+ * The header may be from old perf,
+ * which doesn't include die information.
+ */
+ if (ff->size <= size)
+ return 0;
+
+ if (do_read_u32(ff, &nr))
+ return -1;
+
+ ph->env.nr_sibling_dies = nr;
+ size += sizeof(u32);
+
+ for (i = 0; i < nr; i++) {
+ str = do_read_string(ff);
+ if (!str)
+ goto error;
+
+ /* include a NULL character at the end */
+ if (strbuf_add(&sb, str, strlen(str) + 1) < 0)
+ goto error;
+ size += string_size(str);
+ free(str);
+ }
+ ph->env.sibling_dies = strbuf_detach(&sb, NULL);
+
+ for (i = 0; i < (u32)cpu_nr; i++) {
+ if (do_read_u32(ff, &nr))
+ goto free_cpu;
+
+ ph->env.cpu[i].die_id = nr;
}
return 0;
@@ -2267,7 +2371,7 @@ static int process_numa_topology(struct feat_fd *ff, void *data __maybe_unused)
if (!str)
goto error;
- n->map = cpu_map__new(str);
+ n->map = perf_cpu_map__new(str);
if (!n->map)
goto error;
@@ -2334,7 +2438,7 @@ static int process_group_desc(struct feat_fd *ff, void *data __maybe_unused)
size_t ret = -1;
u32 i, nr, nr_groups;
struct perf_session *session;
- struct perf_evsel *evsel, *leader = NULL;
+ struct evsel *evsel, *leader = NULL;
struct group_desc {
char *name;
u32 leader_idx;
@@ -2381,7 +2485,7 @@ static int process_group_desc(struct feat_fd *ff, void *data __maybe_unused)
evsel->group_name = desc[i].name;
desc[i].name = NULL;
}
- evsel->nr_members = desc[i].nr_members;
+ evsel->core.nr_members = desc[i].nr_members;
if (i >= nr_groups || nr > 0) {
pr_debug("invalid group desc\n");
@@ -2389,7 +2493,7 @@ static int process_group_desc(struct feat_fd *ff, void *data __maybe_unused)
}
leader = evsel;
- nr = evsel->nr_members - 1;
+ nr = evsel->core.nr_members - 1;
i++;
} else if (nr) {
/* This is a group member */
@@ -2720,7 +2824,7 @@ static int process_compressed(struct feat_fd *ff,
}
struct feature_ops {
- int (*write)(struct feat_fd *ff, struct perf_evlist *evlist);
+ int (*write)(struct feat_fd *ff, struct evlist *evlist);
void (*print)(struct feat_fd *ff, FILE *fp);
int (*process)(struct feat_fd *ff, void *data);
const char *name;
@@ -2865,7 +2969,7 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full)
static int do_write_feat(struct feat_fd *ff, int type,
struct perf_file_section **p,
- struct perf_evlist *evlist)
+ struct evlist *evlist)
{
int err;
int ret = 0;
@@ -2895,7 +2999,7 @@ static int do_write_feat(struct feat_fd *ff, int type,
}
static int perf_header__adds_write(struct perf_header *header,
- struct perf_evlist *evlist, int fd)
+ struct evlist *evlist, int fd)
{
int nr_sections;
struct feat_fd ff;
@@ -2963,13 +3067,13 @@ int perf_header__write_pipe(int fd)
}
int perf_session__write_header(struct perf_session *session,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
int fd, bool at_exit)
{
struct perf_file_header f_header;
struct perf_file_attr f_attr;
struct perf_header *header = &session->header;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct feat_fd ff;
u64 attr_offset;
int err;
@@ -2990,7 +3094,7 @@ int perf_session__write_header(struct perf_session *session,
evlist__for_each_entry(evlist, evsel) {
f_attr = (struct perf_file_attr){
- .attr = evsel->attr,
+ .attr = evsel->core.attr,
.ids = {
.offset = evsel->id_offset,
.size = evsel->ids * sizeof(u64),
@@ -3019,7 +3123,7 @@ int perf_session__write_header(struct perf_session *session,
.attr_size = sizeof(f_attr),
.attrs = {
.offset = attr_offset,
- .size = evlist->nr_entries * sizeof(f_attr),
+ .size = evlist->core.nr_entries * sizeof(f_attr),
},
.data = {
.offset = header->data_offset,
@@ -3398,7 +3502,7 @@ static int read_attr(int fd, struct perf_header *ph,
return ret <= 0 ? -1 : 0;
}
-static int perf_evsel__prepare_tracepoint_event(struct perf_evsel *evsel,
+static int perf_evsel__prepare_tracepoint_event(struct evsel *evsel,
struct tep_handle *pevent)
{
struct tep_event *event;
@@ -3413,9 +3517,9 @@ static int perf_evsel__prepare_tracepoint_event(struct perf_evsel *evsel,
return -1;
}
- event = tep_find_event(pevent, evsel->attr.config);
+ event = tep_find_event(pevent, evsel->core.attr.config);
if (event == NULL) {
- pr_debug("cannot find event format for %d\n", (int)evsel->attr.config);
+ pr_debug("cannot find event format for %d\n", (int)evsel->core.attr.config);
return -1;
}
@@ -3430,13 +3534,13 @@ static int perf_evsel__prepare_tracepoint_event(struct perf_evsel *evsel,
return 0;
}
-static int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist,
+static int perf_evlist__prepare_tracepoint_events(struct evlist *evlist,
struct tep_handle *pevent)
{
- struct perf_evsel *pos;
+ struct evsel *pos;
evlist__for_each_entry(evlist, pos) {
- if (pos->attr.type == PERF_TYPE_TRACEPOINT &&
+ if (pos->core.attr.type == PERF_TYPE_TRACEPOINT &&
perf_evsel__prepare_tracepoint_event(pos, pevent))
return -1;
}
@@ -3454,7 +3558,7 @@ int perf_session__read_header(struct perf_session *session)
int nr_attrs, nr_ids, i, j;
int fd = perf_data__fd(data);
- session->evlist = perf_evlist__new();
+ session->evlist = evlist__new();
if (session->evlist == NULL)
return -ENOMEM;
@@ -3478,11 +3582,18 @@ int perf_session__read_header(struct perf_session *session)
data->file.path);
}
+ if (f_header.attr_size == 0) {
+ pr_err("ERROR: The %s file's attr size field is 0 which is unexpected.\n"
+ "Was the 'perf record' command properly terminated?\n",
+ data->file.path);
+ return -EINVAL;
+ }
+
nr_attrs = f_header.attrs.size / f_header.attr_size;
lseek(fd, f_header.attrs.offset, SEEK_SET);
for (i = 0; i < nr_attrs; i++) {
- struct perf_evsel *evsel;
+ struct evsel *evsel;
off_t tmp;
if (read_attr(fd, header, &f_attr) < 0)
@@ -3495,7 +3606,7 @@ int perf_session__read_header(struct perf_session *session)
}
tmp = lseek(fd, 0, SEEK_CUR);
- evsel = perf_evsel__new(&f_attr.attr);
+ evsel = evsel__new(&f_attr.attr);
if (evsel == NULL)
goto out_delete_evlist;
@@ -3503,9 +3614,9 @@ int perf_session__read_header(struct perf_session *session)
evsel->needs_swap = header->needs_swap;
/*
* Do it before so that if perf_evsel__alloc_id fails, this
- * entry gets purged too at perf_evlist__delete().
+ * entry gets purged too at evlist__delete().
*/
- perf_evlist__add(session->evlist, evsel);
+ evlist__add(session->evlist, evsel);
nr_ids = f_attr.ids.size / sizeof(u64);
/*
@@ -3540,7 +3651,7 @@ out_errno:
return -errno;
out_delete_evlist:
- perf_evlist__delete(session->evlist);
+ evlist__delete(session->evlist);
session->evlist = NULL;
return -ENOMEM;
}
@@ -3558,7 +3669,7 @@ int perf_event__synthesize_attr(struct perf_tool *tool,
size += sizeof(struct perf_event_header);
size += ids * sizeof(u64);
- ev = malloc(size);
+ ev = zalloc(size);
if (ev == NULL)
return -ENOMEM;
@@ -3581,12 +3692,12 @@ int perf_event__synthesize_attr(struct perf_tool *tool,
int perf_event__synthesize_features(struct perf_tool *tool,
struct perf_session *session,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
perf_event__handler_t process)
{
struct perf_header *header = &session->header;
struct feat_fd ff;
- struct feature_event *fe;
+ struct perf_record_header_feature *fe;
size_t sz, sz_hdr;
int feat, ret;
@@ -3602,6 +3713,7 @@ int perf_event__synthesize_features(struct perf_tool *tool,
return -ENOMEM;
ff.size = sz - sz_hdr;
+ ff.ph = &session->header;
for_each_set_bit(feat, header->adds_features, HEADER_FEAT_BITS) {
if (!feat_ops[feat].synthesize) {
@@ -3648,7 +3760,7 @@ int perf_event__process_feature(struct perf_session *session,
{
struct perf_tool *tool = session->tool;
struct feat_fd ff = { .fd = 0 };
- struct feature_event *fe = (struct feature_event *)event;
+ struct perf_record_header_feature *fe = (struct perf_record_header_feature *)event;
int type = fe->header.type;
u64 feat = fe->feat_id;
@@ -3665,7 +3777,7 @@ int perf_event__process_feature(struct perf_session *session,
return 0;
ff.buf = (void *)fe->data;
- ff.size = event->header.size - sizeof(event->header);
+ ff.size = event->header.size - sizeof(*fe);
ff.ph = &session->header;
if (feat_ops[feat].process(&ff, NULL))
@@ -3685,10 +3797,10 @@ int perf_event__process_feature(struct perf_session *session,
return 0;
}
-static struct event_update_event *
+static struct perf_record_event_update *
event_update_event__new(size_t size, u64 type, u64 id)
{
- struct event_update_event *ev;
+ struct perf_record_event_update *ev;
size += sizeof(*ev);
size = PERF_ALIGN(size, sizeof(u64));
@@ -3705,10 +3817,10 @@ event_update_event__new(size_t size, u64 type, u64 id)
int
perf_event__synthesize_event_update_unit(struct perf_tool *tool,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
perf_event__handler_t process)
{
- struct event_update_event *ev;
+ struct perf_record_event_update *ev;
size_t size = strlen(evsel->unit);
int err;
@@ -3724,18 +3836,18 @@ perf_event__synthesize_event_update_unit(struct perf_tool *tool,
int
perf_event__synthesize_event_update_scale(struct perf_tool *tool,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
perf_event__handler_t process)
{
- struct event_update_event *ev;
- struct event_update_event_scale *ev_data;
+ struct perf_record_event_update *ev;
+ struct perf_record_event_update_scale *ev_data;
int err;
ev = event_update_event__new(sizeof(*ev_data), PERF_EVENT_UPDATE__SCALE, evsel->id[0]);
if (ev == NULL)
return -ENOMEM;
- ev_data = (struct event_update_event_scale *) ev->data;
+ ev_data = (struct perf_record_event_update_scale *)ev->data;
ev_data->scale = evsel->scale;
err = process(tool, (union perf_event*) ev, NULL, NULL);
free(ev);
@@ -3744,10 +3856,10 @@ perf_event__synthesize_event_update_scale(struct perf_tool *tool,
int
perf_event__synthesize_event_update_name(struct perf_tool *tool,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
perf_event__handler_t process)
{
- struct event_update_event *ev;
+ struct perf_record_event_update *ev;
size_t len = strlen(evsel->name);
int err;
@@ -3763,18 +3875,18 @@ perf_event__synthesize_event_update_name(struct perf_tool *tool,
int
perf_event__synthesize_event_update_cpus(struct perf_tool *tool,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
perf_event__handler_t process)
{
- size_t size = sizeof(struct event_update_event);
- struct event_update_event *ev;
+ size_t size = sizeof(struct perf_record_event_update);
+ struct perf_record_event_update *ev;
int max, err;
u16 type;
- if (!evsel->own_cpus)
+ if (!evsel->core.own_cpus)
return 0;
- ev = cpu_map_data__alloc(evsel->own_cpus, &size, &type, &max);
+ ev = cpu_map_data__alloc(evsel->core.own_cpus, &size, &type, &max);
if (!ev)
return -ENOMEM;
@@ -3783,8 +3895,8 @@ perf_event__synthesize_event_update_cpus(struct perf_tool *tool,
ev->type = PERF_EVENT_UPDATE__CPUS;
ev->id = evsel->id[0];
- cpu_map_data__synthesize((struct cpu_map_data *) ev->data,
- evsel->own_cpus,
+ cpu_map_data__synthesize((struct perf_record_cpu_map_data *)ev->data,
+ evsel->core.own_cpus,
type, max);
err = process(tool, (union perf_event*) ev, NULL, NULL);
@@ -3794,17 +3906,17 @@ perf_event__synthesize_event_update_cpus(struct perf_tool *tool,
size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp)
{
- struct event_update_event *ev = &event->event_update;
- struct event_update_event_scale *ev_scale;
- struct event_update_event_cpus *ev_cpus;
- struct cpu_map *map;
+ struct perf_record_event_update *ev = &event->event_update;
+ struct perf_record_event_update_scale *ev_scale;
+ struct perf_record_event_update_cpus *ev_cpus;
+ struct perf_cpu_map *map;
size_t ret;
- ret = fprintf(fp, "\n... id: %" PRIu64 "\n", ev->id);
+ ret = fprintf(fp, "\n... id: %" PRI_lu64 "\n", ev->id);
switch (ev->type) {
case PERF_EVENT_UPDATE__SCALE:
- ev_scale = (struct event_update_event_scale *) ev->data;
+ ev_scale = (struct perf_record_event_update_scale *)ev->data;
ret += fprintf(fp, "... scale: %f\n", ev_scale->scale);
break;
case PERF_EVENT_UPDATE__UNIT:
@@ -3814,7 +3926,7 @@ size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp)
ret += fprintf(fp, "... name: %s\n", ev->data);
break;
case PERF_EVENT_UPDATE__CPUS:
- ev_cpus = (struct event_update_event_cpus *) ev->data;
+ ev_cpus = (struct perf_record_event_update_cpus *)ev->data;
ret += fprintf(fp, "... ");
map = cpu_map__new_data(&ev_cpus->cpus);
@@ -3832,14 +3944,14 @@ size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp)
}
int perf_event__synthesize_attrs(struct perf_tool *tool,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
perf_event__handler_t process)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
int err = 0;
evlist__for_each_entry(evlist, evsel) {
- err = perf_event__synthesize_attr(tool, &evsel->attr, evsel->ids,
+ err = perf_event__synthesize_attr(tool, &evsel->core.attr, evsel->ids,
evsel->id, process);
if (err) {
pr_debug("failed to create perf header attribute\n");
@@ -3850,22 +3962,22 @@ int perf_event__synthesize_attrs(struct perf_tool *tool,
return err;
}
-static bool has_unit(struct perf_evsel *counter)
+static bool has_unit(struct evsel *counter)
{
return counter->unit && *counter->unit;
}
-static bool has_scale(struct perf_evsel *counter)
+static bool has_scale(struct evsel *counter)
{
return counter->scale != 1;
}
int perf_event__synthesize_extra_attr(struct perf_tool *tool,
- struct perf_evlist *evsel_list,
+ struct evlist *evsel_list,
perf_event__handler_t process,
bool is_pipe)
{
- struct perf_evsel *counter;
+ struct evsel *counter;
int err;
/*
@@ -3895,7 +4007,7 @@ int perf_event__synthesize_extra_attr(struct perf_tool *tool,
}
}
- if (counter->own_cpus) {
+ if (counter->core.own_cpus) {
err = perf_event__synthesize_event_update_cpus(tool, counter, process);
if (err < 0) {
pr_err("Couldn't synthesize evsel cpus.\n");
@@ -3920,23 +4032,23 @@ int perf_event__synthesize_extra_attr(struct perf_tool *tool,
int perf_event__process_attr(struct perf_tool *tool __maybe_unused,
union perf_event *event,
- struct perf_evlist **pevlist)
+ struct evlist **pevlist)
{
u32 i, ids, n_ids;
- struct perf_evsel *evsel;
- struct perf_evlist *evlist = *pevlist;
+ struct evsel *evsel;
+ struct evlist *evlist = *pevlist;
if (evlist == NULL) {
- *pevlist = evlist = perf_evlist__new();
+ *pevlist = evlist = evlist__new();
if (evlist == NULL)
return -ENOMEM;
}
- evsel = perf_evsel__new(&event->attr.attr);
+ evsel = evsel__new(&event->attr.attr);
if (evsel == NULL)
return -ENOMEM;
- perf_evlist__add(evlist, evsel);
+ evlist__add(evlist, evsel);
ids = event->header.size;
ids -= (void *)&event->attr.id - (void *)event;
@@ -3958,14 +4070,14 @@ int perf_event__process_attr(struct perf_tool *tool __maybe_unused,
int perf_event__process_event_update(struct perf_tool *tool __maybe_unused,
union perf_event *event,
- struct perf_evlist **pevlist)
+ struct evlist **pevlist)
{
- struct event_update_event *ev = &event->event_update;
- struct event_update_event_scale *ev_scale;
- struct event_update_event_cpus *ev_cpus;
- struct perf_evlist *evlist;
- struct perf_evsel *evsel;
- struct cpu_map *map;
+ struct perf_record_event_update *ev = &event->event_update;
+ struct perf_record_event_update_scale *ev_scale;
+ struct perf_record_event_update_cpus *ev_cpus;
+ struct evlist *evlist;
+ struct evsel *evsel;
+ struct perf_cpu_map *map;
if (!pevlist || *pevlist == NULL)
return -EINVAL;
@@ -3984,15 +4096,15 @@ int perf_event__process_event_update(struct perf_tool *tool __maybe_unused,
evsel->name = strdup(ev->data);
break;
case PERF_EVENT_UPDATE__SCALE:
- ev_scale = (struct event_update_event_scale *) ev->data;
+ ev_scale = (struct perf_record_event_update_scale *)ev->data;
evsel->scale = ev_scale->scale;
break;
case PERF_EVENT_UPDATE__CPUS:
- ev_cpus = (struct event_update_event_cpus *) ev->data;
+ ev_cpus = (struct perf_record_event_update_cpus *)ev->data;
map = cpu_map__new_data(&ev_cpus->cpus);
if (map)
- evsel->own_cpus = map;
+ evsel->core.own_cpus = map;
else
pr_err("failed to get event_update cpus\n");
default:
@@ -4003,7 +4115,7 @@ int perf_event__process_event_update(struct perf_tool *tool __maybe_unused,
}
int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
perf_event__handler_t process)
{
union perf_event ev;
@@ -4023,7 +4135,7 @@ int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd,
* - write the tracing data from the temp file
* to the pipe
*/
- tdata = tracing_data_get(&evlist->entries, fd, true);
+ tdata = tracing_data_get(&evlist->core.entries, fd, true);
if (!tdata)
return -1;
@@ -4060,7 +4172,7 @@ int perf_event__process_tracing_data(struct perf_session *session,
char buf[BUFSIZ];
/* setup for reading amidst mmap */
- lseek(fd, offset + sizeof(struct tracing_data_event),
+ lseek(fd, offset + sizeof(struct perf_record_header_tracing_data),
SEEK_SET);
size_read = trace_report(fd, &session->tevent,
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 5b3abe4172e2..3e48ae3c49b1 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -92,12 +92,12 @@ struct perf_header {
struct perf_env env;
};
-struct perf_evlist;
+struct evlist;
struct perf_session;
int perf_session__read_header(struct perf_session *session);
int perf_session__write_header(struct perf_session *session,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
int fd, bool at_exit);
int perf_header__write_pipe(int fd);
@@ -117,11 +117,11 @@ int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full);
int perf_event__synthesize_features(struct perf_tool *tool,
struct perf_session *session,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
perf_event__handler_t process);
int perf_event__synthesize_extra_attr(struct perf_tool *tool,
- struct perf_evlist *evsel_list,
+ struct evlist *evsel_list,
perf_event__handler_t process,
bool is_pipe);
@@ -132,29 +132,29 @@ int perf_event__synthesize_attr(struct perf_tool *tool,
struct perf_event_attr *attr, u32 ids, u64 *id,
perf_event__handler_t process);
int perf_event__synthesize_attrs(struct perf_tool *tool,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
perf_event__handler_t process);
int perf_event__synthesize_event_update_unit(struct perf_tool *tool,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
perf_event__handler_t process);
int perf_event__synthesize_event_update_scale(struct perf_tool *tool,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
perf_event__handler_t process);
int perf_event__synthesize_event_update_name(struct perf_tool *tool,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
perf_event__handler_t process);
int perf_event__synthesize_event_update_cpus(struct perf_tool *tool,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
perf_event__handler_t process);
int perf_event__process_attr(struct perf_tool *tool, union perf_event *event,
- struct perf_evlist **pevlist);
+ struct evlist **pevlist);
int perf_event__process_event_update(struct perf_tool *tool,
union perf_event *event,
- struct perf_evlist **pevlist);
+ struct evlist **pevlist);
size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp);
int perf_event__synthesize_tracing_data(struct perf_tool *tool,
- int fd, struct perf_evlist *evlist,
+ int fd, struct evlist *evlist,
perf_event__handler_t process);
int perf_event__process_tracing_data(struct perf_session *session,
union perf_event *event);
diff --git a/tools/perf/util/help-unknown-cmd.c b/tools/perf/util/help-unknown-cmd.c
index 4f07a5ba5030..ab9e16123626 100644
--- a/tools/perf/util/help-unknown-cmd.c
+++ b/tools/perf/util/help-unknown-cmd.c
@@ -3,9 +3,11 @@
#include "config.h"
#include <poll.h>
#include <stdio.h>
+#include <stdlib.h>
#include <subcmd/help.h>
#include "../builtin.h"
#include "levenshtein.h"
+#include <linux/zalloc.h>
static int autocorrect;
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 7ace7a10054d..679a1d75090c 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -1,9 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
#include "callchain.h"
-#include "util.h"
+#include "debug.h"
+#include "dso.h"
#include "build-id.h"
#include "hist.h"
#include "map.h"
+#include "map_symbol.h"
+#include "branch.h"
+#include "mem-events.h"
#include "session.h"
#include "namespaces.h"
#include "sort.h"
@@ -19,7 +23,10 @@
#include <math.h>
#include <inttypes.h>
#include <sys/param.h>
+#include <linux/rbtree.h>
+#include <linux/string.h>
#include <linux/time64.h>
+#include <linux/zalloc.h>
static bool hists__filter_entry_by_dso(struct hists *hists,
struct hist_entry *he);
@@ -193,7 +200,10 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3);
hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12);
hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12);
- hists__new_col_len(hists, HISTC_TIME, 12);
+ if (symbol_conf.nanosecs)
+ hists__new_col_len(hists, HISTC_TIME, 16);
+ else
+ hists__new_col_len(hists, HISTC_TIME, 12);
if (h->srcline) {
len = MAX(strlen(h->srcline), strlen(sort_srcline.se_header));
@@ -376,6 +386,24 @@ void hists__delete_entries(struct hists *hists)
}
}
+struct hist_entry *hists__get_entry(struct hists *hists, int idx)
+{
+ struct rb_node *next = rb_first_cached(&hists->entries);
+ struct hist_entry *n;
+ int i = 0;
+
+ while (next) {
+ n = rb_entry(next, struct hist_entry, rb_node);
+ if (i == idx)
+ return n;
+
+ next = rb_next(&n->rb_node);
+ i++;
+ }
+
+ return NULL;
+}
+
/*
* histogram, sorted on item, collects periods
*/
@@ -454,16 +482,16 @@ static int hist_entry__init(struct hist_entry *he,
return 0;
err_srcline:
- free(he->srcline);
+ zfree(&he->srcline);
err_rawdata:
- free(he->raw_data);
+ zfree(&he->raw_data);
err_infos:
if (he->branch_info) {
map__put(he->branch_info->from.map);
map__put(he->branch_info->to.map);
- free(he->branch_info);
+ zfree(&he->branch_info);
}
if (he->mem_info) {
map__put(he->mem_info->iaddr.map);
@@ -471,7 +499,7 @@ err_infos:
}
err:
map__zput(he->ms.map);
- free(he->stat_acc);
+ zfree(&he->stat_acc);
return -ENOMEM;
}
@@ -574,6 +602,8 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists,
*/
mem_info__zput(entry->mem_info);
+ block_info__zput(entry->block_info);
+
/* If the map of an existing hist_entry has
* become out-of-date due to an exec() or
* similar, update it. Otherwise we will
@@ -645,6 +675,7 @@ __hists__add_entry(struct hists *hists,
struct symbol *sym_parent,
struct branch_info *bi,
struct mem_info *mi,
+ struct block_info *block_info,
struct perf_sample *sample,
bool sample_self,
struct hist_entry_ops *ops)
@@ -677,6 +708,7 @@ __hists__add_entry(struct hists *hists,
.hists = hists,
.branch_info = bi,
.mem_info = mi,
+ .block_info = block_info,
.transaction = sample->transaction,
.raw_data = sample->raw_data,
.raw_size = sample->raw_size,
@@ -699,7 +731,7 @@ struct hist_entry *hists__add_entry(struct hists *hists,
struct perf_sample *sample,
bool sample_self)
{
- return __hists__add_entry(hists, al, sym_parent, bi, mi,
+ return __hists__add_entry(hists, al, sym_parent, bi, mi, NULL,
sample, sample_self, NULL);
}
@@ -712,10 +744,22 @@ struct hist_entry *hists__add_entry_ops(struct hists *hists,
struct perf_sample *sample,
bool sample_self)
{
- return __hists__add_entry(hists, al, sym_parent, bi, mi,
+ return __hists__add_entry(hists, al, sym_parent, bi, mi, NULL,
sample, sample_self, ops);
}
+struct hist_entry *hists__add_entry_block(struct hists *hists,
+ struct addr_location *al,
+ struct block_info *block_info)
+{
+ struct hist_entry entry = {
+ .block_info = block_info,
+ .hists = hists,
+ }, *he = hists__findnew_entry(hists, &entry, al, false);
+
+ return he;
+}
+
static int
iter_next_nop_entry(struct hist_entry_iter *iter __maybe_unused,
struct addr_location *al __maybe_unused)
@@ -782,7 +826,7 @@ static int
iter_finish_mem_entry(struct hist_entry_iter *iter,
struct addr_location *al __maybe_unused)
{
- struct perf_evsel *evsel = iter->evsel;
+ struct evsel *evsel = iter->evsel;
struct hists *hists = evsel__hists(evsel);
struct hist_entry *he = iter->he;
int err = -EINVAL;
@@ -852,7 +896,7 @@ static int
iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
{
struct branch_info *bi;
- struct perf_evsel *evsel = iter->evsel;
+ struct evsel *evsel = iter->evsel;
struct hists *hists = evsel__hists(evsel);
struct perf_sample *sample = iter->sample;
struct hist_entry *he = NULL;
@@ -904,7 +948,7 @@ iter_prepare_normal_entry(struct hist_entry_iter *iter __maybe_unused,
static int
iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location *al)
{
- struct perf_evsel *evsel = iter->evsel;
+ struct evsel *evsel = iter->evsel;
struct perf_sample *sample = iter->sample;
struct hist_entry *he;
@@ -922,7 +966,7 @@ iter_finish_normal_entry(struct hist_entry_iter *iter,
struct addr_location *al __maybe_unused)
{
struct hist_entry *he = iter->he;
- struct perf_evsel *evsel = iter->evsel;
+ struct evsel *evsel = iter->evsel;
struct perf_sample *sample = iter->sample;
if (he == NULL)
@@ -962,7 +1006,7 @@ static int
iter_add_single_cumulative_entry(struct hist_entry_iter *iter,
struct addr_location *al)
{
- struct perf_evsel *evsel = iter->evsel;
+ struct evsel *evsel = iter->evsel;
struct hists *hists = evsel__hists(evsel);
struct perf_sample *sample = iter->sample;
struct hist_entry **he_cache = iter->priv;
@@ -1007,7 +1051,7 @@ static int
iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
struct addr_location *al)
{
- struct perf_evsel *evsel = iter->evsel;
+ struct evsel *evsel = iter->evsel;
struct perf_sample *sample = iter->sample;
struct hist_entry **he_cache = iter->priv;
struct hist_entry *he;
@@ -1213,14 +1257,17 @@ void hist_entry__delete(struct hist_entry *he)
mem_info__zput(he->mem_info);
}
+ if (he->block_info)
+ block_info__zput(he->block_info);
+
zfree(&he->res_samples);
zfree(&he->stat_acc);
free_srcline(he->srcline);
if (he->srcfile && he->srcfile[0])
- free(he->srcfile);
+ zfree(&he->srcfile);
free_callchain(he->callchain);
- free(he->trace_output);
- free(he->raw_data);
+ zfree(&he->trace_output);
+ zfree(&he->raw_data);
ops->free(he);
}
@@ -1836,7 +1883,7 @@ static void output_resort(struct hists *hists, struct ui_progress *prog,
}
}
-void perf_evsel__output_resort_cb(struct perf_evsel *evsel, struct ui_progress *prog,
+void perf_evsel__output_resort_cb(struct evsel *evsel, struct ui_progress *prog,
hists__resort_cb_t cb, void *cb_arg)
{
bool use_callchain;
@@ -1851,7 +1898,7 @@ void perf_evsel__output_resort_cb(struct perf_evsel *evsel, struct ui_progress *
output_resort(evsel__hists(evsel), prog, use_callchain, cb, cb_arg);
}
-void perf_evsel__output_resort(struct perf_evsel *evsel, struct ui_progress *prog)
+void perf_evsel__output_resort(struct evsel *evsel, struct ui_progress *prog)
{
return perf_evsel__output_resort_cb(evsel, prog, NULL, NULL);
}
@@ -2502,6 +2549,25 @@ int hists__link(struct hists *leader, struct hists *other)
return 0;
}
+int hists__unlink(struct hists *hists)
+{
+ struct rb_root_cached *root;
+ struct rb_node *nd;
+ struct hist_entry *pos;
+
+ if (hists__has(hists, need_collapse))
+ root = &hists->entries_collapsed;
+ else
+ root = hists->entries_in;
+
+ for (nd = rb_first_cached(root); nd; nd = rb_next(nd)) {
+ pos = rb_entry(nd, struct hist_entry, rb_node_in);
+ list_del_init(&pos->pairs.node);
+ }
+
+ return 0;
+}
+
void hist__account_cycles(struct branch_stack *bs, struct addr_location *al,
struct perf_sample *sample, bool nonany_branch_mode)
{
@@ -2536,9 +2602,9 @@ void hist__account_cycles(struct branch_stack *bs, struct addr_location *al,
}
}
-size_t perf_evlist__fprintf_nr_events(struct perf_evlist *evlist, FILE *fp)
+size_t perf_evlist__fprintf_nr_events(struct evlist *evlist, FILE *fp)
{
- struct perf_evsel *pos;
+ struct evsel *pos;
size_t ret = 0;
evlist__for_each_entry(evlist, pos) {
@@ -2561,11 +2627,11 @@ int __hists__scnprintf_title(struct hists *hists, char *bf, size_t size, bool sh
char unit;
int printed;
const struct dso *dso = hists->dso_filter;
- const struct thread *thread = hists->thread_filter;
+ struct thread *thread = hists->thread_filter;
int socket_id = hists->socket_filter;
unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
u64 nr_events = hists->stats.total_period;
- struct perf_evsel *evsel = hists_to_evsel(hists);
+ struct evsel *evsel = hists_to_evsel(hists);
const char *ev_name = perf_evsel__name(evsel);
char buf[512], sample_freq_str[64] = "";
size_t buflen = sizeof(buf);
@@ -2578,7 +2644,7 @@ int __hists__scnprintf_title(struct hists *hists, char *bf, size_t size, bool sh
}
if (perf_evsel__is_group_event(evsel)) {
- struct perf_evsel *pos;
+ struct evsel *pos;
perf_evsel__group_desc(evsel, buf, buflen);
ev_name = buf;
@@ -2601,12 +2667,12 @@ int __hists__scnprintf_title(struct hists *hists, char *bf, size_t size, bool sh
enable_ref = true;
if (show_freq)
- scnprintf(sample_freq_str, sizeof(sample_freq_str), " %d Hz,", evsel->attr.sample_freq);
+ scnprintf(sample_freq_str, sizeof(sample_freq_str), " %d Hz,", evsel->core.attr.sample_freq);
nr_samples = convert_unit(nr_samples, &unit);
printed = scnprintf(bf, size,
"Samples: %lu%c of event%s '%s',%s%sEvent count (approx.): %" PRIu64,
- nr_samples, unit, evsel->nr_members > 1 ? "s" : "",
+ nr_samples, unit, evsel->core.nr_members > 1 ? "s" : "",
ev_name, sample_freq_str, enable_ref ? ref : " ", nr_events);
@@ -2694,7 +2760,7 @@ static void hists__delete_all_entries(struct hists *hists)
hists__delete_remaining_entries(&hists->entries_collapsed);
}
-static void hists_evsel__exit(struct perf_evsel *evsel)
+static void hists_evsel__exit(struct evsel *evsel)
{
struct hists *hists = evsel__hists(evsel);
struct perf_hpp_fmt *fmt, *pos;
@@ -2704,15 +2770,15 @@ static void hists_evsel__exit(struct perf_evsel *evsel)
list_for_each_entry_safe(node, tmp, &hists->hpp_formats, list) {
perf_hpp_list__for_each_format_safe(&node->hpp, fmt, pos) {
- list_del(&fmt->list);
+ list_del_init(&fmt->list);
free(fmt);
}
- list_del(&node->list);
+ list_del_init(&node->list);
free(node);
}
}
-static int hists_evsel__init(struct perf_evsel *evsel)
+static int hists_evsel__init(struct evsel *evsel)
{
struct hists *hists = evsel__hists(evsel);
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 76ff6c6d03b8..34803e33dc80 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -6,9 +6,8 @@
#include <linux/types.h>
#include <pthread.h>
#include "evsel.h"
-#include "header.h"
#include "color.h"
-#include "ui/progress.h"
+#include "events_stats.h"
struct hist_entry;
struct hist_entry_ops;
@@ -16,7 +15,9 @@ struct addr_location;
struct map_symbol;
struct mem_info;
struct branch_info;
+struct block_info;
struct symbol;
+struct ui_progress;
enum hist_filter {
HIST_FILTER__DSO,
@@ -115,7 +116,7 @@ struct hist_entry_iter {
bool hide_unresolved;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct perf_sample *sample;
struct hist_entry *he;
struct symbol *parent;
@@ -149,6 +150,10 @@ struct hist_entry *hists__add_entry_ops(struct hists *hists,
struct perf_sample *sample,
bool sample_self);
+struct hist_entry *hists__add_entry_block(struct hists *hists,
+ struct addr_location *al,
+ struct block_info *bi);
+
int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
int max_stack_depth, void *arg);
@@ -166,9 +171,9 @@ void hist_entry__delete(struct hist_entry *he);
typedef int (*hists__resort_cb_t)(struct hist_entry *he, void *arg);
-void perf_evsel__output_resort_cb(struct perf_evsel *evsel, struct ui_progress *prog,
+void perf_evsel__output_resort_cb(struct evsel *evsel, struct ui_progress *prog,
hists__resort_cb_t cb, void *cb_arg);
-void perf_evsel__output_resort(struct perf_evsel *evsel, struct ui_progress *prog);
+void perf_evsel__output_resort(struct evsel *evsel, struct ui_progress *prog);
void hists__output_resort(struct hists *hists, struct ui_progress *prog);
void hists__output_resort_cb(struct hists *hists, struct ui_progress *prog,
hists__resort_cb_t cb);
@@ -178,18 +183,18 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel);
void hists__delete_entries(struct hists *hists);
void hists__output_recalc_col_len(struct hists *hists, int max_rows);
+struct hist_entry *hists__get_entry(struct hists *hists, int idx);
+
u64 hists__total_period(struct hists *hists);
void hists__reset_stats(struct hists *hists);
void hists__inc_stats(struct hists *hists, struct hist_entry *h);
void hists__inc_nr_events(struct hists *hists, u32 type);
void hists__inc_nr_samples(struct hists *hists, bool filtered);
-void events_stats__inc(struct events_stats *stats, u32 type);
-size_t events_stats__fprintf(struct events_stats *stats, FILE *fp);
size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
int max_cols, float min_pcnt, FILE *fp,
bool ignore_callchains);
-size_t perf_evlist__fprintf_nr_events(struct perf_evlist *evlist, FILE *fp);
+size_t perf_evlist__fprintf_nr_events(struct evlist *evlist, FILE *fp);
void hists__filter_by_dso(struct hists *hists);
void hists__filter_by_thread(struct hists *hists);
@@ -210,19 +215,20 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *he);
void hists__match(struct hists *leader, struct hists *other);
int hists__link(struct hists *leader, struct hists *other);
+int hists__unlink(struct hists *hists);
struct hists_evsel {
- struct perf_evsel evsel;
+ struct evsel evsel;
struct hists hists;
};
-static inline struct perf_evsel *hists_to_evsel(struct hists *hists)
+static inline struct evsel *hists_to_evsel(struct hists *hists)
{
struct hists_evsel *hevsel = container_of(hists, struct hists_evsel, hists);
return &hevsel->evsel;
}
-static inline struct hists *evsel__hists(struct perf_evsel *evsel)
+static inline struct hists *evsel__hists(struct evsel *evsel)
{
struct hists_evsel *hevsel = (struct hists_evsel *)evsel;
return &hevsel->hists;
@@ -243,6 +249,7 @@ struct perf_hpp {
size_t size;
const char *sep;
void *ptr;
+ bool skip;
};
struct perf_hpp_fmt {
@@ -359,7 +366,7 @@ void perf_hpp__setup_output_field(struct perf_hpp_list *list);
void perf_hpp__reset_output_field(struct perf_hpp_list *list);
void perf_hpp__append_sort_keys(struct perf_hpp_list *list);
int perf_hpp__setup_hists_formats(struct perf_hpp_list *list,
- struct perf_evlist *evlist);
+ struct evlist *evlist);
bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format);
@@ -424,7 +431,7 @@ static inline size_t perf_hpp__color_overhead(void)
: 0;
}
-struct perf_evlist;
+struct evlist;
struct hist_browser_timer {
void (*timer)(void *arg);
@@ -445,30 +452,30 @@ enum rstype {
#include "../ui/keysyms.h"
void attr_to_script(char *buf, struct perf_event_attr *attr);
-int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
+int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
struct hist_browser_timer *hbt,
struct annotation_options *annotation_opts);
-int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
+int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
struct hist_browser_timer *hbt,
struct annotation_options *annotation_opts);
-int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
+int perf_evlist__tui_browse_hists(struct evlist *evlist, const char *help,
struct hist_browser_timer *hbt,
float min_pcnt,
struct perf_env *env,
bool warn_lost_event,
struct annotation_options *annotation_options);
-int script_browse(const char *script_opt, struct perf_evsel *evsel);
+int script_browse(const char *script_opt, struct evsel *evsel);
void run_script(char *cmd);
int res_sample_browse(struct res_sample *res_samples, int num_res,
- struct perf_evsel *evsel, enum rstype rstype);
+ struct evsel *evsel, enum rstype rstype);
void res_sample_init(void);
#else
static inline
-int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused,
+int perf_evlist__tui_browse_hists(struct evlist *evlist __maybe_unused,
const char *help __maybe_unused,
struct hist_browser_timer *hbt __maybe_unused,
float min_pcnt __maybe_unused,
@@ -479,7 +486,7 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused,
return 0;
}
static inline int map_symbol__tui_annotate(struct map_symbol *ms __maybe_unused,
- struct perf_evsel *evsel __maybe_unused,
+ struct evsel *evsel __maybe_unused,
struct hist_browser_timer *hbt __maybe_unused,
struct annotation_options *annotation_options __maybe_unused)
{
@@ -487,7 +494,7 @@ static inline int map_symbol__tui_annotate(struct map_symbol *ms __maybe_unused,
}
static inline int hist_entry__tui_annotate(struct hist_entry *he __maybe_unused,
- struct perf_evsel *evsel __maybe_unused,
+ struct evsel *evsel __maybe_unused,
struct hist_browser_timer *hbt __maybe_unused,
struct annotation_options *annotation_opts __maybe_unused)
{
@@ -495,14 +502,14 @@ static inline int hist_entry__tui_annotate(struct hist_entry *he __maybe_unused,
}
static inline int script_browse(const char *script_opt __maybe_unused,
- struct perf_evsel *evsel __maybe_unused)
+ struct evsel *evsel __maybe_unused)
{
return 0;
}
static inline int res_sample_browse(struct res_sample *res_samples __maybe_unused,
int num_res __maybe_unused,
- struct perf_evsel *evsel __maybe_unused,
+ struct evsel *evsel __maybe_unused,
enum rstype rstype __maybe_unused)
{
return 0;
diff --git a/tools/perf/util/include/linux/ctype.h b/tools/perf/util/include/linux/ctype.h
deleted file mode 100644
index a53d4ee1e0b7..000000000000
--- a/tools/perf/util/include/linux/ctype.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../util.h"
diff --git a/tools/perf/util/intel-bts.c b/tools/perf/util/intel-bts.c
index 47025bc727e1..aacffa2b0362 100644
--- a/tools/perf/util/intel-bts.c
+++ b/tools/perf/util/intel-bts.c
@@ -1,16 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* intel-bts.c: Intel Processor Trace support
* Copyright (c) 2013-2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#include <endian.h>
@@ -21,16 +12,16 @@
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/log2.h>
+#include <linux/zalloc.h>
#include "cpumap.h"
#include "color.h"
#include "evsel.h"
#include "evlist.h"
#include "machine.h"
-#include "map.h"
#include "symbol.h"
#include "session.h"
-#include "util.h"
+#include "tool.h"
#include "thread.h"
#include "thread-stack.h"
#include "debug.h"
@@ -769,15 +760,15 @@ static int intel_bts_synth_event(struct perf_session *session,
static int intel_bts_synth_events(struct intel_bts *bts,
struct perf_session *session)
{
- struct perf_evlist *evlist = session->evlist;
- struct perf_evsel *evsel;
+ struct evlist *evlist = session->evlist;
+ struct evsel *evsel;
struct perf_event_attr attr;
bool found = false;
u64 id;
int err;
evlist__for_each_entry(evlist, evsel) {
- if (evsel->attr.type == bts->pmu_type && evsel->ids) {
+ if (evsel->core.attr.type == bts->pmu_type && evsel->ids) {
found = true;
break;
}
@@ -791,18 +782,18 @@ static int intel_bts_synth_events(struct intel_bts *bts,
memset(&attr, 0, sizeof(struct perf_event_attr));
attr.size = sizeof(struct perf_event_attr);
attr.type = PERF_TYPE_HARDWARE;
- attr.sample_type = evsel->attr.sample_type & PERF_SAMPLE_MASK;
+ attr.sample_type = evsel->core.attr.sample_type & PERF_SAMPLE_MASK;
attr.sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID |
PERF_SAMPLE_PERIOD;
attr.sample_type &= ~(u64)PERF_SAMPLE_TIME;
attr.sample_type &= ~(u64)PERF_SAMPLE_CPU;
- attr.exclude_user = evsel->attr.exclude_user;
- attr.exclude_kernel = evsel->attr.exclude_kernel;
- attr.exclude_hv = evsel->attr.exclude_hv;
- attr.exclude_host = evsel->attr.exclude_host;
- attr.exclude_guest = evsel->attr.exclude_guest;
- attr.sample_id_all = evsel->attr.sample_id_all;
- attr.read_format = evsel->attr.read_format;
+ attr.exclude_user = evsel->core.attr.exclude_user;
+ attr.exclude_kernel = evsel->core.attr.exclude_kernel;
+ attr.exclude_hv = evsel->core.attr.exclude_hv;
+ attr.exclude_host = evsel->core.attr.exclude_host;
+ attr.exclude_guest = evsel->core.attr.exclude_guest;
+ attr.sample_id_all = evsel->core.attr.sample_id_all;
+ attr.read_format = evsel->core.attr.read_format;
id = evsel->id[0] + 1000000000;
if (!id)
@@ -827,7 +818,7 @@ static int intel_bts_synth_events(struct intel_bts *bts,
* We only use sample types from PERF_SAMPLE_MASK so we can use
* __perf_evsel__sample_size() here.
*/
- bts->branches_event_size = sizeof(struct sample_event) +
+ bts->branches_event_size = sizeof(struct perf_record_sample) +
__perf_evsel__sample_size(attr.sample_type);
}
@@ -843,7 +834,7 @@ static const char * const intel_bts_info_fmts[] = {
[INTEL_BTS_SNAPSHOT_MODE] = " Snapshot mode %"PRId64"\n",
};
-static void intel_bts_print_info(u64 *arr, int start, int finish)
+static void intel_bts_print_info(__u64 *arr, int start, int finish)
{
int i;
@@ -857,12 +848,12 @@ static void intel_bts_print_info(u64 *arr, int start, int finish)
int intel_bts_process_auxtrace_info(union perf_event *event,
struct perf_session *session)
{
- struct auxtrace_info_event *auxtrace_info = &event->auxtrace_info;
+ struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
size_t min_sz = sizeof(u64) * INTEL_BTS_SNAPSHOT_MODE;
struct intel_bts *bts;
int err;
- if (auxtrace_info->header.size < sizeof(struct auxtrace_info_event) +
+ if (auxtrace_info->header.size < sizeof(struct perf_record_auxtrace_info) +
min_sz)
return -EINVAL;
@@ -900,13 +891,12 @@ int intel_bts_process_auxtrace_info(union perf_event *event,
if (dump_trace)
return 0;
- if (session->itrace_synth_opts && session->itrace_synth_opts->set) {
+ if (session->itrace_synth_opts->set) {
bts->synth_opts = *session->itrace_synth_opts;
} else {
itrace_synth_opts__set_default(&bts->synth_opts,
session->itrace_synth_opts->default_no_sample);
- if (session->itrace_synth_opts)
- bts->synth_opts.thread_stack =
+ bts->synth_opts.thread_stack =
session->itrace_synth_opts->thread_stack;
}
diff --git a/tools/perf/util/intel-bts.h b/tools/perf/util/intel-bts.h
index ca65e21b3e83..53d5aa02766a 100644
--- a/tools/perf/util/intel-bts.h
+++ b/tools/perf/util/intel-bts.h
@@ -1,16 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* intel-bts.h: Intel Processor Trace support
* Copyright (c) 2013-2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#ifndef INCLUDE__PERF_INTEL_BTS_H__
diff --git a/tools/perf/util/intel-pt-decoder/Build b/tools/perf/util/intel-pt-decoder/Build
index 23bf788f84b9..bc629359826f 100644
--- a/tools/perf/util/intel-pt-decoder/Build
+++ b/tools/perf/util/intel-pt-decoder/Build
@@ -1,7 +1,7 @@
perf-$(CONFIG_AUXTRACE) += intel-pt-pkt-decoder.o intel-pt-insn-decoder.o intel-pt-log.o intel-pt-decoder.o
-inat_tables_script = util/intel-pt-decoder/gen-insn-attr-x86.awk
-inat_tables_maps = util/intel-pt-decoder/x86-opcode-map.txt
+inat_tables_script = $(srctree)/tools/arch/x86/tools/gen-insn-attr-x86.awk
+inat_tables_maps = $(srctree)/tools/arch/x86/lib/x86-opcode-map.txt
$(OUTPUT)util/intel-pt-decoder/inat-tables.c: $(inat_tables_script) $(inat_tables_maps)
$(call rule_mkdir)
@@ -9,23 +9,7 @@ $(OUTPUT)util/intel-pt-decoder/inat-tables.c: $(inat_tables_script) $(inat_table
# Busybox's diff doesn't have -I, avoid warning in the case
-$(OUTPUT)util/intel-pt-decoder/intel-pt-insn-decoder.o: util/intel-pt-decoder/intel-pt-insn-decoder.c util/intel-pt-decoder/inat.c $(OUTPUT)util/intel-pt-decoder/inat-tables.c
- @(diff -I 2>&1 | grep -q 'option requires an argument' && \
- test -d ../../kernel -a -d ../../tools -a -d ../perf && ( \
- ((diff -B -I'^#include' util/intel-pt-decoder/insn.c ../../arch/x86/lib/insn.c >/dev/null) || \
- (echo "Warning: Intel PT: x86 instruction decoder C file at 'tools/perf/util/intel-pt-decoder/insn.c' differs from latest version at 'arch/x86/lib/insn.c'" >&2)) && \
- ((diff -B -I'^#include' util/intel-pt-decoder/inat.c ../../arch/x86/lib/inat.c >/dev/null) || \
- (echo "Warning: Intel PT: x86 instruction decoder C file at 'tools/perf/util/intel-pt-decoder/inat.c' differs from latest version at 'arch/x86/lib/inat.c'" >&2)) && \
- ((diff -B util/intel-pt-decoder/x86-opcode-map.txt ../../arch/x86/lib/x86-opcode-map.txt >/dev/null) || \
- (echo "Warning: Intel PT: x86 instruction decoder map file at 'tools/perf/util/intel-pt-decoder/x86-opcode-map.txt' differs from latest version at 'arch/x86/lib/x86-opcode-map.txt'" >&2)) && \
- ((diff -B util/intel-pt-decoder/gen-insn-attr-x86.awk ../../arch/x86/tools/gen-insn-attr-x86.awk >/dev/null) || \
- (echo "Warning: Intel PT: x86 instruction decoder script at 'tools/perf/util/intel-pt-decoder/gen-insn-attr-x86.awk' differs from latest version at 'arch/x86/tools/gen-insn-attr-x86.awk'" >&2)) && \
- ((diff -B -I'^#include' util/intel-pt-decoder/insn.h ../../arch/x86/include/asm/insn.h >/dev/null) || \
- (echo "Warning: Intel PT: x86 instruction decoder header at 'tools/perf/util/intel-pt-decoder/insn.h' differs from latest version at 'arch/x86/include/asm/insn.h'" >&2)) && \
- ((diff -B -I'^#include' util/intel-pt-decoder/inat.h ../../arch/x86/include/asm/inat.h >/dev/null) || \
- (echo "Warning: Intel PT: x86 instruction decoder header at 'tools/perf/util/intel-pt-decoder/inat.h' differs from latest version at 'arch/x86/include/asm/inat.h'" >&2)) && \
- ((diff -B -I'^#include' util/intel-pt-decoder/inat_types.h ../../arch/x86/include/asm/inat_types.h >/dev/null) || \
- (echo "Warning: Intel PT: x86 instruction decoder header at 'tools/perf/util/intel-pt-decoder/inat_types.h' differs from latest version at 'arch/x86/include/asm/inat_types.h'" >&2)))) || true
+$(OUTPUT)util/intel-pt-decoder/intel-pt-insn-decoder.o: util/intel-pt-decoder/intel-pt-insn-decoder.c $(OUTPUT)util/intel-pt-decoder/inat-tables.c
$(call rule_mkdir)
$(call if_changed_dep,cc_o_c)
diff --git a/tools/perf/util/intel-pt-decoder/gen-insn-attr-x86.awk b/tools/perf/util/intel-pt-decoder/gen-insn-attr-x86.awk
deleted file mode 100644
index ddd5c4c21129..000000000000
--- a/tools/perf/util/intel-pt-decoder/gen-insn-attr-x86.awk
+++ /dev/null
@@ -1,392 +0,0 @@
-#!/bin/awk -f
-# SPDX-License-Identifier: GPL-2.0
-# gen-insn-attr-x86.awk: Instruction attribute table generator
-# Written by Masami Hiramatsu <mhiramat@redhat.com>
-#
-# Usage: awk -f gen-insn-attr-x86.awk x86-opcode-map.txt > inat-tables.c
-
-# Awk implementation sanity check
-function check_awk_implement() {
- if (sprintf("%x", 0) != "0")
- return "Your awk has a printf-format problem."
- return ""
-}
-
-# Clear working vars
-function clear_vars() {
- delete table
- delete lptable2
- delete lptable1
- delete lptable3
- eid = -1 # escape id
- gid = -1 # group id
- aid = -1 # AVX id
- tname = ""
-}
-
-BEGIN {
- # Implementation error checking
- awkchecked = check_awk_implement()
- if (awkchecked != "") {
- print "Error: " awkchecked > "/dev/stderr"
- print "Please try to use gawk." > "/dev/stderr"
- exit 1
- }
-
- # Setup generating tables
- print "/* x86 opcode map generated from x86-opcode-map.txt */"
- print "/* Do not change this code. */\n"
- ggid = 1
- geid = 1
- gaid = 0
- delete etable
- delete gtable
- delete atable
-
- opnd_expr = "^[A-Za-z/]"
- ext_expr = "^\\("
- sep_expr = "^\\|$"
- group_expr = "^Grp[0-9A-Za-z]+"
-
- imm_expr = "^[IJAOL][a-z]"
- imm_flag["Ib"] = "INAT_MAKE_IMM(INAT_IMM_BYTE)"
- imm_flag["Jb"] = "INAT_MAKE_IMM(INAT_IMM_BYTE)"
- imm_flag["Iw"] = "INAT_MAKE_IMM(INAT_IMM_WORD)"
- imm_flag["Id"] = "INAT_MAKE_IMM(INAT_IMM_DWORD)"
- imm_flag["Iq"] = "INAT_MAKE_IMM(INAT_IMM_QWORD)"
- imm_flag["Ap"] = "INAT_MAKE_IMM(INAT_IMM_PTR)"
- imm_flag["Iz"] = "INAT_MAKE_IMM(INAT_IMM_VWORD32)"
- imm_flag["Jz"] = "INAT_MAKE_IMM(INAT_IMM_VWORD32)"
- imm_flag["Iv"] = "INAT_MAKE_IMM(INAT_IMM_VWORD)"
- imm_flag["Ob"] = "INAT_MOFFSET"
- imm_flag["Ov"] = "INAT_MOFFSET"
- imm_flag["Lx"] = "INAT_MAKE_IMM(INAT_IMM_BYTE)"
-
- modrm_expr = "^([CDEGMNPQRSUVW/][a-z]+|NTA|T[012])"
- force64_expr = "\\([df]64\\)"
- rex_expr = "^REX(\\.[XRWB]+)*"
- fpu_expr = "^ESC" # TODO
-
- lprefix1_expr = "\\((66|!F3)\\)"
- lprefix2_expr = "\\(F3\\)"
- lprefix3_expr = "\\((F2|!F3|66\\&F2)\\)"
- lprefix_expr = "\\((66|F2|F3)\\)"
- max_lprefix = 4
-
- # All opcodes starting with lower-case 'v', 'k' or with (v1) superscript
- # accepts VEX prefix
- vexok_opcode_expr = "^[vk].*"
- vexok_expr = "\\(v1\\)"
- # All opcodes with (v) superscript supports *only* VEX prefix
- vexonly_expr = "\\(v\\)"
- # All opcodes with (ev) superscript supports *only* EVEX prefix
- evexonly_expr = "\\(ev\\)"
-
- prefix_expr = "\\(Prefix\\)"
- prefix_num["Operand-Size"] = "INAT_PFX_OPNDSZ"
- prefix_num["REPNE"] = "INAT_PFX_REPNE"
- prefix_num["REP/REPE"] = "INAT_PFX_REPE"
- prefix_num["XACQUIRE"] = "INAT_PFX_REPNE"
- prefix_num["XRELEASE"] = "INAT_PFX_REPE"
- prefix_num["LOCK"] = "INAT_PFX_LOCK"
- prefix_num["SEG=CS"] = "INAT_PFX_CS"
- prefix_num["SEG=DS"] = "INAT_PFX_DS"
- prefix_num["SEG=ES"] = "INAT_PFX_ES"
- prefix_num["SEG=FS"] = "INAT_PFX_FS"
- prefix_num["SEG=GS"] = "INAT_PFX_GS"
- prefix_num["SEG=SS"] = "INAT_PFX_SS"
- prefix_num["Address-Size"] = "INAT_PFX_ADDRSZ"
- prefix_num["VEX+1byte"] = "INAT_PFX_VEX2"
- prefix_num["VEX+2byte"] = "INAT_PFX_VEX3"
- prefix_num["EVEX"] = "INAT_PFX_EVEX"
-
- clear_vars()
-}
-
-function semantic_error(msg) {
- print "Semantic error at " NR ": " msg > "/dev/stderr"
- exit 1
-}
-
-function debug(msg) {
- print "DEBUG: " msg
-}
-
-function array_size(arr, i,c) {
- c = 0
- for (i in arr)
- c++
- return c
-}
-
-/^Table:/ {
- print "/* " $0 " */"
- if (tname != "")
- semantic_error("Hit Table: before EndTable:.");
-}
-
-/^Referrer:/ {
- if (NF != 1) {
- # escape opcode table
- ref = ""
- for (i = 2; i <= NF; i++)
- ref = ref $i
- eid = escape[ref]
- tname = sprintf("inat_escape_table_%d", eid)
- }
-}
-
-/^AVXcode:/ {
- if (NF != 1) {
- # AVX/escape opcode table
- aid = $2
- if (gaid <= aid)
- gaid = aid + 1
- if (tname == "") # AVX only opcode table
- tname = sprintf("inat_avx_table_%d", $2)
- }
- if (aid == -1 && eid == -1) # primary opcode table
- tname = "inat_primary_table"
-}
-
-/^GrpTable:/ {
- print "/* " $0 " */"
- if (!($2 in group))
- semantic_error("No group: " $2 )
- gid = group[$2]
- tname = "inat_group_table_" gid
-}
-
-function print_table(tbl,name,fmt,n)
-{
- print "const insn_attr_t " name " = {"
- for (i = 0; i < n; i++) {
- id = sprintf(fmt, i)
- if (tbl[id])
- print " [" id "] = " tbl[id] ","
- }
- print "};"
-}
-
-/^EndTable/ {
- if (gid != -1) {
- # print group tables
- if (array_size(table) != 0) {
- print_table(table, tname "[INAT_GROUP_TABLE_SIZE]",
- "0x%x", 8)
- gtable[gid,0] = tname
- }
- if (array_size(lptable1) != 0) {
- print_table(lptable1, tname "_1[INAT_GROUP_TABLE_SIZE]",
- "0x%x", 8)
- gtable[gid,1] = tname "_1"
- }
- if (array_size(lptable2) != 0) {
- print_table(lptable2, tname "_2[INAT_GROUP_TABLE_SIZE]",
- "0x%x", 8)
- gtable[gid,2] = tname "_2"
- }
- if (array_size(lptable3) != 0) {
- print_table(lptable3, tname "_3[INAT_GROUP_TABLE_SIZE]",
- "0x%x", 8)
- gtable[gid,3] = tname "_3"
- }
- } else {
- # print primary/escaped tables
- if (array_size(table) != 0) {
- print_table(table, tname "[INAT_OPCODE_TABLE_SIZE]",
- "0x%02x", 256)
- etable[eid,0] = tname
- if (aid >= 0)
- atable[aid,0] = tname
- }
- if (array_size(lptable1) != 0) {
- print_table(lptable1,tname "_1[INAT_OPCODE_TABLE_SIZE]",
- "0x%02x", 256)
- etable[eid,1] = tname "_1"
- if (aid >= 0)
- atable[aid,1] = tname "_1"
- }
- if (array_size(lptable2) != 0) {
- print_table(lptable2,tname "_2[INAT_OPCODE_TABLE_SIZE]",
- "0x%02x", 256)
- etable[eid,2] = tname "_2"
- if (aid >= 0)
- atable[aid,2] = tname "_2"
- }
- if (array_size(lptable3) != 0) {
- print_table(lptable3,tname "_3[INAT_OPCODE_TABLE_SIZE]",
- "0x%02x", 256)
- etable[eid,3] = tname "_3"
- if (aid >= 0)
- atable[aid,3] = tname "_3"
- }
- }
- print ""
- clear_vars()
-}
-
-function add_flags(old,new) {
- if (old && new)
- return old " | " new
- else if (old)
- return old
- else
- return new
-}
-
-# convert operands to flags.
-function convert_operands(count,opnd, i,j,imm,mod)
-{
- imm = null
- mod = null
- for (j = 1; j <= count; j++) {
- i = opnd[j]
- if (match(i, imm_expr) == 1) {
- if (!imm_flag[i])
- semantic_error("Unknown imm opnd: " i)
- if (imm) {
- if (i != "Ib")
- semantic_error("Second IMM error")
- imm = add_flags(imm, "INAT_SCNDIMM")
- } else
- imm = imm_flag[i]
- } else if (match(i, modrm_expr))
- mod = "INAT_MODRM"
- }
- return add_flags(imm, mod)
-}
-
-/^[0-9a-f]+\:/ {
- if (NR == 1)
- next
- # get index
- idx = "0x" substr($1, 1, index($1,":") - 1)
- if (idx in table)
- semantic_error("Redefine " idx " in " tname)
-
- # check if escaped opcode
- if ("escape" == $2) {
- if ($3 != "#")
- semantic_error("No escaped name")
- ref = ""
- for (i = 4; i <= NF; i++)
- ref = ref $i
- if (ref in escape)
- semantic_error("Redefine escape (" ref ")")
- escape[ref] = geid
- geid++
- table[idx] = "INAT_MAKE_ESCAPE(" escape[ref] ")"
- next
- }
-
- variant = null
- # converts
- i = 2
- while (i <= NF) {
- opcode = $(i++)
- delete opnds
- ext = null
- flags = null
- opnd = null
- # parse one opcode
- if (match($i, opnd_expr)) {
- opnd = $i
- count = split($(i++), opnds, ",")
- flags = convert_operands(count, opnds)
- }
- if (match($i, ext_expr))
- ext = $(i++)
- if (match($i, sep_expr))
- i++
- else if (i < NF)
- semantic_error($i " is not a separator")
-
- # check if group opcode
- if (match(opcode, group_expr)) {
- if (!(opcode in group)) {
- group[opcode] = ggid
- ggid++
- }
- flags = add_flags(flags, "INAT_MAKE_GROUP(" group[opcode] ")")
- }
- # check force(or default) 64bit
- if (match(ext, force64_expr))
- flags = add_flags(flags, "INAT_FORCE64")
-
- # check REX prefix
- if (match(opcode, rex_expr))
- flags = add_flags(flags, "INAT_MAKE_PREFIX(INAT_PFX_REX)")
-
- # check coprocessor escape : TODO
- if (match(opcode, fpu_expr))
- flags = add_flags(flags, "INAT_MODRM")
-
- # check VEX codes
- if (match(ext, evexonly_expr))
- flags = add_flags(flags, "INAT_VEXOK | INAT_EVEXONLY")
- else if (match(ext, vexonly_expr))
- flags = add_flags(flags, "INAT_VEXOK | INAT_VEXONLY")
- else if (match(ext, vexok_expr) || match(opcode, vexok_opcode_expr))
- flags = add_flags(flags, "INAT_VEXOK")
-
- # check prefixes
- if (match(ext, prefix_expr)) {
- if (!prefix_num[opcode])
- semantic_error("Unknown prefix: " opcode)
- flags = add_flags(flags, "INAT_MAKE_PREFIX(" prefix_num[opcode] ")")
- }
- if (length(flags) == 0)
- continue
- # check if last prefix
- if (match(ext, lprefix1_expr)) {
- lptable1[idx] = add_flags(lptable1[idx],flags)
- variant = "INAT_VARIANT"
- }
- if (match(ext, lprefix2_expr)) {
- lptable2[idx] = add_flags(lptable2[idx],flags)
- variant = "INAT_VARIANT"
- }
- if (match(ext, lprefix3_expr)) {
- lptable3[idx] = add_flags(lptable3[idx],flags)
- variant = "INAT_VARIANT"
- }
- if (!match(ext, lprefix_expr)){
- table[idx] = add_flags(table[idx],flags)
- }
- }
- if (variant)
- table[idx] = add_flags(table[idx],variant)
-}
-
-END {
- if (awkchecked != "")
- exit 1
- # print escape opcode map's array
- print "/* Escape opcode map array */"
- print "const insn_attr_t * const inat_escape_tables[INAT_ESC_MAX + 1]" \
- "[INAT_LSTPFX_MAX + 1] = {"
- for (i = 0; i < geid; i++)
- for (j = 0; j < max_lprefix; j++)
- if (etable[i,j])
- print " ["i"]["j"] = "etable[i,j]","
- print "};\n"
- # print group opcode map's array
- print "/* Group opcode map array */"
- print "const insn_attr_t * const inat_group_tables[INAT_GRP_MAX + 1]"\
- "[INAT_LSTPFX_MAX + 1] = {"
- for (i = 0; i < ggid; i++)
- for (j = 0; j < max_lprefix; j++)
- if (gtable[i,j])
- print " ["i"]["j"] = "gtable[i,j]","
- print "};\n"
- # print AVX opcode map's array
- print "/* AVX opcode map array */"
- print "const insn_attr_t * const inat_avx_tables[X86_VEX_M_MAX + 1]"\
- "[INAT_LSTPFX_MAX + 1] = {"
- for (i = 0; i < gaid; i++)
- for (j = 0; j < max_lprefix; j++)
- if (atable[i,j])
- print " ["i"]["j"] = "atable[i,j]","
- print "};"
-}
diff --git a/tools/perf/util/intel-pt-decoder/inat_types.h b/tools/perf/util/intel-pt-decoder/inat_types.h
deleted file mode 100644
index cb3c20ce39cf..000000000000
--- a/tools/perf/util/intel-pt-decoder/inat_types.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef _ASM_X86_INAT_TYPES_H
-#define _ASM_X86_INAT_TYPES_H
-/*
- * x86 instruction attributes
- *
- * Written by Masami Hiramatsu <mhiramat@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-/* Instruction attributes */
-typedef unsigned int insn_attr_t;
-typedef unsigned char insn_byte_t;
-typedef signed int insn_value_t;
-
-#endif
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
index f4c3c84b090f..f8ccfd6be0ee 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
@@ -1,16 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* intel_pt_decoder.c: Intel Processor Trace support
* Copyright (c) 2013-2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#ifndef _GNU_SOURCE
@@ -23,9 +14,9 @@
#include <stdint.h>
#include <inttypes.h>
#include <linux/compiler.h>
+#include <linux/string.h>
+#include <linux/zalloc.h>
-#include "../cache.h"
-#include "../util.h"
#include "../auxtrace.h"
#include "intel-pt-insn-decoder.h"
@@ -104,6 +95,7 @@ struct intel_pt_decoder {
uint64_t *insn_cnt_ptr, uint64_t *ip, uint64_t to_ip,
uint64_t max_insn_cnt, void *data);
bool (*pgd_ip)(uint64_t ip, void *data);
+ int (*lookahead)(void *data, intel_pt_lookahead_cb_t cb, void *cb_data);
void *data;
struct intel_pt_state state;
const unsigned char *buf;
@@ -116,6 +108,7 @@ struct intel_pt_decoder {
bool have_cyc;
bool fixup_last_mtc;
bool have_last_ip;
+ bool in_psb;
enum intel_pt_param_flags flags;
uint64_t pos;
uint64_t last_ip;
@@ -124,6 +117,7 @@ struct intel_pt_decoder {
uint64_t timestamp;
uint64_t tsc_timestamp;
uint64_t ref_timestamp;
+ uint64_t buf_timestamp;
uint64_t sample_timestamp;
uint64_t ret_addr;
uint64_t ctc_timestamp;
@@ -139,6 +133,10 @@ struct intel_pt_decoder {
int mtc_shift;
struct intel_pt_stack stack;
enum intel_pt_pkt_state pkt_state;
+ enum intel_pt_pkt_ctx pkt_ctx;
+ enum intel_pt_pkt_ctx prev_pkt_ctx;
+ enum intel_pt_blk_type blk_type;
+ int blk_type_pos;
struct intel_pt_pkt packet;
struct intel_pt_pkt tnt;
int pkt_step;
@@ -160,6 +158,11 @@ struct intel_pt_decoder {
uint64_t period_mask;
uint64_t period_ticks;
uint64_t last_masked_timestamp;
+ uint64_t tot_cyc_cnt;
+ uint64_t sample_tot_cyc_cnt;
+ uint64_t base_cyc_cnt;
+ uint64_t cyc_cnt_timestamp;
+ double tsc_to_cyc;
bool continuous_period;
bool overflow;
bool set_fup_tx_flags;
@@ -167,6 +170,8 @@ struct intel_pt_decoder {
bool set_fup_mwait;
bool set_fup_pwre;
bool set_fup_exstop;
+ bool set_fup_bep;
+ bool sample_cyc;
unsigned int fup_tx_flags;
unsigned int tx_flags;
uint64_t fup_ptw_payload;
@@ -226,6 +231,7 @@ struct intel_pt_decoder *intel_pt_decoder_new(struct intel_pt_params *params)
decoder->get_trace = params->get_trace;
decoder->walk_insn = params->walk_insn;
decoder->pgd_ip = params->pgd_ip;
+ decoder->lookahead = params->lookahead;
decoder->data = params->data;
decoder->return_compression = params->return_compression;
decoder->branch_enable = params->branch_enable;
@@ -479,7 +485,21 @@ static int intel_pt_bad_packet(struct intel_pt_decoder *decoder)
return -EBADMSG;
}
-static int intel_pt_get_data(struct intel_pt_decoder *decoder)
+static inline void intel_pt_update_sample_time(struct intel_pt_decoder *decoder)
+{
+ decoder->sample_timestamp = decoder->timestamp;
+ decoder->sample_insn_cnt = decoder->timestamp_insn_cnt;
+}
+
+static void intel_pt_reposition(struct intel_pt_decoder *decoder)
+{
+ decoder->ip = 0;
+ decoder->pkt_state = INTEL_PT_STATE_NO_PSB;
+ decoder->timestamp = 0;
+ decoder->have_tma = false;
+}
+
+static int intel_pt_get_data(struct intel_pt_decoder *decoder, bool reposition)
{
struct intel_pt_buffer buffer = { .buf = 0, };
int ret;
@@ -496,12 +516,10 @@ static int intel_pt_get_data(struct intel_pt_decoder *decoder)
intel_pt_log("No more data\n");
return -ENODATA;
}
- if (!buffer.consecutive) {
- decoder->ip = 0;
- decoder->pkt_state = INTEL_PT_STATE_NO_PSB;
+ decoder->buf_timestamp = buffer.ref_timestamp;
+ if (!buffer.consecutive || reposition) {
+ intel_pt_reposition(decoder);
decoder->ref_timestamp = buffer.ref_timestamp;
- decoder->timestamp = 0;
- decoder->have_tma = false;
decoder->state.trace_nr = buffer.trace_nr;
intel_pt_log("Reference timestamp 0x%" PRIx64 "\n",
decoder->ref_timestamp);
@@ -511,10 +529,11 @@ static int intel_pt_get_data(struct intel_pt_decoder *decoder)
return 0;
}
-static int intel_pt_get_next_data(struct intel_pt_decoder *decoder)
+static int intel_pt_get_next_data(struct intel_pt_decoder *decoder,
+ bool reposition)
{
if (!decoder->next_buf)
- return intel_pt_get_data(decoder);
+ return intel_pt_get_data(decoder, reposition);
decoder->buf = decoder->next_buf;
decoder->len = decoder->next_len;
@@ -533,7 +552,7 @@ static int intel_pt_get_split_packet(struct intel_pt_decoder *decoder)
len = decoder->len;
memcpy(buf, decoder->buf, len);
- ret = intel_pt_get_data(decoder);
+ ret = intel_pt_get_data(decoder, false);
if (ret) {
decoder->pos += old_len;
return ret < 0 ? ret : -EINVAL;
@@ -545,7 +564,8 @@ static int intel_pt_get_split_packet(struct intel_pt_decoder *decoder)
memcpy(buf + len, decoder->buf, n);
len += n;
- ret = intel_pt_get_packet(buf, len, &decoder->packet);
+ decoder->prev_pkt_ctx = decoder->pkt_ctx;
+ ret = intel_pt_get_packet(buf, len, &decoder->packet, &decoder->pkt_ctx);
if (ret < (int)old_len) {
decoder->next_buf = decoder->buf;
decoder->next_len = decoder->len;
@@ -580,6 +600,7 @@ static int intel_pt_pkt_lookahead(struct intel_pt_decoder *decoder,
{
struct intel_pt_pkt_info pkt_info;
const unsigned char *buf = decoder->buf;
+ enum intel_pt_pkt_ctx pkt_ctx = decoder->pkt_ctx;
size_t len = decoder->len;
int ret;
@@ -598,7 +619,8 @@ static int intel_pt_pkt_lookahead(struct intel_pt_decoder *decoder,
if (!len)
return INTEL_PT_NEED_MORE_BYTES;
- ret = intel_pt_get_packet(buf, len, &pkt_info.packet);
+ ret = intel_pt_get_packet(buf, len, &pkt_info.packet,
+ &pkt_ctx);
if (!ret)
return INTEL_PT_NEED_MORE_BYTES;
if (ret < 0)
@@ -673,6 +695,10 @@ static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info)
case INTEL_PT_MNT:
case INTEL_PT_PTWRITE:
case INTEL_PT_PTWRITE_IP:
+ case INTEL_PT_BBP:
+ case INTEL_PT_BIP:
+ case INTEL_PT_BEP:
+ case INTEL_PT_BEP_IP:
return 0;
case INTEL_PT_MTC:
@@ -859,13 +885,14 @@ static int intel_pt_get_next_packet(struct intel_pt_decoder *decoder)
decoder->len -= decoder->pkt_step;
if (!decoder->len) {
- ret = intel_pt_get_next_data(decoder);
+ ret = intel_pt_get_next_data(decoder, false);
if (ret)
return ret;
}
+ decoder->prev_pkt_ctx = decoder->pkt_ctx;
ret = intel_pt_get_packet(decoder->buf, decoder->len,
- &decoder->packet);
+ &decoder->packet, &decoder->pkt_ctx);
if (ret == INTEL_PT_NEED_MORE_BYTES && BITS_PER_LONG == 32 &&
decoder->len < INTEL_PT_PKT_MAX_SZ && !decoder->next_buf) {
ret = intel_pt_get_split_packet(decoder);
@@ -1103,6 +1130,14 @@ static bool intel_pt_fup_event(struct intel_pt_decoder *decoder)
decoder->state.to_ip = 0;
ret = true;
}
+ if (decoder->set_fup_bep) {
+ decoder->set_fup_bep = false;
+ decoder->state.type |= INTEL_PT_BLK_ITEMS;
+ decoder->state.type &= ~INTEL_PT_BRANCH;
+ decoder->state.from_ip = decoder->ip;
+ decoder->state.to_ip = 0;
+ ret = true;
+ }
return ret;
}
@@ -1317,10 +1352,10 @@ static int intel_pt_walk_tnt(struct intel_pt_decoder *decoder)
decoder->ip += intel_pt_insn.length;
return 0;
}
+ decoder->sample_cyc = false;
decoder->ip += intel_pt_insn.length;
if (!decoder->tnt.count) {
- decoder->sample_timestamp = decoder->timestamp;
- decoder->sample_insn_cnt = decoder->timestamp_insn_cnt;
+ intel_pt_update_sample_time(decoder);
return -EAGAIN;
}
decoder->tnt.payload <<= 1;
@@ -1354,6 +1389,21 @@ static int intel_pt_mode_tsx(struct intel_pt_decoder *decoder, bool *no_tip)
return 0;
}
+static uint64_t intel_pt_8b_tsc(uint64_t timestamp, uint64_t ref_timestamp)
+{
+ timestamp |= (ref_timestamp & (0xffULL << 56));
+
+ if (timestamp < ref_timestamp) {
+ if (ref_timestamp - timestamp > (1ULL << 55))
+ timestamp += (1ULL << 56);
+ } else {
+ if (timestamp - ref_timestamp > (1ULL << 55))
+ timestamp -= (1ULL << 56);
+ }
+
+ return timestamp;
+}
+
static void intel_pt_calc_tsc_timestamp(struct intel_pt_decoder *decoder)
{
uint64_t timestamp;
@@ -1361,15 +1411,8 @@ static void intel_pt_calc_tsc_timestamp(struct intel_pt_decoder *decoder)
decoder->have_tma = false;
if (decoder->ref_timestamp) {
- timestamp = decoder->packet.payload |
- (decoder->ref_timestamp & (0xffULL << 56));
- if (timestamp < decoder->ref_timestamp) {
- if (decoder->ref_timestamp - timestamp > (1ULL << 55))
- timestamp += (1ULL << 56);
- } else {
- if (timestamp - decoder->ref_timestamp > (1ULL << 55))
- timestamp -= (1ULL << 56);
- }
+ timestamp = intel_pt_8b_tsc(decoder->packet.payload,
+ decoder->ref_timestamp);
decoder->tsc_timestamp = timestamp;
decoder->timestamp = timestamp;
decoder->ref_timestamp = 0;
@@ -1413,6 +1456,42 @@ static int intel_pt_overflow(struct intel_pt_decoder *decoder)
return -EOVERFLOW;
}
+static inline void intel_pt_mtc_cyc_cnt_pge(struct intel_pt_decoder *decoder)
+{
+ if (decoder->have_cyc)
+ return;
+
+ decoder->cyc_cnt_timestamp = decoder->timestamp;
+ decoder->base_cyc_cnt = decoder->tot_cyc_cnt;
+}
+
+static inline void intel_pt_mtc_cyc_cnt_cbr(struct intel_pt_decoder *decoder)
+{
+ decoder->tsc_to_cyc = decoder->cbr / decoder->max_non_turbo_ratio_fp;
+
+ if (decoder->pge)
+ intel_pt_mtc_cyc_cnt_pge(decoder);
+}
+
+static inline void intel_pt_mtc_cyc_cnt_upd(struct intel_pt_decoder *decoder)
+{
+ uint64_t tot_cyc_cnt, tsc_delta;
+
+ if (decoder->have_cyc)
+ return;
+
+ decoder->sample_cyc = true;
+
+ if (!decoder->pge || decoder->timestamp <= decoder->cyc_cnt_timestamp)
+ return;
+
+ tsc_delta = decoder->timestamp - decoder->cyc_cnt_timestamp;
+ tot_cyc_cnt = tsc_delta * decoder->tsc_to_cyc + decoder->base_cyc_cnt;
+
+ if (tot_cyc_cnt > decoder->tot_cyc_cnt)
+ decoder->tot_cyc_cnt = tot_cyc_cnt;
+}
+
static void intel_pt_calc_tma(struct intel_pt_decoder *decoder)
{
uint32_t ctc = decoder->packet.payload;
@@ -1422,6 +1501,11 @@ static void intel_pt_calc_tma(struct intel_pt_decoder *decoder)
if (!decoder->tsc_ctc_ratio_d)
return;
+ if (decoder->pge && !decoder->in_psb)
+ intel_pt_mtc_cyc_cnt_pge(decoder);
+ else
+ intel_pt_mtc_cyc_cnt_upd(decoder);
+
decoder->last_mtc = (ctc >> decoder->mtc_shift) & 0xff;
decoder->ctc_timestamp = decoder->tsc_timestamp - fc;
if (decoder->tsc_ctc_mult) {
@@ -1477,6 +1561,8 @@ static void intel_pt_calc_mtc_timestamp(struct intel_pt_decoder *decoder)
else
decoder->timestamp = timestamp;
+ intel_pt_mtc_cyc_cnt_upd(decoder);
+
decoder->timestamp_insn_cnt = 0;
decoder->last_mtc = mtc;
@@ -1501,6 +1587,8 @@ static void intel_pt_calc_cbr(struct intel_pt_decoder *decoder)
decoder->cbr = cbr;
decoder->cbr_cyc_to_tsc = decoder->max_non_turbo_ratio_fp / cbr;
+
+ intel_pt_mtc_cyc_cnt_cbr(decoder);
}
static void intel_pt_calc_cyc_timestamp(struct intel_pt_decoder *decoder)
@@ -1510,6 +1598,9 @@ static void intel_pt_calc_cyc_timestamp(struct intel_pt_decoder *decoder)
decoder->have_cyc = true;
decoder->cycle_cnt += decoder->packet.payload;
+ if (decoder->pge)
+ decoder->tot_cyc_cnt += decoder->packet.payload;
+ decoder->sample_cyc = true;
if (!decoder->cyc_ref_timestamp)
return;
@@ -1532,19 +1623,62 @@ static void intel_pt_calc_cyc_timestamp(struct intel_pt_decoder *decoder)
intel_pt_log_to("Setting timestamp", decoder->timestamp);
}
+static void intel_pt_bbp(struct intel_pt_decoder *decoder)
+{
+ if (decoder->prev_pkt_ctx == INTEL_PT_NO_CTX) {
+ memset(decoder->state.items.mask, 0, sizeof(decoder->state.items.mask));
+ decoder->state.items.is_32_bit = false;
+ }
+ decoder->blk_type = decoder->packet.payload;
+ decoder->blk_type_pos = intel_pt_blk_type_pos(decoder->blk_type);
+ if (decoder->blk_type == INTEL_PT_GP_REGS)
+ decoder->state.items.is_32_bit = decoder->packet.count;
+ if (decoder->blk_type_pos < 0) {
+ intel_pt_log("WARNING: Unknown block type %u\n",
+ decoder->blk_type);
+ } else if (decoder->state.items.mask[decoder->blk_type_pos]) {
+ intel_pt_log("WARNING: Duplicate block type %u\n",
+ decoder->blk_type);
+ }
+}
+
+static void intel_pt_bip(struct intel_pt_decoder *decoder)
+{
+ uint32_t id = decoder->packet.count;
+ uint32_t bit = 1 << id;
+ int pos = decoder->blk_type_pos;
+
+ if (pos < 0 || id >= INTEL_PT_BLK_ITEM_ID_CNT) {
+ intel_pt_log("WARNING: Unknown block item %u type %d\n",
+ id, decoder->blk_type);
+ return;
+ }
+
+ if (decoder->state.items.mask[pos] & bit) {
+ intel_pt_log("WARNING: Duplicate block item %u type %d\n",
+ id, decoder->blk_type);
+ }
+
+ decoder->state.items.mask[pos] |= bit;
+ decoder->state.items.val[pos][id] = decoder->packet.payload;
+}
+
/* Walk PSB+ packets when already in sync. */
static int intel_pt_walk_psbend(struct intel_pt_decoder *decoder)
{
int err;
+ decoder->in_psb = true;
+
while (1) {
err = intel_pt_get_next_packet(decoder);
if (err)
- return err;
+ goto out;
switch (decoder->packet.type) {
case INTEL_PT_PSBEND:
- return 0;
+ err = 0;
+ goto out;
case INTEL_PT_TIP_PGD:
case INTEL_PT_TIP_PGE:
@@ -1560,12 +1694,18 @@ static int intel_pt_walk_psbend(struct intel_pt_decoder *decoder)
case INTEL_PT_MWAIT:
case INTEL_PT_PWRE:
case INTEL_PT_PWRX:
+ case INTEL_PT_BBP:
+ case INTEL_PT_BIP:
+ case INTEL_PT_BEP:
+ case INTEL_PT_BEP_IP:
decoder->have_tma = false;
intel_pt_log("ERROR: Unexpected packet\n");
- return -EAGAIN;
+ err = -EAGAIN;
+ goto out;
case INTEL_PT_OVF:
- return intel_pt_overflow(decoder);
+ err = intel_pt_overflow(decoder);
+ goto out;
case INTEL_PT_TSC:
intel_pt_calc_tsc_timestamp(decoder);
@@ -1611,6 +1751,10 @@ static int intel_pt_walk_psbend(struct intel_pt_decoder *decoder)
break;
}
}
+out:
+ decoder->in_psb = false;
+
+ return err;
}
static int intel_pt_walk_fup_tip(struct intel_pt_decoder *decoder)
@@ -1647,6 +1791,10 @@ static int intel_pt_walk_fup_tip(struct intel_pt_decoder *decoder)
case INTEL_PT_MWAIT:
case INTEL_PT_PWRE:
case INTEL_PT_PWRX:
+ case INTEL_PT_BBP:
+ case INTEL_PT_BIP:
+ case INTEL_PT_BEP:
+ case INTEL_PT_BEP_IP:
intel_pt_log("ERROR: Missing TIP after FUP\n");
decoder->pkt_state = INTEL_PT_STATE_ERR3;
decoder->pkt_step = 0;
@@ -1684,6 +1832,7 @@ static int intel_pt_walk_fup_tip(struct intel_pt_decoder *decoder)
decoder->state.to_ip = decoder->ip;
}
decoder->state.type |= INTEL_PT_TRACE_BEGIN;
+ intel_pt_mtc_cyc_cnt_pge(decoder);
return 0;
case INTEL_PT_TIP:
@@ -1754,6 +1903,7 @@ next:
case INTEL_PT_TIP_PGE: {
decoder->pge = true;
+ intel_pt_mtc_cyc_cnt_pge(decoder);
if (decoder->packet.count == 0) {
intel_pt_log_at("Skipping zero TIP.PGE",
decoder->pos);
@@ -1825,6 +1975,13 @@ next:
goto next;
if (err)
return err;
+ /*
+ * PSB+ CBR will not have changed but cater for the
+ * possibility of another CBR change that gets caught up
+ * in the PSB+.
+ */
+ if (decoder->cbr != decoder->cbr_seen)
+ return 0;
break;
case INTEL_PT_PIP:
@@ -1865,16 +2022,8 @@ next:
case INTEL_PT_CBR:
intel_pt_calc_cbr(decoder);
- if (!decoder->branch_enable &&
- decoder->cbr != decoder->cbr_seen) {
- decoder->cbr_seen = decoder->cbr;
- decoder->state.type = INTEL_PT_CBR_CHG;
- decoder->state.from_ip = decoder->ip;
- decoder->state.to_ip = 0;
- decoder->state.cbr_payload =
- decoder->packet.payload;
+ if (decoder->cbr != decoder->cbr_seen)
return 0;
- }
break;
case INTEL_PT_MODE_EXEC:
@@ -1966,6 +2115,33 @@ next:
decoder->state.pwrx_payload = decoder->packet.payload;
return 0;
+ case INTEL_PT_BBP:
+ intel_pt_bbp(decoder);
+ break;
+
+ case INTEL_PT_BIP:
+ intel_pt_bip(decoder);
+ break;
+
+ case INTEL_PT_BEP:
+ decoder->state.type = INTEL_PT_BLK_ITEMS;
+ decoder->state.from_ip = decoder->ip;
+ decoder->state.to_ip = 0;
+ return 0;
+
+ case INTEL_PT_BEP_IP:
+ err = intel_pt_get_next_packet(decoder);
+ if (err)
+ return err;
+ if (decoder->packet.type == INTEL_PT_FUP) {
+ decoder->set_fup_bep = true;
+ no_tip = true;
+ } else {
+ intel_pt_log_at("ERROR: Missing FUP after BEP",
+ decoder->pos);
+ }
+ goto next;
+
default:
return intel_pt_bug(decoder);
}
@@ -1984,10 +2160,12 @@ static int intel_pt_walk_psb(struct intel_pt_decoder *decoder)
{
int err;
+ decoder->in_psb = true;
+
while (1) {
err = intel_pt_get_next_packet(decoder);
if (err)
- return err;
+ goto out;
switch (decoder->packet.type) {
case INTEL_PT_TIP_PGD:
@@ -2002,8 +2180,13 @@ static int intel_pt_walk_psb(struct intel_pt_decoder *decoder)
case INTEL_PT_MWAIT:
case INTEL_PT_PWRE:
case INTEL_PT_PWRX:
+ case INTEL_PT_BBP:
+ case INTEL_PT_BIP:
+ case INTEL_PT_BEP:
+ case INTEL_PT_BEP_IP:
intel_pt_log("ERROR: Unexpected packet\n");
- return -ENOENT;
+ err = -ENOENT;
+ goto out;
case INTEL_PT_FUP:
decoder->pge = true;
@@ -2062,16 +2245,20 @@ static int intel_pt_walk_psb(struct intel_pt_decoder *decoder)
decoder->pkt_state = INTEL_PT_STATE_ERR4;
else
decoder->pkt_state = INTEL_PT_STATE_ERR3;
- return -ENOENT;
+ err = -ENOENT;
+ goto out;
case INTEL_PT_BAD: /* Does not happen */
- return intel_pt_bug(decoder);
+ err = intel_pt_bug(decoder);
+ goto out;
case INTEL_PT_OVF:
- return intel_pt_overflow(decoder);
+ err = intel_pt_overflow(decoder);
+ goto out;
case INTEL_PT_PSBEND:
- return 0;
+ err = 0;
+ goto out;
case INTEL_PT_PSB:
case INTEL_PT_VMCS:
@@ -2081,6 +2268,10 @@ static int intel_pt_walk_psb(struct intel_pt_decoder *decoder)
break;
}
}
+out:
+ decoder->in_psb = false;
+
+ return err;
}
static int intel_pt_walk_to_ip(struct intel_pt_decoder *decoder)
@@ -2095,18 +2286,30 @@ static int intel_pt_walk_to_ip(struct intel_pt_decoder *decoder)
switch (decoder->packet.type) {
case INTEL_PT_TIP_PGD:
decoder->continuous_period = false;
- __fallthrough;
+ decoder->pge = false;
+ if (intel_pt_have_ip(decoder))
+ intel_pt_set_ip(decoder);
+ if (!decoder->ip)
+ break;
+ decoder->state.type |= INTEL_PT_TRACE_END;
+ return 0;
+
case INTEL_PT_TIP_PGE:
+ decoder->pge = true;
+ intel_pt_mtc_cyc_cnt_pge(decoder);
+ if (intel_pt_have_ip(decoder))
+ intel_pt_set_ip(decoder);
+ if (!decoder->ip)
+ break;
+ decoder->state.type |= INTEL_PT_TRACE_BEGIN;
+ return 0;
+
case INTEL_PT_TIP:
- decoder->pge = decoder->packet.type != INTEL_PT_TIP_PGD;
+ decoder->pge = true;
if (intel_pt_have_ip(decoder))
intel_pt_set_ip(decoder);
if (!decoder->ip)
break;
- if (decoder->packet.type == INTEL_PT_TIP_PGE)
- decoder->state.type |= INTEL_PT_TRACE_BEGIN;
- if (decoder->packet.type == INTEL_PT_TIP_PGD)
- decoder->state.type |= INTEL_PT_TRACE_END;
return 0;
case INTEL_PT_FUP:
@@ -2187,6 +2390,10 @@ static int intel_pt_walk_to_ip(struct intel_pt_decoder *decoder)
case INTEL_PT_MWAIT:
case INTEL_PT_PWRE:
case INTEL_PT_PWRX:
+ case INTEL_PT_BBP:
+ case INTEL_PT_BIP:
+ case INTEL_PT_BEP:
+ case INTEL_PT_BEP_IP:
default:
break;
}
@@ -2202,6 +2409,7 @@ static int intel_pt_sync_ip(struct intel_pt_decoder *decoder)
decoder->set_fup_mwait = false;
decoder->set_fup_pwre = false;
decoder->set_fup_exstop = false;
+ decoder->set_fup_bep = false;
if (!decoder->branch_enable) {
decoder->pkt_state = INTEL_PT_STATE_IN_SYNC;
@@ -2259,7 +2467,7 @@ static int intel_pt_get_split_psb(struct intel_pt_decoder *decoder,
decoder->pos += decoder->len;
decoder->len = 0;
- ret = intel_pt_get_next_data(decoder);
+ ret = intel_pt_get_next_data(decoder, false);
if (ret)
return ret;
@@ -2285,7 +2493,7 @@ static int intel_pt_scan_for_psb(struct intel_pt_decoder *decoder)
intel_pt_log("Scanning for PSB\n");
while (1) {
if (!decoder->len) {
- ret = intel_pt_get_next_data(decoder);
+ ret = intel_pt_get_next_data(decoder, false);
if (ret)
return ret;
}
@@ -2413,18 +2621,24 @@ const struct intel_pt_state *intel_pt_decode(struct intel_pt_decoder *decoder)
if (err) {
decoder->state.err = intel_pt_ext_err(err);
decoder->state.from_ip = decoder->ip;
- decoder->sample_timestamp = decoder->timestamp;
- decoder->sample_insn_cnt = decoder->timestamp_insn_cnt;
+ intel_pt_update_sample_time(decoder);
+ decoder->sample_tot_cyc_cnt = decoder->tot_cyc_cnt;
} else {
decoder->state.err = 0;
- if (decoder->cbr != decoder->cbr_seen && decoder->state.type) {
+ if (decoder->cbr != decoder->cbr_seen) {
decoder->cbr_seen = decoder->cbr;
+ if (!decoder->state.type) {
+ decoder->state.from_ip = decoder->ip;
+ decoder->state.to_ip = 0;
+ }
decoder->state.type |= INTEL_PT_CBR_CHG;
decoder->state.cbr_payload = decoder->cbr_payload;
+ decoder->state.cbr = decoder->cbr;
}
if (intel_pt_sample_time(decoder->pkt_state)) {
- decoder->sample_timestamp = decoder->timestamp;
- decoder->sample_insn_cnt = decoder->timestamp_insn_cnt;
+ intel_pt_update_sample_time(decoder);
+ if (decoder->sample_cyc)
+ decoder->sample_tot_cyc_cnt = decoder->tot_cyc_cnt;
}
}
@@ -2432,6 +2646,7 @@ const struct intel_pt_state *intel_pt_decode(struct intel_pt_decoder *decoder)
decoder->state.est_timestamp = intel_pt_est_timestamp(decoder);
decoder->state.cr3 = decoder->cr3;
decoder->state.tot_insn_cnt = decoder->tot_insn_cnt;
+ decoder->state.tot_cyc_cnt = decoder->sample_tot_cyc_cnt;
return &decoder->state;
}
@@ -2535,11 +2750,12 @@ static unsigned char *intel_pt_last_psb(unsigned char *buf, size_t len)
static bool intel_pt_next_tsc(unsigned char *buf, size_t len, uint64_t *tsc,
size_t *rem)
{
+ enum intel_pt_pkt_ctx ctx = INTEL_PT_NO_CTX;
struct intel_pt_pkt packet;
int ret;
while (len) {
- ret = intel_pt_get_packet(buf, len, &packet);
+ ret = intel_pt_get_packet(buf, len, &packet, &ctx);
if (ret <= 0)
return false;
if (packet.type == INTEL_PT_TSC) {
@@ -2741,3 +2957,131 @@ unsigned char *intel_pt_find_overlap(unsigned char *buf_a, size_t len_a,
return buf_b; /* No overlap */
}
}
+
+/**
+ * struct fast_forward_data - data used by intel_pt_ff_cb().
+ * @timestamp: timestamp to fast forward towards
+ * @buf_timestamp: buffer timestamp of last buffer with trace data earlier than
+ * the fast forward timestamp.
+ */
+struct fast_forward_data {
+ uint64_t timestamp;
+ uint64_t buf_timestamp;
+};
+
+/**
+ * intel_pt_ff_cb - fast forward lookahead callback.
+ * @buffer: Intel PT trace buffer
+ * @data: opaque pointer to fast forward data (struct fast_forward_data)
+ *
+ * Determine if @buffer trace is past the fast forward timestamp.
+ *
+ * Return: 1 (stop lookahead) if @buffer trace is past the fast forward
+ * timestamp, and 0 otherwise.
+ */
+static int intel_pt_ff_cb(struct intel_pt_buffer *buffer, void *data)
+{
+ struct fast_forward_data *d = data;
+ unsigned char *buf;
+ uint64_t tsc;
+ size_t rem;
+ size_t len;
+
+ buf = (unsigned char *)buffer->buf;
+ len = buffer->len;
+
+ if (!intel_pt_next_psb(&buf, &len) ||
+ !intel_pt_next_tsc(buf, len, &tsc, &rem))
+ return 0;
+
+ tsc = intel_pt_8b_tsc(tsc, buffer->ref_timestamp);
+
+ intel_pt_log("Buffer 1st timestamp " x64_fmt " ref timestamp " x64_fmt "\n",
+ tsc, buffer->ref_timestamp);
+
+ /*
+ * If the buffer contains a timestamp earlier that the fast forward
+ * timestamp, then record it, else stop.
+ */
+ if (tsc < d->timestamp)
+ d->buf_timestamp = buffer->ref_timestamp;
+ else
+ return 1;
+
+ return 0;
+}
+
+/**
+ * intel_pt_fast_forward - reposition decoder forwards.
+ * @decoder: Intel PT decoder
+ * @timestamp: timestamp to fast forward towards
+ *
+ * Reposition decoder at the last PSB with a timestamp earlier than @timestamp.
+ *
+ * Return: 0 on success or negative error code on failure.
+ */
+int intel_pt_fast_forward(struct intel_pt_decoder *decoder, uint64_t timestamp)
+{
+ struct fast_forward_data d = { .timestamp = timestamp };
+ unsigned char *buf;
+ size_t len;
+ int err;
+
+ intel_pt_log("Fast forward towards timestamp " x64_fmt "\n", timestamp);
+
+ /* Find buffer timestamp of buffer to fast forward to */
+ err = decoder->lookahead(decoder->data, intel_pt_ff_cb, &d);
+ if (err < 0)
+ return err;
+
+ /* Walk to buffer with same buffer timestamp */
+ if (d.buf_timestamp) {
+ do {
+ decoder->pos += decoder->len;
+ decoder->len = 0;
+ err = intel_pt_get_next_data(decoder, true);
+ /* -ENOLINK means non-consecutive trace */
+ if (err && err != -ENOLINK)
+ return err;
+ } while (decoder->buf_timestamp != d.buf_timestamp);
+ }
+
+ if (!decoder->buf)
+ return 0;
+
+ buf = (unsigned char *)decoder->buf;
+ len = decoder->len;
+
+ if (!intel_pt_next_psb(&buf, &len))
+ return 0;
+
+ /*
+ * Walk PSBs while the PSB timestamp is less than the fast forward
+ * timestamp.
+ */
+ do {
+ uint64_t tsc;
+ size_t rem;
+
+ if (!intel_pt_next_tsc(buf, len, &tsc, &rem))
+ break;
+ tsc = intel_pt_8b_tsc(tsc, decoder->buf_timestamp);
+ /*
+ * A TSC packet can slip past MTC packets but, after fast
+ * forward, decoding starts at the TSC timestamp. That means
+ * the timestamps may not be exactly the same as the timestamps
+ * that would have been decoded without fast forward.
+ */
+ if (tsc < timestamp) {
+ intel_pt_log("Fast forward to next PSB timestamp " x64_fmt "\n", tsc);
+ decoder->pos += decoder->len - len;
+ decoder->buf = buf;
+ decoder->len = len;
+ intel_pt_reposition(decoder);
+ } else {
+ break;
+ }
+ } while (intel_pt_step_psb(&buf, &len));
+
+ return 0;
+}
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h
index ed088d4726ba..e289e463d635 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h
@@ -1,16 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* intel_pt_decoder.h: Intel Processor Trace support
* Copyright (c) 2013-2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#ifndef INCLUDE__INTEL_PT_DECODER_H__
@@ -39,6 +30,7 @@ enum intel_pt_sample_type {
INTEL_PT_CBR_CHG = 1 << 8,
INTEL_PT_TRACE_BEGIN = 1 << 9,
INTEL_PT_TRACE_END = 1 << 10,
+ INTEL_PT_BLK_ITEMS = 1 << 11,
};
enum intel_pt_period_type {
@@ -70,6 +62,141 @@ enum intel_pt_param_flags {
INTEL_PT_FUP_WITH_NLIP = 1 << 0,
};
+enum intel_pt_blk_type {
+ INTEL_PT_GP_REGS = 1,
+ INTEL_PT_PEBS_BASIC = 4,
+ INTEL_PT_PEBS_MEM = 5,
+ INTEL_PT_LBR_0 = 8,
+ INTEL_PT_LBR_1 = 9,
+ INTEL_PT_LBR_2 = 10,
+ INTEL_PT_XMM = 16,
+ INTEL_PT_BLK_TYPE_MAX
+};
+
+/*
+ * The block type numbers are not sequential but here they are given sequential
+ * positions to avoid wasting space for array placement.
+ */
+enum intel_pt_blk_type_pos {
+ INTEL_PT_GP_REGS_POS,
+ INTEL_PT_PEBS_BASIC_POS,
+ INTEL_PT_PEBS_MEM_POS,
+ INTEL_PT_LBR_0_POS,
+ INTEL_PT_LBR_1_POS,
+ INTEL_PT_LBR_2_POS,
+ INTEL_PT_XMM_POS,
+ INTEL_PT_BLK_TYPE_CNT
+};
+
+/* Get the array position for a block type */
+static inline int intel_pt_blk_type_pos(enum intel_pt_blk_type blk_type)
+{
+#define BLK_TYPE(bt) [INTEL_PT_##bt] = INTEL_PT_##bt##_POS + 1
+ const int map[INTEL_PT_BLK_TYPE_MAX] = {
+ BLK_TYPE(GP_REGS),
+ BLK_TYPE(PEBS_BASIC),
+ BLK_TYPE(PEBS_MEM),
+ BLK_TYPE(LBR_0),
+ BLK_TYPE(LBR_1),
+ BLK_TYPE(LBR_2),
+ BLK_TYPE(XMM),
+ };
+#undef BLK_TYPE
+
+ return blk_type < INTEL_PT_BLK_TYPE_MAX ? map[blk_type] - 1 : -1;
+}
+
+#define INTEL_PT_BLK_ITEM_ID_CNT 32
+
+/*
+ * Use unions so that the block items can be accessed by name or by array index.
+ * There is an array of 32-bit masks for each block type, which indicate which
+ * values are present. Then arrays of 32 64-bit values for each block type.
+ */
+struct intel_pt_blk_items {
+ union {
+ uint32_t mask[INTEL_PT_BLK_TYPE_CNT];
+ struct {
+ uint32_t has_rflags:1;
+ uint32_t has_rip:1;
+ uint32_t has_rax:1;
+ uint32_t has_rcx:1;
+ uint32_t has_rdx:1;
+ uint32_t has_rbx:1;
+ uint32_t has_rsp:1;
+ uint32_t has_rbp:1;
+ uint32_t has_rsi:1;
+ uint32_t has_rdi:1;
+ uint32_t has_r8:1;
+ uint32_t has_r9:1;
+ uint32_t has_r10:1;
+ uint32_t has_r11:1;
+ uint32_t has_r12:1;
+ uint32_t has_r13:1;
+ uint32_t has_r14:1;
+ uint32_t has_r15:1;
+ uint32_t has_unused_0:14;
+ uint32_t has_ip:1;
+ uint32_t has_applicable_counters:1;
+ uint32_t has_timestamp:1;
+ uint32_t has_unused_1:29;
+ uint32_t has_mem_access_address:1;
+ uint32_t has_mem_aux_info:1;
+ uint32_t has_mem_access_latency:1;
+ uint32_t has_tsx_aux_info:1;
+ uint32_t has_unused_2:28;
+ uint32_t has_lbr_0;
+ uint32_t has_lbr_1;
+ uint32_t has_lbr_2;
+ uint32_t has_xmm;
+ };
+ };
+ union {
+ uint64_t val[INTEL_PT_BLK_TYPE_CNT][INTEL_PT_BLK_ITEM_ID_CNT];
+ struct {
+ struct {
+ uint64_t rflags;
+ uint64_t rip;
+ uint64_t rax;
+ uint64_t rcx;
+ uint64_t rdx;
+ uint64_t rbx;
+ uint64_t rsp;
+ uint64_t rbp;
+ uint64_t rsi;
+ uint64_t rdi;
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t unused_0[INTEL_PT_BLK_ITEM_ID_CNT - 18];
+ };
+ struct {
+ uint64_t ip;
+ uint64_t applicable_counters;
+ uint64_t timestamp;
+ uint64_t unused_1[INTEL_PT_BLK_ITEM_ID_CNT - 3];
+ };
+ struct {
+ uint64_t mem_access_address;
+ uint64_t mem_aux_info;
+ uint64_t mem_access_latency;
+ uint64_t tsx_aux_info;
+ uint64_t unused_2[INTEL_PT_BLK_ITEM_ID_CNT - 4];
+ };
+ uint64_t lbr_0[INTEL_PT_BLK_ITEM_ID_CNT];
+ uint64_t lbr_1[INTEL_PT_BLK_ITEM_ID_CNT];
+ uint64_t lbr_2[INTEL_PT_BLK_ITEM_ID_CNT];
+ uint64_t xmm[INTEL_PT_BLK_ITEM_ID_CNT];
+ };
+ };
+ bool is_32_bit;
+};
+
struct intel_pt_state {
enum intel_pt_sample_type type;
int err;
@@ -77,6 +204,7 @@ struct intel_pt_state {
uint64_t to_ip;
uint64_t cr3;
uint64_t tot_insn_cnt;
+ uint64_t tot_cyc_cnt;
uint64_t timestamp;
uint64_t est_timestamp;
uint64_t trace_nr;
@@ -85,10 +213,12 @@ struct intel_pt_state {
uint64_t pwre_payload;
uint64_t pwrx_payload;
uint64_t cbr_payload;
+ uint32_t cbr;
uint32_t flags;
enum intel_pt_insn_op insn_op;
int insn_len;
char insn[INTEL_PT_INSN_BUF_SZ];
+ struct intel_pt_blk_items items;
};
struct intel_pt_insn;
@@ -101,12 +231,15 @@ struct intel_pt_buffer {
uint64_t trace_nr;
};
+typedef int (*intel_pt_lookahead_cb_t)(struct intel_pt_buffer *, void *);
+
struct intel_pt_params {
int (*get_trace)(struct intel_pt_buffer *buffer, void *data);
int (*walk_insn)(struct intel_pt_insn *intel_pt_insn,
uint64_t *insn_cnt_ptr, uint64_t *ip, uint64_t to_ip,
uint64_t max_insn_cnt, void *data);
bool (*pgd_ip)(uint64_t ip, void *data);
+ int (*lookahead)(void *data, intel_pt_lookahead_cb_t cb, void *cb_data);
void *data;
bool return_compression;
bool branch_enable;
@@ -126,6 +259,8 @@ void intel_pt_decoder_free(struct intel_pt_decoder *decoder);
const struct intel_pt_state *intel_pt_decode(struct intel_pt_decoder *decoder);
+int intel_pt_fast_forward(struct intel_pt_decoder *decoder, uint64_t timestamp);
+
unsigned char *intel_pt_find_overlap(unsigned char *buf_a, size_t len_a,
unsigned char *buf_b, size_t len_b,
bool have_tsc, bool *consecutive);
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c
index 1c0e289f01e6..fb8a3558d3d5 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c
@@ -1,29 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* intel_pt_insn_decoder.c: Intel Processor Trace support
* Copyright (c) 2013-2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
+#include <linux/kernel.h>
#include <stdio.h>
#include <string.h>
#include <endian.h>
#include <byteswap.h>
+#include "../../../arch/x86/include/asm/insn.h"
-#include "event.h"
-
-#include "insn.h"
+#include "../../../arch/x86/lib/inat.c"
+#include "../../../arch/x86/lib/insn.c"
-#include "inat.c"
-#include "insn.c"
+#include "event.h"
#include "intel-pt-insn-decoder.h"
#include "dump-insn.h"
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h
index 37ec5627ae9b..95a1eb0141ff 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h
@@ -1,16 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* intel_pt_insn_decoder.h: Intel Processor Trace support
* Copyright (c) 2013-2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#ifndef INCLUDE__INTEL_PT_INSN_DECODER_H__
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-log.c b/tools/perf/util/intel-pt-decoder/intel-pt-log.c
index 5e64da270f97..09feb5b07d32 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-log.c
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-log.c
@@ -1,16 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* intel_pt_log.c: Intel Processor Trace support
* Copyright (c) 2013-2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#include <stdio.h>
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-log.h b/tools/perf/util/intel-pt-decoder/intel-pt-log.h
index cc084937f701..388661f89c44 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-log.h
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-log.h
@@ -1,16 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* intel_pt_log.h: Intel Processor Trace support
* Copyright (c) 2013-2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#ifndef INCLUDE__INTEL_PT_LOG_H__
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c
index d426761a549d..0ccf10a0bf44 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c
@@ -1,16 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* intel_pt_pkt_decoder.c: Intel Processor Trace support
* Copyright (c) 2013-2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#include <stdio.h>
@@ -71,6 +62,10 @@ static const char * const packet_name[] = {
[INTEL_PT_MWAIT] = "MWAIT",
[INTEL_PT_PWRE] = "PWRE",
[INTEL_PT_PWRX] = "PWRX",
+ [INTEL_PT_BBP] = "BBP",
+ [INTEL_PT_BIP] = "BIP",
+ [INTEL_PT_BEP] = "BEP",
+ [INTEL_PT_BEP_IP] = "BEP",
};
const char *intel_pt_pkt_name(enum intel_pt_pkt_type type)
@@ -289,6 +284,55 @@ static int intel_pt_get_pwrx(const unsigned char *buf, size_t len,
return 7;
}
+static int intel_pt_get_bbp(const unsigned char *buf, size_t len,
+ struct intel_pt_pkt *packet)
+{
+ if (len < 3)
+ return INTEL_PT_NEED_MORE_BYTES;
+ packet->type = INTEL_PT_BBP;
+ packet->count = buf[2] >> 7;
+ packet->payload = buf[2] & 0x1f;
+ return 3;
+}
+
+static int intel_pt_get_bip_4(const unsigned char *buf, size_t len,
+ struct intel_pt_pkt *packet)
+{
+ if (len < 5)
+ return INTEL_PT_NEED_MORE_BYTES;
+ packet->type = INTEL_PT_BIP;
+ packet->count = buf[0] >> 3;
+ memcpy_le64(&packet->payload, buf + 1, 4);
+ return 5;
+}
+
+static int intel_pt_get_bip_8(const unsigned char *buf, size_t len,
+ struct intel_pt_pkt *packet)
+{
+ if (len < 9)
+ return INTEL_PT_NEED_MORE_BYTES;
+ packet->type = INTEL_PT_BIP;
+ packet->count = buf[0] >> 3;
+ memcpy_le64(&packet->payload, buf + 1, 8);
+ return 9;
+}
+
+static int intel_pt_get_bep(size_t len, struct intel_pt_pkt *packet)
+{
+ if (len < 2)
+ return INTEL_PT_NEED_MORE_BYTES;
+ packet->type = INTEL_PT_BEP;
+ return 2;
+}
+
+static int intel_pt_get_bep_ip(size_t len, struct intel_pt_pkt *packet)
+{
+ if (len < 2)
+ return INTEL_PT_NEED_MORE_BYTES;
+ packet->type = INTEL_PT_BEP_IP;
+ return 2;
+}
+
static int intel_pt_get_ext(const unsigned char *buf, size_t len,
struct intel_pt_pkt *packet)
{
@@ -329,6 +373,12 @@ static int intel_pt_get_ext(const unsigned char *buf, size_t len,
return intel_pt_get_pwre(buf, len, packet);
case 0xA2: /* PWRX */
return intel_pt_get_pwrx(buf, len, packet);
+ case 0x63: /* BBP */
+ return intel_pt_get_bbp(buf, len, packet);
+ case 0x33: /* BEP no IP */
+ return intel_pt_get_bep(len, packet);
+ case 0xb3: /* BEP with IP */
+ return intel_pt_get_bep_ip(len, packet);
default:
return INTEL_PT_BAD_PACKET;
}
@@ -477,7 +527,8 @@ static int intel_pt_get_mtc(const unsigned char *buf, size_t len,
}
static int intel_pt_do_get_packet(const unsigned char *buf, size_t len,
- struct intel_pt_pkt *packet)
+ struct intel_pt_pkt *packet,
+ enum intel_pt_pkt_ctx ctx)
{
unsigned int byte;
@@ -487,6 +538,22 @@ static int intel_pt_do_get_packet(const unsigned char *buf, size_t len,
return INTEL_PT_NEED_MORE_BYTES;
byte = buf[0];
+
+ switch (ctx) {
+ case INTEL_PT_NO_CTX:
+ break;
+ case INTEL_PT_BLK_4_CTX:
+ if ((byte & 0x7) == 4)
+ return intel_pt_get_bip_4(buf, len, packet);
+ break;
+ case INTEL_PT_BLK_8_CTX:
+ if ((byte & 0x7) == 4)
+ return intel_pt_get_bip_8(buf, len, packet);
+ break;
+ default:
+ break;
+ };
+
if (!(byte & BIT(0))) {
if (byte == 0)
return intel_pt_get_pad(packet);
@@ -525,15 +592,65 @@ static int intel_pt_do_get_packet(const unsigned char *buf, size_t len,
}
}
+void intel_pt_upd_pkt_ctx(const struct intel_pt_pkt *packet,
+ enum intel_pt_pkt_ctx *ctx)
+{
+ switch (packet->type) {
+ case INTEL_PT_BAD:
+ case INTEL_PT_PAD:
+ case INTEL_PT_TSC:
+ case INTEL_PT_TMA:
+ case INTEL_PT_MTC:
+ case INTEL_PT_FUP:
+ case INTEL_PT_CYC:
+ case INTEL_PT_CBR:
+ case INTEL_PT_MNT:
+ case INTEL_PT_EXSTOP:
+ case INTEL_PT_EXSTOP_IP:
+ case INTEL_PT_PWRE:
+ case INTEL_PT_PWRX:
+ case INTEL_PT_BIP:
+ break;
+ case INTEL_PT_TNT:
+ case INTEL_PT_TIP:
+ case INTEL_PT_TIP_PGD:
+ case INTEL_PT_TIP_PGE:
+ case INTEL_PT_MODE_EXEC:
+ case INTEL_PT_MODE_TSX:
+ case INTEL_PT_PIP:
+ case INTEL_PT_OVF:
+ case INTEL_PT_VMCS:
+ case INTEL_PT_TRACESTOP:
+ case INTEL_PT_PSB:
+ case INTEL_PT_PSBEND:
+ case INTEL_PT_PTWRITE:
+ case INTEL_PT_PTWRITE_IP:
+ case INTEL_PT_MWAIT:
+ case INTEL_PT_BEP:
+ case INTEL_PT_BEP_IP:
+ *ctx = INTEL_PT_NO_CTX;
+ break;
+ case INTEL_PT_BBP:
+ if (packet->count)
+ *ctx = INTEL_PT_BLK_4_CTX;
+ else
+ *ctx = INTEL_PT_BLK_8_CTX;
+ break;
+ default:
+ break;
+ }
+}
+
int intel_pt_get_packet(const unsigned char *buf, size_t len,
- struct intel_pt_pkt *packet)
+ struct intel_pt_pkt *packet, enum intel_pt_pkt_ctx *ctx)
{
int ret;
- ret = intel_pt_do_get_packet(buf, len, packet);
+ ret = intel_pt_do_get_packet(buf, len, packet, *ctx);
if (ret > 0) {
while (ret < 8 && len > (size_t)ret && !buf[ret])
ret += 1;
+ intel_pt_upd_pkt_ctx(packet, ctx);
}
return ret;
}
@@ -611,8 +728,10 @@ int intel_pt_pkt_desc(const struct intel_pt_pkt *packet, char *buf,
return snprintf(buf, buf_len, "%s 0x%llx IP:0", name, payload);
case INTEL_PT_PTWRITE_IP:
return snprintf(buf, buf_len, "%s 0x%llx IP:1", name, payload);
+ case INTEL_PT_BEP:
case INTEL_PT_EXSTOP:
return snprintf(buf, buf_len, "%s IP:0", name);
+ case INTEL_PT_BEP_IP:
case INTEL_PT_EXSTOP_IP:
return snprintf(buf, buf_len, "%s IP:1", name);
case INTEL_PT_MWAIT:
@@ -630,6 +749,12 @@ int intel_pt_pkt_desc(const struct intel_pt_pkt *packet, char *buf,
(unsigned int)((payload >> 4) & 0xf),
(unsigned int)(payload & 0xf),
(unsigned int)((payload >> 8) & 0xf));
+ case INTEL_PT_BBP:
+ return snprintf(buf, buf_len, "%s SZ %s-byte Type 0x%llx",
+ name, packet->count ? "4" : "8", payload);
+ case INTEL_PT_BIP:
+ return snprintf(buf, buf_len, "%s ID 0x%02x Value 0x%llx",
+ name, packet->count, payload);
default:
break;
}
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.h b/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.h
index 73ddc3a88d07..17ca9b56d72f 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.h
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.h
@@ -1,16 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* intel_pt_pkt_decoder.h: Intel Processor Trace support
* Copyright (c) 2013-2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#ifndef INCLUDE__INTEL_PT_PKT_DECODER_H__
@@ -59,6 +50,10 @@ enum intel_pt_pkt_type {
INTEL_PT_MWAIT,
INTEL_PT_PWRE,
INTEL_PT_PWRX,
+ INTEL_PT_BBP,
+ INTEL_PT_BIP,
+ INTEL_PT_BEP,
+ INTEL_PT_BEP_IP,
};
struct intel_pt_pkt {
@@ -67,10 +62,25 @@ struct intel_pt_pkt {
uint64_t payload;
};
+/*
+ * Decoding of BIP packets conflicts with single-byte TNT packets. Since BIP
+ * packets only occur in the context of a block (i.e. between BBP and BEP), that
+ * context must be recorded and passed to the packet decoder.
+ */
+enum intel_pt_pkt_ctx {
+ INTEL_PT_NO_CTX, /* BIP packets are invalid */
+ INTEL_PT_BLK_4_CTX, /* 4-byte BIP packets */
+ INTEL_PT_BLK_8_CTX, /* 8-byte BIP packets */
+};
+
const char *intel_pt_pkt_name(enum intel_pt_pkt_type);
int intel_pt_get_packet(const unsigned char *buf, size_t len,
- struct intel_pt_pkt *packet);
+ struct intel_pt_pkt *packet,
+ enum intel_pt_pkt_ctx *ctx);
+
+void intel_pt_upd_pkt_ctx(const struct intel_pt_pkt *packet,
+ enum intel_pt_pkt_ctx *ctx);
int intel_pt_pkt_desc(const struct intel_pt_pkt *packet, char *buf, size_t len);
diff --git a/tools/perf/util/intel-pt-decoder/x86-opcode-map.txt b/tools/perf/util/intel-pt-decoder/x86-opcode-map.txt
deleted file mode 100644
index e0b85930dd77..000000000000
--- a/tools/perf/util/intel-pt-decoder/x86-opcode-map.txt
+++ /dev/null
@@ -1,1072 +0,0 @@
-# x86 Opcode Maps
-#
-# This is (mostly) based on following documentations.
-# - Intel(R) 64 and IA-32 Architectures Software Developer's Manual Vol.2C
-# (#326018-047US, June 2013)
-#
-#<Opcode maps>
-# Table: table-name
-# Referrer: escaped-name
-# AVXcode: avx-code
-# opcode: mnemonic|GrpXXX [operand1[,operand2...]] [(extra1)[,(extra2)...] [| 2nd-mnemonic ...]
-# (or)
-# opcode: escape # escaped-name
-# EndTable
-#
-# mnemonics that begin with lowercase 'v' accept a VEX or EVEX prefix
-# mnemonics that begin with lowercase 'k' accept a VEX prefix
-#
-#<group maps>
-# GrpTable: GrpXXX
-# reg: mnemonic [operand1[,operand2...]] [(extra1)[,(extra2)...] [| 2nd-mnemonic ...]
-# EndTable
-#
-# AVX Superscripts
-# (ev): this opcode requires EVEX prefix.
-# (evo): this opcode is changed by EVEX prefix (EVEX opcode)
-# (v): this opcode requires VEX prefix.
-# (v1): this opcode only supports 128bit VEX.
-#
-# Last Prefix Superscripts
-# - (66): the last prefix is 0x66
-# - (F3): the last prefix is 0xF3
-# - (F2): the last prefix is 0xF2
-# - (!F3) : the last prefix is not 0xF3 (including non-last prefix case)
-# - (66&F2): Both 0x66 and 0xF2 prefixes are specified.
-
-Table: one byte opcode
-Referrer:
-AVXcode:
-# 0x00 - 0x0f
-00: ADD Eb,Gb
-01: ADD Ev,Gv
-02: ADD Gb,Eb
-03: ADD Gv,Ev
-04: ADD AL,Ib
-05: ADD rAX,Iz
-06: PUSH ES (i64)
-07: POP ES (i64)
-08: OR Eb,Gb
-09: OR Ev,Gv
-0a: OR Gb,Eb
-0b: OR Gv,Ev
-0c: OR AL,Ib
-0d: OR rAX,Iz
-0e: PUSH CS (i64)
-0f: escape # 2-byte escape
-# 0x10 - 0x1f
-10: ADC Eb,Gb
-11: ADC Ev,Gv
-12: ADC Gb,Eb
-13: ADC Gv,Ev
-14: ADC AL,Ib
-15: ADC rAX,Iz
-16: PUSH SS (i64)
-17: POP SS (i64)
-18: SBB Eb,Gb
-19: SBB Ev,Gv
-1a: SBB Gb,Eb
-1b: SBB Gv,Ev
-1c: SBB AL,Ib
-1d: SBB rAX,Iz
-1e: PUSH DS (i64)
-1f: POP DS (i64)
-# 0x20 - 0x2f
-20: AND Eb,Gb
-21: AND Ev,Gv
-22: AND Gb,Eb
-23: AND Gv,Ev
-24: AND AL,Ib
-25: AND rAx,Iz
-26: SEG=ES (Prefix)
-27: DAA (i64)
-28: SUB Eb,Gb
-29: SUB Ev,Gv
-2a: SUB Gb,Eb
-2b: SUB Gv,Ev
-2c: SUB AL,Ib
-2d: SUB rAX,Iz
-2e: SEG=CS (Prefix)
-2f: DAS (i64)
-# 0x30 - 0x3f
-30: XOR Eb,Gb
-31: XOR Ev,Gv
-32: XOR Gb,Eb
-33: XOR Gv,Ev
-34: XOR AL,Ib
-35: XOR rAX,Iz
-36: SEG=SS (Prefix)
-37: AAA (i64)
-38: CMP Eb,Gb
-39: CMP Ev,Gv
-3a: CMP Gb,Eb
-3b: CMP Gv,Ev
-3c: CMP AL,Ib
-3d: CMP rAX,Iz
-3e: SEG=DS (Prefix)
-3f: AAS (i64)
-# 0x40 - 0x4f
-40: INC eAX (i64) | REX (o64)
-41: INC eCX (i64) | REX.B (o64)
-42: INC eDX (i64) | REX.X (o64)
-43: INC eBX (i64) | REX.XB (o64)
-44: INC eSP (i64) | REX.R (o64)
-45: INC eBP (i64) | REX.RB (o64)
-46: INC eSI (i64) | REX.RX (o64)
-47: INC eDI (i64) | REX.RXB (o64)
-48: DEC eAX (i64) | REX.W (o64)
-49: DEC eCX (i64) | REX.WB (o64)
-4a: DEC eDX (i64) | REX.WX (o64)
-4b: DEC eBX (i64) | REX.WXB (o64)
-4c: DEC eSP (i64) | REX.WR (o64)
-4d: DEC eBP (i64) | REX.WRB (o64)
-4e: DEC eSI (i64) | REX.WRX (o64)
-4f: DEC eDI (i64) | REX.WRXB (o64)
-# 0x50 - 0x5f
-50: PUSH rAX/r8 (d64)
-51: PUSH rCX/r9 (d64)
-52: PUSH rDX/r10 (d64)
-53: PUSH rBX/r11 (d64)
-54: PUSH rSP/r12 (d64)
-55: PUSH rBP/r13 (d64)
-56: PUSH rSI/r14 (d64)
-57: PUSH rDI/r15 (d64)
-58: POP rAX/r8 (d64)
-59: POP rCX/r9 (d64)
-5a: POP rDX/r10 (d64)
-5b: POP rBX/r11 (d64)
-5c: POP rSP/r12 (d64)
-5d: POP rBP/r13 (d64)
-5e: POP rSI/r14 (d64)
-5f: POP rDI/r15 (d64)
-# 0x60 - 0x6f
-60: PUSHA/PUSHAD (i64)
-61: POPA/POPAD (i64)
-62: BOUND Gv,Ma (i64) | EVEX (Prefix)
-63: ARPL Ew,Gw (i64) | MOVSXD Gv,Ev (o64)
-64: SEG=FS (Prefix)
-65: SEG=GS (Prefix)
-66: Operand-Size (Prefix)
-67: Address-Size (Prefix)
-68: PUSH Iz (d64)
-69: IMUL Gv,Ev,Iz
-6a: PUSH Ib (d64)
-6b: IMUL Gv,Ev,Ib
-6c: INS/INSB Yb,DX
-6d: INS/INSW/INSD Yz,DX
-6e: OUTS/OUTSB DX,Xb
-6f: OUTS/OUTSW/OUTSD DX,Xz
-# 0x70 - 0x7f
-70: JO Jb
-71: JNO Jb
-72: JB/JNAE/JC Jb
-73: JNB/JAE/JNC Jb
-74: JZ/JE Jb
-75: JNZ/JNE Jb
-76: JBE/JNA Jb
-77: JNBE/JA Jb
-78: JS Jb
-79: JNS Jb
-7a: JP/JPE Jb
-7b: JNP/JPO Jb
-7c: JL/JNGE Jb
-7d: JNL/JGE Jb
-7e: JLE/JNG Jb
-7f: JNLE/JG Jb
-# 0x80 - 0x8f
-80: Grp1 Eb,Ib (1A)
-81: Grp1 Ev,Iz (1A)
-82: Grp1 Eb,Ib (1A),(i64)
-83: Grp1 Ev,Ib (1A)
-84: TEST Eb,Gb
-85: TEST Ev,Gv
-86: XCHG Eb,Gb
-87: XCHG Ev,Gv
-88: MOV Eb,Gb
-89: MOV Ev,Gv
-8a: MOV Gb,Eb
-8b: MOV Gv,Ev
-8c: MOV Ev,Sw
-8d: LEA Gv,M
-8e: MOV Sw,Ew
-8f: Grp1A (1A) | POP Ev (d64)
-# 0x90 - 0x9f
-90: NOP | PAUSE (F3) | XCHG r8,rAX
-91: XCHG rCX/r9,rAX
-92: XCHG rDX/r10,rAX
-93: XCHG rBX/r11,rAX
-94: XCHG rSP/r12,rAX
-95: XCHG rBP/r13,rAX
-96: XCHG rSI/r14,rAX
-97: XCHG rDI/r15,rAX
-98: CBW/CWDE/CDQE
-99: CWD/CDQ/CQO
-9a: CALLF Ap (i64)
-9b: FWAIT/WAIT
-9c: PUSHF/D/Q Fv (d64)
-9d: POPF/D/Q Fv (d64)
-9e: SAHF
-9f: LAHF
-# 0xa0 - 0xaf
-a0: MOV AL,Ob
-a1: MOV rAX,Ov
-a2: MOV Ob,AL
-a3: MOV Ov,rAX
-a4: MOVS/B Yb,Xb
-a5: MOVS/W/D/Q Yv,Xv
-a6: CMPS/B Xb,Yb
-a7: CMPS/W/D Xv,Yv
-a8: TEST AL,Ib
-a9: TEST rAX,Iz
-aa: STOS/B Yb,AL
-ab: STOS/W/D/Q Yv,rAX
-ac: LODS/B AL,Xb
-ad: LODS/W/D/Q rAX,Xv
-ae: SCAS/B AL,Yb
-# Note: The May 2011 Intel manual shows Xv for the second parameter of the
-# next instruction but Yv is correct
-af: SCAS/W/D/Q rAX,Yv
-# 0xb0 - 0xbf
-b0: MOV AL/R8L,Ib
-b1: MOV CL/R9L,Ib
-b2: MOV DL/R10L,Ib
-b3: MOV BL/R11L,Ib
-b4: MOV AH/R12L,Ib
-b5: MOV CH/R13L,Ib
-b6: MOV DH/R14L,Ib
-b7: MOV BH/R15L,Ib
-b8: MOV rAX/r8,Iv
-b9: MOV rCX/r9,Iv
-ba: MOV rDX/r10,Iv
-bb: MOV rBX/r11,Iv
-bc: MOV rSP/r12,Iv
-bd: MOV rBP/r13,Iv
-be: MOV rSI/r14,Iv
-bf: MOV rDI/r15,Iv
-# 0xc0 - 0xcf
-c0: Grp2 Eb,Ib (1A)
-c1: Grp2 Ev,Ib (1A)
-c2: RETN Iw (f64)
-c3: RETN
-c4: LES Gz,Mp (i64) | VEX+2byte (Prefix)
-c5: LDS Gz,Mp (i64) | VEX+1byte (Prefix)
-c6: Grp11A Eb,Ib (1A)
-c7: Grp11B Ev,Iz (1A)
-c8: ENTER Iw,Ib
-c9: LEAVE (d64)
-ca: RETF Iw
-cb: RETF
-cc: INT3
-cd: INT Ib
-ce: INTO (i64)
-cf: IRET/D/Q
-# 0xd0 - 0xdf
-d0: Grp2 Eb,1 (1A)
-d1: Grp2 Ev,1 (1A)
-d2: Grp2 Eb,CL (1A)
-d3: Grp2 Ev,CL (1A)
-d4: AAM Ib (i64)
-d5: AAD Ib (i64)
-d6:
-d7: XLAT/XLATB
-d8: ESC
-d9: ESC
-da: ESC
-db: ESC
-dc: ESC
-dd: ESC
-de: ESC
-df: ESC
-# 0xe0 - 0xef
-# Note: "forced64" is Intel CPU behavior: they ignore 0x66 prefix
-# in 64-bit mode. AMD CPUs accept 0x66 prefix, it causes RIP truncation
-# to 16 bits. In 32-bit mode, 0x66 is accepted by both Intel and AMD.
-e0: LOOPNE/LOOPNZ Jb (f64)
-e1: LOOPE/LOOPZ Jb (f64)
-e2: LOOP Jb (f64)
-e3: JrCXZ Jb (f64)
-e4: IN AL,Ib
-e5: IN eAX,Ib
-e6: OUT Ib,AL
-e7: OUT Ib,eAX
-# With 0x66 prefix in 64-bit mode, for AMD CPUs immediate offset
-# in "near" jumps and calls is 16-bit. For CALL,
-# push of return address is 16-bit wide, RSP is decremented by 2
-# but is not truncated to 16 bits, unlike RIP.
-e8: CALL Jz (f64)
-e9: JMP-near Jz (f64)
-ea: JMP-far Ap (i64)
-eb: JMP-short Jb (f64)
-ec: IN AL,DX
-ed: IN eAX,DX
-ee: OUT DX,AL
-ef: OUT DX,eAX
-# 0xf0 - 0xff
-f0: LOCK (Prefix)
-f1:
-f2: REPNE (Prefix) | XACQUIRE (Prefix)
-f3: REP/REPE (Prefix) | XRELEASE (Prefix)
-f4: HLT
-f5: CMC
-f6: Grp3_1 Eb (1A)
-f7: Grp3_2 Ev (1A)
-f8: CLC
-f9: STC
-fa: CLI
-fb: STI
-fc: CLD
-fd: STD
-fe: Grp4 (1A)
-ff: Grp5 (1A)
-EndTable
-
-Table: 2-byte opcode (0x0f)
-Referrer: 2-byte escape
-AVXcode: 1
-# 0x0f 0x00-0x0f
-00: Grp6 (1A)
-01: Grp7 (1A)
-02: LAR Gv,Ew
-03: LSL Gv,Ew
-04:
-05: SYSCALL (o64)
-06: CLTS
-07: SYSRET (o64)
-08: INVD
-09: WBINVD
-0a:
-0b: UD2 (1B)
-0c:
-# AMD's prefetch group. Intel supports prefetchw(/1) only.
-0d: GrpP
-0e: FEMMS
-# 3DNow! uses the last imm byte as opcode extension.
-0f: 3DNow! Pq,Qq,Ib
-# 0x0f 0x10-0x1f
-# NOTE: According to Intel SDM opcode map, vmovups and vmovupd has no operands
-# but it actually has operands. And also, vmovss and vmovsd only accept 128bit.
-# MOVSS/MOVSD has too many forms(3) on SDM. This map just shows a typical form.
-# Many AVX instructions lack v1 superscript, according to Intel AVX-Prgramming
-# Reference A.1
-10: vmovups Vps,Wps | vmovupd Vpd,Wpd (66) | vmovss Vx,Hx,Wss (F3),(v1) | vmovsd Vx,Hx,Wsd (F2),(v1)
-11: vmovups Wps,Vps | vmovupd Wpd,Vpd (66) | vmovss Wss,Hx,Vss (F3),(v1) | vmovsd Wsd,Hx,Vsd (F2),(v1)
-12: vmovlps Vq,Hq,Mq (v1) | vmovhlps Vq,Hq,Uq (v1) | vmovlpd Vq,Hq,Mq (66),(v1) | vmovsldup Vx,Wx (F3) | vmovddup Vx,Wx (F2)
-13: vmovlps Mq,Vq (v1) | vmovlpd Mq,Vq (66),(v1)
-14: vunpcklps Vx,Hx,Wx | vunpcklpd Vx,Hx,Wx (66)
-15: vunpckhps Vx,Hx,Wx | vunpckhpd Vx,Hx,Wx (66)
-16: vmovhps Vdq,Hq,Mq (v1) | vmovlhps Vdq,Hq,Uq (v1) | vmovhpd Vdq,Hq,Mq (66),(v1) | vmovshdup Vx,Wx (F3)
-17: vmovhps Mq,Vq (v1) | vmovhpd Mq,Vq (66),(v1)
-18: Grp16 (1A)
-19:
-# Intel SDM opcode map does not list MPX instructions. For now using Gv for
-# bnd registers and Ev for everything else is OK because the instruction
-# decoder does not use the information except as an indication that there is
-# a ModR/M byte.
-1a: BNDCL Gv,Ev (F3) | BNDCU Gv,Ev (F2) | BNDMOV Gv,Ev (66) | BNDLDX Gv,Ev
-1b: BNDCN Gv,Ev (F2) | BNDMOV Ev,Gv (66) | BNDMK Gv,Ev (F3) | BNDSTX Ev,Gv
-1c:
-1d:
-1e:
-1f: NOP Ev
-# 0x0f 0x20-0x2f
-20: MOV Rd,Cd
-21: MOV Rd,Dd
-22: MOV Cd,Rd
-23: MOV Dd,Rd
-24:
-25:
-26:
-27:
-28: vmovaps Vps,Wps | vmovapd Vpd,Wpd (66)
-29: vmovaps Wps,Vps | vmovapd Wpd,Vpd (66)
-2a: cvtpi2ps Vps,Qpi | cvtpi2pd Vpd,Qpi (66) | vcvtsi2ss Vss,Hss,Ey (F3),(v1) | vcvtsi2sd Vsd,Hsd,Ey (F2),(v1)
-2b: vmovntps Mps,Vps | vmovntpd Mpd,Vpd (66)
-2c: cvttps2pi Ppi,Wps | cvttpd2pi Ppi,Wpd (66) | vcvttss2si Gy,Wss (F3),(v1) | vcvttsd2si Gy,Wsd (F2),(v1)
-2d: cvtps2pi Ppi,Wps | cvtpd2pi Qpi,Wpd (66) | vcvtss2si Gy,Wss (F3),(v1) | vcvtsd2si Gy,Wsd (F2),(v1)
-2e: vucomiss Vss,Wss (v1) | vucomisd Vsd,Wsd (66),(v1)
-2f: vcomiss Vss,Wss (v1) | vcomisd Vsd,Wsd (66),(v1)
-# 0x0f 0x30-0x3f
-30: WRMSR
-31: RDTSC
-32: RDMSR
-33: RDPMC
-34: SYSENTER
-35: SYSEXIT
-36:
-37: GETSEC
-38: escape # 3-byte escape 1
-39:
-3a: escape # 3-byte escape 2
-3b:
-3c:
-3d:
-3e:
-3f:
-# 0x0f 0x40-0x4f
-40: CMOVO Gv,Ev
-41: CMOVNO Gv,Ev | kandw/q Vk,Hk,Uk | kandb/d Vk,Hk,Uk (66)
-42: CMOVB/C/NAE Gv,Ev | kandnw/q Vk,Hk,Uk | kandnb/d Vk,Hk,Uk (66)
-43: CMOVAE/NB/NC Gv,Ev
-44: CMOVE/Z Gv,Ev | knotw/q Vk,Uk | knotb/d Vk,Uk (66)
-45: CMOVNE/NZ Gv,Ev | korw/q Vk,Hk,Uk | korb/d Vk,Hk,Uk (66)
-46: CMOVBE/NA Gv,Ev | kxnorw/q Vk,Hk,Uk | kxnorb/d Vk,Hk,Uk (66)
-47: CMOVA/NBE Gv,Ev | kxorw/q Vk,Hk,Uk | kxorb/d Vk,Hk,Uk (66)
-48: CMOVS Gv,Ev
-49: CMOVNS Gv,Ev
-4a: CMOVP/PE Gv,Ev | kaddw/q Vk,Hk,Uk | kaddb/d Vk,Hk,Uk (66)
-4b: CMOVNP/PO Gv,Ev | kunpckbw Vk,Hk,Uk (66) | kunpckwd/dq Vk,Hk,Uk
-4c: CMOVL/NGE Gv,Ev
-4d: CMOVNL/GE Gv,Ev
-4e: CMOVLE/NG Gv,Ev
-4f: CMOVNLE/G Gv,Ev
-# 0x0f 0x50-0x5f
-50: vmovmskps Gy,Ups | vmovmskpd Gy,Upd (66)
-51: vsqrtps Vps,Wps | vsqrtpd Vpd,Wpd (66) | vsqrtss Vss,Hss,Wss (F3),(v1) | vsqrtsd Vsd,Hsd,Wsd (F2),(v1)
-52: vrsqrtps Vps,Wps | vrsqrtss Vss,Hss,Wss (F3),(v1)
-53: vrcpps Vps,Wps | vrcpss Vss,Hss,Wss (F3),(v1)
-54: vandps Vps,Hps,Wps | vandpd Vpd,Hpd,Wpd (66)
-55: vandnps Vps,Hps,Wps | vandnpd Vpd,Hpd,Wpd (66)
-56: vorps Vps,Hps,Wps | vorpd Vpd,Hpd,Wpd (66)
-57: vxorps Vps,Hps,Wps | vxorpd Vpd,Hpd,Wpd (66)
-58: vaddps Vps,Hps,Wps | vaddpd Vpd,Hpd,Wpd (66) | vaddss Vss,Hss,Wss (F3),(v1) | vaddsd Vsd,Hsd,Wsd (F2),(v1)
-59: vmulps Vps,Hps,Wps | vmulpd Vpd,Hpd,Wpd (66) | vmulss Vss,Hss,Wss (F3),(v1) | vmulsd Vsd,Hsd,Wsd (F2),(v1)
-5a: vcvtps2pd Vpd,Wps | vcvtpd2ps Vps,Wpd (66) | vcvtss2sd Vsd,Hx,Wss (F3),(v1) | vcvtsd2ss Vss,Hx,Wsd (F2),(v1)
-5b: vcvtdq2ps Vps,Wdq | vcvtqq2ps Vps,Wqq (evo) | vcvtps2dq Vdq,Wps (66) | vcvttps2dq Vdq,Wps (F3)
-5c: vsubps Vps,Hps,Wps | vsubpd Vpd,Hpd,Wpd (66) | vsubss Vss,Hss,Wss (F3),(v1) | vsubsd Vsd,Hsd,Wsd (F2),(v1)
-5d: vminps Vps,Hps,Wps | vminpd Vpd,Hpd,Wpd (66) | vminss Vss,Hss,Wss (F3),(v1) | vminsd Vsd,Hsd,Wsd (F2),(v1)
-5e: vdivps Vps,Hps,Wps | vdivpd Vpd,Hpd,Wpd (66) | vdivss Vss,Hss,Wss (F3),(v1) | vdivsd Vsd,Hsd,Wsd (F2),(v1)
-5f: vmaxps Vps,Hps,Wps | vmaxpd Vpd,Hpd,Wpd (66) | vmaxss Vss,Hss,Wss (F3),(v1) | vmaxsd Vsd,Hsd,Wsd (F2),(v1)
-# 0x0f 0x60-0x6f
-60: punpcklbw Pq,Qd | vpunpcklbw Vx,Hx,Wx (66),(v1)
-61: punpcklwd Pq,Qd | vpunpcklwd Vx,Hx,Wx (66),(v1)
-62: punpckldq Pq,Qd | vpunpckldq Vx,Hx,Wx (66),(v1)
-63: packsswb Pq,Qq | vpacksswb Vx,Hx,Wx (66),(v1)
-64: pcmpgtb Pq,Qq | vpcmpgtb Vx,Hx,Wx (66),(v1)
-65: pcmpgtw Pq,Qq | vpcmpgtw Vx,Hx,Wx (66),(v1)
-66: pcmpgtd Pq,Qq | vpcmpgtd Vx,Hx,Wx (66),(v1)
-67: packuswb Pq,Qq | vpackuswb Vx,Hx,Wx (66),(v1)
-68: punpckhbw Pq,Qd | vpunpckhbw Vx,Hx,Wx (66),(v1)
-69: punpckhwd Pq,Qd | vpunpckhwd Vx,Hx,Wx (66),(v1)
-6a: punpckhdq Pq,Qd | vpunpckhdq Vx,Hx,Wx (66),(v1)
-6b: packssdw Pq,Qd | vpackssdw Vx,Hx,Wx (66),(v1)
-6c: vpunpcklqdq Vx,Hx,Wx (66),(v1)
-6d: vpunpckhqdq Vx,Hx,Wx (66),(v1)
-6e: movd/q Pd,Ey | vmovd/q Vy,Ey (66),(v1)
-6f: movq Pq,Qq | vmovdqa Vx,Wx (66) | vmovdqa32/64 Vx,Wx (66),(evo) | vmovdqu Vx,Wx (F3) | vmovdqu32/64 Vx,Wx (F3),(evo) | vmovdqu8/16 Vx,Wx (F2),(ev)
-# 0x0f 0x70-0x7f
-70: pshufw Pq,Qq,Ib | vpshufd Vx,Wx,Ib (66),(v1) | vpshufhw Vx,Wx,Ib (F3),(v1) | vpshuflw Vx,Wx,Ib (F2),(v1)
-71: Grp12 (1A)
-72: Grp13 (1A)
-73: Grp14 (1A)
-74: pcmpeqb Pq,Qq | vpcmpeqb Vx,Hx,Wx (66),(v1)
-75: pcmpeqw Pq,Qq | vpcmpeqw Vx,Hx,Wx (66),(v1)
-76: pcmpeqd Pq,Qq | vpcmpeqd Vx,Hx,Wx (66),(v1)
-# Note: Remove (v), because vzeroall and vzeroupper becomes emms without VEX.
-77: emms | vzeroupper | vzeroall
-78: VMREAD Ey,Gy | vcvttps2udq/pd2udq Vx,Wpd (evo) | vcvttsd2usi Gv,Wx (F2),(ev) | vcvttss2usi Gv,Wx (F3),(ev) | vcvttps2uqq/pd2uqq Vx,Wx (66),(ev)
-79: VMWRITE Gy,Ey | vcvtps2udq/pd2udq Vx,Wpd (evo) | vcvtsd2usi Gv,Wx (F2),(ev) | vcvtss2usi Gv,Wx (F3),(ev) | vcvtps2uqq/pd2uqq Vx,Wx (66),(ev)
-7a: vcvtudq2pd/uqq2pd Vpd,Wx (F3),(ev) | vcvtudq2ps/uqq2ps Vpd,Wx (F2),(ev) | vcvttps2qq/pd2qq Vx,Wx (66),(ev)
-7b: vcvtusi2sd Vpd,Hpd,Ev (F2),(ev) | vcvtusi2ss Vps,Hps,Ev (F3),(ev) | vcvtps2qq/pd2qq Vx,Wx (66),(ev)
-7c: vhaddpd Vpd,Hpd,Wpd (66) | vhaddps Vps,Hps,Wps (F2)
-7d: vhsubpd Vpd,Hpd,Wpd (66) | vhsubps Vps,Hps,Wps (F2)
-7e: movd/q Ey,Pd | vmovd/q Ey,Vy (66),(v1) | vmovq Vq,Wq (F3),(v1)
-7f: movq Qq,Pq | vmovdqa Wx,Vx (66) | vmovdqa32/64 Wx,Vx (66),(evo) | vmovdqu Wx,Vx (F3) | vmovdqu32/64 Wx,Vx (F3),(evo) | vmovdqu8/16 Wx,Vx (F2),(ev)
-# 0x0f 0x80-0x8f
-# Note: "forced64" is Intel CPU behavior (see comment about CALL insn).
-80: JO Jz (f64)
-81: JNO Jz (f64)
-82: JB/JC/JNAE Jz (f64)
-83: JAE/JNB/JNC Jz (f64)
-84: JE/JZ Jz (f64)
-85: JNE/JNZ Jz (f64)
-86: JBE/JNA Jz (f64)
-87: JA/JNBE Jz (f64)
-88: JS Jz (f64)
-89: JNS Jz (f64)
-8a: JP/JPE Jz (f64)
-8b: JNP/JPO Jz (f64)
-8c: JL/JNGE Jz (f64)
-8d: JNL/JGE Jz (f64)
-8e: JLE/JNG Jz (f64)
-8f: JNLE/JG Jz (f64)
-# 0x0f 0x90-0x9f
-90: SETO Eb | kmovw/q Vk,Wk | kmovb/d Vk,Wk (66)
-91: SETNO Eb | kmovw/q Mv,Vk | kmovb/d Mv,Vk (66)
-92: SETB/C/NAE Eb | kmovw Vk,Rv | kmovb Vk,Rv (66) | kmovq/d Vk,Rv (F2)
-93: SETAE/NB/NC Eb | kmovw Gv,Uk | kmovb Gv,Uk (66) | kmovq/d Gv,Uk (F2)
-94: SETE/Z Eb
-95: SETNE/NZ Eb
-96: SETBE/NA Eb
-97: SETA/NBE Eb
-98: SETS Eb | kortestw/q Vk,Uk | kortestb/d Vk,Uk (66)
-99: SETNS Eb | ktestw/q Vk,Uk | ktestb/d Vk,Uk (66)
-9a: SETP/PE Eb
-9b: SETNP/PO Eb
-9c: SETL/NGE Eb
-9d: SETNL/GE Eb
-9e: SETLE/NG Eb
-9f: SETNLE/G Eb
-# 0x0f 0xa0-0xaf
-a0: PUSH FS (d64)
-a1: POP FS (d64)
-a2: CPUID
-a3: BT Ev,Gv
-a4: SHLD Ev,Gv,Ib
-a5: SHLD Ev,Gv,CL
-a6: GrpPDLK
-a7: GrpRNG
-a8: PUSH GS (d64)
-a9: POP GS (d64)
-aa: RSM
-ab: BTS Ev,Gv
-ac: SHRD Ev,Gv,Ib
-ad: SHRD Ev,Gv,CL
-ae: Grp15 (1A),(1C)
-af: IMUL Gv,Ev
-# 0x0f 0xb0-0xbf
-b0: CMPXCHG Eb,Gb
-b1: CMPXCHG Ev,Gv
-b2: LSS Gv,Mp
-b3: BTR Ev,Gv
-b4: LFS Gv,Mp
-b5: LGS Gv,Mp
-b6: MOVZX Gv,Eb
-b7: MOVZX Gv,Ew
-b8: JMPE (!F3) | POPCNT Gv,Ev (F3)
-b9: Grp10 (1A)
-ba: Grp8 Ev,Ib (1A)
-bb: BTC Ev,Gv
-bc: BSF Gv,Ev (!F3) | TZCNT Gv,Ev (F3)
-bd: BSR Gv,Ev (!F3) | LZCNT Gv,Ev (F3)
-be: MOVSX Gv,Eb
-bf: MOVSX Gv,Ew
-# 0x0f 0xc0-0xcf
-c0: XADD Eb,Gb
-c1: XADD Ev,Gv
-c2: vcmpps Vps,Hps,Wps,Ib | vcmppd Vpd,Hpd,Wpd,Ib (66) | vcmpss Vss,Hss,Wss,Ib (F3),(v1) | vcmpsd Vsd,Hsd,Wsd,Ib (F2),(v1)
-c3: movnti My,Gy
-c4: pinsrw Pq,Ry/Mw,Ib | vpinsrw Vdq,Hdq,Ry/Mw,Ib (66),(v1)
-c5: pextrw Gd,Nq,Ib | vpextrw Gd,Udq,Ib (66),(v1)
-c6: vshufps Vps,Hps,Wps,Ib | vshufpd Vpd,Hpd,Wpd,Ib (66)
-c7: Grp9 (1A)
-c8: BSWAP RAX/EAX/R8/R8D
-c9: BSWAP RCX/ECX/R9/R9D
-ca: BSWAP RDX/EDX/R10/R10D
-cb: BSWAP RBX/EBX/R11/R11D
-cc: BSWAP RSP/ESP/R12/R12D
-cd: BSWAP RBP/EBP/R13/R13D
-ce: BSWAP RSI/ESI/R14/R14D
-cf: BSWAP RDI/EDI/R15/R15D
-# 0x0f 0xd0-0xdf
-d0: vaddsubpd Vpd,Hpd,Wpd (66) | vaddsubps Vps,Hps,Wps (F2)
-d1: psrlw Pq,Qq | vpsrlw Vx,Hx,Wx (66),(v1)
-d2: psrld Pq,Qq | vpsrld Vx,Hx,Wx (66),(v1)
-d3: psrlq Pq,Qq | vpsrlq Vx,Hx,Wx (66),(v1)
-d4: paddq Pq,Qq | vpaddq Vx,Hx,Wx (66),(v1)
-d5: pmullw Pq,Qq | vpmullw Vx,Hx,Wx (66),(v1)
-d6: vmovq Wq,Vq (66),(v1) | movq2dq Vdq,Nq (F3) | movdq2q Pq,Uq (F2)
-d7: pmovmskb Gd,Nq | vpmovmskb Gd,Ux (66),(v1)
-d8: psubusb Pq,Qq | vpsubusb Vx,Hx,Wx (66),(v1)
-d9: psubusw Pq,Qq | vpsubusw Vx,Hx,Wx (66),(v1)
-da: pminub Pq,Qq | vpminub Vx,Hx,Wx (66),(v1)
-db: pand Pq,Qq | vpand Vx,Hx,Wx (66),(v1) | vpandd/q Vx,Hx,Wx (66),(evo)
-dc: paddusb Pq,Qq | vpaddusb Vx,Hx,Wx (66),(v1)
-dd: paddusw Pq,Qq | vpaddusw Vx,Hx,Wx (66),(v1)
-de: pmaxub Pq,Qq | vpmaxub Vx,Hx,Wx (66),(v1)
-df: pandn Pq,Qq | vpandn Vx,Hx,Wx (66),(v1) | vpandnd/q Vx,Hx,Wx (66),(evo)
-# 0x0f 0xe0-0xef
-e0: pavgb Pq,Qq | vpavgb Vx,Hx,Wx (66),(v1)
-e1: psraw Pq,Qq | vpsraw Vx,Hx,Wx (66),(v1)
-e2: psrad Pq,Qq | vpsrad Vx,Hx,Wx (66),(v1)
-e3: pavgw Pq,Qq | vpavgw Vx,Hx,Wx (66),(v1)
-e4: pmulhuw Pq,Qq | vpmulhuw Vx,Hx,Wx (66),(v1)
-e5: pmulhw Pq,Qq | vpmulhw Vx,Hx,Wx (66),(v1)
-e6: vcvttpd2dq Vx,Wpd (66) | vcvtdq2pd Vx,Wdq (F3) | vcvtdq2pd/qq2pd Vx,Wdq (F3),(evo) | vcvtpd2dq Vx,Wpd (F2)
-e7: movntq Mq,Pq | vmovntdq Mx,Vx (66)
-e8: psubsb Pq,Qq | vpsubsb Vx,Hx,Wx (66),(v1)
-e9: psubsw Pq,Qq | vpsubsw Vx,Hx,Wx (66),(v1)
-ea: pminsw Pq,Qq | vpminsw Vx,Hx,Wx (66),(v1)
-eb: por Pq,Qq | vpor Vx,Hx,Wx (66),(v1) | vpord/q Vx,Hx,Wx (66),(evo)
-ec: paddsb Pq,Qq | vpaddsb Vx,Hx,Wx (66),(v1)
-ed: paddsw Pq,Qq | vpaddsw Vx,Hx,Wx (66),(v1)
-ee: pmaxsw Pq,Qq | vpmaxsw Vx,Hx,Wx (66),(v1)
-ef: pxor Pq,Qq | vpxor Vx,Hx,Wx (66),(v1) | vpxord/q Vx,Hx,Wx (66),(evo)
-# 0x0f 0xf0-0xff
-f0: vlddqu Vx,Mx (F2)
-f1: psllw Pq,Qq | vpsllw Vx,Hx,Wx (66),(v1)
-f2: pslld Pq,Qq | vpslld Vx,Hx,Wx (66),(v1)
-f3: psllq Pq,Qq | vpsllq Vx,Hx,Wx (66),(v1)
-f4: pmuludq Pq,Qq | vpmuludq Vx,Hx,Wx (66),(v1)
-f5: pmaddwd Pq,Qq | vpmaddwd Vx,Hx,Wx (66),(v1)
-f6: psadbw Pq,Qq | vpsadbw Vx,Hx,Wx (66),(v1)
-f7: maskmovq Pq,Nq | vmaskmovdqu Vx,Ux (66),(v1)
-f8: psubb Pq,Qq | vpsubb Vx,Hx,Wx (66),(v1)
-f9: psubw Pq,Qq | vpsubw Vx,Hx,Wx (66),(v1)
-fa: psubd Pq,Qq | vpsubd Vx,Hx,Wx (66),(v1)
-fb: psubq Pq,Qq | vpsubq Vx,Hx,Wx (66),(v1)
-fc: paddb Pq,Qq | vpaddb Vx,Hx,Wx (66),(v1)
-fd: paddw Pq,Qq | vpaddw Vx,Hx,Wx (66),(v1)
-fe: paddd Pq,Qq | vpaddd Vx,Hx,Wx (66),(v1)
-ff: UD0
-EndTable
-
-Table: 3-byte opcode 1 (0x0f 0x38)
-Referrer: 3-byte escape 1
-AVXcode: 2
-# 0x0f 0x38 0x00-0x0f
-00: pshufb Pq,Qq | vpshufb Vx,Hx,Wx (66),(v1)
-01: phaddw Pq,Qq | vphaddw Vx,Hx,Wx (66),(v1)
-02: phaddd Pq,Qq | vphaddd Vx,Hx,Wx (66),(v1)
-03: phaddsw Pq,Qq | vphaddsw Vx,Hx,Wx (66),(v1)
-04: pmaddubsw Pq,Qq | vpmaddubsw Vx,Hx,Wx (66),(v1)
-05: phsubw Pq,Qq | vphsubw Vx,Hx,Wx (66),(v1)
-06: phsubd Pq,Qq | vphsubd Vx,Hx,Wx (66),(v1)
-07: phsubsw Pq,Qq | vphsubsw Vx,Hx,Wx (66),(v1)
-08: psignb Pq,Qq | vpsignb Vx,Hx,Wx (66),(v1)
-09: psignw Pq,Qq | vpsignw Vx,Hx,Wx (66),(v1)
-0a: psignd Pq,Qq | vpsignd Vx,Hx,Wx (66),(v1)
-0b: pmulhrsw Pq,Qq | vpmulhrsw Vx,Hx,Wx (66),(v1)
-0c: vpermilps Vx,Hx,Wx (66),(v)
-0d: vpermilpd Vx,Hx,Wx (66),(v)
-0e: vtestps Vx,Wx (66),(v)
-0f: vtestpd Vx,Wx (66),(v)
-# 0x0f 0x38 0x10-0x1f
-10: pblendvb Vdq,Wdq (66) | vpsrlvw Vx,Hx,Wx (66),(evo) | vpmovuswb Wx,Vx (F3),(ev)
-11: vpmovusdb Wx,Vd (F3),(ev) | vpsravw Vx,Hx,Wx (66),(ev)
-12: vpmovusqb Wx,Vq (F3),(ev) | vpsllvw Vx,Hx,Wx (66),(ev)
-13: vcvtph2ps Vx,Wx (66),(v) | vpmovusdw Wx,Vd (F3),(ev)
-14: blendvps Vdq,Wdq (66) | vpmovusqw Wx,Vq (F3),(ev) | vprorvd/q Vx,Hx,Wx (66),(evo)
-15: blendvpd Vdq,Wdq (66) | vpmovusqd Wx,Vq (F3),(ev) | vprolvd/q Vx,Hx,Wx (66),(evo)
-16: vpermps Vqq,Hqq,Wqq (66),(v) | vpermps/d Vqq,Hqq,Wqq (66),(evo)
-17: vptest Vx,Wx (66)
-18: vbroadcastss Vx,Wd (66),(v)
-19: vbroadcastsd Vqq,Wq (66),(v) | vbroadcastf32x2 Vqq,Wq (66),(evo)
-1a: vbroadcastf128 Vqq,Mdq (66),(v) | vbroadcastf32x4/64x2 Vqq,Wq (66),(evo)
-1b: vbroadcastf32x8/64x4 Vqq,Mdq (66),(ev)
-1c: pabsb Pq,Qq | vpabsb Vx,Wx (66),(v1)
-1d: pabsw Pq,Qq | vpabsw Vx,Wx (66),(v1)
-1e: pabsd Pq,Qq | vpabsd Vx,Wx (66),(v1)
-1f: vpabsq Vx,Wx (66),(ev)
-# 0x0f 0x38 0x20-0x2f
-20: vpmovsxbw Vx,Ux/Mq (66),(v1) | vpmovswb Wx,Vx (F3),(ev)
-21: vpmovsxbd Vx,Ux/Md (66),(v1) | vpmovsdb Wx,Vd (F3),(ev)
-22: vpmovsxbq Vx,Ux/Mw (66),(v1) | vpmovsqb Wx,Vq (F3),(ev)
-23: vpmovsxwd Vx,Ux/Mq (66),(v1) | vpmovsdw Wx,Vd (F3),(ev)
-24: vpmovsxwq Vx,Ux/Md (66),(v1) | vpmovsqw Wx,Vq (F3),(ev)
-25: vpmovsxdq Vx,Ux/Mq (66),(v1) | vpmovsqd Wx,Vq (F3),(ev)
-26: vptestmb/w Vk,Hx,Wx (66),(ev) | vptestnmb/w Vk,Hx,Wx (F3),(ev)
-27: vptestmd/q Vk,Hx,Wx (66),(ev) | vptestnmd/q Vk,Hx,Wx (F3),(ev)
-28: vpmuldq Vx,Hx,Wx (66),(v1) | vpmovm2b/w Vx,Uk (F3),(ev)
-29: vpcmpeqq Vx,Hx,Wx (66),(v1) | vpmovb2m/w2m Vk,Ux (F3),(ev)
-2a: vmovntdqa Vx,Mx (66),(v1) | vpbroadcastmb2q Vx,Uk (F3),(ev)
-2b: vpackusdw Vx,Hx,Wx (66),(v1)
-2c: vmaskmovps Vx,Hx,Mx (66),(v) | vscalefps/d Vx,Hx,Wx (66),(evo)
-2d: vmaskmovpd Vx,Hx,Mx (66),(v) | vscalefss/d Vx,Hx,Wx (66),(evo)
-2e: vmaskmovps Mx,Hx,Vx (66),(v)
-2f: vmaskmovpd Mx,Hx,Vx (66),(v)
-# 0x0f 0x38 0x30-0x3f
-30: vpmovzxbw Vx,Ux/Mq (66),(v1) | vpmovwb Wx,Vx (F3),(ev)
-31: vpmovzxbd Vx,Ux/Md (66),(v1) | vpmovdb Wx,Vd (F3),(ev)
-32: vpmovzxbq Vx,Ux/Mw (66),(v1) | vpmovqb Wx,Vq (F3),(ev)
-33: vpmovzxwd Vx,Ux/Mq (66),(v1) | vpmovdw Wx,Vd (F3),(ev)
-34: vpmovzxwq Vx,Ux/Md (66),(v1) | vpmovqw Wx,Vq (F3),(ev)
-35: vpmovzxdq Vx,Ux/Mq (66),(v1) | vpmovqd Wx,Vq (F3),(ev)
-36: vpermd Vqq,Hqq,Wqq (66),(v) | vpermd/q Vqq,Hqq,Wqq (66),(evo)
-37: vpcmpgtq Vx,Hx,Wx (66),(v1)
-38: vpminsb Vx,Hx,Wx (66),(v1) | vpmovm2d/q Vx,Uk (F3),(ev)
-39: vpminsd Vx,Hx,Wx (66),(v1) | vpminsd/q Vx,Hx,Wx (66),(evo) | vpmovd2m/q2m Vk,Ux (F3),(ev)
-3a: vpminuw Vx,Hx,Wx (66),(v1) | vpbroadcastmw2d Vx,Uk (F3),(ev)
-3b: vpminud Vx,Hx,Wx (66),(v1) | vpminud/q Vx,Hx,Wx (66),(evo)
-3c: vpmaxsb Vx,Hx,Wx (66),(v1)
-3d: vpmaxsd Vx,Hx,Wx (66),(v1) | vpmaxsd/q Vx,Hx,Wx (66),(evo)
-3e: vpmaxuw Vx,Hx,Wx (66),(v1)
-3f: vpmaxud Vx,Hx,Wx (66),(v1) | vpmaxud/q Vx,Hx,Wx (66),(evo)
-# 0x0f 0x38 0x40-0x8f
-40: vpmulld Vx,Hx,Wx (66),(v1) | vpmulld/q Vx,Hx,Wx (66),(evo)
-41: vphminposuw Vdq,Wdq (66),(v1)
-42: vgetexpps/d Vx,Wx (66),(ev)
-43: vgetexpss/d Vx,Hx,Wx (66),(ev)
-44: vplzcntd/q Vx,Wx (66),(ev)
-45: vpsrlvd/q Vx,Hx,Wx (66),(v)
-46: vpsravd Vx,Hx,Wx (66),(v) | vpsravd/q Vx,Hx,Wx (66),(evo)
-47: vpsllvd/q Vx,Hx,Wx (66),(v)
-# Skip 0x48-0x4b
-4c: vrcp14ps/d Vpd,Wpd (66),(ev)
-4d: vrcp14ss/d Vsd,Hpd,Wsd (66),(ev)
-4e: vrsqrt14ps/d Vpd,Wpd (66),(ev)
-4f: vrsqrt14ss/d Vsd,Hsd,Wsd (66),(ev)
-# Skip 0x50-0x57
-58: vpbroadcastd Vx,Wx (66),(v)
-59: vpbroadcastq Vx,Wx (66),(v) | vbroadcasti32x2 Vx,Wx (66),(evo)
-5a: vbroadcasti128 Vqq,Mdq (66),(v) | vbroadcasti32x4/64x2 Vx,Wx (66),(evo)
-5b: vbroadcasti32x8/64x4 Vqq,Mdq (66),(ev)
-# Skip 0x5c-0x63
-64: vpblendmd/q Vx,Hx,Wx (66),(ev)
-65: vblendmps/d Vx,Hx,Wx (66),(ev)
-66: vpblendmb/w Vx,Hx,Wx (66),(ev)
-# Skip 0x67-0x74
-75: vpermi2b/w Vx,Hx,Wx (66),(ev)
-76: vpermi2d/q Vx,Hx,Wx (66),(ev)
-77: vpermi2ps/d Vx,Hx,Wx (66),(ev)
-78: vpbroadcastb Vx,Wx (66),(v)
-79: vpbroadcastw Vx,Wx (66),(v)
-7a: vpbroadcastb Vx,Rv (66),(ev)
-7b: vpbroadcastw Vx,Rv (66),(ev)
-7c: vpbroadcastd/q Vx,Rv (66),(ev)
-7d: vpermt2b/w Vx,Hx,Wx (66),(ev)
-7e: vpermt2d/q Vx,Hx,Wx (66),(ev)
-7f: vpermt2ps/d Vx,Hx,Wx (66),(ev)
-80: INVEPT Gy,Mdq (66)
-81: INVVPID Gy,Mdq (66)
-82: INVPCID Gy,Mdq (66)
-83: vpmultishiftqb Vx,Hx,Wx (66),(ev)
-88: vexpandps/d Vpd,Wpd (66),(ev)
-89: vpexpandd/q Vx,Wx (66),(ev)
-8a: vcompressps/d Wx,Vx (66),(ev)
-8b: vpcompressd/q Wx,Vx (66),(ev)
-8c: vpmaskmovd/q Vx,Hx,Mx (66),(v)
-8d: vpermb/w Vx,Hx,Wx (66),(ev)
-8e: vpmaskmovd/q Mx,Vx,Hx (66),(v)
-# 0x0f 0x38 0x90-0xbf (FMA)
-90: vgatherdd/q Vx,Hx,Wx (66),(v) | vpgatherdd/q Vx,Wx (66),(evo)
-91: vgatherqd/q Vx,Hx,Wx (66),(v) | vpgatherqd/q Vx,Wx (66),(evo)
-92: vgatherdps/d Vx,Hx,Wx (66),(v)
-93: vgatherqps/d Vx,Hx,Wx (66),(v)
-94:
-95:
-96: vfmaddsub132ps/d Vx,Hx,Wx (66),(v)
-97: vfmsubadd132ps/d Vx,Hx,Wx (66),(v)
-98: vfmadd132ps/d Vx,Hx,Wx (66),(v)
-99: vfmadd132ss/d Vx,Hx,Wx (66),(v),(v1)
-9a: vfmsub132ps/d Vx,Hx,Wx (66),(v)
-9b: vfmsub132ss/d Vx,Hx,Wx (66),(v),(v1)
-9c: vfnmadd132ps/d Vx,Hx,Wx (66),(v)
-9d: vfnmadd132ss/d Vx,Hx,Wx (66),(v),(v1)
-9e: vfnmsub132ps/d Vx,Hx,Wx (66),(v)
-9f: vfnmsub132ss/d Vx,Hx,Wx (66),(v),(v1)
-a0: vpscatterdd/q Wx,Vx (66),(ev)
-a1: vpscatterqd/q Wx,Vx (66),(ev)
-a2: vscatterdps/d Wx,Vx (66),(ev)
-a3: vscatterqps/d Wx,Vx (66),(ev)
-a6: vfmaddsub213ps/d Vx,Hx,Wx (66),(v)
-a7: vfmsubadd213ps/d Vx,Hx,Wx (66),(v)
-a8: vfmadd213ps/d Vx,Hx,Wx (66),(v)
-a9: vfmadd213ss/d Vx,Hx,Wx (66),(v),(v1)
-aa: vfmsub213ps/d Vx,Hx,Wx (66),(v)
-ab: vfmsub213ss/d Vx,Hx,Wx (66),(v),(v1)
-ac: vfnmadd213ps/d Vx,Hx,Wx (66),(v)
-ad: vfnmadd213ss/d Vx,Hx,Wx (66),(v),(v1)
-ae: vfnmsub213ps/d Vx,Hx,Wx (66),(v)
-af: vfnmsub213ss/d Vx,Hx,Wx (66),(v),(v1)
-b4: vpmadd52luq Vx,Hx,Wx (66),(ev)
-b5: vpmadd52huq Vx,Hx,Wx (66),(ev)
-b6: vfmaddsub231ps/d Vx,Hx,Wx (66),(v)
-b7: vfmsubadd231ps/d Vx,Hx,Wx (66),(v)
-b8: vfmadd231ps/d Vx,Hx,Wx (66),(v)
-b9: vfmadd231ss/d Vx,Hx,Wx (66),(v),(v1)
-ba: vfmsub231ps/d Vx,Hx,Wx (66),(v)
-bb: vfmsub231ss/d Vx,Hx,Wx (66),(v),(v1)
-bc: vfnmadd231ps/d Vx,Hx,Wx (66),(v)
-bd: vfnmadd231ss/d Vx,Hx,Wx (66),(v),(v1)
-be: vfnmsub231ps/d Vx,Hx,Wx (66),(v)
-bf: vfnmsub231ss/d Vx,Hx,Wx (66),(v),(v1)
-# 0x0f 0x38 0xc0-0xff
-c4: vpconflictd/q Vx,Wx (66),(ev)
-c6: Grp18 (1A)
-c7: Grp19 (1A)
-c8: sha1nexte Vdq,Wdq | vexp2ps/d Vx,Wx (66),(ev)
-c9: sha1msg1 Vdq,Wdq
-ca: sha1msg2 Vdq,Wdq | vrcp28ps/d Vx,Wx (66),(ev)
-cb: sha256rnds2 Vdq,Wdq | vrcp28ss/d Vx,Hx,Wx (66),(ev)
-cc: sha256msg1 Vdq,Wdq | vrsqrt28ps/d Vx,Wx (66),(ev)
-cd: sha256msg2 Vdq,Wdq | vrsqrt28ss/d Vx,Hx,Wx (66),(ev)
-db: VAESIMC Vdq,Wdq (66),(v1)
-dc: VAESENC Vdq,Hdq,Wdq (66),(v1)
-dd: VAESENCLAST Vdq,Hdq,Wdq (66),(v1)
-de: VAESDEC Vdq,Hdq,Wdq (66),(v1)
-df: VAESDECLAST Vdq,Hdq,Wdq (66),(v1)
-f0: MOVBE Gy,My | MOVBE Gw,Mw (66) | CRC32 Gd,Eb (F2) | CRC32 Gd,Eb (66&F2)
-f1: MOVBE My,Gy | MOVBE Mw,Gw (66) | CRC32 Gd,Ey (F2) | CRC32 Gd,Ew (66&F2)
-f2: ANDN Gy,By,Ey (v)
-f3: Grp17 (1A)
-f5: BZHI Gy,Ey,By (v) | PEXT Gy,By,Ey (F3),(v) | PDEP Gy,By,Ey (F2),(v)
-f6: ADCX Gy,Ey (66) | ADOX Gy,Ey (F3) | MULX By,Gy,rDX,Ey (F2),(v)
-f7: BEXTR Gy,Ey,By (v) | SHLX Gy,Ey,By (66),(v) | SARX Gy,Ey,By (F3),(v) | SHRX Gy,Ey,By (F2),(v)
-EndTable
-
-Table: 3-byte opcode 2 (0x0f 0x3a)
-Referrer: 3-byte escape 2
-AVXcode: 3
-# 0x0f 0x3a 0x00-0xff
-00: vpermq Vqq,Wqq,Ib (66),(v)
-01: vpermpd Vqq,Wqq,Ib (66),(v)
-02: vpblendd Vx,Hx,Wx,Ib (66),(v)
-03: valignd/q Vx,Hx,Wx,Ib (66),(ev)
-04: vpermilps Vx,Wx,Ib (66),(v)
-05: vpermilpd Vx,Wx,Ib (66),(v)
-06: vperm2f128 Vqq,Hqq,Wqq,Ib (66),(v)
-07:
-08: vroundps Vx,Wx,Ib (66) | vrndscaleps Vx,Wx,Ib (66),(evo)
-09: vroundpd Vx,Wx,Ib (66) | vrndscalepd Vx,Wx,Ib (66),(evo)
-0a: vroundss Vss,Wss,Ib (66),(v1) | vrndscaless Vx,Hx,Wx,Ib (66),(evo)
-0b: vroundsd Vsd,Wsd,Ib (66),(v1) | vrndscalesd Vx,Hx,Wx,Ib (66),(evo)
-0c: vblendps Vx,Hx,Wx,Ib (66)
-0d: vblendpd Vx,Hx,Wx,Ib (66)
-0e: vpblendw Vx,Hx,Wx,Ib (66),(v1)
-0f: palignr Pq,Qq,Ib | vpalignr Vx,Hx,Wx,Ib (66),(v1)
-14: vpextrb Rd/Mb,Vdq,Ib (66),(v1)
-15: vpextrw Rd/Mw,Vdq,Ib (66),(v1)
-16: vpextrd/q Ey,Vdq,Ib (66),(v1)
-17: vextractps Ed,Vdq,Ib (66),(v1)
-18: vinsertf128 Vqq,Hqq,Wqq,Ib (66),(v) | vinsertf32x4/64x2 Vqq,Hqq,Wqq,Ib (66),(evo)
-19: vextractf128 Wdq,Vqq,Ib (66),(v) | vextractf32x4/64x2 Wdq,Vqq,Ib (66),(evo)
-1a: vinsertf32x8/64x4 Vqq,Hqq,Wqq,Ib (66),(ev)
-1b: vextractf32x8/64x4 Wdq,Vqq,Ib (66),(ev)
-1d: vcvtps2ph Wx,Vx,Ib (66),(v)
-1e: vpcmpud/q Vk,Hd,Wd,Ib (66),(ev)
-1f: vpcmpd/q Vk,Hd,Wd,Ib (66),(ev)
-20: vpinsrb Vdq,Hdq,Ry/Mb,Ib (66),(v1)
-21: vinsertps Vdq,Hdq,Udq/Md,Ib (66),(v1)
-22: vpinsrd/q Vdq,Hdq,Ey,Ib (66),(v1)
-23: vshuff32x4/64x2 Vx,Hx,Wx,Ib (66),(ev)
-25: vpternlogd/q Vx,Hx,Wx,Ib (66),(ev)
-26: vgetmantps/d Vx,Wx,Ib (66),(ev)
-27: vgetmantss/d Vx,Hx,Wx,Ib (66),(ev)
-30: kshiftrb/w Vk,Uk,Ib (66),(v)
-31: kshiftrd/q Vk,Uk,Ib (66),(v)
-32: kshiftlb/w Vk,Uk,Ib (66),(v)
-33: kshiftld/q Vk,Uk,Ib (66),(v)
-38: vinserti128 Vqq,Hqq,Wqq,Ib (66),(v) | vinserti32x4/64x2 Vqq,Hqq,Wqq,Ib (66),(evo)
-39: vextracti128 Wdq,Vqq,Ib (66),(v) | vextracti32x4/64x2 Wdq,Vqq,Ib (66),(evo)
-3a: vinserti32x8/64x4 Vqq,Hqq,Wqq,Ib (66),(ev)
-3b: vextracti32x8/64x4 Wdq,Vqq,Ib (66),(ev)
-3e: vpcmpub/w Vk,Hk,Wx,Ib (66),(ev)
-3f: vpcmpb/w Vk,Hk,Wx,Ib (66),(ev)
-40: vdpps Vx,Hx,Wx,Ib (66)
-41: vdppd Vdq,Hdq,Wdq,Ib (66),(v1)
-42: vmpsadbw Vx,Hx,Wx,Ib (66),(v1) | vdbpsadbw Vx,Hx,Wx,Ib (66),(evo)
-43: vshufi32x4/64x2 Vx,Hx,Wx,Ib (66),(ev)
-44: vpclmulqdq Vdq,Hdq,Wdq,Ib (66),(v1)
-46: vperm2i128 Vqq,Hqq,Wqq,Ib (66),(v)
-4a: vblendvps Vx,Hx,Wx,Lx (66),(v)
-4b: vblendvpd Vx,Hx,Wx,Lx (66),(v)
-4c: vpblendvb Vx,Hx,Wx,Lx (66),(v1)
-50: vrangeps/d Vx,Hx,Wx,Ib (66),(ev)
-51: vrangess/d Vx,Hx,Wx,Ib (66),(ev)
-54: vfixupimmps/d Vx,Hx,Wx,Ib (66),(ev)
-55: vfixupimmss/d Vx,Hx,Wx,Ib (66),(ev)
-56: vreduceps/d Vx,Wx,Ib (66),(ev)
-57: vreducess/d Vx,Hx,Wx,Ib (66),(ev)
-60: vpcmpestrm Vdq,Wdq,Ib (66),(v1)
-61: vpcmpestri Vdq,Wdq,Ib (66),(v1)
-62: vpcmpistrm Vdq,Wdq,Ib (66),(v1)
-63: vpcmpistri Vdq,Wdq,Ib (66),(v1)
-66: vfpclassps/d Vk,Wx,Ib (66),(ev)
-67: vfpclassss/d Vk,Wx,Ib (66),(ev)
-cc: sha1rnds4 Vdq,Wdq,Ib
-df: VAESKEYGEN Vdq,Wdq,Ib (66),(v1)
-f0: RORX Gy,Ey,Ib (F2),(v)
-EndTable
-
-GrpTable: Grp1
-0: ADD
-1: OR
-2: ADC
-3: SBB
-4: AND
-5: SUB
-6: XOR
-7: CMP
-EndTable
-
-GrpTable: Grp1A
-0: POP
-EndTable
-
-GrpTable: Grp2
-0: ROL
-1: ROR
-2: RCL
-3: RCR
-4: SHL/SAL
-5: SHR
-6:
-7: SAR
-EndTable
-
-GrpTable: Grp3_1
-0: TEST Eb,Ib
-1: TEST Eb,Ib
-2: NOT Eb
-3: NEG Eb
-4: MUL AL,Eb
-5: IMUL AL,Eb
-6: DIV AL,Eb
-7: IDIV AL,Eb
-EndTable
-
-GrpTable: Grp3_2
-0: TEST Ev,Iz
-1:
-2: NOT Ev
-3: NEG Ev
-4: MUL rAX,Ev
-5: IMUL rAX,Ev
-6: DIV rAX,Ev
-7: IDIV rAX,Ev
-EndTable
-
-GrpTable: Grp4
-0: INC Eb
-1: DEC Eb
-EndTable
-
-GrpTable: Grp5
-0: INC Ev
-1: DEC Ev
-# Note: "forced64" is Intel CPU behavior (see comment about CALL insn).
-2: CALLN Ev (f64)
-3: CALLF Ep
-4: JMPN Ev (f64)
-5: JMPF Mp
-6: PUSH Ev (d64)
-7:
-EndTable
-
-GrpTable: Grp6
-0: SLDT Rv/Mw
-1: STR Rv/Mw
-2: LLDT Ew
-3: LTR Ew
-4: VERR Ew
-5: VERW Ew
-EndTable
-
-GrpTable: Grp7
-0: SGDT Ms | VMCALL (001),(11B) | VMLAUNCH (010),(11B) | VMRESUME (011),(11B) | VMXOFF (100),(11B)
-1: SIDT Ms | MONITOR (000),(11B) | MWAIT (001),(11B) | CLAC (010),(11B) | STAC (011),(11B)
-2: LGDT Ms | XGETBV (000),(11B) | XSETBV (001),(11B) | VMFUNC (100),(11B) | XEND (101)(11B) | XTEST (110)(11B)
-3: LIDT Ms
-4: SMSW Mw/Rv
-5: rdpkru (110),(11B) | wrpkru (111),(11B)
-6: LMSW Ew
-7: INVLPG Mb | SWAPGS (o64),(000),(11B) | RDTSCP (001),(11B)
-EndTable
-
-GrpTable: Grp8
-4: BT
-5: BTS
-6: BTR
-7: BTC
-EndTable
-
-GrpTable: Grp9
-1: CMPXCHG8B/16B Mq/Mdq
-3: xrstors
-4: xsavec
-5: xsaves
-6: VMPTRLD Mq | VMCLEAR Mq (66) | VMXON Mq (F3) | RDRAND Rv (11B)
-7: VMPTRST Mq | VMPTRST Mq (F3) | RDSEED Rv (11B)
-EndTable
-
-GrpTable: Grp10
-# all are UD1
-0: UD1
-1: UD1
-2: UD1
-3: UD1
-4: UD1
-5: UD1
-6: UD1
-7: UD1
-EndTable
-
-# Grp11A and Grp11B are expressed as Grp11 in Intel SDM
-GrpTable: Grp11A
-0: MOV Eb,Ib
-7: XABORT Ib (000),(11B)
-EndTable
-
-GrpTable: Grp11B
-0: MOV Eb,Iz
-7: XBEGIN Jz (000),(11B)
-EndTable
-
-GrpTable: Grp12
-2: psrlw Nq,Ib (11B) | vpsrlw Hx,Ux,Ib (66),(11B),(v1)
-4: psraw Nq,Ib (11B) | vpsraw Hx,Ux,Ib (66),(11B),(v1)
-6: psllw Nq,Ib (11B) | vpsllw Hx,Ux,Ib (66),(11B),(v1)
-EndTable
-
-GrpTable: Grp13
-0: vprord/q Hx,Wx,Ib (66),(ev)
-1: vprold/q Hx,Wx,Ib (66),(ev)
-2: psrld Nq,Ib (11B) | vpsrld Hx,Ux,Ib (66),(11B),(v1)
-4: psrad Nq,Ib (11B) | vpsrad Hx,Ux,Ib (66),(11B),(v1) | vpsrad/q Hx,Ux,Ib (66),(evo)
-6: pslld Nq,Ib (11B) | vpslld Hx,Ux,Ib (66),(11B),(v1)
-EndTable
-
-GrpTable: Grp14
-2: psrlq Nq,Ib (11B) | vpsrlq Hx,Ux,Ib (66),(11B),(v1)
-3: vpsrldq Hx,Ux,Ib (66),(11B),(v1)
-6: psllq Nq,Ib (11B) | vpsllq Hx,Ux,Ib (66),(11B),(v1)
-7: vpslldq Hx,Ux,Ib (66),(11B),(v1)
-EndTable
-
-GrpTable: Grp15
-0: fxsave | RDFSBASE Ry (F3),(11B)
-1: fxstor | RDGSBASE Ry (F3),(11B)
-2: vldmxcsr Md (v1) | WRFSBASE Ry (F3),(11B)
-3: vstmxcsr Md (v1) | WRGSBASE Ry (F3),(11B)
-4: XSAVE | ptwrite Ey (F3),(11B)
-5: XRSTOR | lfence (11B)
-6: XSAVEOPT | clwb (66) | mfence (11B)
-7: clflush | clflushopt (66) | sfence (11B)
-EndTable
-
-GrpTable: Grp16
-0: prefetch NTA
-1: prefetch T0
-2: prefetch T1
-3: prefetch T2
-EndTable
-
-GrpTable: Grp17
-1: BLSR By,Ey (v)
-2: BLSMSK By,Ey (v)
-3: BLSI By,Ey (v)
-EndTable
-
-GrpTable: Grp18
-1: vgatherpf0dps/d Wx (66),(ev)
-2: vgatherpf1dps/d Wx (66),(ev)
-5: vscatterpf0dps/d Wx (66),(ev)
-6: vscatterpf1dps/d Wx (66),(ev)
-EndTable
-
-GrpTable: Grp19
-1: vgatherpf0qps/d Wx (66),(ev)
-2: vgatherpf1qps/d Wx (66),(ev)
-5: vscatterpf0qps/d Wx (66),(ev)
-6: vscatterpf1qps/d Wx (66),(ev)
-EndTable
-
-# AMD's Prefetch Group
-GrpTable: GrpP
-0: PREFETCH
-1: PREFETCHW
-EndTable
-
-GrpTable: GrpPDLK
-0: MONTMUL
-1: XSHA1
-2: XSHA2
-EndTable
-
-GrpTable: GrpRNG
-0: xstore-rng
-1: xcrypt-ecb
-2: xcrypt-cbc
-4: xcrypt-cfb
-5: xcrypt-ofb
-EndTable
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c
index 6d288237887b..9b56fb74bedf 100644
--- a/tools/perf/util/intel-pt.c
+++ b/tools/perf/util/intel-pt.c
@@ -1,16 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* intel_pt.c: Intel Processor Trace support
* Copyright (c) 2013-2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#include <inttypes.h>
@@ -18,9 +9,10 @@
#include <stdbool.h>
#include <errno.h>
#include <linux/kernel.h>
+#include <linux/string.h>
#include <linux/types.h>
+#include <linux/zalloc.h>
-#include "../perf.h"
#include "session.h"
#include "machine.h"
#include "memswap.h"
@@ -31,7 +23,6 @@
#include "evsel.h"
#include "map.h"
#include "color.h"
-#include "util.h"
#include "thread.h"
#include "thread-stack.h"
#include "symbol.h"
@@ -42,6 +33,9 @@
#include "tsc.h"
#include "intel-pt.h"
#include "config.h"
+#include "time-utils.h"
+
+#include "../arch/x86/include/uapi/asm/perf_regs.h"
#include "intel-pt-decoder/intel-pt-log.h"
#include "intel-pt-decoder/intel-pt-decoder.h"
@@ -50,6 +44,11 @@
#define MAX_TIMESTAMP (~0ULL)
+struct range {
+ u64 start;
+ u64 end;
+};
+
struct intel_pt {
struct auxtrace auxtrace;
struct auxtrace_queues queues;
@@ -57,7 +56,7 @@ struct intel_pt {
u32 auxtrace_type;
struct perf_session *session;
struct machine *machine;
- struct perf_evsel *switch_evsel;
+ struct evsel *switch_evsel;
struct thread *unknown_thread;
bool timeless_decoding;
bool sampling_mode;
@@ -104,6 +103,9 @@ struct intel_pt {
u64 pwrx_id;
u64 cbr_id;
+ bool sample_pebs;
+ struct evsel *pebs_evsel;
+
u64 tsc_bit;
u64 mtc_bit;
u64 mtc_freq_bits;
@@ -118,6 +120,9 @@ struct intel_pt {
char *filter;
struct addr_filters filts;
+
+ struct range *time_ranges;
+ unsigned int range_cnt;
};
enum switch_state {
@@ -154,9 +159,19 @@ struct intel_pt_queue {
bool have_sample;
u64 time;
u64 timestamp;
+ u64 sel_timestamp;
+ bool sel_start;
+ unsigned int sel_idx;
u32 flags;
u16 insn_len;
u64 last_insn_cnt;
+ u64 ipc_insn_cnt;
+ u64 ipc_cyc_cnt;
+ u64 last_in_insn_cnt;
+ u64 last_in_cyc_cnt;
+ u64 last_br_insn_cnt;
+ u64 last_br_cyc_cnt;
+ unsigned int cbr_seen;
char insn[INTEL_PT_INSN_BUF_SZ];
};
@@ -168,13 +183,14 @@ static void intel_pt_dump(struct intel_pt *pt __maybe_unused,
int ret, pkt_len, i;
char desc[INTEL_PT_PKT_DESC_MAX];
const char *color = PERF_COLOR_BLUE;
+ enum intel_pt_pkt_ctx ctx = INTEL_PT_NO_CTX;
color_fprintf(stdout, color,
". ... Intel Processor Trace data: size %zu bytes\n",
len);
while (len) {
- ret = intel_pt_get_packet(buf, len, &packet);
+ ret = intel_pt_get_packet(buf, len, &packet, &ctx);
if (ret > 0)
pkt_len = ret;
else
@@ -233,32 +249,13 @@ static int intel_pt_do_fix_overlap(struct intel_pt *pt, struct auxtrace_buffer *
return 0;
}
-/* This function assumes data is processed sequentially only */
-static int intel_pt_get_trace(struct intel_pt_buffer *b, void *data)
+static int intel_pt_get_buffer(struct intel_pt_queue *ptq,
+ struct auxtrace_buffer *buffer,
+ struct auxtrace_buffer *old_buffer,
+ struct intel_pt_buffer *b)
{
- struct intel_pt_queue *ptq = data;
- struct auxtrace_buffer *buffer = ptq->buffer;
- struct auxtrace_buffer *old_buffer = ptq->old_buffer;
- struct auxtrace_queue *queue;
bool might_overlap;
- if (ptq->stop) {
- b->len = 0;
- return 0;
- }
-
- queue = &ptq->pt->queues.queue_array[ptq->queue_nr];
-
- buffer = auxtrace_buffer__next(queue, buffer);
- if (!buffer) {
- if (old_buffer)
- auxtrace_buffer__drop_data(old_buffer);
- b->len = 0;
- return 0;
- }
-
- ptq->buffer = buffer;
-
if (!buffer->data) {
int fd = perf_data__fd(ptq->pt->session->data);
@@ -288,6 +285,95 @@ static int intel_pt_get_trace(struct intel_pt_buffer *b, void *data)
b->consecutive = true;
}
+ return 0;
+}
+
+/* Do not drop buffers with references - refer intel_pt_get_trace() */
+static void intel_pt_lookahead_drop_buffer(struct intel_pt_queue *ptq,
+ struct auxtrace_buffer *buffer)
+{
+ if (!buffer || buffer == ptq->buffer || buffer == ptq->old_buffer)
+ return;
+
+ auxtrace_buffer__drop_data(buffer);
+}
+
+/* Must be serialized with respect to intel_pt_get_trace() */
+static int intel_pt_lookahead(void *data, intel_pt_lookahead_cb_t cb,
+ void *cb_data)
+{
+ struct intel_pt_queue *ptq = data;
+ struct auxtrace_buffer *buffer = ptq->buffer;
+ struct auxtrace_buffer *old_buffer = ptq->old_buffer;
+ struct auxtrace_queue *queue;
+ int err = 0;
+
+ queue = &ptq->pt->queues.queue_array[ptq->queue_nr];
+
+ while (1) {
+ struct intel_pt_buffer b = { .len = 0 };
+
+ buffer = auxtrace_buffer__next(queue, buffer);
+ if (!buffer)
+ break;
+
+ err = intel_pt_get_buffer(ptq, buffer, old_buffer, &b);
+ if (err)
+ break;
+
+ if (b.len) {
+ intel_pt_lookahead_drop_buffer(ptq, old_buffer);
+ old_buffer = buffer;
+ } else {
+ intel_pt_lookahead_drop_buffer(ptq, buffer);
+ continue;
+ }
+
+ err = cb(&b, cb_data);
+ if (err)
+ break;
+ }
+
+ if (buffer != old_buffer)
+ intel_pt_lookahead_drop_buffer(ptq, buffer);
+ intel_pt_lookahead_drop_buffer(ptq, old_buffer);
+
+ return err;
+}
+
+/*
+ * This function assumes data is processed sequentially only.
+ * Must be serialized with respect to intel_pt_lookahead()
+ */
+static int intel_pt_get_trace(struct intel_pt_buffer *b, void *data)
+{
+ struct intel_pt_queue *ptq = data;
+ struct auxtrace_buffer *buffer = ptq->buffer;
+ struct auxtrace_buffer *old_buffer = ptq->old_buffer;
+ struct auxtrace_queue *queue;
+ int err;
+
+ if (ptq->stop) {
+ b->len = 0;
+ return 0;
+ }
+
+ queue = &ptq->pt->queues.queue_array[ptq->queue_nr];
+
+ buffer = auxtrace_buffer__next(queue, buffer);
+ if (!buffer) {
+ if (old_buffer)
+ auxtrace_buffer__drop_data(old_buffer);
+ b->len = 0;
+ return 0;
+ }
+
+ ptq->buffer = buffer;
+
+ err = intel_pt_get_buffer(ptq, buffer, old_buffer, b);
+ if (err)
+ return err;
+
if (ptq->step_through_buffers)
ptq->stop = true;
@@ -637,11 +723,11 @@ static bool intel_pt_get_config(struct intel_pt *pt,
static bool intel_pt_exclude_kernel(struct intel_pt *pt)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(pt->session->evlist, evsel) {
- if (intel_pt_get_config(pt, &evsel->attr, NULL) &&
- !evsel->attr.exclude_kernel)
+ if (intel_pt_get_config(pt, &evsel->core.attr, NULL) &&
+ !evsel->core.attr.exclude_kernel)
return false;
}
return true;
@@ -649,14 +735,14 @@ static bool intel_pt_exclude_kernel(struct intel_pt *pt)
static bool intel_pt_return_compression(struct intel_pt *pt)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
u64 config;
if (!pt->noretcomp_bit)
return true;
evlist__for_each_entry(pt->session->evlist, evsel) {
- if (intel_pt_get_config(pt, &evsel->attr, &config) &&
+ if (intel_pt_get_config(pt, &evsel->core.attr, &config) &&
(config & pt->noretcomp_bit))
return false;
}
@@ -665,11 +751,11 @@ static bool intel_pt_return_compression(struct intel_pt *pt)
static bool intel_pt_branch_enable(struct intel_pt *pt)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
u64 config;
evlist__for_each_entry(pt->session->evlist, evsel) {
- if (intel_pt_get_config(pt, &evsel->attr, &config) &&
+ if (intel_pt_get_config(pt, &evsel->core.attr, &config) &&
(config & 1) && !(config & 0x2000))
return false;
}
@@ -678,7 +764,7 @@ static bool intel_pt_branch_enable(struct intel_pt *pt)
static unsigned int intel_pt_mtc_period(struct intel_pt *pt)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
unsigned int shift;
u64 config;
@@ -689,7 +775,7 @@ static unsigned int intel_pt_mtc_period(struct intel_pt *pt)
config >>= 1;
evlist__for_each_entry(pt->session->evlist, evsel) {
- if (intel_pt_get_config(pt, &evsel->attr, &config))
+ if (intel_pt_get_config(pt, &evsel->core.attr, &config))
return (config & pt->mtc_freq_bits) >> shift;
}
return 0;
@@ -697,7 +783,7 @@ static unsigned int intel_pt_mtc_period(struct intel_pt *pt)
static bool intel_pt_timeless_decoding(struct intel_pt *pt)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
bool timeless_decoding = true;
u64 config;
@@ -705,9 +791,9 @@ static bool intel_pt_timeless_decoding(struct intel_pt *pt)
return true;
evlist__for_each_entry(pt->session->evlist, evsel) {
- if (!(evsel->attr.sample_type & PERF_SAMPLE_TIME))
+ if (!(evsel->core.attr.sample_type & PERF_SAMPLE_TIME))
return true;
- if (intel_pt_get_config(pt, &evsel->attr, &config)) {
+ if (intel_pt_get_config(pt, &evsel->core.attr, &config)) {
if (config & pt->tsc_bit)
timeless_decoding = false;
else
@@ -719,11 +805,11 @@ static bool intel_pt_timeless_decoding(struct intel_pt *pt)
static bool intel_pt_tracing_kernel(struct intel_pt *pt)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(pt->session->evlist, evsel) {
- if (intel_pt_get_config(pt, &evsel->attr, NULL) &&
- !evsel->attr.exclude_kernel)
+ if (intel_pt_get_config(pt, &evsel->core.attr, NULL) &&
+ !evsel->core.attr.exclude_kernel)
return true;
}
return false;
@@ -731,7 +817,7 @@ static bool intel_pt_tracing_kernel(struct intel_pt *pt)
static bool intel_pt_have_tsc(struct intel_pt *pt)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
bool have_tsc = false;
u64 config;
@@ -739,7 +825,7 @@ static bool intel_pt_have_tsc(struct intel_pt *pt)
return false;
evlist__for_each_entry(pt->session->evlist, evsel) {
- if (intel_pt_get_config(pt, &evsel->attr, &config)) {
+ if (intel_pt_get_config(pt, &evsel->core.attr, &config)) {
if (config & pt->tsc_bit)
have_tsc = true;
else
@@ -807,6 +893,7 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt,
params.get_trace = intel_pt_get_trace;
params.walk_insn = intel_pt_walk_next_insn;
+ params.lookahead = intel_pt_lookahead;
params.data = ptq;
params.return_compression = intel_pt_return_compression(pt);
params.branch_enable = intel_pt_branch_enable(pt);
@@ -930,6 +1017,23 @@ static void intel_pt_sample_flags(struct intel_pt_queue *ptq)
ptq->flags |= PERF_IP_FLAG_TRACE_END;
}
+static void intel_pt_setup_time_range(struct intel_pt *pt,
+ struct intel_pt_queue *ptq)
+{
+ if (!pt->range_cnt)
+ return;
+
+ ptq->sel_timestamp = pt->time_ranges[0].start;
+ ptq->sel_idx = 0;
+
+ if (ptq->sel_timestamp) {
+ ptq->sel_start = true;
+ } else {
+ ptq->sel_timestamp = pt->time_ranges[0].end;
+ ptq->sel_start = false;
+ }
+}
+
static int intel_pt_setup_queue(struct intel_pt *pt,
struct auxtrace_queue *queue,
unsigned int queue_nr)
@@ -949,11 +1053,15 @@ static int intel_pt_setup_queue(struct intel_pt *pt,
ptq->cpu = queue->cpu;
ptq->tid = queue->tid;
+ ptq->cbr_seen = UINT_MAX;
+
if (pt->sampling_mode && !pt->snapshot_mode &&
pt->timeless_decoding)
ptq->step_through_buffers = true;
ptq->sync_switch = pt->sync_switch;
+
+ intel_pt_setup_time_range(pt, ptq);
}
if (!ptq->on_heap &&
@@ -968,6 +1076,14 @@ static int intel_pt_setup_queue(struct intel_pt *pt,
intel_pt_log("queue %u getting timestamp\n", queue_nr);
intel_pt_log("queue %u decoding cpu %d pid %d tid %d\n",
queue_nr, ptq->cpu, ptq->pid, ptq->tid);
+
+ if (ptq->sel_start && ptq->sel_timestamp) {
+ ret = intel_pt_fast_forward(ptq->decoder,
+ ptq->sel_timestamp);
+ if (ret)
+ return ret;
+ }
+
while (1) {
state = intel_pt_decode(ptq->decoder);
if (state->err) {
@@ -987,6 +1103,9 @@ static int intel_pt_setup_queue(struct intel_pt *pt,
queue_nr, ptq->timestamp);
ptq->state = state;
ptq->have_sample = true;
+ if (ptq->sel_start && ptq->sel_timestamp &&
+ ptq->timestamp < ptq->sel_timestamp)
+ ptq->have_sample = false;
intel_pt_sample_flags(ptq);
ret = auxtrace_heap__add(&pt->heap, queue_nr, ptq->timestamp);
if (ret)
@@ -1068,28 +1187,48 @@ static inline bool intel_pt_skip_event(struct intel_pt *pt)
pt->num_events++ < pt->synth_opts.initial_skip;
}
+/*
+ * Cannot count CBR as skipped because it won't go away until cbr == cbr_seen.
+ * Also ensure CBR is first non-skipped event by allowing for 4 more samples
+ * from this decoder state.
+ */
+static inline bool intel_pt_skip_cbr_event(struct intel_pt *pt)
+{
+ return pt->synth_opts.initial_skip &&
+ pt->num_events + 4 < pt->synth_opts.initial_skip;
+}
+
+static void intel_pt_prep_a_sample(struct intel_pt_queue *ptq,
+ union perf_event *event,
+ struct perf_sample *sample)
+{
+ event->sample.header.type = PERF_RECORD_SAMPLE;
+ event->sample.header.size = sizeof(struct perf_event_header);
+
+ sample->pid = ptq->pid;
+ sample->tid = ptq->tid;
+ sample->cpu = ptq->cpu;
+ sample->insn_len = ptq->insn_len;
+ memcpy(sample->insn, ptq->insn, INTEL_PT_INSN_BUF_SZ);
+}
+
static void intel_pt_prep_b_sample(struct intel_pt *pt,
struct intel_pt_queue *ptq,
union perf_event *event,
struct perf_sample *sample)
{
+ intel_pt_prep_a_sample(ptq, event, sample);
+
if (!pt->timeless_decoding)
sample->time = tsc_to_perf_time(ptq->timestamp, &pt->tc);
sample->ip = ptq->state->from_ip;
sample->cpumode = intel_pt_cpumode(pt, sample->ip);
- sample->pid = ptq->pid;
- sample->tid = ptq->tid;
sample->addr = ptq->state->to_ip;
sample->period = 1;
- sample->cpu = ptq->cpu;
sample->flags = ptq->flags;
- sample->insn_len = ptq->insn_len;
- memcpy(sample->insn, ptq->insn, INTEL_PT_INSN_BUF_SZ);
- event->sample.header.type = PERF_RECORD_SAMPLE;
event->sample.header.misc = sample->cpumode;
- event->sample.header.size = sizeof(struct perf_event_header);
}
static int intel_pt_inject_event(union perf_event *event,
@@ -1162,6 +1301,13 @@ static int intel_pt_synth_branch_sample(struct intel_pt_queue *ptq)
sample.branch_stack = (struct branch_stack *)&dummy_bs;
}
+ sample.cyc_cnt = ptq->ipc_cyc_cnt - ptq->last_br_cyc_cnt;
+ if (sample.cyc_cnt) {
+ sample.insn_cnt = ptq->ipc_insn_cnt - ptq->last_br_insn_cnt;
+ ptq->last_br_insn_cnt = ptq->ipc_insn_cnt;
+ ptq->last_br_cyc_cnt = ptq->ipc_cyc_cnt;
+ }
+
return intel_pt_deliver_synth_b_event(pt, event, &sample,
pt->branches_sample_type);
}
@@ -1217,6 +1363,13 @@ static int intel_pt_synth_instruction_sample(struct intel_pt_queue *ptq)
sample.stream_id = ptq->pt->instructions_id;
sample.period = ptq->state->tot_insn_cnt - ptq->last_insn_cnt;
+ sample.cyc_cnt = ptq->ipc_cyc_cnt - ptq->last_in_cyc_cnt;
+ if (sample.cyc_cnt) {
+ sample.insn_cnt = ptq->ipc_insn_cnt - ptq->last_in_insn_cnt;
+ ptq->last_in_insn_cnt = ptq->ipc_insn_cnt;
+ ptq->last_in_cyc_cnt = ptq->ipc_cyc_cnt;
+ }
+
ptq->last_insn_cnt = ptq->state->tot_insn_cnt;
return intel_pt_deliver_synth_event(pt, ptq, event, &sample,
@@ -1290,9 +1443,11 @@ static int intel_pt_synth_cbr_sample(struct intel_pt_queue *ptq)
struct perf_synth_intel_cbr raw;
u32 flags;
- if (intel_pt_skip_event(pt))
+ if (intel_pt_skip_cbr_event(pt))
return 0;
+ ptq->cbr_seen = ptq->state->cbr;
+
intel_pt_prep_p_sample(pt, ptq, event, &sample);
sample.id = ptq->pt->cbr_id;
@@ -1410,6 +1565,261 @@ static int intel_pt_synth_pwrx_sample(struct intel_pt_queue *ptq)
pt->pwr_events_sample_type);
}
+/*
+ * PEBS gp_regs array indexes plus 1 so that 0 means not present. Refer
+ * intel_pt_add_gp_regs().
+ */
+static const int pebs_gp_regs[] = {
+ [PERF_REG_X86_FLAGS] = 1,
+ [PERF_REG_X86_IP] = 2,
+ [PERF_REG_X86_AX] = 3,
+ [PERF_REG_X86_CX] = 4,
+ [PERF_REG_X86_DX] = 5,
+ [PERF_REG_X86_BX] = 6,
+ [PERF_REG_X86_SP] = 7,
+ [PERF_REG_X86_BP] = 8,
+ [PERF_REG_X86_SI] = 9,
+ [PERF_REG_X86_DI] = 10,
+ [PERF_REG_X86_R8] = 11,
+ [PERF_REG_X86_R9] = 12,
+ [PERF_REG_X86_R10] = 13,
+ [PERF_REG_X86_R11] = 14,
+ [PERF_REG_X86_R12] = 15,
+ [PERF_REG_X86_R13] = 16,
+ [PERF_REG_X86_R14] = 17,
+ [PERF_REG_X86_R15] = 18,
+};
+
+static u64 *intel_pt_add_gp_regs(struct regs_dump *intr_regs, u64 *pos,
+ const struct intel_pt_blk_items *items,
+ u64 regs_mask)
+{
+ const u64 *gp_regs = items->val[INTEL_PT_GP_REGS_POS];
+ u32 mask = items->mask[INTEL_PT_GP_REGS_POS];
+ u32 bit;
+ int i;
+
+ for (i = 0, bit = 1; i < PERF_REG_X86_64_MAX; i++, bit <<= 1) {
+ /* Get the PEBS gp_regs array index */
+ int n = pebs_gp_regs[i] - 1;
+
+ if (n < 0)
+ continue;
+ /*
+ * Add only registers that were requested (i.e. 'regs_mask') and
+ * that were provided (i.e. 'mask'), and update the resulting
+ * mask (i.e. 'intr_regs->mask') accordingly.
+ */
+ if (mask & 1 << n && regs_mask & bit) {
+ intr_regs->mask |= bit;
+ *pos++ = gp_regs[n];
+ }
+ }
+
+ return pos;
+}
+
+#ifndef PERF_REG_X86_XMM0
+#define PERF_REG_X86_XMM0 32
+#endif
+
+static void intel_pt_add_xmm(struct regs_dump *intr_regs, u64 *pos,
+ const struct intel_pt_blk_items *items,
+ u64 regs_mask)
+{
+ u32 mask = items->has_xmm & (regs_mask >> PERF_REG_X86_XMM0);
+ const u64 *xmm = items->xmm;
+
+ /*
+ * If there are any XMM registers, then there should be all of them.
+ * Nevertheless, follow the logic to add only registers that were
+ * requested (i.e. 'regs_mask') and that were provided (i.e. 'mask'),
+ * and update the resulting mask (i.e. 'intr_regs->mask') accordingly.
+ */
+ intr_regs->mask |= (u64)mask << PERF_REG_X86_XMM0;
+
+ for (; mask; mask >>= 1, xmm++) {
+ if (mask & 1)
+ *pos++ = *xmm;
+ }
+}
+
+#define LBR_INFO_MISPRED (1ULL << 63)
+#define LBR_INFO_IN_TX (1ULL << 62)
+#define LBR_INFO_ABORT (1ULL << 61)
+#define LBR_INFO_CYCLES 0xffff
+
+/* Refer kernel's intel_pmu_store_pebs_lbrs() */
+static u64 intel_pt_lbr_flags(u64 info)
+{
+ union {
+ struct branch_flags flags;
+ u64 result;
+ } u = {
+ .flags = {
+ .mispred = !!(info & LBR_INFO_MISPRED),
+ .predicted = !(info & LBR_INFO_MISPRED),
+ .in_tx = !!(info & LBR_INFO_IN_TX),
+ .abort = !!(info & LBR_INFO_ABORT),
+ .cycles = info & LBR_INFO_CYCLES,
+ }
+ };
+
+ return u.result;
+}
+
+static void intel_pt_add_lbrs(struct branch_stack *br_stack,
+ const struct intel_pt_blk_items *items)
+{
+ u64 *to;
+ int i;
+
+ br_stack->nr = 0;
+
+ to = &br_stack->entries[0].from;
+
+ for (i = INTEL_PT_LBR_0_POS; i <= INTEL_PT_LBR_2_POS; i++) {
+ u32 mask = items->mask[i];
+ const u64 *from = items->val[i];
+
+ for (; mask; mask >>= 3, from += 3) {
+ if ((mask & 7) == 7) {
+ *to++ = from[0];
+ *to++ = from[1];
+ *to++ = intel_pt_lbr_flags(from[2]);
+ br_stack->nr += 1;
+ }
+ }
+ }
+}
+
+/* INTEL_PT_LBR_0, INTEL_PT_LBR_1 and INTEL_PT_LBR_2 */
+#define LBRS_MAX (INTEL_PT_BLK_ITEM_ID_CNT * 3)
+
+static int intel_pt_synth_pebs_sample(struct intel_pt_queue *ptq)
+{
+ const struct intel_pt_blk_items *items = &ptq->state->items;
+ struct perf_sample sample = { .ip = 0, };
+ union perf_event *event = ptq->event_buf;
+ struct intel_pt *pt = ptq->pt;
+ struct evsel *evsel = pt->pebs_evsel;
+ u64 sample_type = evsel->core.attr.sample_type;
+ u64 id = evsel->id[0];
+ u8 cpumode;
+
+ if (intel_pt_skip_event(pt))
+ return 0;
+
+ intel_pt_prep_a_sample(ptq, event, &sample);
+
+ sample.id = id;
+ sample.stream_id = id;
+
+ if (!evsel->core.attr.freq)
+ sample.period = evsel->core.attr.sample_period;
+
+ /* No support for non-zero CS base */
+ if (items->has_ip)
+ sample.ip = items->ip;
+ else if (items->has_rip)
+ sample.ip = items->rip;
+ else
+ sample.ip = ptq->state->from_ip;
+
+ /* No support for guest mode at this time */
+ cpumode = sample.ip < ptq->pt->kernel_start ?
+ PERF_RECORD_MISC_USER :
+ PERF_RECORD_MISC_KERNEL;
+
+ event->sample.header.misc = cpumode | PERF_RECORD_MISC_EXACT_IP;
+
+ sample.cpumode = cpumode;
+
+ if (sample_type & PERF_SAMPLE_TIME) {
+ u64 timestamp = 0;
+
+ if (items->has_timestamp)
+ timestamp = items->timestamp;
+ else if (!pt->timeless_decoding)
+ timestamp = ptq->timestamp;
+ if (timestamp)
+ sample.time = tsc_to_perf_time(timestamp, &pt->tc);
+ }
+
+ if (sample_type & PERF_SAMPLE_CALLCHAIN &&
+ pt->synth_opts.callchain) {
+ thread_stack__sample(ptq->thread, ptq->cpu, ptq->chain,
+ pt->synth_opts.callchain_sz, sample.ip,
+ pt->kernel_start);
+ sample.callchain = ptq->chain;
+ }
+
+ if (sample_type & PERF_SAMPLE_REGS_INTR &&
+ items->mask[INTEL_PT_GP_REGS_POS]) {
+ u64 regs[sizeof(sample.intr_regs.mask)];
+ u64 regs_mask = evsel->core.attr.sample_regs_intr;
+ u64 *pos;
+
+ sample.intr_regs.abi = items->is_32_bit ?
+ PERF_SAMPLE_REGS_ABI_32 :
+ PERF_SAMPLE_REGS_ABI_64;
+ sample.intr_regs.regs = regs;
+
+ pos = intel_pt_add_gp_regs(&sample.intr_regs, regs, items, regs_mask);
+
+ intel_pt_add_xmm(&sample.intr_regs, pos, items, regs_mask);
+ }
+
+ if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
+ struct {
+ struct branch_stack br_stack;
+ struct branch_entry entries[LBRS_MAX];
+ } br;
+
+ if (items->mask[INTEL_PT_LBR_0_POS] ||
+ items->mask[INTEL_PT_LBR_1_POS] ||
+ items->mask[INTEL_PT_LBR_2_POS]) {
+ intel_pt_add_lbrs(&br.br_stack, items);
+ sample.branch_stack = &br.br_stack;
+ } else if (pt->synth_opts.last_branch) {
+ intel_pt_copy_last_branch_rb(ptq);
+ sample.branch_stack = ptq->last_branch;
+ } else {
+ br.br_stack.nr = 0;
+ sample.branch_stack = &br.br_stack;
+ }
+ }
+
+ if (sample_type & PERF_SAMPLE_ADDR && items->has_mem_access_address)
+ sample.addr = items->mem_access_address;
+
+ if (sample_type & PERF_SAMPLE_WEIGHT) {
+ /*
+ * Refer kernel's setup_pebs_adaptive_sample_data() and
+ * intel_hsw_weight().
+ */
+ if (items->has_mem_access_latency)
+ sample.weight = items->mem_access_latency;
+ if (!sample.weight && items->has_tsx_aux_info) {
+ /* Cycles last block */
+ sample.weight = (u32)items->tsx_aux_info;
+ }
+ }
+
+ if (sample_type & PERF_SAMPLE_TRANSACTION && items->has_tsx_aux_info) {
+ u64 ax = items->has_rax ? items->rax : 0;
+ /* Refer kernel's intel_hsw_transaction() */
+ u64 txn = (u8)(items->tsx_aux_info >> 32);
+
+ /* For RTM XABORTs also log the abort code from AX */
+ if (txn & PERF_TXN_TRANSACTION && ax & 1)
+ txn |= ((ax >> 24) & 0xff) << PERF_TXN_ABORT_SHIFT;
+ sample.transaction = txn;
+ }
+
+ return intel_pt_deliver_synth_event(pt, ptq, event, &sample, sample_type);
+}
+
static int intel_pt_synth_error(struct intel_pt *pt, int code, int cpu,
pid_t pid, pid_t tid, u64 ip, u64 timestamp)
{
@@ -1474,8 +1884,7 @@ static inline bool intel_pt_is_switch_ip(struct intel_pt_queue *ptq, u64 ip)
}
#define INTEL_PT_PWR_EVT (INTEL_PT_MWAIT_OP | INTEL_PT_PWR_ENTRY | \
- INTEL_PT_EX_STOP | INTEL_PT_PWR_EXIT | \
- INTEL_PT_CBR_CHG)
+ INTEL_PT_EX_STOP | INTEL_PT_PWR_EXIT)
static int intel_pt_sample(struct intel_pt_queue *ptq)
{
@@ -1488,31 +1897,52 @@ static int intel_pt_sample(struct intel_pt_queue *ptq)
ptq->have_sample = false;
- if (pt->sample_pwr_events && (state->type & INTEL_PT_PWR_EVT)) {
- if (state->type & INTEL_PT_CBR_CHG) {
+ if (ptq->state->tot_cyc_cnt > ptq->ipc_cyc_cnt) {
+ /*
+ * Cycle count and instruction count only go together to create
+ * a valid IPC ratio when the cycle count changes.
+ */
+ ptq->ipc_insn_cnt = ptq->state->tot_insn_cnt;
+ ptq->ipc_cyc_cnt = ptq->state->tot_cyc_cnt;
+ }
+
+ /*
+ * Do PEBS first to allow for the possibility that the PEBS timestamp
+ * precedes the current timestamp.
+ */
+ if (pt->sample_pebs && state->type & INTEL_PT_BLK_ITEMS) {
+ err = intel_pt_synth_pebs_sample(ptq);
+ if (err)
+ return err;
+ }
+
+ if (pt->sample_pwr_events) {
+ if (ptq->state->cbr != ptq->cbr_seen) {
err = intel_pt_synth_cbr_sample(ptq);
if (err)
return err;
}
- if (state->type & INTEL_PT_MWAIT_OP) {
- err = intel_pt_synth_mwait_sample(ptq);
- if (err)
- return err;
- }
- if (state->type & INTEL_PT_PWR_ENTRY) {
- err = intel_pt_synth_pwre_sample(ptq);
- if (err)
- return err;
- }
- if (state->type & INTEL_PT_EX_STOP) {
- err = intel_pt_synth_exstop_sample(ptq);
- if (err)
- return err;
- }
- if (state->type & INTEL_PT_PWR_EXIT) {
- err = intel_pt_synth_pwrx_sample(ptq);
- if (err)
- return err;
+ if (state->type & INTEL_PT_PWR_EVT) {
+ if (state->type & INTEL_PT_MWAIT_OP) {
+ err = intel_pt_synth_mwait_sample(ptq);
+ if (err)
+ return err;
+ }
+ if (state->type & INTEL_PT_PWR_ENTRY) {
+ err = intel_pt_synth_pwre_sample(ptq);
+ if (err)
+ return err;
+ }
+ if (state->type & INTEL_PT_EX_STOP) {
+ err = intel_pt_synth_exstop_sample(ptq);
+ if (err)
+ return err;
+ }
+ if (state->type & INTEL_PT_PWR_EXIT) {
+ err = intel_pt_synth_pwrx_sample(ptq);
+ if (err)
+ return err;
+ }
}
}
@@ -1650,10 +2080,83 @@ static void intel_pt_enable_sync_switch(struct intel_pt *pt)
}
}
+/*
+ * To filter against time ranges, it is only necessary to look at the next start
+ * or end time.
+ */
+static bool intel_pt_next_time(struct intel_pt_queue *ptq)
+{
+ struct intel_pt *pt = ptq->pt;
+
+ if (ptq->sel_start) {
+ /* Next time is an end time */
+ ptq->sel_start = false;
+ ptq->sel_timestamp = pt->time_ranges[ptq->sel_idx].end;
+ return true;
+ } else if (ptq->sel_idx + 1 < pt->range_cnt) {
+ /* Next time is a start time */
+ ptq->sel_start = true;
+ ptq->sel_idx += 1;
+ ptq->sel_timestamp = pt->time_ranges[ptq->sel_idx].start;
+ return true;
+ }
+
+ /* No next time */
+ return false;
+}
+
+static int intel_pt_time_filter(struct intel_pt_queue *ptq, u64 *ff_timestamp)
+{
+ int err;
+
+ while (1) {
+ if (ptq->sel_start) {
+ if (ptq->timestamp >= ptq->sel_timestamp) {
+ /* After start time, so consider next time */
+ intel_pt_next_time(ptq);
+ if (!ptq->sel_timestamp) {
+ /* No end time */
+ return 0;
+ }
+ /* Check against end time */
+ continue;
+ }
+ /* Before start time, so fast forward */
+ ptq->have_sample = false;
+ if (ptq->sel_timestamp > *ff_timestamp) {
+ if (ptq->sync_switch) {
+ intel_pt_next_tid(ptq->pt, ptq);
+ ptq->switch_state = INTEL_PT_SS_UNKNOWN;
+ }
+ *ff_timestamp = ptq->sel_timestamp;
+ err = intel_pt_fast_forward(ptq->decoder,
+ ptq->sel_timestamp);
+ if (err)
+ return err;
+ }
+ return 0;
+ } else if (ptq->timestamp > ptq->sel_timestamp) {
+ /* After end time, so consider next time */
+ if (!intel_pt_next_time(ptq)) {
+ /* No next time range, so stop decoding */
+ ptq->have_sample = false;
+ ptq->switch_state = INTEL_PT_SS_NOT_TRACING;
+ return 1;
+ }
+ /* Check against next start time */
+ continue;
+ } else {
+ /* Before end time */
+ return 0;
+ }
+ }
+}
+
static int intel_pt_run_decoder(struct intel_pt_queue *ptq, u64 *timestamp)
{
const struct intel_pt_state *state = ptq->state;
struct intel_pt *pt = ptq->pt;
+ u64 ff_timestamp = 0;
int err;
if (!pt->kernel_start) {
@@ -1718,6 +2221,12 @@ static int intel_pt_run_decoder(struct intel_pt_queue *ptq, u64 *timestamp)
ptq->timestamp = state->timestamp;
}
+ if (ptq->sel_timestamp) {
+ err = intel_pt_time_filter(ptq, &ff_timestamp);
+ if (err)
+ return err;
+ }
+
if (!pt->timeless_decoding && ptq->timestamp >= *timestamp) {
*timestamp = ptq->timestamp;
return 0;
@@ -1859,7 +2368,6 @@ static int intel_pt_sync_switch(struct intel_pt *pt, int cpu, pid_t tid,
switch (ptq->switch_state) {
case INTEL_PT_SS_NOT_TRACING:
- ptq->next_tid = -1;
break;
case INTEL_PT_SS_UNKNOWN:
case INTEL_PT_SS_TRACING:
@@ -1879,20 +2387,21 @@ static int intel_pt_sync_switch(struct intel_pt *pt, int cpu, pid_t tid,
ptq->switch_state = INTEL_PT_SS_TRACING;
break;
case INTEL_PT_SS_EXPECTING_SWITCH_IP:
- ptq->next_tid = tid;
intel_pt_log("ERROR: cpu %d expecting switch ip\n", cpu);
break;
default:
break;
}
+ ptq->next_tid = -1;
+
return 1;
}
static int intel_pt_process_switch(struct intel_pt *pt,
struct perf_sample *sample)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
pid_t tid;
int cpu, ret;
@@ -1914,6 +2423,44 @@ static int intel_pt_process_switch(struct intel_pt *pt,
return machine__set_current_tid(pt->machine, cpu, -1, tid);
}
+static int intel_pt_context_switch_in(struct intel_pt *pt,
+ struct perf_sample *sample)
+{
+ pid_t pid = sample->pid;
+ pid_t tid = sample->tid;
+ int cpu = sample->cpu;
+
+ if (pt->sync_switch) {
+ struct intel_pt_queue *ptq;
+
+ ptq = intel_pt_cpu_to_ptq(pt, cpu);
+ if (ptq && ptq->sync_switch) {
+ ptq->next_tid = -1;
+ switch (ptq->switch_state) {
+ case INTEL_PT_SS_NOT_TRACING:
+ case INTEL_PT_SS_UNKNOWN:
+ case INTEL_PT_SS_TRACING:
+ break;
+ case INTEL_PT_SS_EXPECTING_SWITCH_EVENT:
+ case INTEL_PT_SS_EXPECTING_SWITCH_IP:
+ ptq->switch_state = INTEL_PT_SS_TRACING;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /*
+ * If the current tid has not been updated yet, ensure it is now that
+ * a "switch in" event has occurred.
+ */
+ if (machine__get_current_tid(pt->machine, cpu) == tid)
+ return 0;
+
+ return machine__set_current_tid(pt->machine, cpu, pid, tid);
+}
+
static int intel_pt_context_switch(struct intel_pt *pt, union perf_event *event,
struct perf_sample *sample)
{
@@ -1925,7 +2472,7 @@ static int intel_pt_context_switch(struct intel_pt *pt, union perf_event *event,
if (pt->have_sched_switch == 3) {
if (!out)
- return 0;
+ return intel_pt_context_switch_in(pt, sample);
if (event->header.type != PERF_RECORD_SWITCH_CPU_WIDE) {
pr_err("Expecting CPU-wide context switch event\n");
return -EINVAL;
@@ -2085,6 +2632,7 @@ static void intel_pt_free(struct perf_session *session)
thread__put(pt->unknown_thread);
addr_filters__exit(&pt->filts);
zfree(&pt->filter);
+ zfree(&pt->time_ranges);
free(pt);
}
@@ -2165,10 +2713,10 @@ static int intel_pt_synth_event(struct perf_session *session, const char *name,
return err;
}
-static void intel_pt_set_event_name(struct perf_evlist *evlist, u64 id,
+static void intel_pt_set_event_name(struct evlist *evlist, u64 id,
const char *name)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) {
if (evsel->id && evsel->id[0] == id) {
@@ -2180,13 +2728,13 @@ static void intel_pt_set_event_name(struct perf_evlist *evlist, u64 id,
}
}
-static struct perf_evsel *intel_pt_evsel(struct intel_pt *pt,
- struct perf_evlist *evlist)
+static struct evsel *intel_pt_evsel(struct intel_pt *pt,
+ struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) {
- if (evsel->attr.type == pt->pmu_type && evsel->ids)
+ if (evsel->core.attr.type == pt->pmu_type && evsel->ids)
return evsel;
}
@@ -2196,8 +2744,8 @@ static struct perf_evsel *intel_pt_evsel(struct intel_pt *pt,
static int intel_pt_synth_events(struct intel_pt *pt,
struct perf_session *session)
{
- struct perf_evlist *evlist = session->evlist;
- struct perf_evsel *evsel = intel_pt_evsel(pt, evlist);
+ struct evlist *evlist = session->evlist;
+ struct evsel *evsel = intel_pt_evsel(pt, evlist);
struct perf_event_attr attr;
u64 id;
int err;
@@ -2210,7 +2758,7 @@ static int intel_pt_synth_events(struct intel_pt *pt,
memset(&attr, 0, sizeof(struct perf_event_attr));
attr.size = sizeof(struct perf_event_attr);
attr.type = PERF_TYPE_HARDWARE;
- attr.sample_type = evsel->attr.sample_type & PERF_SAMPLE_MASK;
+ attr.sample_type = evsel->core.attr.sample_type & PERF_SAMPLE_MASK;
attr.sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID |
PERF_SAMPLE_PERIOD;
if (pt->timeless_decoding)
@@ -2219,13 +2767,13 @@ static int intel_pt_synth_events(struct intel_pt *pt,
attr.sample_type |= PERF_SAMPLE_TIME;
if (!pt->per_cpu_mmaps)
attr.sample_type &= ~(u64)PERF_SAMPLE_CPU;
- attr.exclude_user = evsel->attr.exclude_user;
- attr.exclude_kernel = evsel->attr.exclude_kernel;
- attr.exclude_hv = evsel->attr.exclude_hv;
- attr.exclude_host = evsel->attr.exclude_host;
- attr.exclude_guest = evsel->attr.exclude_guest;
- attr.sample_id_all = evsel->attr.sample_id_all;
- attr.read_format = evsel->attr.read_format;
+ attr.exclude_user = evsel->core.attr.exclude_user;
+ attr.exclude_kernel = evsel->core.attr.exclude_kernel;
+ attr.exclude_hv = evsel->core.attr.exclude_hv;
+ attr.exclude_host = evsel->core.attr.exclude_host;
+ attr.exclude_guest = evsel->core.attr.exclude_guest;
+ attr.sample_id_all = evsel->core.attr.sample_id_all;
+ attr.read_format = evsel->core.attr.read_format;
id = evsel->id[0] + 1000000000;
if (!id)
@@ -2309,7 +2857,7 @@ static int intel_pt_synth_events(struct intel_pt *pt,
id += 1;
}
- if (pt->synth_opts.pwr_events && (evsel->attr.config & 0x10)) {
+ if (pt->synth_opts.pwr_events && (evsel->core.attr.config & 0x10)) {
attr.config = PERF_SYNTH_INTEL_MWAIT;
err = intel_pt_synth_event(session, "mwait", &attr, id);
if (err)
@@ -2346,9 +2894,25 @@ static int intel_pt_synth_events(struct intel_pt *pt,
return 0;
}
-static struct perf_evsel *intel_pt_find_sched_switch(struct perf_evlist *evlist)
+static void intel_pt_setup_pebs_events(struct intel_pt *pt)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
+
+ if (!pt->synth_opts.other_events)
+ return;
+
+ evlist__for_each_entry(pt->session->evlist, evsel) {
+ if (evsel->core.attr.aux_output && evsel->id) {
+ pt->sample_pebs = true;
+ pt->pebs_evsel = evsel;
+ return;
+ }
+ }
+}
+
+static struct evsel *intel_pt_find_sched_switch(struct evlist *evlist)
+{
+ struct evsel *evsel;
evlist__for_each_entry_reverse(evlist, evsel) {
const char *name = perf_evsel__name(evsel);
@@ -2360,12 +2924,12 @@ static struct perf_evsel *intel_pt_find_sched_switch(struct perf_evlist *evlist)
return NULL;
}
-static bool intel_pt_find_switch(struct perf_evlist *evlist)
+static bool intel_pt_find_switch(struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) {
- if (evsel->attr.context_switch)
+ if (evsel->core.attr.context_switch)
return true;
}
@@ -2382,6 +2946,85 @@ static int intel_pt_perf_config(const char *var, const char *value, void *data)
return 0;
}
+/* Find least TSC which converts to ns or later */
+static u64 intel_pt_tsc_start(u64 ns, struct intel_pt *pt)
+{
+ u64 tsc, tm;
+
+ tsc = perf_time_to_tsc(ns, &pt->tc);
+
+ while (1) {
+ tm = tsc_to_perf_time(tsc, &pt->tc);
+ if (tm < ns)
+ break;
+ tsc -= 1;
+ }
+
+ while (tm < ns)
+ tm = tsc_to_perf_time(++tsc, &pt->tc);
+
+ return tsc;
+}
+
+/* Find greatest TSC which converts to ns or earlier */
+static u64 intel_pt_tsc_end(u64 ns, struct intel_pt *pt)
+{
+ u64 tsc, tm;
+
+ tsc = perf_time_to_tsc(ns, &pt->tc);
+
+ while (1) {
+ tm = tsc_to_perf_time(tsc, &pt->tc);
+ if (tm > ns)
+ break;
+ tsc += 1;
+ }
+
+ while (tm > ns)
+ tm = tsc_to_perf_time(--tsc, &pt->tc);
+
+ return tsc;
+}
+
+static int intel_pt_setup_time_ranges(struct intel_pt *pt,
+ struct itrace_synth_opts *opts)
+{
+ struct perf_time_interval *p = opts->ptime_range;
+ int n = opts->range_num;
+ int i;
+
+ if (!n || !p || pt->timeless_decoding)
+ return 0;
+
+ pt->time_ranges = calloc(n, sizeof(struct range));
+ if (!pt->time_ranges)
+ return -ENOMEM;
+
+ pt->range_cnt = n;
+
+ intel_pt_log("%s: %u range(s)\n", __func__, n);
+
+ for (i = 0; i < n; i++) {
+ struct range *r = &pt->time_ranges[i];
+ u64 ts = p[i].start;
+ u64 te = p[i].end;
+
+ /*
+ * Take care to ensure the TSC range matches the perf-time range
+ * when converted back to perf-time.
+ */
+ r->start = ts ? intel_pt_tsc_start(ts, pt) : 0;
+ r->end = te ? intel_pt_tsc_end(te, pt) : 0;
+
+ intel_pt_log("range %d: perf time interval: %"PRIu64" to %"PRIu64"\n",
+ i, ts, te);
+ intel_pt_log("range %d: TSC time interval: %#"PRIx64" to %#"PRIx64"\n",
+ i, r->start, r->end);
+ }
+
+ return 0;
+}
+
static const char * const intel_pt_info_fmts[] = {
[INTEL_PT_PMU_TYPE] = " PMU Type %"PRId64"\n",
[INTEL_PT_TIME_SHIFT] = " Time Shift %"PRIu64"\n",
@@ -2401,7 +3044,7 @@ static const char * const intel_pt_info_fmts[] = {
[INTEL_PT_FILTER_STR_LEN] = " Filter string len. %"PRIu64"\n",
};
-static void intel_pt_print_info(u64 *arr, int start, int finish)
+static void intel_pt_print_info(__u64 *arr, int start, int finish)
{
int i;
@@ -2420,23 +3063,23 @@ static void intel_pt_print_info_str(const char *name, const char *str)
fprintf(stdout, " %-20s%s\n", name, str ? str : "");
}
-static bool intel_pt_has(struct auxtrace_info_event *auxtrace_info, int pos)
+static bool intel_pt_has(struct perf_record_auxtrace_info *auxtrace_info, int pos)
{
return auxtrace_info->header.size >=
- sizeof(struct auxtrace_info_event) + (sizeof(u64) * (pos + 1));
+ sizeof(struct perf_record_auxtrace_info) + (sizeof(u64) * (pos + 1));
}
int intel_pt_process_auxtrace_info(union perf_event *event,
struct perf_session *session)
{
- struct auxtrace_info_event *auxtrace_info = &event->auxtrace_info;
+ struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
size_t min_sz = sizeof(u64) * INTEL_PT_PER_CPU_MMAPS;
struct intel_pt *pt;
void *info_end;
- u64 *info;
+ __u64 *info;
int err;
- if (auxtrace_info->header.size < sizeof(struct auxtrace_info_event) +
+ if (auxtrace_info->header.size < sizeof(struct perf_record_auxtrace_info) +
min_sz)
return -EINVAL;
@@ -2583,17 +3226,17 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
goto err_delete_thread;
}
- if (session->itrace_synth_opts && session->itrace_synth_opts->set) {
+ if (session->itrace_synth_opts->set) {
pt->synth_opts = *session->itrace_synth_opts;
} else {
itrace_synth_opts__set_default(&pt->synth_opts,
session->itrace_synth_opts->default_no_sample);
- if (use_browser != -1) {
+ if (!session->itrace_synth_opts->default_no_sample &&
+ !session->itrace_synth_opts->inject) {
pt->synth_opts.branches = false;
pt->synth_opts.callchain = true;
}
- if (session->itrace_synth_opts)
- pt->synth_opts.thread_stack =
+ pt->synth_opts.thread_stack =
session->itrace_synth_opts->thread_stack;
}
@@ -2613,6 +3256,10 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
pt->cbr2khz = tsc_freq / pt->max_non_turbo_ratio / 1000;
}
+ err = intel_pt_setup_time_ranges(pt, session->itrace_synth_opts);
+ if (err)
+ goto err_delete_thread;
+
if (pt->synth_opts.calls)
pt->branches_filter |= PERF_IP_FLAG_CALL | PERF_IP_FLAG_ASYNC |
PERF_IP_FLAG_TRACE_END;
@@ -2632,6 +3279,8 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
if (err)
goto err_delete_thread;
+ intel_pt_setup_pebs_events(pt);
+
err = auxtrace_queues__process_index(&pt->queues, session);
if (err)
goto err_delete_thread;
@@ -2653,6 +3302,7 @@ err_free_queues:
err_free:
addr_filters__exit(&pt->filts);
zfree(&pt->filter);
+ zfree(&pt->time_ranges);
free(pt);
return err;
}
diff --git a/tools/perf/util/intel-pt.h b/tools/perf/util/intel-pt.h
index e13b14e5a37b..c7d6068e3a6b 100644
--- a/tools/perf/util/intel-pt.h
+++ b/tools/perf/util/intel-pt.h
@@ -1,16 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* intel_pt.h: Intel Processor Trace support
* Copyright (c) 2013-2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#ifndef INCLUDE__PERF_INTEL_PT_H__
diff --git a/tools/perf/util/intlist.c b/tools/perf/util/intlist.c
index 89715b64a315..84e5304e151a 100644
--- a/tools/perf/util/intlist.c
+++ b/tools/perf/util/intlist.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Based on intlist.c by:
* (c) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
- *
- * Licensed under the GPLv2.
*/
#include <errno.h>
diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c
index eda28d3570bc..b80f29bfc7bb 100644
--- a/tools/perf/util/jitdump.c
+++ b/tools/perf/util/jitdump.c
@@ -14,6 +14,7 @@
#include <sys/mman.h>
#include <linux/stringify.h>
+#include "build-id.h"
#include "util.h"
#include "event.h"
#include "debug.h"
@@ -28,7 +29,8 @@
#include "genelf.h"
#include "../builtin.h"
-#include "sane_ctype.h"
+#include <linux/ctype.h>
+#include <linux/zalloc.h>
struct jit_buf_desc {
struct perf_data *output;
@@ -117,13 +119,13 @@ jit_close(struct jit_buf_desc *jd)
static int
jit_validate_events(struct perf_session *session)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
/*
* check that all events use CLOCK_MONOTONIC
*/
evlist__for_each_entry(session->evlist, evsel) {
- if (evsel->attr.use_clockid == 0 || evsel->attr.clockid != CLOCK_MONOTONIC)
+ if (evsel->core.attr.use_clockid == 0 || evsel->core.attr.clockid != CLOCK_MONOTONIC)
return -1;
}
return 0;
@@ -431,14 +433,12 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
jd->unwinding_data, jd->eh_frame_hdr_size, jd->unwinding_size);
if (jd->debug_data && jd->nr_debug_entries) {
- free(jd->debug_data);
- jd->debug_data = NULL;
+ zfree(&jd->debug_data);
jd->nr_debug_entries = 0;
}
if (jd->unwinding_data && jd->eh_frame_hdr_size) {
- free(jd->unwinding_data);
- jd->unwinding_data = NULL;
+ zfree(&jd->unwinding_data);
jd->eh_frame_hdr_size = 0;
jd->unwinding_mapped_size = 0;
jd->unwinding_size = 0;
@@ -759,7 +759,7 @@ jit_process(struct perf_session *session,
pid_t pid,
u64 *nbytes)
{
- struct perf_evsel *first;
+ struct evsel *first;
struct jit_buf_desc jd;
int ret;
@@ -780,7 +780,7 @@ jit_process(struct perf_session *session,
* perf sets the same sample type to all events as of now
*/
first = perf_evlist__first(session->evlist);
- jd.sample_type = first->attr.sample_type;
+ jd.sample_type = first->core.attr.sample_type;
*nbytes = 0;
diff --git a/tools/perf/util/jitdump.h b/tools/perf/util/jitdump.h
index c6b9b67f43bf..f2c3823cc81a 100644
--- a/tools/perf/util/jitdump.h
+++ b/tools/perf/util/jitdump.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* jitdump.h: jitted code info encapsulation file format
*
diff --git a/tools/perf/util/kvm-stat.h b/tools/perf/util/kvm-stat.h
index 1403dec189b4..46913637085b 100644
--- a/tools/perf/util/kvm-stat.h
+++ b/tools/perf/util/kvm-stat.h
@@ -2,12 +2,12 @@
#ifndef __PERF_KVM_STAT_H
#define __PERF_KVM_STAT_H
-#include "../perf.h"
#include "tool.h"
#include "stat.h"
+#include "record.h"
-struct perf_evsel;
-struct perf_evlist;
+struct evsel;
+struct evlist;
struct perf_session;
struct event_key {
@@ -45,17 +45,17 @@ struct kvm_event_key {
struct perf_kvm_stat;
struct child_event_ops {
- void (*get_key)(struct perf_evsel *evsel,
+ void (*get_key)(struct evsel *evsel,
struct perf_sample *sample,
struct event_key *key);
const char *name;
};
struct kvm_events_ops {
- bool (*is_begin_event)(struct perf_evsel *evsel,
+ bool (*is_begin_event)(struct evsel *evsel,
struct perf_sample *sample,
struct event_key *key);
- bool (*is_end_event)(struct perf_evsel *evsel,
+ bool (*is_end_event)(struct evsel *evsel,
struct perf_sample *sample, struct event_key *key);
struct child_event_ops *child_ops;
void (*decode_key)(struct perf_kvm_stat *kvm, struct event_key *key,
@@ -74,7 +74,7 @@ struct exit_reasons_table {
struct perf_kvm_stat {
struct perf_tool tool;
struct record_opts opts;
- struct perf_evlist *evlist;
+ struct evlist *evlist;
struct perf_session *session;
const char *file_name;
@@ -109,21 +109,21 @@ struct kvm_reg_events_ops {
struct kvm_events_ops *ops;
};
-void exit_event_get_key(struct perf_evsel *evsel,
+void exit_event_get_key(struct evsel *evsel,
struct perf_sample *sample,
struct event_key *key);
-bool exit_event_begin(struct perf_evsel *evsel,
+bool exit_event_begin(struct evsel *evsel,
struct perf_sample *sample,
struct event_key *key);
-bool exit_event_end(struct perf_evsel *evsel,
+bool exit_event_end(struct evsel *evsel,
struct perf_sample *sample,
struct event_key *key);
void exit_event_decode_key(struct perf_kvm_stat *kvm,
struct event_key *key,
char *decode);
-bool kvm_exit_event(struct perf_evsel *evsel);
-bool kvm_entry_event(struct perf_evsel *evsel);
+bool kvm_exit_event(struct evsel *evsel);
+bool kvm_entry_event(struct evsel *evsel);
int setup_kvm_events_tp(struct perf_kvm_stat *kvm);
#define define_exit_reasons_table(name, symbols) \
diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
index 5b0b60f00275..55fb4b3b1157 100644
--- a/tools/perf/util/llvm-utils.c
+++ b/tools/perf/util/llvm-utils.c
@@ -9,6 +9,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/zalloc.h>
#include "debug.h"
#include "llvm-utils.h"
#include "config.h"
@@ -352,8 +354,7 @@ void llvm__get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts)
" \toption in [llvm] to \"\" to suppress this detection.\n\n",
*kbuild_dir);
- free(*kbuild_dir);
- *kbuild_dir = NULL;
+ zfree(kbuild_dir);
goto errout;
}
diff --git a/tools/perf/util/llvm-utils.h b/tools/perf/util/llvm-utils.h
index bf3f3f4c4fe2..7878a0e3fa98 100644
--- a/tools/perf/util/llvm-utils.h
+++ b/tools/perf/util/llvm-utils.h
@@ -6,7 +6,7 @@
#ifndef __LLVM_UTILS_H
#define __LLVM_UTILS_H
-#include "debug.h"
+#include <stdbool.h>
struct llvm_param {
/* Path of clang executable */
diff --git a/tools/perf/util/lzma.c b/tools/perf/util/lzma.c
index b1dd29a9d915..397447066033 100644
--- a/tools/perf/util/lzma.c
+++ b/tools/perf/util/lzma.c
@@ -9,6 +9,7 @@
#include "compress.h"
#include "util.h"
#include "debug.h"
+#include <string.h>
#include <unistd.h>
#define BUFSIZE 8192
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 28a9541c4835..b4749d3eed08 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -3,17 +3,26 @@
#include <errno.h>
#include <inttypes.h>
#include <regex.h>
+#include <stdlib.h>
#include "callchain.h"
#include "debug.h"
+#include "dso.h"
+#include "env.h"
#include "event.h"
#include "evsel.h"
#include "hist.h"
#include "machine.h"
#include "map.h"
+#include "map_symbol.h"
+#include "branch.h"
+#include "mem-events.h"
+#include "srcline.h"
#include "symbol.h"
#include "sort.h"
#include "strlist.h"
+#include "target.h"
#include "thread.h"
+#include "util.h"
#include "vdso.h"
#include <stdbool.h>
#include <sys/types.h>
@@ -24,9 +33,11 @@
#include "asm/bug.h"
#include "bpf-event.h"
-#include "sane_ctype.h"
+#include <linux/ctype.h>
#include <symbol/kallsyms.h>
#include <linux/mman.h>
+#include <linux/string.h>
+#include <linux/zalloc.h>
static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock);
@@ -209,6 +220,18 @@ void machine__exit(struct machine *machine)
for (i = 0; i < THREADS__TABLE_SIZE; i++) {
struct threads *threads = &machine->threads[i];
+ struct thread *thread, *n;
+ /*
+ * Forget about the dead, at this point whatever threads were
+ * left in the dead lists better have a reference count taken
+ * by who is using them, and then, when they drop those references
+ * and it finally hits zero, thread__put() will check and see that
+ * its not in the dead threads list and will not try to remove it
+ * from there, just calling thread__delete() straight away.
+ */
+ list_for_each_entry_safe(thread, n, &threads->dead, node)
+ list_del_init(&thread->node);
+
exit_rwsem(&threads->lock);
}
}
@@ -629,7 +652,7 @@ int machine__process_namespaces_event(struct machine *machine __maybe_unused,
int machine__process_lost_event(struct machine *machine __maybe_unused,
union perf_event *event, struct perf_sample *sample __maybe_unused)
{
- dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n",
+ dump_printf(": id:%" PRI_lu64 ": lost:%" PRI_lu64 "\n",
event->lost.id, event->lost.lost);
return 0;
}
@@ -637,7 +660,7 @@ int machine__process_lost_event(struct machine *machine __maybe_unused,
int machine__process_lost_samples_event(struct machine *machine __maybe_unused,
union perf_event *event, struct perf_sample *sample)
{
- dump_printf(": id:%" PRIu64 ": lost samples :%" PRIu64 "\n",
+ dump_printf(": id:%" PRIu64 ": lost samples :%" PRI_lu64 "\n",
sample->id, event->lost_samples.lost);
return 0;
}
@@ -697,20 +720,20 @@ static int machine__process_ksymbol_register(struct machine *machine,
struct symbol *sym;
struct map *map;
- map = map_groups__find(&machine->kmaps, event->ksymbol_event.addr);
+ map = map_groups__find(&machine->kmaps, event->ksymbol.addr);
if (!map) {
- map = dso__new_map(event->ksymbol_event.name);
+ map = dso__new_map(event->ksymbol.name);
if (!map)
return -ENOMEM;
- map->start = event->ksymbol_event.addr;
- map->pgoff = map->start;
- map->end = map->start + event->ksymbol_event.len;
+ map->start = event->ksymbol.addr;
+ map->end = map->start + event->ksymbol.len;
map_groups__insert(&machine->kmaps, map);
}
- sym = symbol__new(event->ksymbol_event.addr, event->ksymbol_event.len,
- 0, 0, event->ksymbol_event.name);
+ sym = symbol__new(map->map_ip(map, map->start),
+ event->ksymbol.len,
+ 0, 0, event->ksymbol.name);
if (!sym)
return -ENOMEM;
dso__insert_symbol(map->dso, sym);
@@ -723,7 +746,7 @@ static int machine__process_ksymbol_unregister(struct machine *machine,
{
struct map *map;
- map = map_groups__find(&machine->kmaps, event->ksymbol_event.addr);
+ map = map_groups__find(&machine->kmaps, event->ksymbol.addr);
if (map)
map_groups__remove(&machine->kmaps, map);
@@ -737,7 +760,7 @@ int machine__process_ksymbol(struct machine *machine __maybe_unused,
if (dump_trace)
perf_event__fprintf_ksymbol(event, stdout);
- if (event->ksymbol_event.flags & PERF_RECORD_KSYMBOL_FLAGS_UNREGISTER)
+ if (event->ksymbol.flags & PERF_RECORD_KSYMBOL_FLAGS_UNREGISTER)
return machine__process_ksymbol_unregister(machine, event,
sample);
return machine__process_ksymbol_register(machine, event, sample);
@@ -797,7 +820,7 @@ struct map *machine__findnew_module_map(struct machine *machine, u64 start,
out:
/* put the dso here, corresponding to machine__findnew_module_dso */
dso__put(dso);
- free(m.name);
+ zfree(&m.name);
return map;
}
@@ -924,7 +947,8 @@ const char *ref_reloc_sym_names[] = {"_text", "_stext", NULL};
* symbol_name if it's not that important.
*/
static int machine__get_running_kernel_start(struct machine *machine,
- const char **symbol_name, u64 *start)
+ const char **symbol_name,
+ u64 *start, u64 *end)
{
char filename[PATH_MAX];
int i, err = -1;
@@ -949,6 +973,11 @@ static int machine__get_running_kernel_start(struct machine *machine,
*symbol_name = name;
*start = addr;
+
+ err = kallsyms__get_function_start(filename, "_etext", &addr);
+ if (!err)
+ *end = addr;
+
return 0;
}
@@ -1235,9 +1264,9 @@ static char *get_kernel_version(const char *root_dir)
return NULL;
tmp = fgets(version, sizeof(version), file);
- if (!tmp)
- *version = '\0';
fclose(file);
+ if (!tmp)
+ return NULL;
name = strstr(version, prefix);
if (!name)
@@ -1331,7 +1360,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg,
if (m.kmod)
ret = map_groups__set_module_path(mg, path, &m);
- free(m.name);
+ zfree(&m.name);
if (ret)
goto out;
@@ -1359,6 +1388,7 @@ static int machine__set_modules_path(struct machine *machine)
return map_groups__set_modules_path_dir(&machine->kmaps, modules_path, 0);
}
int __weak arch__fix_module_text_start(u64 *start __maybe_unused,
+ u64 *size __maybe_unused,
const char *name __maybe_unused)
{
return 0;
@@ -1370,7 +1400,7 @@ static int machine__create_module(void *arg, const char *name, u64 start,
struct machine *machine = arg;
struct map *map;
- if (arch__fix_module_text_start(&start, name) < 0)
+ if (arch__fix_module_text_start(&start, &size, name) < 0)
return -1;
map = machine__findnew_module_map(machine, start, name);
@@ -1441,7 +1471,7 @@ int machine__create_kernel_maps(struct machine *machine)
struct dso *kernel = machine__get_kernel(machine);
const char *name = NULL;
struct map *map;
- u64 addr = 0;
+ u64 start = 0, end = ~0ULL;
int ret;
if (kernel == NULL)
@@ -1460,9 +1490,9 @@ int machine__create_kernel_maps(struct machine *machine)
"continuing anyway...\n", machine->pid);
}
- if (!machine__get_running_kernel_start(machine, &name, &addr)) {
+ if (!machine__get_running_kernel_start(machine, &name, &start, &end)) {
if (name &&
- map__set_kallsyms_ref_reloc_sym(machine->vmlinux_map, name, addr)) {
+ map__set_kallsyms_ref_reloc_sym(machine->vmlinux_map, name, start)) {
machine__destroy_kernel_maps(machine);
ret = -1;
goto out_put;
@@ -1472,16 +1502,19 @@ int machine__create_kernel_maps(struct machine *machine)
* we have a real start address now, so re-order the kmaps
* assume it's the last in the kmaps
*/
- machine__update_kernel_mmap(machine, addr, ~0ULL);
+ machine__update_kernel_mmap(machine, start, end);
}
if (machine__create_extra_kernel_maps(machine, kernel))
pr_debug("Problems creating extra kernel maps, continuing anyway...\n");
- /* update end address of the kernel map using adjacent module address */
- map = map__next(machine__kernel_map(machine));
- if (map)
- machine__set_kernel_mmap(machine, addr, map->start);
+ if (end == ~0ULL) {
+ /* update end address of the kernel map using adjacent module address */
+ map = map__next(machine__kernel_map(machine));
+ if (map)
+ machine__set_kernel_mmap(machine, start, map->start);
+ }
+
out_put:
dso__put(kernel);
return ret;
@@ -1749,9 +1782,11 @@ static void __machine__remove_thread(struct machine *machine, struct thread *th,
if (threads->last_match == th)
threads__set_last_match(threads, NULL);
- BUG_ON(refcount_read(&th->refcnt) == 0);
if (lock)
down_write(&threads->lock);
+
+ BUG_ON(refcount_read(&th->refcnt) == 0);
+
rb_erase_cached(&th->rb_node, &threads->entries);
RB_CLEAR_NODE(&th->rb_node);
--threads->nr;
@@ -1761,9 +1796,16 @@ static void __machine__remove_thread(struct machine *machine, struct thread *th,
* will be called and we will remove it from the dead_threads list.
*/
list_add_tail(&th->node, &threads->dead);
+
+ /*
+ * We need to do the put here because if this is the last refcount,
+ * then we will be touching the threads->dead head when removing the
+ * thread.
+ */
+ thread__put(th);
+
if (lock)
up_write(&threads->lock);
- thread__put(th);
}
void machine__remove_thread(struct machine *machine, struct thread *th)
@@ -1887,7 +1929,7 @@ int machine__process_event(struct machine *machine, union perf_event *event,
case PERF_RECORD_KSYMBOL:
ret = machine__process_ksymbol(machine, event, sample); break;
case PERF_RECORD_BPF_EVENT:
- ret = machine__process_bpf_event(machine, event, sample); break;
+ ret = machine__process_bpf(machine, event, sample); break;
default:
ret = -1;
break;
@@ -2256,7 +2298,7 @@ static int find_prev_cpumode(struct ip_callchain *chain, struct thread *thread,
static int thread__resolve_callchain_sample(struct thread *thread,
struct callchain_cursor *cursor,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct symbol **parent,
struct addr_location *root_al,
@@ -2462,13 +2504,13 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)
static int thread__resolve_callchain_unwind(struct thread *thread,
struct callchain_cursor *cursor,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
int max_stack)
{
/* Can we do dwarf post unwind? */
- if (!((evsel->attr.sample_type & PERF_SAMPLE_REGS_USER) &&
- (evsel->attr.sample_type & PERF_SAMPLE_STACK_USER)))
+ if (!((evsel->core.attr.sample_type & PERF_SAMPLE_REGS_USER) &&
+ (evsel->core.attr.sample_type & PERF_SAMPLE_STACK_USER)))
return 0;
/* Bail out if nothing was captured. */
@@ -2482,7 +2524,7 @@ static int thread__resolve_callchain_unwind(struct thread *thread,
int thread__resolve_callchain(struct thread *thread,
struct callchain_cursor *cursor,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct symbol **parent,
struct addr_location *root_al,
@@ -2568,7 +2610,7 @@ int machines__for_each_thread(struct machines *machines,
}
int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool,
- struct target *target, struct thread_map *threads,
+ struct target *target, struct perf_thread_map *threads,
perf_event__handler_t process, bool data_mmap,
unsigned int nr_threads_synthesize)
{
@@ -2584,7 +2626,9 @@ int __machine__synthesize_threads(struct machine *machine, struct perf_tool *too
pid_t machine__get_current_tid(struct machine *machine, int cpu)
{
- if (cpu < 0 || cpu >= MAX_NR_CPUS || !machine->current_tid)
+ int nr_cpus = min(machine->env->nr_cpus_online, MAX_NR_CPUS);
+
+ if (cpu < 0 || cpu >= nr_cpus || !machine->current_tid)
return -1;
return machine->current_tid[cpu];
@@ -2594,6 +2638,7 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid,
pid_t tid)
{
struct thread *thread;
+ int nr_cpus = min(machine->env->nr_cpus_online, MAX_NR_CPUS);
if (cpu < 0)
return -EINVAL;
@@ -2601,14 +2646,14 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid,
if (!machine->current_tid) {
int i;
- machine->current_tid = calloc(MAX_NR_CPUS, sizeof(pid_t));
+ machine->current_tid = calloc(nr_cpus, sizeof(pid_t));
if (!machine->current_tid)
return -ENOMEM;
- for (i = 0; i < MAX_NR_CPUS; i++)
+ for (i = 0; i < nr_cpus; i++)
machine->current_tid[i] = -1;
}
- if (cpu >= MAX_NR_CPUS) {
+ if (cpu >= nr_cpus) {
pr_err("Requested CPU %d too large. ", cpu);
pr_err("Consider raising MAX_NR_CPUS\n");
return -EINVAL;
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index f70ab98a7bde..ffd391a925a6 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -5,15 +5,17 @@
#include <sys/types.h>
#include <linux/rbtree.h>
#include "map_groups.h"
-#include "dso.h"
+#include "dsos.h"
#include "event.h"
#include "rwsem.h"
struct addr_location;
struct branch_stack;
-struct perf_evsel;
+struct dso;
+struct evsel;
struct perf_sample;
struct symbol;
+struct target;
struct thread;
union perf_event;
@@ -175,7 +177,7 @@ struct callchain_cursor;
int thread__resolve_callchain(struct thread *thread,
struct callchain_cursor *cursor,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct perf_sample *sample,
struct symbol **parent,
struct addr_location *root_al,
@@ -222,7 +224,7 @@ struct symbol *machine__find_kernel_symbol_by_name(struct machine *machine,
struct map *machine__findnew_module_map(struct machine *machine, u64 start,
const char *filename);
-int arch__fix_module_text_start(u64 *start, const char *name);
+int arch__fix_module_text_start(u64 *start, u64 *size, const char *name);
int machine__load_kallsyms(struct machine *machine, const char *filename);
@@ -251,12 +253,12 @@ int machines__for_each_thread(struct machines *machines,
void *priv);
int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool,
- struct target *target, struct thread_map *threads,
+ struct target *target, struct perf_thread_map *threads,
perf_event__handler_t process, bool data_mmap,
unsigned int nr_threads_synthesize);
static inline
int machine__synthesize_threads(struct machine *machine, struct target *target,
- struct thread_map *threads, bool data_mmap,
+ struct perf_thread_map *threads, bool data_mmap,
unsigned int nr_threads_synthesize)
{
return __machine__synthesize_threads(machine, NULL, target, threads,
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index ee71efb9db62..5b83ed1ebbd6 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -8,18 +8,21 @@
#include <stdio.h>
#include <unistd.h>
#include <uapi/linux/mman.h> /* To get things like MAP_HUGETLB even on older libc headers */
+#include "dso.h"
#include "map.h"
+#include "map_symbol.h"
#include "thread.h"
#include "vdso.h"
#include "build-id.h"
-#include "util.h"
#include "debug.h"
#include "machine.h"
#include <linux/string.h>
+#include <linux/zalloc.h>
#include "srcline.h"
#include "namespaces.h"
#include "unwind.h"
#include "srccode.h"
+#include "ui/ui.h"
static void __maps__insert(struct maps *maps, struct map *map);
static void __maps__insert_name(struct maps *maps, struct map *map);
@@ -405,6 +408,7 @@ size_t map__fprintf(struct map *map, FILE *fp)
size_t map__fprintf_dsoname(struct map *map, FILE *fp)
{
+ char buf[symbol_conf.pad_output_len_dso + 1];
const char *dsoname = "[unknown]";
if (map && map->dso) {
@@ -414,6 +418,11 @@ size_t map__fprintf_dsoname(struct map *map, FILE *fp)
dsoname = map->dso->name;
}
+ if (symbol_conf.pad_output_len_dso) {
+ scnprintf_pad(buf, symbol_conf.pad_output_len_dso, "%s", dsoname);
+ dsoname = buf;
+ }
+
return fprintf(fp, "%s", dsoname);
}
@@ -470,8 +479,11 @@ int map__fprintf_srccode(struct map *map, u64 addr,
goto out_free_line;
ret = fprintf(fp, "|%-8d %.*s", line, len, srccode);
- state->srcfile = srcfile;
- state->line = line;
+
+ if (state) {
+ state->srcfile = srcfile;
+ state->line = line;
+ }
return ret;
out_free_line:
@@ -627,7 +639,7 @@ bool map_groups__empty(struct map_groups *mg)
struct map_groups *map_groups__new(struct machine *machine)
{
- struct map_groups *mg = malloc(sizeof(*mg));
+ struct map_groups *mg = zalloc(sizeof(*mg));
if (mg != NULL)
map_groups__init(mg, machine);
@@ -638,6 +650,7 @@ struct map_groups *map_groups__new(struct machine *machine)
void map_groups__delete(struct map_groups *mg)
{
map_groups__exit(mg);
+ unwind__finish_access(mg);
free(mg);
}
@@ -878,7 +891,7 @@ int map_groups__clone(struct thread *thread, struct map_groups *parent)
if (new == NULL)
goto out_unlock;
- err = unwind__prepare_access(thread, new, NULL);
+ err = unwind__prepare_access(mg, new, NULL);
if (err)
goto out_unlock;
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index dc93787c74f0..c3614195ddc7 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -16,7 +16,7 @@ struct ip_callchain;
struct ref_reloc_sym;
struct map_groups;
struct machine;
-struct perf_evsel;
+struct evsel;
struct map {
union {
diff --git a/tools/perf/util/map_groups.h b/tools/perf/util/map_groups.h
index 4dcda33e0fdf..77252e14008f 100644
--- a/tools/perf/util/map_groups.h
+++ b/tools/perf/util/map_groups.h
@@ -31,6 +31,10 @@ struct map_groups {
struct maps maps;
struct machine *machine;
refcount_t refcnt;
+#ifdef HAVE_LIBUNWIND_SUPPORT
+ void *addr_space;
+ struct unwind_libunwind_ops *unwind_libunwind_ops;
+#endif
};
#define KMAP_NAME_LEN 256
@@ -88,4 +92,6 @@ int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, FILE
struct map *map_groups__find_by_name(struct map_groups *mg, const char *name);
+int map_groups__merge_in(struct map_groups *kmaps, struct map *new_map);
+
#endif // __PERF_MAP_GROUPS_H
diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c
index 42c3e5a229d2..3d391583f2ae 100644
--- a/tools/perf/util/mem-events.c
+++ b/tools/perf/util/mem-events.c
@@ -8,10 +8,10 @@
#include <unistd.h>
#include <api/fs/fs.h>
#include <linux/kernel.h>
+#include "map_symbol.h"
#include "mem-events.h"
#include "debug.h"
#include "symbol.h"
-#include "sort.h"
unsigned int perf_mem_events__loads_ldlat = 30;
diff --git a/tools/perf/util/mem-events.h b/tools/perf/util/mem-events.h
index a889ec2fa9f5..f1389bdae7bf 100644
--- a/tools/perf/util/mem-events.h
+++ b/tools/perf/util/mem-events.h
@@ -6,6 +6,8 @@
#include <stdint.h>
#include <stdio.h>
#include <linux/types.h>
+#include <linux/refcount.h>
+#include <linux/perf_event.h>
#include "stat.h"
struct perf_mem_event {
@@ -16,6 +18,13 @@ struct perf_mem_event {
const char *sysfs_name;
};
+struct mem_info {
+ struct addr_map_symbol iaddr;
+ struct addr_map_symbol daddr;
+ union perf_mem_data_src data_src;
+ refcount_t refcnt;
+};
+
enum {
PERF_MEM_EVENTS__LOAD,
PERF_MEM_EVENTS__STORE,
diff --git a/tools/perf/util/mem2node.c b/tools/perf/util/mem2node.c
index c6fd81c02586..797d86a1ab09 100644
--- a/tools/perf/util/mem2node.c
+++ b/tools/perf/util/mem2node.c
@@ -1,8 +1,11 @@
#include <errno.h>
#include <inttypes.h>
#include <linux/bitmap.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include "debug.h"
+#include "env.h"
#include "mem2node.h"
-#include "util.h"
struct phys_entry {
struct rb_node rb_node;
diff --git a/tools/perf/util/mem2node.h b/tools/perf/util/mem2node.h
index 59c4752a2181..8dfa2b58d0cd 100644
--- a/tools/perf/util/mem2node.h
+++ b/tools/perf/util/mem2node.h
@@ -2,8 +2,9 @@
#define __MEM2NODE_H
#include <linux/rbtree.h>
-#include "env.h"
+#include <linux/types.h>
+struct perf_env;
struct phys_entry;
struct mem2node {
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index b8d864ed4afe..a7c0424dbda3 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -1,35 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
/* Manage metrics and groups of metrics from JSON files */
#include "metricgroup.h"
+#include "debug.h"
#include "evlist.h"
+#include "evsel.h"
#include "strbuf.h"
#include "pmu.h"
#include "expr.h"
#include "rblist.h"
#include <string.h>
-#include <stdbool.h>
#include <errno.h>
#include "pmu-events/pmu-events.h"
#include "strlist.h"
#include <assert.h>
-#include <ctype.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/zalloc.h>
+#include <subcmd/parse-options.h>
struct metric_event *metricgroup__lookup(struct rblist *metric_events,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
bool create)
{
struct rb_node *nd;
@@ -92,40 +87,68 @@ struct egroup {
const char **ids;
const char *metric_name;
const char *metric_expr;
+ const char *metric_unit;
};
-static struct perf_evsel *find_evsel(struct perf_evlist *perf_evlist,
- const char **ids,
- int idnum,
- struct perf_evsel **metric_events)
+static struct evsel *find_evsel_group(struct evlist *perf_evlist,
+ const char **ids,
+ int idnum,
+ struct evsel **metric_events)
{
- struct perf_evsel *ev, *start = NULL;
- int ind = 0;
+ struct evsel *ev;
+ int i = 0;
+ bool leader_found;
evlist__for_each_entry (perf_evlist, ev) {
- if (!strcmp(ev->name, ids[ind])) {
- metric_events[ind] = ev;
- if (ind == 0)
- start = ev;
- if (++ind == idnum) {
- metric_events[ind] = NULL;
- return start;
- }
+ if (!strcmp(ev->name, ids[i])) {
+ if (!metric_events[i])
+ metric_events[i] = ev;
} else {
- ind = 0;
- start = NULL;
+ if (++i == idnum) {
+ /* Discard the whole match and start again */
+ i = 0;
+ memset(metric_events, 0,
+ sizeof(struct evsel *) * idnum);
+ continue;
+ }
+
+ if (!strcmp(ev->name, ids[i]))
+ metric_events[i] = ev;
+ else {
+ /* Discard the whole match and start again */
+ i = 0;
+ memset(metric_events, 0,
+ sizeof(struct evsel *) * idnum);
+ continue;
+ }
}
}
- /*
- * This can happen when an alias expands to multiple
- * events, like for uncore events.
- * We don't support this case for now.
- */
- return NULL;
+
+ if (i != idnum - 1) {
+ /* Not whole match */
+ return NULL;
+ }
+
+ metric_events[idnum] = NULL;
+
+ for (i = 0; i < idnum; i++) {
+ leader_found = false;
+ evlist__for_each_entry(perf_evlist, ev) {
+ if (!leader_found && (ev == metric_events[i]))
+ leader_found = true;
+
+ if (leader_found &&
+ !strcmp(ev->name, metric_events[i]->name)) {
+ ev->metric_leader = metric_events[i];
+ }
+ }
+ }
+
+ return metric_events[0];
}
static int metricgroup__setup_events(struct list_head *groups,
- struct perf_evlist *perf_evlist,
+ struct evlist *perf_evlist,
struct rblist *metric_events_list)
{
struct metric_event *me;
@@ -133,18 +156,18 @@ static int metricgroup__setup_events(struct list_head *groups,
int i = 0;
int ret = 0;
struct egroup *eg;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
list_for_each_entry (eg, groups, nd) {
- struct perf_evsel **metric_events;
+ struct evsel **metric_events;
metric_events = calloc(sizeof(void *), eg->idnum + 1);
if (!metric_events) {
ret = -ENOMEM;
break;
}
- evsel = find_evsel(perf_evlist, eg->ids, eg->idnum,
- metric_events);
+ evsel = find_evsel_group(perf_evlist, eg->ids, eg->idnum,
+ metric_events);
if (!evsel) {
pr_debug("Cannot resolve %s: %s\n",
eg->metric_name, eg->metric_expr);
@@ -164,6 +187,7 @@ static int metricgroup__setup_events(struct list_head *groups,
}
expr->metric_expr = eg->metric_expr;
expr->metric_name = eg->metric_name;
+ expr->metric_unit = eg->metric_unit;
expr->metric_events = metric_events;
list_add(&expr->nd, &me->head);
}
@@ -221,7 +245,7 @@ static struct rb_node *mep_new(struct rblist *rl __maybe_unused,
goto out_name;
return &me->nd;
out_name:
- free((char *)me->name);
+ zfree(&me->name);
out_me:
free(me);
return NULL;
@@ -249,7 +273,7 @@ static void mep_delete(struct rblist *rl __maybe_unused,
struct mep *me = container_of(nd, struct mep, nd);
strlist__delete(me->metrics);
- free((void *)me->name);
+ zfree(&me->name);
free(me);
}
@@ -317,10 +341,9 @@ void metricgroup__print(bool metrics, bool metricgroups, char *filter,
struct mep *me;
char *s;
+ g = skip_spaces(g);
if (*g == 0)
g = "No_group";
- while (isspace(*g))
- g++;
if (filter && !strstr(g, filter))
continue;
if (raw)
@@ -362,7 +385,7 @@ void metricgroup__print(bool metrics, bool metricgroups, char *filter,
struct mep *me = container_of(node, struct mep, nd);
if (metricgroups)
- printf("%s%s%s", me->name, metrics ? ":" : "", raw ? " " : "\n");
+ printf("%s%s%s", me->name, metrics && !raw ? ":" : "", raw ? " " : "\n");
if (metrics)
metricgroup__print_strlist(me->metrics, raw);
next = rb_next(node);
@@ -396,6 +419,7 @@ static int metricgroup__add_metric(const char *metric, struct strbuf *events,
const char **ids;
int idnum;
struct egroup *eg;
+ bool no_group = false;
pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name);
@@ -406,11 +430,25 @@ static int metricgroup__add_metric(const char *metric, struct strbuf *events,
strbuf_addf(events, ",");
for (j = 0; j < idnum; j++) {
pr_debug("found event %s\n", ids[j]);
+ /*
+ * Duration time maps to a software event and can make
+ * groups not count. Always use it outside a
+ * group.
+ */
+ if (!strcmp(ids[j], "duration_time")) {
+ if (j > 0)
+ strbuf_addf(events, "}:W,");
+ strbuf_addf(events, "duration_time");
+ no_group = true;
+ continue;
+ }
strbuf_addf(events, "%s%s",
- j == 0 ? "{" : ",",
+ j == 0 || no_group ? "{" : ",",
ids[j]);
+ no_group = false;
}
- strbuf_addf(events, "}:W");
+ if (!no_group)
+ strbuf_addf(events, "}:W");
eg = malloc(sizeof(struct egroup));
if (!eg) {
@@ -421,6 +459,7 @@ static int metricgroup__add_metric(const char *metric, struct strbuf *events,
eg->idnum = idnum;
eg->metric_name = pe->metric_name;
eg->metric_expr = pe->metric_expr;
+ eg->metric_unit = pe->unit;
list_add_tail(&eg->nd, group_list);
ret = 0;
}
@@ -461,8 +500,9 @@ static void metricgroup__free_egroups(struct list_head *group_list)
list_for_each_entry_safe (eg, egtmp, group_list, nd) {
for (i = 0; i < eg->idnum; i++)
- free((char *)eg->ids[i]);
- free(eg->ids);
+ zfree(&eg->ids[i]);
+ zfree(&eg->ids);
+ list_del_init(&eg->nd);
free(eg);
}
}
@@ -472,7 +512,7 @@ int metricgroup__parse_groups(const struct option *opt,
struct rblist *metric_events)
{
struct parse_events_error parse_error;
- struct perf_evlist *perf_evlist = *(struct perf_evlist **)opt->value;
+ struct evlist *perf_evlist = *(struct evlist **)opt->value;
struct strbuf extra_events;
LIST_HEAD(group_list);
int ret;
diff --git a/tools/perf/util/metricgroup.h b/tools/perf/util/metricgroup.h
index 5c52097a5c63..475c7f912864 100644
--- a/tools/perf/util/metricgroup.h
+++ b/tools/perf/util/metricgroup.h
@@ -1,15 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-only
#ifndef METRICGROUP_H
#define METRICGROUP_H 1
-#include "linux/list.h"
-#include "rblist.h"
-#include <subcmd/parse-options.h>
-#include "evlist.h"
-#include "strbuf.h"
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <stdbool.h>
+
+struct evsel;
+struct option;
+struct rblist;
struct metric_event {
struct rb_node nd;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct list_head head; /* list of metric_expr */
};
@@ -17,11 +20,12 @@ struct metric_expr {
struct list_head nd;
const char *metric_expr;
const char *metric_name;
- struct perf_evsel **metric_events;
+ const char *metric_unit;
+ struct evsel **metric_events;
};
struct metric_event *metricgroup__lookup(struct rblist *metric_events,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
bool create);
int metricgroup__parse_groups(const struct option *opt,
const char *str,
diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c
index 868c0b0e909c..33c5b5495482 100644
--- a/tools/perf/util/mmap.c
+++ b/tools/perf/util/mmap.c
@@ -1,21 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011-2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
*
* Parts came from evlist.c builtin-{top,stat,record}.c, see those files for further
* copyright notes.
- *
- * Released under the GPL v2. (and only v2, not any later version)
*/
#include <sys/mman.h>
#include <inttypes.h>
#include <asm/bug.h>
+#include <linux/zalloc.h>
+#include <stdlib.h>
+#include <string.h>
#ifdef HAVE_LIBNUMA_SUPPORT
#include <numaif.h>
#endif
+#include "cpumap.h"
#include "debug.h"
#include "event.h"
#include "mmap.h"
+#include "../perf.h"
#include "util.h" /* page_size */
size_t perf_mmap__mmap_len(struct perf_mmap *map)
@@ -150,7 +154,7 @@ void __weak auxtrace_mmap_params__init(struct auxtrace_mmap_params *mp __maybe_u
}
void __weak auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp __maybe_unused,
- struct perf_evlist *evlist __maybe_unused,
+ struct evlist *evlist __maybe_unused,
int idx __maybe_unused,
bool per_cpu __maybe_unused)
{
@@ -325,13 +329,13 @@ void perf_mmap__munmap(struct perf_mmap *map)
static void build_node_mask(int node, cpu_set_t *mask)
{
int c, cpu, nr_cpus;
- const struct cpu_map *cpu_map = NULL;
+ const struct perf_cpu_map *cpu_map = NULL;
cpu_map = cpu_map__online();
if (!cpu_map)
return;
- nr_cpus = cpu_map__nr(cpu_map);
+ nr_cpus = perf_cpu_map__nr(cpu_map);
for (c = 0; c < nr_cpus; c++) {
cpu = cpu_map->map[c]; /* map c index to online cpu index */
if (cpu__get_node(cpu) == node)
diff --git a/tools/perf/util/mmap.h b/tools/perf/util/mmap.h
index 274ce389cd84..3857a49e8f96 100644
--- a/tools/perf/util/mmap.h
+++ b/tools/perf/util/mmap.h
@@ -6,6 +6,7 @@
#include <linux/types.h>
#include <linux/ring_buffer.h>
#include <stdbool.h>
+#include <pthread.h> // for cpu_set_t
#ifdef HAVE_AIO_SUPPORT
#include <aio.h>
#endif
diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
index aed170bd4384..99be15dd2b6b 100644
--- a/tools/perf/util/namespaces.c
+++ b/tools/perf/util/namespaces.c
@@ -1,14 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2, as
- * published by the Free Software Foundation.
*
* Copyright (C) 2017 Hari Bathini, IBM Corporation
*/
#include "namespaces.h"
-#include "util.h"
#include "event.h"
+#include "get_current_dir_name.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -19,8 +17,9 @@
#include <string.h>
#include <unistd.h>
#include <asm/bug.h>
+#include <linux/zalloc.h>
-struct namespaces *namespaces__new(struct namespaces_event *event)
+struct namespaces *namespaces__new(struct perf_record_namespaces *event)
{
struct namespaces *namespaces;
u64 link_info_size = ((event ? event->nr_namespaces : NR_NAMESPACES) *
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
index d5f46c09ea31..40edef56cb52 100644
--- a/tools/perf/util/namespaces.h
+++ b/tools/perf/util/namespaces.h
@@ -1,7 +1,5 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2, as
- * published by the Free Software Foundation.
*
* Copyright (C) 2017 Hari Bathini, IBM Corporation
*/
@@ -15,7 +13,11 @@
#include <linux/refcount.h>
#include <linux/types.h>
-struct namespaces_event;
+#ifndef HAVE_SETNS_SUPPORT
+int setns(int fd, int nstype);
+#endif
+
+struct perf_record_namespaces;
struct namespaces {
struct list_head list;
@@ -23,7 +25,7 @@ struct namespaces {
struct perf_ns_link_info link_info[];
};
-struct namespaces *namespaces__new(struct namespaces_event *event);
+struct namespaces *namespaces__new(struct perf_record_namespaces *event);
void namespaces__free(struct namespaces *namespaces);
struct nsinfo {
diff --git a/tools/perf/util/ordered-events.c b/tools/perf/util/ordered-events.c
index 989fed6f43b5..359db2b1fcef 100644
--- a/tools/perf/util/ordered-events.c
+++ b/tools/perf/util/ordered-events.c
@@ -8,6 +8,7 @@
#include "session.h"
#include "asm/bug.h"
#include "debug.h"
+#include "ui/progress.h"
#define pr_N(n, fmt, ...) \
eprintf(n, debug_ordered_events, fmt, ##__VA_ARGS__)
@@ -138,7 +139,7 @@ static struct ordered_event *alloc_event(struct ordered_events *oe,
if (!list_empty(cache)) {
new = list_entry(cache->next, struct ordered_event, list);
- list_del(&new->list);
+ list_del_init(&new->list);
} else if (oe->buffer) {
new = &oe->buffer->event[oe->buffer_idx];
if (++oe->buffer_idx == MAX_SAMPLE_BUFFER)
@@ -394,13 +395,13 @@ void ordered_events__free(struct ordered_events *oe)
* yet, we need to free only allocated ones ...
*/
if (oe->buffer) {
- list_del(&oe->buffer->list);
+ list_del_init(&oe->buffer->list);
ordered_events_buffer__free(oe->buffer, oe->buffer_idx, oe);
}
/* ... and continue with the rest */
list_for_each_entry_safe(buffer, tmp, &oe->to_free, list) {
- list_del(&buffer->list);
+ list_del_init(&buffer->list);
ordered_events_buffer__free(buffer, MAX_SAMPLE_BUFFER, oe);
}
}
diff --git a/tools/perf/util/parse-branch-options.c b/tools/perf/util/parse-branch-options.c
index bd779d9f4d1e..bb4aa88c50a8 100644
--- a/tools/perf/util/parse-branch-options.c
+++ b/tools/perf/util/parse-branch-options.c
@@ -1,9 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
-#include "perf.h"
-#include "util/util.h"
#include "util/debug.h"
+#include "util/event.h"
#include <subcmd/parse-options.h>
#include "util/parse-branch-options.h"
+#include <stdlib.h>
+#include <string.h>
#define BRANCH_OPT(n, m) \
{ .name = n, .mode = (m) }
@@ -30,6 +31,7 @@ static const struct branch_mode branch_modes[] = {
BRANCH_OPT("ind_jmp", PERF_SAMPLE_BRANCH_IND_JUMP),
BRANCH_OPT("call", PERF_SAMPLE_BRANCH_CALL),
BRANCH_OPT("save_type", PERF_SAMPLE_BRANCH_TYPE_SAVE),
+ BRANCH_OPT("stack", PERF_SAMPLE_BRANCH_CALL_STACK),
BRANCH_END
};
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index cf0b9b81c5aa..5ec21d21113c 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/hw_breakpoint.h>
#include <linux/err.h>
+#include <linux/zalloc.h>
#include <dirent.h>
#include <errno.h>
#include <sys/ioctl.h>
@@ -9,20 +10,21 @@
#include <fcntl.h>
#include <sys/param.h>
#include "term.h"
-#include "../perf.h"
+#include "build-id.h"
#include "evlist.h"
#include "evsel.h"
+#include <subcmd/pager.h>
#include <subcmd/parse-options.h>
#include "parse-events.h"
#include <subcmd/exec-cmd.h>
#include "string2.h"
#include "strlist.h"
#include "symbol.h"
-#include "cache.h"
#include "header.h"
#include "bpf-loader.h"
#include "debug.h"
#include <api/fs/tracing_path.h>
+#include <perf/cpumap.h>
#include "parse-events-bison.h"
#define YY_EXTRA_TYPE int
#include "parse-events-flex.h"
@@ -313,16 +315,16 @@ static char *get_config_name(struct list_head *head_terms)
return NULL;
}
-static struct perf_evsel *
+static struct evsel *
__add_event(struct list_head *list, int *idx,
struct perf_event_attr *attr,
char *name, struct perf_pmu *pmu,
struct list_head *config_terms, bool auto_merge_stats,
const char *cpu_list)
{
- struct perf_evsel *evsel;
- struct cpu_map *cpus = pmu ? pmu->cpus :
- cpu_list ? cpu_map__new(cpu_list) : NULL;
+ struct evsel *evsel;
+ struct perf_cpu_map *cpus = pmu ? pmu->cpus :
+ cpu_list ? perf_cpu_map__new(cpu_list) : NULL;
event_attr_init(attr);
@@ -331,8 +333,8 @@ __add_event(struct list_head *list, int *idx,
return NULL;
(*idx)++;
- evsel->cpus = cpu_map__get(cpus);
- evsel->own_cpus = cpu_map__get(cpus);
+ evsel->core.cpus = perf_cpu_map__get(cpus);
+ evsel->core.own_cpus = perf_cpu_map__get(cpus);
evsel->system_wide = pmu ? pmu->is_uncore : false;
evsel->auto_merge_stats = auto_merge_stats;
@@ -342,7 +344,7 @@ __add_event(struct list_head *list, int *idx,
if (config_terms)
list_splice(config_terms, &evsel->config_terms);
- list_add_tail(&evsel->node, list);
+ list_add_tail(&evsel->core.node, list);
return evsel;
}
@@ -356,7 +358,7 @@ static int add_event(struct list_head *list, int *idx,
static int add_event_tool(struct list_head *list, int *idx,
enum perf_tool_event tool_event)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct perf_event_attr attr = {
.type = PERF_TYPE_SOFTWARE,
.config = PERF_COUNT_SW_DUMMY,
@@ -509,7 +511,7 @@ static int add_tracepoint(struct list_head *list, int *idx,
struct parse_events_error *err,
struct list_head *head_config)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evsel = perf_evsel__newtp_idx(sys_name, evt_name, (*idx)++);
if (IS_ERR(evsel)) {
@@ -525,7 +527,7 @@ static int add_tracepoint(struct list_head *list, int *idx,
list_splice(&config_terms, &evsel->config_terms);
}
- list_add_tail(&evsel->node, list);
+ list_add_tail(&evsel->core.node, list);
return 0;
}
@@ -629,15 +631,24 @@ struct __add_bpf_event_param {
struct list_head *head_config;
};
-static int add_bpf_event(const char *group, const char *event, int fd,
+static int add_bpf_event(const char *group, const char *event, int fd, struct bpf_object *obj,
void *_param)
{
LIST_HEAD(new_evsels);
struct __add_bpf_event_param *param = _param;
struct parse_events_state *parse_state = param->parse_state;
struct list_head *list = param->list;
- struct perf_evsel *pos;
+ struct evsel *pos;
int err;
+ /*
+ * Check if we should add the event, i.e. if it is a TP but starts with a '!',
+ * then don't add the tracepoint, this will be used for something else, like
+ * adding to a BPF_MAP_TYPE_PROG_ARRAY.
+ *
+ * See tools/perf/examples/bpf/augmented_raw_syscalls.c
+ */
+ if (group[0] == '!')
+ return 0;
pr_debug("add bpf event %s:%s and attach bpf program %d\n",
group, event, fd);
@@ -646,22 +657,23 @@ static int add_bpf_event(const char *group, const char *event, int fd,
event, parse_state->error,
param->head_config);
if (err) {
- struct perf_evsel *evsel, *tmp;
+ struct evsel *evsel, *tmp;
pr_debug("Failed to add BPF event %s:%s\n",
group, event);
- list_for_each_entry_safe(evsel, tmp, &new_evsels, node) {
- list_del(&evsel->node);
- perf_evsel__delete(evsel);
+ list_for_each_entry_safe(evsel, tmp, &new_evsels, core.node) {
+ list_del_init(&evsel->core.node);
+ evsel__delete(evsel);
}
return err;
}
pr_debug("adding %s:%s\n", group, event);
- list_for_each_entry(pos, &new_evsels, node) {
+ list_for_each_entry(pos, &new_evsels, core.node) {
pr_debug("adding %s:%s to %p\n",
group, event, pos);
pos->bpf_fd = fd;
+ pos->bpf_obj = obj;
}
list_splice(&new_evsels, list);
return 0;
@@ -951,6 +963,7 @@ static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = {
[PARSE_EVENTS__TERM_TYPE_NOOVERWRITE] = "no-overwrite",
[PARSE_EVENTS__TERM_TYPE_DRV_CFG] = "driver-config",
[PARSE_EVENTS__TERM_TYPE_PERCORE] = "percore",
+ [PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT] = "aux-output",
};
static bool config_term_shrinked;
@@ -1071,6 +1084,9 @@ do { \
return -EINVAL;
}
break;
+ case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT:
+ CHECK_TYPE_VAL(NUM);
+ break;
default:
err->str = strdup("unknown term");
err->idx = term->err_term;
@@ -1121,6 +1137,7 @@ static int config_term_tracepoint(struct perf_event_attr *attr,
case PARSE_EVENTS__TERM_TYPE_MAX_EVENTS:
case PARSE_EVENTS__TERM_TYPE_OVERWRITE:
case PARSE_EVENTS__TERM_TYPE_NOOVERWRITE:
+ case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT:
return config_term_common(attr, term, err);
default:
if (err) {
@@ -1213,6 +1230,9 @@ do { \
ADD_CONFIG_TERM(PERCORE, percore,
term->val.num ? true : false);
break;
+ case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT:
+ ADD_CONFIG_TERM(AUX_OUTPUT, aux_output, term->val.num ? 1 : 0);
+ break;
default:
break;
}
@@ -1295,7 +1315,7 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
struct perf_event_attr attr;
struct perf_pmu_info info;
struct perf_pmu *pmu;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct parse_events_error *err = parse_state->error;
bool use_uncore_alias;
LIST_HEAD(config_terms);
@@ -1442,13 +1462,13 @@ static int
parse_events__set_leader_for_uncore_aliase(char *name, struct list_head *list,
struct parse_events_state *parse_state)
{
- struct perf_evsel *evsel, *leader;
+ struct evsel *evsel, *leader;
uintptr_t *leaders;
bool is_leader = true;
int i, nr_pmu = 0, total_members, ret = 0;
- leader = list_first_entry(list, struct perf_evsel, node);
- evsel = list_last_entry(list, struct perf_evsel, node);
+ leader = list_first_entry(list, struct evsel, core.node);
+ evsel = list_last_entry(list, struct evsel, core.node);
total_members = evsel->idx - leader->idx + 1;
leaders = calloc(total_members, sizeof(uintptr_t));
@@ -1510,13 +1530,13 @@ parse_events__set_leader_for_uncore_aliase(char *name, struct list_head *list,
__evlist__for_each_entry(list, evsel) {
if (i >= nr_pmu)
i = 0;
- evsel->leader = (struct perf_evsel *) leaders[i++];
+ evsel->leader = (struct evsel *) leaders[i++];
}
/* The number of members and group name are same for each group */
for (i = 0; i < nr_pmu; i++) {
- evsel = (struct perf_evsel *) leaders[i];
- evsel->nr_members = total_members / nr_pmu;
+ evsel = (struct evsel *) leaders[i];
+ evsel->core.nr_members = total_members / nr_pmu;
evsel->group_name = name ? strdup(name) : NULL;
}
@@ -1533,7 +1553,7 @@ out:
void parse_events__set_leader(char *name, struct list_head *list,
struct parse_events_state *parse_state)
{
- struct perf_evsel *leader;
+ struct evsel *leader;
if (list_empty(list)) {
WARN_ONCE(true, "WARNING: failed to set leader: empty list");
@@ -1544,7 +1564,7 @@ void parse_events__set_leader(char *name, struct list_head *list,
return;
__perf_evlist__set_leader(list);
- leader = list_entry(list->next, struct perf_evsel, node);
+ leader = list_entry(list->next, struct evsel, core.node);
leader->group_name = name ? strdup(name) : NULL;
}
@@ -1577,18 +1597,18 @@ struct event_modifier {
};
static int get_event_modifier(struct event_modifier *mod, char *str,
- struct perf_evsel *evsel)
-{
- int eu = evsel ? evsel->attr.exclude_user : 0;
- int ek = evsel ? evsel->attr.exclude_kernel : 0;
- int eh = evsel ? evsel->attr.exclude_hv : 0;
- int eH = evsel ? evsel->attr.exclude_host : 0;
- int eG = evsel ? evsel->attr.exclude_guest : 0;
- int eI = evsel ? evsel->attr.exclude_idle : 0;
- int precise = evsel ? evsel->attr.precise_ip : 0;
+ struct evsel *evsel)
+{
+ int eu = evsel ? evsel->core.attr.exclude_user : 0;
+ int ek = evsel ? evsel->core.attr.exclude_kernel : 0;
+ int eh = evsel ? evsel->core.attr.exclude_hv : 0;
+ int eH = evsel ? evsel->core.attr.exclude_host : 0;
+ int eG = evsel ? evsel->core.attr.exclude_guest : 0;
+ int eI = evsel ? evsel->core.attr.exclude_idle : 0;
+ int precise = evsel ? evsel->core.attr.precise_ip : 0;
int precise_max = 0;
int sample_read = 0;
- int pinned = evsel ? evsel->attr.pinned : 0;
+ int pinned = evsel ? evsel->core.attr.pinned : 0;
int exclude = eu | ek | eh;
int exclude_GH = evsel ? evsel->exclude_GH : 0;
@@ -1690,7 +1710,7 @@ static int check_modifier(char *str)
int parse_events__modifier_event(struct list_head *list, char *str, bool add)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct event_modifier mod;
if (str == NULL)
@@ -1706,20 +1726,20 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add)
if (add && get_event_modifier(&mod, str, evsel))
return -EINVAL;
- evsel->attr.exclude_user = mod.eu;
- evsel->attr.exclude_kernel = mod.ek;
- evsel->attr.exclude_hv = mod.eh;
- evsel->attr.precise_ip = mod.precise;
- evsel->attr.exclude_host = mod.eH;
- evsel->attr.exclude_guest = mod.eG;
- evsel->attr.exclude_idle = mod.eI;
+ evsel->core.attr.exclude_user = mod.eu;
+ evsel->core.attr.exclude_kernel = mod.ek;
+ evsel->core.attr.exclude_hv = mod.eh;
+ evsel->core.attr.precise_ip = mod.precise;
+ evsel->core.attr.exclude_host = mod.eH;
+ evsel->core.attr.exclude_guest = mod.eG;
+ evsel->core.attr.exclude_idle = mod.eI;
evsel->exclude_GH = mod.exclude_GH;
evsel->sample_read = mod.sample_read;
evsel->precise_max = mod.precise_max;
evsel->weak_group = mod.weak;
if (perf_evsel__is_group_leader(evsel))
- evsel->attr.pinned = mod.pinned;
+ evsel->core.attr.pinned = mod.pinned;
}
return 0;
@@ -1727,7 +1747,7 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add)
int parse_events_name(struct list_head *list, char *name)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
__evlist__for_each_entry(list, evsel) {
if (!evsel->name)
@@ -1893,12 +1913,12 @@ int parse_events_terms(struct list_head *terms, const char *str)
return ret;
}
-int parse_events(struct perf_evlist *evlist, const char *str,
+int parse_events(struct evlist *evlist, const char *str,
struct parse_events_error *err)
{
struct parse_events_state parse_state = {
.list = LIST_HEAD_INIT(parse_state.list),
- .idx = evlist->nr_entries,
+ .idx = evlist->core.nr_entries,
.error = err,
.evlist = evlist,
};
@@ -1907,7 +1927,7 @@ int parse_events(struct perf_evlist *evlist, const char *str,
ret = parse_events__scanner(str, &parse_state, PE_START_EVENTS);
perf_pmu__parse_cleanup();
if (!ret) {
- struct perf_evsel *last;
+ struct evsel *last;
if (list_empty(&parse_state.list)) {
WARN_ONCE(true, "WARNING: event parser found nothing\n");
@@ -1924,7 +1944,7 @@ int parse_events(struct perf_evlist *evlist, const char *str,
/*
* There are 2 users - builtin-record and builtin-test objects.
- * Both call perf_evlist__delete in case of error, so we dont
+ * Both call evlist__delete in case of error, so we dont
* need to bother.
*/
return ret;
@@ -2002,7 +2022,7 @@ void parse_events_print_error(struct parse_events_error *err,
int parse_events_option(const struct option *opt, const char *str,
int unset __maybe_unused)
{
- struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
+ struct evlist *evlist = *(struct evlist **)opt->value;
struct parse_events_error err = { .idx = 0, };
int ret = parse_events(evlist, str, &err);
@@ -2015,12 +2035,12 @@ int parse_events_option(const struct option *opt, const char *str,
}
static int
-foreach_evsel_in_last_glob(struct perf_evlist *evlist,
- int (*func)(struct perf_evsel *evsel,
+foreach_evsel_in_last_glob(struct evlist *evlist,
+ int (*func)(struct evsel *evsel,
const void *arg),
const void *arg)
{
- struct perf_evsel *last = NULL;
+ struct evsel *last = NULL;
int err;
/*
@@ -2029,7 +2049,7 @@ foreach_evsel_in_last_glob(struct perf_evlist *evlist,
*
* So no need to WARN here, let *func do this.
*/
- if (evlist->nr_entries > 0)
+ if (evlist->core.nr_entries > 0)
last = perf_evlist__last(evlist);
do {
@@ -2039,15 +2059,15 @@ foreach_evsel_in_last_glob(struct perf_evlist *evlist,
if (!last)
return 0;
- if (last->node.prev == &evlist->entries)
+ if (last->core.node.prev == &evlist->core.entries)
return 0;
- last = list_entry(last->node.prev, struct perf_evsel, node);
+ last = list_entry(last->core.node.prev, struct evsel, core.node);
} while (!last->cmdline_group_boundary);
return 0;
}
-static int set_filter(struct perf_evsel *evsel, const void *arg)
+static int set_filter(struct evsel *evsel, const void *arg)
{
const char *str = arg;
bool found = false;
@@ -2060,7 +2080,7 @@ static int set_filter(struct perf_evsel *evsel, const void *arg)
return -1;
}
- if (evsel->attr.type == PERF_TYPE_TRACEPOINT) {
+ if (evsel->core.attr.type == PERF_TYPE_TRACEPOINT) {
if (perf_evsel__append_tp_filter(evsel, str) < 0) {
fprintf(stderr,
"not enough memory to hold filter string\n");
@@ -2071,7 +2091,7 @@ static int set_filter(struct perf_evsel *evsel, const void *arg)
}
while ((pmu = perf_pmu__scan(pmu)) != NULL)
- if (pmu->type == evsel->attr.type) {
+ if (pmu->type == evsel->core.attr.type) {
found = true;
break;
}
@@ -2098,18 +2118,18 @@ static int set_filter(struct perf_evsel *evsel, const void *arg)
int parse_filter(const struct option *opt, const char *str,
int unset __maybe_unused)
{
- struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
+ struct evlist *evlist = *(struct evlist **)opt->value;
return foreach_evsel_in_last_glob(evlist, set_filter,
(const void *)str);
}
-static int add_exclude_perf_filter(struct perf_evsel *evsel,
+static int add_exclude_perf_filter(struct evsel *evsel,
const void *arg __maybe_unused)
{
char new_filter[64];
- if (evsel == NULL || evsel->attr.type != PERF_TYPE_TRACEPOINT) {
+ if (evsel == NULL || evsel->core.attr.type != PERF_TYPE_TRACEPOINT) {
fprintf(stderr,
"--exclude-perf option should follow a -e tracepoint option\n");
return -1;
@@ -2130,7 +2150,7 @@ int exclude_perf(const struct option *opt,
const char *arg __maybe_unused,
int unset __maybe_unused)
{
- struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
+ struct evlist *evlist = *(struct evlist **)opt->value;
return foreach_evsel_in_last_glob(evlist, add_exclude_perf_filter,
NULL);
@@ -2296,20 +2316,20 @@ static bool is_event_supported(u8 type, unsigned config)
{
bool ret = true;
int open_return;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct perf_event_attr attr = {
.type = type,
.config = config,
.disabled = 1,
};
- struct thread_map *tmap = thread_map__new_by_tid(0);
+ struct perf_thread_map *tmap = thread_map__new_by_tid(0);
if (tmap == NULL)
return false;
- evsel = perf_evsel__new(&attr);
+ evsel = evsel__new(&attr);
if (evsel) {
- open_return = perf_evsel__open(evsel, NULL, tmap);
+ open_return = evsel__open(evsel, NULL, tmap);
ret = open_return >= 0;
if (open_return == -EACCES) {
@@ -2320,13 +2340,13 @@ static bool is_event_supported(u8 type, unsigned config)
* by default as some ARM machines do not support it.
*
*/
- evsel->attr.exclude_kernel = 1;
- ret = perf_evsel__open(evsel, NULL, tmap) >= 0;
+ evsel->core.attr.exclude_kernel = 1;
+ ret = evsel__open(evsel, NULL, tmap) >= 0;
}
- perf_evsel__delete(evsel);
+ evsel__delete(evsel);
}
- thread_map__put(tmap);
+ perf_thread_map__put(tmap);
return ret;
}
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index f7139e1a2fd3..616ca1eda0eb 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -12,8 +12,8 @@
#include <string.h>
struct list_head;
-struct perf_evsel;
-struct perf_evlist;
+struct evsel;
+struct evlist;
struct parse_events_error;
struct option;
@@ -31,7 +31,7 @@ bool have_tracepoints(struct list_head *evlist);
const char *event_type(int type);
int parse_events_option(const struct option *opt, const char *str, int unset);
-int parse_events(struct perf_evlist *evlist, const char *str,
+int parse_events(struct evlist *evlist, const char *str,
struct parse_events_error *error);
int parse_events_terms(struct list_head *terms, const char *str);
int parse_filter(const struct option *opt, const char *str, int unset);
@@ -76,6 +76,7 @@ enum {
PARSE_EVENTS__TERM_TYPE_OVERWRITE,
PARSE_EVENTS__TERM_TYPE_DRV_CFG,
PARSE_EVENTS__TERM_TYPE_PERCORE,
+ PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT,
__PARSE_EVENTS__TERM_TYPE_NR,
};
@@ -119,7 +120,7 @@ struct parse_events_state {
int idx;
int nr_groups;
struct parse_events_error *error;
- struct perf_evlist *evlist;
+ struct evlist *evlist;
struct list_head *terms;
};
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index ca6098874fe2..7469497cd28e 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -284,6 +284,7 @@ no-inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOINHERIT); }
overwrite { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_OVERWRITE); }
no-overwrite { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOOVERWRITE); }
percore { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_PERCORE); }
+aux-output { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT); }
, { return ','; }
"/" { BEGIN(INITIAL); return '/'; }
{name_minus} { return str(yyscanner, PE_NAME); }
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 6ad8d4914969..f1c36ed1cf36 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -480,7 +480,6 @@ event_bpf_file:
PE_BPF_OBJECT opt_event_config
{
struct parse_events_state *parse_state = _parse_state;
- struct parse_events_error *error = parse_state->error;
struct list_head *list;
ALLOC_LIST(list);
@@ -626,7 +625,6 @@ PE_TERM
PE_NAME array '=' PE_NAME
{
struct parse_events_term *term;
- int i;
ABORT_ON(parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER,
$1, $4, &@1, &@4));
diff --git a/tools/perf/util/parse-regs-options.c b/tools/perf/util/parse-regs-options.c
index 08581e276225..ef46c2848808 100644
--- a/tools/perf/util/parse-regs-options.c
+++ b/tools/perf/util/parse-regs-options.c
@@ -1,8 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
-#include "perf.h"
-#include "util/util.h"
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
#include "util/debug.h"
#include <subcmd/parse-options.h>
+#include "util/perf_regs.h"
#include "util/parse-regs-options.h"
static int
diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c
index ca56ba2dd3da..caed0336429f 100644
--- a/tools/perf/util/path.c
+++ b/tools/perf/util/path.c
@@ -11,11 +11,12 @@
*
* which is what it's designed for.
*/
-#include "cache.h"
#include "path.h"
+#include "cache.h"
#include <linux/kernel.h>
#include <limits.h>
#include <stdio.h>
+#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
diff --git a/tools/perf/util/path.h b/tools/perf/util/path.h
index f014f905df50..083429b7efa3 100644
--- a/tools/perf/util/path.h
+++ b/tools/perf/util/path.h
@@ -2,6 +2,9 @@
#ifndef _PERF_PATH_H
#define _PERF_PATH_H
+#include <stddef.h>
+#include <stdbool.h>
+
struct dirent;
int path__join(char *bf, size_t size, const char *path1, const char *path2);
diff --git a/tools/perf/util/perf-hooks.c b/tools/perf/util/perf-hooks.c
index 4f3aa8d99ef4..e635c594f773 100644
--- a/tools/perf/util/perf-hooks.c
+++ b/tools/perf/util/perf-hooks.c
@@ -8,6 +8,7 @@
#include <errno.h>
#include <stdlib.h>
+#include <string.h>
#include <setjmp.h>
#include <linux/err.h>
#include <linux/kernel.h>
diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h
index cb9c246c8962..47fe34e5f7d5 100644
--- a/tools/perf/util/perf_regs.h
+++ b/tools/perf/util/perf_regs.h
@@ -29,12 +29,16 @@ uint64_t arch__user_reg_mask(void);
#ifdef HAVE_PERF_REGS_SUPPORT
#include <perf_regs.h>
+#define DWARF_MINIMAL_REGS ((1ULL << PERF_REG_IP) | (1ULL << PERF_REG_SP))
+
int perf_reg_value(u64 *valp, struct regs_dump *regs, int id);
#else
#define PERF_REGS_MASK 0
#define PERF_REGS_MAX 0
+#define DWARF_MINIMAL_REGS PERF_REGS_MASK
+
static inline const char *perf_reg_name(int id __maybe_unused)
{
return NULL;
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index e0429f4ef335..fb597fa94234 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/list.h>
#include <linux/compiler.h>
+#include <linux/string.h>
+#include <linux/zalloc.h>
+#include <subcmd/pager.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
@@ -13,14 +16,15 @@
#include <api/fs/fs.h>
#include <locale.h>
#include <regex.h>
-#include "util.h"
+#include <perf/cpumap.h>
+#include "debug.h"
#include "pmu.h"
#include "parse-events.h"
#include "cpumap.h"
#include "header.h"
#include "pmu-events/pmu-events.h"
-#include "cache.h"
#include "string2.h"
+#include "strbuf.h"
struct perf_pmu_format {
char *name;
@@ -98,7 +102,7 @@ static int pmu_format(const char *name, struct list_head *format)
return 0;
}
-static int convert_scale(const char *scale, char **end, double *sval)
+int perf_pmu__convert_scale(const char *scale, char **end, double *sval)
{
char *lc;
int ret = 0;
@@ -161,7 +165,7 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *
else
scale[sret] = '\0';
- ret = convert_scale(scale, NULL, &alias->scale);
+ ret = perf_pmu__convert_scale(scale, NULL, &alias->scale);
error:
close(fd);
return ret;
@@ -369,7 +373,7 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,
desc ? strdup(desc) : NULL;
alias->topic = topic ? strdup(topic) : NULL;
if (unit) {
- if (convert_scale(unit, &unit, &alias->scale) < 0)
+ if (perf_pmu__convert_scale(unit, &unit, &alias->scale) < 0)
return -1;
snprintf(alias->unit, sizeof(alias->unit), "%s", unit);
}
@@ -394,7 +398,7 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI
buf[ret] = 0;
/* Remove trailing newline from sysfs file */
- rtrim(buf);
+ strim(buf);
return __perf_pmu__new_alias(list, dir, name, NULL, buf, NULL, NULL, NULL,
NULL, NULL, NULL);
@@ -571,16 +575,16 @@ static void pmu_read_sysfs(void)
closedir(dir);
}
-static struct cpu_map *__pmu_cpumask(const char *path)
+static struct perf_cpu_map *__pmu_cpumask(const char *path)
{
FILE *file;
- struct cpu_map *cpus;
+ struct perf_cpu_map *cpus;
file = fopen(path, "r");
if (!file)
return NULL;
- cpus = cpu_map__read(file);
+ cpus = perf_cpu_map__read(file);
fclose(file);
return cpus;
}
@@ -592,10 +596,10 @@ static struct cpu_map *__pmu_cpumask(const char *path)
#define CPUS_TEMPLATE_UNCORE "%s/bus/event_source/devices/%s/cpumask"
#define CPUS_TEMPLATE_CPU "%s/bus/event_source/devices/%s/cpus"
-static struct cpu_map *pmu_cpumask(const char *name)
+static struct perf_cpu_map *pmu_cpumask(const char *name)
{
char path[PATH_MAX];
- struct cpu_map *cpus;
+ struct perf_cpu_map *cpus;
const char *sysfs = sysfs__mountpoint();
const char *templates[] = {
CPUS_TEMPLATE_UNCORE,
@@ -620,12 +624,12 @@ static struct cpu_map *pmu_cpumask(const char *name)
static bool pmu_is_uncore(const char *name)
{
char path[PATH_MAX];
- struct cpu_map *cpus;
+ struct perf_cpu_map *cpus;
const char *sysfs = sysfs__mountpoint();
snprintf(path, PATH_MAX, CPUS_TEMPLATE_UNCORE, sysfs, name);
cpus = __pmu_cpumask(path);
- cpu_map__put(cpus);
+ perf_cpu_map__put(cpus);
return !!cpus;
}
@@ -700,6 +704,46 @@ struct pmu_events_map *perf_pmu__find_map(struct perf_pmu *pmu)
return map;
}
+static bool pmu_uncore_alias_match(const char *pmu_name, const char *name)
+{
+ char *tmp = NULL, *tok, *str;
+ bool res;
+
+ str = strdup(pmu_name);
+ if (!str)
+ return false;
+
+ /*
+ * uncore alias may be from different PMU with common prefix
+ */
+ tok = strtok_r(str, ",", &tmp);
+ if (strncmp(pmu_name, tok, strlen(tok))) {
+ res = false;
+ goto out;
+ }
+
+ /*
+ * Match more complex aliases where the alias name is a comma-delimited
+ * list of tokens, orderly contained in the matching PMU name.
+ *
+ * Example: For alias "socket,pmuname" and PMU "socketX_pmunameY", we
+ * match "socket" in "socketX_pmunameY" and then "pmuname" in
+ * "pmunameY".
+ */
+ for (; tok; name += strlen(tok), tok = strtok_r(NULL, ",", &tmp)) {
+ name = strstr(name, tok);
+ if (!name) {
+ res = false;
+ goto out;
+ }
+ }
+
+ res = true;
+out:
+ free(str);
+ return res;
+}
+
/*
* From the pmu_events_map, find the table of PMU events that corresponds
* to the current running CPU. Then, add all PMU events from that table
@@ -709,9 +753,7 @@ static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu)
{
int i;
struct pmu_events_map *map;
- struct pmu_event *pe;
const char *name = pmu->name;
- const char *pname;
map = perf_pmu__find_map(pmu);
if (!map)
@@ -722,28 +764,22 @@ static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu)
*/
i = 0;
while (1) {
+ const char *cpu_name = is_arm_pmu_core(name) ? name : "cpu";
+ struct pmu_event *pe = &map->table[i++];
+ const char *pname = pe->pmu ? pe->pmu : cpu_name;
- pe = &map->table[i++];
if (!pe->name) {
if (pe->metric_group || pe->metric_name)
continue;
break;
}
- if (!is_arm_pmu_core(name)) {
- pname = pe->pmu ? pe->pmu : "cpu";
-
- /*
- * uncore alias may be from different PMU
- * with common prefix
- */
- if (pmu_is_uncore(name) &&
- !strncmp(pname, name, strlen(pname)))
- goto new_alias;
+ if (pmu_is_uncore(name) &&
+ pmu_uncore_alias_match(pname, name))
+ goto new_alias;
- if (strcmp(pname, name))
- continue;
- }
+ if (strcmp(pname, name))
+ continue;
new_alias:
/* need type casts to override 'const' */
@@ -1212,7 +1248,7 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
info->metric_expr = alias->metric_expr;
info->metric_name = alias->metric_name;
- list_del(&term->list);
+ list_del_init(&term->list);
free(term);
}
@@ -1343,7 +1379,7 @@ static void wordwrap(char *s, int start, int max, int corr)
break;
s += wlen;
column += n;
- s = ltrim(s);
+ s = skip_spaces(s);
}
}
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index bd9ec2704a57..f36ade6df76d 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -28,7 +28,7 @@ struct perf_pmu {
bool is_uncore;
int max_precise;
struct perf_event_attr *default_config;
- struct cpu_map *cpus;
+ struct perf_cpu_map *cpus;
struct list_head format; /* HEAD struct perf_pmu_format -> list */
struct list_head aliases; /* HEAD struct perf_pmu_alias -> list */
struct list_head list; /* ELEM */
@@ -96,4 +96,6 @@ struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu);
struct pmu_events_map *perf_pmu__find_map(struct perf_pmu *pmu);
+int perf_pmu__convert_scale(const char *scale, char **end, double *sval);
+
#endif /* __PMU_H */
diff --git a/tools/perf/util/print_binary.c b/tools/perf/util/print_binary.c
index 23e367063446..599a1543871d 100644
--- a/tools/perf/util/print_binary.c
+++ b/tools/perf/util/print_binary.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "print_binary.h"
#include <linux/log2.h>
-#include "sane_ctype.h"
+#include <linux/ctype.h>
int binary__fprintf(unsigned char *data, size_t len,
size_t bytes_per_line, binary__fprintf_t printer,
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 198e09ff611e..b8e0967c5c21 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* probe-event.c : perf-probe definition to probe_events format converter
*
* Written by Masami Hiramatsu <mhiramat@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
*/
#include <inttypes.h>
@@ -33,18 +19,17 @@
#include <limits.h>
#include <elf.h>
-#include "util.h"
+#include "build-id.h"
#include "event.h"
#include "namespaces.h"
#include "strlist.h"
#include "strfilter.h"
#include "debug.h"
-#include "cache.h"
+#include "dso.h"
#include "color.h"
#include "map.h"
#include "map_groups.h"
#include "symbol.h"
-#include "thread.h"
#include <api/fs/fs.h>
#include "trace-event.h" /* For __maybe_unused */
#include "probe-event.h"
@@ -52,8 +37,11 @@
#include "probe-file.h"
#include "session.h"
#include "string2.h"
+#include "strbuf.h"
-#include "sane_ctype.h"
+#include <subcmd/pager.h>
+#include <linux/ctype.h>
+#include <linux/zalloc.h>
#define PERFPROBE_GROUP "probe"
@@ -228,9 +216,9 @@ out:
static void clear_perf_probe_point(struct perf_probe_point *pp)
{
- free(pp->file);
- free(pp->function);
- free(pp->lazy_line);
+ zfree(&pp->file);
+ zfree(&pp->function);
+ zfree(&pp->lazy_line);
}
static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
@@ -1189,12 +1177,11 @@ int show_available_vars(struct perf_probe_event *pevs __maybe_unused,
void line_range__clear(struct line_range *lr)
{
- free(lr->function);
- free(lr->file);
- free(lr->path);
- free(lr->comp_dir);
+ zfree(&lr->function);
+ zfree(&lr->file);
+ zfree(&lr->path);
+ zfree(&lr->comp_dir);
intlist__delete(lr->line_list);
- memset(lr, 0, sizeof(*lr));
}
int line_range__init(struct line_range *lr)
@@ -1577,6 +1564,17 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
str = tmp + 1;
}
+ tmp = strchr(str, '@');
+ if (tmp && tmp != str && strcmp(tmp + 1, "user")) { /* user attr */
+ if (!user_access_is_supported()) {
+ semantic_error("ftrace does not support user access\n");
+ return -EINVAL;
+ }
+ *tmp = '\0';
+ arg->user_access = true;
+ pr_debug("user_access ");
+ }
+
tmp = strchr(str, ':');
if (tmp) { /* Type setting */
*tmp = '\0';
@@ -2217,15 +2215,15 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
struct perf_probe_arg_field *field, *next;
int i;
- free(pev->event);
- free(pev->group);
- free(pev->target);
+ zfree(&pev->event);
+ zfree(&pev->group);
+ zfree(&pev->target);
clear_perf_probe_point(&pev->point);
for (i = 0; i < pev->nargs; i++) {
- free(pev->args[i].name);
- free(pev->args[i].var);
- free(pev->args[i].type);
+ zfree(&pev->args[i].name);
+ zfree(&pev->args[i].var);
+ zfree(&pev->args[i].type);
field = pev->args[i].field;
while (field) {
next = field->next;
@@ -2234,8 +2232,8 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
field = next;
}
}
- free(pev->args);
- memset(pev, 0, sizeof(*pev));
+ pev->nargs = 0;
+ zfree(&pev->args);
}
#define strdup_or_goto(str, label) \
@@ -2316,15 +2314,15 @@ void clear_probe_trace_event(struct probe_trace_event *tev)
struct probe_trace_arg_ref *ref, *next;
int i;
- free(tev->event);
- free(tev->group);
- free(tev->point.symbol);
- free(tev->point.realname);
- free(tev->point.module);
+ zfree(&tev->event);
+ zfree(&tev->group);
+ zfree(&tev->point.symbol);
+ zfree(&tev->point.realname);
+ zfree(&tev->point.module);
for (i = 0; i < tev->nargs; i++) {
- free(tev->args[i].name);
- free(tev->args[i].value);
- free(tev->args[i].type);
+ zfree(&tev->args[i].name);
+ zfree(&tev->args[i].value);
+ zfree(&tev->args[i].type);
ref = tev->args[i].ref;
while (ref) {
next = ref->next;
@@ -2332,8 +2330,7 @@ void clear_probe_trace_event(struct probe_trace_event *tev)
ref = next;
}
}
- free(tev->args);
- memset(tev, 0, sizeof(*tev));
+ zfree(&tev->args);
}
struct kprobe_blacklist_node {
@@ -2350,8 +2347,8 @@ static void kprobe_blacklist__delete(struct list_head *blacklist)
while (!list_empty(blacklist)) {
node = list_first_entry(blacklist,
struct kprobe_blacklist_node, list);
- list_del(&node->list);
- free(node->symbol);
+ list_del_init(&node->list);
+ zfree(&node->symbol);
free(node);
}
}
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 05c8d571a901..96a319cd2378 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -37,6 +37,7 @@ struct probe_trace_point {
struct probe_trace_arg_ref {
struct probe_trace_arg_ref *next; /* Next reference */
long offset; /* Offset value */
+ bool user_access; /* User-memory access */
};
/* kprobe-tracer and uprobe-tracer tracing argument */
@@ -82,6 +83,7 @@ struct perf_probe_arg {
char *var; /* Variable name */
char *type; /* Type name */
struct perf_probe_arg_field *field; /* Structure fields */
+ bool user_access; /* User-memory access */
};
/* Perf probe probing event (point + arg) */
diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c
index 4062bc4412a9..d13db55a2feb 100644
--- a/tools/perf/util/probe-file.c
+++ b/tools/perf/util/probe-file.c
@@ -1,18 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* probe-file.c : operate ftrace k/uprobe events files
*
* Written by Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <errno.h>
#include <fcntl.h>
@@ -20,16 +10,16 @@
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
+#include <linux/zalloc.h>
#include "namespaces.h"
-#include "util.h"
#include "event.h"
#include "strlist.h"
#include "strfilter.h"
#include "debug.h"
-#include "cache.h"
+#include "dso.h"
#include "color.h"
#include "symbol.h"
-#include "thread.h"
+#include "strbuf.h"
#include <api/fs/tracing_path.h>
#include "probe-event.h"
#include "probe-file.h"
@@ -1015,6 +1005,7 @@ enum ftrace_readme {
FTRACE_README_PROBE_TYPE_X = 0,
FTRACE_README_KRETPROBE_OFFSET,
FTRACE_README_UPROBE_REF_CTR,
+ FTRACE_README_USER_ACCESS,
FTRACE_README_END,
};
@@ -1027,6 +1018,7 @@ static struct {
DEFINE_TYPE(FTRACE_README_PROBE_TYPE_X, "*type: * x8/16/32/64,*"),
DEFINE_TYPE(FTRACE_README_KRETPROBE_OFFSET, "*place (kretprobe): *"),
DEFINE_TYPE(FTRACE_README_UPROBE_REF_CTR, "*ref_ctr_offset*"),
+ DEFINE_TYPE(FTRACE_README_USER_ACCESS, "*[u]<offset>*"),
};
static bool scan_ftrace_readme(enum ftrace_readme type)
@@ -1087,3 +1079,8 @@ bool uprobe_ref_ctr_is_supported(void)
{
return scan_ftrace_readme(FTRACE_README_UPROBE_REF_CTR);
}
+
+bool user_access_is_supported(void)
+{
+ return scan_ftrace_readme(FTRACE_README_USER_ACCESS);
+}
diff --git a/tools/perf/util/probe-file.h b/tools/perf/util/probe-file.h
index 2a249182f2a6..986c1c94f64f 100644
--- a/tools/perf/util/probe-file.h
+++ b/tools/perf/util/probe-file.h
@@ -70,6 +70,7 @@ int probe_cache__show_all_caches(struct strfilter *filter);
bool probe_type_is_available(enum probe_type type);
bool kretprobe_offset_is_supported(void);
bool uprobe_ref_ctr_is_supported(void);
+bool user_access_is_supported(void);
#else /* ! HAVE_LIBELF_SUPPORT */
static inline struct probe_cache *probe_cache__new(const char *tgt __maybe_unused, struct nsinfo *nsi __maybe_unused)
{
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index c37fbef1711d..505905fc21c5 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* probe-finder.c : C expression to kprobe event converter
*
* Written by Masami Hiramatsu <mhiramat@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
*/
#include <inttypes.h>
@@ -33,11 +19,12 @@
#include <dwarf-regs.h>
#include <linux/bitops.h>
+#include <linux/zalloc.h>
#include "event.h"
#include "dso.h"
#include "debug.h"
#include "intlist.h"
-#include "util.h"
+#include "strbuf.h"
#include "strlist.h"
#include "symbol.h"
#include "probe-finder.h"
@@ -294,7 +281,7 @@ static_var:
static int convert_variable_type(Dwarf_Die *vr_die,
struct probe_trace_arg *tvar,
- const char *cast)
+ const char *cast, bool user_access)
{
struct probe_trace_arg_ref **ref_ptr = &tvar->ref;
Dwarf_Die type;
@@ -334,7 +321,8 @@ static int convert_variable_type(Dwarf_Die *vr_die,
pr_debug("%s type is %s.\n",
dwarf_diename(vr_die), dwarf_diename(&type));
- if (cast && strcmp(cast, "string") == 0) { /* String type */
+ if (cast && (!strcmp(cast, "string") || !strcmp(cast, "ustring"))) {
+ /* String type */
ret = dwarf_tag(&type);
if (ret != DW_TAG_pointer_type &&
ret != DW_TAG_array_type) {
@@ -357,6 +345,7 @@ static int convert_variable_type(Dwarf_Die *vr_die,
pr_warning("Out of memory error\n");
return -ENOMEM;
}
+ (*ref_ptr)->user_access = user_access;
}
if (!die_compare_name(&type, "char") &&
!die_compare_name(&type, "unsigned char")) {
@@ -411,7 +400,7 @@ formatted:
static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
struct perf_probe_arg_field *field,
struct probe_trace_arg_ref **ref_ptr,
- Dwarf_Die *die_mem)
+ Dwarf_Die *die_mem, bool user_access)
{
struct probe_trace_arg_ref *ref = *ref_ptr;
Dwarf_Die type;
@@ -448,6 +437,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
*ref_ptr = ref;
}
ref->offset += dwarf_bytesize(&type) * field->index;
+ ref->user_access = user_access;
goto next;
} else if (tag == DW_TAG_pointer_type) {
/* Check the pointer and dereference */
@@ -519,17 +509,18 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
}
}
ref->offset += (long)offs;
+ ref->user_access = user_access;
/* If this member is unnamed, we need to reuse this field */
if (!dwarf_diename(die_mem))
return convert_variable_fields(die_mem, varname, field,
- &ref, die_mem);
+ &ref, die_mem, user_access);
next:
/* Converting next field */
if (field->next)
return convert_variable_fields(die_mem, field->name,
- field->next, &ref, die_mem);
+ field->next, &ref, die_mem, user_access);
else
return 0;
}
@@ -555,11 +546,12 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
else if (ret == 0 && pf->pvar->field) {
ret = convert_variable_fields(vr_die, pf->pvar->var,
pf->pvar->field, &pf->tvar->ref,
- &die_mem);
+ &die_mem, pf->pvar->user_access);
vr_die = &die_mem;
}
if (ret == 0)
- ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type);
+ ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type,
+ pf->pvar->user_access);
/* *expr will be cached in libdw. Don't free it. */
return ret;
}
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index 16252980ff00..670c477bf8cf 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -5,7 +5,7 @@
#include <stdbool.h>
#include "intlist.h"
#include "probe-event.h"
-#include "sane_ctype.h"
+#include <linux/ctype.h>
#define MAX_PROBE_BUFFER 1024
#define MAX_PROBES 128
diff --git a/tools/perf/util/pstack.c b/tools/perf/util/pstack.c
index 797fe1ae2d2e..80ff41fc45be 100644
--- a/tools/perf/util/pstack.c
+++ b/tools/perf/util/pstack.c
@@ -5,11 +5,12 @@
* (c) 2010 Arnaldo Carvalho de Melo <acme@redhat.com>
*/
-#include "util.h"
#include "pstack.h"
#include "debug.h"
#include <linux/kernel.h>
+#include <linux/zalloc.h>
#include <stdlib.h>
+#include <string.h>
struct pstack {
unsigned short top;
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources
index 7aa0ea64544e..c6dd478956f1 100644
--- a/tools/perf/util/python-ext-sources
+++ b/tools/perf/util/python-ext-sources
@@ -6,7 +6,8 @@
#
util/python.c
-util/ctype.c
+../lib/ctype.c
+util/cap.c
util/evlist.c
util/evsel.c
util/cpumap.c
@@ -16,10 +17,10 @@ util/namespaces.c
../lib/bitmap.c
../lib/find_bit.c
../lib/hweight.c
+../lib/string.c
../lib/vsprintf.c
util/thread_map.c
util/util.c
-util/xyarray.c
util/cgroup.c
util/parse-branch-options.c
util/rblist.c
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 6aa7e2352e16..07ca4535e6f7 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -4,6 +4,9 @@
#include <inttypes.h>
#include <poll.h>
#include <linux/err.h>
+#include <perf/cpumap.h>
+#include <traceevent/event-parse.h>
+#include "debug.h"
#include "evlist.h"
#include "callchain.h"
#include "evsel.h"
@@ -11,7 +14,10 @@
#include "cpumap.h"
#include "print_binary.h"
#include "thread_map.h"
+#include "trace-event.h"
#include "mmap.h"
+#include "util.h"
+#include "../perf-sys.h"
#if PY_MAJOR_VERSION < 3
#define _PyUnicode_FromString(arg) \
@@ -92,7 +98,7 @@ PyMODINIT_FUNC PyInit_perf(void);
struct pyrf_event {
PyObject_HEAD
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct perf_sample sample;
union perf_event event;
};
@@ -114,12 +120,12 @@ static PyMemberDef pyrf_mmap_event__members[] = {
sample_members
member_def(perf_event_header, type, T_UINT, "event type"),
member_def(perf_event_header, misc, T_UINT, "event misc"),
- member_def(mmap_event, pid, T_UINT, "event pid"),
- member_def(mmap_event, tid, T_UINT, "event tid"),
- member_def(mmap_event, start, T_ULONGLONG, "start of the map"),
- member_def(mmap_event, len, T_ULONGLONG, "map length"),
- member_def(mmap_event, pgoff, T_ULONGLONG, "page offset"),
- member_def(mmap_event, filename, T_STRING_INPLACE, "backing store"),
+ member_def(perf_record_mmap, pid, T_UINT, "event pid"),
+ member_def(perf_record_mmap, tid, T_UINT, "event tid"),
+ member_def(perf_record_mmap, start, T_ULONGLONG, "start of the map"),
+ member_def(perf_record_mmap, len, T_ULONGLONG, "map length"),
+ member_def(perf_record_mmap, pgoff, T_ULONGLONG, "page offset"),
+ member_def(perf_record_mmap, filename, T_STRING_INPLACE, "backing store"),
{ .name = NULL, },
};
@@ -128,8 +134,8 @@ static PyObject *pyrf_mmap_event__repr(struct pyrf_event *pevent)
PyObject *ret;
char *s;
- if (asprintf(&s, "{ type: mmap, pid: %u, tid: %u, start: %#" PRIx64 ", "
- "length: %#" PRIx64 ", offset: %#" PRIx64 ", "
+ if (asprintf(&s, "{ type: mmap, pid: %u, tid: %u, start: %#" PRI_lx64 ", "
+ "length: %#" PRI_lx64 ", offset: %#" PRI_lx64 ", "
"filename: %s }",
pevent->event.mmap.pid, pevent->event.mmap.tid,
pevent->event.mmap.start, pevent->event.mmap.len,
@@ -157,18 +163,18 @@ static char pyrf_task_event__doc[] = PyDoc_STR("perf task (fork/exit) event obje
static PyMemberDef pyrf_task_event__members[] = {
sample_members
member_def(perf_event_header, type, T_UINT, "event type"),
- member_def(fork_event, pid, T_UINT, "event pid"),
- member_def(fork_event, ppid, T_UINT, "event ppid"),
- member_def(fork_event, tid, T_UINT, "event tid"),
- member_def(fork_event, ptid, T_UINT, "event ptid"),
- member_def(fork_event, time, T_ULONGLONG, "timestamp"),
+ member_def(perf_record_fork, pid, T_UINT, "event pid"),
+ member_def(perf_record_fork, ppid, T_UINT, "event ppid"),
+ member_def(perf_record_fork, tid, T_UINT, "event tid"),
+ member_def(perf_record_fork, ptid, T_UINT, "event ptid"),
+ member_def(perf_record_fork, time, T_ULONGLONG, "timestamp"),
{ .name = NULL, },
};
static PyObject *pyrf_task_event__repr(struct pyrf_event *pevent)
{
return _PyUnicode_FromFormat("{ type: %s, pid: %u, ppid: %u, tid: %u, "
- "ptid: %u, time: %" PRIu64 "}",
+ "ptid: %u, time: %" PRI_lu64 "}",
pevent->event.header.type == PERF_RECORD_FORK ? "fork" : "exit",
pevent->event.fork.pid,
pevent->event.fork.ppid,
@@ -192,9 +198,9 @@ static char pyrf_comm_event__doc[] = PyDoc_STR("perf comm event object.");
static PyMemberDef pyrf_comm_event__members[] = {
sample_members
member_def(perf_event_header, type, T_UINT, "event type"),
- member_def(comm_event, pid, T_UINT, "event pid"),
- member_def(comm_event, tid, T_UINT, "event tid"),
- member_def(comm_event, comm, T_STRING_INPLACE, "process name"),
+ member_def(perf_record_comm, pid, T_UINT, "event pid"),
+ member_def(perf_record_comm, tid, T_UINT, "event tid"),
+ member_def(perf_record_comm, comm, T_STRING_INPLACE, "process name"),
{ .name = NULL, },
};
@@ -221,18 +227,18 @@ static char pyrf_throttle_event__doc[] = PyDoc_STR("perf throttle event object."
static PyMemberDef pyrf_throttle_event__members[] = {
sample_members
member_def(perf_event_header, type, T_UINT, "event type"),
- member_def(throttle_event, time, T_ULONGLONG, "timestamp"),
- member_def(throttle_event, id, T_ULONGLONG, "event id"),
- member_def(throttle_event, stream_id, T_ULONGLONG, "event stream id"),
+ member_def(perf_record_throttle, time, T_ULONGLONG, "timestamp"),
+ member_def(perf_record_throttle, id, T_ULONGLONG, "event id"),
+ member_def(perf_record_throttle, stream_id, T_ULONGLONG, "event stream id"),
{ .name = NULL, },
};
static PyObject *pyrf_throttle_event__repr(struct pyrf_event *pevent)
{
- struct throttle_event *te = (struct throttle_event *)(&pevent->event.header + 1);
+ struct perf_record_throttle *te = (struct perf_record_throttle *)(&pevent->event.header + 1);
- return _PyUnicode_FromFormat("{ type: %sthrottle, time: %" PRIu64 ", id: %" PRIu64
- ", stream_id: %" PRIu64 " }",
+ return _PyUnicode_FromFormat("{ type: %sthrottle, time: %" PRI_lu64 ", id: %" PRI_lu64
+ ", stream_id: %" PRI_lu64 " }",
pevent->event.header.type == PERF_RECORD_THROTTLE ? "" : "un",
te->time, te->id, te->stream_id);
}
@@ -251,8 +257,8 @@ static char pyrf_lost_event__doc[] = PyDoc_STR("perf lost event object.");
static PyMemberDef pyrf_lost_event__members[] = {
sample_members
- member_def(lost_event, id, T_ULONGLONG, "event id"),
- member_def(lost_event, lost, T_ULONGLONG, "number of lost events"),
+ member_def(perf_record_lost, id, T_ULONGLONG, "event id"),
+ member_def(perf_record_lost, lost, T_ULONGLONG, "number of lost events"),
{ .name = NULL, },
};
@@ -261,8 +267,8 @@ static PyObject *pyrf_lost_event__repr(struct pyrf_event *pevent)
PyObject *ret;
char *s;
- if (asprintf(&s, "{ type: lost, id: %#" PRIx64 ", "
- "lost: %#" PRIx64 " }",
+ if (asprintf(&s, "{ type: lost, id: %#" PRI_lx64 ", "
+ "lost: %#" PRI_lx64 " }",
pevent->event.lost.id, pevent->event.lost.lost) < 0) {
ret = PyErr_NoMemory();
} else {
@@ -286,8 +292,8 @@ static char pyrf_read_event__doc[] = PyDoc_STR("perf read event object.");
static PyMemberDef pyrf_read_event__members[] = {
sample_members
- member_def(read_event, pid, T_UINT, "event pid"),
- member_def(read_event, tid, T_UINT, "event tid"),
+ member_def(perf_record_read, pid, T_UINT, "event pid"),
+ member_def(perf_record_read, tid, T_UINT, "event tid"),
{ .name = NULL, },
};
@@ -336,7 +342,7 @@ static PyObject *pyrf_sample_event__repr(struct pyrf_event *pevent)
static bool is_tracepoint(struct pyrf_event *pevent)
{
- return pevent->evsel->attr.type == PERF_TYPE_TRACEPOINT;
+ return pevent->evsel->core.attr.type == PERF_TYPE_TRACEPOINT;
}
static PyObject*
@@ -382,13 +388,13 @@ static PyObject*
get_tracepoint_field(struct pyrf_event *pevent, PyObject *attr_name)
{
const char *str = _PyUnicode_AsString(PyObject_Str(attr_name));
- struct perf_evsel *evsel = pevent->evsel;
+ struct evsel *evsel = pevent->evsel;
struct tep_format_field *field;
if (!evsel->tp_format) {
struct tep_event *tp_format;
- tp_format = trace_event__tp_format_id(evsel->attr.config);
+ tp_format = trace_event__tp_format_id(evsel->core.attr.config);
if (!tp_format)
return NULL;
@@ -429,8 +435,8 @@ static char pyrf_context_switch_event__doc[] = PyDoc_STR("perf context_switch ev
static PyMemberDef pyrf_context_switch_event__members[] = {
sample_members
member_def(perf_event_header, type, T_UINT, "event type"),
- member_def(context_switch_event, next_prev_pid, T_UINT, "next/prev pid"),
- member_def(context_switch_event, next_prev_tid, T_UINT, "next/prev tid"),
+ member_def(perf_record_switch, next_prev_pid, T_UINT, "next/prev pid"),
+ member_def(perf_record_switch, next_prev_tid, T_UINT, "next/prev tid"),
{ .name = NULL, },
};
@@ -535,7 +541,7 @@ static PyObject *pyrf_event__new(union perf_event *event)
struct pyrf_cpu_map {
PyObject_HEAD
- struct cpu_map *cpus;
+ struct perf_cpu_map *cpus;
};
static int pyrf_cpu_map__init(struct pyrf_cpu_map *pcpus,
@@ -548,7 +554,7 @@ static int pyrf_cpu_map__init(struct pyrf_cpu_map *pcpus,
kwlist, &cpustr))
return -1;
- pcpus->cpus = cpu_map__new(cpustr);
+ pcpus->cpus = perf_cpu_map__new(cpustr);
if (pcpus->cpus == NULL)
return -1;
return 0;
@@ -556,7 +562,7 @@ static int pyrf_cpu_map__init(struct pyrf_cpu_map *pcpus,
static void pyrf_cpu_map__delete(struct pyrf_cpu_map *pcpus)
{
- cpu_map__put(pcpus->cpus);
+ perf_cpu_map__put(pcpus->cpus);
Py_TYPE(pcpus)->tp_free((PyObject*)pcpus);
}
@@ -604,7 +610,7 @@ static int pyrf_cpu_map__setup_types(void)
struct pyrf_thread_map {
PyObject_HEAD
- struct thread_map *threads;
+ struct perf_thread_map *threads;
};
static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads,
@@ -625,7 +631,7 @@ static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads,
static void pyrf_thread_map__delete(struct pyrf_thread_map *pthreads)
{
- thread_map__put(pthreads->threads);
+ perf_thread_map__put(pthreads->threads);
Py_TYPE(pthreads)->tp_free((PyObject*)pthreads);
}
@@ -673,7 +679,7 @@ static int pyrf_thread_map__setup_types(void)
struct pyrf_evsel {
PyObject_HEAD
- struct perf_evsel evsel;
+ struct evsel evsel;
};
static int pyrf_evsel__init(struct pyrf_evsel *pevsel,
@@ -781,7 +787,7 @@ static int pyrf_evsel__init(struct pyrf_evsel *pevsel,
attr.sample_id_all = sample_id_all;
attr.size = sizeof(attr);
- perf_evsel__init(&pevsel->evsel, &attr, idx);
+ evsel__init(&pevsel->evsel, &attr, idx);
return 0;
}
@@ -794,9 +800,9 @@ static void pyrf_evsel__delete(struct pyrf_evsel *pevsel)
static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel,
PyObject *args, PyObject *kwargs)
{
- struct perf_evsel *evsel = &pevsel->evsel;
- struct cpu_map *cpus = NULL;
- struct thread_map *threads = NULL;
+ struct evsel *evsel = &pevsel->evsel;
+ struct perf_cpu_map *cpus = NULL;
+ struct perf_thread_map *threads = NULL;
PyObject *pcpus = NULL, *pthreads = NULL;
int group = 0, inherit = 0;
static char *kwlist[] = { "cpus", "threads", "group", "inherit", NULL };
@@ -811,12 +817,12 @@ static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel,
if (pcpus != NULL)
cpus = ((struct pyrf_cpu_map *)pcpus)->cpus;
- evsel->attr.inherit = inherit;
+ evsel->core.attr.inherit = inherit;
/*
* This will group just the fds for this single evsel, to group
* multiple events, use evlist.open().
*/
- if (perf_evsel__open(evsel, cpus, threads) < 0) {
+ if (evsel__open(evsel, cpus, threads) < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
@@ -857,22 +863,22 @@ static int pyrf_evsel__setup_types(void)
struct pyrf_evlist {
PyObject_HEAD
- struct perf_evlist evlist;
+ struct evlist evlist;
};
static int pyrf_evlist__init(struct pyrf_evlist *pevlist,
PyObject *args, PyObject *kwargs __maybe_unused)
{
PyObject *pcpus = NULL, *pthreads = NULL;
- struct cpu_map *cpus;
- struct thread_map *threads;
+ struct perf_cpu_map *cpus;
+ struct perf_thread_map *threads;
if (!PyArg_ParseTuple(args, "OO", &pcpus, &pthreads))
return -1;
threads = ((struct pyrf_thread_map *)pthreads)->threads;
cpus = ((struct pyrf_cpu_map *)pcpus)->cpus;
- perf_evlist__init(&pevlist->evlist, cpus, threads);
+ evlist__init(&pevlist->evlist, cpus, threads);
return 0;
}
@@ -885,7 +891,7 @@ static void pyrf_evlist__delete(struct pyrf_evlist *pevlist)
static PyObject *pyrf_evlist__mmap(struct pyrf_evlist *pevlist,
PyObject *args, PyObject *kwargs)
{
- struct perf_evlist *evlist = &pevlist->evlist;
+ struct evlist *evlist = &pevlist->evlist;
static char *kwlist[] = { "pages", "overwrite", NULL };
int pages = 128, overwrite = false;
@@ -905,7 +911,7 @@ static PyObject *pyrf_evlist__mmap(struct pyrf_evlist *pevlist,
static PyObject *pyrf_evlist__poll(struct pyrf_evlist *pevlist,
PyObject *args, PyObject *kwargs)
{
- struct perf_evlist *evlist = &pevlist->evlist;
+ struct evlist *evlist = &pevlist->evlist;
static char *kwlist[] = { "timeout", NULL };
int timeout = -1, n;
@@ -925,7 +931,7 @@ static PyObject *pyrf_evlist__get_pollfd(struct pyrf_evlist *pevlist,
PyObject *args __maybe_unused,
PyObject *kwargs __maybe_unused)
{
- struct perf_evlist *evlist = &pevlist->evlist;
+ struct evlist *evlist = &pevlist->evlist;
PyObject *list = PyList_New(0);
int i;
@@ -963,22 +969,22 @@ static PyObject *pyrf_evlist__add(struct pyrf_evlist *pevlist,
PyObject *args,
PyObject *kwargs __maybe_unused)
{
- struct perf_evlist *evlist = &pevlist->evlist;
+ struct evlist *evlist = &pevlist->evlist;
PyObject *pevsel;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
if (!PyArg_ParseTuple(args, "O", &pevsel))
return NULL;
Py_INCREF(pevsel);
evsel = &((struct pyrf_evsel *)pevsel)->evsel;
- evsel->idx = evlist->nr_entries;
- perf_evlist__add(evlist, evsel);
+ evsel->idx = evlist->core.nr_entries;
+ evlist__add(evlist, evsel);
- return Py_BuildValue("i", evlist->nr_entries);
+ return Py_BuildValue("i", evlist->core.nr_entries);
}
-static struct perf_mmap *get_md(struct perf_evlist *evlist, int cpu)
+static struct perf_mmap *get_md(struct evlist *evlist, int cpu)
{
int i;
@@ -995,7 +1001,7 @@ static struct perf_mmap *get_md(struct perf_evlist *evlist, int cpu)
static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist,
PyObject *args, PyObject *kwargs)
{
- struct perf_evlist *evlist = &pevlist->evlist;
+ struct evlist *evlist = &pevlist->evlist;
union perf_event *event;
int sample_id_all = 1, cpu;
static char *kwlist[] = { "cpu", "sample_id_all", NULL };
@@ -1017,7 +1023,7 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist,
if (event != NULL) {
PyObject *pyevent = pyrf_event__new(event);
struct pyrf_event *pevent = (struct pyrf_event *)pyevent;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
if (pyevent == NULL)
return PyErr_NoMemory();
@@ -1048,7 +1054,7 @@ end:
static PyObject *pyrf_evlist__open(struct pyrf_evlist *pevlist,
PyObject *args, PyObject *kwargs)
{
- struct perf_evlist *evlist = &pevlist->evlist;
+ struct evlist *evlist = &pevlist->evlist;
int group = 0;
static char *kwlist[] = { "group", NULL };
@@ -1058,7 +1064,7 @@ static PyObject *pyrf_evlist__open(struct pyrf_evlist *pevlist,
if (group)
perf_evlist__set_leader(evlist);
- if (perf_evlist__open(evlist) < 0) {
+ if (evlist__open(evlist) < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
@@ -1111,15 +1117,15 @@ static Py_ssize_t pyrf_evlist__length(PyObject *obj)
{
struct pyrf_evlist *pevlist = (void *)obj;
- return pevlist->evlist.nr_entries;
+ return pevlist->evlist.core.nr_entries;
}
static PyObject *pyrf_evlist__item(PyObject *obj, Py_ssize_t i)
{
struct pyrf_evlist *pevlist = (void *)obj;
- struct perf_evsel *pos;
+ struct evsel *pos;
- if (i >= pevlist->evlist.nr_entries)
+ if (i >= pevlist->evlist.core.nr_entries)
return NULL;
evlist__for_each_entry(&pevlist->evlist, pos) {
diff --git a/tools/perf/util/rblist.c b/tools/perf/util/rblist.c
index 11e07fab20dc..f399b7ec4d8d 100644
--- a/tools/perf/util/rblist.c
+++ b/tools/perf/util/rblist.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Based on strlist.c by:
* (c) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
- *
- * Licensed under the GPLv2.
*/
#include <errno.h>
diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c
index 9cfc7bf16531..286fe816c0f3 100644
--- a/tools/perf/util/record.c
+++ b/tools/perf/util/record.c
@@ -1,25 +1,31 @@
// SPDX-License-Identifier: GPL-2.0
+#include "debug.h"
#include "evlist.h"
#include "evsel.h"
#include "cpumap.h"
#include "parse-events.h"
#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
#include <api/fs/fs.h>
#include <subcmd/parse-options.h>
+#include <perf/cpumap.h>
#include "util.h"
#include "cloexec.h"
+#include "record.h"
+#include "../perf-sys.h"
-typedef void (*setup_probe_fn_t)(struct perf_evsel *evsel);
+typedef void (*setup_probe_fn_t)(struct evsel *evsel);
static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str)
{
- struct perf_evlist *evlist;
- struct perf_evsel *evsel;
+ struct evlist *evlist;
+ struct evsel *evsel;
unsigned long flags = perf_event_open_cloexec_flag();
int err = -EAGAIN, fd;
static pid_t pid = -1;
- evlist = perf_evlist__new();
+ evlist = evlist__new();
if (!evlist)
return -ENOMEM;
@@ -29,7 +35,7 @@ static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str)
evsel = perf_evlist__first(evlist);
while (1) {
- fd = sys_perf_event_open(&evsel->attr, pid, cpu, -1, flags);
+ fd = sys_perf_event_open(&evsel->core.attr, pid, cpu, -1, flags);
if (fd < 0) {
if (pid == -1 && errno == EACCES) {
pid = 0;
@@ -43,7 +49,7 @@ static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str)
fn(evsel);
- fd = sys_perf_event_open(&evsel->attr, pid, cpu, -1, flags);
+ fd = sys_perf_event_open(&evsel->core.attr, pid, cpu, -1, flags);
if (fd < 0) {
if (errno == EINVAL)
err = -EINVAL;
@@ -53,21 +59,21 @@ static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str)
err = 0;
out_delete:
- perf_evlist__delete(evlist);
+ evlist__delete(evlist);
return err;
}
static bool perf_probe_api(setup_probe_fn_t fn)
{
const char *try[] = {"cycles:u", "instructions:u", "cpu-clock:u", NULL};
- struct cpu_map *cpus;
+ struct perf_cpu_map *cpus;
int cpu, ret, i = 0;
- cpus = cpu_map__new(NULL);
+ cpus = perf_cpu_map__new(NULL);
if (!cpus)
return false;
cpu = cpus->map[0];
- cpu_map__put(cpus);
+ perf_cpu_map__put(cpus);
do {
ret = perf_do_probe_api(fn, cpu, try[i++]);
@@ -78,19 +84,19 @@ static bool perf_probe_api(setup_probe_fn_t fn)
return false;
}
-static void perf_probe_sample_identifier(struct perf_evsel *evsel)
+static void perf_probe_sample_identifier(struct evsel *evsel)
{
- evsel->attr.sample_type |= PERF_SAMPLE_IDENTIFIER;
+ evsel->core.attr.sample_type |= PERF_SAMPLE_IDENTIFIER;
}
-static void perf_probe_comm_exec(struct perf_evsel *evsel)
+static void perf_probe_comm_exec(struct evsel *evsel)
{
- evsel->attr.comm_exec = 1;
+ evsel->core.attr.comm_exec = 1;
}
-static void perf_probe_context_switch(struct perf_evsel *evsel)
+static void perf_probe_context_switch(struct evsel *evsel)
{
- evsel->attr.context_switch = 1;
+ evsel->core.attr.context_switch = 1;
}
bool perf_can_sample_identifier(void)
@@ -115,14 +121,14 @@ bool perf_can_record_cpu_wide(void)
.config = PERF_COUNT_SW_CPU_CLOCK,
.exclude_kernel = 1,
};
- struct cpu_map *cpus;
+ struct perf_cpu_map *cpus;
int cpu, fd;
- cpus = cpu_map__new(NULL);
+ cpus = perf_cpu_map__new(NULL);
if (!cpus)
return false;
cpu = cpus->map[0];
- cpu_map__put(cpus);
+ perf_cpu_map__put(cpus);
fd = sys_perf_event_open(&attr, -1, cpu, -1, 0);
if (fd < 0)
@@ -132,10 +138,10 @@ bool perf_can_record_cpu_wide(void)
return true;
}
-void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts,
+void perf_evlist__config(struct evlist *evlist, struct record_opts *opts,
struct callchain_param *callchain)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
bool use_sample_identifier = false;
bool use_comm_exec;
bool sample_id = opts->sample_id;
@@ -147,7 +153,7 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts,
if (opts->group)
perf_evlist__set_leader(evlist);
- if (evlist->cpus->map[0] < 0)
+ if (evlist->core.cpus->map[0] < 0)
opts->no_inherit = true;
use_comm_exec = perf_can_comm_exec();
@@ -155,7 +161,7 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts,
evlist__for_each_entry(evlist, evsel) {
perf_evsel__config(evsel, opts, callchain);
if (evsel->tracking && use_comm_exec)
- evsel->attr.comm_exec = 1;
+ evsel->core.attr.comm_exec = 1;
}
if (opts->full_auxtrace) {
@@ -166,11 +172,11 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts,
*/
use_sample_identifier = perf_can_sample_identifier();
sample_id = true;
- } else if (evlist->nr_entries > 1) {
- struct perf_evsel *first = perf_evlist__first(evlist);
+ } else if (evlist->core.nr_entries > 1) {
+ struct evsel *first = perf_evlist__first(evlist);
evlist__for_each_entry(evlist, evsel) {
- if (evsel->attr.sample_type == first->attr.sample_type)
+ if (evsel->core.attr.sample_type == first->core.attr.sample_type)
continue;
use_sample_identifier = perf_can_sample_identifier();
break;
@@ -256,15 +262,15 @@ int record_opts__config(struct record_opts *opts)
return record_opts__config_freq(opts);
}
-bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str)
+bool perf_evlist__can_select_event(struct evlist *evlist, const char *str)
{
- struct perf_evlist *temp_evlist;
- struct perf_evsel *evsel;
+ struct evlist *temp_evlist;
+ struct evsel *evsel;
int err, fd, cpu;
bool ret = false;
pid_t pid = -1;
- temp_evlist = perf_evlist__new();
+ temp_evlist = evlist__new();
if (!temp_evlist)
return false;
@@ -274,17 +280,17 @@ bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str)
evsel = perf_evlist__last(temp_evlist);
- if (!evlist || cpu_map__empty(evlist->cpus)) {
- struct cpu_map *cpus = cpu_map__new(NULL);
+ if (!evlist || perf_cpu_map__empty(evlist->core.cpus)) {
+ struct perf_cpu_map *cpus = perf_cpu_map__new(NULL);
cpu = cpus ? cpus->map[0] : 0;
- cpu_map__put(cpus);
+ perf_cpu_map__put(cpus);
} else {
- cpu = evlist->cpus->map[0];
+ cpu = evlist->core.cpus->map[0];
}
while (1) {
- fd = sys_perf_event_open(&evsel->attr, pid, cpu, -1,
+ fd = sys_perf_event_open(&evsel->core.attr, pid, cpu, -1,
perf_event_open_cloexec_flag());
if (fd < 0) {
if (pid == -1 && errno == EACCES) {
@@ -299,7 +305,7 @@ bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str)
ret = true;
out_delete:
- perf_evlist__delete(temp_evlist);
+ evlist__delete(temp_evlist);
return ret;
}
diff --git a/tools/perf/util/record.h b/tools/perf/util/record.h
new file mode 100644
index 000000000000..00275afc524d
--- /dev/null
+++ b/tools/perf/util/record.h
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _PERF_RECORD_H
+#define _PERF_RECORD_H
+
+#include <time.h>
+#include <stdbool.h>
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/perf_event.h>
+#include "util/target.h"
+
+struct option;
+
+struct record_opts {
+ struct target target;
+ bool group;
+ bool inherit_stat;
+ bool no_buffering;
+ bool no_inherit;
+ bool no_inherit_set;
+ bool no_samples;
+ bool raw_samples;
+ bool sample_address;
+ bool sample_phys_addr;
+ bool sample_weight;
+ bool sample_time;
+ bool sample_time_set;
+ bool sample_cpu;
+ bool period;
+ bool period_set;
+ bool running_time;
+ bool full_auxtrace;
+ bool auxtrace_snapshot_mode;
+ bool auxtrace_snapshot_on_exit;
+ bool record_namespaces;
+ bool record_switch_events;
+ bool all_kernel;
+ bool all_user;
+ bool kernel_callchains;
+ bool user_callchains;
+ bool tail_synthesize;
+ bool overwrite;
+ bool ignore_missing_thread;
+ bool strict_freq;
+ bool sample_id;
+ bool no_bpf_event;
+ unsigned int freq;
+ unsigned int mmap_pages;
+ unsigned int auxtrace_mmap_pages;
+ unsigned int user_freq;
+ u64 branch_stack;
+ u64 sample_intr_regs;
+ u64 sample_user_regs;
+ u64 default_interval;
+ u64 user_interval;
+ size_t auxtrace_snapshot_size;
+ const char *auxtrace_snapshot_opts;
+ bool sample_transaction;
+ unsigned initial_delay;
+ bool use_clockid;
+ clockid_t clockid;
+ u64 clockid_res_ns;
+ int nr_cblocks;
+ int affinity;
+ int mmap_flush;
+ unsigned int comp_level;
+};
+
+extern const char * const *record_usage;
+extern struct option *record_options;
+
+int record__parse_freq(const struct option *opt, const char *str, int unset);
+
+#endif // _PERF_RECORD_H
diff --git a/tools/perf/util/rlimit.c b/tools/perf/util/rlimit.c
new file mode 100644
index 000000000000..13521d392a22
--- /dev/null
+++ b/tools/perf/util/rlimit.c
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+#include "util/debug.h"
+#include "util/rlimit.h"
+#include <sys/time.h>
+#include <sys/resource.h>
+
+/*
+ * Bump the memlock so that we can get bpf maps of a reasonable size,
+ * like the ones used with 'perf trace' and with 'perf test bpf',
+ * improve this to some specific request if needed.
+ */
+void rlimit__bump_memlock(void)
+{
+ struct rlimit rlim;
+
+ if (getrlimit(RLIMIT_MEMLOCK, &rlim) == 0) {
+ rlim.rlim_cur *= 4;
+ rlim.rlim_max *= 4;
+
+ if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0) {
+ rlim.rlim_cur /= 2;
+ rlim.rlim_max /= 2;
+
+ if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0)
+ pr_debug("Couldn't bump rlimit(MEMLOCK), failures may take place when creating BPF maps, etc\n");
+ }
+ }
+}
diff --git a/tools/perf/util/rlimit.h b/tools/perf/util/rlimit.h
new file mode 100644
index 000000000000..9f59d8e710a3
--- /dev/null
+++ b/tools/perf/util/rlimit.h
@@ -0,0 +1,6 @@
+#ifndef __PERF_RLIMIT_H_
+#define __PERF_RLIMIT_H_
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+void rlimit__bump_memlock(void);
+#endif // __PERF_RLIMIT_H_
diff --git a/tools/perf/util/s390-cpumsf.c b/tools/perf/util/s390-cpumsf.c
index c215704931dc..24a99909d8b3 100644
--- a/tools/perf/util/s390-cpumsf.c
+++ b/tools/perf/util/s390-cpumsf.c
@@ -17,8 +17,8 @@
* see Documentation/perf.data-file-format.txt.
* PERF_RECORD_AUXTRACE_INFO:
* Defines a table of contains for PERF_RECORD_AUXTRACE records. This
- * record is generated during 'perf record' command. Each record contains up
- * to 256 entries describing offset and size of the AUXTRACE data in the
+ * record is generated during 'perf record' command. Each record contains
+ * up to 256 entries describing offset and size of the AUXTRACE data in the
* perf.data file.
* PERF_RECORD_AUXTRACE_ERROR:
* Indicates an error during AUXTRACE collection such as buffer overflow.
@@ -146,6 +146,7 @@
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/log2.h>
+#include <linux/zalloc.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -156,8 +157,7 @@
#include "evlist.h"
#include "machine.h"
#include "session.h"
-#include "util.h"
-#include "thread.h"
+#include "tool.h"
#include "debug.h"
#include "auxtrace.h"
#include "s390-cpumsf.h"
@@ -237,10 +237,33 @@ static int s390_cpumcf_dumpctr(struct s390_cpumsf *sf,
return rc;
}
-/* Display s390 CPU measurement facility basic-sampling data entry */
+/* Display s390 CPU measurement facility basic-sampling data entry
+ * Data written on s390 in big endian byte order and contains bit
+ * fields across byte boundaries.
+ */
static bool s390_cpumsf_basic_show(const char *color, size_t pos,
- struct hws_basic_entry *basic)
+ struct hws_basic_entry *basicp)
{
+ struct hws_basic_entry *basic = basicp;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ struct hws_basic_entry local;
+ unsigned long long word = be64toh(*(unsigned long long *)basicp);
+
+ memset(&local, 0, sizeof(local));
+ local.def = be16toh(basicp->def);
+ local.prim_asn = word & 0xffff;
+ local.CL = word >> 30 & 0x3;
+ local.I = word >> 32 & 0x1;
+ local.AS = word >> 33 & 0x3;
+ local.P = word >> 35 & 0x1;
+ local.W = word >> 36 & 0x1;
+ local.T = word >> 37 & 0x1;
+ local.U = word >> 40 & 0xf;
+ local.ia = be64toh(basicp->ia);
+ local.gpp = be64toh(basicp->gpp);
+ local.hpp = be64toh(basicp->hpp);
+ basic = &local;
+#endif
if (basic->def != 1) {
pr_err("Invalid AUX trace basic entry [%#08zx]\n", pos);
return false;
@@ -258,10 +281,22 @@ static bool s390_cpumsf_basic_show(const char *color, size_t pos,
return true;
}
-/* Display s390 CPU measurement facility diagnostic-sampling data entry */
+/* Display s390 CPU measurement facility diagnostic-sampling data entry.
+ * Data written on s390 in big endian byte order and contains bit
+ * fields across byte boundaries.
+ */
static bool s390_cpumsf_diag_show(const char *color, size_t pos,
- struct hws_diag_entry *diag)
+ struct hws_diag_entry *diagp)
{
+ struct hws_diag_entry *diag = diagp;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ struct hws_diag_entry local;
+ unsigned long long word = be64toh(*(unsigned long long *)diagp);
+
+ local.def = be16toh(diagp->def);
+ local.I = word >> 32 & 0x1;
+ diag = &local;
+#endif
if (diag->def < S390_CPUMSF_DIAG_DEF_FIRST) {
pr_err("Invalid AUX trace diagnostic entry [%#08zx]\n", pos);
return false;
@@ -272,35 +307,52 @@ static bool s390_cpumsf_diag_show(const char *color, size_t pos,
}
/* Return TOD timestamp contained in an trailer entry */
-static unsigned long long trailer_timestamp(struct hws_trailer_entry *te)
+static unsigned long long trailer_timestamp(struct hws_trailer_entry *te,
+ int idx)
{
/* te->t set: TOD in STCKE format, bytes 8-15
* to->t not set: TOD in STCK format, bytes 0-7
*/
unsigned long long ts;
- memcpy(&ts, &te->timestamp[te->t], sizeof(ts));
- return ts;
+ memcpy(&ts, &te->timestamp[idx], sizeof(ts));
+ return be64toh(ts);
}
/* Display s390 CPU measurement facility trailer entry */
static bool s390_cpumsf_trailer_show(const char *color, size_t pos,
struct hws_trailer_entry *te)
{
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ struct hws_trailer_entry local;
+ const unsigned long long flags = be64toh(te->flags);
+
+ memset(&local, 0, sizeof(local));
+ local.f = flags >> 63 & 0x1;
+ local.a = flags >> 62 & 0x1;
+ local.t = flags >> 61 & 0x1;
+ local.bsdes = be16toh((flags >> 16 & 0xffff));
+ local.dsdes = be16toh((flags & 0xffff));
+ memcpy(&local.timestamp, te->timestamp, sizeof(te->timestamp));
+ local.overflow = be64toh(te->overflow);
+ local.clock_base = be64toh(te->progusage[0]) >> 63 & 1;
+ local.progusage2 = be64toh(te->progusage2);
+ te = &local;
+#endif
if (te->bsdes != sizeof(struct hws_basic_entry)) {
pr_err("Invalid AUX trace trailer entry [%#08zx]\n", pos);
return false;
}
color_fprintf(stdout, color, " [%#08zx] Trailer %c%c%c bsdes:%d"
" dsdes:%d Overflow:%lld Time:%#llx\n"
- "\t\tC:%d TOD:%#lx 1:%#llx 2:%#llx\n",
+ "\t\tC:%d TOD:%#lx\n",
pos,
te->f ? 'F' : ' ',
te->a ? 'A' : ' ',
te->t ? 'T' : ' ',
te->bsdes, te->dsdes, te->overflow,
- trailer_timestamp(te), te->clock_base, te->progusage2,
- te->progusage[0], te->progusage[1]);
+ trailer_timestamp(te, te->clock_base),
+ te->clock_base, te->progusage2);
return true;
}
@@ -327,13 +379,13 @@ static bool s390_cpumsf_validate(int machine_type,
*dsdes = *bsdes = 0;
if (len & (S390_CPUMSF_PAGESZ - 1)) /* Illegal size */
return false;
- if (basic->def != 1) /* No basic set entry, must be first */
+ if (be16toh(basic->def) != 1) /* No basic set entry, must be first */
return false;
/* Check for trailer entry at end of SDB */
te = (struct hws_trailer_entry *)(buf + S390_CPUMSF_PAGESZ
- sizeof(*te));
- *bsdes = te->bsdes;
- *dsdes = te->dsdes;
+ *bsdes = be16toh(te->bsdes);
+ *dsdes = be16toh(te->dsdes);
if (!te->bsdes && !te->dsdes) {
/* Very old hardware, use CPUID */
switch (machine_type) {
@@ -495,19 +547,27 @@ static bool s390_cpumsf_make_event(size_t pos,
static unsigned long long get_trailer_time(const unsigned char *buf)
{
struct hws_trailer_entry *te;
- unsigned long long aux_time;
+ unsigned long long aux_time, progusage2;
+ bool clock_base;
te = (struct hws_trailer_entry *)(buf + S390_CPUMSF_PAGESZ
- sizeof(*te));
- if (!te->clock_base) /* TOD_CLOCK_BASE value missing */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ clock_base = be64toh(te->progusage[0]) >> 63 & 0x1;
+ progusage2 = be64toh(te->progusage[1]);
+#else
+ clock_base = te->clock_base;
+ progusage2 = te->progusage2;
+#endif
+ if (!clock_base) /* TOD_CLOCK_BASE value missing */
return 0;
/* Correct calculation to convert time stamp in trailer entry to
* nano seconds (taken from arch/s390 function tod_to_ns()).
* TOD_CLOCK_BASE is stored in trailer entry member progusage2.
*/
- aux_time = trailer_timestamp(te) - te->progusage2;
+ aux_time = trailer_timestamp(te, clock_base) - progusage2;
aux_time = (aux_time >> 9) * 125 + (((aux_time & 0x1ff) * 125) >> 9);
return aux_time;
}
@@ -696,7 +756,7 @@ static int s390_cpumsf_run_decoder(struct s390_cpumsf_queue *sfq,
*/
if (err) {
sfq->buffer = NULL;
- list_del(&buffer->list);
+ list_del_init(&buffer->list);
auxtrace_buffer__free(buffer);
if (err > 0) /* Buffer done, no error */
err = 0;
@@ -858,7 +918,7 @@ s390_cpumsf_process_event(struct perf_session *session,
struct s390_cpumsf,
auxtrace);
u64 timestamp = sample->time;
- struct perf_evsel *ev_bc000;
+ struct evsel *ev_bc000;
int err = 0;
@@ -875,7 +935,7 @@ s390_cpumsf_process_event(struct perf_session *session,
/* Handle event with raw data */
ev_bc000 = perf_evlist__event2evsel(session->evlist, event);
if (ev_bc000 &&
- ev_bc000->attr.config == PERF_EVENT_CPUM_CF_DIAG)
+ ev_bc000->core.attr.config == PERF_EVENT_CPUM_CF_DIAG)
err = s390_cpumcf_dumpctr(sf, sample);
return err;
}
@@ -984,7 +1044,7 @@ static void s390_cpumsf_free(struct perf_session *session)
auxtrace_heap__free(&sf->heap);
s390_cpumsf_free_queues(session);
session->auxtrace = NULL;
- free(sf->logdir);
+ zfree(&sf->logdir);
free(sf);
}
@@ -1041,8 +1101,7 @@ static int s390_cpumsf__config(const char *var, const char *value, void *cb)
if (rc == -1 || !S_ISDIR(stbuf.st_mode)) {
pr_err("Missing auxtrace log directory %s,"
" continue with current directory...\n", value);
- free(sf->logdir);
- sf->logdir = NULL;
+ zfree(&sf->logdir);
}
return 1;
}
@@ -1050,11 +1109,11 @@ static int s390_cpumsf__config(const char *var, const char *value, void *cb)
int s390_cpumsf_process_auxtrace_info(union perf_event *event,
struct perf_session *session)
{
- struct auxtrace_info_event *auxtrace_info = &event->auxtrace_info;
+ struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
struct s390_cpumsf *sf;
int err;
- if (auxtrace_info->header.size < sizeof(struct auxtrace_info_event))
+ if (auxtrace_info->header.size < sizeof(struct perf_record_auxtrace_info))
return -EINVAL;
sf = zalloc(sizeof(struct s390_cpumsf));
@@ -1102,7 +1161,7 @@ err_free_queues:
auxtrace_queues__free(&sf->queues);
session->auxtrace = NULL;
err_free:
- free(sf->logdir);
+ zfree(&sf->logdir);
free(sf);
return err;
}
diff --git a/tools/perf/util/s390-sample-raw.c b/tools/perf/util/s390-sample-raw.c
index 6650f599ed9c..4d9593e331ea 100644
--- a/tools/perf/util/s390-sample-raw.c
+++ b/tools/perf/util/s390-sample-raw.c
@@ -23,10 +23,8 @@
#include "debug.h"
#include "util.h"
-#include "auxtrace.h"
#include "session.h"
#include "evlist.h"
-#include "config.h"
#include "color.h"
#include "sample-raw.h"
#include "s390-cpumcf-kernel.h"
@@ -200,17 +198,17 @@ static void s390_cpumcfdg_dump(struct perf_sample *sample)
* its raw data.
* The function is only invoked when the dump flag -D is set.
*/
-void perf_evlist__s390_sample_raw(struct perf_evlist *evlist, union perf_event *event,
+void perf_evlist__s390_sample_raw(struct evlist *evlist, union perf_event *event,
struct perf_sample *sample)
{
- struct perf_evsel *ev_bc000;
+ struct evsel *ev_bc000;
if (event->header.type != PERF_RECORD_SAMPLE)
return;
ev_bc000 = perf_evlist__event2evsel(evlist, event);
if (ev_bc000 == NULL ||
- ev_bc000->attr.config != PERF_EVENT_CPUM_CF_DIAG)
+ ev_bc000->core.attr.config != PERF_EVENT_CPUM_CF_DIAG)
return;
/* Display raw data on screen */
diff --git a/tools/perf/util/sample-raw.c b/tools/perf/util/sample-raw.c
index c21e1311fb0f..e84bbe0e441a 100644
--- a/tools/perf/util/sample-raw.c
+++ b/tools/perf/util/sample-raw.c
@@ -9,7 +9,7 @@
* Check platform the perf data file was created on and perform platform
* specific interpretation.
*/
-void perf_evlist__init_trace_event_sample_raw(struct perf_evlist *evlist)
+void perf_evlist__init_trace_event_sample_raw(struct evlist *evlist)
{
const char *arch_pf = perf_env__arch(evlist->env);
diff --git a/tools/perf/util/sample-raw.h b/tools/perf/util/sample-raw.h
index 95d445c87e93..afe1491a117e 100644
--- a/tools/perf/util/sample-raw.h
+++ b/tools/perf/util/sample-raw.h
@@ -2,13 +2,13 @@
#ifndef __SAMPLE_RAW_H
#define __SAMPLE_RAW_H 1
-struct perf_evlist;
+struct evlist;
union perf_event;
struct perf_sample;
-void perf_evlist__s390_sample_raw(struct perf_evlist *evlist,
+void perf_evlist__s390_sample_raw(struct evlist *evlist,
union perf_event *event,
struct perf_sample *sample);
-void perf_evlist__init_trace_event_sample_raw(struct perf_evlist *evlist);
+void perf_evlist__init_trace_event_sample_raw(struct evlist *evlist);
#endif /* __PERF_EVLIST_H */
diff --git a/tools/perf/util/sane_ctype.h b/tools/perf/util/sane_ctype.h
deleted file mode 100644
index c2b42ff9ff32..000000000000
--- a/tools/perf/util/sane_ctype.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _PERF_SANE_CTYPE_H
-#define _PERF_SANE_CTYPE_H
-
-extern const char *graph_line;
-extern const char *graph_dotted_line;
-extern const char *spaces;
-extern const char *dots;
-
-/* Sane ctype - no locale, and works with signed chars */
-#undef isascii
-#undef isspace
-#undef isdigit
-#undef isxdigit
-#undef isalpha
-#undef isprint
-#undef isalnum
-#undef islower
-#undef isupper
-#undef tolower
-#undef toupper
-
-extern unsigned char sane_ctype[256];
-#define GIT_SPACE 0x01
-#define GIT_DIGIT 0x02
-#define GIT_ALPHA 0x04
-#define GIT_GLOB_SPECIAL 0x08
-#define GIT_REGEX_SPECIAL 0x10
-#define GIT_PRINT_EXTRA 0x20
-#define GIT_PRINT 0x3E
-#define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
-#define isascii(x) (((x) & ~0x7f) == 0)
-#define isspace(x) sane_istest(x,GIT_SPACE)
-#define isdigit(x) sane_istest(x,GIT_DIGIT)
-#define isxdigit(x) \
- (sane_istest(toupper(x), GIT_ALPHA | GIT_DIGIT) && toupper(x) < 'G')
-#define isalpha(x) sane_istest(x,GIT_ALPHA)
-#define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
-#define isprint(x) sane_istest(x,GIT_PRINT)
-#define islower(x) (sane_istest(x,GIT_ALPHA) && (x & 0x20))
-#define isupper(x) (sane_istest(x,GIT_ALPHA) && !(x & 0x20))
-#define tolower(x) sane_case((unsigned char)(x), 0x20)
-#define toupper(x) sane_case((unsigned char)(x), 0)
-
-static inline int sane_case(int x, int high)
-{
- if (sane_istest(x, GIT_ALPHA))
- x = (x & ~0x20) | high;
- return x;
-}
-
-#endif /* _PERF_SANE_CTYPE_H */
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c
index 61aa7f3df915..15961854ba67 100644
--- a/tools/perf/util/scripting-engines/trace-event-perl.c
+++ b/tools/perf/util/scripting-engines/trace-event-perl.c
@@ -34,8 +34,8 @@
#include <EXTERN.h>
#include <perl.h>
-#include "../../perf.h"
#include "../callchain.h"
+#include "../dso.h"
#include "../machine.h"
#include "../map.h"
#include "../symbol.h"
@@ -258,7 +258,7 @@ static void define_event_symbols(struct tep_event *event,
}
static SV *perl_process_callchain(struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct addr_location *al)
{
AV *list;
@@ -336,7 +336,7 @@ exit:
}
static void perl_process_tracepoint(struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct addr_location *al)
{
struct thread *thread = al->thread;
@@ -353,11 +353,11 @@ static void perl_process_tracepoint(struct perf_sample *sample,
dSP;
- if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
+ if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
return;
if (!event) {
- pr_debug("ug! no event found for type %" PRIu64, (u64)evsel->attr.config);
+ pr_debug("ug! no event found for type %" PRIu64, (u64)evsel->core.attr.config);
return;
}
@@ -431,7 +431,7 @@ static void perl_process_tracepoint(struct perf_sample *sample,
static void perl_process_event_generic(union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel)
+ struct evsel *evsel)
{
dSP;
@@ -442,7 +442,7 @@ static void perl_process_event_generic(union perf_event *event,
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVpvn((const char *)event, event->header.size)));
- XPUSHs(sv_2mortal(newSVpvn((const char *)&evsel->attr, sizeof(evsel->attr))));
+ XPUSHs(sv_2mortal(newSVpvn((const char *)&evsel->core.attr, sizeof(evsel->core.attr))));
XPUSHs(sv_2mortal(newSVpvn((const char *)sample, sizeof(*sample))));
XPUSHs(sv_2mortal(newSVpvn((const char *)sample->raw_data, sample->raw_size)));
PUTBACK;
@@ -455,7 +455,7 @@ static void perl_process_event_generic(union perf_event *event,
static void perl_process_event(union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct addr_location *al)
{
perl_process_tracepoint(sample, evsel, al);
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 22f52b669871..666a56e88d8e 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -31,8 +31,10 @@
#include <linux/compiler.h>
#include <linux/time64.h>
-#include "../../perf.h"
+#include "../build-id.h"
+#include "../counts.h"
#include "../debug.h"
+#include "../dso.h"
#include "../callchain.h"
#include "../evsel.h"
#include "../util.h"
@@ -112,6 +114,8 @@ struct tables {
PyObject *sample_handler;
PyObject *call_path_handler;
PyObject *call_return_handler;
+ PyObject *synth_handler;
+ PyObject *context_switch_handler;
bool db_export_mode;
};
@@ -390,7 +394,7 @@ static const char *get_dsoname(struct map *map)
}
static PyObject *python_process_callchain(struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct addr_location *al)
{
PyObject *pylist;
@@ -632,9 +636,9 @@ static PyObject *get_sample_value_as_tuple(struct sample_read_value *value)
static void set_sample_read_in_dict(PyObject *dict_sample,
struct perf_sample *sample,
- struct perf_evsel *evsel)
+ struct evsel *evsel)
{
- u64 read_format = evsel->attr.read_format;
+ u64 read_format = evsel->core.attr.read_format;
PyObject *values;
unsigned int i;
@@ -703,9 +707,9 @@ static int regs_map(struct regs_dump *regs, uint64_t mask, char *bf, int size)
static void set_regs_in_dict(PyObject *dict,
struct perf_sample *sample,
- struct perf_evsel *evsel)
+ struct evsel *evsel)
{
- struct perf_event_attr *attr = &evsel->attr;
+ struct perf_event_attr *attr = &evsel->core.attr;
char bf[512];
regs_map(&sample->intr_regs, attr->sample_regs_intr, bf, sizeof(bf));
@@ -720,7 +724,7 @@ static void set_regs_in_dict(PyObject *dict,
}
static PyObject *get_perf_sample_dict(struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct addr_location *al,
PyObject *callchain)
{
@@ -735,7 +739,7 @@ static PyObject *get_perf_sample_dict(struct perf_sample *sample,
Py_FatalError("couldn't create Python dictionary");
pydict_set_item_string_decref(dict, "ev_name", _PyUnicode_FromString(perf_evsel__name(evsel)));
- pydict_set_item_string_decref(dict, "attr", _PyBytes_FromStringAndSize((const char *)&evsel->attr, sizeof(evsel->attr)));
+ pydict_set_item_string_decref(dict, "attr", _PyBytes_FromStringAndSize((const char *)&evsel->core.attr, sizeof(evsel->core.attr)));
pydict_set_item_string_decref(dict_sample, "pid",
_PyLong_FromLong(sample->pid));
@@ -788,7 +792,7 @@ static PyObject *get_perf_sample_dict(struct perf_sample *sample,
}
static void python_process_tracepoint(struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct addr_location *al)
{
struct tep_event *event = evsel->tp_format;
@@ -807,7 +811,7 @@ static void python_process_tracepoint(struct perf_sample *sample,
if (!event) {
snprintf(handler_name, sizeof(handler_name),
- "ug! no event found for type %" PRIu64, (u64)evsel->attr.config);
+ "ug! no event found for type %" PRIu64, (u64)evsel->core.attr.config);
Py_FatalError(handler_name);
}
@@ -947,7 +951,13 @@ static int tuple_set_string(PyObject *t, unsigned int pos, const char *s)
return PyTuple_SetItem(t, pos, _PyUnicode_FromString(s));
}
-static int python_export_evsel(struct db_export *dbe, struct perf_evsel *evsel)
+static int tuple_set_bytes(PyObject *t, unsigned int pos, void *bytes,
+ unsigned int sz)
+{
+ return PyTuple_SetItem(t, pos, _PyBytes_FromStringAndSize(bytes, sz));
+}
+
+static int python_export_evsel(struct db_export *dbe, struct evsel *evsel)
{
struct tables *tables = container_of(dbe, struct tables, dbe);
PyObject *t;
@@ -1004,15 +1014,19 @@ static int python_export_thread(struct db_export *dbe, struct thread *thread,
return 0;
}
-static int python_export_comm(struct db_export *dbe, struct comm *comm)
+static int python_export_comm(struct db_export *dbe, struct comm *comm,
+ struct thread *thread)
{
struct tables *tables = container_of(dbe, struct tables, dbe);
PyObject *t;
- t = tuple_new(2);
+ t = tuple_new(5);
tuple_set_u64(t, 0, comm->db_id);
tuple_set_string(t, 1, comm__str(comm));
+ tuple_set_u64(t, 2, thread->db_id);
+ tuple_set_u64(t, 3, comm->start);
+ tuple_set_s32(t, 4, comm->exec);
call_object(tables->comm_handler, t, "comm_table");
@@ -1105,13 +1119,13 @@ static int python_export_branch_type(struct db_export *dbe, u32 branch_type,
return 0;
}
-static int python_export_sample(struct db_export *dbe,
- struct export_sample *es)
+static void python_export_sample_table(struct db_export *dbe,
+ struct export_sample *es)
{
struct tables *tables = container_of(dbe, struct tables, dbe);
PyObject *t;
- t = tuple_new(22);
+ t = tuple_new(24);
tuple_set_u64(t, 0, es->db_id);
tuple_set_u64(t, 1, es->evsel->db_id);
@@ -1135,10 +1149,39 @@ static int python_export_sample(struct db_export *dbe,
tuple_set_s32(t, 19, es->sample->flags & PERF_BRANCH_MASK);
tuple_set_s32(t, 20, !!(es->sample->flags & PERF_IP_FLAG_IN_TX));
tuple_set_u64(t, 21, es->call_path_id);
+ tuple_set_u64(t, 22, es->sample->insn_cnt);
+ tuple_set_u64(t, 23, es->sample->cyc_cnt);
call_object(tables->sample_handler, t, "sample_table");
Py_DECREF(t);
+}
+
+static void python_export_synth(struct db_export *dbe, struct export_sample *es)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(3);
+
+ tuple_set_u64(t, 0, es->db_id);
+ tuple_set_u64(t, 1, es->evsel->core.attr.config);
+ tuple_set_bytes(t, 2, es->sample->raw_data, es->sample->raw_size);
+
+ call_object(tables->synth_handler, t, "synth_data");
+
+ Py_DECREF(t);
+}
+
+static int python_export_sample(struct db_export *dbe,
+ struct export_sample *es)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+
+ python_export_sample_table(dbe, es);
+
+ if (es->evsel->core.attr.type == PERF_TYPE_SYNTH && tables->synth_handler)
+ python_export_synth(dbe, es);
return 0;
}
@@ -1173,7 +1216,7 @@ static int python_export_call_return(struct db_export *dbe,
u64 comm_db_id = cr->comm ? cr->comm->db_id : 0;
PyObject *t;
- t = tuple_new(12);
+ t = tuple_new(14);
tuple_set_u64(t, 0, cr->db_id);
tuple_set_u64(t, 1, cr->thread->db_id);
@@ -1187,6 +1230,8 @@ static int python_export_call_return(struct db_export *dbe,
tuple_set_u64(t, 9, cr->cp->parent->db_id);
tuple_set_s32(t, 10, cr->flags);
tuple_set_u64(t, 11, cr->parent_db_id);
+ tuple_set_u64(t, 12, cr->insn_count);
+ tuple_set_u64(t, 13, cr->cyc_count);
call_object(tables->call_return_handler, t, "call_return_table");
@@ -1195,6 +1240,34 @@ static int python_export_call_return(struct db_export *dbe,
return 0;
}
+static int python_export_context_switch(struct db_export *dbe, u64 db_id,
+ struct machine *machine,
+ struct perf_sample *sample,
+ u64 th_out_id, u64 comm_out_id,
+ u64 th_in_id, u64 comm_in_id, int flags)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(9);
+
+ tuple_set_u64(t, 0, db_id);
+ tuple_set_u64(t, 1, machine->db_id);
+ tuple_set_u64(t, 2, sample->time);
+ tuple_set_s32(t, 3, sample->cpu);
+ tuple_set_u64(t, 4, th_out_id);
+ tuple_set_u64(t, 5, comm_out_id);
+ tuple_set_u64(t, 6, th_in_id);
+ tuple_set_u64(t, 7, comm_in_id);
+ tuple_set_s32(t, 8, flags);
+
+ call_object(tables->context_switch_handler, t, "context_switch");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
static int python_process_call_return(struct call_return *cr, u64 *parent_db_id,
void *data)
{
@@ -1204,7 +1277,7 @@ static int python_process_call_return(struct call_return *cr, u64 *parent_db_id,
}
static void python_process_general_event(struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct addr_location *al)
{
PyObject *handler, *t, *dict, *callchain;
@@ -1240,12 +1313,12 @@ static void python_process_general_event(struct perf_sample *sample,
static void python_process_event(union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct addr_location *al)
{
struct tables *tables = &tables_global;
- switch (evsel->attr.type) {
+ switch (evsel->core.attr.type) {
case PERF_TYPE_TRACEPOINT:
python_process_tracepoint(sample, evsel, al);
break;
@@ -1258,8 +1331,18 @@ static void python_process_event(union perf_event *event,
}
}
+static void python_process_switch(union perf_event *event,
+ struct perf_sample *sample,
+ struct machine *machine)
+{
+ struct tables *tables = &tables_global;
+
+ if (tables->db_export_mode)
+ db_export__switch(&tables->dbe, event, sample, machine);
+}
+
static void get_handler_name(char *str, size_t size,
- struct perf_evsel *evsel)
+ struct evsel *evsel)
{
char *p = str;
@@ -1272,7 +1355,7 @@ static void get_handler_name(char *str, size_t size,
}
static void
-process_stat(struct perf_evsel *counter, int cpu, int thread, u64 tstamp,
+process_stat(struct evsel *counter, int cpu, int thread, u64 tstamp,
struct perf_counts_values *count)
{
PyObject *handler, *t;
@@ -1309,10 +1392,10 @@ process_stat(struct perf_evsel *counter, int cpu, int thread, u64 tstamp,
}
static void python_process_stat(struct perf_stat_config *config,
- struct perf_evsel *counter, u64 tstamp)
+ struct evsel *counter, u64 tstamp)
{
- struct thread_map *threads = counter->threads;
- struct cpu_map *cpus = counter->cpus;
+ struct perf_thread_map *threads = counter->core.threads;
+ struct perf_cpu_map *cpus = counter->core.cpus;
int cpu, thread;
if (config->aggr_mode == AGGR_GLOBAL) {
@@ -1324,7 +1407,7 @@ static void python_process_stat(struct perf_stat_config *config,
for (thread = 0; thread < threads->nr; thread++) {
for (cpu = 0; cpu < cpus->nr; cpu++) {
process_stat(counter, cpus->map[cpu],
- thread_map__pid(threads, thread), tstamp,
+ perf_thread_map__pid(threads, thread), tstamp,
perf_counts(counter->counts, cpu, thread));
}
}
@@ -1473,6 +1556,15 @@ static void set_table_handlers(struct tables *tables)
SET_TABLE_HANDLER(sample);
SET_TABLE_HANDLER(call_path);
SET_TABLE_HANDLER(call_return);
+ SET_TABLE_HANDLER(context_switch);
+
+ /*
+ * Synthesized events are samples but with architecture-specific data
+ * stored in sample->raw_data. They are exported via
+ * python_export_sample() and consequently do not need a separate export
+ * callback.
+ */
+ tables->synth_handler = get_handler("synth_data");
}
#if PY_MAJOR_VERSION < 3
@@ -1574,9 +1666,7 @@ error:
static int python_flush_script(void)
{
- struct tables *tables = &tables_global;
-
- return db_export__flush(&tables->dbe);
+ return 0;
}
/*
@@ -1785,6 +1875,7 @@ struct scripting_ops python_scripting_ops = {
.flush_script = python_flush_script,
.stop_script = python_stop_script,
.process_event = python_process_event,
+ .process_switch = python_process_switch,
.process_stat = python_process_stat,
.process_stat_interval = python_process_stat_interval,
.generate_script = python_generate_script,
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 2310a1752983..e9e4a04f15db 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -1,15 +1,20 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <inttypes.h>
+#include <linux/err.h>
#include <linux/kernel.h>
-#include <traceevent/event-parse.h>
+#include <linux/zalloc.h>
#include <api/fs/fs.h>
#include <byteswap.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
+#include <perf/cpumap.h>
+#include "map_symbol.h"
+#include "branch.h"
+#include "debug.h"
#include "evlist.h"
#include "evsel.h"
#include "memswap.h"
@@ -17,8 +22,6 @@
#include "symbol.h"
#include "session.h"
#include "tool.h"
-#include "sort.h"
-#include "util.h"
#include "cpumap.h"
#include "perf_regs.h"
#include "asm/bug.h"
@@ -27,6 +30,9 @@
#include "thread-stack.h"
#include "sample-raw.h"
#include "stat.h"
+#include "util.h"
+#include "ui/progress.h"
+#include "../perf.h"
#include "arch/common.h"
#ifdef HAVE_ZSTD_SUPPORT
@@ -36,10 +42,16 @@ static int perf_session__process_compressed_event(struct perf_session *session,
void *src;
size_t decomp_size, src_size;
u64 decomp_last_rem = 0;
- size_t decomp_len = session->header.env.comp_mmap_len;
+ size_t mmap_len, decomp_len = session->header.env.comp_mmap_len;
struct decomp *decomp, *decomp_last = session->decomp_last;
- decomp = mmap(NULL, sizeof(struct decomp) + decomp_len, PROT_READ|PROT_WRITE,
+ if (decomp_last) {
+ decomp_last_rem = decomp_last->size - decomp_last->head;
+ decomp_len += decomp_last_rem;
+ }
+
+ mmap_len = sizeof(struct decomp) + decomp_len;
+ decomp = mmap(NULL, mmap_len, PROT_READ|PROT_WRITE,
MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
if (decomp == MAP_FAILED) {
pr_err("Couldn't allocate memory for decompression\n");
@@ -47,21 +59,21 @@ static int perf_session__process_compressed_event(struct perf_session *session,
}
decomp->file_pos = file_offset;
+ decomp->mmap_len = mmap_len;
decomp->head = 0;
- if (decomp_last) {
- decomp_last_rem = decomp_last->size - decomp_last->head;
+ if (decomp_last_rem) {
memcpy(decomp->data, &(decomp_last->data[decomp_last->head]), decomp_last_rem);
decomp->size = decomp_last_rem;
}
- src = (void *)event + sizeof(struct compressed_event);
- src_size = event->pack.header.size - sizeof(struct compressed_event);
+ src = (void *)event + sizeof(struct perf_record_compressed);
+ src_size = event->pack.header.size - sizeof(struct perf_record_compressed);
decomp_size = zstd_decompress_stream(&(session->zstd_data), src, src_size,
&(decomp->data[decomp_last_rem]), decomp_len - decomp_last_rem);
if (!decomp_size) {
- munmap(decomp, sizeof(struct decomp) + decomp_len);
+ munmap(decomp, mmap_len);
pr_err("Couldn't decompress data\n");
return -1;
}
@@ -145,10 +157,10 @@ static void perf_session__destroy_kernel_maps(struct perf_session *session)
static bool perf_session__has_comm_exec(struct perf_session *session)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(session->evlist, evsel) {
- if (evsel->attr.comm_exec)
+ if (evsel->core.attr.comm_exec)
return true;
}
@@ -255,15 +267,15 @@ static void perf_session__delete_threads(struct perf_session *session)
static void perf_session__release_decomp_events(struct perf_session *session)
{
struct decomp *next, *decomp;
- size_t decomp_len;
+ size_t mmap_len;
next = session->decomp;
- decomp_len = session->header.env.comp_mmap_len;
do {
decomp = next;
if (decomp == NULL)
break;
next = decomp->next;
- munmap(decomp, decomp_len + sizeof(struct decomp));
+ mmap_len = decomp->mmap_len;
+ munmap(decomp, mmap_len);
} while (1);
}
@@ -294,7 +306,7 @@ static int process_event_synth_tracing_data_stub(struct perf_session *session
static int process_event_synth_attr_stub(struct perf_tool *tool __maybe_unused,
union perf_event *event __maybe_unused,
- struct perf_evlist **pevlist
+ struct evlist **pevlist
__maybe_unused)
{
dump_printf(": unhandled!\n");
@@ -303,7 +315,7 @@ static int process_event_synth_attr_stub(struct perf_tool *tool __maybe_unused,
static int process_event_synth_event_update_stub(struct perf_tool *tool __maybe_unused,
union perf_event *event __maybe_unused,
- struct perf_evlist **pevlist
+ struct evlist **pevlist
__maybe_unused)
{
if (dump_trace)
@@ -316,7 +328,7 @@ static int process_event_synth_event_update_stub(struct perf_tool *tool __maybe_
static int process_event_sample_stub(struct perf_tool *tool __maybe_unused,
union perf_event *event __maybe_unused,
struct perf_sample *sample __maybe_unused,
- struct perf_evsel *evsel __maybe_unused,
+ struct evsel *evsel __maybe_unused,
struct machine *machine __maybe_unused)
{
dump_printf(": unhandled!\n");
@@ -465,8 +477,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
tool->context_switch = perf_event__process_switch;
if (tool->ksymbol == NULL)
tool->ksymbol = perf_event__process_ksymbol;
- if (tool->bpf_event == NULL)
- tool->bpf_event = perf_event__process_bpf_event;
+ if (tool->bpf == NULL)
+ tool->bpf = perf_event__process_bpf;
if (tool->read == NULL)
tool->read = process_event_sample_stub;
if (tool->throttle == NULL)
@@ -647,6 +659,26 @@ static void perf_event__throttle_swap(union perf_event *event,
swap_sample_id_all(event, &event->throttle + 1);
}
+static void perf_event__namespaces_swap(union perf_event *event,
+ bool sample_id_all)
+{
+ u64 i;
+
+ event->namespaces.pid = bswap_32(event->namespaces.pid);
+ event->namespaces.tid = bswap_32(event->namespaces.tid);
+ event->namespaces.nr_namespaces = bswap_64(event->namespaces.nr_namespaces);
+
+ for (i = 0; i < event->namespaces.nr_namespaces; i++) {
+ struct perf_ns_link_info *ns = &event->namespaces.link_info[i];
+
+ ns->dev = bswap_64(ns->dev);
+ ns->ino = bswap_64(ns->ino);
+ }
+
+ if (sample_id_all)
+ swap_sample_id_all(event, &event->namespaces.link_info[i]);
+}
+
static u8 revbyte(u8 b)
{
int rev = (b >> 4) | ((b & 0xf) << 4);
@@ -807,9 +839,9 @@ static void perf_event__thread_map_swap(union perf_event *event,
static void perf_event__cpu_map_swap(union perf_event *event,
bool sample_id_all __maybe_unused)
{
- struct cpu_map_data *data = &event->cpu_map.data;
+ struct perf_record_cpu_map_data *data = &event->cpu_map.data;
struct cpu_map_entries *cpus;
- struct cpu_map_mask *mask;
+ struct perf_record_record_cpu_map *mask;
unsigned i;
data->type = bswap_64(data->type);
@@ -824,7 +856,7 @@ static void perf_event__cpu_map_swap(union perf_event *event,
cpus->cpu[i] = bswap_16(cpus->cpu[i]);
break;
case PERF_CPU_MAP__MASK:
- mask = (struct cpu_map_mask *) data->data;
+ mask = (struct perf_record_record_cpu_map *)data->data;
mask->nr = bswap_16(mask->nr);
mask->long_size = bswap_16(mask->long_size);
@@ -887,6 +919,7 @@ static perf_event__swap_op perf_event__swap_ops[] = {
[PERF_RECORD_LOST_SAMPLES] = perf_event__all64_swap,
[PERF_RECORD_SWITCH] = perf_event__switch_swap,
[PERF_RECORD_SWITCH_CPU_WIDE] = perf_event__switch_swap,
+ [PERF_RECORD_NAMESPACES] = perf_event__namespaces_swap,
[PERF_RECORD_HEADER_ATTR] = perf_event__hdr_attr_swap,
[PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap,
[PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap,
@@ -1006,7 +1039,7 @@ static void callchain__lbr_callstack_printf(struct perf_sample *sample)
}
}
-static void callchain__printf(struct perf_evsel *evsel,
+static void callchain__printf(struct evsel *evsel,
struct perf_sample *sample)
{
unsigned int i;
@@ -1022,23 +1055,30 @@ static void callchain__printf(struct perf_evsel *evsel,
i, callchain->ips[i]);
}
-static void branch_stack__printf(struct perf_sample *sample)
+static void branch_stack__printf(struct perf_sample *sample, bool callstack)
{
uint64_t i;
- printf("... branch stack: nr:%" PRIu64 "\n", sample->branch_stack->nr);
+ printf("%s: nr:%" PRIu64 "\n",
+ !callstack ? "... branch stack" : "... branch callstack",
+ sample->branch_stack->nr);
for (i = 0; i < sample->branch_stack->nr; i++) {
struct branch_entry *e = &sample->branch_stack->entries[i];
- printf("..... %2"PRIu64": %016" PRIx64 " -> %016" PRIx64 " %hu cycles %s%s%s%s %x\n",
- i, e->from, e->to,
- (unsigned short)e->flags.cycles,
- e->flags.mispred ? "M" : " ",
- e->flags.predicted ? "P" : " ",
- e->flags.abort ? "A" : " ",
- e->flags.in_tx ? "T" : " ",
- (unsigned)e->flags.reserved);
+ if (!callstack) {
+ printf("..... %2"PRIu64": %016" PRIx64 " -> %016" PRIx64 " %hu cycles %s%s%s%s %x\n",
+ i, e->from, e->to,
+ (unsigned short)e->flags.cycles,
+ e->flags.mispred ? "M" : " ",
+ e->flags.predicted ? "P" : " ",
+ e->flags.abort ? "A" : " ",
+ e->flags.in_tx ? "T" : " ",
+ (unsigned)e->flags.reserved);
+ } else {
+ printf("..... %2"PRIu64": %016" PRIx64 "\n",
+ i, i > 0 ? e->from : e->to);
+ }
}
}
@@ -1102,7 +1142,7 @@ static void stack_user__printf(struct stack_dump *dump)
dump->size, dump->offset);
}
-static void perf_evlist__print_tstamp(struct perf_evlist *evlist,
+static void perf_evlist__print_tstamp(struct evlist *evlist,
union perf_event *event,
struct perf_sample *sample)
{
@@ -1151,7 +1191,7 @@ static void sample_read__printf(struct perf_sample *sample, u64 read_format)
sample->read.one.id, sample->read.one.value);
}
-static void dump_event(struct perf_evlist *evlist, union perf_event *event,
+static void dump_event(struct evlist *evlist, union perf_event *event,
u64 file_offset, struct perf_sample *sample)
{
if (!dump_trace)
@@ -1171,7 +1211,7 @@ static void dump_event(struct perf_evlist *evlist, union perf_event *event,
event->header.size, perf_event__name(event->header.type));
}
-static void dump_sample(struct perf_evsel *evsel, union perf_event *event,
+static void dump_sample(struct evsel *evsel, union perf_event *event,
struct perf_sample *sample)
{
u64 sample_type;
@@ -1183,13 +1223,13 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,
event->header.misc, sample->pid, sample->tid, sample->ip,
sample->period, sample->addr);
- sample_type = evsel->attr.sample_type;
+ sample_type = evsel->core.attr.sample_type;
if (evsel__has_callchain(evsel))
callchain__printf(evsel, sample);
- if ((sample_type & PERF_SAMPLE_BRANCH_STACK) && !perf_evsel__has_branch_callstack(evsel))
- branch_stack__printf(sample);
+ if (sample_type & PERF_SAMPLE_BRANCH_STACK)
+ branch_stack__printf(sample, perf_evsel__has_branch_callstack(evsel));
if (sample_type & PERF_SAMPLE_REGS_USER)
regs_user__printf(sample);
@@ -1213,31 +1253,34 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,
printf("... transaction: %" PRIx64 "\n", sample->transaction);
if (sample_type & PERF_SAMPLE_READ)
- sample_read__printf(sample, evsel->attr.read_format);
+ sample_read__printf(sample, evsel->core.attr.read_format);
}
-static void dump_read(struct perf_evsel *evsel, union perf_event *event)
+static void dump_read(struct evsel *evsel, union perf_event *event)
{
- struct read_event *read_event = &event->read;
+ struct perf_record_read *read_event = &event->read;
u64 read_format;
if (!dump_trace)
return;
- printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid,
- evsel ? perf_evsel__name(evsel) : "FAIL",
+ printf(": %d %d %s %" PRI_lu64 "\n", event->read.pid, event->read.tid,
+ perf_evsel__name(evsel),
event->read.value);
- read_format = evsel->attr.read_format;
+ if (!evsel)
+ return;
+
+ read_format = evsel->core.attr.read_format;
if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
- printf("... time enabled : %" PRIu64 "\n", read_event->time_enabled);
+ printf("... time enabled : %" PRI_lu64 "\n", read_event->time_enabled);
if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
- printf("... time running : %" PRIu64 "\n", read_event->time_running);
+ printf("... time running : %" PRI_lu64 "\n", read_event->time_running);
if (read_format & PERF_FORMAT_ID)
- printf("... id : %" PRIu64 "\n", read_event->id);
+ printf("... id : %" PRI_lu64 "\n", read_event->id);
}
static struct machine *machines__find_for_cpumode(struct machines *machines,
@@ -1266,7 +1309,7 @@ static struct machine *machines__find_for_cpumode(struct machines *machines,
return &machines->host;
}
-static int deliver_sample_value(struct perf_evlist *evlist,
+static int deliver_sample_value(struct evlist *evlist,
struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
@@ -1296,7 +1339,7 @@ static int deliver_sample_value(struct perf_evlist *evlist,
return tool->sample(tool, event, sample, sid->evsel, machine);
}
-static int deliver_sample_group(struct perf_evlist *evlist,
+static int deliver_sample_group(struct evlist *evlist,
struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
@@ -1317,16 +1360,16 @@ static int deliver_sample_group(struct perf_evlist *evlist,
}
static int
- perf_evlist__deliver_sample(struct perf_evlist *evlist,
+ perf_evlist__deliver_sample(struct evlist *evlist,
struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct machine *machine)
{
/* We know evsel != NULL. */
- u64 sample_type = evsel->attr.sample_type;
- u64 read_format = evsel->attr.read_format;
+ u64 sample_type = evsel->core.attr.sample_type;
+ u64 read_format = evsel->core.attr.read_format;
/* Standard sample delivery. */
if (!(sample_type & PERF_SAMPLE_READ))
@@ -1342,12 +1385,12 @@ static int
}
static int machines__deliver_event(struct machines *machines,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
union perf_event *event,
struct perf_sample *sample,
struct perf_tool *tool, u64 file_offset)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct machine *machine;
dump_event(evlist, event, file_offset, sample);
@@ -1413,7 +1456,7 @@ static int machines__deliver_event(struct machines *machines,
case PERF_RECORD_KSYMBOL:
return tool->ksymbol(tool, event, sample, machine);
case PERF_RECORD_BPF_EVENT:
- return tool->bpf_event(tool, event, sample, machine);
+ return tool->bpf(tool, event, sample, machine);
default:
++evlist->stats.nr_unknown_events;
return -1;
@@ -1523,7 +1566,7 @@ int perf_session__deliver_synth_event(struct perf_session *session,
union perf_event *event,
struct perf_sample *sample)
{
- struct perf_evlist *evlist = session->evlist;
+ struct evlist *evlist = session->evlist;
struct perf_tool *tool = session->tool;
events_stats__inc(&evlist->stats, event->header.type);
@@ -1601,7 +1644,7 @@ out_parse_sample:
static s64 perf_session__process_event(struct perf_session *session,
union perf_event *event, u64 file_offset)
{
- struct perf_evlist *evlist = session->evlist;
+ struct evlist *evlist = session->evlist;
struct perf_tool *tool = session->tool;
int ret;
@@ -1675,11 +1718,11 @@ static void
perf_session__warn_order(const struct perf_session *session)
{
const struct ordered_events *oe = &session->ordered_events;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
bool should_warn = true;
evlist__for_each_entry(session->evlist, evsel) {
- if (evsel->attr.write_backward)
+ if (evsel->core.attr.write_backward)
should_warn = false;
}
@@ -1924,7 +1967,9 @@ fetch_mmaped_event(struct perf_session *session,
/* We're not fetching the event so swap back again */
if (session->header.needs_swap)
perf_event_header__bswap(&event->header);
- return NULL;
+ pr_debug("%s: head=%#" PRIx64 " event->header_size=%#x, mmap_size=%#zx: fuzzed perf.data?\n",
+ __func__, head, event->header.size, mmap_size);
+ return ERR_PTR(-EINVAL);
}
return event;
@@ -1942,6 +1987,9 @@ static int __perf_session__process_decomp_events(struct perf_session *session)
while (decomp->head < decomp->size && !session_done()) {
union perf_event *event = fetch_mmaped_event(session, decomp->head, decomp->size, decomp->data);
+ if (IS_ERR(event))
+ return PTR_ERR(event);
+
if (!event)
break;
@@ -2041,6 +2089,9 @@ remap:
more:
event = fetch_mmaped_event(session, head, mmap_size, buf);
+ if (IS_ERR(event))
+ return PTR_ERR(event);
+
if (!event) {
if (mmaps[map_idx]) {
munmap(mmaps[map_idx], mmap_size);
@@ -2153,10 +2204,10 @@ int perf_session__process_events(struct perf_session *session)
bool perf_session__has_traces(struct perf_session *session, const char *msg)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(session->evlist, evsel) {
- if (evsel->attr.type == PERF_TYPE_TRACEPOINT)
+ if (evsel->core.attr.type == PERF_TYPE_TRACEPOINT)
return true;
}
@@ -2227,13 +2278,13 @@ size_t perf_session__fprintf(struct perf_session *session, FILE *fp)
return machine__fprintf(&session->machines.host, fp);
}
-struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
+struct evsel *perf_session__find_first_evtype(struct perf_session *session,
unsigned int type)
{
- struct perf_evsel *pos;
+ struct evsel *pos;
evlist__for_each_entry(session->evlist, pos) {
- if (pos->attr.type == type)
+ if (pos->core.attr.type == type)
return pos;
}
return NULL;
@@ -2243,23 +2294,24 @@ int perf_session__cpu_bitmap(struct perf_session *session,
const char *cpu_list, unsigned long *cpu_bitmap)
{
int i, err = -1;
- struct cpu_map *map;
+ struct perf_cpu_map *map;
+ int nr_cpus = min(session->header.env.nr_cpus_online, MAX_NR_CPUS);
for (i = 0; i < PERF_TYPE_MAX; ++i) {
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evsel = perf_session__find_first_evtype(session, i);
if (!evsel)
continue;
- if (!(evsel->attr.sample_type & PERF_SAMPLE_CPU)) {
+ if (!(evsel->core.attr.sample_type & PERF_SAMPLE_CPU)) {
pr_err("File does not contain CPU events. "
"Remove -C option to proceed.\n");
return -1;
}
}
- map = cpu_map__new(cpu_list);
+ map = perf_cpu_map__new(cpu_list);
if (map == NULL) {
pr_err("Invalid cpu_list\n");
return -1;
@@ -2268,7 +2320,7 @@ int perf_session__cpu_bitmap(struct perf_session *session,
for (i = 0; i < map->nr; i++) {
int cpu = map->map[i];
- if (cpu >= MAX_NR_CPUS) {
+ if (cpu >= nr_cpus) {
pr_err("Requested CPU %d too large. "
"Consider raising MAX_NR_CPUS\n", cpu);
goto out_delete_map;
@@ -2280,7 +2332,7 @@ int perf_session__cpu_bitmap(struct perf_session *session,
err = 0;
out_delete_map:
- cpu_map__put(map);
+ perf_cpu_map__put(map);
return err;
}
@@ -2297,10 +2349,10 @@ void perf_session__fprintf_info(struct perf_session *session, FILE *fp,
int __perf_session__set_tracepoints_handlers(struct perf_session *session,
- const struct perf_evsel_str_handler *assocs,
+ const struct evsel_str_handler *assocs,
size_t nr_assocs)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
size_t i;
int err;
@@ -2327,11 +2379,11 @@ out:
int perf_event__process_id_index(struct perf_session *session,
union perf_event *event)
{
- struct perf_evlist *evlist = session->evlist;
- struct id_index_event *ie = &event->id_index;
+ struct evlist *evlist = session->evlist;
+ struct perf_record_id_index *ie = &event->id_index;
size_t i, nr, max_nr;
- max_nr = (ie->header.size - sizeof(struct id_index_event)) /
+ max_nr = (ie->header.size - sizeof(struct perf_record_id_index)) /
sizeof(struct id_index_entry);
nr = ie->nr;
if (nr > max_nr)
@@ -2345,10 +2397,10 @@ int perf_event__process_id_index(struct perf_session *session,
struct perf_sample_id *sid;
if (dump_trace) {
- fprintf(stdout, " ... id: %"PRIu64, e->id);
- fprintf(stdout, " idx: %"PRIu64, e->idx);
- fprintf(stdout, " cpu: %"PRId64, e->cpu);
- fprintf(stdout, " tid: %"PRId64"\n", e->tid);
+ fprintf(stdout, " ... id: %"PRI_lu64, e->id);
+ fprintf(stdout, " idx: %"PRI_lu64, e->idx);
+ fprintf(stdout, " cpu: %"PRI_ld64, e->cpu);
+ fprintf(stdout, " tid: %"PRI_ld64"\n", e->tid);
}
sid = perf_evlist__id2sid(evlist, e->id);
@@ -2363,24 +2415,24 @@ int perf_event__process_id_index(struct perf_session *session,
int perf_event__synthesize_id_index(struct perf_tool *tool,
perf_event__handler_t process,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
struct machine *machine)
{
union perf_event *ev;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
size_t nr = 0, i = 0, sz, max_nr, n;
int err;
pr_debug2("Synthesizing id index\n");
- max_nr = (UINT16_MAX - sizeof(struct id_index_event)) /
+ max_nr = (UINT16_MAX - sizeof(struct perf_record_id_index)) /
sizeof(struct id_index_entry);
evlist__for_each_entry(evlist, evsel)
nr += evsel->ids;
n = nr > max_nr ? max_nr : nr;
- sz = sizeof(struct id_index_event) + n * sizeof(struct id_index_entry);
+ sz = sizeof(struct perf_record_id_index) + n * sizeof(struct id_index_entry);
ev = zalloc(sz);
if (!ev)
return -ENOMEM;
@@ -2420,7 +2472,7 @@ int perf_event__synthesize_id_index(struct perf_tool *tool,
}
}
- sz = sizeof(struct id_index_event) + nr * sizeof(struct id_index_entry);
+ sz = sizeof(struct perf_record_id_index) + nr * sizeof(struct id_index_entry);
ev->id_index.header.size = sz;
ev->id_index.nr = nr;
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index dd8920b745bc..b7aa076ab6fd 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -23,12 +23,12 @@ struct itrace_synth_opts;
struct perf_session {
struct perf_header header;
struct machines machines;
- struct perf_evlist *evlist;
+ struct evlist *evlist;
struct auxtrace *auxtrace;
struct itrace_synth_opts *itrace_synth_opts;
struct list_head auxtrace_index;
struct trace_event tevent;
- struct time_conv_event time_conv;
+ struct perf_record_time_conv time_conv;
bool repipe;
bool one_mmap;
void *one_mmap_addr;
@@ -46,6 +46,7 @@ struct perf_session {
struct decomp {
struct decomp *next;
u64 file_pos;
+ size_t mmap_len;
u64 head;
size_t size;
char data[];
@@ -72,7 +73,7 @@ int perf_session__queue_event(struct perf_session *s, union perf_event *event,
void perf_tool__fill_defaults(struct perf_tool *tool);
int perf_session__resolve_callchain(struct perf_session *session,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct thread *thread,
struct ip_callchain *chain,
struct symbol **parent);
@@ -109,7 +110,7 @@ size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp
size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp);
-struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
+struct evsel *perf_session__find_first_evtype(struct perf_session *session,
unsigned int type);
int perf_session__cpu_bitmap(struct perf_session *session,
@@ -117,10 +118,10 @@ int perf_session__cpu_bitmap(struct perf_session *session,
void perf_session__fprintf_info(struct perf_session *s, FILE *fp, bool full);
-struct perf_evsel_str_handler;
+struct evsel_str_handler;
int __perf_session__set_tracepoints_handlers(struct perf_session *session,
- const struct perf_evsel_str_handler *assocs,
+ const struct evsel_str_handler *assocs,
size_t nr_assocs);
#define perf_session__set_tracepoints_handlers(session, array) \
@@ -139,7 +140,7 @@ int perf_event__process_id_index(struct perf_session *session,
int perf_event__synthesize_id_index(struct perf_tool *tool,
perf_event__handler_t process,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
struct machine *machine);
#endif /* __PERF_SESSION_H */
diff --git a/tools/perf/util/setns.c b/tools/perf/util/setns.c
index ce8fc290fce8..48f9c0af63b2 100644
--- a/tools/perf/util/setns.c
+++ b/tools/perf/util/setns.c
@@ -1,4 +1,6 @@
-#include "util.h"
+// SPDX-License-Identifier: LGPL-2.1
+
+#include "namespaces.h"
#include <unistd.h>
#include <sys/syscall.h>
diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py
index 5b5a167b43ce..aa344a163eaf 100644
--- a/tools/perf/util/setup.py
+++ b/tools/perf/util/setup.py
@@ -17,6 +17,8 @@ if cc == "clang":
vars[var] = sub("-fcf-protection", "", vars[var])
if not clang_has_option("-fstack-clash-protection"):
vars[var] = sub("-fstack-clash-protection", "", vars[var])
+ if not clang_has_option("-fstack-protector-strong"):
+ vars[var] = sub("-fstack-protector-strong", "", vars[var])
from distutils.core import setup, Extension
@@ -46,6 +48,7 @@ build_lib = getenv('PYTHON_EXTBUILD_LIB')
build_tmp = getenv('PYTHON_EXTBUILD_TMP')
libtraceevent = getenv('LIBTRACEEVENT')
libapikfs = getenv('LIBAPI')
+libperf = getenv('LIBPERF')
ext_sources = [f.strip() for f in open('util/python-ext-sources')
if len(f.strip()) > 0 and f[0] != '#']
@@ -56,13 +59,15 @@ ext_sources = list(map(lambda x: '%s/%s' % (src_perf, x) , ext_sources))
extra_libraries = []
if '-DHAVE_LIBNUMA_SUPPORT' in cflags:
extra_libraries = [ 'numa' ]
+if '-DHAVE_LIBCAP_SUPPORT' in cflags:
+ extra_libraries += [ 'cap' ]
perf = Extension('perf',
sources = ext_sources,
include_dirs = ['util/include'],
libraries = extra_libraries,
extra_compile_args = cflags,
- extra_objects = [libtraceevent, libapikfs],
+ extra_objects = [libtraceevent, libapikfs, libperf],
)
setup(name='perf',
diff --git a/tools/perf/util/smt.c b/tools/perf/util/smt.c
index 453f6f6f29f3..3b791ef2cd50 100644
--- a/tools/perf/util/smt.c
+++ b/tools/perf/util/smt.c
@@ -23,8 +23,12 @@ int smt_on(void)
char fn[256];
snprintf(fn, sizeof fn,
- "devices/system/cpu/cpu%d/topology/thread_siblings",
- cpu);
+ "devices/system/cpu/cpu%d/topology/core_cpus", cpu);
+ if (access(fn, F_OK) == -1) {
+ snprintf(fn, sizeof fn,
+ "devices/system/cpu/cpu%d/topology/thread_siblings",
+ cpu);
+ }
if (sysfs__read_str(fn, &str, &strlen) < 0)
continue;
/* Entry is hex, but does not have 0x, so need custom parser */
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 5d2518e89fc4..a2308eb77681 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -2,16 +2,23 @@
#include <errno.h>
#include <inttypes.h>
#include <regex.h>
+#include <stdlib.h>
#include <linux/mman.h>
#include <linux/time64.h>
+#include "debug.h"
+#include "dso.h"
#include "sort.h"
#include "hist.h"
+#include "cacheline.h"
#include "comm.h"
#include "map.h"
#include "symbol.h"
+#include "map_symbol.h"
+#include "branch.h"
#include "thread.h"
#include "evsel.h"
#include "evlist.h"
+#include "srcline.h"
#include "strlist.h"
#include "strbuf.h"
#include <traceevent/event-parse.h>
@@ -19,6 +26,7 @@
#include "annotate.h"
#include "time-utils.h"
#include <linux/kernel.h>
+#include <linux/string.h>
regex_t parent_regex;
const char default_parent_pattern[] = "^sys_|^do_page_fault";
@@ -668,17 +676,11 @@ sort__time_cmp(struct hist_entry *left, struct hist_entry *right)
static int hist_entry__time_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
- unsigned long secs;
- unsigned long long nsecs;
char he_time[32];
- nsecs = he->time;
- secs = nsecs / NSEC_PER_SEC;
- nsecs -= secs * NSEC_PER_SEC;
-
if (symbol_conf.nanosecs)
- snprintf(he_time, sizeof he_time, "%5lu.%09llu: ",
- secs, nsecs);
+ timestamp__scnprintf_nsec(he->time, he_time,
+ sizeof(he_time));
else
timestamp__scnprintf_usec(he->time, he_time,
sizeof(he_time));
@@ -698,7 +700,7 @@ struct sort_entry sort_time = {
static char *get_trace_output(struct hist_entry *he)
{
struct trace_seq seq;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct tep_record rec = {
.data = he->raw_data,
.size = he->raw_size,
@@ -711,7 +713,8 @@ static char *get_trace_output(struct hist_entry *he)
tep_print_fields(&seq, he->raw_data, he->raw_size,
evsel->tp_format);
} else {
- tep_event_info(&seq, evsel->tp_format, &rec);
+ tep_print_event(evsel->tp_format->tep,
+ &seq, &rec, "%s", TEP_PRINT_INFO);
}
/*
* Trim the buffer, it starts at 4KB and we're not going to
@@ -723,10 +726,10 @@ static char *get_trace_output(struct hist_entry *he)
static int64_t
sort__trace_cmp(struct hist_entry *left, struct hist_entry *right)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evsel = hists_to_evsel(left->hists);
- if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
+ if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
return 0;
if (left->trace_output == NULL)
@@ -740,10 +743,10 @@ sort__trace_cmp(struct hist_entry *left, struct hist_entry *right)
static int hist_entry__trace_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evsel = hists_to_evsel(he->hists);
- if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
+ if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
return scnprintf(bf, size, "%-.*s", width, "N/A");
if (he->trace_output == NULL)
@@ -1984,7 +1987,7 @@ static int __sort_dimension__add_hpp_output(struct sort_dimension *sd,
struct hpp_dynamic_entry {
struct perf_hpp_fmt hpp;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct tep_format_field *field;
unsigned dynamic_len;
bool raw_trace;
@@ -2218,7 +2221,7 @@ static void hde_free(struct perf_hpp_fmt *fmt)
}
static struct hpp_dynamic_entry *
-__alloc_dynamic_entry(struct perf_evsel *evsel, struct tep_format_field *field,
+__alloc_dynamic_entry(struct evsel *evsel, struct tep_format_field *field,
int level)
{
struct hpp_dynamic_entry *hde;
@@ -2313,17 +2316,17 @@ static int parse_field_name(char *str, char **event, char **field, char **opt)
* 2. full event name (e.g. sched:sched_switch)
* 3. partial event name (should not contain ':')
*/
-static struct perf_evsel *find_evsel(struct perf_evlist *evlist, char *event_name)
+static struct evsel *find_evsel(struct evlist *evlist, char *event_name)
{
- struct perf_evsel *evsel = NULL;
- struct perf_evsel *pos;
+ struct evsel *evsel = NULL;
+ struct evsel *pos;
bool full_name;
/* case 1 */
if (event_name[0] == '%') {
int nr = strtol(event_name+1, NULL, 0);
- if (nr > evlist->nr_entries)
+ if (nr > evlist->core.nr_entries)
return NULL;
evsel = perf_evlist__first(evlist);
@@ -2352,7 +2355,7 @@ static struct perf_evsel *find_evsel(struct perf_evlist *evlist, char *event_nam
return evsel;
}
-static int __dynamic_dimension__add(struct perf_evsel *evsel,
+static int __dynamic_dimension__add(struct evsel *evsel,
struct tep_format_field *field,
bool raw_trace, int level)
{
@@ -2368,7 +2371,7 @@ static int __dynamic_dimension__add(struct perf_evsel *evsel,
return 0;
}
-static int add_evsel_fields(struct perf_evsel *evsel, bool raw_trace, int level)
+static int add_evsel_fields(struct evsel *evsel, bool raw_trace, int level)
{
int ret;
struct tep_format_field *field;
@@ -2384,14 +2387,14 @@ static int add_evsel_fields(struct perf_evsel *evsel, bool raw_trace, int level)
return 0;
}
-static int add_all_dynamic_fields(struct perf_evlist *evlist, bool raw_trace,
+static int add_all_dynamic_fields(struct evlist *evlist, bool raw_trace,
int level)
{
int ret;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) {
- if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
+ if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
continue;
ret = add_evsel_fields(evsel, raw_trace, level);
@@ -2401,15 +2404,15 @@ static int add_all_dynamic_fields(struct perf_evlist *evlist, bool raw_trace,
return 0;
}
-static int add_all_matching_fields(struct perf_evlist *evlist,
+static int add_all_matching_fields(struct evlist *evlist,
char *field_name, bool raw_trace, int level)
{
int ret = -ESRCH;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct tep_format_field *field;
evlist__for_each_entry(evlist, evsel) {
- if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
+ if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
continue;
field = tep_find_any_field(evsel->tp_format, field_name);
@@ -2423,11 +2426,11 @@ static int add_all_matching_fields(struct perf_evlist *evlist,
return ret;
}
-static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok,
+static int add_dynamic_entry(struct evlist *evlist, const char *tok,
int level)
{
char *str, *event_name, *field_name, *opt_name;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
struct tep_format_field *field;
bool raw_trace = symbol_conf.raw_trace;
int ret = 0;
@@ -2470,7 +2473,7 @@ static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok,
goto out;
}
- if (evsel->attr.type != PERF_TYPE_TRACEPOINT) {
+ if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT) {
pr_debug("%s is not a tracepoint event\n", event_name);
ret = -EINVAL;
goto out;
@@ -2567,7 +2570,7 @@ int hpp_dimension__add_output(unsigned col)
}
int sort_dimension__add(struct perf_hpp_list *list, const char *tok,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
int level)
{
unsigned int i;
@@ -2663,7 +2666,7 @@ int sort_dimension__add(struct perf_hpp_list *list, const char *tok,
}
static int setup_sort_list(struct perf_hpp_list *list, char *str,
- struct perf_evlist *evlist)
+ struct evlist *evlist)
{
char *tmp, *tok;
int ret = 0;
@@ -2709,7 +2712,7 @@ static int setup_sort_list(struct perf_hpp_list *list, char *str,
return ret;
}
-static const char *get_default_sort_order(struct perf_evlist *evlist)
+static const char *get_default_sort_order(struct evlist *evlist)
{
const char *default_sort_orders[] = {
default_sort_order,
@@ -2720,7 +2723,7 @@ static const char *get_default_sort_order(struct perf_evlist *evlist)
default_tracepoint_sort_order,
};
bool use_trace = true;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders));
@@ -2728,7 +2731,7 @@ static const char *get_default_sort_order(struct perf_evlist *evlist)
goto out_no_evlist;
evlist__for_each_entry(evlist, evsel) {
- if (evsel->attr.type != PERF_TYPE_TRACEPOINT) {
+ if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT) {
use_trace = false;
break;
}
@@ -2743,7 +2746,7 @@ out_no_evlist:
return default_sort_orders[sort__mode];
}
-static int setup_sort_order(struct perf_evlist *evlist)
+static int setup_sort_order(struct evlist *evlist)
{
char *new_sort_order;
@@ -2804,7 +2807,7 @@ static char *setup_overhead(char *keys)
return keys;
}
-static int __setup_sorting(struct perf_evlist *evlist)
+static int __setup_sorting(struct evlist *evlist)
{
char *str;
const char *sort_keys;
@@ -3057,7 +3060,7 @@ out:
return ret;
}
-int setup_sorting(struct perf_evlist *evlist)
+int setup_sorting(struct evlist *evlist)
{
int err;
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index ce376a73f964..7b93f34ac1f4 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -1,29 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __PERF_SORT_H
#define __PERF_SORT_H
-#include "../builtin.h"
-
#include <regex.h>
-
-#include "color.h"
+#include <stdbool.h>
#include <linux/list.h>
-#include "cache.h"
#include <linux/rbtree.h>
#include "map_symbol.h"
#include "symbol_conf.h"
-#include "string.h"
#include "callchain.h"
#include "values.h"
-
-#include "../perf.h"
-#include "debug.h"
-#include "header.h"
-
-#include <subcmd/parse-options.h>
-#include "parse-events.h"
#include "hist.h"
-#include "srcline.h"
+struct option;
struct thread;
extern regex_t parent_regex;
@@ -79,6 +67,9 @@ struct hist_entry_diff {
/* HISTC_WEIGHTED_DIFF */
s64 wdiff;
+
+ /* PERF_HPP_DIFF__CYCLES */
+ s64 cycles;
};
};
@@ -144,6 +135,7 @@ struct hist_entry {
long time;
struct hists *hists;
struct mem_info *mem_info;
+ struct block_info *block_info;
void *raw_data;
u32 raw_size;
int num_res;
@@ -200,18 +192,6 @@ static inline float hist_entry__get_percent_limit(struct hist_entry *he)
return period * 100.0 / total_period;
}
-static inline u64 cl_address(u64 address)
-{
- /* return the cacheline of the address */
- return (address & ~(cacheline_size() - 1));
-}
-
-static inline u64 cl_offset(u64 address)
-{
- /* return the cacheline of the address */
- return (address & (cacheline_size() - 1));
-}
-
enum sort_mode {
SORT_MODE__NORMAL,
SORT_MODE__BRANCH,
@@ -285,12 +265,21 @@ struct sort_entry {
u8 se_width_idx;
};
+struct block_hist {
+ struct hists block_hists;
+ struct perf_hpp_list block_list;
+ struct perf_hpp_fmt block_fmt;
+ int block_idx;
+ bool valid;
+ struct hist_entry he;
+};
+
extern struct sort_entry sort_thread;
extern struct list_head hist_entry__sort_list;
-struct perf_evlist;
+struct evlist;
struct tep_handle;
-int setup_sorting(struct perf_evlist *evlist);
+int setup_sorting(struct evlist *evlist);
int setup_output_field(void);
void reset_output_field(void);
void sort__setup_elide(FILE *fp);
@@ -305,7 +294,7 @@ bool is_strict_order(const char *order);
int hpp_dimension__add_output(unsigned col);
void reset_dimensions(void);
int sort_dimension__add(struct perf_hpp_list *list, const char *tok,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
int level);
int output_field_add(struct perf_hpp_list *list, char *tok);
int64_t
diff --git a/tools/perf/util/srccode.c b/tools/perf/util/srccode.c
index fcc8630f6dff..adfcf1ff464c 100644
--- a/tools/perf/util/srccode.c
+++ b/tools/perf/util/srccode.c
@@ -1,18 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Manage printing of source lines
* Copyright (c) 2017, Intel Corporation.
* Author: Andi Kleen
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
-#include "linux/list.h"
+#include <linux/list.h>
+#include <linux/zalloc.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
@@ -90,12 +83,12 @@ static void fill_lines(char **lines, int maxline, char *map, int maplen)
static void free_srcfile(struct srcfile *sf)
{
- list_del(&sf->nd);
+ list_del_init(&sf->nd);
hlist_del(&sf->hash_nd);
map_total_sz -= sf->maplen;
munmap(sf->map, sf->maplen);
- free(sf->lines);
- free(sf->fn);
+ zfree(&sf->lines);
+ zfree(&sf->fn);
free(sf);
num_srcfiles--;
}
@@ -161,7 +154,7 @@ static struct srcfile *find_srcfile(char *fn)
out_map:
munmap(h->map, sz);
out_fn:
- free(h->fn);
+ zfree(&h->fn);
out_h:
free(h);
return NULL;
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c
index 10ca1533937e..6ccf6f6d09df 100644
--- a/tools/perf/util/srcline.c
+++ b/tools/perf/util/srcline.c
@@ -5,11 +5,13 @@
#include <string.h>
#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/zalloc.h>
#include "util/dso.h"
-#include "util/util.h"
#include "util/debug.h"
#include "util/callchain.h"
+#include "util/symbol_conf.h"
#include "srcline.h"
#include "string2.h"
#include "symbol.h"
@@ -287,7 +289,8 @@ static int addr2line(const char *dso_name, u64 addr,
}
if (a2l == NULL) {
- pr_warning("addr2line_init failed for %s\n", dso_name);
+ if (!symbol_conf.disable_add2line_warn)
+ pr_warning("addr2line_init failed for %s\n", dso_name);
return 0;
}
@@ -464,7 +467,7 @@ static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
char *srcline;
struct symbol *inline_sym;
- rtrim(funcname);
+ strim(funcname);
if (getline(&filename, &filelen, fp) == -1)
goto out;
diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c
index 4c53bae5644b..ed3b0ac2f785 100644
--- a/tools/perf/util/stat-display.c
+++ b/tools/perf/util/stat-display.c
@@ -1,8 +1,11 @@
+#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
+#include <linux/string.h>
#include <linux/time64.h>
#include <math.h>
#include "color.h"
+#include "counts.h"
#include "evlist.h"
#include "evsel.h"
#include "stat.h"
@@ -10,9 +13,8 @@
#include "thread_map.h"
#include "cpumap.h"
#include "string2.h"
-#include "sane_ctype.h"
+#include <linux/ctype.h>
#include "cgroup.h"
-#include <math.h>
#include <api/fs/fs.h>
#define CNTR_NOT_SUPPORTED "<not supported>"
@@ -44,7 +46,7 @@ static void print_noise_pct(struct perf_stat_config *config,
}
static void print_noise(struct perf_stat_config *config,
- struct perf_evsel *evsel, double avg)
+ struct evsel *evsel, double avg)
{
struct perf_stat_evsel *ps;
@@ -55,7 +57,7 @@ static void print_noise(struct perf_stat_config *config,
print_noise_pct(config, stddev_stats(&ps->res_stats[0]), avg);
}
-static void print_cgroup(struct perf_stat_config *config, struct perf_evsel *evsel)
+static void print_cgroup(struct perf_stat_config *config, struct evsel *evsel)
{
if (nr_cgroups) {
const char *cgrp_name = evsel->cgrp ? evsel->cgrp->name : "";
@@ -65,12 +67,13 @@ static void print_cgroup(struct perf_stat_config *config, struct perf_evsel *evs
static void aggr_printout(struct perf_stat_config *config,
- struct perf_evsel *evsel, int id, int nr)
+ struct evsel *evsel, int id, int nr)
{
switch (config->aggr_mode) {
case AGGR_CORE:
- fprintf(config->output, "S%d-C%*d%s%*d%s",
+ fprintf(config->output, "S%d-D%d-C%*d%s%*d%s",
cpu_map__id_to_socket(id),
+ cpu_map__id_to_die(id),
config->csv_output ? 0 : -8,
cpu_map__id_to_cpu(id),
config->csv_sep,
@@ -78,6 +81,16 @@ static void aggr_printout(struct perf_stat_config *config,
nr,
config->csv_sep);
break;
+ case AGGR_DIE:
+ fprintf(config->output, "S%d-D%*d%s%*d%s",
+ cpu_map__id_to_socket(id << 16),
+ config->csv_output ? 0 : -8,
+ cpu_map__id_to_die(id << 16),
+ config->csv_sep,
+ config->csv_output ? 0 : 4,
+ nr,
+ config->csv_sep);
+ break;
case AGGR_SOCKET:
fprintf(config->output, "S%*d%s%*d%s",
config->csv_output ? 0 : -5,
@@ -89,23 +102,24 @@ static void aggr_printout(struct perf_stat_config *config,
break;
case AGGR_NONE:
if (evsel->percore) {
- fprintf(config->output, "S%d-C%*d%s",
+ fprintf(config->output, "S%d-D%d-C%*d%s",
cpu_map__id_to_socket(id),
+ cpu_map__id_to_die(id),
config->csv_output ? 0 : -5,
cpu_map__id_to_cpu(id), config->csv_sep);
} else {
fprintf(config->output, "CPU%*d%s ",
config->csv_output ? 0 : -5,
- perf_evsel__cpus(evsel)->map[id],
+ evsel__cpus(evsel)->map[id],
config->csv_sep);
}
break;
case AGGR_THREAD:
fprintf(config->output, "%*s-%*d%s",
config->csv_output ? 0 : 16,
- thread_map__comm(evsel->threads, id),
+ perf_thread_map__comm(evsel->core.threads, id),
config->csv_output ? 0 : -8,
- thread_map__pid(evsel->threads, id),
+ perf_thread_map__pid(evsel->core.threads, id),
config->csv_sep);
break;
case AGGR_GLOBAL:
@@ -121,7 +135,7 @@ struct outstate {
const char *prefix;
int nfields;
int id, nr;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
};
#define METRIC_LEN 35
@@ -199,13 +213,11 @@ static void print_metric_csv(struct perf_stat_config *config __maybe_unused,
return;
}
snprintf(buf, sizeof(buf), fmt, val);
- ends = vals = ltrim(buf);
+ ends = vals = skip_spaces(buf);
while (isdigit(*ends) || *ends == '.')
ends++;
*ends = 0;
- while (isspace(*unit))
- unit++;
- fprintf(out, "%s%s%s%s", config->csv_sep, vals, config->csv_sep, unit);
+ fprintf(out, "%s%s%s%s", config->csv_sep, vals, config->csv_sep, skip_spaces(unit));
}
/* Filter out some columns that don't work well in metrics only mode */
@@ -222,7 +234,7 @@ static bool valid_only_metric(const char *unit)
return true;
}
-static const char *fixunit(char *buf, struct perf_evsel *evsel,
+static const char *fixunit(char *buf, struct evsel *evsel,
const char *unit)
{
if (!strncmp(unit, "of all", 6)) {
@@ -269,7 +281,7 @@ static void print_metric_only_csv(struct perf_stat_config *config __maybe_unused
return;
unit = fixunit(tbuf, os->evsel, unit);
snprintf(buf, sizeof buf, fmt, val);
- ends = vals = ltrim(buf);
+ ends = vals = skip_spaces(buf);
while (isdigit(*ends) || *ends == '.')
ends++;
*ends = 0;
@@ -299,9 +311,9 @@ static void print_metric_header(struct perf_stat_config *config,
}
static int first_shadow_cpu(struct perf_stat_config *config,
- struct perf_evsel *evsel, int id)
+ struct evsel *evsel, int id)
{
- struct perf_evlist *evlist = evsel->evlist;
+ struct evlist *evlist = evsel->evlist;
int i;
if (!config->aggr_get_id)
@@ -314,16 +326,16 @@ static int first_shadow_cpu(struct perf_stat_config *config,
return 0;
for (i = 0; i < perf_evsel__nr_cpus(evsel); i++) {
- int cpu2 = perf_evsel__cpus(evsel)->map[i];
+ int cpu2 = evsel__cpus(evsel)->map[i];
- if (config->aggr_get_id(config, evlist->cpus, cpu2) == id)
+ if (config->aggr_get_id(config, evlist->core.cpus, cpu2) == id)
return cpu2;
}
return 0;
}
static void abs_printout(struct perf_stat_config *config,
- int id, int nr, struct perf_evsel *evsel, double avg)
+ int id, int nr, struct evsel *evsel, double avg)
{
FILE *output = config->output;
double sc = evsel->scale;
@@ -352,24 +364,24 @@ static void abs_printout(struct perf_stat_config *config,
print_cgroup(config, evsel);
}
-static bool is_mixed_hw_group(struct perf_evsel *counter)
+static bool is_mixed_hw_group(struct evsel *counter)
{
- struct perf_evlist *evlist = counter->evlist;
- u32 pmu_type = counter->attr.type;
- struct perf_evsel *pos;
+ struct evlist *evlist = counter->evlist;
+ u32 pmu_type = counter->core.attr.type;
+ struct evsel *pos;
- if (counter->nr_members < 2)
+ if (counter->core.nr_members < 2)
return false;
evlist__for_each_entry(evlist, pos) {
/* software events can be part of any hardware group */
- if (pos->attr.type == PERF_TYPE_SOFTWARE)
+ if (pos->core.attr.type == PERF_TYPE_SOFTWARE)
continue;
if (pmu_type == PERF_TYPE_SOFTWARE) {
- pmu_type = pos->attr.type;
+ pmu_type = pos->core.attr.type;
continue;
}
- if (pmu_type != pos->attr.type)
+ if (pmu_type != pos->core.attr.type)
return true;
}
@@ -377,7 +389,7 @@ static bool is_mixed_hw_group(struct perf_evsel *counter)
}
static void printout(struct perf_stat_config *config, int id, int nr,
- struct perf_evsel *counter, double uval,
+ struct evsel *counter, double uval,
char *prefix, u64 run, u64 ena, double noise,
struct runtime_stat *st)
{
@@ -407,6 +419,7 @@ static void printout(struct perf_stat_config *config, int id, int nr,
[AGGR_THREAD] = 1,
[AGGR_NONE] = 1,
[AGGR_SOCKET] = 2,
+ [AGGR_DIE] = 2,
[AGGR_CORE] = 2,
};
@@ -477,18 +490,18 @@ static void printout(struct perf_stat_config *config, int id, int nr,
}
static void aggr_update_shadow(struct perf_stat_config *config,
- struct perf_evlist *evlist)
+ struct evlist *evlist)
{
int cpu, s2, id, s;
u64 val;
- struct perf_evsel *counter;
+ struct evsel *counter;
for (s = 0; s < config->aggr_map->nr; s++) {
id = config->aggr_map->map[s];
evlist__for_each_entry(evlist, counter) {
val = 0;
for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
- s2 = config->aggr_get_id(config, evlist->cpus, cpu);
+ s2 = config->aggr_get_id(config, evlist->core.cpus, cpu);
if (s2 != id)
continue;
val += perf_counts(counter->counts, cpu, 0)->val;
@@ -500,7 +513,7 @@ static void aggr_update_shadow(struct perf_stat_config *config,
}
}
-static void uniquify_event_name(struct perf_evsel *counter)
+static void uniquify_event_name(struct evsel *counter)
{
char *new_name;
char *config;
@@ -528,29 +541,30 @@ static void uniquify_event_name(struct perf_evsel *counter)
counter->uniquified_name = true;
}
-static void collect_all_aliases(struct perf_stat_config *config, struct perf_evsel *counter,
- void (*cb)(struct perf_stat_config *config, struct perf_evsel *counter, void *data,
+static void collect_all_aliases(struct perf_stat_config *config, struct evsel *counter,
+ void (*cb)(struct perf_stat_config *config, struct evsel *counter, void *data,
bool first),
void *data)
{
- struct perf_evlist *evlist = counter->evlist;
- struct perf_evsel *alias;
+ struct evlist *evlist = counter->evlist;
+ struct evsel *alias;
- alias = list_prepare_entry(counter, &(evlist->entries), node);
- list_for_each_entry_continue (alias, &evlist->entries, node) {
+ alias = list_prepare_entry(counter, &(evlist->core.entries), core.node);
+ list_for_each_entry_continue (alias, &evlist->core.entries, core.node) {
if (strcmp(perf_evsel__name(alias), perf_evsel__name(counter)) ||
alias->scale != counter->scale ||
alias->cgrp != counter->cgrp ||
strcmp(alias->unit, counter->unit) ||
- perf_evsel__is_clock(alias) != perf_evsel__is_clock(counter))
+ perf_evsel__is_clock(alias) != perf_evsel__is_clock(counter) ||
+ !strcmp(alias->pmu_name, counter->pmu_name))
break;
alias->merged_stat = true;
cb(config, alias, data, false);
}
}
-static bool collect_data(struct perf_stat_config *config, struct perf_evsel *counter,
- void (*cb)(struct perf_stat_config *config, struct perf_evsel *counter, void *data,
+static bool collect_data(struct perf_stat_config *config, struct evsel *counter,
+ void (*cb)(struct perf_stat_config *config, struct evsel *counter, void *data,
bool first),
void *data)
{
@@ -572,7 +586,7 @@ struct aggr_data {
};
static void aggr_cb(struct perf_stat_config *config,
- struct perf_evsel *counter, void *data, bool first)
+ struct evsel *counter, void *data, bool first)
{
struct aggr_data *ad = data;
int cpu, s2;
@@ -580,7 +594,7 @@ static void aggr_cb(struct perf_stat_config *config,
for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
struct perf_counts_values *counts;
- s2 = config->aggr_get_id(config, perf_evsel__cpus(counter), cpu);
+ s2 = config->aggr_get_id(config, evsel__cpus(counter), cpu);
if (s2 != ad->id)
continue;
if (first)
@@ -603,7 +617,7 @@ static void aggr_cb(struct perf_stat_config *config,
}
static void print_counter_aggrdata(struct perf_stat_config *config,
- struct perf_evsel *counter, int s,
+ struct evsel *counter, int s,
char *prefix, bool metric_only,
bool *first)
{
@@ -638,12 +652,12 @@ static void print_counter_aggrdata(struct perf_stat_config *config,
}
static void print_aggr(struct perf_stat_config *config,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
char *prefix)
{
bool metric_only = config->metric_only;
FILE *output = config->output;
- struct perf_evsel *counter;
+ struct evsel *counter;
int s;
bool first;
@@ -678,7 +692,7 @@ static int cmp_val(const void *a, const void *b)
}
static struct perf_aggr_thread_value *sort_aggr_thread(
- struct perf_evsel *counter,
+ struct evsel *counter,
int nthreads, int ncpus,
int *ret,
struct target *_target)
@@ -728,11 +742,11 @@ static struct perf_aggr_thread_value *sort_aggr_thread(
static void print_aggr_thread(struct perf_stat_config *config,
struct target *_target,
- struct perf_evsel *counter, char *prefix)
+ struct evsel *counter, char *prefix)
{
FILE *output = config->output;
- int nthreads = thread_map__nr(counter->threads);
- int ncpus = cpu_map__nr(counter->cpus);
+ int nthreads = perf_thread_map__nr(counter->core.threads);
+ int ncpus = perf_cpu_map__nr(counter->core.cpus);
int thread, sorted_threads, id;
struct perf_aggr_thread_value *buf;
@@ -766,7 +780,7 @@ struct caggr_data {
};
static void counter_aggr_cb(struct perf_stat_config *config __maybe_unused,
- struct perf_evsel *counter, void *data,
+ struct evsel *counter, void *data,
bool first __maybe_unused)
{
struct caggr_data *cd = data;
@@ -782,7 +796,7 @@ static void counter_aggr_cb(struct perf_stat_config *config __maybe_unused,
* aggregated counts in system-wide mode
*/
static void print_counter_aggr(struct perf_stat_config *config,
- struct perf_evsel *counter, char *prefix)
+ struct evsel *counter, char *prefix)
{
bool metric_only = config->metric_only;
FILE *output = config->output;
@@ -803,7 +817,7 @@ static void print_counter_aggr(struct perf_stat_config *config,
}
static void counter_cb(struct perf_stat_config *config __maybe_unused,
- struct perf_evsel *counter, void *data,
+ struct evsel *counter, void *data,
bool first __maybe_unused)
{
struct aggr_data *ad = data;
@@ -818,7 +832,7 @@ static void counter_cb(struct perf_stat_config *config __maybe_unused,
* does not use aggregated count in system-wide
*/
static void print_counter(struct perf_stat_config *config,
- struct perf_evsel *counter, char *prefix)
+ struct evsel *counter, char *prefix)
{
FILE *output = config->output;
u64 ena, run, val;
@@ -846,16 +860,16 @@ static void print_counter(struct perf_stat_config *config,
}
static void print_no_aggr_metric(struct perf_stat_config *config,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
char *prefix)
{
int cpu;
int nrcpus = 0;
- struct perf_evsel *counter;
+ struct evsel *counter;
u64 ena, run, val;
double uval;
- nrcpus = evlist->cpus->nr;
+ nrcpus = evlist->core.cpus->nr;
for (cpu = 0; cpu < nrcpus; cpu++) {
bool first = true;
@@ -879,7 +893,8 @@ static void print_no_aggr_metric(struct perf_stat_config *config,
}
static int aggr_header_lens[] = {
- [AGGR_CORE] = 18,
+ [AGGR_CORE] = 24,
+ [AGGR_DIE] = 18,
[AGGR_SOCKET] = 12,
[AGGR_NONE] = 6,
[AGGR_THREAD] = 24,
@@ -888,6 +903,7 @@ static int aggr_header_lens[] = {
static const char *aggr_header_csv[] = {
[AGGR_CORE] = "core,cpus,",
+ [AGGR_DIE] = "die,cpus",
[AGGR_SOCKET] = "socket,cpus",
[AGGR_NONE] = "cpu,",
[AGGR_THREAD] = "comm-pid,",
@@ -895,11 +911,11 @@ static const char *aggr_header_csv[] = {
};
static void print_metric_headers(struct perf_stat_config *config,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
const char *prefix, bool no_indent)
{
struct perf_stat_output_ctx out;
- struct perf_evsel *counter;
+ struct evsel *counter;
struct outstate os = {
.fh = config->output
};
@@ -934,7 +950,7 @@ static void print_metric_headers(struct perf_stat_config *config,
}
static void print_interval(struct perf_stat_config *config,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
char *prefix, struct timespec *ts)
{
bool metric_only = config->metric_only;
@@ -954,8 +970,13 @@ static void print_interval(struct perf_stat_config *config,
if (!metric_only)
fprintf(output, " counts %*s events\n", unit_width, "unit");
break;
+ case AGGR_DIE:
+ fprintf(output, "# time die cpus");
+ if (!metric_only)
+ fprintf(output, " counts %*s events\n", unit_width, "unit");
+ break;
case AGGR_CORE:
- fprintf(output, "# time core cpus");
+ fprintf(output, "# time core cpus");
if (!metric_only)
fprintf(output, " counts %*s events\n", unit_width, "unit");
break;
@@ -1112,7 +1133,7 @@ static void print_footer(struct perf_stat_config *config)
}
static void print_percore(struct perf_stat_config *config,
- struct perf_evsel *counter, char *prefix)
+ struct evsel *counter, char *prefix)
{
bool metric_only = config->metric_only;
FILE *output = config->output;
@@ -1136,7 +1157,7 @@ static void print_percore(struct perf_stat_config *config,
}
void
-perf_evlist__print_counters(struct perf_evlist *evlist,
+perf_evlist__print_counters(struct evlist *evlist,
struct perf_stat_config *config,
struct target *_target,
struct timespec *ts,
@@ -1144,7 +1165,7 @@ perf_evlist__print_counters(struct perf_evlist *evlist,
{
bool metric_only = config->metric_only;
int interval = config->interval;
- struct perf_evsel *counter;
+ struct evsel *counter;
char buf[64], *prefix = NULL;
if (interval)
@@ -1165,6 +1186,7 @@ perf_evlist__print_counters(struct perf_evlist *evlist,
switch (config->aggr_mode) {
case AGGR_CORE:
+ case AGGR_DIE:
case AGGR_SOCKET:
print_aggr(config, evlist, prefix);
break;
diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c
index 83d8094be4fe..70c87fdb2a43 100644
--- a/tools/perf/util/stat-shadow.c
+++ b/tools/perf/util/stat-shadow.c
@@ -8,10 +8,12 @@
#include "evlist.h"
#include "expr.h"
#include "metricgroup.h"
+#include <linux/zalloc.h>
/*
* AGGR_GLOBAL: Use CPU 0
* AGGR_SOCKET: Use first CPU of socket
+ * AGGR_DIE: Use first CPU of die
* AGGR_CORE: Use first CPU of core
* AGGR_NONE: Use matching CPU
* AGGR_THREAD: Not supported?
@@ -23,12 +25,14 @@ struct stats walltime_nsecs_stats;
struct saved_value {
struct rb_node rb_node;
- struct perf_evsel *evsel;
+ struct evsel *evsel;
enum stat_type type;
int ctx;
int cpu;
struct runtime_stat *stat;
struct stats stats;
+ u64 metric_total;
+ int metric_other;
};
static int saved_value_cmp(struct rb_node *rb_node, const void *entry)
@@ -92,7 +96,7 @@ static void saved_value_delete(struct rblist *rblist __maybe_unused,
free(v);
}
-static struct saved_value *saved_value_lookup(struct perf_evsel *evsel,
+static struct saved_value *saved_value_lookup(struct evsel *evsel,
int cpu,
bool create,
enum stat_type type,
@@ -144,19 +148,19 @@ void perf_stat__init_shadow_stats(void)
runtime_stat__init(&rt_stat);
}
-static int evsel_context(struct perf_evsel *evsel)
+static int evsel_context(struct evsel *evsel)
{
int ctx = 0;
- if (evsel->attr.exclude_kernel)
+ if (evsel->core.attr.exclude_kernel)
ctx |= CTX_BIT_KERNEL;
- if (evsel->attr.exclude_user)
+ if (evsel->core.attr.exclude_user)
ctx |= CTX_BIT_USER;
- if (evsel->attr.exclude_hv)
+ if (evsel->core.attr.exclude_hv)
ctx |= CTX_BIT_HV;
- if (evsel->attr.exclude_host)
+ if (evsel->core.attr.exclude_host)
ctx |= CTX_BIT_HOST;
- if (evsel->attr.exclude_idle)
+ if (evsel->core.attr.exclude_idle)
ctx |= CTX_BIT_IDLE;
return ctx;
@@ -205,11 +209,12 @@ static void update_runtime_stat(struct runtime_stat *st,
* more semantic information such as miss/hit ratios,
* instruction rates, etc:
*/
-void perf_stat__update_shadow_stats(struct perf_evsel *counter, u64 count,
+void perf_stat__update_shadow_stats(struct evsel *counter, u64 count,
int cpu, struct runtime_stat *st)
{
int ctx = evsel_context(counter);
u64 count_ns = count;
+ struct saved_value *v;
count *= counter->scale;
@@ -264,9 +269,15 @@ void perf_stat__update_shadow_stats(struct perf_evsel *counter, u64 count,
update_runtime_stat(st, STAT_APERF, ctx, cpu, count);
if (counter->collect_stat) {
- struct saved_value *v = saved_value_lookup(counter, cpu, true,
- STAT_NONE, 0, st);
+ v = saved_value_lookup(counter, cpu, true, STAT_NONE, 0, st);
update_stats(&v->stats, count);
+ if (counter->metric_leader)
+ v->metric_total += count;
+ } else if (counter->metric_leader) {
+ v = saved_value_lookup(counter->metric_leader,
+ cpu, true, STAT_NONE, 0, st);
+ v->metric_total += count;
+ v->metric_other++;
}
}
@@ -297,22 +308,22 @@ static const char *get_ratio_color(enum grc_type type, double ratio)
return color;
}
-static struct perf_evsel *perf_stat__find_event(struct perf_evlist *evsel_list,
+static struct evsel *perf_stat__find_event(struct evlist *evsel_list,
const char *name)
{
- struct perf_evsel *c2;
+ struct evsel *c2;
evlist__for_each_entry (evsel_list, c2) {
- if (!strcasecmp(c2->name, name))
+ if (!strcasecmp(c2->name, name) && !c2->collect_stat)
return c2;
}
return NULL;
}
/* Mark MetricExpr target events and link events using them to them. */
-void perf_stat__collect_metric_expr(struct perf_evlist *evsel_list)
+void perf_stat__collect_metric_expr(struct evlist *evsel_list)
{
- struct perf_evsel *counter, *leader, **metric_events, *oc;
+ struct evsel *counter, *leader, **metric_events, *oc;
bool found;
const char **metric_names;
int i;
@@ -330,7 +341,7 @@ void perf_stat__collect_metric_expr(struct perf_evlist *evsel_list)
&metric_names, &num_metric_names) < 0)
continue;
- metric_events = calloc(sizeof(struct perf_evsel *),
+ metric_events = calloc(sizeof(struct evsel *),
num_metric_names + 1);
if (!metric_events)
return;
@@ -342,7 +353,8 @@ void perf_stat__collect_metric_expr(struct perf_evlist *evsel_list)
if (leader) {
/* Search in group */
for_each_group_member (oc, leader) {
- if (!strcasecmp(oc->name, metric_names[i])) {
+ if (!strcasecmp(oc->name, metric_names[i]) &&
+ !oc->collect_stat) {
found = true;
break;
}
@@ -412,7 +424,7 @@ static double runtime_stat_n(struct runtime_stat *st,
static void print_stalled_cycles_frontend(struct perf_stat_config *config,
int cpu,
- struct perf_evsel *evsel, double avg,
+ struct evsel *evsel, double avg,
struct perf_stat_output_ctx *out,
struct runtime_stat *st)
{
@@ -436,7 +448,7 @@ static void print_stalled_cycles_frontend(struct perf_stat_config *config,
static void print_stalled_cycles_backend(struct perf_stat_config *config,
int cpu,
- struct perf_evsel *evsel, double avg,
+ struct evsel *evsel, double avg,
struct perf_stat_output_ctx *out,
struct runtime_stat *st)
{
@@ -456,7 +468,7 @@ static void print_stalled_cycles_backend(struct perf_stat_config *config,
static void print_branch_misses(struct perf_stat_config *config,
int cpu,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
double avg,
struct perf_stat_output_ctx *out,
struct runtime_stat *st)
@@ -477,7 +489,7 @@ static void print_branch_misses(struct perf_stat_config *config,
static void print_l1_dcache_misses(struct perf_stat_config *config,
int cpu,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
double avg,
struct perf_stat_output_ctx *out,
struct runtime_stat *st)
@@ -499,7 +511,7 @@ static void print_l1_dcache_misses(struct perf_stat_config *config,
static void print_l1_icache_misses(struct perf_stat_config *config,
int cpu,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
double avg,
struct perf_stat_output_ctx *out,
struct runtime_stat *st)
@@ -520,7 +532,7 @@ static void print_l1_icache_misses(struct perf_stat_config *config,
static void print_dtlb_cache_misses(struct perf_stat_config *config,
int cpu,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
double avg,
struct perf_stat_output_ctx *out,
struct runtime_stat *st)
@@ -540,7 +552,7 @@ static void print_dtlb_cache_misses(struct perf_stat_config *config,
static void print_itlb_cache_misses(struct perf_stat_config *config,
int cpu,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
double avg,
struct perf_stat_output_ctx *out,
struct runtime_stat *st)
@@ -560,7 +572,7 @@ static void print_itlb_cache_misses(struct perf_stat_config *config,
static void print_ll_cache_misses(struct perf_stat_config *config,
int cpu,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
double avg,
struct perf_stat_output_ctx *out,
struct runtime_stat *st)
@@ -683,7 +695,7 @@ static double td_be_bound(int ctx, int cpu, struct runtime_stat *st)
}
static void print_smi_cost(struct perf_stat_config *config,
- int cpu, struct perf_evsel *evsel,
+ int cpu, struct evsel *evsel,
struct perf_stat_output_ctx *out,
struct runtime_stat *st)
{
@@ -709,9 +721,10 @@ static void print_smi_cost(struct perf_stat_config *config,
static void generic_metric(struct perf_stat_config *config,
const char *metric_expr,
- struct perf_evsel **metric_events,
+ struct evsel **metric_events,
char *name,
const char *metric_name,
+ const char *metric_unit,
double avg,
int cpu,
struct perf_stat_output_ctx *out,
@@ -719,16 +732,16 @@ static void generic_metric(struct perf_stat_config *config,
{
print_metric_t print_metric = out->print_metric;
struct parse_ctx pctx;
- double ratio;
+ double ratio, scale;
int i;
void *ctxp = out->ctx;
+ char *n, *pn;
expr__ctx_init(&pctx);
- expr__add_id(&pctx, name, avg);
for (i = 0; metric_events[i]; i++) {
struct saved_value *v;
struct stats *stats;
- double scale;
+ u64 metric_total = 0;
if (!strcmp(metric_events[i]->name, "duration_time")) {
stats = &walltime_nsecs_stats;
@@ -740,28 +753,69 @@ static void generic_metric(struct perf_stat_config *config,
break;
stats = &v->stats;
scale = 1.0;
+
+ if (v->metric_other)
+ metric_total = v->metric_total;
}
- expr__add_id(&pctx, metric_events[i]->name, avg_stats(stats)*scale);
+
+ n = strdup(metric_events[i]->name);
+ if (!n)
+ return;
+ /*
+ * This display code with --no-merge adds [cpu] postfixes.
+ * These are not supported by the parser. Remove everything
+ * after the space.
+ */
+ pn = strchr(n, ' ');
+ if (pn)
+ *pn = 0;
+
+ if (metric_total)
+ expr__add_id(&pctx, n, metric_total);
+ else
+ expr__add_id(&pctx, n, avg_stats(stats)*scale);
}
+
+ expr__add_id(&pctx, name, avg);
+
if (!metric_events[i]) {
const char *p = metric_expr;
- if (expr__parse(&ratio, &pctx, &p) == 0)
- print_metric(config, ctxp, NULL, "%8.1f",
- metric_name ?
- metric_name :
- out->force_header ? name : "",
- ratio);
- else
+ if (expr__parse(&ratio, &pctx, &p) == 0) {
+ char *unit;
+ char metric_bf[64];
+
+ if (metric_unit && metric_name) {
+ if (perf_pmu__convert_scale(metric_unit,
+ &unit, &scale) >= 0) {
+ ratio *= scale;
+ }
+
+ scnprintf(metric_bf, sizeof(metric_bf),
+ "%s %s", unit, metric_name);
+ print_metric(config, ctxp, NULL, "%8.1f",
+ metric_bf, ratio);
+ } else {
+ print_metric(config, ctxp, NULL, "%8.1f",
+ metric_name ?
+ metric_name :
+ out->force_header ? name : "",
+ ratio);
+ }
+ } else {
print_metric(config, ctxp, NULL, NULL,
out->force_header ?
(metric_name ? metric_name : name) : "", 0);
+ }
} else
print_metric(config, ctxp, NULL, NULL, "", 0);
+
+ for (i = 1; i < pctx.num_ids; i++)
+ zfree(&pctx.ids[i].name);
}
void perf_stat__print_shadow_stats(struct perf_stat_config *config,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
double avg, int cpu,
struct perf_stat_output_ctx *out,
struct rblist *metric_events,
@@ -800,7 +854,8 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
"stalled cycles per insn",
ratio);
} else if (have_frontend_stalled) {
- print_metric(config, ctxp, NULL, NULL,
+ out->new_line(config, ctxp);
+ print_metric(config, ctxp, NULL, "%7.2f ",
"stalled cycles per insn", 0);
}
} else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES)) {
@@ -809,8 +864,8 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
else
print_metric(config, ctxp, NULL, NULL, "of all branches", 0);
} else if (
- evsel->attr.type == PERF_TYPE_HW_CACHE &&
- evsel->attr.config == ( PERF_COUNT_HW_CACHE_L1D |
+ evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
+ evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_L1D |
((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
@@ -819,8 +874,8 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
else
print_metric(config, ctxp, NULL, NULL, "of all L1-dcache hits", 0);
} else if (
- evsel->attr.type == PERF_TYPE_HW_CACHE &&
- evsel->attr.config == ( PERF_COUNT_HW_CACHE_L1I |
+ evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
+ evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_L1I |
((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
@@ -829,8 +884,8 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
else
print_metric(config, ctxp, NULL, NULL, "of all L1-icache hits", 0);
} else if (
- evsel->attr.type == PERF_TYPE_HW_CACHE &&
- evsel->attr.config == ( PERF_COUNT_HW_CACHE_DTLB |
+ evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
+ evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_DTLB |
((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
@@ -839,8 +894,8 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
else
print_metric(config, ctxp, NULL, NULL, "of all dTLB cache hits", 0);
} else if (
- evsel->attr.type == PERF_TYPE_HW_CACHE &&
- evsel->attr.config == ( PERF_COUNT_HW_CACHE_ITLB |
+ evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
+ evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_ITLB |
((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
@@ -849,8 +904,8 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
else
print_metric(config, ctxp, NULL, NULL, "of all iTLB cache hits", 0);
} else if (
- evsel->attr.type == PERF_TYPE_HW_CACHE &&
- evsel->attr.config == ( PERF_COUNT_HW_CACHE_LL |
+ evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
+ evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_LL |
((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
@@ -972,7 +1027,7 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
print_metric(config, ctxp, NULL, NULL, name, 0);
} else if (evsel->metric_expr) {
generic_metric(config, evsel->metric_expr, evsel->metric_events, evsel->name,
- evsel->metric_name, avg, cpu, out, st);
+ evsel->metric_name, NULL, avg, cpu, out, st);
} else if (runtime_stat_n(st, STAT_NSECS, 0, cpu) != 0) {
char unit = 'M';
char unit_buf[10];
@@ -1001,7 +1056,7 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
out->new_line(config, ctxp);
generic_metric(config, mexp->metric_expr, mexp->metric_events,
evsel->name, mexp->metric_name,
- avg, cpu, out, st);
+ mexp->metric_unit, avg, cpu, out, st);
}
}
if (num == 0)
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c
index c3115d939b0b..8f1ea27f976f 100644
--- a/tools/perf/util/stat.c
+++ b/tools/perf/util/stat.c
@@ -2,10 +2,17 @@
#include <errno.h>
#include <inttypes.h>
#include <math.h>
+#include <string.h>
+#include "counts.h"
+#include "debug.h"
+#include "header.h"
#include "stat.h"
+#include "session.h"
+#include "target.h"
#include "evlist.h"
#include "evsel.h"
#include "thread_map.h"
+#include <linux/zalloc.h>
void update_stats(struct stats *stats, u64 val)
{
@@ -67,7 +74,7 @@ double rel_stddev_stats(double stddev, double avg)
return pct;
}
-bool __perf_evsel_stat__is(struct perf_evsel *evsel,
+bool __perf_evsel_stat__is(struct evsel *evsel,
enum perf_stat_evsel_id id)
{
struct perf_stat_evsel *ps = evsel->stats;
@@ -92,7 +99,7 @@ static const char *id_str[PERF_STAT_EVSEL_ID__MAX] = {
};
#undef ID
-static void perf_stat_evsel_id_init(struct perf_evsel *evsel)
+static void perf_stat_evsel_id_init(struct evsel *evsel)
{
struct perf_stat_evsel *ps = evsel->stats;
int i;
@@ -107,7 +114,7 @@ static void perf_stat_evsel_id_init(struct perf_evsel *evsel)
}
}
-static void perf_evsel__reset_stat_priv(struct perf_evsel *evsel)
+static void perf_evsel__reset_stat_priv(struct evsel *evsel)
{
int i;
struct perf_stat_evsel *ps = evsel->stats;
@@ -118,7 +125,7 @@ static void perf_evsel__reset_stat_priv(struct perf_evsel *evsel)
perf_stat_evsel_id_init(evsel);
}
-static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel)
+static int perf_evsel__alloc_stat_priv(struct evsel *evsel)
{
evsel->stats = zalloc(sizeof(struct perf_stat_evsel));
if (evsel->stats == NULL)
@@ -127,16 +134,16 @@ static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel)
return 0;
}
-static void perf_evsel__free_stat_priv(struct perf_evsel *evsel)
+static void perf_evsel__free_stat_priv(struct evsel *evsel)
{
struct perf_stat_evsel *ps = evsel->stats;
if (ps)
- free(ps->group_data);
+ zfree(&ps->group_data);
zfree(&evsel->stats);
}
-static int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel,
+static int perf_evsel__alloc_prev_raw_counts(struct evsel *evsel,
int ncpus, int nthreads)
{
struct perf_counts *counts;
@@ -148,16 +155,16 @@ static int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel,
return counts ? 0 : -ENOMEM;
}
-static void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel)
+static void perf_evsel__free_prev_raw_counts(struct evsel *evsel)
{
perf_counts__delete(evsel->prev_raw_counts);
evsel->prev_raw_counts = NULL;
}
-static int perf_evsel__alloc_stats(struct perf_evsel *evsel, bool alloc_raw)
+static int perf_evsel__alloc_stats(struct evsel *evsel, bool alloc_raw)
{
int ncpus = perf_evsel__nr_cpus(evsel);
- int nthreads = thread_map__nr(evsel->threads);
+ int nthreads = perf_thread_map__nr(evsel->core.threads);
if (perf_evsel__alloc_stat_priv(evsel) < 0 ||
perf_evsel__alloc_counts(evsel, ncpus, nthreads) < 0 ||
@@ -167,9 +174,9 @@ static int perf_evsel__alloc_stats(struct perf_evsel *evsel, bool alloc_raw)
return 0;
}
-int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw)
+int perf_evlist__alloc_stats(struct evlist *evlist, bool alloc_raw)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) {
if (perf_evsel__alloc_stats(evsel, alloc_raw))
@@ -183,9 +190,9 @@ out_free:
return -1;
}
-void perf_evlist__free_stats(struct perf_evlist *evlist)
+void perf_evlist__free_stats(struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) {
perf_evsel__free_stat_priv(evsel);
@@ -194,9 +201,9 @@ void perf_evlist__free_stats(struct perf_evlist *evlist)
}
}
-void perf_evlist__reset_stats(struct perf_evlist *evlist)
+void perf_evlist__reset_stats(struct evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct evsel *evsel;
evlist__for_each_entry(evlist, evsel) {
perf_evsel__reset_stat_priv(evsel);
@@ -204,17 +211,17 @@ void perf_evlist__reset_stats(struct perf_evlist *evlist)
}
}
-static void zero_per_pkg(struct perf_evsel *counter)
+static void zero_per_pkg(struct evsel *counter)
{
if (counter->per_pkg_mask)
- memset(counter->per_pkg_mask, 0, MAX_NR_CPUS);
+ memset(counter->per_pkg_mask, 0, cpu__max_cpu());
}
-static int check_per_pkg(struct perf_evsel *counter,
+static int check_per_pkg(struct evsel *counter,
struct perf_counts_values *vals, int cpu, bool *skip)
{
unsigned long *mask = counter->per_pkg_mask;
- struct cpu_map *cpus = perf_evsel__cpus(counter);
+ struct perf_cpu_map *cpus = evsel__cpus(counter);
int s;
*skip = false;
@@ -222,11 +229,11 @@ static int check_per_pkg(struct perf_evsel *counter,
if (!counter->per_pkg)
return 0;
- if (cpu_map__empty(cpus))
+ if (perf_cpu_map__empty(cpus))
return 0;
if (!mask) {
- mask = zalloc(MAX_NR_CPUS);
+ mask = zalloc(cpu__max_cpu());
if (!mask)
return -ENOMEM;
@@ -253,7 +260,7 @@ static int check_per_pkg(struct perf_evsel *counter,
}
static int
-process_counter_values(struct perf_stat_config *config, struct perf_evsel *evsel,
+process_counter_values(struct perf_stat_config *config, struct evsel *evsel,
int cpu, int thread,
struct perf_counts_values *count)
{
@@ -272,6 +279,7 @@ process_counter_values(struct perf_stat_config *config, struct perf_evsel *evsel
switch (config->aggr_mode) {
case AGGR_THREAD:
case AGGR_CORE:
+ case AGGR_DIE:
case AGGR_SOCKET:
case AGGR_NONE:
if (!evsel->snapshot)
@@ -304,9 +312,9 @@ process_counter_values(struct perf_stat_config *config, struct perf_evsel *evsel
}
static int process_counter_maps(struct perf_stat_config *config,
- struct perf_evsel *counter)
+ struct evsel *counter)
{
- int nthreads = thread_map__nr(counter->threads);
+ int nthreads = perf_thread_map__nr(counter->core.threads);
int ncpus = perf_evsel__nr_cpus(counter);
int cpu, thread;
@@ -325,7 +333,7 @@ static int process_counter_maps(struct perf_stat_config *config,
}
int perf_stat_process_counter(struct perf_stat_config *config,
- struct perf_evsel *counter)
+ struct evsel *counter)
{
struct perf_counts_values *aggr = &counter->counts->aggr;
struct perf_stat_evsel *ps = counter->stats;
@@ -378,8 +386,8 @@ int perf_event__process_stat_event(struct perf_session *session,
union perf_event *event)
{
struct perf_counts_values count;
- struct stat_event *st = &event->stat;
- struct perf_evsel *counter;
+ struct perf_record_stat *st = &event->stat;
+ struct evsel *counter;
count.val = st->val;
count.ena = st->ena;
@@ -398,12 +406,12 @@ int perf_event__process_stat_event(struct perf_session *session,
size_t perf_event__fprintf_stat(union perf_event *event, FILE *fp)
{
- struct stat_event *st = (struct stat_event *) event;
+ struct perf_record_stat *st = (struct perf_record_stat *)event;
size_t ret;
- ret = fprintf(fp, "\n... id %" PRIu64 ", cpu %d, thread %d\n",
+ ret = fprintf(fp, "\n... id %" PRI_lu64 ", cpu %d, thread %d\n",
st->id, st->cpu, st->thread);
- ret += fprintf(fp, "... value %" PRIu64 ", enabled %" PRIu64 ", running %" PRIu64 "\n",
+ ret += fprintf(fp, "... value %" PRI_lu64 ", enabled %" PRI_lu64 ", running %" PRI_lu64 "\n",
st->val, st->ena, st->run);
return ret;
@@ -411,10 +419,10 @@ size_t perf_event__fprintf_stat(union perf_event *event, FILE *fp)
size_t perf_event__fprintf_stat_round(union perf_event *event, FILE *fp)
{
- struct stat_round_event *rd = (struct stat_round_event *)event;
+ struct perf_record_stat_round *rd = (struct perf_record_stat_round *)event;
size_t ret;
- ret = fprintf(fp, "\n... time %" PRIu64 ", type %s\n", rd->time,
+ ret = fprintf(fp, "\n... time %" PRI_lu64 ", type %s\n", rd->time,
rd->type == PERF_STAT_ROUND_TYPE__FINAL ? "FINAL" : "INTERVAL");
return ret;
@@ -435,12 +443,12 @@ size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp)
return ret;
}
-int create_perf_stat_counter(struct perf_evsel *evsel,
+int create_perf_stat_counter(struct evsel *evsel,
struct perf_stat_config *config,
struct target *target)
{
- struct perf_event_attr *attr = &evsel->attr;
- struct perf_evsel *leader = evsel->leader;
+ struct perf_event_attr *attr = &evsel->core.attr;
+ struct evsel *leader = evsel->leader;
attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
PERF_FORMAT_TOTAL_TIME_RUNNING;
@@ -450,7 +458,7 @@ int create_perf_stat_counter(struct perf_evsel *evsel,
* the group read (for leader) and ID retrieval for all
* members.
*/
- if (leader->nr_members > 1)
+ if (leader->core.nr_members > 1)
attr->read_format |= PERF_FORMAT_ID|PERF_FORMAT_GROUP;
attr->inherit = !config->no_inherit;
@@ -481,14 +489,14 @@ int create_perf_stat_counter(struct perf_evsel *evsel,
}
if (target__has_cpu(target) && !target__has_per_thread(target))
- return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel));
+ return perf_evsel__open_per_cpu(evsel, evsel__cpus(evsel));
- return perf_evsel__open_per_thread(evsel, evsel->threads);
+ return perf_evsel__open_per_thread(evsel, evsel->core.threads);
}
int perf_stat_synthesize_config(struct perf_stat_config *config,
struct perf_tool *tool,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
perf_event__handler_t process,
bool attrs)
{
@@ -505,14 +513,14 @@ int perf_stat_synthesize_config(struct perf_stat_config *config,
err = perf_event__synthesize_extra_attr(tool, evlist, process,
attrs);
- err = perf_event__synthesize_thread_map2(tool, evlist->threads,
+ err = perf_event__synthesize_thread_map2(tool, evlist->core.threads,
process, NULL);
if (err < 0) {
pr_err("Couldn't synthesize thread map.\n");
return err;
}
- err = perf_event__synthesize_cpu_map(tool, evlist->cpus,
+ err = perf_event__synthesize_cpu_map(tool, evlist->core.cpus,
process, NULL);
if (err < 0) {
pr_err("Couldn't synthesize thread map.\n");
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h
index 2f9c9159a364..14fe3e548229 100644
--- a/tools/perf/util/stat.h
+++ b/tools/perf/util/stat.h
@@ -5,14 +5,12 @@
#include <linux/types.h>
#include <stdio.h>
#include <sys/types.h>
-#include <sys/time.h>
#include <sys/resource.h>
-#include <sys/wait.h>
-#include "xyarray.h"
#include "rblist.h"
-#include "perf.h"
#include "event.h"
+struct timespec;
+
struct stats {
double n, mean, M2;
u64 max, min;
@@ -44,6 +42,7 @@ enum aggr_mode {
AGGR_NONE,
AGGR_GLOBAL,
AGGR_SOCKET,
+ AGGR_DIE,
AGGR_CORE,
AGGR_THREAD,
AGGR_UNSET,
@@ -91,7 +90,7 @@ struct runtime_stat {
};
typedef int (*aggr_get_id_t)(struct perf_stat_config *config,
- struct cpu_map *m, int cpu);
+ struct perf_cpu_map *m, int cpu);
struct perf_stat_config {
enum aggr_mode aggr_mode;
@@ -121,9 +120,9 @@ struct perf_stat_config {
const char *csv_sep;
struct stats *walltime_nsecs_stats;
struct rusage ru_data;
- struct cpu_map *aggr_map;
+ struct perf_cpu_map *aggr_map;
aggr_get_id_t aggr_get_id;
- struct cpu_map *cpus_aggr_map;
+ struct perf_cpu_map *cpus_aggr_map;
u64 *walltime_run;
struct rblist metric_events;
};
@@ -142,11 +141,11 @@ static inline void init_stats(struct stats *stats)
stats->max = 0;
}
-struct perf_evsel;
-struct perf_evlist;
+struct evsel;
+struct evlist;
struct perf_aggr_thread_value {
- struct perf_evsel *counter;
+ struct evsel *counter;
int id;
double uval;
u64 val;
@@ -154,7 +153,7 @@ struct perf_aggr_thread_value {
u64 ena;
};
-bool __perf_evsel_stat__is(struct perf_evsel *evsel,
+bool __perf_evsel_stat__is(struct evsel *evsel,
enum perf_stat_evsel_id id);
#define perf_stat_evsel__is(evsel, id) \
@@ -173,7 +172,7 @@ void runtime_stat__exit(struct runtime_stat *st);
void perf_stat__init_shadow_stats(void);
void perf_stat__reset_shadow_stats(void);
void perf_stat__reset_shadow_per_stat(struct runtime_stat *st);
-void perf_stat__update_shadow_stats(struct perf_evsel *counter, u64 count,
+void perf_stat__update_shadow_stats(struct evsel *counter, u64 count,
int cpu, struct runtime_stat *st);
struct perf_stat_output_ctx {
void *ctx;
@@ -183,22 +182,24 @@ struct perf_stat_output_ctx {
};
void perf_stat__print_shadow_stats(struct perf_stat_config *config,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
double avg, int cpu,
struct perf_stat_output_ctx *out,
struct rblist *metric_events,
struct runtime_stat *st);
-void perf_stat__collect_metric_expr(struct perf_evlist *);
+void perf_stat__collect_metric_expr(struct evlist *);
-int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw);
-void perf_evlist__free_stats(struct perf_evlist *evlist);
-void perf_evlist__reset_stats(struct perf_evlist *evlist);
+int perf_evlist__alloc_stats(struct evlist *evlist, bool alloc_raw);
+void perf_evlist__free_stats(struct evlist *evlist);
+void perf_evlist__reset_stats(struct evlist *evlist);
int perf_stat_process_counter(struct perf_stat_config *config,
- struct perf_evsel *counter);
+ struct evsel *counter);
struct perf_tool;
union perf_event;
struct perf_session;
+struct target;
+
int perf_event__process_stat_event(struct perf_session *session,
union perf_event *event);
@@ -206,16 +207,16 @@ size_t perf_event__fprintf_stat(union perf_event *event, FILE *fp);
size_t perf_event__fprintf_stat_round(union perf_event *event, FILE *fp);
size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp);
-int create_perf_stat_counter(struct perf_evsel *evsel,
+int create_perf_stat_counter(struct evsel *evsel,
struct perf_stat_config *config,
struct target *target);
int perf_stat_synthesize_config(struct perf_stat_config *config,
struct perf_tool *tool,
- struct perf_evlist *evlist,
+ struct evlist *evlist,
perf_event__handler_t process,
bool attrs);
void
-perf_evlist__print_counters(struct perf_evlist *evlist,
+perf_evlist__print_counters(struct evlist *evlist,
struct perf_stat_config *config,
struct target *_target,
struct timespec *ts,
diff --git a/tools/perf/util/strbuf.c b/tools/perf/util/strbuf.c
index 23092fd6451d..a64a37628f12 100644
--- a/tools/perf/util/strbuf.c
+++ b/tools/perf/util/strbuf.c
@@ -1,8 +1,14 @@
// SPDX-License-Identifier: GPL-2.0
+#include "cache.h"
#include "debug.h"
-#include "util.h"
+#include "strbuf.h"
#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/zalloc.h>
#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
/*
* Used as the default ->buf value, so that people can always assume
diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c
index 7f3253d44afd..78aa4c3b990d 100644
--- a/tools/perf/util/strfilter.c
+++ b/tools/perf/util/strfilter.c
@@ -1,10 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
-#include "util.h"
#include "string2.h"
#include "strfilter.h"
#include <errno.h>
-#include "sane_ctype.h"
+#include <stdlib.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/zalloc.h>
/* Operators */
static const char *OP_and = "&"; /* Logical AND */
@@ -37,8 +39,7 @@ static const char *get_token(const char *s, const char **e)
{
const char *p;
- while (isspace(*s)) /* Skip spaces */
- s++;
+ s = skip_spaces(s);
if (*s == '\0') {
p = s;
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
index d8bfd0c4d2cb..52603876c548 100644
--- a/tools/perf/util/string.c
+++ b/tools/perf/util/string.c
@@ -4,7 +4,16 @@
#include <linux/string.h>
#include <stdlib.h>
-#include "sane_ctype.h"
+#include <linux/ctype.h>
+
+const char *graph_dotted_line =
+ "---------------------------------------------------------------------"
+ "---------------------------------------------------------------------"
+ "---------------------------------------------------------------------";
+const char *dots =
+ "....................................................................."
+ "....................................................................."
+ ".....................................................................";
#define K 1024LL
/*
@@ -60,109 +69,6 @@ out_err:
return -1;
}
-/*
- * Helper function for splitting a string into an argv-like array.
- * originally copied from lib/argv_split.c
- */
-static const char *skip_sep(const char *cp)
-{
- while (*cp && isspace(*cp))
- cp++;
-
- return cp;
-}
-
-static const char *skip_arg(const char *cp)
-{
- while (*cp && !isspace(*cp))
- cp++;
-
- return cp;
-}
-
-static int count_argc(const char *str)
-{
- int count = 0;
-
- while (*str) {
- str = skip_sep(str);
- if (*str) {
- count++;
- str = skip_arg(str);
- }
- }
-
- return count;
-}
-
-/**
- * argv_free - free an argv
- * @argv - the argument vector to be freed
- *
- * Frees an argv and the strings it points to.
- */
-void argv_free(char **argv)
-{
- char **p;
- for (p = argv; *p; p++) {
- free(*p);
- *p = NULL;
- }
-
- free(argv);
-}
-
-/**
- * argv_split - split a string at whitespace, returning an argv
- * @str: the string to be split
- * @argcp: returned argument count
- *
- * Returns an array of pointers to strings which are split out from
- * @str. This is performed by strictly splitting on white-space; no
- * quote processing is performed. Multiple whitespace characters are
- * considered to be a single argument separator. The returned array
- * is always NULL-terminated. Returns NULL on memory allocation
- * failure.
- */
-char **argv_split(const char *str, int *argcp)
-{
- int argc = count_argc(str);
- char **argv = calloc(argc + 1, sizeof(*argv));
- char **argvp;
-
- if (argv == NULL)
- goto out;
-
- if (argcp)
- *argcp = argc;
-
- argvp = argv;
-
- while (*str) {
- str = skip_sep(str);
-
- if (*str) {
- const char *p = str;
- char *t;
-
- str = skip_arg(str);
-
- t = strndup(p, str-p);
- if (t == NULL)
- goto fail;
- *argvp++ = t;
- }
- }
- *argvp = NULL;
-
-out:
- return argv;
-
-fail:
- argv_free(argv);
- return NULL;
-}
-
/* Character class matching */
static bool __match_charclass(const char *pat, char c, const char **npat)
{
@@ -303,61 +209,6 @@ int strtailcmp(const char *s1, const char *s2)
return 0;
}
-/**
- * strxfrchar - Locate and replace character in @s
- * @s: The string to be searched/changed.
- * @from: Source character to be replaced.
- * @to: Destination character.
- *
- * Return pointer to the changed string.
- */
-char *strxfrchar(char *s, char from, char to)
-{
- char *p = s;
-
- while ((p = strchr(p, from)) != NULL)
- *p++ = to;
-
- return s;
-}
-
-/**
- * ltrim - Removes leading whitespace from @s.
- * @s: The string to be stripped.
- *
- * Return pointer to the first non-whitespace character in @s.
- */
-char *ltrim(char *s)
-{
- while (isspace(*s))
- s++;
-
- return s;
-}
-
-/**
- * rtrim - Removes trailing whitespace from @s.
- * @s: The string to be stripped.
- *
- * Note that the first trailing whitespace is replaced with a %NUL-terminator
- * in the given string @s. Returns @s.
- */
-char *rtrim(char *s)
-{
- size_t size = strlen(s);
- char *end;
-
- if (!size)
- return s;
-
- end = s + size - 1;
- while (end >= s && isspace(*end))
- end--;
- *(end + 1) = '\0';
-
- return s;
-}
-
char *asprintf_expr_inout_ints(const char *var, bool in, size_t nints, int *ints)
{
/*
diff --git a/tools/perf/util/string2.h b/tools/perf/util/string2.h
index 4c68a09b97e8..708805f5573e 100644
--- a/tools/perf/util/string2.h
+++ b/tools/perf/util/string2.h
@@ -2,13 +2,15 @@
#ifndef PERF_STRING_H
#define PERF_STRING_H
+#include <linux/string.h>
#include <linux/types.h>
#include <stddef.h>
#include <string.h>
+extern const char *graph_dotted_line;
+extern const char *dots;
+
s64 perf_atoll(const char *str);
-char **argv_split(const char *str, int *argcp);
-void argv_free(char **argv);
bool strglobmatch(const char *str, const char *pat);
bool strglobmatch_nocase(const char *str, const char *pat);
bool strlazymatch(const char *str, const char *pat);
@@ -17,15 +19,6 @@ static inline bool strisglob(const char *str)
return strpbrk(str, "*?[") != NULL;
}
int strtailcmp(const char *s1, const char *s2);
-char *strxfrchar(char *s, char from, char to);
-
-char *ltrim(char *s);
-char *rtrim(char *s);
-
-static inline char *trim(char *s)
-{
- return ltrim(rtrim(s));
-}
char *asprintf_expr_inout_ints(const char *var, bool in, size_t nints, int *ints);
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c
index 9de5434bb49e..8a868cbeffae 100644
--- a/tools/perf/util/strlist.c
+++ b/tools/perf/util/strlist.c
@@ -1,16 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (c) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
- *
- * Licensed under the GPLv2.
*/
#include "strlist.h"
-#include "util.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <linux/zalloc.h>
static
struct rb_node *strlist__node_new(struct rblist *rblist, const void *entry)
diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c
index f735ee038713..582f4a69cd48 100644
--- a/tools/perf/util/svghelper.c
+++ b/tools/perf/util/svghelper.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* svghelper.c - helper functions for outputting svg
*
@@ -5,11 +6,6 @@
*
* Authors:
* Arjan van de Ven <arjan@linux.intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; version 2
- * of the License.
*/
#include <inttypes.h>
@@ -18,11 +14,13 @@
#include <unistd.h>
#include <string.h>
#include <linux/bitmap.h>
+#include <linux/string.h>
#include <linux/time64.h>
+#include <linux/zalloc.h>
+#include <perf/cpumap.h>
-#include "perf.h"
+#include "env.h"
#include "svghelper.h"
-#include "util.h"
#include "cpumap.h"
static u64 first_time, last_time;
@@ -698,7 +696,8 @@ struct topology {
int sib_thr_nr;
};
-static void scan_thread_topology(int *map, struct topology *t, int cpu, int *pos)
+static void scan_thread_topology(int *map, struct topology *t, int cpu,
+ int *pos, int nr_cpus)
{
int i;
int thr;
@@ -707,41 +706,37 @@ static void scan_thread_topology(int *map, struct topology *t, int cpu, int *pos
if (!test_bit(cpu, cpumask_bits(&t->sib_thr[i])))
continue;
- for_each_set_bit(thr,
- cpumask_bits(&t->sib_thr[i]),
- MAX_NR_CPUS)
+ for_each_set_bit(thr, cpumask_bits(&t->sib_thr[i]), nr_cpus)
if (map[thr] == -1)
map[thr] = (*pos)++;
}
}
-static void scan_core_topology(int *map, struct topology *t)
+static void scan_core_topology(int *map, struct topology *t, int nr_cpus)
{
int pos = 0;
int i;
int cpu;
for (i = 0; i < t->sib_core_nr; i++)
- for_each_set_bit(cpu,
- cpumask_bits(&t->sib_core[i]),
- MAX_NR_CPUS)
- scan_thread_topology(map, t, cpu, &pos);
+ for_each_set_bit(cpu, cpumask_bits(&t->sib_core[i]), nr_cpus)
+ scan_thread_topology(map, t, cpu, &pos, nr_cpus);
}
-static int str_to_bitmap(char *s, cpumask_t *b)
+static int str_to_bitmap(char *s, cpumask_t *b, int nr_cpus)
{
int i;
int ret = 0;
- struct cpu_map *m;
+ struct perf_cpu_map *m;
int c;
- m = cpu_map__new(s);
+ m = perf_cpu_map__new(s);
if (!m)
return -1;
for (i = 0; i < m->nr; i++) {
c = m->map[i];
- if (c >= MAX_NR_CPUS) {
+ if (c >= nr_cpus) {
ret = -1;
break;
}
@@ -749,29 +744,34 @@ static int str_to_bitmap(char *s, cpumask_t *b)
set_bit(c, cpumask_bits(b));
}
- cpu_map__put(m);
+ perf_cpu_map__put(m);
return ret;
}
-int svg_build_topology_map(char *sib_core, int sib_core_nr,
- char *sib_thr, int sib_thr_nr)
+int svg_build_topology_map(struct perf_env *env)
{
- int i;
+ int i, nr_cpus;
struct topology t;
+ char *sib_core, *sib_thr;
+
+ nr_cpus = min(env->nr_cpus_online, MAX_NR_CPUS);
+
+ t.sib_core_nr = env->nr_sibling_cores;
+ t.sib_thr_nr = env->nr_sibling_threads;
+ t.sib_core = calloc(env->nr_sibling_cores, sizeof(cpumask_t));
+ t.sib_thr = calloc(env->nr_sibling_threads, sizeof(cpumask_t));
- t.sib_core_nr = sib_core_nr;
- t.sib_thr_nr = sib_thr_nr;
- t.sib_core = calloc(sib_core_nr, sizeof(cpumask_t));
- t.sib_thr = calloc(sib_thr_nr, sizeof(cpumask_t));
+ sib_core = env->sibling_cores;
+ sib_thr = env->sibling_threads;
if (!t.sib_core || !t.sib_thr) {
fprintf(stderr, "topology: no memory\n");
goto exit;
}
- for (i = 0; i < sib_core_nr; i++) {
- if (str_to_bitmap(sib_core, &t.sib_core[i])) {
+ for (i = 0; i < env->nr_sibling_cores; i++) {
+ if (str_to_bitmap(sib_core, &t.sib_core[i], nr_cpus)) {
fprintf(stderr, "topology: can't parse siblings map\n");
goto exit;
}
@@ -779,8 +779,8 @@ int svg_build_topology_map(char *sib_core, int sib_core_nr,
sib_core += strlen(sib_core) + 1;
}
- for (i = 0; i < sib_thr_nr; i++) {
- if (str_to_bitmap(sib_thr, &t.sib_thr[i])) {
+ for (i = 0; i < env->nr_sibling_threads; i++) {
+ if (str_to_bitmap(sib_thr, &t.sib_thr[i], nr_cpus)) {
fprintf(stderr, "topology: can't parse siblings map\n");
goto exit;
}
@@ -788,16 +788,16 @@ int svg_build_topology_map(char *sib_core, int sib_core_nr,
sib_thr += strlen(sib_thr) + 1;
}
- topology_map = malloc(sizeof(int) * MAX_NR_CPUS);
+ topology_map = malloc(sizeof(int) * nr_cpus);
if (!topology_map) {
fprintf(stderr, "topology: no memory\n");
goto exit;
}
- for (i = 0; i < MAX_NR_CPUS; i++)
+ for (i = 0; i < nr_cpus; i++)
topology_map[i] = -1;
- scan_core_topology(topology_map, &t);
+ scan_core_topology(topology_map, &t, nr_cpus);
return 0;
diff --git a/tools/perf/util/svghelper.h b/tools/perf/util/svghelper.h
index e55338d5c3bd..81823e8bae3e 100644
--- a/tools/perf/util/svghelper.h
+++ b/tools/perf/util/svghelper.h
@@ -4,6 +4,8 @@
#include <linux/types.h>
+struct perf_env;
+
void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end);
void svg_ubox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges);
void svg_lbox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges);
@@ -28,7 +30,7 @@ void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc
void svg_interrupt(u64 start, int row, const char *backtrace);
void svg_text(int Yslot, u64 start, const char *text);
void svg_close(void);
-int svg_build_topology_map(char *sib_core, int sib_core_nr, char *sib_thr, int sib_thr_nr);
+int svg_build_topology_map(struct perf_env *env);
extern int svg_page_width;
extern u64 svg_highlight;
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 4ad106a5f2c0..9428639872a6 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -2,6 +2,7 @@
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
+#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
@@ -9,12 +10,15 @@
#include "map.h"
#include "map_groups.h"
#include "symbol.h"
+#include "symsrc.h"
#include "demangle-java.h"
#include "demangle-rust.h"
#include "machine.h"
#include "vdso.h"
#include "debug.h"
-#include "sane_ctype.h"
+#include "util.h"
+#include <linux/ctype.h>
+#include <linux/zalloc.h>
#include <symbol/kallsyms.h>
#ifndef EM_AARCH64
@@ -37,6 +41,12 @@
typedef Elf64_Nhdr GElf_Nhdr;
+#ifndef DMGL_PARAMS
+#define DMGL_NO_OPTS 0 /* For readability... */
+#define DMGL_PARAMS (1 << 0) /* Include function args */
+#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
+#endif
+
#ifdef HAVE_CPLUS_DEMANGLE_SUPPORT
extern char *cplus_demangle(const char *, int);
@@ -699,7 +709,6 @@ bool __weak elf__needs_adjust_symbols(GElf_Ehdr ehdr)
int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
enum dso_binary_type type)
{
- int err = -1;
GElf_Ehdr ehdr;
Elf *elf;
int fd;
@@ -793,7 +802,7 @@ out_elf_end:
elf_end(elf);
out_close:
close(fd);
- return err;
+ return -1;
}
/**
@@ -1476,7 +1485,7 @@ static void kcore_copy__free_phdrs(struct kcore_copy_info *kci)
struct phdr_data *p, *tmp;
list_for_each_entry_safe(p, tmp, &kci->phdrs, node) {
- list_del(&p->node);
+ list_del_init(&p->node);
free(p);
}
}
@@ -1499,7 +1508,7 @@ static void kcore_copy__free_syms(struct kcore_copy_info *kci)
struct sym_data *s, *tmp;
list_for_each_entry_safe(s, tmp, &kci->syms, node) {
- list_del(&s->node);
+ list_del_init(&s->node);
free(s);
}
}
@@ -2131,11 +2140,11 @@ static int populate_sdt_note(Elf **elf, const char *data, size_t len,
return 0;
out_free_args:
- free(tmp->args);
+ zfree(&tmp->args);
out_free_name:
- free(tmp->name);
+ zfree(&tmp->name);
out_free_prov:
- free(tmp->provider);
+ zfree(&tmp->provider);
out_free_note:
free(tmp);
out_err:
@@ -2250,9 +2259,9 @@ int cleanup_sdt_note_list(struct list_head *sdt_notes)
int nr_free = 0;
list_for_each_entry_safe(pos, tmp, sdt_notes, note_list) {
- list_del(&pos->note_list);
- free(pos->name);
- free(pos->provider);
+ list_del_init(&pos->note_list);
+ zfree(&pos->name);
+ zfree(&pos->provider);
free(pos);
nr_free++;
}
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c
index 17edbd4f6f85..7e2813ec9498 100644
--- a/tools/perf/util/symbol-minimal.c
+++ b/tools/perf/util/symbol-minimal.c
@@ -1,5 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
+#include "dso.h"
#include "symbol.h"
+#include "symsrc.h"
#include "util.h"
#include <errno.h>
@@ -7,9 +9,10 @@
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
+#include <stdlib.h>
#include <byteswap.h>
#include <sys/stat.h>
-
+#include <linux/zalloc.h>
static bool check_need_swap(int file_endian)
{
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 5cbad55cd99d..765c75df2904 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -4,8 +4,10 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <linux/capability.h>
#include <linux/kernel.h>
#include <linux/mman.h>
+#include <linux/string.h>
#include <linux/time64.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -15,17 +17,24 @@
#include <inttypes.h>
#include "annotate.h"
#include "build-id.h"
+#include "cap.h"
+#include "dso.h"
#include "util.h"
#include "debug.h"
+#include "event.h"
#include "machine.h"
#include "map.h"
#include "symbol.h"
+#include "map_symbol.h"
+#include "mem-events.h"
+#include "symsrc.h"
#include "strlist.h"
#include "intlist.h"
#include "namespaces.h"
#include "header.h"
#include "path.h"
-#include "sane_ctype.h"
+#include <linux/ctype.h>
+#include <linux/zalloc.h>
#include <elf.h>
#include <limits.h>
@@ -91,6 +100,11 @@ static int prefix_underscores_count(const char *str)
return tail - str;
}
+void __weak arch__symbols__fixup_end(struct symbol *p, struct symbol *c)
+{
+ p->end = c->start;
+}
+
const char * __weak arch__normalize_symbol_name(const char *name)
{
return name;
@@ -217,7 +231,7 @@ void symbols__fixup_end(struct rb_root_cached *symbols)
curr = rb_entry(nd, struct symbol, rb_node);
if (prev->end == prev->start && prev->end != curr->start)
- prev->end = curr->start;
+ arch__symbols__fixup_end(prev, curr);
}
/* Last entry */
@@ -1166,6 +1180,85 @@ static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data)
return 0;
}
+/*
+ * Merges map into map_groups by splitting the new map
+ * within the existing map regions.
+ */
+int map_groups__merge_in(struct map_groups *kmaps, struct map *new_map)
+{
+ struct map *old_map;
+ LIST_HEAD(merged);
+
+ for (old_map = map_groups__first(kmaps); old_map;
+ old_map = map_groups__next(old_map)) {
+
+ /* no overload with this one */
+ if (new_map->end < old_map->start ||
+ new_map->start >= old_map->end)
+ continue;
+
+ if (new_map->start < old_map->start) {
+ /*
+ * |new......
+ * |old....
+ */
+ if (new_map->end < old_map->end) {
+ /*
+ * |new......| -> |new..|
+ * |old....| -> |old....|
+ */
+ new_map->end = old_map->start;
+ } else {
+ /*
+ * |new.............| -> |new..| |new..|
+ * |old....| -> |old....|
+ */
+ struct map *m = map__clone(new_map);
+
+ if (!m)
+ return -ENOMEM;
+
+ m->end = old_map->start;
+ list_add_tail(&m->node, &merged);
+ new_map->start = old_map->end;
+ }
+ } else {
+ /*
+ * |new......
+ * |old....
+ */
+ if (new_map->end < old_map->end) {
+ /*
+ * |new..| -> x
+ * |old.........| -> |old.........|
+ */
+ map__put(new_map);
+ new_map = NULL;
+ break;
+ } else {
+ /*
+ * |new......| -> |new...|
+ * |old....| -> |old....|
+ */
+ new_map->start = old_map->end;
+ }
+ }
+ }
+
+ while (!list_empty(&merged)) {
+ old_map = list_entry(merged.next, struct map, node);
+ list_del_init(&old_map->node);
+ map_groups__insert(kmaps, old_map);
+ map__put(old_map);
+ }
+
+ if (new_map) {
+ map_groups__insert(kmaps, new_map);
+ map__put(new_map);
+ }
+ return 0;
+}
+
static int dso__load_kcore(struct dso *dso, struct map *map,
const char *kallsyms_filename)
{
@@ -1222,7 +1315,12 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
while (old_map) {
struct map *next = map_groups__next(old_map);
- if (old_map != map)
+ /*
+ * We need to preserve eBPF maps even if they are
+ * covered by kcore, because we need to access
+ * eBPF dso for source data.
+ */
+ if (old_map != map && !__map__is_bpf_prog(old_map))
map_groups__remove(kmaps, old_map);
old_map = next;
}
@@ -1256,11 +1354,16 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
map_groups__remove(kmaps, map);
map_groups__insert(kmaps, map);
map__put(map);
+ map__put(new_map);
} else {
- map_groups__insert(kmaps, new_map);
+ /*
+ * Merge kcore map into existing maps,
+ * and ensure that current maps (eBPF)
+ * stay intact.
+ */
+ if (map_groups__merge_in(kmaps, new_map))
+ goto out_err;
}
-
- map__put(new_map);
}
if (machine__is(machine, "x86_64")) {
@@ -2100,13 +2203,19 @@ static bool symbol__read_kptr_restrict(void)
char line[8];
if (fgets(line, sizeof(line), fp) != NULL)
- value = ((geteuid() != 0) || (getuid() != 0)) ?
- (atoi(line) != 0) :
- (atoi(line) == 2);
+ value = perf_cap__capable(CAP_SYSLOG) ?
+ (atoi(line) >= 2) :
+ (atoi(line) != 0);
fclose(fp);
}
+ /* Per kernel/kallsyms.c:
+ * we also restrict when perf_event_paranoid > 1 w/o CAP_SYSLOG
+ */
+ if (perf_event_paranoid() > 1 && !perf_cap__capable(CAP_SYSLOG))
+ value = true;
+
return value;
}
@@ -2262,3 +2371,25 @@ struct mem_info *mem_info__new(void)
refcount_set(&mi->refcnt, 1);
return mi;
}
+
+struct block_info *block_info__get(struct block_info *bi)
+{
+ if (bi)
+ refcount_inc(&bi->refcnt);
+ return bi;
+}
+
+void block_info__put(struct block_info *bi)
+{
+ if (bi && refcount_dec_and_test(&bi->refcnt))
+ free(bi);
+}
+
+struct block_info *block_info__new(void)
+{
+ struct block_info *bi = zalloc(sizeof(*bi));
+
+ if (bi)
+ refcount_set(&bi->refcnt, 1);
+ return bi;
+}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 9a8fe012910a..0b0c6b5b1899 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -3,13 +3,12 @@
#define __PERF_SYMBOL 1
#include <linux/types.h>
+#include <linux/refcount.h>
#include <stdbool.h>
#include <stdint.h>
#include <linux/list.h>
#include <linux/rbtree.h>
#include <stdio.h>
-#include "map_symbol.h"
-#include "branch.h"
#include "path.h"
#include "symbol_conf.h"
@@ -19,8 +18,7 @@
#endif
#include <elf.h>
-#include "dso.h"
-
+struct dso;
struct map;
struct map_groups;
struct option;
@@ -40,15 +38,6 @@ Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
GElf_Shdr *shp, const char *name, size_t *idx);
#endif
-#ifndef DMGL_PARAMS
-#define DMGL_NO_OPTS 0 /* For readability... */
-#define DMGL_PARAMS (1 << 0) /* Include function args */
-#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
-#endif
-
-#define DSO__NAME_KALLSYMS "[kernel.kallsyms]"
-#define DSO__NAME_KCORE "[kernel.kcore]"
-
/** struct symbol - symtab entry
*
* @ignore - resolvable but tools ignore it (e.g. idle routines)
@@ -116,18 +105,14 @@ struct ref_reloc_sym {
u64 unrelocated_addr;
};
-struct branch_info {
- struct addr_map_symbol from;
- struct addr_map_symbol to;
- struct branch_flags flags;
- char *srcline_from;
- char *srcline_to;
-};
-
-struct mem_info {
- struct addr_map_symbol iaddr;
- struct addr_map_symbol daddr;
- union perf_mem_data_src data_src;
+struct block_info {
+ struct symbol *sym;
+ u64 start;
+ u64 end;
+ u64 cycles;
+ u64 cycles_aggr;
+ int num;
+ int num_aggr;
refcount_t refcnt;
};
@@ -145,37 +130,6 @@ struct addr_location {
s32 socket;
};
-struct symsrc {
- char *name;
- int fd;
- enum dso_binary_type type;
-
-#ifdef HAVE_LIBELF_SUPPORT
- Elf *elf;
- GElf_Ehdr ehdr;
-
- Elf_Scn *opdsec;
- size_t opdidx;
- GElf_Shdr opdshdr;
-
- Elf_Scn *symtab;
- GElf_Shdr symshdr;
-
- Elf_Scn *dynsym;
- size_t dynsym_idx;
- GElf_Shdr dynshdr;
-
- bool adjust_symbols;
- bool is_64_bit;
-#endif
-};
-
-void symsrc__destroy(struct symsrc *ss);
-int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
- enum dso_binary_type type);
-bool symsrc__has_symtab(struct symsrc *ss);
-bool symsrc__possibly_runtime(struct symsrc *ss);
-
int dso__load(struct dso *dso, struct map *map);
int dso__load_vmlinux(struct dso *dso, struct map *map,
const char *vmlinux, bool vmlinux_allocated);
@@ -229,6 +183,8 @@ bool symbol__restricted_filename(const char *filename,
int symbol__config_symfs(const struct option *opt __maybe_unused,
const char *dir, int unset __maybe_unused);
+struct symsrc;
+
int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss,
struct symsrc *runtime_ss, int kmodule);
int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss);
@@ -277,6 +233,7 @@ const char *arch__normalize_symbol_name(const char *name);
#define SYMBOL_A 0
#define SYMBOL_B 1
+void arch__symbols__fixup_end(struct symbol *p, struct symbol *c);
int arch__compare_symbol_names(const char *namea, const char *nameb);
int arch__compare_symbol_names_n(const char *namea, const char *nameb,
unsigned int n);
@@ -332,4 +289,16 @@ static inline void __mem_info__zput(struct mem_info **mi)
#define mem_info__zput(mi) __mem_info__zput(&mi)
+struct block_info *block_info__new(void);
+struct block_info *block_info__get(struct block_info *bi);
+void block_info__put(struct block_info *bi);
+
+static inline void __block_info__zput(struct block_info **bi)
+{
+ block_info__put(*bi);
+ *bi = NULL;
+}
+
+#define block_info__zput(bi) __block_info__zput(&bi)
+
#endif /* __PERF_SYMBOL */
diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h
index 6c55fa6fccec..e6880789864c 100644
--- a/tools/perf/util/symbol_conf.h
+++ b/tools/perf/util/symbol_conf.h
@@ -39,7 +39,9 @@ struct symbol_conf {
hide_unresolved,
raw_trace,
report_hierarchy,
- inline_name;
+ report_block,
+ inline_name,
+ disable_add2line_warn;
const char *vmlinux_name,
*kallsyms_name,
*source_prefix,
@@ -69,6 +71,7 @@ struct symbol_conf {
*tid_list;
const char *symfs;
int res_sample;
+ int pad_output_len_dso;
};
extern struct symbol_conf symbol_conf;
diff --git a/tools/perf/util/symbol_fprintf.c b/tools/perf/util/symbol_fprintf.c
index 02e89b02c2ce..35c936ce33ef 100644
--- a/tools/perf/util/symbol_fprintf.c
+++ b/tools/perf/util/symbol_fprintf.c
@@ -3,6 +3,7 @@
#include <inttypes.h>
#include <stdio.h>
+#include "dso.h"
#include "map.h"
#include "symbol.h"
diff --git a/tools/perf/util/symsrc.h b/tools/perf/util/symsrc.h
new file mode 100644
index 000000000000..2665b4bde751
--- /dev/null
+++ b/tools/perf/util/symsrc.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PERF_SYMSRC_
+#define __PERF_SYMSRC_ 1
+
+#include <stdbool.h>
+#include <stddef.h>
+#include "dso.h"
+
+#ifdef HAVE_LIBELF_SUPPORT
+#include <libelf.h>
+#include <gelf.h>
+#endif
+#include <elf.h>
+
+struct symsrc {
+ char *name;
+ int fd;
+ enum dso_binary_type type;
+
+#ifdef HAVE_LIBELF_SUPPORT
+ Elf *elf;
+ GElf_Ehdr ehdr;
+
+ Elf_Scn *opdsec;
+ size_t opdidx;
+ GElf_Shdr opdshdr;
+
+ Elf_Scn *symtab;
+ GElf_Shdr symshdr;
+
+ Elf_Scn *dynsym;
+ size_t dynsym_idx;
+ GElf_Shdr dynshdr;
+
+ bool adjust_symbols;
+ bool is_64_bit;
+#endif
+};
+
+int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, enum dso_binary_type type);
+void symsrc__destroy(struct symsrc *ss);
+
+bool symsrc__has_symtab(struct symsrc *ss);
+bool symsrc__possibly_runtime(struct symsrc *ss);
+
+#endif /* __PERF_SYMSRC_ */
diff --git a/tools/perf/util/syscalltbl.c b/tools/perf/util/syscalltbl.c
index 3393d7ee9401..820fceeb19a9 100644
--- a/tools/perf/util/syscalltbl.c
+++ b/tools/perf/util/syscalltbl.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* System call table mapper
*
* (C) 2016 Arnaldo Carvalho de Melo <acme@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#include "syscalltbl.h"
@@ -18,9 +10,9 @@
#include <linux/compiler.h>
#ifdef HAVE_SYSCALL_TABLE_SUPPORT
+#include <linux/zalloc.h>
#include <string.h>
#include "string2.h"
-#include "util.h"
#if defined(__x86_64__)
#include <asm/syscalls_64.c>
@@ -87,6 +79,7 @@ static int syscalltbl__init_native(struct syscalltbl *tbl)
qsort(tbl->syscalls.entries, nr_entries, sizeof(struct syscall), syscallcmp);
tbl->syscalls.nr_entries = nr_entries;
+ tbl->syscalls.max_id = syscalltbl_native_max_id;
return 0;
}
diff --git a/tools/perf/util/syscalltbl.h b/tools/perf/util/syscalltbl.h
index c8e7e9ce0f01..9172613028d0 100644
--- a/tools/perf/util/syscalltbl.h
+++ b/tools/perf/util/syscalltbl.h
@@ -6,6 +6,7 @@ struct syscalltbl {
union {
int audit_machine;
struct {
+ int max_id;
int nr_entries;
void *entries;
} syscalls;
diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c
index 21c4d9b23c24..565f7aef7e6c 100644
--- a/tools/perf/util/target.c
+++ b/tools/perf/util/target.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Helper functions for handling target threads/cpus
*
* Copyright (C) 2012, LG Electronics, Namhyung Kim <namhyung.kim@lge.com>
- *
- * Released under the GPL v2.
*/
#include "target.h"
@@ -11,8 +10,11 @@
#include "debug.h"
#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-
+#include <linux/kernel.h>
+#include <linux/string.h>
enum target_errno target__validate(struct target *target)
{
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
index 41942c2aaa18..cd8a948d03ec 100644
--- a/tools/perf/util/thread-stack.c
+++ b/tools/perf/util/thread-stack.c
@@ -1,27 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* thread-stack.c: Synthesize a thread's stack using call / return events
* Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#include <linux/rbtree.h>
#include <linux/list.h>
#include <linux/log2.h>
+#include <linux/zalloc.h>
#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
#include "thread.h"
#include "event.h"
#include "machine.h"
#include "env.h"
-#include "util.h"
#include "debug.h"
#include "symbol.h"
#include "comm.h"
@@ -49,6 +42,8 @@ enum retpoline_state_t {
* @timestamp: timestamp (if known)
* @ref: external reference (e.g. db_id of sample)
* @branch_count: the branch count when the entry was created
+ * @insn_count: the instruction count when the entry was created
+ * @cyc_count the cycle count when the entry was created
* @db_id: id used for db-export
* @cp: call path
* @no_call: a 'call' was not seen
@@ -60,6 +55,8 @@ struct thread_stack_entry {
u64 timestamp;
u64 ref;
u64 branch_count;
+ u64 insn_count;
+ u64 cyc_count;
u64 db_id;
struct call_path *cp;
bool no_call;
@@ -75,6 +72,8 @@ struct thread_stack_entry {
* @sz: current maximum stack size
* @trace_nr: current trace number
* @branch_count: running branch count
+ * @insn_count: running instruction count
+ * @cyc_count running cycle count
* @kernel_start: kernel start address
* @last_time: last timestamp
* @crp: call/return processor
@@ -88,6 +87,8 @@ struct thread_stack {
size_t sz;
u64 trace_nr;
u64 branch_count;
+ u64 insn_count;
+ u64 cyc_count;
u64 kernel_start;
u64 last_time;
struct call_return_processor *crp;
@@ -289,6 +290,8 @@ static int thread_stack__call_return(struct thread *thread,
cr.call_time = tse->timestamp;
cr.return_time = timestamp;
cr.branch_count = ts->branch_count - tse->branch_count;
+ cr.insn_count = ts->insn_count - tse->insn_count;
+ cr.cyc_count = ts->cyc_count - tse->cyc_count;
cr.db_id = tse->db_id;
cr.call_ref = tse->ref;
cr.return_ref = ref;
@@ -544,6 +547,8 @@ static int thread_stack__push_cp(struct thread_stack *ts, u64 ret_addr,
tse->timestamp = timestamp;
tse->ref = ref;
tse->branch_count = ts->branch_count;
+ tse->insn_count = ts->insn_count;
+ tse->cyc_count = ts->cyc_count;
tse->cp = cp;
tse->no_call = no_call;
tse->trace_end = trace_end;
@@ -625,6 +630,23 @@ static int thread_stack__bottom(struct thread_stack *ts,
true, false);
}
+static int thread_stack__pop_ks(struct thread *thread, struct thread_stack *ts,
+ struct perf_sample *sample, u64 ref)
+{
+ u64 tm = sample->time;
+ int err;
+
+ /* Return to userspace, so pop all kernel addresses */
+ while (thread_stack__in_kernel(ts)) {
+ err = thread_stack__call_return(thread, ts, --ts->cnt,
+ tm, ref, true);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
static int thread_stack__no_call_return(struct thread *thread,
struct thread_stack *ts,
struct perf_sample *sample,
@@ -644,12 +666,9 @@ static int thread_stack__no_call_return(struct thread *thread,
if (ip >= ks && addr < ks) {
/* Return to userspace, so pop all kernel addresses */
- while (thread_stack__in_kernel(ts)) {
- err = thread_stack__call_return(thread, ts, --ts->cnt,
- tm, ref, true);
- if (err)
- return err;
- }
+ err = thread_stack__pop_ks(thread, ts, sample, ref);
+ if (err)
+ return err;
/* If the stack is empty, push the userspace address */
if (!ts->cnt) {
@@ -659,12 +678,9 @@ static int thread_stack__no_call_return(struct thread *thread,
}
} else if (thread_stack__in_kernel(ts) && ip < ks) {
/* Return to userspace, so pop all kernel addresses */
- while (thread_stack__in_kernel(ts)) {
- err = thread_stack__call_return(thread, ts, --ts->cnt,
- tm, ref, true);
- if (err)
- return err;
- }
+ err = thread_stack__pop_ks(thread, ts, sample, ref);
+ if (err)
+ return err;
}
if (ts->cnt)
@@ -874,6 +890,8 @@ int thread_stack__process(struct thread *thread, struct comm *comm,
}
ts->branch_count += 1;
+ ts->insn_count += sample->insn_cnt;
+ ts->cyc_count += sample->cyc_cnt;
ts->last_time = sample->time;
if (sample->flags & PERF_IP_FLAG_CALL) {
@@ -905,7 +923,18 @@ int thread_stack__process(struct thread *thread, struct comm *comm,
ts->rstate = X86_RETPOLINE_DETECTED;
} else if (sample->flags & PERF_IP_FLAG_RETURN) {
- if (!sample->ip || !sample->addr)
+ if (!sample->addr) {
+ u32 return_from_kernel = PERF_IP_FLAG_SYSCALLRET |
+ PERF_IP_FLAG_INTERRUPT;
+
+ if (!(sample->flags & return_from_kernel))
+ return 0;
+
+ /* Pop kernel stack */
+ return thread_stack__pop_ks(thread, ts, sample, ref);
+ }
+
+ if (!sample->ip)
return 0;
/* x86 retpoline 'return' doesn't match the stack */
diff --git a/tools/perf/util/thread-stack.h b/tools/perf/util/thread-stack.h
index 9c45f947f5a9..e1ec5a58f1b2 100644
--- a/tools/perf/util/thread-stack.h
+++ b/tools/perf/util/thread-stack.h
@@ -1,16 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* thread-stack.h: Synthesize a thread's stack using call / return events
* Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
*/
#ifndef __PERF_THREAD_STACK_H
@@ -52,6 +43,8 @@ enum {
* @call_time: timestamp of call (if known)
* @return_time: timestamp of return (if known)
* @branch_count: number of branches seen between call and return
+ * @insn_count: approx. number of instructions between call and return
+ * @cyc_count: approx. number of cycles between call and return
* @call_ref: external reference to 'call' sample (e.g. db_id)
* @return_ref: external reference to 'return' sample (e.g. db_id)
* @db_id: id used for db-export
@@ -65,6 +58,8 @@ struct call_return {
u64 call_time;
u64 return_time;
u64 branch_count;
+ u64 insn_count;
+ u64 cyc_count;
u64 call_ref;
u64 return_ref;
u64 db_id;
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 403045a2bbea..b64e9e049636 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -1,14 +1,14 @@
// SPDX-License-Identifier: GPL-2.0
-#include "../perf.h"
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include "dso.h"
#include "session.h"
#include "thread.h"
#include "thread-stack.h"
-#include "util.h"
#include "debug.h"
#include "namespaces.h"
#include "comm.h"
@@ -93,19 +93,18 @@ void thread__delete(struct thread *thread)
down_write(&thread->namespaces_lock);
list_for_each_entry_safe(namespaces, tmp_namespaces,
&thread->namespaces_list, list) {
- list_del(&namespaces->list);
+ list_del_init(&namespaces->list);
namespaces__free(namespaces);
}
up_write(&thread->namespaces_lock);
down_write(&thread->comm_lock);
list_for_each_entry_safe(comm, tmp_comm, &thread->comm_list, list) {
- list_del(&comm->list);
+ list_del_init(&comm->list);
comm__free(comm);
}
up_write(&thread->comm_lock);
- unwind__finish_access(thread);
nsinfo__zput(thread->nsinfo);
srccode_state_free(&thread->srccode_state);
@@ -125,15 +124,32 @@ void thread__put(struct thread *thread)
{
if (thread && refcount_dec_and_test(&thread->refcnt)) {
/*
- * Remove it from the dead_threads list, as last reference
- * is gone.
+ * Remove it from the dead threads list, as last reference is
+ * gone, if it is in a dead threads list.
+ *
+ * We may not be there anymore if say, the machine where it was
+ * stored was already deleted, so we already removed it from
+ * the dead threads and some other piece of code still keeps a
+ * reference.
+ *
+ * This is what 'perf sched' does and finally drops it in
+ * perf_sched__lat(), where it calls perf_sched__read_events(),
+ * that processes the events by creating a session and deleting
+ * it, which ends up destroying the list heads for the dead
+ * threads, but before it does that it removes all threads from
+ * it using list_del_init().
+ *
+ * So we need to check here if it is in a dead threads list and
+ * if so, remove it before finally deleting the thread, to avoid
+ * an use after free situation.
*/
- list_del_init(&thread->node);
+ if (!list_empty(&thread->node))
+ list_del_init(&thread->node);
thread__delete(thread);
}
}
-struct namespaces *thread__namespaces(const struct thread *thread)
+static struct namespaces *__thread__namespaces(const struct thread *thread)
{
if (list_empty(&thread->namespaces_list))
return NULL;
@@ -141,10 +157,21 @@ struct namespaces *thread__namespaces(const struct thread *thread)
return list_first_entry(&thread->namespaces_list, struct namespaces, list);
}
+struct namespaces *thread__namespaces(struct thread *thread)
+{
+ struct namespaces *ns;
+
+ down_read(&thread->namespaces_lock);
+ ns = __thread__namespaces(thread);
+ up_read(&thread->namespaces_lock);
+
+ return ns;
+}
+
static int __thread__set_namespaces(struct thread *thread, u64 timestamp,
- struct namespaces_event *event)
+ struct perf_record_namespaces *event)
{
- struct namespaces *new, *curr = thread__namespaces(thread);
+ struct namespaces *new, *curr = __thread__namespaces(thread);
new = namespaces__new(event);
if (!new)
@@ -166,7 +193,7 @@ static int __thread__set_namespaces(struct thread *thread, u64 timestamp,
}
int thread__set_namespaces(struct thread *thread, u64 timestamp,
- struct namespaces_event *event)
+ struct perf_record_namespaces *event)
{
int ret;
@@ -186,14 +213,24 @@ struct comm *thread__comm(const struct thread *thread)
struct comm *thread__exec_comm(const struct thread *thread)
{
- struct comm *comm, *last = NULL;
+ struct comm *comm, *last = NULL, *second_last = NULL;
list_for_each_entry(comm, &thread->comm_list, list) {
if (comm->exec)
return comm;
+ second_last = last;
last = comm;
}
+ /*
+ * 'last' with no start time might be the parent's comm of a synthesized
+ * thread (created by processing a synthesized fork event). For a main
+ * thread, that is very probably wrong. Prefer a later comm to avoid
+ * that case.
+ */
+ if (second_last && !last->start && thread->pid_ == thread->tid)
+ return second_last;
+
return last;
}
@@ -214,7 +251,7 @@ static int ____thread__set_comm(struct thread *thread, const char *str,
list_add(&new->list, &thread->comm_list);
if (exec)
- unwind__flush_access(thread);
+ unwind__flush_access(thread->mg);
}
thread->comm_set = true;
@@ -260,13 +297,13 @@ static const char *__thread__comm_str(const struct thread *thread)
return comm__str(comm);
}
-const char *thread__comm_str(const struct thread *thread)
+const char *thread__comm_str(struct thread *thread)
{
const char *str;
- down_read((struct rw_semaphore *)&thread->comm_lock);
+ down_read(&thread->comm_lock);
str = __thread__comm_str(thread);
- up_read((struct rw_semaphore *)&thread->comm_lock);
+ up_read(&thread->comm_lock);
return str;
}
@@ -294,7 +331,7 @@ int thread__insert_map(struct thread *thread, struct map *map)
{
int ret;
- ret = unwind__prepare_access(thread, map, NULL);
+ ret = unwind__prepare_access(thread->mg, map, NULL);
if (ret)
return ret;
@@ -314,7 +351,7 @@ static int __thread__prepare_access(struct thread *thread)
down_read(&maps->lock);
for (map = maps__first(maps); map; map = map__next(map)) {
- err = unwind__prepare_access(thread, map, &initialized);
+ err = unwind__prepare_access(thread->mg, map, &initialized);
if (err || initialized)
break;
}
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index cf8375c017a0..51bdb9a7af7f 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -16,7 +16,7 @@
struct addr_location;
struct map;
-struct namespaces_event;
+struct perf_record_namespaces;
struct thread_stack;
struct unwind_libunwind_ops;
@@ -44,10 +44,6 @@ struct thread {
struct thread_stack *ts;
struct nsinfo *nsinfo;
struct srccode_state srccode_state;
-#ifdef HAVE_LIBUNWIND_SUPPORT
- void *addr_space;
- struct unwind_libunwind_ops *unwind_libunwind_ops;
-#endif
bool filter;
int filter_entry_depth;
};
@@ -76,9 +72,9 @@ static inline void thread__exited(struct thread *thread)
thread->dead = true;
}
-struct namespaces *thread__namespaces(const struct thread *thread);
+struct namespaces *thread__namespaces(struct thread *thread);
int thread__set_namespaces(struct thread *thread, u64 timestamp,
- struct namespaces_event *event);
+ struct perf_record_namespaces *event);
int __thread__set_comm(struct thread *thread, const char *comm, u64 timestamp,
bool exec);
@@ -93,7 +89,7 @@ int thread__set_comm_from_proc(struct thread *thread);
int thread__comm_len(struct thread *thread);
struct comm *thread__comm(const struct thread *thread);
struct comm *thread__exec_comm(const struct thread *thread);
-const char *thread__comm_str(const struct thread *thread);
+const char *thread__comm_str(struct thread *thread);
int thread__insert_map(struct thread *thread, struct map *map);
int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp, bool do_maps_clone);
size_t thread__fprintf(struct thread *thread, FILE *fp);
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c
index 5d467d8ae9ab..c9bfe4696943 100644
--- a/tools/perf/util/thread_map.c
+++ b/tools/perf/util/thread_map.c
@@ -12,9 +12,10 @@
#include "strlist.h"
#include <string.h>
#include <api/fs/fs.h>
+#include <linux/string.h>
+#include <linux/zalloc.h>
#include "asm/bug.h"
#include "thread_map.h"
-#include "util.h"
#include "debug.h"
#include "event.h"
@@ -27,34 +28,11 @@ static int filter(const struct dirent *dir)
return 1;
}
-static void thread_map__reset(struct thread_map *map, int start, int nr)
-{
- size_t size = (nr - start) * sizeof(map->map[0]);
-
- memset(&map->map[start], 0, size);
- map->err_thread = -1;
-}
+#define thread_map__alloc(__nr) perf_thread_map__realloc(NULL, __nr)
-static struct thread_map *thread_map__realloc(struct thread_map *map, int nr)
+struct perf_thread_map *thread_map__new_by_pid(pid_t pid)
{
- size_t size = sizeof(*map) + sizeof(map->map[0]) * nr;
- int start = map ? map->nr : 0;
-
- map = realloc(map, size);
- /*
- * We only realloc to add more items, let's reset new items.
- */
- if (map)
- thread_map__reset(map, start, nr);
-
- return map;
-}
-
-#define thread_map__alloc(__nr) thread_map__realloc(NULL, __nr)
-
-struct thread_map *thread_map__new_by_pid(pid_t pid)
-{
- struct thread_map *threads;
+ struct perf_thread_map *threads;
char name[256];
int items;
struct dirent **namelist = NULL;
@@ -68,7 +46,7 @@ struct thread_map *thread_map__new_by_pid(pid_t pid)
threads = thread_map__alloc(items);
if (threads != NULL) {
for (i = 0; i < items; i++)
- thread_map__set_pid(threads, i, atoi(namelist[i]->d_name));
+ perf_thread_map__set_pid(threads, i, atoi(namelist[i]->d_name));
threads->nr = items;
refcount_set(&threads->refcnt, 1);
}
@@ -80,12 +58,12 @@ struct thread_map *thread_map__new_by_pid(pid_t pid)
return threads;
}
-struct thread_map *thread_map__new_by_tid(pid_t tid)
+struct perf_thread_map *thread_map__new_by_tid(pid_t tid)
{
- struct thread_map *threads = thread_map__alloc(1);
+ struct perf_thread_map *threads = thread_map__alloc(1);
if (threads != NULL) {
- thread_map__set_pid(threads, 0, tid);
+ perf_thread_map__set_pid(threads, 0, tid);
threads->nr = 1;
refcount_set(&threads->refcnt, 1);
}
@@ -93,13 +71,13 @@ struct thread_map *thread_map__new_by_tid(pid_t tid)
return threads;
}
-static struct thread_map *__thread_map__new_all_cpus(uid_t uid)
+static struct perf_thread_map *__thread_map__new_all_cpus(uid_t uid)
{
DIR *proc;
int max_threads = 32, items, i;
char path[NAME_MAX + 1 + 6];
struct dirent *dirent, **namelist = NULL;
- struct thread_map *threads = thread_map__alloc(max_threads);
+ struct perf_thread_map *threads = thread_map__alloc(max_threads);
if (threads == NULL)
goto out;
@@ -139,9 +117,9 @@ static struct thread_map *__thread_map__new_all_cpus(uid_t uid)
}
if (grow) {
- struct thread_map *tmp;
+ struct perf_thread_map *tmp;
- tmp = thread_map__realloc(threads, max_threads);
+ tmp = perf_thread_map__realloc(threads, max_threads);
if (tmp == NULL)
goto out_free_namelist;
@@ -149,8 +127,8 @@ static struct thread_map *__thread_map__new_all_cpus(uid_t uid)
}
for (i = 0; i < items; i++) {
- thread_map__set_pid(threads, threads->nr + i,
- atoi(namelist[i]->d_name));
+ perf_thread_map__set_pid(threads, threads->nr + i,
+ atoi(namelist[i]->d_name));
}
for (i = 0; i < items; i++)
@@ -179,17 +157,17 @@ out_free_closedir:
goto out_closedir;
}
-struct thread_map *thread_map__new_all_cpus(void)
+struct perf_thread_map *thread_map__new_all_cpus(void)
{
return __thread_map__new_all_cpus(UINT_MAX);
}
-struct thread_map *thread_map__new_by_uid(uid_t uid)
+struct perf_thread_map *thread_map__new_by_uid(uid_t uid)
{
return __thread_map__new_all_cpus(uid);
}
-struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid)
+struct perf_thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid)
{
if (pid != -1)
return thread_map__new_by_pid(pid);
@@ -200,9 +178,9 @@ struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid)
return thread_map__new_by_tid(tid);
}
-static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
+static struct perf_thread_map *thread_map__new_by_pid_str(const char *pid_str)
{
- struct thread_map *threads = NULL, *nt;
+ struct perf_thread_map *threads = NULL, *nt;
char name[256];
int items, total_tasks = 0;
struct dirent **namelist = NULL;
@@ -232,14 +210,14 @@ static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
goto out_free_threads;
total_tasks += items;
- nt = thread_map__realloc(threads, total_tasks);
+ nt = perf_thread_map__realloc(threads, total_tasks);
if (nt == NULL)
goto out_free_namelist;
threads = nt;
for (i = 0; i < items; i++) {
- thread_map__set_pid(threads, j++, atoi(namelist[i]->d_name));
+ perf_thread_map__set_pid(threads, j++, atoi(namelist[i]->d_name));
zfree(&namelist[i]);
}
threads->nr = total_tasks;
@@ -262,21 +240,9 @@ out_free_threads:
goto out;
}
-struct thread_map *thread_map__new_dummy(void)
-{
- struct thread_map *threads = thread_map__alloc(1);
-
- if (threads != NULL) {
- thread_map__set_pid(threads, 0, -1);
- threads->nr = 1;
- refcount_set(&threads->refcnt, 1);
- }
- return threads;
-}
-
-struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
+struct perf_thread_map *thread_map__new_by_tid_str(const char *tid_str)
{
- struct thread_map *threads = NULL, *nt;
+ struct perf_thread_map *threads = NULL, *nt;
int ntasks = 0;
pid_t tid, prev_tid = INT_MAX;
char *end_ptr;
@@ -286,7 +252,7 @@ struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
/* perf-stat expects threads to be generated even if tid not given */
if (!tid_str)
- return thread_map__new_dummy();
+ return perf_thread_map__new_dummy();
slist = strlist__new(tid_str, &slist_config);
if (!slist)
@@ -303,13 +269,13 @@ struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
continue;
ntasks++;
- nt = thread_map__realloc(threads, ntasks);
+ nt = perf_thread_map__realloc(threads, ntasks);
if (nt == NULL)
goto out_free_threads;
threads = nt;
- thread_map__set_pid(threads, ntasks - 1, tid);
+ perf_thread_map__set_pid(threads, ntasks - 1, tid);
threads->nr = ntasks;
}
out:
@@ -323,7 +289,7 @@ out_free_threads:
goto out;
}
-struct thread_map *thread_map__new_str(const char *pid, const char *tid,
+struct perf_thread_map *thread_map__new_str(const char *pid, const char *tid,
uid_t uid, bool all_threads)
{
if (pid)
@@ -338,39 +304,13 @@ struct thread_map *thread_map__new_str(const char *pid, const char *tid,
return thread_map__new_by_tid_str(tid);
}
-static void thread_map__delete(struct thread_map *threads)
-{
- if (threads) {
- int i;
-
- WARN_ONCE(refcount_read(&threads->refcnt) != 0,
- "thread map refcnt unbalanced\n");
- for (i = 0; i < threads->nr; i++)
- free(thread_map__comm(threads, i));
- free(threads);
- }
-}
-
-struct thread_map *thread_map__get(struct thread_map *map)
-{
- if (map)
- refcount_inc(&map->refcnt);
- return map;
-}
-
-void thread_map__put(struct thread_map *map)
-{
- if (map && refcount_dec_and_test(&map->refcnt))
- thread_map__delete(map);
-}
-
-size_t thread_map__fprintf(struct thread_map *threads, FILE *fp)
+size_t thread_map__fprintf(struct perf_thread_map *threads, FILE *fp)
{
int i;
size_t printed = fprintf(fp, "%d thread%s: ",
threads->nr, threads->nr > 1 ? "s" : "");
for (i = 0; i < threads->nr; ++i)
- printed += fprintf(fp, "%s%d", i ? ", " : "", thread_map__pid(threads, i));
+ printed += fprintf(fp, "%s%d", i ? ", " : "", perf_thread_map__pid(threads, i));
return printed + fprintf(fp, "\n");
}
@@ -392,16 +332,16 @@ static int get_comm(char **comm, pid_t pid)
* mark the end of the string.
*/
(*comm)[size] = 0;
- rtrim(*comm);
+ strim(*comm);
}
free(path);
return err;
}
-static void comm_init(struct thread_map *map, int i)
+static void comm_init(struct perf_thread_map *map, int i)
{
- pid_t pid = thread_map__pid(map, i);
+ pid_t pid = perf_thread_map__pid(map, i);
char *comm = NULL;
/* dummy pid comm initialization */
@@ -420,7 +360,7 @@ static void comm_init(struct thread_map *map, int i)
map->map[i].comm = comm;
}
-void thread_map__read_comms(struct thread_map *threads)
+void thread_map__read_comms(struct perf_thread_map *threads)
{
int i;
@@ -428,24 +368,24 @@ void thread_map__read_comms(struct thread_map *threads)
comm_init(threads, i);
}
-static void thread_map__copy_event(struct thread_map *threads,
- struct thread_map_event *event)
+static void thread_map__copy_event(struct perf_thread_map *threads,
+ struct perf_record_thread_map *event)
{
unsigned i;
threads->nr = (int) event->nr;
for (i = 0; i < event->nr; i++) {
- thread_map__set_pid(threads, i, (pid_t) event->entries[i].pid);
+ perf_thread_map__set_pid(threads, i, (pid_t) event->entries[i].pid);
threads->map[i].comm = strndup(event->entries[i].comm, 16);
}
refcount_set(&threads->refcnt, 1);
}
-struct thread_map *thread_map__new_event(struct thread_map_event *event)
+struct perf_thread_map *thread_map__new_event(struct perf_record_thread_map *event)
{
- struct thread_map *threads;
+ struct perf_thread_map *threads;
threads = thread_map__alloc(event->nr);
if (threads)
@@ -454,7 +394,7 @@ struct thread_map *thread_map__new_event(struct thread_map_event *event)
return threads;
}
-bool thread_map__has(struct thread_map *threads, pid_t pid)
+bool thread_map__has(struct perf_thread_map *threads, pid_t pid)
{
int i;
@@ -466,7 +406,7 @@ bool thread_map__has(struct thread_map *threads, pid_t pid)
return false;
}
-int thread_map__remove(struct thread_map *threads, int idx)
+int thread_map__remove(struct perf_thread_map *threads, int idx)
{
int i;
@@ -479,7 +419,7 @@ int thread_map__remove(struct thread_map *threads, int idx)
/*
* Free the 'idx' item and shift the rest up.
*/
- free(threads->map[idx].comm);
+ zfree(&threads->map[idx].comm);
for (i = idx; i < threads->nr - 1; i++)
threads->map[i] = threads->map[i + 1];
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h
index 2f689c90a8c6..3bb860a32b8e 100644
--- a/tools/perf/util/thread_map.h
+++ b/tools/perf/util/thread_map.h
@@ -5,61 +5,27 @@
#include <sys/types.h>
#include <stdio.h>
#include <linux/refcount.h>
+#include <internal/threadmap.h>
+#include <perf/threadmap.h>
-struct thread_map_data {
- pid_t pid;
- char *comm;
-};
+struct perf_record_thread_map;
-struct thread_map {
- refcount_t refcnt;
- int nr;
- int err_thread;
- struct thread_map_data map[];
-};
+struct perf_thread_map *thread_map__new_dummy(void);
+struct perf_thread_map *thread_map__new_by_pid(pid_t pid);
+struct perf_thread_map *thread_map__new_by_tid(pid_t tid);
+struct perf_thread_map *thread_map__new_by_uid(uid_t uid);
+struct perf_thread_map *thread_map__new_all_cpus(void);
+struct perf_thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid);
+struct perf_thread_map *thread_map__new_event(struct perf_record_thread_map *event);
-struct thread_map_event;
-
-struct thread_map *thread_map__new_dummy(void);
-struct thread_map *thread_map__new_by_pid(pid_t pid);
-struct thread_map *thread_map__new_by_tid(pid_t tid);
-struct thread_map *thread_map__new_by_uid(uid_t uid);
-struct thread_map *thread_map__new_all_cpus(void);
-struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid);
-struct thread_map *thread_map__new_event(struct thread_map_event *event);
-
-struct thread_map *thread_map__get(struct thread_map *map);
-void thread_map__put(struct thread_map *map);
-
-struct thread_map *thread_map__new_str(const char *pid,
+struct perf_thread_map *thread_map__new_str(const char *pid,
const char *tid, uid_t uid, bool all_threads);
-struct thread_map *thread_map__new_by_tid_str(const char *tid_str);
-
-size_t thread_map__fprintf(struct thread_map *threads, FILE *fp);
-
-static inline int thread_map__nr(struct thread_map *threads)
-{
- return threads ? threads->nr : 1;
-}
-
-static inline pid_t thread_map__pid(struct thread_map *map, int thread)
-{
- return map->map[thread].pid;
-}
-
-static inline void
-thread_map__set_pid(struct thread_map *map, int thread, pid_t pid)
-{
- map->map[thread].pid = pid;
-}
+struct perf_thread_map *thread_map__new_by_tid_str(const char *tid_str);
-static inline char *thread_map__comm(struct thread_map *map, int thread)
-{
- return map->map[thread].comm;
-}
+size_t thread_map__fprintf(struct perf_thread_map *threads, FILE *fp);
-void thread_map__read_comms(struct thread_map *threads);
-bool thread_map__has(struct thread_map *threads, pid_t pid);
-int thread_map__remove(struct thread_map *threads, int idx);
+void thread_map__read_comms(struct perf_thread_map *threads);
+bool thread_map__has(struct perf_thread_map *threads, pid_t pid);
+int thread_map__remove(struct perf_thread_map *threads, int idx);
#endif /* __PERF_THREAD_MAP_H */
diff --git a/tools/perf/util/time-utils.c b/tools/perf/util/time-utils.c
index 20663a460df3..9796a2e43f67 100644
--- a/tools/perf/util/time-utils.c
+++ b/tools/perf/util/time-utils.c
@@ -1,14 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdlib.h>
#include <string.h>
+#include <linux/string.h>
#include <sys/time.h>
#include <linux/time64.h>
#include <time.h>
#include <errno.h>
#include <inttypes.h>
#include <math.h>
+#include <linux/ctype.h>
-#include "perf.h"
#include "debug.h"
#include "time-utils.h"
#include "session.h"
@@ -116,6 +117,66 @@ int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr)
return rc;
}
+static int perf_time__parse_strs(struct perf_time_interval *ptime,
+ const char *ostr, int size)
+{
+ const char *cp;
+ char *str, *arg, *p;
+ int i, num = 0, rc = 0;
+
+ /* Count the commas */
+ for (cp = ostr; *cp; cp++)
+ num += !!(*cp == ',');
+
+ if (!num)
+ return -EINVAL;
+
+ BUG_ON(num > size);
+
+ str = strdup(ostr);
+ if (!str)
+ return -ENOMEM;
+
+ /* Split the string and parse each piece, except the last */
+ for (i = 0, p = str; i < num - 1; i++) {
+ arg = p;
+ /* Find next comma, there must be one */
+ p = skip_spaces(strchr(p, ',') + 1);
+ /* Skip the value, must not contain space or comma */
+ while (*p && !isspace(*p)) {
+ if (*p++ == ',') {
+ rc = -EINVAL;
+ goto out;
+ }
+ }
+ /* Split and parse */
+ if (*p)
+ *p++ = 0;
+ rc = perf_time__parse_str(ptime + i, arg);
+ if (rc < 0)
+ goto out;
+ }
+
+ /* Parse the last piece */
+ rc = perf_time__parse_str(ptime + i, p);
+ if (rc < 0)
+ goto out;
+
+ /* Check there is no overlap */
+ for (i = 0; i < num - 1; i++) {
+ if (ptime[i].end >= ptime[i + 1].start) {
+ rc = -EINVAL;
+ goto out;
+ }
+ }
+
+ rc = num;
+out:
+ free(str);
+
+ return rc;
+}
+
static int parse_percent(double *pcnt, char *str)
{
char *c, *endptr;
@@ -135,12 +196,30 @@ static int parse_percent(double *pcnt, char *str)
return 0;
}
+static int set_percent_time(struct perf_time_interval *ptime, double start_pcnt,
+ double end_pcnt, u64 start, u64 end)
+{
+ u64 total = end - start;
+
+ if (start_pcnt < 0.0 || start_pcnt > 1.0 ||
+ end_pcnt < 0.0 || end_pcnt > 1.0) {
+ return -1;
+ }
+
+ ptime->start = start + round(start_pcnt * total);
+ ptime->end = start + round(end_pcnt * total);
+
+ if (ptime->end > ptime->start && ptime->end != end)
+ ptime->end -= 1;
+
+ return 0;
+}
+
static int percent_slash_split(char *str, struct perf_time_interval *ptime,
u64 start, u64 end)
{
char *p, *end_str;
double pcnt, start_pcnt, end_pcnt;
- u64 total = end - start;
int i;
/*
@@ -168,15 +247,7 @@ static int percent_slash_split(char *str, struct perf_time_interval *ptime,
start_pcnt = pcnt * (i - 1);
end_pcnt = pcnt * i;
- if (start_pcnt < 0.0 || start_pcnt > 1.0 ||
- end_pcnt < 0.0 || end_pcnt > 1.0) {
- return -1;
- }
-
- ptime->start = start + round(start_pcnt * total);
- ptime->end = start + round(end_pcnt * total);
-
- return 0;
+ return set_percent_time(ptime, start_pcnt, end_pcnt, start, end);
}
static int percent_dash_split(char *str, struct perf_time_interval *ptime,
@@ -184,7 +255,6 @@ static int percent_dash_split(char *str, struct perf_time_interval *ptime,
{
char *start_str = NULL, *end_str;
double start_pcnt, end_pcnt;
- u64 total = end - start;
int ret;
/*
@@ -203,16 +273,7 @@ static int percent_dash_split(char *str, struct perf_time_interval *ptime,
free(start_str);
- if (start_pcnt < 0.0 || start_pcnt > 1.0 ||
- end_pcnt < 0.0 || end_pcnt > 1.0 ||
- start_pcnt > end_pcnt) {
- return -1;
- }
-
- ptime->start = start + round(start_pcnt * total);
- ptime->end = start + round(end_pcnt * total);
-
- return 0;
+ return set_percent_time(ptime, start_pcnt, end_pcnt, start, end);
}
typedef int (*time_pecent_split)(char *, struct perf_time_interval *,
@@ -389,13 +450,12 @@ bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf,
ptime = &ptime_buf[i];
if (timestamp >= ptime->start &&
- ((timestamp < ptime->end && i < num - 1) ||
- (timestamp <= ptime->end && i == num - 1))) {
- break;
+ (timestamp <= ptime->end || !ptime->end)) {
+ return false;
}
}
- return (i == num) ? true : false;
+ return true;
}
int perf_time__parse_for_ranges(const char *time_str,
@@ -403,20 +463,20 @@ int perf_time__parse_for_ranges(const char *time_str,
struct perf_time_interval **ranges,
int *range_size, int *range_num)
{
+ bool has_percent = strchr(time_str, '%');
struct perf_time_interval *ptime_range;
- int size, num, ret;
+ int size, num, ret = -EINVAL;
ptime_range = perf_time__range_alloc(time_str, &size);
if (!ptime_range)
return -ENOMEM;
- if (perf_time__parse_str(ptime_range, time_str) != 0) {
+ if (has_percent) {
if (session->evlist->first_sample_time == 0 &&
session->evlist->last_sample_time == 0) {
pr_err("HINT: no first/last sample time found in perf data.\n"
"Please use latest perf binary to execute 'perf record'\n"
"(if '--buildid-all' is enabled, please set '--timestamp-boundary').\n");
- ret = -EINVAL;
goto error;
}
@@ -425,21 +485,20 @@ int perf_time__parse_for_ranges(const char *time_str,
time_str,
session->evlist->first_sample_time,
session->evlist->last_sample_time);
-
- if (num < 0) {
- pr_err("Invalid time string\n");
- ret = -EINVAL;
- goto error;
- }
} else {
- num = 1;
+ num = perf_time__parse_strs(ptime_range, time_str, size);
}
+ if (num < 0)
+ goto error_invalid;
+
*range_size = size;
*range_num = num;
*ranges = ptime_range;
return 0;
+error_invalid:
+ pr_err("Invalid time string\n");
error:
free(ptime_range);
return ret;
diff --git a/tools/perf/util/time-utils.h b/tools/perf/util/time-utils.h
index 72a42ea1d513..4f42988eb2f7 100644
--- a/tools/perf/util/time-utils.h
+++ b/tools/perf/util/time-utils.h
@@ -3,6 +3,7 @@
#define _TIME_UTILS_H_
#include <stddef.h>
+#include <time.h>
#include <linux/types.h>
struct perf_time_interval {
@@ -34,4 +35,12 @@ int timestamp__scnprintf_nsec(u64 timestamp, char *buf, size_t sz);
int fetch_current_timestamp(char *buf, size_t sz);
+static inline unsigned long long rdclock(void)
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+}
+
#endif
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h
index 9096a6e3de59..2abbf668b8de 100644
--- a/tools/perf/util/tool.h
+++ b/tools/perf/util/tool.h
@@ -8,8 +8,8 @@
struct perf_session;
union perf_event;
-struct perf_evlist;
-struct perf_evsel;
+struct evlist;
+struct evsel;
struct perf_sample;
struct perf_tool;
struct machine;
@@ -17,14 +17,14 @@ struct ordered_events;
typedef int (*event_sample)(struct perf_tool *tool, union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel, struct machine *machine);
+ struct evsel *evsel, struct machine *machine);
typedef int (*event_op)(struct perf_tool *tool, union perf_event *event,
struct perf_sample *sample, struct machine *machine);
typedef int (*event_attr_op)(struct perf_tool *tool,
union perf_event *event,
- struct perf_evlist **pevlist);
+ struct evlist **pevlist);
typedef int (*event_op2)(struct perf_session *session, union perf_event *event);
typedef s64 (*event_op3)(struct perf_session *session, union perf_event *event);
@@ -56,7 +56,7 @@ struct perf_tool {
throttle,
unthrottle,
ksymbol,
- bpf_event;
+ bpf;
event_attr_op attr;
event_attr_op event_update;
diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c
index 4c8da8c4435f..51fb574998bb 100644
--- a/tools/perf/util/top.c
+++ b/tools/perf/util/top.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
*
* Refactored from builtin-top.c, see that files for further copyright notes.
- *
- * Released under the GPL v2. (and only v2, not any later version)
*/
#include "cpumap.h"
@@ -13,6 +12,7 @@
#include "parse-events.h"
#include "symbol.h"
#include "top.h"
+#include "../perf.h"
#include <inttypes.h>
#define SNPRINTF(buf, size, fmt, args...) \
@@ -71,10 +71,10 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
esamples_percent);
}
- if (top->evlist->nr_entries == 1) {
- struct perf_evsel *first = perf_evlist__first(top->evlist);
+ if (top->evlist->core.nr_entries == 1) {
+ struct evsel *first = perf_evlist__first(top->evlist);
ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ",
- (uint64_t)first->attr.sample_period,
+ (uint64_t)first->core.attr.sample_period,
opts->freq ? "Hz" : "");
}
@@ -96,15 +96,15 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
if (target->cpu_list)
ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)",
- top->evlist->cpus->nr > 1 ? "s" : "",
+ top->evlist->core.cpus->nr > 1 ? "s" : "",
target->cpu_list);
else {
if (target->tid)
ret += SNPRINTF(bf + ret, size - ret, ")");
else
ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)",
- top->evlist->cpus->nr,
- top->evlist->cpus->nr > 1 ? "s" : "");
+ top->evlist->core.cpus->nr,
+ top->evlist->core.cpus->nr > 1 ? "s" : "");
}
perf_top__reset_sample_counters(top);
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h
index 19f95eaf75c8..f117d4f4821e 100644
--- a/tools/perf/util/top.h
+++ b/tools/perf/util/top.h
@@ -3,21 +3,25 @@
#define __PERF_TOP_H 1
#include "tool.h"
+#include "evswitch.h"
#include "annotate.h"
+#include "ordered-events.h"
+#include "record.h"
#include <linux/types.h>
#include <stddef.h>
#include <stdbool.h>
#include <sys/ioctl.h>
-struct perf_evlist;
-struct perf_evsel;
+struct evlist;
+struct evsel;
struct perf_session;
struct perf_top {
struct perf_tool tool;
- struct perf_evlist *evlist;
+ struct evlist *evlist;
struct record_opts record_opts;
struct annotation_options annotation_opts;
+ struct evswitch evswitch;
/*
* Symbols will be added here in perf_event__process_sample and will
* get out after decayed.
@@ -33,7 +37,7 @@ struct perf_top {
bool vmlinux_warned;
bool dump_symtab;
struct hist_entry *sym_filter_entry;
- struct perf_evsel *sym_evsel;
+ struct evsel *sym_evsel;
struct perf_session *session;
struct winsize winsize;
int realtime_prio;
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c
index 8ad8e755127b..d63d542b2cde 100644
--- a/tools/perf/util/trace-event-info.c
+++ b/tools/perf/util/trace-event-info.c
@@ -1,22 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2008,2009, Steven Rostedt <srostedt@redhat.com>
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License (not later!)
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include "util.h"
#include <dirent.h>
@@ -34,8 +18,8 @@
#include <stdbool.h>
#include <linux/list.h>
#include <linux/kernel.h>
+#include <linux/zalloc.h>
-#include "../perf.h"
#include "trace-event.h"
#include <api/fs/tracing_path.h>
#include "evsel.h"
@@ -420,11 +404,11 @@ static struct tracepoint_path *
get_tracepoints_path(struct list_head *pattrs)
{
struct tracepoint_path path, *ppath = &path;
- struct perf_evsel *pos;
+ struct evsel *pos;
int nr_tracepoints = 0;
- list_for_each_entry(pos, pattrs, node) {
- if (pos->attr.type != PERF_TYPE_TRACEPOINT)
+ list_for_each_entry(pos, pattrs, core.node) {
+ if (pos->core.attr.type != PERF_TYPE_TRACEPOINT)
continue;
++nr_tracepoints;
@@ -440,7 +424,7 @@ get_tracepoints_path(struct list_head *pattrs)
}
try_id:
- ppath->next = tracepoint_id_to_path(pos->attr.config);
+ ppath->next = tracepoint_id_to_path(pos->core.attr.config);
if (!ppath->next) {
error:
pr_debug("No memory to alloc tracepoints list\n");
@@ -456,10 +440,10 @@ next:
bool have_tracepoints(struct list_head *pattrs)
{
- struct perf_evsel *pos;
+ struct evsel *pos;
- list_for_each_entry(pos, pattrs, node)
- if (pos->attr.type == PERF_TYPE_TRACEPOINT)
+ list_for_each_entry(pos, pattrs, core.node)
+ if (pos->core.attr.type == PERF_TYPE_TRACEPOINT)
return true;
return false;
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c
index 863955e4094e..5d6bfc70b210 100644
--- a/tools/perf/util/trace-event-parse.c
+++ b/tools/perf/util/trace-event-parse.c
@@ -1,33 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License (not later!)
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
-#include "../perf.h"
#include "debug.h"
#include "trace-event.h"
-#include "sane_ctype.h"
+#include <linux/ctype.h>
static int get_common_field(struct scripting_context *context,
int *offset, int *size, const char *type)
@@ -126,7 +109,7 @@ void event_format__fprintf(struct tep_event *event,
record.data = data;
trace_seq_init(&s);
- tep_event_info(&s, event, &record);
+ tep_print_event(event->tep, &s, &record, "%s", TEP_PRINT_INFO);
trace_seq_do_fprintf(&s, fp);
trace_seq_destroy(&s);
}
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c
index 48d53d8e3e16..b6c0db068be0 100644
--- a/tools/perf/util/trace-event-read.c
+++ b/tools/perf/util/trace-event-read.c
@@ -1,22 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License (not later!)
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <dirent.h>
#include <stdio.h>
@@ -31,7 +15,6 @@
#include <unistd.h>
#include <errno.h>
-#include "../perf.h"
#include "util.h"
#include "trace-event.h"
#include "debug.h"
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c
index b749f812ac70..714581b0de65 100644
--- a/tools/perf/util/trace-event-scripting.c
+++ b/tools/perf/util/trace-event-scripting.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* trace-event-scripting. Scripting engine common and initialization code.
*
* Copyright (C) 2009-2010 Tom Zanussi <tzanussi@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <stdio.h>
@@ -24,10 +10,9 @@
#include <string.h>
#include <errno.h>
-#include "../perf.h"
#include "debug.h"
-#include "util.h"
#include "trace-event.h"
+#include <linux/zalloc.h>
struct scripting_context *scripting_context;
@@ -43,7 +28,7 @@ static int stop_script_unsupported(void)
static void process_event_unsupported(union perf_event *event __maybe_unused,
struct perf_sample *sample __maybe_unused,
- struct perf_evsel *evsel __maybe_unused,
+ struct evsel *evsel __maybe_unused,
struct addr_location *al __maybe_unused)
{
}
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index d9b0a942090a..2e158387b3d7 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -3,7 +3,6 @@
#define _PERF_UTIL_TRACE_EVENT_H
#include <traceevent/event-parse.h>
-#include <traceevent/trace-seq.h>
#include "parse-events.h"
struct machine;
@@ -79,10 +78,13 @@ struct scripting_ops {
int (*stop_script) (void);
void (*process_event) (union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel,
+ struct evsel *evsel,
struct addr_location *al);
+ void (*process_switch)(union perf_event *event,
+ struct perf_sample *sample,
+ struct machine *machine);
void (*process_stat)(struct perf_stat_config *config,
- struct perf_evsel *evsel, u64 tstamp);
+ struct evsel *evsel, u64 tstamp);
void (*process_stat_interval)(u64 tstamp);
int (*generate_script) (struct tep_handle *pevent, const char *outfile);
};
diff --git a/tools/perf/util/trigger.h b/tools/perf/util/trigger.h
index 88223bc7c82b..33e997f9ccc8 100644
--- a/tools/perf/util/trigger.h
+++ b/tools/perf/util/trigger.h
@@ -2,7 +2,6 @@
#ifndef __TRIGGER_H_
#define __TRIGGER_H_ 1
-#include "util/debug.h"
#include "asm/bug.h"
/*
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 407d0167b942..9ece188ae48a 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -5,6 +5,7 @@
#include <inttypes.h>
#include <errno.h>
#include "debug.h"
+#include "dso.h"
#include "unwind.h"
#include "unwind-libdw.h"
#include "machine.h"
@@ -12,6 +13,7 @@
#include "symbol.h"
#include "thread.h"
#include <linux/types.h>
+#include <linux/zalloc.h>
#include "event.h"
#include "perf_regs.h"
#include "callchain.h"
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
index 25e1406b1f8b..ebdbb056510c 100644
--- a/tools/perf/util/unwind-libunwind-local.c
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -25,6 +25,7 @@
#include <unistd.h>
#include <sys/mman.h>
#include <linux/list.h>
+#include <linux/zalloc.h>
#ifndef REMOTE_UNWIND_LIBUNWIND
#include <libunwind.h>
#include <libunwind-ptrace.h>
@@ -345,7 +346,7 @@ static int read_unwind_spec_debug_frame(struct dso *dso,
__func__,
dso->symsrc_filename,
debuglink);
- free(dso->symsrc_filename);
+ zfree(&dso->symsrc_filename);
}
dso->symsrc_filename = debuglink;
} else {
@@ -615,26 +616,26 @@ static unw_accessors_t accessors = {
.get_proc_name = get_proc_name,
};
-static int _unwind__prepare_access(struct thread *thread)
+static int _unwind__prepare_access(struct map_groups *mg)
{
- thread->addr_space = unw_create_addr_space(&accessors, 0);
- if (!thread->addr_space) {
+ mg->addr_space = unw_create_addr_space(&accessors, 0);
+ if (!mg->addr_space) {
pr_err("unwind: Can't create unwind address space.\n");
return -ENOMEM;
}
- unw_set_caching_policy(thread->addr_space, UNW_CACHE_GLOBAL);
+ unw_set_caching_policy(mg->addr_space, UNW_CACHE_GLOBAL);
return 0;
}
-static void _unwind__flush_access(struct thread *thread)
+static void _unwind__flush_access(struct map_groups *mg)
{
- unw_flush_cache(thread->addr_space, 0, 0);
+ unw_flush_cache(mg->addr_space, 0, 0);
}
-static void _unwind__finish_access(struct thread *thread)
+static void _unwind__finish_access(struct map_groups *mg)
{
- unw_destroy_addr_space(thread->addr_space);
+ unw_destroy_addr_space(mg->addr_space);
}
static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
@@ -659,7 +660,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
*/
if (max_stack - 1 > 0) {
WARN_ONCE(!ui->thread, "WARNING: ui->thread is NULL");
- addr_space = ui->thread->addr_space;
+ addr_space = ui->thread->mg->addr_space;
if (addr_space == NULL)
return -1;
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index c0811977d7d5..a24fb57c9b2c 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include "unwind.h"
+#include "dso.h"
#include "map.h"
#include "thread.h"
#include "session.h"
@@ -11,13 +12,13 @@ struct unwind_libunwind_ops __weak *local_unwind_libunwind_ops;
struct unwind_libunwind_ops __weak *x86_32_unwind_libunwind_ops;
struct unwind_libunwind_ops __weak *arm64_unwind_libunwind_ops;
-static void unwind__register_ops(struct thread *thread,
+static void unwind__register_ops(struct map_groups *mg,
struct unwind_libunwind_ops *ops)
{
- thread->unwind_libunwind_ops = ops;
+ mg->unwind_libunwind_ops = ops;
}
-int unwind__prepare_access(struct thread *thread, struct map *map,
+int unwind__prepare_access(struct map_groups *mg, struct map *map,
bool *initialized)
{
const char *arch;
@@ -28,7 +29,7 @@ int unwind__prepare_access(struct thread *thread, struct map *map,
if (!dwarf_callchain_users)
return 0;
- if (thread->addr_space) {
+ if (mg->addr_space) {
pr_debug("unwind: thread map already set, dso=%s\n",
map->dso->name);
if (initialized)
@@ -37,14 +38,14 @@ int unwind__prepare_access(struct thread *thread, struct map *map,
}
/* env->arch is NULL for live-mode (i.e. perf top) */
- if (!thread->mg->machine->env || !thread->mg->machine->env->arch)
+ if (!mg->machine->env || !mg->machine->env->arch)
goto out_register;
- dso_type = dso__type(map->dso, thread->mg->machine);
+ dso_type = dso__type(map->dso, mg->machine);
if (dso_type == DSO__TYPE_UNKNOWN)
return 0;
- arch = perf_env__arch(thread->mg->machine->env);
+ arch = perf_env__arch(mg->machine->env);
if (!strcmp(arch, "x86")) {
if (dso_type != DSO__TYPE_64BIT)
@@ -59,37 +60,31 @@ int unwind__prepare_access(struct thread *thread, struct map *map,
return 0;
}
out_register:
- unwind__register_ops(thread, ops);
+ unwind__register_ops(mg, ops);
- err = thread->unwind_libunwind_ops->prepare_access(thread);
+ err = mg->unwind_libunwind_ops->prepare_access(mg);
if (initialized)
*initialized = err ? false : true;
return err;
}
-void unwind__flush_access(struct thread *thread)
+void unwind__flush_access(struct map_groups *mg)
{
- if (!dwarf_callchain_users)
- return;
-
- if (thread->unwind_libunwind_ops)
- thread->unwind_libunwind_ops->flush_access(thread);
+ if (mg->unwind_libunwind_ops)
+ mg->unwind_libunwind_ops->flush_access(mg);
}
-void unwind__finish_access(struct thread *thread)
+void unwind__finish_access(struct map_groups *mg)
{
- if (!dwarf_callchain_users)
- return;
-
- if (thread->unwind_libunwind_ops)
- thread->unwind_libunwind_ops->finish_access(thread);
+ if (mg->unwind_libunwind_ops)
+ mg->unwind_libunwind_ops->finish_access(mg);
}
int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct thread *thread,
struct perf_sample *data, int max_stack)
{
- if (thread->unwind_libunwind_ops)
- return thread->unwind_libunwind_ops->get_entries(cb, arg, thread, data, max_stack);
+ if (thread->mg->unwind_libunwind_ops)
+ return thread->mg->unwind_libunwind_ops->get_entries(cb, arg, thread, data, max_stack);
return 0;
}
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index 8a44a1569a21..3a7d00c20d86 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -6,6 +6,7 @@
#include <linux/types.h>
struct map;
+struct map_groups;
struct perf_sample;
struct symbol;
struct thread;
@@ -19,9 +20,9 @@ struct unwind_entry {
typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg);
struct unwind_libunwind_ops {
- int (*prepare_access)(struct thread *thread);
- void (*flush_access)(struct thread *thread);
- void (*finish_access)(struct thread *thread);
+ int (*prepare_access)(struct map_groups *mg);
+ void (*flush_access)(struct map_groups *mg);
+ void (*finish_access)(struct map_groups *mg);
int (*get_entries)(unwind_entry_cb_t cb, void *arg,
struct thread *thread,
struct perf_sample *data, int max_stack);
@@ -46,20 +47,20 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
#endif
int LIBUNWIND__ARCH_REG_ID(int regnum);
-int unwind__prepare_access(struct thread *thread, struct map *map,
+int unwind__prepare_access(struct map_groups *mg, struct map *map,
bool *initialized);
-void unwind__flush_access(struct thread *thread);
-void unwind__finish_access(struct thread *thread);
+void unwind__flush_access(struct map_groups *mg);
+void unwind__finish_access(struct map_groups *mg);
#else
-static inline int unwind__prepare_access(struct thread *thread __maybe_unused,
+static inline int unwind__prepare_access(struct map_groups *mg __maybe_unused,
struct map *map __maybe_unused,
bool *initialized __maybe_unused)
{
return 0;
}
-static inline void unwind__flush_access(struct thread *thread __maybe_unused) {}
-static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
+static inline void unwind__flush_access(struct map_groups *mg __maybe_unused) {}
+static inline void unwind__finish_access(struct map_groups *mg __maybe_unused) {}
#endif
#else
static inline int
@@ -72,14 +73,14 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
return 0;
}
-static inline int unwind__prepare_access(struct thread *thread __maybe_unused,
+static inline int unwind__prepare_access(struct map_groups *mg __maybe_unused,
struct map *map __maybe_unused,
bool *initialized __maybe_unused)
{
return 0;
}
-static inline void unwind__flush_access(struct thread *thread __maybe_unused) {}
-static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
+static inline void unwind__flush_access(struct map_groups *mg __maybe_unused) {}
+static inline void unwind__finish_access(struct map_groups *mg __maybe_unused) {}
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
#endif /* __UNWIND_H */
diff --git a/tools/perf/util/usage.c b/tools/perf/util/usage.c
index 070d25ceea6a..3949a60b00ae 100644
--- a/tools/perf/util/usage.c
+++ b/tools/perf/util/usage.c
@@ -9,6 +9,9 @@
*/
#include "util.h"
#include "debug.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <linux/compiler.h>
static __noreturn void usage_builtin(const char *err)
{
diff --git a/tools/perf/util/util-cxx.h b/tools/perf/util/util-cxx.h
deleted file mode 100644
index 80a99e458d4e..000000000000
--- a/tools/perf/util/util-cxx.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Support C++ source use utilities defined in util.h
- */
-
-#ifndef PERF_UTIL_UTIL_CXX_H
-#define PERF_UTIL_UTIL_CXX_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Now 'new' is the only C++ keyword found in util.h:
- * in tools/include/linux/rbtree.h
- *
- * Other keywords, like class and delete, should be
- * redefined if necessary.
- */
-#define new _new
-#include "util.h"
-#undef new
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index d388f80d8703..32322a20a68b 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
-#include "../perf.h"
#include "util.h"
#include "debug.h"
+#include "event.h"
#include "namespaces.h"
#include <api/fs/fs.h>
#include <sys/mman.h>
@@ -16,10 +16,12 @@
#include <string.h>
#include <errno.h>
#include <limits.h>
+#include <linux/capability.h>
#include <linux/kernel.h>
#include <linux/log2.h>
#include <linux/time64.h>
#include <unistd.h>
+#include "cap.h"
#include "strlist.h"
#include "string2.h"
@@ -41,26 +43,6 @@ void perf_set_multithreaded(void)
unsigned int page_size;
-#ifdef _SC_LEVEL1_DCACHE_LINESIZE
-#define cache_line_size(cacheline_sizep) *cacheline_sizep = sysconf(_SC_LEVEL1_DCACHE_LINESIZE)
-#else
-static void cache_line_size(int *cacheline_sizep)
-{
- if (sysfs__read_int("devices/system/cpu/cpu0/cache/index0/coherency_line_size", cacheline_sizep))
- pr_debug("cannot determine cache line size");
-}
-#endif
-
-int cacheline_size(void)
-{
- static int size;
-
- if (!size)
- cache_line_size(&size);
-
- return size;
-}
-
int sysctl_perf_event_max_stack = PERF_MAX_STACK_DEPTH;
int sysctl_perf_event_max_contexts_per_stack = PERF_MAX_CONTEXTS_PER_STACK;
@@ -384,46 +366,6 @@ int copyfile(const char *from, const char *to)
return copyfile_mode(from, to, 0755);
}
-static ssize_t ion(bool is_read, int fd, void *buf, size_t n)
-{
- void *buf_start = buf;
- size_t left = n;
-
- while (left) {
- /* buf must be treated as const if !is_read. */
- ssize_t ret = is_read ? read(fd, buf, left) :
- write(fd, buf, left);
-
- if (ret < 0 && errno == EINTR)
- continue;
- if (ret <= 0)
- return ret;
-
- left -= ret;
- buf += ret;
- }
-
- BUG_ON((size_t)(buf - buf_start) != n);
- return n;
-}
-
-/*
- * Read exactly 'n' bytes or return an error.
- */
-ssize_t readn(int fd, void *buf, size_t n)
-{
- return ion(true, fd, buf, n);
-}
-
-/*
- * Write exactly 'n' bytes or return an error.
- */
-ssize_t writen(int fd, const void *buf, size_t n)
-{
- /* ion does not modify buf. */
- return ion(false, fd, (void *)buf, n);
-}
-
size_t hex_width(u64 v)
{
size_t n = 1;
@@ -434,19 +376,6 @@ size_t hex_width(u64 v)
return n;
}
-/*
- * While we find nice hex chars, build a long_val.
- * Return number of chars processed.
- */
-int hex2u64(const char *ptr, u64 *long_val)
-{
- char *p;
-
- *long_val = strtoull(ptr, &p, 16);
-
- return p - ptr;
-}
-
int perf_event_paranoid(void)
{
int value;
@@ -456,6 +385,13 @@ int perf_event_paranoid(void)
return value;
}
+
+bool perf_event_paranoid_check(int max_level)
+{
+ return perf_cap__capable(CAP_SYS_ADMIN) ||
+ perf_event_paranoid() <= max_level;
+}
+
static int
fetch_ubuntu_kernel_version(unsigned int *puint)
{
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 09c1b0f91f65..45a5c6f20197 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -9,22 +9,14 @@
#include <fcntl.h>
#include <stdbool.h>
#include <stddef.h>
-#include <stdlib.h>
-#include <stdarg.h>
#include <linux/compiler.h>
#include <sys/types.h>
+#include <internal/lib.h>
/* General helper functions */
void usage(const char *err) __noreturn;
void die(const char *err, ...) __noreturn __printf(1, 2);
-static inline void *zalloc(size_t size)
-{
- return calloc(1, size);
-}
-
-#define zfree(ptr) ({ free(*ptr); *ptr = NULL; })
-
struct dirent;
struct nsinfo;
struct strlist;
@@ -39,14 +31,9 @@ int copyfile_mode(const char *from, const char *to, mode_t mode);
int copyfile_ns(const char *from, const char *to, struct nsinfo *nsi);
int copyfile_offset(int ifd, loff_t off_in, int ofd, loff_t off_out, u64 size);
-ssize_t readn(int fd, void *buf, size_t n);
-ssize_t writen(int fd, const void *buf, size_t n);
-
size_t hex_width(u64 v);
-int hex2u64(const char *ptr, u64 *val);
extern unsigned int page_size;
-int __pure cacheline_size(void);
int sysctl__max_stack(void);
@@ -60,18 +47,10 @@ int fetch_kernel_version(unsigned int *puint,
const char *perf_tip(const char *dirpath);
-#ifndef HAVE_GET_CURRENT_DIR_NAME
-char *get_current_dir_name(void);
-#endif
-
#ifndef HAVE_SCHED_GETCPU_SUPPORT
int sched_getcpu(void);
#endif
-#ifndef HAVE_SETNS_SUPPORT
-int setns(int fd, int nstype);
-#endif
-
extern bool perf_singlethreaded;
void perf_set_singlethreaded(void);
diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c
index 4b7a303e4ba8..b9823f414f10 100644
--- a/tools/perf/util/values.c
+++ b/tools/perf/util/values.c
@@ -2,9 +2,10 @@
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
+#include <linux/zalloc.h>
-#include "util.h"
#include "values.h"
#include "debug.h"
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index 5031b7b22bbd..e5e6599603f4 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -10,12 +10,14 @@
#include <linux/kernel.h>
#include "vdso.h"
+#include "dso.h"
#include "util.h"
#include "map.h"
#include "symbol.h"
#include "machine.h"
#include "thread.h"
#include "linux/string.h"
+#include <linux/zalloc.h>
#include "debug.h"
/*
diff --git a/tools/perf/util/xyarray.c b/tools/perf/util/xyarray.c
index dc95154f5646..86889ebc3514 100644
--- a/tools/perf/util/xyarray.c
+++ b/tools/perf/util/xyarray.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
#include "xyarray.h"
-#include "util.h"
#include <stdlib.h>
#include <string.h>
+#include <linux/zalloc.h>
struct xyarray *xyarray__new(int xlen, int ylen, size_t entry_size)
{
diff --git a/tools/perf/util/zlib.c b/tools/perf/util/zlib.c
index 512ad7c09b13..59d456f716e9 100644
--- a/tools/perf/util/zlib.c
+++ b/tools/perf/util/zlib.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <fcntl.h>
#include <stdio.h>
+#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
diff --git a/tools/perf/util/zstd.c b/tools/perf/util/zstd.c
index 23bdb9884576..d2202392ffdb 100644
--- a/tools/perf/util/zstd.c
+++ b/tools/perf/util/zstd.c
@@ -99,8 +99,8 @@ size_t zstd_decompress_stream(struct zstd_data *data, void *src, size_t src_size
while (input.pos < input.size) {
ret = ZSTD_decompressStream(data->dstream, &output, &input);
if (ZSTD_isError(ret)) {
- pr_err("failed to decompress (B): %ld -> %ld : %s\n",
- src_size, output.size, ZSTD_getErrorName(ret));
+ pr_err("failed to decompress (B): %ld -> %ld, dst_size %ld : %s\n",
+ src_size, output.size, dst_size, ZSTD_getErrorName(ret));
break;
}
output.dst = dst + output.pos;
diff --git a/tools/power/acpi/.gitignore b/tools/power/acpi/.gitignore
index cba3d994995c..f698a0e5bfa6 100644
--- a/tools/power/acpi/.gitignore
+++ b/tools/power/acpi/.gitignore
@@ -1,4 +1,4 @@
-acpidbg
-acpidump
-ec
-include
+/acpidbg
+/acpidump
+/ec
+/include/
diff --git a/tools/power/acpi/Makefile b/tools/power/acpi/Makefile
index a8bf9081512b..ebd3e1a1c28e 100644
--- a/tools/power/acpi/Makefile
+++ b/tools/power/acpi/Makefile
@@ -1,12 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
# tools/power/acpi/Makefile - ACPI tool Makefile
#
# Copyright (c) 2013, Intel Corporation
# Author: Lv Zheng <lv.zheng@intel.com>
#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; version 2
-# of the License.
include ../../scripts/Makefile.include
diff --git a/tools/power/acpi/Makefile.config b/tools/power/acpi/Makefile.config
index f304be71c278..0111d246d1ca 100644
--- a/tools/power/acpi/Makefile.config
+++ b/tools/power/acpi/Makefile.config
@@ -1,12 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
# tools/power/acpi/Makefile.config - ACPI tool Makefile
#
# Copyright (c) 2015, Intel Corporation
# Author: Lv Zheng <lv.zheng@intel.com>
#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; version 2
-# of the License.
ifeq ($(srctree),)
srctree := $(patsubst %/,%,$(dir $(shell pwd)))
diff --git a/tools/power/acpi/Makefile.rules b/tools/power/acpi/Makefile.rules
index 373738338f51..2a6c170b57cd 100644
--- a/tools/power/acpi/Makefile.rules
+++ b/tools/power/acpi/Makefile.rules
@@ -1,12 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
# tools/power/acpi/Makefile.rules - ACPI tool Makefile
#
# Copyright (c) 2015, Intel Corporation
# Author: Lv Zheng <lv.zheng@intel.com>
#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; version 2
-# of the License.
objdir := $(OUTPUT)tools/$(TOOL)/
toolobjs := $(addprefix $(objdir),$(TOOL_OBJS))
diff --git a/tools/power/acpi/tools/acpidbg/Makefile b/tools/power/acpi/tools/acpidbg/Makefile
index f2d06e773eb4..2ce0ee5d0deb 100644
--- a/tools/power/acpi/tools/acpidbg/Makefile
+++ b/tools/power/acpi/tools/acpidbg/Makefile
@@ -1,12 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
# tools/power/acpi/tools/acpidbg/Makefile - ACPI tool Makefile
#
# Copyright (c) 2015, Intel Corporation
# Author: Lv Zheng <lv.zheng@intel.com>
#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; version 2
-# of the License.
include ../../Makefile.config
diff --git a/tools/power/acpi/tools/acpidbg/acpidbg.c b/tools/power/acpi/tools/acpidbg/acpidbg.c
index 4308362d7068..3d2bfd716028 100644
--- a/tools/power/acpi/tools/acpidbg/acpidbg.c
+++ b/tools/power/acpi/tools/acpidbg/acpidbg.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ACPI AML interfacing userspace utility
*
* Copyright (C) 2015, Intel Corporation
* Authors: Lv Zheng <lv.zheng@intel.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <acpi/acpi.h>
diff --git a/tools/power/acpi/tools/acpidump/Makefile b/tools/power/acpi/tools/acpidump/Makefile
index b436f8675f6a..1208a105a871 100644
--- a/tools/power/acpi/tools/acpidump/Makefile
+++ b/tools/power/acpi/tools/acpidump/Makefile
@@ -1,12 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
# tools/power/acpi/tools/acpidump/Makefile - ACPI tool Makefile
#
# Copyright (c) 2015, Intel Corporation
# Author: Lv Zheng <lv.zheng@intel.com>
#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; version 2
-# of the License.
include ../../Makefile.config
diff --git a/tools/power/acpi/tools/acpidump/apfiles.c b/tools/power/acpi/tools/acpidump/apfiles.c
index a42cfcaa3293..16d919bd133b 100644
--- a/tools/power/acpi/tools/acpidump/apfiles.c
+++ b/tools/power/acpi/tools/acpidump/apfiles.c
@@ -29,18 +29,24 @@ static int ap_is_existing_file(char *pathname)
{
#if !defined(_GNU_EFI) && !defined(_EDK2_EFI)
struct stat stat_info;
+ int in_char;
if (!stat(pathname, &stat_info)) {
fprintf(stderr,
"Target path already exists, overwrite? [y|n] ");
- if (getchar() != 'y') {
+ in_char = fgetc(stdin);
+ if (in_char == '\n') {
+ in_char = fgetc(stdin);
+ }
+
+ if (in_char != 'y' && in_char != 'Y') {
return (-1);
}
}
#endif
- return 0;
+ return (0);
}
/******************************************************************************
diff --git a/tools/power/acpi/tools/ec/Makefile b/tools/power/acpi/tools/ec/Makefile
index 75d8a127b6ee..d0abac0ec23a 100644
--- a/tools/power/acpi/tools/ec/Makefile
+++ b/tools/power/acpi/tools/ec/Makefile
@@ -1,12 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
# tools/power/acpi/tools/acpidump/Makefile - ACPI tool Makefile
#
# Copyright (c) 2015, Intel Corporation
# Author: Lv Zheng <lv.zheng@intel.com>
#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; version 2
-# of the License.
include ../../Makefile.config
diff --git a/tools/power/acpi/tools/ec/ec_access.c b/tools/power/acpi/tools/ec/ec_access.c
index 5f50642386db..8bb271b210d8 100644
--- a/tools/power/acpi/tools/ec/ec_access.c
+++ b/tools/power/acpi/tools/ec/ec_access.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ec_access.c
*
* Copyright (C) 2010 SUSE Linux Products GmbH
* Author:
* Thomas Renninger <trenn@suse.de>
- *
- * This work is licensed under the terms of the GNU GPL, version 2.
*/
#include <fcntl.h>
diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile
index fd8765af19bb..c8622497ef23 100644
--- a/tools/power/cpupower/Makefile
+++ b/tools/power/cpupower/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# Makefile for cpupower
#
# Copyright (C) 2005,2006 Dominik Brodowski <linux@dominikbrodowski.net>
@@ -6,19 +7,6 @@
#
# Copyright (C) 2003,2004 Greg Kroah-Hartman <greg@kroah.com>
#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; version 2 of the License.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
OUTPUT=./
ifeq ("$(origin O)", "command line")
OUTPUT := $(O)/
@@ -30,7 +18,6 @@ OUTDIR := $(shell cd $(OUTPUT) && pwd)
$(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist))
endif
-include ../../scripts/Makefile.arch
# --- CONFIGURATION BEGIN ---
@@ -81,11 +68,6 @@ bindir ?= /usr/bin
sbindir ?= /usr/sbin
mandir ?= /usr/man
includedir ?= /usr/include
-ifeq ($(IS_64_BIT), 1)
-libdir ?= /usr/lib64
-else
-libdir ?= /usr/lib
-endif
localedir ?= /usr/share/locale
docdir ?= /usr/share/doc/packages/cpupower
confdir ?= /etc/
@@ -112,6 +94,14 @@ RANLIB = $(CROSS)ranlib
HOSTCC = gcc
MKDIR = mkdir
+# 64bit library detection
+include ../../scripts/Makefile.arch
+
+ifeq ($(IS_64_BIT), 1)
+libdir ?= /usr/lib64
+else
+libdir ?= /usr/lib
+endif
# Now we set up the build system
#
diff --git a/tools/power/cpupower/bench/benchmark.c b/tools/power/cpupower/bench/benchmark.c
index 429d51ab8031..c7234cf3f6ff 100644
--- a/tools/power/cpupower/bench/benchmark.c
+++ b/tools/power/cpupower/bench/benchmark.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* cpufreq-bench CPUFreq microbenchmark
*
* Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
diff --git a/tools/power/cpupower/bench/benchmark.h b/tools/power/cpupower/bench/benchmark.h
index 51d7f50ac2bb..bf612de897eb 100644
--- a/tools/power/cpupower/bench/benchmark.h
+++ b/tools/power/cpupower/bench/benchmark.h
@@ -1,20 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* cpufreq-bench CPUFreq microbenchmark
*
* Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* load loop, this schould take about 1 to 2ms to complete */
diff --git a/tools/power/cpupower/bench/config.h b/tools/power/cpupower/bench/config.h
index ee6f258e5336..fec5b0b055f2 100644
--- a/tools/power/cpupower/bench/config.h
+++ b/tools/power/cpupower/bench/config.h
@@ -1,20 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* cpufreq-bench CPUFreq microbenchmark
*
* Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* initial loop count for the load calibration */
diff --git a/tools/power/cpupower/bench/cpufreq-bench_plot.sh b/tools/power/cpupower/bench/cpufreq-bench_plot.sh
index 410021a12f40..f5f8b3c8f062 100644
--- a/tools/power/cpupower/bench/cpufreq-bench_plot.sh
+++ b/tools/power/cpupower/bench/cpufreq-bench_plot.sh
@@ -1,19 +1,6 @@
#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2, or (at your option)
-# any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, USA.
# Author/Copyright(c): 2009, Thomas Renninger <trenn@suse.de>, Novell Inc.
@@ -101,4 +88,4 @@ done
echo >> $dir/plot_script.gpl
gnuplot $dir/plot_script.gpl
-rm -r $dir \ No newline at end of file
+rm -r $dir
diff --git a/tools/power/cpupower/bench/cpufreq-bench_script.sh b/tools/power/cpupower/bench/cpufreq-bench_script.sh
index de20d2a06879..785a3679c704 100644
--- a/tools/power/cpupower/bench/cpufreq-bench_script.sh
+++ b/tools/power/cpupower/bench/cpufreq-bench_script.sh
@@ -1,19 +1,6 @@
#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2, or (at your option)
-# any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, USA.
# Author/Copyright(c): 2009, Thomas Renninger <trenn@suse.de>, Novell Inc.
@@ -98,4 +85,4 @@ function create_plots()
}
measure
-create_plots \ No newline at end of file
+create_plots
diff --git a/tools/power/cpupower/bench/main.c b/tools/power/cpupower/bench/main.c
index 24910313a521..429d1b6b8bc8 100644
--- a/tools/power/cpupower/bench/main.c
+++ b/tools/power/cpupower/bench/main.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* cpufreq-bench CPUFreq microbenchmark
*
* Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
diff --git a/tools/power/cpupower/bench/parse.c b/tools/power/cpupower/bench/parse.c
index 84caee38418f..e63dc11fa3a5 100644
--- a/tools/power/cpupower/bench/parse.c
+++ b/tools/power/cpupower/bench/parse.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* cpufreq-bench CPUFreq microbenchmark
*
* Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
diff --git a/tools/power/cpupower/bench/parse.h b/tools/power/cpupower/bench/parse.h
index a8dc632d9eee..d5b3e34d7064 100644
--- a/tools/power/cpupower/bench/parse.h
+++ b/tools/power/cpupower/bench/parse.h
@@ -1,20 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* cpufreq-bench CPUFreq microbenchmark
*
* Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* struct that holds the required config parameters */
diff --git a/tools/power/cpupower/bench/system.c b/tools/power/cpupower/bench/system.c
index 2bb3eef7d5c1..40f3679e70b5 100644
--- a/tools/power/cpupower/bench/system.c
+++ b/tools/power/cpupower/bench/system.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* cpufreq-bench CPUFreq microbenchmark
*
* Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
diff --git a/tools/power/cpupower/bench/system.h b/tools/power/cpupower/bench/system.h
index 3a8c858b78f0..530fa28230d1 100644
--- a/tools/power/cpupower/bench/system.h
+++ b/tools/power/cpupower/bench/system.h
@@ -1,20 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* cpufreq-bench CPUFreq microbenchmark
*
* Copyright (C) 2008 Christian Kornacker <ckornacker@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "parse.h"
diff --git a/tools/power/cpupower/debug/i386/centrino-decode.c b/tools/power/cpupower/debug/i386/centrino-decode.c
index 7ef24cce4926..700cd31a7d02 100644
--- a/tools/power/cpupower/debug/i386/centrino-decode.c
+++ b/tools/power/cpupower/debug/i386/centrino-decode.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2003 - 2004 Dominik Brodowski <linux@dominikbrodowski.de>
*
- * Licensed under the terms of the GNU GPL License version 2.
- *
* Based on code found in
* linux/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c
* and originally developed by Jeremy Fitzhardinge.
diff --git a/tools/power/cpupower/debug/i386/intel_gsic.c b/tools/power/cpupower/debug/i386/intel_gsic.c
index d032c826d42e..e5e926f46d6b 100644
--- a/tools/power/cpupower/debug/i386/intel_gsic.c
+++ b/tools/power/cpupower/debug/i386/intel_gsic.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2003 Bruno Ducrot
* (C) 2004 Dominik Brodowski <linux@dominikbrodowski.de>
*
- * Licensed under the terms of the GNU GPL License version 2.
- *
* Based on code found in
* linux/include/asm-i386/ist.h and linux/arch/i386/kernel/setup.c
* and originally developed by Andy Grover <andrew.grover@intel.com>
diff --git a/tools/power/cpupower/debug/i386/powernow-k8-decode.c b/tools/power/cpupower/debug/i386/powernow-k8-decode.c
index 638a6b3bfd97..735dca1e25bc 100644
--- a/tools/power/cpupower/debug/i386/powernow-k8-decode.c
+++ b/tools/power/cpupower/debug/i386/powernow-k8-decode.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2004 Bruno Ducrot <ducrot@poupinou.org>
*
- * Licensed under the terms of the GNU GPL License version 2.
- *
* Based on code found in
* linux/arch/i386/kernel/cpu/cpufreq/powernow-k8.c
* and originally developed by Paul Devriendt
diff --git a/tools/power/cpupower/debug/kernel/Makefile b/tools/power/cpupower/debug/kernel/Makefile
index c23e5a6ceb7e..7b5c43684be1 100644
--- a/tools/power/cpupower/debug/kernel/Makefile
+++ b/tools/power/cpupower/debug/kernel/Makefile
@@ -12,8 +12,8 @@ default:
$(MAKE) -C $(KDIR) M=$(CURDIR)
clean:
- - rm -rf *.o *.ko .tmp-versions .*.cmd .*.mod.* *.mod.c
- - rm -rf .tmp_versions* Module.symvers modules.order
+ - rm -rf *.o *.ko .*.cmd .*.mod.* *.mod.c
+ - rm -rf Module.symvers modules.order
install: default
install -d $(KMISC)
diff --git a/tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c b/tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c
index 6ff8383f2941..e364b170bf85 100644
--- a/tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c
+++ b/tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* test module to check whether the TSC-based delay routine continues
* to work properly after cpufreq transitions. Needs ACPI to work
diff --git a/tools/power/cpupower/lib/cpufreq.c b/tools/power/cpupower/lib/cpufreq.c
index 80650497fb80..2f55d4d23446 100644
--- a/tools/power/cpupower/lib/cpufreq.c
+++ b/tools/power/cpupower/lib/cpufreq.c
@@ -1,7 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
- *
- * Licensed under the terms of the GNU GPL License version 2.
*/
diff --git a/tools/power/cpupower/lib/cpufreq.h b/tools/power/cpupower/lib/cpufreq.h
index 775738269cbf..a55f0d19215b 100644
--- a/tools/power/cpupower/lib/cpufreq.h
+++ b/tools/power/cpupower/lib/cpufreq.h
@@ -1,16 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cpufreq.h - definitions for libcpufreq
*
* Copyright (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#ifndef __CPUPOWER_CPUFREQ_H__
diff --git a/tools/power/cpupower/lib/cpuidle.c b/tools/power/cpupower/lib/cpuidle.c
index 852d25462388..479c5971aa6d 100644
--- a/tools/power/cpupower/lib/cpuidle.c
+++ b/tools/power/cpupower/lib/cpuidle.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
* (C) 2011 Thomas Renninger <trenn@novell.com> Novell Inc.
- *
- * Licensed under the terms of the GNU GPL License version 2.
*/
#include <stdio.h>
diff --git a/tools/power/cpupower/lib/cpupower.c b/tools/power/cpupower/lib/cpupower.c
index 9711d628b0f4..3656e697537e 100644
--- a/tools/power/cpupower/lib/cpupower.c
+++ b/tools/power/cpupower/lib/cpupower.c
@@ -1,7 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
- *
- * Licensed under the terms of the GNU GPL License version 2.
*/
#include <sys/types.h>
diff --git a/tools/power/cpupower/man/cpupower-monitor.1 b/tools/power/cpupower/man/cpupower-monitor.1
index 914cbb9d9cd0..70a56476f4b0 100644
--- a/tools/power/cpupower/man/cpupower-monitor.1
+++ b/tools/power/cpupower/man/cpupower-monitor.1
@@ -61,7 +61,7 @@ Only display specific monitors. Use the monitor string(s) provided by \-l option
.PP
\-i seconds
.RS 4
-Measure intervall.
+Measure interval.
.RE
.PP
\-c
diff --git a/tools/power/cpupower/po/cs.po b/tools/power/cpupower/po/cs.po
index cb22c45c5069..bfc7e1702ec9 100644
--- a/tools/power/cpupower/po/cs.po
+++ b/tools/power/cpupower/po/cs.po
@@ -98,7 +98,7 @@ msgstr ""
#: utils/idle_monitor/cpupower-monitor.c:74
#, c-format
-msgid "\t -i: time intervall to measure for in seconds (default 1)\n"
+msgid "\t -i: time interval to measure for in seconds (default 1)\n"
msgstr ""
#: utils/idle_monitor/cpupower-monitor.c:75
diff --git a/tools/power/cpupower/po/de.po b/tools/power/cpupower/po/de.po
index 840c17cc450a..9780a447bb55 100644
--- a/tools/power/cpupower/po/de.po
+++ b/tools/power/cpupower/po/de.po
@@ -8,66 +8,66 @@ msgstr ""
"Project-Id-Version: cpufrequtils 006\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-03-08 17:03+0100\n"
-"PO-Revision-Date: 2009-08-08 17:18+0100\n"
-"Last-Translator: <linux@dominikbrodowski.net>\n"
+"PO-Revision-Date: 2019-06-02 15:23+0200\n"
+"Last-Translator: Benjamin Weis <benjamin.weis@gmx.com>\n"
"Language-Team: NONE\n"
"Language: \n"
"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=ISO-8859-1\n"
+"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: utils/idle_monitor/nhm_idle.c:36
msgid "Processor Core C3"
-msgstr ""
+msgstr "Prozessorkern C3"
#: utils/idle_monitor/nhm_idle.c:43
msgid "Processor Core C6"
-msgstr ""
+msgstr "Prozessorkern C6"
#: utils/idle_monitor/nhm_idle.c:51
msgid "Processor Package C3"
-msgstr ""
+msgstr "Prozessorpaket C3"
#: utils/idle_monitor/nhm_idle.c:58 utils/idle_monitor/amd_fam14h_idle.c:70
msgid "Processor Package C6"
-msgstr ""
+msgstr "Prozessorpaket C6"
#: utils/idle_monitor/snb_idle.c:33
msgid "Processor Core C7"
-msgstr ""
+msgstr "Prozessorkern C7"
#: utils/idle_monitor/snb_idle.c:40
msgid "Processor Package C2"
-msgstr ""
+msgstr "Prozessorpaket C2"
#: utils/idle_monitor/snb_idle.c:47
msgid "Processor Package C7"
-msgstr ""
+msgstr "Prozessorpaket C7"
#: utils/idle_monitor/amd_fam14h_idle.c:56
msgid "Package in sleep state (PC1 or deeper)"
-msgstr ""
+msgstr "Paket in Schlafzustand (PC1 oder tiefer)"
#: utils/idle_monitor/amd_fam14h_idle.c:63
msgid "Processor Package C1"
-msgstr ""
+msgstr "Prozessorpaket C1"
#: utils/idle_monitor/amd_fam14h_idle.c:77
msgid "North Bridge P1 boolean counter (returns 0 or 1)"
-msgstr ""
+msgstr "North Bridge P1 boolescher Zähler (gibt 0 oder 1 zurück)"
#: utils/idle_monitor/mperf_monitor.c:35
msgid "Processor Core not idle"
-msgstr ""
+msgstr "Prozessorkern ist nicht im Leerlauf"
#: utils/idle_monitor/mperf_monitor.c:42
msgid "Processor Core in an idle state"
-msgstr ""
+msgstr "Prozessorkern ist in einem Ruhezustand"
#: utils/idle_monitor/mperf_monitor.c:50
msgid "Average Frequency (including boost) in MHz"
-msgstr ""
+msgstr "Durchschnittliche Frequenz (einschließlich Boost) in MHz"
#: utils/idle_monitor/cpupower-monitor.c:66
#, c-format
@@ -75,6 +75,8 @@ msgid ""
"cpupower monitor: [-h] [ [-t] | [-l] | [-m <mon1>,[<mon2>] ] ] [-i "
"interval_sec | -c command ...]\n"
msgstr ""
+"cpupower monitor: [-h] [ [-t] | [-l] | [-m <mon1>,[<mon2>] ] ] [-i "
+"interval_sec | -c Befehl ...]\n"
#: utils/idle_monitor/cpupower-monitor.c:69
#, c-format
@@ -82,36 +84,40 @@ msgid ""
"cpupower monitor: [-v] [-h] [ [-t] | [-l] | [-m <mon1>,[<mon2>] ] ] [-i "
"interval_sec | -c command ...]\n"
msgstr ""
+"cpupower monitor: [-v] [-h] [ [-t] | [-l] | [-m <mon1>,[<mon2>] ] ] [-i "
+"interval_sec | -c Befehl ...]\n"
#: utils/idle_monitor/cpupower-monitor.c:71
#, c-format
msgid "\t -v: be more verbose\n"
-msgstr ""
+msgstr "\t -v: ausführlicher\n"
#: utils/idle_monitor/cpupower-monitor.c:73
#, c-format
msgid "\t -h: print this help\n"
-msgstr ""
+msgstr "\t -h: diese Hilfe ausgeben\n"
#: utils/idle_monitor/cpupower-monitor.c:74
#, c-format
-msgid "\t -i: time intervall to measure for in seconds (default 1)\n"
-msgstr ""
+msgid "\t -i: time interval to measure for in seconds (default 1)\n"
+msgstr "\t -i: Zeitintervall für die Messung in Sekunden (Standard 1)\n"
#: utils/idle_monitor/cpupower-monitor.c:75
#, c-format
msgid "\t -t: show CPU topology/hierarchy\n"
-msgstr ""
+msgstr "\t -t: CPU-Topologie/Hierarchie anzeigen\n"
#: utils/idle_monitor/cpupower-monitor.c:76
#, c-format
msgid "\t -l: list available CPU sleep monitors (for use with -m)\n"
msgstr ""
+"\t -l: verfügbare CPU-Schlafwächter auflisten (für Verwendung mit -m)\n"
#: utils/idle_monitor/cpupower-monitor.c:77
#, c-format
msgid "\t -m: show specific CPU sleep monitors only (in same order)\n"
msgstr ""
+"\t -m: spezifische CPU-Schlafwächter anzeigen (in gleicher Reihenfolge)\n"
#: utils/idle_monitor/cpupower-monitor.c:79
#, c-format
@@ -119,71 +125,73 @@ msgid ""
"only one of: -t, -l, -m are allowed\n"
"If none of them is passed,"
msgstr ""
+"nur einer von: -t, -l, -m ist erlaubt\n"
+"Wenn keiner von ihnen übergeben wird,"
#: utils/idle_monitor/cpupower-monitor.c:80
#, c-format
msgid " all supported monitors are shown\n"
-msgstr ""
+msgstr " werden alle unterstützten Wächter angezeigt\n"
#: utils/idle_monitor/cpupower-monitor.c:197
#, c-format
msgid "Monitor %s, Counter %s has no count function. Implementation error\n"
-msgstr ""
+msgstr "Wächter %s, Zähler %s hat keine Zählfunktion. Implementierungsfehler\n"
#: utils/idle_monitor/cpupower-monitor.c:207
#, c-format
msgid " *is offline\n"
-msgstr ""
+msgstr " *ist offline\n"
#: utils/idle_monitor/cpupower-monitor.c:236
#, c-format
msgid "%s: max monitor name length (%d) exceeded\n"
-msgstr ""
+msgstr "%s: max. Wächternamenslänge (%d) überschritten\n"
#: utils/idle_monitor/cpupower-monitor.c:250
#, c-format
msgid "No matching monitor found in %s, try -l option\n"
-msgstr ""
+msgstr "Kein passender Wächter in %s gefunden, versuchen Sie die Option -l\n"
#: utils/idle_monitor/cpupower-monitor.c:266
#, c-format
msgid "Monitor \"%s\" (%d states) - Might overflow after %u s\n"
-msgstr ""
+msgstr "Wächter \"%s\" (%d Zustände) - Könnte nach %u s überlaufen\n"
#: utils/idle_monitor/cpupower-monitor.c:319
#, c-format
msgid "%s took %.5f seconds and exited with status %d\n"
-msgstr ""
+msgstr "%s hat %.5f Sekunden gedauert und hat sich mit Status %d beendet\n"
#: utils/idle_monitor/cpupower-monitor.c:406
#, c-format
msgid "Cannot read number of available processors\n"
-msgstr ""
+msgstr "Anzahl der verfügbaren Prozessoren kann nicht gelesen werden\n"
#: utils/idle_monitor/cpupower-monitor.c:417
#, c-format
msgid "Available monitor %s needs root access\n"
-msgstr ""
+msgstr "Verfügbarer Wächter %s benötigt root-Zugriff\n"
#: utils/idle_monitor/cpupower-monitor.c:428
#, c-format
msgid "No HW Cstate monitors found\n"
-msgstr ""
+msgstr "Keine HW C-Zustandswächter gefunden\n"
#: utils/cpupower.c:78
#, c-format
msgid "cpupower [ -c cpulist ] subcommand [ARGS]\n"
-msgstr ""
+msgstr "cpupower [ -c cpulist ] Unterbefehl [ARGS]\n"
#: utils/cpupower.c:79
#, c-format
msgid "cpupower --version\n"
-msgstr ""
+msgstr "cpupower --version\n"
#: utils/cpupower.c:80
#, c-format
msgid "Supported subcommands are:\n"
-msgstr ""
+msgstr "Unterstützte Unterbefehle sind:\n"
#: utils/cpupower.c:83
#, c-format
@@ -191,11 +199,15 @@ msgid ""
"\n"
"Some subcommands can make use of the -c cpulist option.\n"
msgstr ""
+"\n"
+"Einige Unterbefehle können die Option -c cpulist verwenden.\n"
#: utils/cpupower.c:84
#, c-format
msgid "Look at the general cpupower manpage how to use it\n"
msgstr ""
+"Schauen Sie sich die allgemeine cpupower manpage an, um zu erfahren, wie man "
+"es benutzt\n"
#: utils/cpupower.c:85
#, c-format
@@ -217,30 +229,31 @@ msgstr "Bitte melden Sie Fehler an %s.\n"
#: utils/cpupower.c:114
#, c-format
msgid "Error parsing cpu list\n"
-msgstr ""
+msgstr "Fehler beim Parsen der CPU-Liste\n"
#: utils/cpupower.c:172
#, c-format
msgid "Subcommand %s needs root privileges\n"
-msgstr ""
+msgstr "Unterbefehl %s benötigt root-Rechte\n"
#: utils/cpufreq-info.c:31
#, c-format
msgid "Couldn't count the number of CPUs (%s: %s), assuming 1\n"
msgstr ""
-"Konnte nicht die Anzahl der CPUs herausfinden (%s : %s), nehme daher 1 an.\n"
+"Anzahl der CPUs konnte nicht herausgefinden werden (%s: %s), es wird daher 1 "
+"angenommen\n"
#: utils/cpufreq-info.c:63
#, c-format
msgid ""
" minimum CPU frequency - maximum CPU frequency - governor\n"
-msgstr ""
-" minimale CPU-Taktfreq. - maximale CPU-Taktfreq. - Regler \n"
+msgstr " minimale CPU-Frequenz - maximale CPU-Frequenz - Regler\n"
#: utils/cpufreq-info.c:151
#, c-format
msgid "Error while evaluating Boost Capabilities on CPU %d -- are you root?\n"
msgstr ""
+"Fehler beim Evaluieren der Boost-Fähigkeiten bei CPU %d -- sind Sie root?\n"
#. P state changes via MSR are identified via cpuid 80000007
#. on Intel and AMD, but we assume boost capable machines can do that
@@ -250,50 +263,50 @@ msgstr ""
#: utils/cpufreq-info.c:161
#, c-format
msgid " boost state support: \n"
-msgstr ""
+msgstr " Boost-Zustand-Unterstützung: \n"
#: utils/cpufreq-info.c:163
#, c-format
msgid " Supported: %s\n"
-msgstr ""
+msgstr " Unterstützt: %s\n"
#: utils/cpufreq-info.c:163 utils/cpufreq-info.c:164
msgid "yes"
-msgstr ""
+msgstr "ja"
#: utils/cpufreq-info.c:163 utils/cpufreq-info.c:164
msgid "no"
-msgstr ""
+msgstr "nein"
#: utils/cpufreq-info.c:164
-#, fuzzy, c-format
+#, c-format
msgid " Active: %s\n"
-msgstr " Treiber: %s\n"
+msgstr " Aktiv: %s\n"
#: utils/cpufreq-info.c:177
#, c-format
msgid " Boost States: %d\n"
-msgstr ""
+msgstr " Boost-Zustände: %d\n"
#: utils/cpufreq-info.c:178
#, c-format
msgid " Total States: %d\n"
-msgstr ""
+msgstr " Gesamtzustände: %d\n"
#: utils/cpufreq-info.c:181
#, c-format
msgid " Pstate-Pb%d: %luMHz (boost state)\n"
-msgstr ""
+msgstr " Pstate-Pb%d: %luMHz (Boost-Zustand)\n"
#: utils/cpufreq-info.c:184
#, c-format
msgid " Pstate-P%d: %luMHz\n"
-msgstr ""
+msgstr " Pstate-P%d: %luMHz\n"
#: utils/cpufreq-info.c:211
#, c-format
msgid " no or unknown cpufreq driver is active on this CPU\n"
-msgstr " kein oder nicht bestimmbarer cpufreq-Treiber aktiv\n"
+msgstr " kein oder ein unbekannter cpufreq-Treiber ist auf dieser CPU aktiv\n"
#: utils/cpufreq-info.c:213
#, c-format
@@ -303,12 +316,12 @@ msgstr " Treiber: %s\n"
#: utils/cpufreq-info.c:219
#, c-format
msgid " CPUs which run at the same hardware frequency: "
-msgstr " Folgende CPUs laufen mit der gleichen Hardware-Taktfrequenz: "
+msgstr " CPUs, die mit der gleichen Hardwarefrequenz laufen: "
#: utils/cpufreq-info.c:230
#, c-format
msgid " CPUs which need to have their frequency coordinated by software: "
-msgstr " Die Taktfrequenz folgender CPUs werden per Software koordiniert: "
+msgstr " CPUs, die ihre Frequenz mit Software koordinieren müssen: "
#: utils/cpufreq-info.c:241
#, c-format
@@ -318,22 +331,22 @@ msgstr " Maximale Dauer eines Taktfrequenzwechsels: "
#: utils/cpufreq-info.c:247
#, c-format
msgid " hardware limits: "
-msgstr " Hardwarebedingte Grenzen der Taktfrequenz: "
+msgstr " Hardwarebegrenzungen: "
#: utils/cpufreq-info.c:256
#, c-format
msgid " available frequency steps: "
-msgstr " mögliche Taktfrequenzen: "
+msgstr " verfügbare Frequenzschritte: "
#: utils/cpufreq-info.c:269
#, c-format
msgid " available cpufreq governors: "
-msgstr " mögliche Regler: "
+msgstr " verfügbare cpufreq-Regler: "
#: utils/cpufreq-info.c:280
#, c-format
msgid " current policy: frequency should be within "
-msgstr " momentane Taktik: die Frequenz soll innerhalb "
+msgstr " momentane Richtlinie: Frequenz sollte innerhalb "
#: utils/cpufreq-info.c:282
#, c-format
@@ -346,29 +359,28 @@ msgid ""
"The governor \"%s\" may decide which speed to use\n"
" within this range.\n"
msgstr ""
-" liegen. Der Regler \"%s\" kann frei entscheiden,\n"
-" welche Taktfrequenz innerhalb dieser Grenze verwendet "
-"wird.\n"
+" sein. Der Regler \"%s\" kann frei entscheiden,\n"
+" welche Geschwindigkeit er in diesem Bereich verwendet.\n"
#: utils/cpufreq-info.c:293
#, c-format
msgid " current CPU frequency is "
-msgstr " momentane Taktfrequenz ist "
+msgstr " momentane CPU-Frequenz ist "
#: utils/cpufreq-info.c:296
#, c-format
msgid " (asserted by call to hardware)"
-msgstr " (verifiziert durch Nachfrage bei der Hardware)"
+msgstr " (durch Aufruf der Hardware sichergestellt)"
#: utils/cpufreq-info.c:304
#, c-format
msgid " cpufreq stats: "
-msgstr " Statistik:"
+msgstr " cpufreq-Statistiken: "
#: utils/cpufreq-info.c:472
-#, fuzzy, c-format
+#, c-format
msgid "Usage: cpupower freqinfo [options]\n"
-msgstr "Aufruf: cpufreq-info [Optionen]\n"
+msgstr "Aufruf: cpupower freqinfo [Optionen]\n"
#: utils/cpufreq-info.c:473 utils/cpufreq-set.c:26 utils/cpupower-set.c:23
#: utils/cpupower-info.c:22 utils/cpuidle-info.c:148
@@ -377,11 +389,9 @@ msgid "Options:\n"
msgstr "Optionen:\n"
#: utils/cpufreq-info.c:474
-#, fuzzy, c-format
+#, c-format
msgid " -e, --debug Prints out debug information [default]\n"
-msgstr ""
-" -e, --debug Erzeugt detaillierte Informationen, hilfreich\n"
-" zum Aufspüren von Fehlern\n"
+msgstr " -e, --debug Gibt Debug-Informationen aus [Standard]\n"
#: utils/cpufreq-info.c:475
#, c-format
@@ -424,7 +434,7 @@ msgstr " -p, --policy Findet die momentane Taktik heraus *\n"
#: utils/cpufreq-info.c:482
#, c-format
msgid " -g, --governors Determines available cpufreq governors *\n"
-msgstr " -g, --governors Erzeugt eine Liste mit verfügbaren Reglern *\n"
+msgstr " -g, --governors Ermittelt verfügbare cpufreq-Regler *\n"
#: utils/cpufreq-info.c:483
#, c-format
@@ -449,8 +459,7 @@ msgstr ""
#: utils/cpufreq-info.c:486
#, c-format
msgid " -s, --stats Shows cpufreq statistics if available\n"
-msgstr ""
-" -s, --stats Zeigt, sofern möglich, Statistiken über cpufreq an.\n"
+msgstr " -s, --stats Zeigt cpufreq-Statistiken an, falls vorhanden\n"
#: utils/cpufreq-info.c:487
#, c-format
@@ -464,13 +473,13 @@ msgstr ""
#: utils/cpufreq-info.c:488
#, c-format
msgid " -b, --boost Checks for turbo or boost modes *\n"
-msgstr ""
+msgstr " -b, --boost Prüft auf Turbo- oder Boost-Modi *\n"
#: utils/cpufreq-info.c:489
#, c-format
msgid ""
-" -o, --proc Prints out information like provided by the /proc/"
-"cpufreq\n"
+" -o, --proc Prints out information like provided by the "
+"/proc/cpufreq\n"
" interface in 2.4. and early 2.6. kernels\n"
msgstr ""
" -o, --proc Erzeugt Informationen in einem ähnlichem Format zu "
@@ -509,8 +518,8 @@ msgid ""
"For the arguments marked with *, omitting the -c or --cpu argument is\n"
"equivalent to setting it to zero\n"
msgstr ""
-"Bei den mit * markierten Parametern wird '--cpu 0' angenommen, soweit nicht\n"
-"mittels -c oder --cpu etwas anderes angegeben wird\n"
+"Für die mit * markierten Argumente ist das Weglassen des Arguments\n"
+"-c oder --cpu gleichbedeutend mit der Einstellung auf Null\n"
#: utils/cpufreq-info.c:580
#, c-format
@@ -525,8 +534,8 @@ msgid ""
"You can't specify more than one --cpu parameter and/or\n"
"more than one output-specific argument\n"
msgstr ""
-"Man kann nicht mehr als einen --cpu-Parameter und/oder mehr als einen\n"
-"informationsspezifischen Parameter gleichzeitig angeben\n"
+"Sie können nicht mehr als einen Parameter --cpu und/oder\n"
+"mehr als ein ausgabespezifisches Argument angeben\n"
#: utils/cpufreq-info.c:600 utils/cpufreq-set.c:82 utils/cpupower-set.c:42
#: utils/cpupower-info.c:42 utils/cpuidle-info.c:213
@@ -538,17 +547,17 @@ msgstr "unbekannter oder falscher Parameter\n"
#, c-format
msgid "couldn't analyze CPU %d as it doesn't seem to be present\n"
msgstr ""
-"Konnte nicht die CPU %d analysieren, da sie (scheinbar?) nicht existiert.\n"
+"CPU %d konnte nicht analysiert werden, da sie scheinbar nicht existiert\n"
#: utils/cpufreq-info.c:620 utils/cpupower-info.c:142
#, c-format
msgid "analyzing CPU %d:\n"
-msgstr "analysiere CPU %d:\n"
+msgstr "CPU %d wird analysiert:\n"
#: utils/cpufreq-set.c:25
-#, fuzzy, c-format
+#, c-format
msgid "Usage: cpupower frequency-set [options]\n"
-msgstr "Aufruf: cpufreq-set [Optionen]\n"
+msgstr "Aufruf: cpupower frequency-set [Optionen]\n"
#: utils/cpufreq-set.c:27
#, c-format
@@ -556,7 +565,7 @@ msgid ""
" -d FREQ, --min FREQ new minimum CPU frequency the governor may "
"select\n"
msgstr ""
-" -d FREQ, --min FREQ neue minimale Taktfrequenz, die der Regler\n"
+" -d FREQ, --min FREQ neue minimale CPU-Frequenz, die der Regler\n"
" auswählen darf\n"
#: utils/cpufreq-set.c:28
@@ -571,7 +580,7 @@ msgstr ""
#: utils/cpufreq-set.c:29
#, c-format
msgid " -g GOV, --governor GOV new cpufreq governor\n"
-msgstr " -g GOV, --governors GOV wechsle zu Regler GOV\n"
+msgstr " -g GOV, --governors GOV neuer cpufreq-Regler\n"
#: utils/cpufreq-set.c:30
#, c-format
@@ -579,29 +588,29 @@ msgid ""
" -f FREQ, --freq FREQ specific frequency to be set. Requires userspace\n"
" governor to be available and loaded\n"
msgstr ""
-" -f FREQ, --freq FREQ setze exakte Taktfrequenz. Benötigt den Regler\n"
-" 'userspace'.\n"
+" -f FREQ, --freq FREQ bestimmte Frequenz, die eingestellt werden soll.\n"
+" Erfordert einen verfügbaren und geladenen "
+"userspace-Regler\n"
#: utils/cpufreq-set.c:32
#, c-format
msgid " -r, --related Switches all hardware-related CPUs\n"
-msgstr ""
-" -r, --related Setze Werte für alle CPUs, deren Taktfrequenz\n"
-" hardwarebedingt identisch ist.\n"
+msgstr " -r, --related Schaltet alle hardwarebezogenen CPUs um\n"
#: utils/cpufreq-set.c:33 utils/cpupower-set.c:28 utils/cpupower-info.c:27
#, c-format
msgid " -h, --help Prints out this screen\n"
-msgstr " -h, --help Gibt diese Kurzübersicht aus\n"
+msgstr " -h, --help Gibt diesen Bildschirm aus\n"
#: utils/cpufreq-set.c:35
-#, fuzzy, c-format
+#, c-format
msgid ""
"Notes:\n"
"1. Omitting the -c or --cpu argument is equivalent to setting it to \"all\"\n"
msgstr ""
-"Bei den mit * markierten Parametern wird '--cpu 0' angenommen, soweit nicht\n"
-"mittels -c oder --cpu etwas anderes angegeben wird\n"
+"Hinweis:\n"
+"1. Das Weglassen des Arguments -c oder --cpu ist gleichbedeutend mit der "
+"Einstellung auf \"all\"\n"
#: utils/cpufreq-set.c:37
#, fuzzy, c-format
@@ -636,17 +645,21 @@ msgid ""
"frequency\n"
" or because the userspace governor isn't loaded?\n"
msgstr ""
-"Beim Einstellen ist ein Fehler aufgetreten. Typische Fehlerquellen sind:\n"
-"- nicht ausreichende Rechte (Administrator)\n"
-"- der Regler ist nicht verfügbar bzw. nicht geladen\n"
-"- die angegebene Taktik ist inkorrekt\n"
-"- eine spezifische Frequenz wurde angegeben, aber der Regler 'userspace'\n"
-" kann entweder hardwarebedingt nicht genutzt werden oder ist nicht geladen\n"
+"Fehler beim Festlegen neuer Werte. Häufige Fehler:\n"
+"- Verfügen Sie über die erforderlichen Administrationsrechte? (Superuser?)\n"
+"- Ist der von Ihnen gewünschte Regler verfügbar und mittels modprobe "
+"geladen?\n"
+"- Versuchen Sie eine ungültige Richtlinie festzulegen?\n"
+"- Versuchen Sie eine bestimmte Frequenz festzulegen, aber der "
+"userspace-Regler ist nicht verfügbar,\n"
+" z.B. wegen Hardware, die nicht auf eine bestimmte Frequenz eingestellt "
+"werden kann\n"
+" oder weil der userspace-Regler nicht geladen ist?\n"
#: utils/cpufreq-set.c:170
#, c-format
msgid "wrong, unknown or unhandled CPU?\n"
-msgstr "unbekannte oder nicht regelbare CPU\n"
+msgstr "falsche, unbekannte oder nicht regelbare CPU?\n"
#: utils/cpufreq-set.c:302
#, c-format
@@ -654,8 +667,8 @@ msgid ""
"the -f/--freq parameter cannot be combined with -d/--min, -u/--max or\n"
"-g/--governor parameters\n"
msgstr ""
-"Der -f bzw. --freq-Parameter kann nicht mit den Parametern -d/--min, -u/--"
-"max\n"
+"Der -f bzw. --freq-Parameter kann nicht mit den Parametern -d/--min, "
+"-u/--max\n"
"oder -g/--governor kombiniert werden\n"
#: utils/cpufreq-set.c:308
@@ -664,18 +677,18 @@ msgid ""
"At least one parameter out of -f/--freq, -d/--min, -u/--max, and\n"
"-g/--governor must be passed\n"
msgstr ""
-"Es muss mindestens ein Parameter aus -f/--freq, -d/--min, -u/--max oder\n"
-"-g/--governor angegeben werden.\n"
+"Mindestens ein Parameter aus -f/--freq, -d/--min, -u/--max und\n"
+"-g/--governor muss übergeben werden\n"
#: utils/cpufreq-set.c:347
#, c-format
msgid "Setting cpu: %d\n"
-msgstr ""
+msgstr "CPU einstellen: %d\n"
#: utils/cpupower-set.c:22
#, c-format
msgid "Usage: cpupower set [ -b val ] [ -m val ] [ -s val ]\n"
-msgstr ""
+msgstr "Aufruf: cpupower set [ -b val ] [ -m val ] [ -s val ]\n"
#: utils/cpupower-set.c:24
#, c-format
@@ -689,6 +702,8 @@ msgstr ""
msgid ""
" -m, --sched-mc [VAL] Sets the kernel's multi core scheduler policy.\n"
msgstr ""
+" -m, --sched-mc [VAL] Legt die Mehrkern-Scheduler-Richtlinie des "
+"Kernels fest.\n"
#: utils/cpupower-set.c:27
#, c-format
@@ -700,37 +715,37 @@ msgstr ""
#: utils/cpupower-set.c:80
#, c-format
msgid "--perf-bias param out of range [0-%d]\n"
-msgstr ""
+msgstr "--perf-bias-Parameter außerhalb des Bereichs [0-%d]\n"
#: utils/cpupower-set.c:91
#, c-format
msgid "--sched-mc param out of range [0-%d]\n"
-msgstr ""
+msgstr "Parameter --sched-mc außerhalb des Bereichs [0-%d]\n"
#: utils/cpupower-set.c:102
#, c-format
msgid "--sched-smt param out of range [0-%d]\n"
-msgstr ""
+msgstr "Parameter --sched-smt außerhalb des Bereichs [0-%d]\n"
#: utils/cpupower-set.c:121
#, c-format
msgid "Error setting sched-mc %s\n"
-msgstr ""
+msgstr "Fehler beim Einstellen von sched-mc %s\n"
#: utils/cpupower-set.c:127
#, c-format
msgid "Error setting sched-smt %s\n"
-msgstr ""
+msgstr "Fehler beim Einstellen von sched-smt %s\n"
#: utils/cpupower-set.c:146
#, c-format
msgid "Error setting perf-bias value on CPU %d\n"
-msgstr ""
+msgstr "Fehler beim Einstellen des perf-bias-Wertes auf der CPU %d\n"
#: utils/cpupower-info.c:21
#, c-format
msgid "Usage: cpupower info [ -b ] [ -m ] [ -s ]\n"
-msgstr ""
+msgstr "Aufruf: cpupower info [ -b ] [ -m ] [ -s ]\n"
#: utils/cpupower-info.c:23
#, c-format
@@ -740,9 +755,10 @@ msgid ""
msgstr ""
#: utils/cpupower-info.c:25
-#, fuzzy, c-format
+#, c-format
msgid " -m, --sched-mc Gets the kernel's multi core scheduler policy.\n"
-msgstr " -p, --policy Findet die momentane Taktik heraus *\n"
+msgstr ""
+" -m, --sched-mc Ruft die Mehrkern-Scheduler-Richtlinie des Kernels ab.\n"
#: utils/cpupower-info.c:26
#, c-format
@@ -756,17 +772,20 @@ msgid ""
"\n"
"Passing no option will show all info, by default only on core 0\n"
msgstr ""
+"\n"
+"Wenn Sie keine Option übergeben, werden alle Informationen angezeigt, "
+"standardmäßig nur auf Kern 0\n"
#: utils/cpupower-info.c:102
#, c-format
msgid "System's multi core scheduler setting: "
-msgstr ""
+msgstr "Mehrkern-Scheduler-Einstellung des Systems: "
#. if sysfs file is missing it's: errno == ENOENT
#: utils/cpupower-info.c:105 utils/cpupower-info.c:114
#, c-format
msgid "not supported\n"
-msgstr ""
+msgstr "nicht unterstützt\n"
#: utils/cpupower-info.c:111
#, c-format
@@ -786,164 +805,161 @@ msgstr ""
#: utils/cpupower-info.c:147
#, c-format
msgid "Could not read perf-bias value\n"
-msgstr ""
+msgstr "perf-bias-Wert konnte nicht gelesen werden\n"
#: utils/cpupower-info.c:150
#, c-format
msgid "perf-bias: %d\n"
-msgstr ""
+msgstr "perf-bias: %d\n"
#: utils/cpuidle-info.c:28
-#, fuzzy, c-format
+#, c-format
msgid "Analyzing CPU %d:\n"
-msgstr "analysiere CPU %d:\n"
+msgstr "CPU %d wird analysiert:\n"
#: utils/cpuidle-info.c:32
#, c-format
msgid "CPU %u: No idle states\n"
-msgstr ""
+msgstr "CPU %u: Keine Ruhezustände\n"
#: utils/cpuidle-info.c:36
#, c-format
msgid "CPU %u: Can't read idle state info\n"
-msgstr ""
+msgstr "CPU %u: Ruhezustands-Informationen können nicht gelesen werden\n"
#: utils/cpuidle-info.c:41
#, c-format
msgid "Could not determine max idle state %u\n"
-msgstr ""
+msgstr "Max. Ruhezustand %u konnte nicht bestimmt werden\n"
#: utils/cpuidle-info.c:46
#, c-format
msgid "Number of idle states: %d\n"
-msgstr ""
+msgstr "Anzahl der Ruhezustände: %d\n"
#: utils/cpuidle-info.c:48
-#, fuzzy, c-format
+#, c-format
msgid "Available idle states:"
-msgstr " mögliche Taktfrequenzen: "
+msgstr "Verfügbare Ruhezustände:"
#: utils/cpuidle-info.c:71
#, c-format
msgid "Flags/Description: %s\n"
-msgstr ""
+msgstr "Merker/Beschreibung: %s\n"
#: utils/cpuidle-info.c:74
#, c-format
msgid "Latency: %lu\n"
-msgstr ""
+msgstr "Latenz: %lu\n"
#: utils/cpuidle-info.c:76
#, c-format
msgid "Usage: %lu\n"
-msgstr ""
+msgstr "Aufruf: %lu\n"
#: utils/cpuidle-info.c:78
#, c-format
msgid "Duration: %llu\n"
-msgstr ""
+msgstr "Dauer: %llu\n"
#: utils/cpuidle-info.c:90
#, c-format
msgid "Could not determine cpuidle driver\n"
-msgstr ""
+msgstr "cpuidle-Treiber konnte nicht bestimmt werden\n"
#: utils/cpuidle-info.c:94
-#, fuzzy, c-format
+#, c-format
msgid "CPUidle driver: %s\n"
-msgstr " Treiber: %s\n"
+msgstr "CPUidle-Treiber: %s\n"
#: utils/cpuidle-info.c:99
#, c-format
msgid "Could not determine cpuidle governor\n"
-msgstr ""
+msgstr "cpuidle-Regler konnte nicht bestimmt werden\n"
#: utils/cpuidle-info.c:103
#, c-format
msgid "CPUidle governor: %s\n"
-msgstr ""
+msgstr "CPUidle-Regler: %s\n"
#: utils/cpuidle-info.c:122
#, c-format
msgid "CPU %u: Can't read C-state info\n"
-msgstr ""
+msgstr "CPU %u: C-Zustands-Informationen können nicht gelesen werden\n"
#. printf("Cstates: %d\n", cstates);
#: utils/cpuidle-info.c:127
#, c-format
msgid "active state: C0\n"
-msgstr ""
+msgstr "aktiver Zustand: C0\n"
#: utils/cpuidle-info.c:128
#, c-format
msgid "max_cstate: C%u\n"
-msgstr ""
+msgstr "max_cstate: C%u\n"
#: utils/cpuidle-info.c:129
-#, fuzzy, c-format
+#, c-format
msgid "maximum allowed latency: %lu usec\n"
-msgstr " Maximale Dauer eines Taktfrequenzwechsels: "
+msgstr "maximal erlaubte Latenz: %lu usec\n"
#: utils/cpuidle-info.c:130
#, c-format
msgid "states:\t\n"
-msgstr ""
+msgstr "Zustände:\t\n"
#: utils/cpuidle-info.c:132
#, c-format
msgid " C%d: type[C%d] "
-msgstr ""
+msgstr " C%d: Typ[C%d] "
#: utils/cpuidle-info.c:134
#, c-format
msgid "promotion[--] demotion[--] "
-msgstr ""
+msgstr "promotion[--] demotion[--] "
#: utils/cpuidle-info.c:135
#, c-format
msgid "latency[%03lu] "
-msgstr ""
+msgstr "Latenz[%03lu] "
#: utils/cpuidle-info.c:137
#, c-format
msgid "usage[%08lu] "
-msgstr ""
+msgstr "Aufruf[%08lu] "
#: utils/cpuidle-info.c:139
#, c-format
msgid "duration[%020Lu] \n"
-msgstr ""
+msgstr "Dauer[%020Lu] \n"
#: utils/cpuidle-info.c:147
-#, fuzzy, c-format
+#, c-format
msgid "Usage: cpupower idleinfo [options]\n"
-msgstr "Aufruf: cpufreq-info [Optionen]\n"
+msgstr "Aufruf: cpupower idleinfo [Optionen]\n"
#: utils/cpuidle-info.c:149
-#, fuzzy, c-format
+#, c-format
msgid " -s, --silent Only show general C-state information\n"
msgstr ""
-" -e, --debug Erzeugt detaillierte Informationen, hilfreich\n"
-" zum Aufspüren von Fehlern\n"
+" -s, --silent Nur allgemeine C-Zustands-Informationen anzeigen\n"
#: utils/cpuidle-info.c:150
-#, fuzzy, c-format
+#, c-format
msgid ""
-" -o, --proc Prints out information like provided by the /proc/"
-"acpi/processor/*/power\n"
+" -o, --proc Prints out information like provided by the "
+"/proc/acpi/processor/*/power\n"
" interface in older kernels\n"
msgstr ""
-" -o, --proc Erzeugt Informationen in einem ähnlichem Format zu "
-"dem\n"
-" der /proc/cpufreq-Datei in 2.4. und frühen 2.6.\n"
-" Kernel-Versionen\n"
+" -o, --proc Gibt Informationen so aus, wie sie von der "
+"Schnittstelle\n"
+" /proc/acpi/processor/*/power in älteren Kerneln "
+"bereitgestellt werden\n"
#: utils/cpuidle-info.c:209
-#, fuzzy, c-format
+#, c-format
msgid "You can't specify more than one output-specific argument\n"
-msgstr ""
-"Man kann nicht mehr als einen --cpu-Parameter und/oder mehr als einen\n"
-"informationsspezifischen Parameter gleichzeitig angeben\n"
+msgstr "Sie können nicht mehr als ein ausgabenspezifisches Argument angeben\n"
#~ msgid ""
#~ " -c CPU, --cpu CPU CPU number which information shall be determined "
@@ -956,6 +972,6 @@ msgstr ""
#~ " -c CPU, --cpu CPU number of CPU where cpufreq settings shall be "
#~ "modified\n"
#~ msgstr ""
-#~ " -c CPU, --cpu CPU Nummer der CPU, deren Taktfrequenz-"
-#~ "Einstellung\n"
+#~ " -c CPU, --cpu CPU Nummer der CPU, deren "
+#~ "Taktfrequenz-Einstellung\n"
#~ " werden soll\n"
diff --git a/tools/power/cpupower/po/fr.po b/tools/power/cpupower/po/fr.po
index b46ca2548f86..b6e505b34e4a 100644
--- a/tools/power/cpupower/po/fr.po
+++ b/tools/power/cpupower/po/fr.po
@@ -95,7 +95,7 @@ msgstr ""
#: utils/idle_monitor/cpupower-monitor.c:74
#, c-format
-msgid "\t -i: time intervall to measure for in seconds (default 1)\n"
+msgid "\t -i: time interval to measure for in seconds (default 1)\n"
msgstr ""
#: utils/idle_monitor/cpupower-monitor.c:75
diff --git a/tools/power/cpupower/po/it.po b/tools/power/cpupower/po/it.po
index f80c4ddb9bda..a1deeb52c9e0 100644
--- a/tools/power/cpupower/po/it.po
+++ b/tools/power/cpupower/po/it.po
@@ -95,7 +95,7 @@ msgstr ""
#: utils/idle_monitor/cpupower-monitor.c:74
#, c-format
-msgid "\t -i: time intervall to measure for in seconds (default 1)\n"
+msgid "\t -i: time interval to measure for in seconds (default 1)\n"
msgstr ""
#: utils/idle_monitor/cpupower-monitor.c:75
diff --git a/tools/power/cpupower/po/pt.po b/tools/power/cpupower/po/pt.po
index 990f5267ffe8..902186585bb9 100644
--- a/tools/power/cpupower/po/pt.po
+++ b/tools/power/cpupower/po/pt.po
@@ -93,7 +93,7 @@ msgstr ""
#: utils/idle_monitor/cpupower-monitor.c:74
#, c-format
-msgid "\t -i: time intervall to measure for in seconds (default 1)\n"
+msgid "\t -i: time interval to measure for in seconds (default 1)\n"
msgstr ""
#: utils/idle_monitor/cpupower-monitor.c:75
diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c
index 10290b308797..e63cf55f81cf 100644
--- a/tools/power/cpupower/utils/cpufreq-info.c
+++ b/tools/power/cpupower/utils/cpufreq-info.c
@@ -1,7 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
- *
- * Licensed under the terms of the GNU GPL License version 2.
*/
diff --git a/tools/power/cpupower/utils/cpufreq-set.c b/tools/power/cpupower/utils/cpufreq-set.c
index 1eef0aed6423..6ed82fba5aaa 100644
--- a/tools/power/cpupower/utils/cpufreq-set.c
+++ b/tools/power/cpupower/utils/cpufreq-set.c
@@ -1,7 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
- *
- * Licensed under the terms of the GNU GPL License version 2.
*/
@@ -306,6 +305,8 @@ int cmd_freq_set(int argc, char **argv)
bitmask_setbit(cpus_chosen, cpus->cpu);
cpus = cpus->next;
}
+ /* Set the last cpu in related cpus list */
+ bitmask_setbit(cpus_chosen, cpus->cpu);
cpufreq_put_related_cpus(cpus);
}
}
diff --git a/tools/power/cpupower/utils/cpuidle-info.c b/tools/power/cpupower/utils/cpuidle-info.c
index b59c85defa05..f2b202c5552a 100644
--- a/tools/power/cpupower/utils/cpuidle-info.c
+++ b/tools/power/cpupower/utils/cpuidle-info.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
* (C) 2010 Thomas Renninger <trenn@suse.de>
- *
- * Licensed under the terms of the GNU GPL License version 2.
*/
diff --git a/tools/power/cpupower/utils/cpupower-info.c b/tools/power/cpupower/utils/cpupower-info.c
index c7caa8eaa6d0..4c9d342b70ff 100644
--- a/tools/power/cpupower/utils/cpupower-info.c
+++ b/tools/power/cpupower/utils/cpupower-info.c
@@ -1,7 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
- *
- * Licensed under the terms of the GNU GPL License version 2.
*/
diff --git a/tools/power/cpupower/utils/cpupower-set.c b/tools/power/cpupower/utils/cpupower-set.c
index 532f46b9a335..3cd95c6cb974 100644
--- a/tools/power/cpupower/utils/cpupower-set.c
+++ b/tools/power/cpupower/utils/cpupower-set.c
@@ -1,7 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
- *
- * Licensed under the terms of the GNU GPL License version 2.
*/
diff --git a/tools/power/cpupower/utils/cpupower.c b/tools/power/cpupower/utils/cpupower.c
index 2dccf4998599..8e3d08042825 100644
--- a/tools/power/cpupower/utils/cpupower.c
+++ b/tools/power/cpupower/utils/cpupower.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
*
- * Licensed under the terms of the GNU GPL License version 2.
- *
* Ideas taken over from the perf userspace tool (included in the Linus
* kernel git repo): subcommand builtins and param parsing.
*/
diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h
index 902139689315..357b19bb136e 100644
--- a/tools/power/cpupower/utils/helpers/helpers.h
+++ b/tools/power/cpupower/utils/helpers/helpers.h
@@ -1,8 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
*
- * Licensed under the terms of the GNU GPL License version 2.
- *
* Miscellaneous helpers which do not fit or are worth
* to put into separate headers
*/
diff --git a/tools/power/cpupower/utils/helpers/sysfs.c b/tools/power/cpupower/utils/helpers/sysfs.c
index 4e8fe2c7b054..e13ff38329a0 100644
--- a/tools/power/cpupower/utils/helpers/sysfs.c
+++ b/tools/power/cpupower/utils/helpers/sysfs.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
* (C) 2011 Thomas Renninger <trenn@novell.com> Novell Inc.
- *
- * Licensed under the terms of the GNU GPL License version 2.
*/
#include <stdio.h>
diff --git a/tools/power/cpupower/utils/helpers/topology.c b/tools/power/cpupower/utils/helpers/topology.c
index a1a6c6041a1e..3dd0925d7594 100644
--- a/tools/power/cpupower/utils/helpers/topology.c
+++ b/tools/power/cpupower/utils/helpers/topology.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
*
- * Licensed under the terms of the GNU GPL License version 2.
- *
* ToDo: Needs to be done more properly for AMD/Intel specifics
*/
diff --git a/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c b/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c
index 2116df9ad832..3f893b99b337 100644
--- a/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c
+++ b/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
*
- * Licensed under the terms of the GNU GPL License version 2.
- *
* PCI initialization based on example code from:
* Andreas Herrmann <andreas.herrmann3@amd.com>
*/
diff --git a/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c b/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c
index 5b8c4956ff9a..f634aeb65c5f 100644
--- a/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c
+++ b/tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c
@@ -1,8 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc
- *
- * Licensed under the terms of the GNU GPL License version 2.
- *
*/
#include <stdio.h>
diff --git a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c
index 051da0a7c454..d3c3e6e7aa26 100644
--- a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c
+++ b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c
@@ -1,10 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
*
- * Licensed under the terms of the GNU GPL License version 2.
- *
* Output format inspired by Len Brown's <lenb@kernel.org> turbostat tool.
- *
*/
diff --git a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.h b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.h
index 2ae50b499e0a..a2d901d3bfaf 100644
--- a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.h
+++ b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.h
@@ -1,8 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
- *
- * Licensed under the terms of the GNU GPL License version 2.
- *
*/
#ifndef __CPUIDLE_INFO_HW__
diff --git a/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c b/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c
index f794d6bbb7e9..7c7451d3f494 100644
--- a/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c
+++ b/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
*
- * Licensed under the terms of the GNU GPL License version 2.
- *
* Based on SandyBridge monitor. Implements the new package C-states
* (PC8, PC9, PC10) coming with a specific Haswell (family 0x45) CPU.
*/
diff --git a/tools/power/cpupower/utils/idle_monitor/idle_monitors.h b/tools/power/cpupower/utils/idle_monitor/idle_monitors.h
index 4fcdeb1e07e8..e9e567ec879e 100644
--- a/tools/power/cpupower/utils/idle_monitor/idle_monitors.h
+++ b/tools/power/cpupower/utils/idle_monitor/idle_monitors.h
@@ -1,10 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
*
- * Licensed under the terms of the GNU GPL License version 2.
- *
* Based on the idea from Michael Matz <matz@suse.de>
- *
*/
#ifndef _CPUIDLE_IDLE_MONITORS_H_
diff --git a/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c
index f2a7e9cfd577..44806a6dae11 100644
--- a/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c
+++ b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c
@@ -1,7 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
- *
- * Licensed under the terms of the GNU GPL License version 2.
*/
#if defined(__i386__) || defined(__x86_64__)
diff --git a/tools/power/cpupower/utils/idle_monitor/nhm_idle.c b/tools/power/cpupower/utils/idle_monitor/nhm_idle.c
index abf8cb5f7349..be7256696a37 100644
--- a/tools/power/cpupower/utils/idle_monitor/nhm_idle.c
+++ b/tools/power/cpupower/utils/idle_monitor/nhm_idle.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
*
- * Licensed under the terms of the GNU GPL License version 2.
- *
* Based on Len Brown's <lenb@kernel.org> turbostat tool.
*/
diff --git a/tools/power/cpupower/utils/idle_monitor/snb_idle.c b/tools/power/cpupower/utils/idle_monitor/snb_idle.c
index a2b45219648d..968333571cad 100644
--- a/tools/power/cpupower/utils/idle_monitor/snb_idle.c
+++ b/tools/power/cpupower/utils/idle_monitor/snb_idle.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
*
- * Licensed under the terms of the GNU GPL License version 2.
- *
* Based on Len Brown's <lenb@kernel.org> turbostat tool.
*/
diff --git a/tools/power/pm-graph/README b/tools/power/pm-graph/README
new file mode 100644
index 000000000000..96259f6e5715
--- /dev/null
+++ b/tools/power/pm-graph/README
@@ -0,0 +1,556 @@
+ p m - g r a p h
+
+ pm-graph: suspend/resume/boot timing analysis tools
+ Version: 5.5
+ Author: Todd Brandt <todd.e.brandt@intel.com>
+ Home Page: https://01.org/pm-graph
+
+ Report bugs/issues at bugzilla.kernel.org Tools/pm-graph
+ - https://bugzilla.kernel.org/buglist.cgi?component=pm-graph&product=Tools
+
+ Full documentation available online & in man pages
+ - Getting Started:
+ https://01.org/pm-graph/documentation/getting-started
+
+ - Config File Format:
+ https://01.org/pm-graph/documentation/3-config-file-format
+
+ - upstream version in git:
+ https://github.com/intel/pm-graph/
+
+ Requirements:
+ - runs with python2 or python3, choice is made by /usr/bin/python link
+ - python2 now requires python-configparser be installed
+
+ Table of Contents
+ - Overview
+ - Setup
+ - Usage
+ - Basic Usage
+ - Dev Mode Usage
+ - Proc Mode Usage
+ - Configuration Files
+ - Usage Examples
+ - Config File Options
+ - Custom Timeline Entries
+ - Adding/Editing Timeline Functions
+ - Adding/Editing Dev Timeline Source Functions
+ - Verifying your Custom Functions
+ - Testing on consumer linux Operating Systems
+ - Android
+
+------------------------------------------------------------------
+| OVERVIEW |
+------------------------------------------------------------------
+
+ This tool suite is designed to assist kernel and OS developers in optimizing
+ their linux stack's suspend/resume & boot time. Using a kernel image built
+ with a few extra options enabled, the tools will execute a suspend or boot,
+ and will capture dmesg and ftrace data. This data is transformed into a set of
+ timelines and a callgraph to give a quick and detailed view of which devices
+ and kernel processes are taking the most time in suspend/resume & boot.
+
+------------------------------------------------------------------
+| SETUP |
+------------------------------------------------------------------
+
+ These packages are required to execute the scripts
+ - python
+ - python-requests
+
+ Ubuntu:
+ sudo apt-get install python python-requests
+
+ Fedora:
+ sudo dnf install python python-requests
+
+ The tools can most easily be installed via git clone and make install
+
+ $> git clone http://github.com/intel/pm-graph.git
+ $> cd pm-graph
+ $> sudo make install
+ $> man sleepgraph ; man bootgraph
+
+ Setup involves some minor kernel configuration
+
+ The following kernel build options are required for all kernels:
+ CONFIG_DEVMEM=y
+ CONFIG_PM_DEBUG=y
+ CONFIG_PM_SLEEP_DEBUG=y
+ CONFIG_FTRACE=y
+ CONFIG_FUNCTION_TRACER=y
+ CONFIG_FUNCTION_GRAPH_TRACER=y
+ CONFIG_KPROBES=y
+ CONFIG_KPROBES_ON_FTRACE=y
+
+ In kernel 3.15.0, two patches were upstreamed which enable the
+ v3.0 behavior. These patches allow the tool to read all the
+ data from trace events instead of from dmesg. You can enable
+ this behavior on earlier kernels with these patches:
+
+ (kernel/pre-3.15/enable_trace_events_suspend_resume.patch)
+ (kernel/pre-3.15/enable_trace_events_device_pm_callback.patch)
+
+ If you're using a kernel older than 3.15.0, the following
+ additional kernel parameters are required:
+ (e.g. in file /etc/default/grub)
+ GRUB_CMDLINE_LINUX_DEFAULT="... initcall_debug log_buf_len=32M ..."
+
+ If you're using a kernel older than 3.11-rc2, the following simple
+ patch must be applied to enable ftrace data:
+ in file: kernel/power/suspend.c
+ in function: int suspend_devices_and_enter(suspend_state_t state)
+ remove call to "ftrace_stop();"
+ remove call to "ftrace_start();"
+
+ There is a patch which does this for kernel v3.8.0:
+ (kernel/pre-3.11-rc2/enable_ftrace_in_suspendresume.patch)
+
+
+
+------------------------------------------------------------------
+| USAGE |
+------------------------------------------------------------------
+
+Basic Usage
+___________
+
+ 1) First configure a kernel using the instructions from the previous sections.
+ Then build, install, and boot with it.
+ 2) Open up a terminal window and execute the mode list command:
+
+ %> sudo ./sleepgraph.py -modes
+ ['freeze', 'mem', 'disk']
+
+ Execute a test using one of the available power modes, e.g. mem (S3):
+
+ %> sudo ./sleepgraph.py -m mem -rtcwake 15
+
+ or with a config file
+
+ %> sudo ./sleepgraph.py -config config/suspend.cfg
+
+ When the system comes back you'll see the script finishing up and
+ creating the output files in the test subdir. It generates output
+ files in subdirectory: suspend-mmddyy-HHMMSS. The ftrace file can
+ be used to regenerate the html timeline with different options
+
+ HTML output: <hostname>_<mode>.html
+ raw dmesg output: <hostname>_<mode>_dmesg.txt
+ raw ftrace output: <hostname>_<mode>_ftrace.txt
+
+ View the html in firefox or chrome.
+
+
+Dev Mode Usage
+______________
+
+ Developer mode adds information on low level source calls to the timeline.
+ The tool sets kprobes on all delay and mutex calls to see which devices
+ are waiting for something and when. It also sets a suite of kprobes on
+ subsystem dependent calls to better fill out the timeline.
+
+ The tool will also expose kernel threads that don't normally show up in the
+ timeline. This is useful in discovering dependent threads to get a better
+ idea of what each device is waiting for. For instance, the scsi_eh thread,
+ a.k.a. scsi resume error handler, is what each SATA disk device waits for
+ before it can continue resume.
+
+ The timeline will be much larger if run with dev mode, so it can be useful
+ to set the -mindev option to clip out any device blocks that are too small
+ to see easily. The following command will give a nice dev mode run:
+
+ %> sudo ./sleepgraph.py -m mem -rtcwake 15 -mindev 1 -dev
+
+ or with a config file
+
+ %> sudo ./sleepgraph.py -config config/suspend-dev.cfg
+
+
+Proc Mode Usage
+_______________
+
+ Proc mode adds user process info to the timeline. This is done in a manner
+ similar to the bootchart utility, which graphs init processes and their
+ execution as the system boots. This tool option does the same thing but for
+ the period before and after suspend/resume.
+
+ In order to see any process info, there needs to be some delay before or
+ after resume since processes are frozen in suspend_prepare and thawed in
+ resume_complete. The predelay and postdelay args allow you to do this. It
+ can also be useful to run in x2 mode with an x2 delay, this way you can
+ see process activity before and after resume, and in between two
+ successive suspend/resumes.
+
+ The command can be run like this:
+
+ %> sudo ./sleepgraph.py -m mem -rtcwake 15 -x2 -x2delay 1000 -predelay 1000 -postdelay 1000 -proc
+
+ or with a config file
+
+ %> sudo ./sleepgraph.py -config config/suspend-proc.cfg
+
+
+------------------------------------------------------------------
+| CONFIGURATION FILES |
+------------------------------------------------------------------
+
+ Since 4.0 we've moved to using config files in lieu of command line options.
+ The config folder contains a collection of typical use cases.
+ There are corresponding configs for other power modes:
+
+ Simple suspend/resume with basic timeline (mem/freeze/standby)
+ config/suspend.cfg
+ config/freeze.cfg
+ config/standby.cfg
+
+ Dev mode suspend/resume with dev timeline (mem/freeze/standby)
+ config/suspend-dev.cfg
+ config/freeze-dev.cfg
+ config/standby-dev.cfg
+
+ Simple suspend/resume with timeline and callgraph (mem/freeze/standby)
+ config/suspend-callgraph.cfg
+ config/freeze-callgraph.cfg
+ config/standby-callgraph.cfg
+
+ Sample proc mode x2 run using mem suspend
+ config/suspend-x2-proc.cfg
+
+ Sample for editing timeline funcs (moves internal functions into config)
+ config/custom-timeline-functions.cfg
+
+ Sample debug config for serio subsystem
+ config/debug-serio-suspend.cfg
+
+
+Usage Examples
+______________
+
+ Run a simple mem suspend:
+ %> sudo ./sleepgraph.py -config config/suspend.cfg
+
+ Run a mem suspend with callgraph data:
+ %> sudo ./sleepgraph.py -config config/suspend-callgraph.cfg
+
+ Run a mem suspend with dev mode detail:
+ %> sudo ./sleepgraph.py -config config/suspend-dev.cfg
+
+
+Config File Options
+___________________
+
+ [Settings]
+
+ # Verbosity: print verbose messages (def: false)
+ verbose: false
+
+ # Suspend Mode: e.g. standby, mem, freeze, disk (def: mem)
+ mode: mem
+
+ # Output Directory Format: {hostname}, {date}, {time} give current values
+ output-dir: suspend-{hostname}-{date}-{time}
+
+ # Automatic Wakeup: use rtcwake to wakeup after X seconds (def: infinity)
+ rtcwake: 15
+
+ # Add Logs: add the dmesg and ftrace log to the html output (def: false)
+ addlogs: false
+
+ # Sus/Res Gap: insert a gap between sus & res in the timeline (def: false)
+ srgap: false
+
+ # Custom Command: Command to execute in lieu of suspend (def: "")
+ command: echo mem > /sys/power/state
+
+ # Proc mode: graph user processes and cpu usage in the timeline (def: false)
+ proc: false
+
+ # Dev mode: graph source functions in the timeline (def: false)
+ dev: false
+
+ # Suspend/Resume x2: run 2 suspend/resumes back to back (def: false)
+ x2: false
+
+ # x2 Suspend Delay: time delay between the two test runs in ms (def: 0 ms)
+ x2delay: 0
+
+ # Pre Suspend Delay: nclude an N ms delay before (1st) suspend (def: 0 ms)
+ predelay: 0
+
+ # Post Resume Delay: include an N ms delay after (last) resume (def: 0 ms)
+ postdelay: 0
+
+ # Min Device Length: graph only dev callbacks longer than min (def: 0.001 ms)
+ mindev: 0.001
+
+ # Callgraph: gather ftrace callgraph data on all timeline events (def: false)
+ callgraph: false
+
+ # Expand Callgraph: pre-expand the callgraph treeviews in html (def: false)
+ expandcg: false
+
+ # Min Callgraph Length: show callgraphs only if longer than min (def: 1 ms)
+ mincg: 1
+
+ # Timestamp Precision: number of sig digits in timestamps (0:S, [3:ms], 6:us)
+ timeprec: 3
+
+ # Device Filter: show only devs whose name/driver includes one of these strings
+ devicefilter: _cpu_up,_cpu_down,i915,usb
+
+ # Override default timeline entries:
+ # Do not use the internal default functions for timeline entries (def: false)
+ # Set this to true if you intend to only use the ones defined in the config
+ override-timeline-functions: true
+
+ # Override default dev timeline entries:
+ # Do not use the internal default functions for dev timeline entries (def: false)
+ # Set this to true if you intend to only use the ones defined in the config
+ override-dev-timeline-functions: true
+
+ # Call Loop Max Gap (dev mode only)
+ # merge loops of the same call if each is less than maxgap apart (def: 100us)
+ callloop-maxgap: 0.0001
+
+ # Call Loop Max Length (dev mode only)
+ # merge loops of the same call if each is less than maxlen in length (def: 5ms)
+ callloop-maxlen: 0.005
+
+------------------------------------------------------------------
+| CUSTOM TIMELINE ENTRIES |
+------------------------------------------------------------------
+
+Adding or Editing Timeline Functions
+____________________________________
+
+ The tool uses an array of function names to fill out empty spaces in the
+ timeline where device callbacks don't appear. For instance, in suspend_prepare
+ the tool adds the sys_sync and freeze_processes calls as virtual device blocks
+ in the timeline to show you where the time is going. These calls should fill
+ the timeline with contiguous data so that most kernel execution is covered.
+
+ It is possible to add new function calls to the timeline by adding them to
+ the config. It's also possible to copy the internal timeline functions into
+ the config so that you can override and edit them. Place them in the
+ timeline_functions_ARCH section with the name of your architecture appended.
+ i.e. for x86_64: [timeline_functions_x86_64]
+
+ Use the override-timeline-functions option if you only want to use your
+ custom calls, or leave it false to append them to the internal ones.
+
+ This section includes a list of functions (set using kprobes) which use both
+ symbol data and function arg data. The args are pulled directly from the
+ stack using this architecture's registers and stack formatting. Each entry
+ can include up to four pieces of info: The function name, a format string,
+ an argument list, and a color. But only a function name is required.
+
+ For a full example config, see config/custom-timeline-functions.cfg. It pulls
+ all the internal timeline functions into the config and allows you to edit
+ them.
+
+ Entry format:
+
+ function: format{fn_arg1}_{fn_arg2} fn_arg1 fn_arg2 ... [color=purple]
+
+ Required Arguments:
+
+ function: The symbol name for the function you want probed, this is the
+ minimum required for an entry, it will show up as the function
+ name with no arguments.
+
+ example: _cpu_up:
+
+ Optional Arguments:
+
+ format: The format to display the data on the timeline in. Use braces to
+ enclose the arg names.
+
+ example: CPU_ON[{cpu}]
+
+ color: The color of the entry block in the timeline. The default color is
+ transparent, so the entry shares the phase color. The color is an
+ html color string, either a word, or an RGB.
+
+ example: [color=#CC00CC]
+
+ arglist: A list of arguments from registers/stack addresses. See URL:
+ https://www.kernel.org/doc/Documentation/trace/kprobetrace.txt
+
+ example: cpu=%di:s32
+
+ Here is a full example entry. It displays cpu resume calls in the timeline
+ in orange. They will appear as CPU_ON[0], CPU_ON[1], etc.
+
+ [timeline_functions_x86_64]
+ _cpu_up: CPU_ON[{cpu}] cpu=%di:s32 [color=orange]
+
+
+Adding or Editing Dev Mode Timeline Source Functions
+____________________________________________________
+
+ In dev mode, the tool uses an array of function names to monitor source
+ execution within the timeline entries.
+
+ The function calls are displayed inside the main device/call blocks in the
+ timeline. However, if a function call is not within a main timeline event,
+ it will spawn an entirely new event named after the caller's kernel thread.
+ These asynchronous kernel threads will populate in a separate section
+ beneath the main device/call section.
+
+ The tool has a set of hard coded calls which focus on the most common use
+ cases: msleep, udelay, schedule_timeout, mutex_lock_slowpath, etc. These are
+ the functions that add a hardcoded time delay to the suspend/resume path.
+ The tool also includes some common functions native to important
+ subsystems: ata, i915, and ACPI, etc.
+
+ It is possible to add new function calls to the dev timeline by adding them
+ to the config. It's also possible to copy the internal dev timeline
+ functions into the config so that you can override and edit them. Place them
+ in the dev_timeline_functions_ARCH section with the name of your architecture
+ appended. i.e. for x86_64: [dev_timeline_functions_x86_64]
+
+ Use the override-dev-timeline-functions option if you only want to use your
+ custom calls, or leave it false to append them to the internal ones.
+
+ The format is the same as the timeline_functions_x86_64 section. It's a
+ list of functions (set using kprobes) which use both symbol data and function
+ arg data. The args are pulled directly from the stack using this
+ architecture's registers and stack formatting. Each entry can include up
+ to four pieces of info: The function name, a format string, an argument list,
+ and a color. But only the function name is required.
+
+ For a full example config, see config/custom-timeline-functions.cfg. It pulls
+ all the internal dev timeline functions into the config and allows you to edit
+ them.
+
+ Here is a full example entry. It displays the ATA port reset calls as
+ ataN_port_reset in the timeline. This is where most of the SATA disk resume
+ time goes, so it can be helpful to see the low level call.
+
+ [dev_timeline_functions_x86_64]
+ ata_eh_recover: ata{port}_port_reset port=+36(%di):s32 [color=#CC00CC]
+
+
+Verifying your custom functions
+_______________________________
+
+ Once you have a set of functions (kprobes) defined, it can be useful to
+ perform a quick check to see if you formatted them correctly and if the system
+ actually supports them. To do this, run the tool with your config file
+ and the -status option. The tool will go through all the kprobes (both
+ custom and internal if you haven't overridden them) and actually attempts
+ to set them in ftrace. It will then print out success or fail for you.
+
+ Note that kprobes which don't actually exist in the kernel won't stop the
+ tool, they just wont show up.
+
+ For example:
+
+ sudo ./sleepgraph.py -config config/custom-timeline-functions.cfg -status
+ Checking this system (myhostname)...
+ have root access: YES
+ is sysfs mounted: YES
+ is "mem" a valid power mode: YES
+ is ftrace supported: YES
+ are kprobes supported: YES
+ timeline data source: FTRACE (all trace events found)
+ is rtcwake supported: YES
+ verifying timeline kprobes work:
+ _cpu_down: YES
+ _cpu_up: YES
+ acpi_pm_finish: YES
+ acpi_pm_prepare: YES
+ freeze_kernel_threads: YES
+ freeze_processes: YES
+ sys_sync: YES
+ thaw_processes: YES
+ verifying dev kprobes work:
+ __const_udelay: YES
+ __mutex_lock_slowpath: YES
+ acpi_os_stall: YES
+ acpi_ps_parse_aml: YES
+ intel_opregion_init: NO
+ intel_opregion_register: NO
+ intel_opregion_setup: NO
+ msleep: YES
+ schedule_timeout: YES
+ schedule_timeout_uninterruptible: YES
+ usleep_range: YES
+
+
+------------------------------------------------------------------
+| TESTING ON CONSUMER LINUX OPERATING SYSTEMS |
+------------------------------------------------------------------
+
+Android
+_______
+
+ The easiest way to execute on an android device is to run the android.sh
+ script on the device, then pull the ftrace log back to the host and run
+ sleepgraph.py on it.
+
+ Here are the steps:
+
+ [download and install the tool on the device]
+
+ host%> wget https://raw.githubusercontent.com/intel/pm-graph/master/tools/android.sh
+ host%> adb connect 192.168.1.6
+ host%> adb root
+ # push the script to a writeable location
+ host%> adb push android.sh /sdcard/
+
+ [check whether the tool will run on your device]
+
+ host%> adb shell
+ dev%> cd /sdcard
+ dev%> sh android.sh status
+ host : asus_t100
+ kernel : 3.14.0-i386-dirty
+ modes : freeze mem
+ rtcwake : supported
+ ftrace : supported
+ trace events {
+ suspend_resume: found
+ device_pm_callback_end: found
+ device_pm_callback_start: found
+ }
+ # the above is what you see on a system that's properly patched
+
+ [execute the suspend]
+
+ # NOTE: The suspend will only work if the screen isn't timed out,
+ # so you have to press some keys first to wake it up b4 suspend)
+ dev%> sh android.sh suspend mem
+ ------------------------------------
+ Suspend/Resume timing test initiated
+ ------------------------------------
+ hostname : asus_t100
+ kernel : 3.14.0-i386-dirty
+ mode : mem
+ ftrace out : /mnt/shell/emulated/0/ftrace.txt
+ dmesg out : /mnt/shell/emulated/0/dmesg.txt
+ log file : /mnt/shell/emulated/0/log.txt
+ ------------------------------------
+ INITIALIZING FTRACE........DONE
+ STARTING FTRACE
+ SUSPEND START @ 21:24:02 (rtcwake in 10 seconds)
+ <adb connection will now terminate>
+
+ [retrieve the data from the device]
+
+ # I find that you have to actually kill the adb process and
+ # reconnect sometimes in order for the connection to work post-suspend
+ host%> adb connect 192.168.1.6
+ # (required) get the ftrace data, this is the most important piece
+ host%> adb pull /sdcard/ftrace.txt
+ # (optional) get the dmesg data, this is for debugging
+ host%> adb pull /sdcard/dmesg.txt
+ # (optional) get the log, which just lists some test times for comparison
+ host%> adb pull /sdcard/log.txt
+
+ [create an output html file using sleepgraph.py]
+
+ host%> sleepgraph.py -ftrace ftrace.txt
+
+ You should now have an output.html with the android data, enjoy!
diff --git a/tools/power/pm-graph/bootgraph.py b/tools/power/pm-graph/bootgraph.py
index 6dae57041537..d3b99a1e92d6 100755
--- a/tools/power/pm-graph/bootgraph.py
+++ b/tools/power/pm-graph/bootgraph.py
@@ -1,4 +1,5 @@
-#!/usr/bin/python2
+#!/usr/bin/python
+# SPDX-License-Identifier: GPL-2.0-only
#
# Tool for analyzing boot timing
# Copyright (c) 2013, Intel Corporation.
@@ -89,7 +90,7 @@ class SystemValues(aslib.SystemValues):
cmdline = 'initcall_debug log_buf_len=32M'
if self.useftrace:
if self.cpucount > 0:
- bs = min(self.memtotal / 2, 2*1024*1024) / self.cpucount
+ bs = min(self.memtotal // 2, 2*1024*1024) // self.cpucount
else:
bs = 131072
cmdline += ' trace_buf_size=%dK trace_clock=global '\
@@ -145,13 +146,13 @@ class SystemValues(aslib.SystemValues):
if arg in ['-h', '-v', '-cronjob', '-reboot', '-verbose']:
continue
elif arg in ['-o', '-dmesg', '-ftrace', '-func']:
- args.next()
+ next(args)
continue
elif arg == '-result':
- cmdline += ' %s "%s"' % (arg, os.path.abspath(args.next()))
+ cmdline += ' %s "%s"' % (arg, os.path.abspath(next(args)))
continue
elif arg == '-cgskip':
- file = self.configFile(args.next())
+ file = self.configFile(next(args))
cmdline += ' %s "%s"' % (arg, os.path.abspath(file))
continue
cmdline += ' '+arg
@@ -300,11 +301,11 @@ def parseKernelLog():
tp = aslib.TestProps()
devtemp = dict()
if(sysvals.dmesgfile):
- lf = open(sysvals.dmesgfile, 'r')
+ lf = open(sysvals.dmesgfile, 'rb')
else:
lf = Popen('dmesg', stdout=PIPE).stdout
for line in lf:
- line = line.replace('\r\n', '')
+ line = aslib.ascii(line).replace('\r\n', '')
# grab the stamp and sysinfo
if re.match(tp.stampfmt, line):
tp.stamp = line
@@ -333,9 +334,9 @@ def parseKernelLog():
if(not sysvals.stamp['kernel']):
sysvals.stamp['kernel'] = sysvals.kernelVersion(msg)
continue
- m = re.match('.* setting system clock to (?P<t>.*) UTC.*', msg)
+ m = re.match('.* setting system clock to (?P<d>[0-9\-]*)[ A-Z](?P<t>[0-9:]*) UTC.*', msg)
if(m):
- bt = datetime.strptime(m.group('t'), '%Y-%m-%d %H:%M:%S')
+ bt = datetime.strptime(m.group('d')+' '+m.group('t'), '%Y-%m-%d %H:%M:%S')
bt = bt - timedelta(seconds=int(ktime))
data.boottime = bt.strftime('%Y-%m-%d_%H:%M:%S')
sysvals.stamp['time'] = bt.strftime('%B %d %Y, %I:%M:%S %p')
@@ -356,7 +357,7 @@ def parseKernelLog():
data.newAction(phase, f, pid, start, ktime, int(r), int(t))
del devtemp[f]
continue
- if(re.match('^Freeing unused kernel memory.*', msg)):
+ if(re.match('^Freeing unused kernel .*', msg)):
data.tUserMode = ktime
data.dmesg['kernel']['end'] = ktime
data.dmesg['user']['start'] = ktime
@@ -657,7 +658,7 @@ def createBootGraph(data):
statinfo += '\t"%s": [\n\t\t"%s",\n' % (n, devstats[n]['info'])
if 'fstat' in devstats[n]:
funcs = devstats[n]['fstat']
- for f in sorted(funcs, key=funcs.get, reverse=True):
+ for f in sorted(funcs, key=lambda k:(funcs[k], k), reverse=True):
if funcs[f][0] < 0.01 and len(funcs) > 10:
break
statinfo += '\t\t"%f|%s|%d",\n' % (funcs[f][0], f, funcs[f][1])
@@ -737,7 +738,7 @@ def updateCron(restore=False):
op.write('@reboot python %s\n' % sysvals.cronjobCmdString())
op.close()
res = call([cmd, cronfile])
- except Exception, e:
+ except Exception as e:
pprint('Exception: %s' % str(e))
shutil.move(backfile, cronfile)
res = -1
@@ -753,7 +754,7 @@ def updateGrub(restore=False):
try:
call(sysvals.blexec, stderr=PIPE, stdout=PIPE,
env={'PATH': '.:/sbin:/usr/sbin:/usr/bin:/sbin:/bin'})
- except Exception, e:
+ except Exception as e:
pprint('Exception: %s\n' % str(e))
return
# extract the option and create a grub config without it
@@ -800,7 +801,7 @@ def updateGrub(restore=False):
op.close()
res = call(sysvals.blexec)
os.remove(grubfile)
- except Exception, e:
+ except Exception as e:
pprint('Exception: %s' % str(e))
res = -1
# cleanup
@@ -874,6 +875,7 @@ def printHelp():
'Other commands:\n'\
' -flistall Print all functions capable of being captured in ftrace\n'\
' -sysinfo Print out system info extracted from BIOS\n'\
+ ' -which exec Print an executable path, should function even without PATH\n'\
' [redo]\n'\
' -dmesg file Create HTML output using dmesg input (used with -ftrace)\n'\
' -ftrace file Create HTML output using ftrace input (used with -dmesg)\n'\
@@ -915,13 +917,13 @@ if __name__ == '__main__':
sysvals.mincglen = aslib.getArgFloat('-mincg', args, 0.0, 10000.0)
elif(arg == '-cgfilter'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No callgraph functions supplied', True)
sysvals.setCallgraphFilter(val)
elif(arg == '-cgskip'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No file supplied', True)
if val.lower() in switchoff:
@@ -932,7 +934,7 @@ if __name__ == '__main__':
doError('%s does not exist' % cgskip)
elif(arg == '-bl'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No boot loader name supplied', True)
if val.lower() not in ['grub']:
@@ -945,7 +947,7 @@ if __name__ == '__main__':
sysvals.max_graph_depth = aslib.getArgInt('-maxdepth', args, 0, 1000)
elif(arg == '-func'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No filter functions supplied', True)
sysvals.useftrace = True
@@ -954,7 +956,7 @@ if __name__ == '__main__':
sysvals.setGraphFilter(val)
elif(arg == '-ftrace'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No ftrace file supplied', True)
if(os.path.exists(val) == False):
@@ -967,7 +969,7 @@ if __name__ == '__main__':
sysvals.cgexp = True
elif(arg == '-dmesg'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No dmesg file supplied', True)
if(os.path.exists(val) == False):
@@ -976,13 +978,13 @@ if __name__ == '__main__':
sysvals.dmesgfile = val
elif(arg == '-o'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No subdirectory name supplied', True)
sysvals.testdir = sysvals.setOutputFolder(val)
elif(arg == '-result'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No result file supplied', True)
sysvals.result = val
@@ -994,6 +996,17 @@ if __name__ == '__main__':
# remaining options are only for cron job use
elif(arg == '-cronjob'):
sysvals.iscronjob = True
+ elif(arg == '-which'):
+ try:
+ val = next(args)
+ except:
+ doError('No executable supplied', True)
+ out = sysvals.getExec(val)
+ if not out:
+ print('%s not found' % val)
+ sys.exit(1)
+ print(out)
+ sys.exit(0)
else:
doError('Invalid argument: '+arg, True)
@@ -1016,7 +1029,7 @@ if __name__ == '__main__':
updateKernelParams()
elif cmd == 'flistall':
for f in sysvals.getBootFtraceFilterFunctions():
- print f
+ print(f)
elif cmd == 'checkbl':
sysvals.getBootLoader()
pprint('Boot Loader: %s\n%s' % (sysvals.bootloader, sysvals.blexec))
diff --git a/tools/power/pm-graph/config/example.cfg b/tools/power/pm-graph/config/example.cfg
index 05b2efb9bb54..1ef3eb9383fa 100644
--- a/tools/power/pm-graph/config/example.cfg
+++ b/tools/power/pm-graph/config/example.cfg
@@ -98,12 +98,34 @@ postdelay: 0
# graph only devices longer than min in the timeline (default: 0.001 ms)
mindev: 0.001
+# Call Loop Max Gap (dev mode only)
+# merge loops of the same call if each is less than maxgap apart (def: 100us)
+callloop-maxgap: 0.0001
+
+# Call Loop Max Length (dev mode only)
+# merge loops of the same call if each is less than maxlen in length (def: 5ms)
+callloop-maxlen: 0.005
+
+# Override default timeline entries:
+# Do not use the internal default functions for timeline entries (def: false)
+# Set this to true if you intend to only use the ones defined in the config
+override-timeline-functions: true
+
+# Override default dev timeline entries:
+# Do not use the internal default functions for dev timeline entries (def: false)
+# Set this to true if you intend to only use the ones defined in the config
+override-dev-timeline-functions: true
+
# ---- Debug Options ----
# Callgraph
# gather detailed ftrace callgraph data on all timeline events (default: false)
callgraph: false
+# Max graph depth
+# limit the callgraph trace to this depth (default: 0 = all)
+maxdepth: 2
+
# Callgraph phase filter
# Only enable callgraphs for one phase, i.e. resume_noirq (default: all)
cgphase: suspend
@@ -131,3 +153,7 @@ timeprec: 6
# Add kprobe functions to the timeline
# Add functions to the timeline from a text file (default: no-action)
# fadd: file.txt
+
+# Ftrace buffer size
+# Set trace buffer size to N kilo-bytes (default: all of free memory up to 3GB)
+# bufsize: 1000
diff --git a/tools/power/pm-graph/sleepgraph.8 b/tools/power/pm-graph/sleepgraph.8
index 24a2e7d0ae63..43aee64316df 100644
--- a/tools/power/pm-graph/sleepgraph.8
+++ b/tools/power/pm-graph/sleepgraph.8
@@ -53,6 +53,11 @@ disable rtcwake and require a user keypress to resume.
Add the dmesg and ftrace logs to the html output. They will be viewable by
clicking buttons in the timeline.
.TP
+\fB-noturbostat\fR
+By default, if turbostat is found and the requested mode is freeze, sleepgraph
+will execute the suspend via turbostat and collect data in the timeline log.
+This option disables the use of turbostat.
+.TP
\fB-result \fIfile\fR
Export a results table to a text file for parsing.
.TP
@@ -121,6 +126,10 @@ be created in a new subdirectory with a summary page: suspend-xN-{date}-{time}.
Use ftrace to create device callgraphs (default: disabled). This can produce
very large outputs, i.e. 10MB - 100MB.
.TP
+\fB-ftop\fR
+Use ftrace on the top level call: "suspend_devices_and_enter" only (default: disabled).
+This option implies -f and creates a single callgraph covering all of suspend/resume.
+.TP
\fB-maxdepth \fIlevel\fR
limit the callgraph trace depth to \fIlevel\fR (default: 0=all). This is
the best way to limit the output size when using callgraphs via -f.
@@ -138,8 +147,8 @@ which are barely visible in the timeline.
The value is a float: e.g. 0.001 represents 1 us.
.TP
\fB-cgfilter \fI"func1,func2,..."\fR
-Reduce callgraph output in the timeline by limiting it to a list of calls. The
-argument can be a single function name or a comma delimited list.
+Reduce callgraph output in the timeline by limiting it certain devices. The
+argument can be a single device name or a comma delimited list.
(default: none)
.TP
\fB-cgskip \fIfile\fR
@@ -183,6 +192,9 @@ Print out the contents of the ACPI Firmware Performance Data Table.
\fB-battery\fR
Print out battery status and current charge.
.TP
+\fB-wifi\fR
+Print out wifi status and connection details.
+.TP
\fB-xon/-xoff/-xstandby/-xsuspend\fR
Test xset by attempting to switch the display to the given mode. This
is the same command which will be issued by \fB-display \fImode\fR.
diff --git a/tools/power/pm-graph/sleepgraph.py b/tools/power/pm-graph/sleepgraph.py
index 52618f3444d4..f7d1c1f62f86 100755
--- a/tools/power/pm-graph/sleepgraph.py
+++ b/tools/power/pm-graph/sleepgraph.py
@@ -1,4 +1,5 @@
-#!/usr/bin/python2
+#!/usr/bin/python
+# SPDX-License-Identifier: GPL-2.0-only
#
# Tool for analyzing suspend/resume timing
# Copyright (c) 2013, Intel Corporation.
@@ -17,9 +18,9 @@
#
# Links:
# Home Page
-# https://01.org/suspendresume
+# https://01.org/pm-graph
# Source repo
-# git@github.com:01org/pm-graph
+# git@github.com:intel/pm-graph
#
# Description:
# This tool is designed to assist kernel and OS developers in optimizing
@@ -32,6 +33,7 @@
# viewed in firefox or chrome.
#
# The following kernel build options are required:
+# CONFIG_DEVMEM=y
# CONFIG_PM_DEBUG=y
# CONFIG_PM_SLEEP_DEBUG=y
# CONFIG_FTRACE=y
@@ -55,17 +57,22 @@ import string
import re
import platform
import signal
+import codecs
from datetime import datetime
import struct
-import ConfigParser
+import configparser
import gzip
from threading import Thread
from subprocess import call, Popen, PIPE
+import base64
def pprint(msg):
print(msg)
sys.stdout.flush()
+def ascii(text):
+ return text.decode('ascii', 'ignore')
+
# ----------------- CLASSES --------------------
# Class: SystemValues
@@ -74,7 +81,7 @@ def pprint(msg):
# store system values and test parameters
class SystemValues:
title = 'SleepGraph'
- version = '5.2'
+ version = '5.5'
ansi = False
rs = 0
display = ''
@@ -82,8 +89,9 @@ class SystemValues:
sync = False
verbose = False
testlog = True
- dmesglog = False
+ dmesglog = True
ftracelog = False
+ tstat = True
mindevlen = 0.0
mincglen = 0.0
cgphase = ''
@@ -107,6 +115,8 @@ class SystemValues:
pmdpath = '/sys/power/pm_debug_messages'
traceevents = [
'suspend_resume',
+ 'wakeup_source_activate',
+ 'wakeup_source_deactivate',
'device_pm_callback_end',
'device_pm_callback_start'
]
@@ -138,6 +148,8 @@ class SystemValues:
x2delay = 0
skiphtml = False
usecallgraph = False
+ ftopfunc = 'suspend_devices_and_enter'
+ ftop = False
usetraceevents = False
usetracemarkers = True
usekprobes = True
@@ -148,6 +160,7 @@ class SystemValues:
devdump = False
mixedphaseheight = True
devprops = dict()
+ platinfo = []
predelay = 0
postdelay = 0
pmdebug = ''
@@ -166,6 +179,13 @@ class SystemValues:
'acpi_hibernation_leave': {},
'acpi_pm_freeze': {},
'acpi_pm_thaw': {},
+ 'acpi_s2idle_end': {},
+ 'acpi_s2idle_sync': {},
+ 'acpi_s2idle_begin': {},
+ 'acpi_s2idle_prepare': {},
+ 'acpi_s2idle_wake': {},
+ 'acpi_s2idle_wakeup': {},
+ 'acpi_s2idle_restore': {},
'hibernate_preallocate_memory': {},
'create_basic_memory_bitmaps': {},
'swsusp_write': {},
@@ -199,9 +219,14 @@ class SystemValues:
'usleep_range': { 'args_x86_64': {'min':'%di:s32', 'max':'%si:s32'}, 'ub': 1 },
'mutex_lock_slowpath': { 'func':'__mutex_lock_slowpath', 'ub': 1 },
'acpi_os_stall': {'ub': 1},
+ 'rt_mutex_slowlock': {'ub': 1},
# ACPI
'acpi_resume_power_resources': {},
- 'acpi_ps_parse_aml': {},
+ 'acpi_ps_execute_method': { 'args_x86_64': {
+ 'fullpath':'+0(+40(%di)):string',
+ }},
+ # mei_me
+ 'mei_reset': {},
# filesystem
'ext4_sync_fs': {},
# 80211
@@ -250,6 +275,7 @@ class SystemValues:
timeformat = '%.3f'
cmdline = '%s %s' % \
(os.path.basename(sys.argv[0]), ' '.join(sys.argv[1:]))
+ kparams = ''
sudouser = ''
def __init__(self):
self.archargs = 'args_'+platform.machine()
@@ -311,13 +337,20 @@ class SystemValues:
sys.exit(1)
return False
def getExec(self, cmd):
- dirlist = ['/sbin', '/bin', '/usr/sbin', '/usr/bin',
- '/usr/local/sbin', '/usr/local/bin']
- for path in dirlist:
+ try:
+ fp = Popen(['which', cmd], stdout=PIPE, stderr=PIPE).stdout
+ out = ascii(fp.read()).strip()
+ fp.close()
+ except:
+ out = ''
+ if out:
+ return out
+ for path in ['/sbin', '/bin', '/usr/sbin', '/usr/bin',
+ '/usr/local/sbin', '/usr/local/bin']:
cmdfull = os.path.join(path, cmd)
if os.path.exists(cmdfull):
return cmdfull
- return ''
+ return out
def setPrecision(self, num):
if num < 0 or num > 6:
return
@@ -328,6 +361,7 @@ class SystemValues:
args['date'] = n.strftime('%y%m%d')
args['time'] = n.strftime('%H%M%S')
args['hostname'] = args['host'] = self.hostname
+ args['mode'] = self.suspendmode
return value.format(**args)
def setOutputFile(self):
if self.dmesgfile != '':
@@ -339,21 +373,28 @@ class SystemValues:
if(m):
self.htmlfile = m.group('name')+'.html'
def systemInfo(self, info):
- p = c = m = b = ''
+ p = m = ''
if 'baseboard-manufacturer' in info:
m = info['baseboard-manufacturer']
elif 'system-manufacturer' in info:
m = info['system-manufacturer']
- if 'baseboard-product-name' in info:
- p = info['baseboard-product-name']
- elif 'system-product-name' in info:
+ if 'system-product-name' in info:
p = info['system-product-name']
- if 'processor-version' in info:
- c = info['processor-version']
- if 'bios-version' in info:
- b = info['bios-version']
- self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | numcpu:%d | memsz:%d | memfr:%d' % \
- (m, p, c, b, self.cpucount, self.memtotal, self.memfree)
+ elif 'baseboard-product-name' in info:
+ p = info['baseboard-product-name']
+ if m[:5].lower() == 'intel' and 'baseboard-product-name' in info:
+ p = info['baseboard-product-name']
+ c = info['processor-version'] if 'processor-version' in info else ''
+ b = info['bios-version'] if 'bios-version' in info else ''
+ r = info['bios-release-date'] if 'bios-release-date' in info else ''
+ self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | biosdate:%s | numcpu:%d | memsz:%d | memfr:%d' % \
+ (m, p, c, b, r, self.cpucount, self.memtotal, self.memfree)
+ try:
+ kcmd = open('/proc/cmdline', 'r').read().strip()
+ except:
+ kcmd = ''
+ if kcmd:
+ self.sysstamp += '\n# kparams | %s' % kcmd
def printSystemInfo(self, fatal=False):
self.rootCheck(True)
out = dmidecode(self.mempath, fatal)
@@ -361,10 +402,10 @@ class SystemValues:
return
fmt = '%-24s: %s'
for name in sorted(out):
- print fmt % (name, out[name])
- print fmt % ('cpucount', ('%d' % self.cpucount))
- print fmt % ('memtotal', ('%d kB' % self.memtotal))
- print fmt % ('memfree', ('%d kB' % self.memfree))
+ print(fmt % (name, out[name]))
+ print(fmt % ('cpucount', ('%d' % self.cpucount)))
+ print(fmt % ('memtotal', ('%d kB' % self.memtotal)))
+ print(fmt % ('memfree', ('%d kB' % self.memfree)))
def cpuInfo(self):
self.cpucount = 0
fp = open('/proc/cpuinfo', 'r')
@@ -384,7 +425,7 @@ class SystemValues:
def initTestOutput(self, name):
self.prefix = self.hostname
v = open('/proc/version', 'r').read().strip()
- kver = string.split(v)[2]
+ kver = v.split()[2]
fmt = name+'-%m%d%y-%H%M%S'
testtime = datetime.now().strftime(fmt)
self.teststamp = \
@@ -399,7 +440,7 @@ class SystemValues:
self.htmlfile = \
self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html'
if not os.path.isdir(self.testdir):
- os.mkdir(self.testdir)
+ os.makedirs(self.testdir)
def getValueList(self, value):
out = []
for i in value.split(','):
@@ -410,6 +451,12 @@ class SystemValues:
self.devicefilter = self.getValueList(value)
def setCallgraphFilter(self, value):
self.cgfilter = self.getValueList(value)
+ def skipKprobes(self, value):
+ for k in self.getValueList(value):
+ if k in self.tracefuncs:
+ del self.tracefuncs[k]
+ if k in self.dev_tracefuncs:
+ del self.dev_tracefuncs[k]
def setCallgraphBlacklist(self, file):
self.cgblacklist = self.listFromFile(file)
def rtcWakeAlarmOn(self):
@@ -429,7 +476,7 @@ class SystemValues:
fp = Popen('dmesg', stdout=PIPE).stdout
ktime = '0'
for line in fp:
- line = line.replace('\r\n', '')
+ line = ascii(line).replace('\r\n', '')
idx = line.find('[')
if idx > 1:
line = line[idx:]
@@ -443,7 +490,7 @@ class SystemValues:
# store all new dmesg lines since initdmesg was called
fp = Popen('dmesg', stdout=PIPE).stdout
for line in fp:
- line = line.replace('\r\n', '')
+ line = ascii(line).replace('\r\n', '')
idx = line.find('[')
if idx > 1:
line = line[idx:]
@@ -475,13 +522,13 @@ class SystemValues:
call('cat '+self.tpath+'available_filter_functions', shell=True)
return
master = self.listFromFile(self.tpath+'available_filter_functions')
- for i in self.tracefuncs:
+ for i in sorted(self.tracefuncs):
if 'func' in self.tracefuncs[i]:
i = self.tracefuncs[i]['func']
if i in master:
- print i
+ print(i)
else:
- print self.colorText(i)
+ print(self.colorText(i))
def setFtraceFilterFunctions(self, list):
master = self.listFromFile(self.tpath+'available_filter_functions')
flist = ''
@@ -602,7 +649,7 @@ class SystemValues:
self.fsetVal(kprobeevents, 'kprobe_events')
if output:
check = self.fgetVal('kprobe_events')
- linesack = (len(check.split('\n')) - 1) / 2
+ linesack = (len(check.split('\n')) - 1) // 2
pprint(' kprobe functions enabled: %d/%d' % (linesack, linesout))
self.fsetVal('1', 'events/kprobes/enable')
def testKprobe(self, kname, kprobe):
@@ -620,19 +667,19 @@ class SystemValues:
if linesack < linesout:
return False
return True
- def setVal(self, val, file, mode='w'):
+ def setVal(self, val, file):
if not os.path.exists(file):
return False
try:
- fp = open(file, mode, 0)
- fp.write(val)
+ fp = open(file, 'wb', 0)
+ fp.write(val.encode())
fp.flush()
fp.close()
except:
return False
return True
- def fsetVal(self, val, path, mode='w'):
- return self.setVal(val, self.tpath+path, mode)
+ def fsetVal(self, val, path):
+ return self.setVal(val, self.tpath+path)
def getVal(self, file):
res = ''
if not os.path.exists(file):
@@ -688,11 +735,12 @@ class SystemValues:
if self.bufsize > 0:
tgtsize = self.bufsize
elif self.usecallgraph or self.usedevsrc:
- bmax = (1*1024*1024) if self.suspendmode == 'disk' else (3*1024*1024)
+ bmax = (1*1024*1024) if self.suspendmode in ['disk', 'command'] \
+ else (3*1024*1024)
tgtsize = min(self.memfree, bmax)
else:
tgtsize = 65536
- while not self.fsetVal('%d' % (tgtsize / cpus), 'buffer_size_kb'):
+ while not self.fsetVal('%d' % (tgtsize // cpus), 'buffer_size_kb'):
# if the size failed to set, lower it and keep trying
tgtsize -= 65536
if tgtsize < 65536:
@@ -723,7 +771,10 @@ class SystemValues:
cf.append(self.tracefuncs[fn]['func'])
else:
cf.append(fn)
- self.setFtraceFilterFunctions(cf)
+ if self.ftop:
+ self.setFtraceFilterFunctions([self.ftopfunc])
+ else:
+ self.setFtraceFilterFunctions(cf)
# initialize the kprobe trace
elif self.usekprobes:
for name in self.tracefuncs:
@@ -776,9 +827,21 @@ class SystemValues:
fw = test['fw']
if(fw):
fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1]))
+ if 'mcelog' in test:
+ fp.write('# mcelog %s\n' % test['mcelog'])
+ if 'turbo' in test:
+ fp.write('# turbostat %s\n' % test['turbo'])
if 'bat' in test:
(a1, c1), (a2, c2) = test['bat']
fp.write('# battery %s %d %s %d\n' % (a1, c1, a2, c2))
+ if 'wifi' in test:
+ wstr = []
+ for wifi in test['wifi']:
+ tmp = []
+ for key in sorted(wifi):
+ tmp.append('%s:%s' % (key, wifi[key]))
+ wstr.append('|'.join(tmp))
+ fp.write('# wifi %s\n' % (','.join(wstr)))
if test['error'] or len(testdata) > 1:
fp.write('# enter_sleep_error %s\n' % test['error'])
return fp
@@ -821,14 +884,244 @@ class SystemValues:
isgz = self.gzip
if mode == 'r':
try:
- with gzip.open(filename, mode+'b') as fp:
+ with gzip.open(filename, mode+'t') as fp:
test = fp.read(64)
isgz = True
except:
isgz = False
if isgz:
- return gzip.open(filename, mode+'b')
+ return gzip.open(filename, mode+'t')
return open(filename, mode)
+ def b64unzip(self, data):
+ try:
+ out = codecs.decode(base64.b64decode(data), 'zlib').decode()
+ except:
+ out = data
+ return out
+ def b64zip(self, data):
+ out = base64.b64encode(codecs.encode(data.encode(), 'zlib')).decode()
+ return out
+ def mcelog(self, clear=False):
+ cmd = self.getExec('mcelog')
+ if not cmd:
+ return ''
+ if clear:
+ call(cmd+' > /dev/null 2>&1', shell=True)
+ return ''
+ try:
+ fp = Popen([cmd], stdout=PIPE, stderr=PIPE).stdout
+ out = ascii(fp.read()).strip()
+ fp.close()
+ except:
+ return ''
+ if not out:
+ return ''
+ return self.b64zip(out)
+ def platforminfo(self):
+ # add platform info on to a completed ftrace file
+ if not os.path.exists(self.ftracefile):
+ return False
+ footer = '#\n'
+
+ # add test command string line if need be
+ if self.suspendmode == 'command' and self.testcommand:
+ footer += '# platform-testcmd: %s\n' % (self.testcommand)
+
+ # get a list of target devices from the ftrace file
+ props = dict()
+ tp = TestProps()
+ tf = self.openlog(self.ftracefile, 'r')
+ for line in tf:
+ # determine the trace data type (required for further parsing)
+ m = re.match(tp.tracertypefmt, line)
+ if(m):
+ tp.setTracerType(m.group('t'))
+ continue
+ # parse only valid lines, if this is not one move on
+ m = re.match(tp.ftrace_line_fmt, line)
+ if(not m or 'device_pm_callback_start' not in line):
+ continue
+ m = re.match('.*: (?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*', m.group('msg'));
+ if(not m):
+ continue
+ dev = m.group('d')
+ if dev not in props:
+ props[dev] = DevProps()
+ tf.close()
+
+ # now get the syspath for each target device
+ for dirname, dirnames, filenames in os.walk('/sys/devices'):
+ if(re.match('.*/power', dirname) and 'async' in filenames):
+ dev = dirname.split('/')[-2]
+ if dev in props and (not props[dev].syspath or len(dirname) < len(props[dev].syspath)):
+ props[dev].syspath = dirname[:-6]
+
+ # now fill in the properties for our target devices
+ for dev in sorted(props):
+ dirname = props[dev].syspath
+ if not dirname or not os.path.exists(dirname):
+ continue
+ with open(dirname+'/power/async') as fp:
+ text = fp.read()
+ props[dev].isasync = False
+ if 'enabled' in text:
+ props[dev].isasync = True
+ fields = os.listdir(dirname)
+ if 'product' in fields:
+ with open(dirname+'/product', 'rb') as fp:
+ props[dev].altname = ascii(fp.read())
+ elif 'name' in fields:
+ with open(dirname+'/name', 'rb') as fp:
+ props[dev].altname = ascii(fp.read())
+ elif 'model' in fields:
+ with open(dirname+'/model', 'rb') as fp:
+ props[dev].altname = ascii(fp.read())
+ elif 'description' in fields:
+ with open(dirname+'/description', 'rb') as fp:
+ props[dev].altname = ascii(fp.read())
+ elif 'id' in fields:
+ with open(dirname+'/id', 'rb') as fp:
+ props[dev].altname = ascii(fp.read())
+ elif 'idVendor' in fields and 'idProduct' in fields:
+ idv, idp = '', ''
+ with open(dirname+'/idVendor', 'rb') as fp:
+ idv = ascii(fp.read()).strip()
+ with open(dirname+'/idProduct', 'rb') as fp:
+ idp = ascii(fp.read()).strip()
+ props[dev].altname = '%s:%s' % (idv, idp)
+ if props[dev].altname:
+ out = props[dev].altname.strip().replace('\n', ' ')\
+ .replace(',', ' ').replace(';', ' ')
+ props[dev].altname = out
+
+ # add a devinfo line to the bottom of ftrace
+ out = ''
+ for dev in sorted(props):
+ out += props[dev].out(dev)
+ footer += '# platform-devinfo: %s\n' % self.b64zip(out)
+
+ # add a line for each of these commands with their outputs
+ cmds = [
+ ['pcidevices', 'lspci', '-tv'],
+ ['interrupts', 'cat', '/proc/interrupts'],
+ ['gpecounts', 'sh', '-c', 'grep -v invalid /sys/firmware/acpi/interrupts/gpe*'],
+ ]
+ for cargs in cmds:
+ name = cargs[0]
+ cmdline = ' '.join(cargs[1:])
+ cmdpath = self.getExec(cargs[1])
+ if not cmdpath:
+ continue
+ cmd = [cmdpath] + cargs[2:]
+ try:
+ fp = Popen(cmd, stdout=PIPE, stderr=PIPE).stdout
+ info = ascii(fp.read()).strip()
+ fp.close()
+ except:
+ continue
+ if not info:
+ continue
+ footer += '# platform-%s: %s | %s\n' % (name, cmdline, self.b64zip(info))
+
+ with self.openlog(self.ftracefile, 'a') as fp:
+ fp.write(footer)
+ return True
+ def haveTurbostat(self):
+ if not self.tstat:
+ return False
+ cmd = self.getExec('turbostat')
+ if not cmd:
+ return False
+ fp = Popen([cmd, '-v'], stdout=PIPE, stderr=PIPE).stderr
+ out = ascii(fp.read()).strip()
+ fp.close()
+ if re.match('turbostat version [0-9\.]* .*', out):
+ sysvals.vprint(out)
+ return True
+ return False
+ def turbostat(self):
+ cmd = self.getExec('turbostat')
+ rawout = keyline = valline = ''
+ fullcmd = '%s -q -S echo freeze > %s' % (cmd, self.powerfile)
+ fp = Popen(['sh', '-c', fullcmd], stdout=PIPE, stderr=PIPE).stderr
+ for line in fp:
+ line = ascii(line)
+ rawout += line
+ if keyline and valline:
+ continue
+ if re.match('(?i)Avg_MHz.*', line):
+ keyline = line.strip().split()
+ elif keyline:
+ valline = line.strip().split()
+ fp.close()
+ if not keyline or not valline or len(keyline) != len(valline):
+ errmsg = 'unrecognized turbostat output:\n'+rawout.strip()
+ sysvals.vprint(errmsg)
+ if not sysvals.verbose:
+ pprint(errmsg)
+ return ''
+ if sysvals.verbose:
+ pprint(rawout.strip())
+ out = []
+ for key in keyline:
+ idx = keyline.index(key)
+ val = valline[idx]
+ out.append('%s=%s' % (key, val))
+ return '|'.join(out)
+ def checkWifi(self):
+ out = dict()
+ iwcmd, ifcmd = self.getExec('iwconfig'), self.getExec('ifconfig')
+ if not iwcmd or not ifcmd:
+ return out
+ fp = Popen(iwcmd, stdout=PIPE, stderr=PIPE).stdout
+ for line in fp:
+ m = re.match('(?P<dev>\S*) .* ESSID:(?P<ess>\S*)', ascii(line))
+ if not m:
+ continue
+ out['device'] = m.group('dev')
+ if '"' in m.group('ess'):
+ out['essid'] = m.group('ess').strip('"')
+ break
+ fp.close()
+ if 'device' in out:
+ fp = Popen([ifcmd, out['device']], stdout=PIPE, stderr=PIPE).stdout
+ for line in fp:
+ m = re.match('.* inet (?P<ip>[0-9\.]*)', ascii(line))
+ if m:
+ out['ip'] = m.group('ip')
+ break
+ fp.close()
+ return out
+ def errorSummary(self, errinfo, msg):
+ found = False
+ for entry in errinfo:
+ if re.match(entry['match'], msg):
+ entry['count'] += 1
+ if self.hostname not in entry['urls']:
+ entry['urls'][self.hostname] = [self.htmlfile]
+ elif self.htmlfile not in entry['urls'][self.hostname]:
+ entry['urls'][self.hostname].append(self.htmlfile)
+ found = True
+ break
+ if found:
+ return
+ arr = msg.split()
+ for j in range(len(arr)):
+ if re.match('^[0-9,\-\.]*$', arr[j]):
+ arr[j] = '[0-9,\-\.]*'
+ else:
+ arr[j] = arr[j]\
+ .replace('\\', '\\\\').replace(']', '\]').replace('[', '\[')\
+ .replace('.', '\.').replace('+', '\+').replace('*', '\*')\
+ .replace('(', '\(').replace(')', '\)')
+ mstr = ' '.join(arr)
+ entry = {
+ 'line': msg,
+ 'match': mstr,
+ 'count': 1,
+ 'urls': {self.hostname: [self.htmlfile]}
+ }
+ errinfo.append(entry)
sysvals = SystemValues()
switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0']
@@ -848,13 +1141,13 @@ class DevProps:
def __init__(self):
self.syspath = ''
self.altname = ''
- self.async = True
+ self.isasync = True
self.xtraclass = ''
self.xtrainfo = ''
def out(self, dev):
- return '%s,%s,%d;' % (dev, self.altname, self.async)
+ return '%s,%s,%d;' % (dev, self.altname, self.isasync)
def debug(self, dev):
- pprint('%s:\n\taltname = %s\n\t async = %s' % (dev, self.altname, self.async))
+ pprint('%s:\n\taltname = %s\n\t async = %s' % (dev, self.altname, self.isasync))
def altName(self, dev):
if not self.altname or self.altname == dev:
return dev
@@ -862,13 +1155,13 @@ class DevProps:
def xtraClass(self):
if self.xtraclass:
return ' '+self.xtraclass
- if not self.async:
+ if not self.isasync:
return ' sync'
return ''
def xtraInfo(self):
if self.xtraclass:
return ' '+self.xtraclass
- if self.async:
+ if self.isasync:
return ' async_device'
return ' sync_device'
@@ -923,7 +1216,14 @@ class Data:
'ERROR' : '.*ERROR.*',
'WARNING' : '.*WARNING.*',
'IRQ' : '.*genirq: .*',
- 'TASKFAIL': '.*Freezing of tasks failed.*',
+ 'TASKFAIL': '.*Freezing of tasks *.*',
+ 'ACPI' : '.*ACPI *(?P<b>[A-Za-z]*) *Error[: ].*',
+ 'DEVFAIL' : '.* failed to (?P<b>[a-z]*) async: .*',
+ 'DISKFULL': '.*No space left on device.*',
+ 'USBERR' : '.*usb .*device .*, error [0-9-]*',
+ 'ATAERR' : ' *ata[0-9\.]*: .*failed.*',
+ 'MEIERR' : ' *mei.*: .*failed.*',
+ 'TPMERR' : '(?i) *tpm *tpm[0-9]*: .*error.*',
}
def __init__(self, num):
idchar = 'abcdefghij'
@@ -941,6 +1241,9 @@ class Data:
self.outfile = ''
self.kerror = False
self.battery = 0
+ self.wifi = 0
+ self.turbostat = 0
+ self.mcelog = 0
self.enterfail = ''
self.currphase = ''
self.pstl = dict() # process timeline
@@ -956,7 +1259,7 @@ class Data:
return sorted(self.dmesg, key=lambda k:self.dmesg[k]['order'])
def initDevicegroups(self):
# called when phases are all finished being added
- for phase in self.dmesg.keys():
+ for phase in sorted(self.dmesg.keys()):
if '*' in phase:
p = phase.split('*')
pnew = '%s%d' % (p[0], len(p))
@@ -975,8 +1278,24 @@ class Data:
if len(plist) < 1:
return ''
return plist[-1]
+ def turbostatInfo(self):
+ tp = TestProps()
+ out = {'syslpi':'N/A','pkgpc10':'N/A'}
+ for line in self.dmesgtext:
+ m = re.match(tp.tstatfmt, line)
+ if not m:
+ continue
+ for i in m.group('t').split('|'):
+ if 'SYS%LPI' in i:
+ out['syslpi'] = i.split('=')[-1]+'%'
+ elif 'pc10' in i:
+ out['pkgpc10'] = i.split('=')[-1]+'%'
+ break
+ return out
def extractErrorInfo(self):
- lf = sysvals.openlog(sysvals.dmesgfile, 'r')
+ lf = self.dmesgtext
+ if len(self.dmesgtext) < 1 and sysvals.dmesgfile:
+ lf = sysvals.openlog(sysvals.dmesgfile, 'r')
i = 0
list = []
for line in lf:
@@ -991,16 +1310,19 @@ class Data:
msg = m.group('msg')
for err in self.errlist:
if re.match(self.errlist[err], msg):
- list.append((err, dir, t, i, i))
+ list.append((msg, err, dir, t, i, i))
self.kerror = True
break
- for e in list:
- type, dir, t, idx1, idx2 = e
+ msglist = []
+ for msg, type, dir, t, idx1, idx2 in list:
+ msglist.append(msg)
sysvals.vprint('kernel %s found in %s at %f' % (type, dir, t))
self.errorinfo[dir].append((type, t, idx1, idx2))
if self.kerror:
sysvals.dmesglog = True
- lf.close()
+ if len(self.dmesgtext) < 1 and sysvals.dmesgfile:
+ lf.close()
+ return msglist
def setStart(self, time):
self.start = time
def setEnd(self, time):
@@ -1259,16 +1581,7 @@ class Data:
return phase
def sortedDevices(self, phase):
list = self.dmesg[phase]['list']
- slist = []
- tmp = dict()
- for devname in list:
- dev = list[devname]
- if dev['length'] == 0:
- continue
- tmp[dev['start']] = devname
- for t in sorted(tmp):
- slist.append(tmp[t])
- return slist
+ return sorted(list, key=lambda k:list[k]['start'])
def fixupInitcalls(self, phase):
# if any calls never returned, clip them at system resume end
phaselist = self.dmesg[phase]['list']
@@ -1405,7 +1718,7 @@ class Data:
maxname = '%d' % self.maxDeviceNameSize(phase)
fmt = '%3d) %'+maxname+'s - %f - %f'
c = 1
- for name in devlist:
+ for name in sorted(devlist):
s = devlist[name]['start']
e = devlist[name]['end']
sysvals.vprint(fmt % (c, name, s, e))
@@ -1417,7 +1730,7 @@ class Data:
devlist = []
for phase in self.sortedPhases():
list = self.deviceChildren(devname, phase)
- for dev in list:
+ for dev in sorted(list):
if dev not in devlist:
devlist.append(dev)
return devlist
@@ -1457,16 +1770,16 @@ class Data:
def rootDeviceList(self):
# list of devices graphed
real = []
- for phase in self.dmesg:
+ for phase in self.sortedPhases():
list = self.dmesg[phase]['list']
- for dev in list:
+ for dev in sorted(list):
if list[dev]['pid'] >= 0 and dev not in real:
real.append(dev)
# list of top-most root devices
rootlist = []
- for phase in self.dmesg:
+ for phase in self.sortedPhases():
list = self.dmesg[phase]['list']
- for dev in list:
+ for dev in sorted(list):
pdev = list[dev]['par']
pid = list[dev]['pid']
if(pid < 0 or re.match('[0-9]*-[0-9]*\.[0-9]*[\.0-9]*\:[\.0-9]*$', pdev)):
@@ -1547,9 +1860,9 @@ class Data:
def createProcessUsageEvents(self):
# get an array of process names
proclist = []
- for t in self.pstl:
+ for t in sorted(self.pstl):
pslist = self.pstl[t]
- for ps in pslist:
+ for ps in sorted(pslist):
if ps not in proclist:
proclist.append(ps)
# get a list of data points for suspend and resume
@@ -1594,7 +1907,7 @@ class Data:
def debugPrint(self):
for p in self.sortedPhases():
list = self.dmesg[p]['list']
- for devname in list:
+ for devname in sorted(list):
dev = list[devname]
if 'ftrace' in dev:
dev['ftrace'].debugPrint(' [%s]' % devname)
@@ -2053,7 +2366,7 @@ class FTraceCallGraph:
if(data.dmesg[p]['start'] <= self.start and
self.start <= data.dmesg[p]['end']):
list = data.dmesg[p]['list']
- for devname in list:
+ for devname in sorted(list, key=lambda k:list[k]['start']):
dev = list[devname]
if(pid == dev['pid'] and
self.start <= dev['start'] and
@@ -2295,7 +2608,7 @@ class Timeline:
# if there is 1 line per row, draw them the standard way
for t, p in standardphases:
for i in sorted(self.rowheight[t][p]):
- self.rowheight[t][p][i] = self.bodyH/len(self.rowlines[t][p])
+ self.rowheight[t][p][i] = float(self.bodyH)/len(self.rowlines[t][p])
def createZoomBox(self, mode='command', testcount=1):
# Create bounding box, add buttons
html_zoombox = '<center><button id="zoomin">ZOOM IN +</button><button id="zoomout">ZOOM OUT -</button><button id="zoomdef">ZOOM 1:1</button></center>\n'
@@ -2358,11 +2671,15 @@ class TestProps:
'(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\
' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$'
batteryfmt = '^# battery (?P<a1>\w*) (?P<c1>\d*) (?P<a2>\w*) (?P<c2>\d*)'
+ wififmt = '^# wifi (?P<w>.*)'
+ tstatfmt = '^# turbostat (?P<t>\S*)'
+ mcelogfmt = '^# mcelog (?P<m>\S*)'
testerrfmt = '^# enter_sleep_error (?P<e>.*)'
sysinfofmt = '^# sysinfo .*'
cmdlinefmt = '^# command \| (?P<cmd>.*)'
kparamsfmt = '^# kparams \| (?P<kp>.*)'
devpropfmt = '# Device Properties: .*'
+ pinfofmt = '# platform-(?P<val>[a-z,A-Z,0-9]*): (?P<info>.*)'
tracertypefmt = '# tracer: (?P<t>.*)'
firmwarefmt = '# fwsuspend (?P<s>[0-9]*) fwresume (?P<r>[0-9]*)$'
procexecfmt = 'ps - (?P<ps>.*)$'
@@ -2380,7 +2697,10 @@ class TestProps:
self.cmdline = ''
self.kparams = ''
self.testerror = []
+ self.mcelog = []
+ self.turbostat = []
self.battery = []
+ self.wifi = []
self.fwdata = []
self.ftrace_line_fmt = self.ftrace_line_fmt_nop
self.cgformat = False
@@ -2394,6 +2714,38 @@ class TestProps:
self.ftrace_line_fmt = self.ftrace_line_fmt_nop
else:
doError('Invalid tracer format: [%s]' % tracer)
+ def stampInfo(self, line):
+ if re.match(self.stampfmt, line):
+ self.stamp = line
+ return True
+ elif re.match(self.sysinfofmt, line):
+ self.sysinfo = line
+ return True
+ elif re.match(self.kparamsfmt, line):
+ self.kparams = line
+ return True
+ elif re.match(self.cmdlinefmt, line):
+ self.cmdline = line
+ return True
+ elif re.match(self.mcelogfmt, line):
+ self.mcelog.append(line)
+ return True
+ elif re.match(self.tstatfmt, line):
+ self.turbostat.append(line)
+ return True
+ elif re.match(self.batteryfmt, line):
+ self.battery.append(line)
+ return True
+ elif re.match(self.wififmt, line):
+ self.wifi.append(line)
+ return True
+ elif re.match(self.testerrfmt, line):
+ self.testerror.append(line)
+ return True
+ elif re.match(self.firmwarefmt, line):
+ self.fwdata.append(line)
+ return True
+ return False
def parseStamp(self, data, sv):
# global test data
m = re.match(self.stampfmt, self.stamp)
@@ -2436,19 +2788,76 @@ class TestProps:
sv.stamp = data.stamp
# firmware data
if sv.suspendmode == 'mem' and len(self.fwdata) > data.testnumber:
- data.fwSuspend, data.fwResume = self.fwdata[data.testnumber]
- if(data.fwSuspend > 0 or data.fwResume > 0):
- data.fwValid = True
+ m = re.match(self.firmwarefmt, self.fwdata[data.testnumber])
+ if m:
+ data.fwSuspend, data.fwResume = int(m.group('s')), int(m.group('r'))
+ if(data.fwSuspend > 0 or data.fwResume > 0):
+ data.fwValid = True
+ # mcelog data
+ if len(self.mcelog) > data.testnumber:
+ m = re.match(self.mcelogfmt, self.mcelog[data.testnumber])
+ if m:
+ data.mcelog = sv.b64unzip(m.group('m'))
+ # turbostat data
+ if len(self.turbostat) > data.testnumber:
+ m = re.match(self.tstatfmt, self.turbostat[data.testnumber])
+ if m:
+ data.turbostat = m.group('t')
# battery data
if len(self.battery) > data.testnumber:
m = re.match(self.batteryfmt, self.battery[data.testnumber])
if m:
data.battery = m.groups()
+ # wifi data
+ if len(self.wifi) > data.testnumber:
+ m = re.match(self.wififmt, self.wifi[data.testnumber])
+ if m:
+ data.wifi = m.group('w')
# sleep mode enter errors
if len(self.testerror) > data.testnumber:
m = re.match(self.testerrfmt, self.testerror[data.testnumber])
if m:
data.enterfail = m.group('e')
+ def devprops(self, data):
+ props = dict()
+ devlist = data.split(';')
+ for dev in devlist:
+ f = dev.split(',')
+ if len(f) < 3:
+ continue
+ dev = f[0]
+ props[dev] = DevProps()
+ props[dev].altname = f[1]
+ if int(f[2]):
+ props[dev].isasync = True
+ else:
+ props[dev].isasync = False
+ return props
+ def parseDevprops(self, line, sv):
+ idx = line.index(': ') + 2
+ if idx >= len(line):
+ return
+ props = self.devprops(line[idx:])
+ if sv.suspendmode == 'command' and 'testcommandstring' in props:
+ sv.testcommand = props['testcommandstring'].altname
+ sv.devprops = props
+ def parsePlatformInfo(self, line, sv):
+ m = re.match(self.pinfofmt, line)
+ if not m:
+ return
+ name, info = m.group('val'), m.group('info')
+ if name == 'devinfo':
+ sv.devprops = self.devprops(sv.b64unzip(info))
+ return
+ elif name == 'testcmd':
+ sv.testcommand = info
+ return
+ field = info.split('|')
+ if len(field) < 2:
+ return
+ cmdline = field[0].strip()
+ output = sv.b64unzip(field[1].strip())
+ sv.platinfo.append([name, cmdline, output])
# Class: TestRun
# Description:
@@ -2469,7 +2878,7 @@ class ProcessMonitor:
process = Popen(c, shell=True, stdout=PIPE)
running = dict()
for line in process.stdout:
- data = line.split()
+ data = ascii(line).split()
pid = data[0]
name = re.sub('[()]', '', data[1])
user = int(data[13])
@@ -2513,9 +2922,9 @@ class ProcessMonitor:
# Quickly determine if the ftrace log has all of the trace events,
# markers, and/or kprobes required for primary parsing.
def doesTraceLogHaveTraceEvents():
- kpcheck = ['_cal: (', '_cpu_down()']
+ kpcheck = ['_cal: (', '_ret: (']
techeck = ['suspend_resume', 'device_pm_callback']
- tmcheck = ['tracing_mark_write']
+ tmcheck = ['SUSPEND START', 'RESUME COMPLETE']
sysvals.usekprobes = False
fp = sysvals.openlog(sysvals.ftracefile, 'r')
for line in fp:
@@ -2564,21 +2973,7 @@ def appendIncompleteTraceLog(testruns):
for line in tf:
# remove any latent carriage returns
line = line.replace('\r\n', '')
- # grab the stamp and sysinfo
- if re.match(tp.stampfmt, line):
- tp.stamp = line
- continue
- elif re.match(tp.sysinfofmt, line):
- tp.sysinfo = line
- continue
- elif re.match(tp.cmdlinefmt, line):
- tp.cmdline = line
- continue
- elif re.match(tp.batteryfmt, line):
- tp.battery.append(line)
- continue
- elif re.match(tp.testerrfmt, line):
- tp.testerror.append(line)
+ if tp.stampInfo(line):
continue
# determine the trace data type (required for further parsing)
m = re.match(tp.tracertypefmt, line)
@@ -2587,7 +2982,11 @@ def appendIncompleteTraceLog(testruns):
continue
# device properties line
if(re.match(tp.devpropfmt, line)):
- devProps(line)
+ tp.parseDevprops(line, sysvals)
+ continue
+ # platform info line
+ if(re.match(tp.pinfofmt, line)):
+ tp.parsePlatformInfo(line, sysvals)
continue
# parse only valid lines, if this is not one move on
m = re.match(tp.ftrace_line_fmt, line)
@@ -2684,7 +3083,7 @@ def parseTraceLog(live=False):
sysvals.setupAllKprobes()
ksuscalls = ['pm_prepare_console']
krescalls = ['pm_restore_console']
- tracewatch = []
+ tracewatch = ['irq_wakeup']
if sysvals.usekprobes:
tracewatch += ['sync_filesystems', 'freeze_processes', 'syscore_suspend',
'syscore_resume', 'resume_console', 'thaw_processes', 'CPU_ON',
@@ -2701,26 +3100,7 @@ def parseTraceLog(live=False):
for line in tf:
# remove any latent carriage returns
line = line.replace('\r\n', '')
- # stamp and sysinfo lines
- if re.match(tp.stampfmt, line):
- tp.stamp = line
- continue
- elif re.match(tp.sysinfofmt, line):
- tp.sysinfo = line
- continue
- elif re.match(tp.cmdlinefmt, line):
- tp.cmdline = line
- continue
- elif re.match(tp.batteryfmt, line):
- tp.battery.append(line)
- continue
- elif re.match(tp.testerrfmt, line):
- tp.testerror.append(line)
- continue
- # firmware line: pull out any firmware data
- m = re.match(tp.firmwarefmt, line)
- if(m):
- tp.fwdata.append((int(m.group('s')), int(m.group('r'))))
+ if tp.stampInfo(line):
continue
# tracer type line: determine the trace data type
m = re.match(tp.tracertypefmt, line)
@@ -2729,7 +3109,11 @@ def parseTraceLog(live=False):
continue
# device properties line
if(re.match(tp.devpropfmt, line)):
- devProps(line)
+ tp.parseDevprops(line, sysvals)
+ continue
+ # platform info line
+ if(re.match(tp.pinfofmt, line)):
+ tp.parsePlatformInfo(line, sysvals)
continue
# ignore all other commented lines
if line[0] == '#':
@@ -2802,16 +3186,11 @@ def parseTraceLog(live=False):
isbegin = False
else:
continue
- m = re.match('(?P<name>.*)\[(?P<val>[0-9]*)\] .*', t.name)
- if(m):
- val = m.group('val')
- if val == '0':
- name = m.group('name')
- else:
- name = m.group('name')+'['+val+']'
+ if '[' in t.name:
+ m = re.match('(?P<name>.*)\[.*', t.name)
else:
m = re.match('(?P<name>.*) .*', t.name)
- name = m.group('name')
+ name = m.group('name')
# ignore these events
if(name.split('[')[0] in tracewatch):
continue
@@ -2846,6 +3225,8 @@ def parseTraceLog(live=False):
elif(re.match('machine_suspend\[.*', t.name)):
if(isbegin):
lp = data.lastPhase()
+ if lp == 'resume_machine':
+ data.dmesg[lp]['end'] = t.time
phase = data.setPhase('suspend_machine', data.dmesg[lp]['end'], True)
data.setPhase(phase, t.time, False)
if data.tSuspended == 0:
@@ -2933,7 +3314,7 @@ def parseTraceLog(live=False):
tp.ktemp[key].append({
'pid': pid,
'begin': t.time,
- 'end': t.time,
+ 'end': -1,
'name': displayname,
'cdata': kprobedata,
'proc': m_proc,
@@ -2944,12 +3325,11 @@ def parseTraceLog(live=False):
elif(t.freturn):
if(key not in tp.ktemp) or len(tp.ktemp[key]) < 1:
continue
- e = tp.ktemp[key][-1]
- if e['begin'] < 0.0 or t.time - e['begin'] < 0.000001:
- tp.ktemp[key].pop()
- else:
- e['end'] = t.time
- e['rdata'] = kprobedata
+ e = next((x for x in reversed(tp.ktemp[key]) if x['end'] < 0), 0)
+ if not e:
+ continue
+ e['end'] = t.time
+ e['rdata'] = kprobedata
# end of kernel resume
if(phase != 'suspend_prepare' and kprobename in krescalls):
if phase in data.dmesg:
@@ -2971,8 +3351,10 @@ def parseTraceLog(live=False):
if(res == -1):
testrun.ftemp[key][-1].addLine(t)
tf.close()
+ if len(testdata) < 1:
+ sysvals.vprint('WARNING: ftrace start marker is missing')
if data and not data.devicegroups:
- sysvals.vprint('WARNING: end marker is missing')
+ sysvals.vprint('WARNING: ftrace end marker is missing')
data.handleEndMarker(t.time)
if sysvals.suspendmode == 'command':
@@ -3013,36 +3395,38 @@ def parseTraceLog(live=False):
# add the traceevent data to the device hierarchy
if(sysvals.usetraceevents):
# add actual trace funcs
- for name in test.ttemp:
+ for name in sorted(test.ttemp):
for event in test.ttemp[name]:
data.newActionGlobal(name, event['begin'], event['end'], event['pid'])
# add the kprobe based virtual tracefuncs as actual devices
- for key in tp.ktemp:
+ for key in sorted(tp.ktemp):
name, pid = key
if name not in sysvals.tracefuncs:
continue
+ if pid not in data.devpids:
+ data.devpids.append(pid)
for e in tp.ktemp[key]:
kb, ke = e['begin'], e['end']
- if kb == ke or tlb > kb or tle <= kb:
+ if ke - kb < 0.000001 or tlb > kb or tle <= kb:
continue
color = sysvals.kprobeColor(name)
data.newActionGlobal(e['name'], kb, ke, pid, color)
# add config base kprobes and dev kprobes
if sysvals.usedevsrc:
- for key in tp.ktemp:
+ for key in sorted(tp.ktemp):
name, pid = key
if name in sysvals.tracefuncs or name not in sysvals.dev_tracefuncs:
continue
for e in tp.ktemp[key]:
kb, ke = e['begin'], e['end']
- if kb == ke or tlb > kb or tle <= kb:
+ if ke - kb < 0.000001 or tlb > kb or tle <= kb:
continue
data.addDeviceFunctionCall(e['name'], name, e['proc'], pid, kb,
ke, e['cdata'], e['rdata'])
if sysvals.usecallgraph:
# add the callgraph data to the device hierarchy
sortlist = dict()
- for key in test.ftemp:
+ for key in sorted(test.ftemp):
proc, pid = key
for cg in test.ftemp[key]:
if len(cg.list) < 1 or cg.invalid or (cg.end - cg.start == 0):
@@ -3059,7 +3443,7 @@ def parseTraceLog(live=False):
if not devname:
sortkey = '%f%f%d' % (cg.start, cg.end, pid)
sortlist[sortkey] = cg
- elif len(cg.list) > 1000000:
+ elif len(cg.list) > 1000000 and cg.name != sysvals.ftopfunc:
sysvals.vprint('WARNING: the callgraph for %s is massive (%d lines)' %\
(devname, len(cg.list)))
# create blocks for orphan cg data
@@ -3141,25 +3525,7 @@ def loadKernelLog():
idx = line.find('[')
if idx > 1:
line = line[idx:]
- # grab the stamp and sysinfo
- if re.match(tp.stampfmt, line):
- tp.stamp = line
- continue
- elif re.match(tp.sysinfofmt, line):
- tp.sysinfo = line
- continue
- elif re.match(tp.cmdlinefmt, line):
- tp.cmdline = line
- continue
- elif re.match(tp.batteryfmt, line):
- tp.battery.append(line)
- continue
- elif re.match(tp.testerrfmt, line):
- tp.testerror.append(line)
- continue
- m = re.match(tp.firmwarefmt, line)
- if(m):
- tp.fwdata.append((int(m.group('s')), int(m.group('r'))))
+ if tp.stampInfo(line):
continue
m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
if(not m):
@@ -3184,7 +3550,7 @@ def loadKernelLog():
if data:
testruns.append(data)
if len(testruns) < 1:
- pprint('ERROR: dmesg log has no suspend/resume data: %s' \
+ doError('dmesg log has no suspend/resume data: %s' \
% sysvals.dmesgfile)
# fix lines with same timestamp/function with the call and return swapped
@@ -3398,7 +3764,7 @@ def parseKernelLog(data):
# if trace events are not available, these are better than nothing
if(not sysvals.usetraceevents):
# look for known actions
- for a in at:
+ for a in sorted(at):
if(re.match(at[a]['smsg'], msg)):
if(a not in actions):
actions[a] = []
@@ -3457,7 +3823,7 @@ def parseKernelLog(data):
data.tResumed = data.tSuspended
# fill in any actions we've found
- for name in actions:
+ for name in sorted(actions):
for event in actions[name]:
data.newActionGlobal(name, event['begin'], event['end'])
@@ -3523,6 +3889,8 @@ def addCallgraphs(sv, hf, data):
name += ' '+p
if('ftrace' in dev):
cg = dev['ftrace']
+ if cg.name == sv.ftopfunc:
+ name = 'top level suspend/resume call'
num = callgraphHTML(sv, hf, num, cg,
name, color, dev['id'])
if('ftraces' in dev):
@@ -3531,22 +3899,16 @@ def addCallgraphs(sv, hf, data):
name+' &rarr; '+cg.name, color, dev['id'])
hf.write('\n\n </section>\n')
-# Function: createHTMLSummarySimple
-# Description:
-# Create summary html file for a series of tests
-# Arguments:
-# testruns: array of Data objects from parseTraceLog
-def createHTMLSummarySimple(testruns, htmlfile, title):
- # write the html header first (html head, css code, up to body start)
- html = '<!DOCTYPE html>\n<html>\n<head>\n\
+def summaryCSS(title, center=True):
+ tdcenter = 'text-align:center;' if center else ''
+ out = '<!DOCTYPE html>\n<html>\n<head>\n\
<meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
- <title>SleepGraph Summary</title>\n\
+ <title>'+title+'</title>\n\
<style type=\'text/css\'>\n\
.stamp {width: 100%;text-align:center;background:#888;line-height:30px;color:white;font: 25px Arial;}\n\
- table {width:100%;border-collapse: collapse;}\n\
- .summary {border:1px solid;}\n\
+ table {width:100%;border-collapse: collapse;border:1px solid;}\n\
th {border: 1px solid black;background:#222;color:white;}\n\
- td {font: 14px "Times New Roman";text-align: center;}\n\
+ td {font: 14px "Times New Roman";'+tdcenter+'}\n\
tr.head td {border: 1px solid black;background:#aaa;}\n\
tr.alt {background-color:#ddd;}\n\
tr.notice {color:red;}\n\
@@ -3555,12 +3917,23 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
.maxval {background-color:#FFBBBB;}\n\
.head a {color:#000;text-decoration: none;}\n\
</style>\n</head>\n<body>\n'
+ return out
+
+# Function: createHTMLSummarySimple
+# Description:
+# Create summary html file for a series of tests
+# Arguments:
+# testruns: array of Data objects from parseTraceLog
+def createHTMLSummarySimple(testruns, htmlfile, title):
+ # write the html header first (html head, css code, up to body start)
+ html = summaryCSS('Summary - SleepGraph')
# extract the test data into list
list = dict()
- tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [[], []]
+ tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()]
iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
num = 0
+ useturbo = False
lastmode = ''
cnt = dict()
for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])):
@@ -3570,28 +3943,36 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
if lastmode and lastmode != mode and num > 0:
for i in range(2):
s = sorted(tMed[i])
- list[lastmode]['med'][i] = s[int(len(s)/2)]
- iMed[i] = tMed[i].index(list[lastmode]['med'][i])
+ list[lastmode]['med'][i] = s[int(len(s)//2)]
+ iMed[i] = tMed[i][list[lastmode]['med'][i]]
list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num]
list[lastmode]['min'] = tMin
list[lastmode]['max'] = tMax
list[lastmode]['idx'] = (iMin, iMed, iMax)
- tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [[], []]
+ tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()]
iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
num = 0
+ pkgpc10 = syslpi = ''
+ if 'pkgpc10' in data and 'syslpi' in data:
+ pkgpc10 = data['pkgpc10']
+ syslpi = data['syslpi']
+ useturbo = True
+ res = data['result']
tVal = [float(data['suspend']), float(data['resume'])]
list[mode]['data'].append([data['host'], data['kernel'],
- data['time'], tVal[0], tVal[1], data['url'], data['result'],
+ data['time'], tVal[0], tVal[1], data['url'], res,
data['issues'], data['sus_worst'], data['sus_worsttime'],
- data['res_worst'], data['res_worsttime']])
+ data['res_worst'], data['res_worsttime'], pkgpc10, syslpi])
idx = len(list[mode]['data']) - 1
- if data['result'] not in cnt:
- cnt[data['result']] = 1
+ if res.startswith('fail in'):
+ res = 'fail'
+ if res not in cnt:
+ cnt[res] = 1
else:
- cnt[data['result']] += 1
- if data['result'] == 'pass':
+ cnt[res] += 1
+ if res == 'pass':
for i in range(2):
- tMed[i].append(tVal[i])
+ tMed[i][tVal[i]] = idx
tAvg[i] += tVal[i]
if tMin[i] == 0 or tVal[i] < tMin[i]:
iMin[i] = idx
@@ -3604,8 +3985,8 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
if lastmode and num > 0:
for i in range(2):
s = sorted(tMed[i])
- list[lastmode]['med'][i] = s[int(len(s)/2)]
- iMed[i] = tMed[i].index(list[lastmode]['med'][i])
+ list[lastmode]['med'][i] = s[int(len(s)//2)]
+ iMed[i] = tMed[i][list[lastmode]['med'][i]]
list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num]
list[lastmode]['min'] = tMin
list[lastmode]['max'] = tMax
@@ -3621,19 +4002,21 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
td = '\t<td>{0}</td>\n'
tdh = '\t<td{1}>{0}</td>\n'
tdlink = '\t<td><a href="{0}">html</a></td>\n'
+ colspan = '14' if useturbo else '12'
# table header
- html += '<table class="summary">\n<tr>\n' + th.format('#') +\
+ html += '<table>\n<tr>\n' + th.format('#') +\
th.format('Mode') + th.format('Host') + th.format('Kernel') +\
th.format('Test Time') + th.format('Result') + th.format('Issues') +\
th.format('Suspend') + th.format('Resume') +\
th.format('Worst Suspend Device') + th.format('SD Time') +\
- th.format('Worst Resume Device') + th.format('RD Time') +\
- th.format('Detail') + '</tr>\n'
-
+ th.format('Worst Resume Device') + th.format('RD Time')
+ if useturbo:
+ html += th.format('PkgPC10') + th.format('SysLPI')
+ html += th.format('Detail')+'</tr>\n'
# export list into html
head = '<tr class="head"><td>{0}</td><td>{1}</td>'+\
- '<td colspan=12 class="sus">Suspend Avg={2} '+\
+ '<td colspan='+colspan+' class="sus">Suspend Avg={2} '+\
'<span class=minval><a href="#s{10}min">Min={3}</a></span> '+\
'<span class=medval><a href="#s{10}med">Med={4}</a></span> '+\
'<span class=maxval><a href="#s{10}max">Max={5}</a></span> '+\
@@ -3642,8 +4025,9 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
'<span class=medval><a href="#r{10}med">Med={8}</a></span> '+\
'<span class=maxval><a href="#r{10}max">Max={9}</a></span></td>'+\
'</tr>\n'
- headnone = '<tr class="head"><td>{0}</td><td>{1}</td><td colspan=12></td></tr>\n'
- for mode in list:
+ headnone = '<tr class="head"><td>{0}</td><td>{1}</td><td colspan='+\
+ colspan+'></td></tr>\n'
+ for mode in sorted(list):
# header line for each suspend mode
num = 0
tAvg, tMin, tMax, tMed = list[mode]['avg'], list[mode]['min'],\
@@ -3689,6 +4073,9 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
html += td.format('%.3f ms' % d[9]) if d[9] else td.format('') # sus_worst time
html += td.format(d[10]) # res_worst
html += td.format('%.3f ms' % d[11]) if d[11] else td.format('') # res_worst time
+ if useturbo:
+ html += td.format(d[12]) # pkg_pc10
+ html += td.format(d[13]) # syslpi
html += tdlink.format(d[5]) if d[5] else td.format('') # url
html += '</tr>\n'
num += 1
@@ -3698,6 +4085,116 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
hf.write(html+'</table>\n</body>\n</html>\n')
hf.close()
+def createHTMLDeviceSummary(testruns, htmlfile, title):
+ html = summaryCSS('Device Summary - SleepGraph', False)
+
+ # create global device list from all tests
+ devall = dict()
+ for data in testruns:
+ host, url, devlist = data['host'], data['url'], data['devlist']
+ for type in devlist:
+ if type not in devall:
+ devall[type] = dict()
+ mdevlist, devlist = devall[type], data['devlist'][type]
+ for name in devlist:
+ length = devlist[name]
+ if name not in mdevlist:
+ mdevlist[name] = {'name': name, 'host': host,
+ 'worst': length, 'total': length, 'count': 1,
+ 'url': url}
+ else:
+ if length > mdevlist[name]['worst']:
+ mdevlist[name]['worst'] = length
+ mdevlist[name]['url'] = url
+ mdevlist[name]['host'] = host
+ mdevlist[name]['total'] += length
+ mdevlist[name]['count'] += 1
+
+ # generate the html
+ th = '\t<th>{0}</th>\n'
+ td = '\t<td align=center>{0}</td>\n'
+ tdr = '\t<td align=right>{0}</td>\n'
+ tdlink = '\t<td align=center><a href="{0}">html</a></td>\n'
+ limit = 1
+ for type in sorted(devall, reverse=True):
+ num = 0
+ devlist = devall[type]
+ # table header
+ html += '<div class="stamp">%s (%s devices > %d ms)</div><table>\n' % \
+ (title, type.upper(), limit)
+ html += '<tr>\n' + '<th align=right>Device Name</th>' +\
+ th.format('Average Time') + th.format('Count') +\
+ th.format('Worst Time') + th.format('Host (worst time)') +\
+ th.format('Link (worst time)') + '</tr>\n'
+ for name in sorted(devlist, key=lambda k:(devlist[k]['worst'], \
+ devlist[k]['total'], devlist[k]['name']), reverse=True):
+ data = devall[type][name]
+ data['average'] = data['total'] / data['count']
+ if data['average'] < limit:
+ continue
+ # row classes - alternate row color
+ rcls = ['alt'] if num % 2 == 1 else []
+ html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n'
+ html += tdr.format(data['name']) # name
+ html += td.format('%.3f ms' % data['average']) # average
+ html += td.format(data['count']) # count
+ html += td.format('%.3f ms' % data['worst']) # worst
+ html += td.format(data['host']) # host
+ html += tdlink.format(data['url']) # url
+ html += '</tr>\n'
+ num += 1
+ html += '</table>\n'
+
+ # flush the data to file
+ hf = open(htmlfile, 'w')
+ hf.write(html+'</body>\n</html>\n')
+ hf.close()
+ return devall
+
+def createHTMLIssuesSummary(testruns, issues, htmlfile, title, extra=''):
+ multihost = len([e for e in issues if len(e['urls']) > 1]) > 0
+ html = summaryCSS('Issues Summary - SleepGraph', False)
+ total = len(testruns)
+
+ # generate the html
+ th = '\t<th>{0}</th>\n'
+ td = '\t<td align={0}>{1}</td>\n'
+ tdlink = '<a href="{1}">{0}</a>'
+ subtitle = '%d issues' % len(issues) if len(issues) > 0 else 'no issues'
+ html += '<div class="stamp">%s (%s)</div><table>\n' % (title, subtitle)
+ html += '<tr>\n' + th.format('Issue') + th.format('Count')
+ if multihost:
+ html += th.format('Hosts')
+ html += th.format('Tests') + th.format('Fail Rate') +\
+ th.format('First Instance') + '</tr>\n'
+
+ num = 0
+ for e in sorted(issues, key=lambda v:v['count'], reverse=True):
+ testtotal = 0
+ links = []
+ for host in sorted(e['urls']):
+ links.append(tdlink.format(host, e['urls'][host][0]))
+ testtotal += len(e['urls'][host])
+ rate = '%d/%d (%.2f%%)' % (testtotal, total, 100*float(testtotal)/float(total))
+ # row classes - alternate row color
+ rcls = ['alt'] if num % 2 == 1 else []
+ html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n'
+ html += td.format('left', e['line']) # issue
+ html += td.format('center', e['count']) # count
+ if multihost:
+ html += td.format('center', len(e['urls'])) # hosts
+ html += td.format('center', testtotal) # test count
+ html += td.format('center', rate) # test rate
+ html += td.format('center nowrap', '<br>'.join(links)) # links
+ html += '</tr>\n'
+ num += 1
+
+ # flush the data to file
+ hf = open(htmlfile, 'w')
+ hf.write(html+'</table>\n'+extra+'</body>\n</html>\n')
+ hf.close()
+ return issues
+
def ordinal(value):
suffix = 'th'
if value < 10 or value > 19:
@@ -3771,7 +4268,7 @@ def createHTML(testruns, testfail):
if(tTotal == 0):
doError('No timeline data')
if(len(data.tLow) > 0):
- low_time = '|'.join(data.tLow)
+ low_time = '+'.join(data.tLow)
if sysvals.suspendmode == 'command':
run_time = '%.0f'%((data.end-data.start)*1000)
if sysvals.testcommand:
@@ -3837,7 +4334,7 @@ def createHTML(testruns, testfail):
for group in data.devicegroups:
devlist = []
for phase in group:
- for devname in data.tdevlist[phase]:
+ for devname in sorted(data.tdevlist[phase]):
d = DevItem(data.testnumber, phase, data.dmesg[phase]['list'][devname])
devlist.append(d)
if d.isa('kth'):
@@ -3916,7 +4413,7 @@ def createHTML(testruns, testfail):
for b in phases[dir]:
# draw the devices for this phase
phaselist = data.dmesg[b]['list']
- for d in data.tdevlist[b]:
+ for d in sorted(data.tdevlist[b]):
name = d
drv = ''
dev = phaselist[d]
@@ -3999,7 +4496,7 @@ def createHTML(testruns, testfail):
for word in phase.split('_'):
id += word[0]
order = '%.2f' % ((p['order'] * pdelta) + pmargin)
- name = string.replace(phase, '_', ' &nbsp;')
+ name = phase.replace('_', ' &nbsp;')
devtl.html += devtl.html_legend.format(order, p['color'], name, id)
devtl.html += '</div>\n'
@@ -4588,6 +5085,7 @@ def setRuntimeSuspend(before=True):
def executeSuspend():
pm = ProcessMonitor()
tp = sysvals.tpath
+ wifi = sysvals.checkWifi()
testdata = []
battery = True if getBattery() else False
# run these commands to prepare the system for suspend
@@ -4621,6 +5119,7 @@ def executeSuspend():
pprint('SUSPEND START')
else:
pprint('SUSPEND START (press a key to resume)')
+ sysvals.mcelog(True)
bat1 = getBattery() if battery else False
# set rtcwake
if(sysvals.rtcwake):
@@ -4652,13 +5151,19 @@ def executeSuspend():
pf = open(sysvals.diskpowerfile, 'w')
pf.write(sysvals.diskmode)
pf.close()
- pf = open(sysvals.powerfile, 'w')
- pf.write(mode)
- # execution will pause here
- try:
- pf.close()
- except Exception as e:
- tdata['error'] = str(e)
+ if mode == 'freeze' and sysvals.haveTurbostat():
+ # execution will pause here
+ turbo = sysvals.turbostat()
+ if turbo:
+ tdata['turbo'] = turbo
+ else:
+ pf = open(sysvals.powerfile, 'w')
+ pf.write(mode)
+ # execution will pause here
+ try:
+ pf.close()
+ except Exception as e:
+ tdata['error'] = str(e)
if(sysvals.rtcwake):
sysvals.rtcWakeAlarmOff()
# postdelay delay
@@ -4672,9 +5177,14 @@ def executeSuspend():
sysvals.fsetVal('RESUME COMPLETE', 'trace_marker')
if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'):
tdata['fw'] = getFPDT(False)
+ mcelog = sysvals.mcelog()
+ if mcelog:
+ tdata['mcelog'] = mcelog
bat2 = getBattery() if battery else False
if battery and bat1 and bat2:
tdata['bat'] = (bat1, bat2)
+ if 'device' in wifi and 'ip' in wifi:
+ tdata['wifi'] = (wifi, sysvals.checkWifi())
testdata.append(tdata)
# stop ftrace
if(sysvals.usecallgraph or sysvals.usetraceevents):
@@ -4693,7 +5203,8 @@ def executeSuspend():
op.write(line)
op.close()
sysvals.fsetVal('', 'trace')
- devProps()
+ sysvals.platforminfo()
+ return testdata
def readFile(file):
if os.path.islink(file):
@@ -4708,9 +5219,9 @@ def readFile(file):
# The time string, e.g. "1901m16s"
def ms2nice(val):
val = int(val)
- h = val / 3600000
- m = (val / 60000) % 60
- s = (val / 1000) % 60
+ h = val // 3600000
+ m = (val // 60000) % 60
+ s = (val // 1000) % 60
if h > 0:
return '%d:%02d:%02d' % (h, m, s)
if m > 0:
@@ -4780,130 +5291,9 @@ def deviceInfo(output=''):
ms2nice(power['runtime_active_time']), \
ms2nice(power['runtime_suspended_time']))
for i in sorted(lines):
- print lines[i]
+ print(lines[i])
return res
-# Function: devProps
-# Description:
-# Retrieve a list of properties for all devices in the trace log
-def devProps(data=0):
- props = dict()
-
- if data:
- idx = data.index(': ') + 2
- if idx >= len(data):
- return
- devlist = data[idx:].split(';')
- for dev in devlist:
- f = dev.split(',')
- if len(f) < 3:
- continue
- dev = f[0]
- props[dev] = DevProps()
- props[dev].altname = f[1]
- if int(f[2]):
- props[dev].async = True
- else:
- props[dev].async = False
- sysvals.devprops = props
- if sysvals.suspendmode == 'command' and 'testcommandstring' in props:
- sysvals.testcommand = props['testcommandstring'].altname
- return
-
- if(os.path.exists(sysvals.ftracefile) == False):
- doError('%s does not exist' % sysvals.ftracefile)
-
- # first get the list of devices we need properties for
- msghead = 'Additional data added by AnalyzeSuspend'
- alreadystamped = False
- tp = TestProps()
- tf = sysvals.openlog(sysvals.ftracefile, 'r')
- for line in tf:
- if msghead in line:
- alreadystamped = True
- continue
- # determine the trace data type (required for further parsing)
- m = re.match(tp.tracertypefmt, line)
- if(m):
- tp.setTracerType(m.group('t'))
- continue
- # parse only valid lines, if this is not one move on
- m = re.match(tp.ftrace_line_fmt, line)
- if(not m or 'device_pm_callback_start' not in line):
- continue
- m = re.match('.*: (?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*', m.group('msg'));
- if(not m):
- continue
- dev = m.group('d')
- if dev not in props:
- props[dev] = DevProps()
- tf.close()
-
- if not alreadystamped and sysvals.suspendmode == 'command':
- out = '#\n# '+msghead+'\n# Device Properties: '
- out += 'testcommandstring,%s,0;' % (sysvals.testcommand)
- with sysvals.openlog(sysvals.ftracefile, 'a') as fp:
- fp.write(out+'\n')
- sysvals.devprops = props
- return
-
- # now get the syspath for each of our target devices
- for dirname, dirnames, filenames in os.walk('/sys/devices'):
- if(re.match('.*/power', dirname) and 'async' in filenames):
- dev = dirname.split('/')[-2]
- if dev in props and (not props[dev].syspath or len(dirname) < len(props[dev].syspath)):
- props[dev].syspath = dirname[:-6]
-
- # now fill in the properties for our target devices
- for dev in props:
- dirname = props[dev].syspath
- if not dirname or not os.path.exists(dirname):
- continue
- with open(dirname+'/power/async') as fp:
- text = fp.read()
- props[dev].async = False
- if 'enabled' in text:
- props[dev].async = True
- fields = os.listdir(dirname)
- if 'product' in fields:
- with open(dirname+'/product') as fp:
- props[dev].altname = fp.read()
- elif 'name' in fields:
- with open(dirname+'/name') as fp:
- props[dev].altname = fp.read()
- elif 'model' in fields:
- with open(dirname+'/model') as fp:
- props[dev].altname = fp.read()
- elif 'description' in fields:
- with open(dirname+'/description') as fp:
- props[dev].altname = fp.read()
- elif 'id' in fields:
- with open(dirname+'/id') as fp:
- props[dev].altname = fp.read()
- elif 'idVendor' in fields and 'idProduct' in fields:
- idv, idp = '', ''
- with open(dirname+'/idVendor') as fp:
- idv = fp.read().strip()
- with open(dirname+'/idProduct') as fp:
- idp = fp.read().strip()
- props[dev].altname = '%s:%s' % (idv, idp)
-
- if props[dev].altname:
- out = props[dev].altname.strip().replace('\n', ' ')
- out = out.replace(',', ' ')
- out = out.replace(';', ' ')
- props[dev].altname = out
-
- # and now write the data to the ftrace file
- if not alreadystamped:
- out = '#\n# '+msghead+'\n# Device Properties: '
- for dev in sorted(props):
- out += props[dev].out(dev)
- with sysvals.openlog(sysvals.ftracefile, 'a') as fp:
- fp.write(out+'\n')
-
- sysvals.devprops = props
-
# Function: getModes
# Description:
# Determine the supported power modes on this system
@@ -4913,12 +5303,12 @@ def getModes():
modes = []
if(os.path.exists(sysvals.powerfile)):
fp = open(sysvals.powerfile, 'r')
- modes = string.split(fp.read())
+ modes = fp.read().split()
fp.close()
if(os.path.exists(sysvals.mempowerfile)):
deep = False
fp = open(sysvals.mempowerfile, 'r')
- for m in string.split(fp.read()):
+ for m in fp.read().split():
memmode = m.strip('[]')
if memmode == 'deep':
deep = True
@@ -4929,7 +5319,7 @@ def getModes():
modes.remove('mem')
if('disk' in modes and os.path.exists(sysvals.diskpowerfile)):
fp = open(sysvals.diskpowerfile, 'r')
- for m in string.split(fp.read()):
+ for m in fp.read().split():
modes.append('disk-%s' % m.strip('[]'))
fp.close()
return modes
@@ -4992,25 +5382,26 @@ def dmidecode(mempath, fatal=False):
continue
# read in the memory for scanning
- fp = open(mempath, 'rb')
try:
+ fp = open(mempath, 'rb')
fp.seek(memaddr)
buf = fp.read(memsize)
except:
if(fatal):
doError('DMI table is unreachable, sorry')
else:
+ pprint('WARNING: /dev/mem is not readable, ignoring DMI data')
return out
fp.close()
# search for either an SM table or DMI table
i = base = length = num = 0
while(i < memsize):
- if buf[i:i+4] == '_SM_' and i < memsize - 16:
+ if buf[i:i+4] == b'_SM_' and i < memsize - 16:
length = struct.unpack('H', buf[i+22:i+24])[0]
base, num = struct.unpack('IH', buf[i+24:i+30])
break
- elif buf[i:i+5] == '_DMI_':
+ elif buf[i:i+5] == b'_DMI_':
length = struct.unpack('H', buf[i+6:i+8])[0]
base, num = struct.unpack('IH', buf[i+8:i+14])
break
@@ -5022,14 +5413,15 @@ def dmidecode(mempath, fatal=False):
return out
# read in the SM or DMI table
- fp = open(mempath, 'rb')
try:
+ fp = open(mempath, 'rb')
fp.seek(base)
buf = fp.read(length)
except:
if(fatal):
doError('DMI table is unreachable, sorry')
else:
+ pprint('WARNING: /dev/mem is not readable, ignoring DMI data')
return out
fp.close()
@@ -5042,15 +5434,15 @@ def dmidecode(mempath, fatal=False):
if 0 == struct.unpack('H', buf[n:n+2])[0]:
break
n += 1
- data = buf[i+size:n+2].split('\0')
+ data = buf[i+size:n+2].split(b'\0')
for name in info:
itype, idxadr = info[name]
if itype == type:
- idx = struct.unpack('B', buf[i+idxadr])[0]
+ idx = struct.unpack('B', buf[i+idxadr:i+idxadr+1])[0]
if idx > 0 and idx < len(data) - 1:
- s = data[idx-1].strip()
- if s and s.lower() != 'to be filled by o.e.m.':
- out[name] = data[idx-1]
+ s = data[idx-1].decode('utf-8')
+ if s.strip() and s.strip().lower() != 'to be filled by o.e.m.':
+ out[name] = s
i = n + 2
count += 1
return out
@@ -5075,7 +5467,7 @@ def getBattery():
return (ac, charge)
def displayControl(cmd):
- xset, ret = 'xset -d :0.0 {0}', 0
+ xset, ret = 'timeout 10 xset -d :0.0 {0}', 0
if sysvals.sudouser:
xset = 'sudo -u %s %s' % (sysvals.sudouser, xset)
if cmd == 'init':
@@ -5099,7 +5491,7 @@ def displayControl(cmd):
fp = Popen(xset.format('q').split(' '), stdout=PIPE).stdout
ret = 'unknown'
for line in fp:
- m = re.match('[\s]*Monitor is (?P<m>.*)', line)
+ m = re.match('[\s]*Monitor is (?P<m>.*)', ascii(line))
if(m and len(m.group('m')) >= 2):
out = m.group('m').lower()
ret = out[3:] if out[0:2] == 'in' else out
@@ -5161,10 +5553,11 @@ def getFPDT(output):
' OEM Revision : %u\n'\
' Creator ID : %s\n'\
' Creator Revision : 0x%x\n'\
- '' % (table[0], table[0], table[1], table[2], table[3],
- table[4], table[5], table[6], table[7], table[8]))
+ '' % (ascii(table[0]), ascii(table[0]), table[1], table[2],
+ table[3], ascii(table[4]), ascii(table[5]), table[6],
+ ascii(table[7]), table[8]))
- if(table[0] != 'FPDT'):
+ if(table[0] != b'FPDT'):
if(output):
doError('Invalid FPDT table')
return False
@@ -5173,7 +5566,11 @@ def getFPDT(output):
i = 0
fwData = [0, 0]
records = buf[36:]
- fp = open(sysvals.mempath, 'rb')
+ try:
+ fp = open(sysvals.mempath, 'rb')
+ except:
+ pprint('WARNING: /dev/mem is not readable, ignoring the FPDT data')
+ return False
while(i < len(records)):
header = struct.unpack('HBB', records[i:i+4])
if(header[0] not in rectype):
@@ -5192,8 +5589,8 @@ def getFPDT(output):
return [0, 0]
rechead = struct.unpack('4sI', first)
recdata = fp.read(rechead[1]-8)
- if(rechead[0] == 'FBPT'):
- record = struct.unpack('HBBIQQQQQ', recdata)
+ if(rechead[0] == b'FBPT'):
+ record = struct.unpack('HBBIQQQQQ', recdata[:48])
if(output):
pprint('%s (%s)\n'\
' Reset END : %u ns\n'\
@@ -5201,11 +5598,11 @@ def getFPDT(output):
' OS Loader StartImage Start : %u ns\n'\
' ExitBootServices Entry : %u ns\n'\
' ExitBootServices Exit : %u ns'\
- '' % (rectype[header[0]], rechead[0], record[4], record[5],
+ '' % (rectype[header[0]], ascii(rechead[0]), record[4], record[5],
record[6], record[7], record[8]))
- elif(rechead[0] == 'S3PT'):
+ elif(rechead[0] == b'S3PT'):
if(output):
- pprint('%s (%s)' % (rectype[header[0]], rechead[0]))
+ pprint('%s (%s)' % (rectype[header[0]], ascii(rechead[0])))
j = 0
while(j < len(recdata)):
prechead = struct.unpack('HBB', recdata[j:j+4])
@@ -5290,13 +5687,14 @@ def statusCheck(probecheck=False):
pprint(' is ftrace supported: %s' % res)
# check if kprobes are available
- res = sysvals.colorText('NO')
- sysvals.usekprobes = sysvals.verifyKprobes()
- if(sysvals.usekprobes):
- res = 'YES'
- else:
- sysvals.usedevsrc = False
- pprint(' are kprobes supported: %s' % res)
+ if sysvals.usekprobes:
+ res = sysvals.colorText('NO')
+ sysvals.usekprobes = sysvals.verifyKprobes()
+ if(sysvals.usekprobes):
+ res = 'YES'
+ else:
+ sysvals.usedevsrc = False
+ pprint(' are kprobes supported: %s' % res)
# what data source are we using
res = 'DMESG'
@@ -5350,7 +5748,7 @@ def doError(msg, help=False):
def getArgInt(name, args, min, max, main=True):
if main:
try:
- arg = args.next()
+ arg = next(args)
except:
doError(name+': no argument supplied', True)
else:
@@ -5369,7 +5767,7 @@ def getArgInt(name, args, min, max, main=True):
def getArgFloat(name, args, min, max, main=True):
if main:
try:
- arg = args.next()
+ arg = next(args)
except:
doError(name+': no argument supplied', True)
else:
@@ -5384,6 +5782,8 @@ def getArgFloat(name, args, min, max, main=True):
def processData(live=False):
pprint('PROCESSING DATA')
+ sysvals.vprint('usetraceevents=%s, usetracemarkers=%s, usekprobes=%s' % \
+ (sysvals.usetraceevents, sysvals.usetracemarkers, sysvals.usekprobes))
error = ''
if(sysvals.usetraceevents):
testruns, error = parseTraceLog(live)
@@ -5396,14 +5796,46 @@ def processData(live=False):
parseKernelLog(data)
if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)):
appendIncompleteTraceLog(testruns)
+ shown = ['bios', 'biosdate', 'cpu', 'host', 'kernel', 'man', 'memfr',
+ 'memsz', 'mode', 'numcpu', 'plat', 'time']
+ sysvals.vprint('System Info:')
+ for key in sorted(sysvals.stamp):
+ if key in shown:
+ sysvals.vprint(' %-8s : %s' % (key.upper(), sysvals.stamp[key]))
+ if sysvals.kparams:
+ sysvals.vprint('Kparams:\n %s' % sysvals.kparams)
sysvals.vprint('Command:\n %s' % sysvals.cmdline)
for data in testruns:
+ if data.mcelog:
+ sysvals.vprint('MCELOG Data:')
+ for line in data.mcelog.split('\n'):
+ sysvals.vprint(' %s' % line)
+ if data.turbostat:
+ idx, s = 0, 'Turbostat:\n '
+ for val in data.turbostat.split('|'):
+ idx += len(val) + 1
+ if idx >= 80:
+ idx = 0
+ s += '\n '
+ s += val + ' '
+ sysvals.vprint(s)
if data.battery:
a1, c1, a2, c2 = data.battery
s = 'Battery:\n Before - AC: %s, Charge: %d\n After - AC: %s, Charge: %d' % \
(a1, int(c1), a2, int(c2))
sysvals.vprint(s)
+ if data.wifi:
+ w = data.wifi.replace('|', ' ').split(',')
+ s = 'Wifi:\n Before %s\n After %s' % \
+ (w[0], w[1])
+ sysvals.vprint(s)
data.printDetails()
+ if len(sysvals.platinfo) > 0:
+ sysvals.vprint('\nPlatform Info:')
+ for info in sysvals.platinfo:
+ sysvals.vprint(info[0]+' - '+info[1])
+ sysvals.vprint(info[2])
+ sysvals.vprint('')
if sysvals.cgdump:
for data in testruns:
data.debugPrint()
@@ -5426,12 +5858,15 @@ def processData(live=False):
# Function: rerunTest
# Description:
# generate an output from an existing set of ftrace/dmesg logs
-def rerunTest():
+def rerunTest(htmlfile=''):
if sysvals.ftracefile:
doesTraceLogHaveTraceEvents()
if not sysvals.dmesgfile and not sysvals.usetraceevents:
doError('recreating this html output requires a dmesg file')
- sysvals.setOutputFile()
+ if htmlfile:
+ sysvals.htmlfile = htmlfile
+ else:
+ sysvals.setOutputFile()
if os.path.exists(sysvals.htmlfile):
if not os.path.isfile(sysvals.htmlfile):
doError('a directory already exists with this name: %s' % sysvals.htmlfile)
@@ -5450,14 +5885,18 @@ def runTest(n=0):
sysvals.initTestOutput('suspend')
# execute the test
- executeSuspend()
+ testdata = executeSuspend()
sysvals.cleanupFtrace()
if sysvals.skiphtml:
sysvals.sudoUserchown(sysvals.testdir)
return
- testruns, stamp = processData(True)
- for data in testruns:
- del data
+ if not testdata[0]['error']:
+ testruns, stamp = processData(True)
+ for data in testruns:
+ del data
+ else:
+ stamp = testdata[0]
+
sysvals.sudoUserchown(sysvals.testdir)
sysvals.outputResult(stamp, n)
if 'error' in stamp:
@@ -5487,10 +5926,13 @@ def find_in_html(html, start, end, firstonly=True):
return ''
return out
-def data_from_html(file, outpath, devlist=False):
+def data_from_html(file, outpath, issues, fulldetail=False):
html = open(file, 'r').read()
+ sysvals.htmlfile = os.path.relpath(file, outpath)
+ # extract general info
suspend = find_in_html(html, 'Kernel Suspend', 'ms')
resume = find_in_html(html, 'Kernel Resume', 'ms')
+ sysinfo = find_in_html(html, '<div class="stamp sysinfo">', '</div>')
line = find_in_html(html, '<div class="stamp">', '</div>')
stmp = line.split()
if not suspend or not resume or len(stmp) != 8:
@@ -5499,6 +5941,7 @@ def data_from_html(file, outpath, devlist=False):
dt = datetime.strptime(' '.join(stmp[3:]), '%B %d %Y, %I:%M:%S %p')
except:
return False
+ sysvals.hostname = stmp[0]
tstr = dt.strftime('%Y/%m/%d %H:%M:%S')
error = find_in_html(html, '<table class="testfail"><tr><td>', '</td>')
if error:
@@ -5509,13 +5952,45 @@ def data_from_html(file, outpath, devlist=False):
result = 'fail'
else:
result = 'pass'
+ # extract error info
ilist = []
- e = find_in_html(html, 'class="err"[\w=":;\.%\- ]*>', '&rarr;</div>', False)
- for i in list(set(e)):
- ilist.append('%sx%d' % (i, e.count(i)) if e.count(i) > 1 else i)
+ extra = dict()
+ log = find_in_html(html, '<div id="dmesglog" style="display:none;">',
+ '</div>').strip()
+ if log:
+ d = Data(0)
+ d.end = 999999999
+ d.dmesgtext = log.split('\n')
+ msglist = d.extractErrorInfo()
+ for msg in msglist:
+ sysvals.errorSummary(issues, msg)
+ if stmp[2] == 'freeze':
+ extra = d.turbostatInfo()
+ elist = dict()
+ for dir in d.errorinfo:
+ for err in d.errorinfo[dir]:
+ if err[0] not in elist:
+ elist[err[0]] = 0
+ elist[err[0]] += 1
+ for i in elist:
+ ilist.append('%sx%d' % (i, elist[i]) if elist[i] > 1 else i)
low = find_in_html(html, 'freeze time: <b>', ' ms</b>')
if low and '|' in low:
- ilist.append('FREEZEx%d' % len(low.split('|')))
+ issue = 'FREEZEx%d' % len(low.split('|'))
+ match = [i for i in issues if i['match'] == issue]
+ if len(match) > 0:
+ match[0]['count'] += 1
+ if sysvals.hostname not in match[0]['urls']:
+ match[0]['urls'][sysvals.hostname] = [sysvals.htmlfile]
+ elif sysvals.htmlfile not in match[0]['urls'][sysvals.hostname]:
+ match[0]['urls'][sysvals.hostname].append(sysvals.htmlfile)
+ else:
+ issues.append({
+ 'match': issue, 'count': 1, 'line': issue,
+ 'urls': {sysvals.hostname: [sysvals.htmlfile]},
+ })
+ ilist.append(issue)
+ # extract device info
devices = dict()
for line in html.split('\n'):
m = re.match(' *<div id=\"[a,0-9]*\" *title=\"(?P<title>.*)\" class=\"thread.*', line)
@@ -5527,82 +6002,98 @@ def data_from_html(file, outpath, devlist=False):
name, time, phase = m.group('n'), m.group('t'), m.group('p')
if ' async' in name or ' sync' in name:
name = ' '.join(name.split(' ')[:-1])
- d = phase.split('_')[0]
+ if phase.startswith('suspend'):
+ d = 'suspend'
+ elif phase.startswith('resume'):
+ d = 'resume'
+ else:
+ continue
if d not in devices:
devices[d] = dict()
if name not in devices[d]:
devices[d][name] = 0.0
devices[d][name] += float(time)
- worst = {'suspend': {'name':'', 'time': 0.0},
- 'resume': {'name':'', 'time': 0.0}}
- for d in devices:
- if d not in worst:
- worst[d] = dict()
- dev = devices[d]
- if len(dev.keys()) > 0:
- n = sorted(dev, key=dev.get, reverse=True)[0]
+ # create worst device info
+ worst = dict()
+ for d in ['suspend', 'resume']:
+ worst[d] = {'name':'', 'time': 0.0}
+ dev = devices[d] if d in devices else 0
+ if dev and len(dev.keys()) > 0:
+ n = sorted(dev, key=lambda k:(dev[k], k), reverse=True)[0]
worst[d]['name'], worst[d]['time'] = n, dev[n]
data = {
'mode': stmp[2],
'host': stmp[0],
'kernel': stmp[1],
+ 'sysinfo': sysinfo,
'time': tstr,
'result': result,
'issues': ' '.join(ilist),
'suspend': suspend,
'resume': resume,
+ 'devlist': devices,
'sus_worst': worst['suspend']['name'],
'sus_worsttime': worst['suspend']['time'],
'res_worst': worst['resume']['name'],
'res_worsttime': worst['resume']['time'],
- 'url': os.path.relpath(file, outpath),
+ 'url': sysvals.htmlfile,
}
- if devlist:
- data['devlist'] = devices
+ for key in extra:
+ data[key] = extra[key]
+ if fulldetail:
+ data['funclist'] = find_in_html(html, '<div title="', '" class="traceevent"', False)
return data
+def genHtml(subdir, force=False):
+ for dirname, dirnames, filenames in os.walk(subdir):
+ sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = ''
+ for filename in filenames:
+ if(re.match('.*_dmesg.txt', filename)):
+ sysvals.dmesgfile = os.path.join(dirname, filename)
+ elif(re.match('.*_ftrace.txt', filename)):
+ sysvals.ftracefile = os.path.join(dirname, filename)
+ sysvals.setOutputFile()
+ if sysvals.ftracefile and sysvals.htmlfile and \
+ (force or not os.path.exists(sysvals.htmlfile)):
+ pprint('FTRACE: %s' % sysvals.ftracefile)
+ if sysvals.dmesgfile:
+ pprint('DMESG : %s' % sysvals.dmesgfile)
+ rerunTest()
+
# Function: runSummary
# Description:
# create a summary of tests in a sub-directory
def runSummary(subdir, local=True, genhtml=False):
inpath = os.path.abspath(subdir)
outpath = os.path.abspath('.') if local else inpath
- pprint('Generating a summary of folder "%s"' % inpath)
+ pprint('Generating a summary of folder:\n %s' % inpath)
if genhtml:
- for dirname, dirnames, filenames in os.walk(subdir):
- sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = ''
- for filename in filenames:
- if(re.match('.*_dmesg.txt', filename)):
- sysvals.dmesgfile = os.path.join(dirname, filename)
- elif(re.match('.*_ftrace.txt', filename)):
- sysvals.ftracefile = os.path.join(dirname, filename)
- sysvals.setOutputFile()
- if sysvals.ftracefile and sysvals.htmlfile and \
- not os.path.exists(sysvals.htmlfile):
- pprint('FTRACE: %s' % sysvals.ftracefile)
- if sysvals.dmesgfile:
- pprint('DMESG : %s' % sysvals.dmesgfile)
- rerunTest()
+ genHtml(subdir)
+ issues = []
testruns = []
desc = {'host':[],'mode':[],'kernel':[]}
for dirname, dirnames, filenames in os.walk(subdir):
for filename in filenames:
if(not re.match('.*.html', filename)):
continue
- data = data_from_html(os.path.join(dirname, filename), outpath)
+ data = data_from_html(os.path.join(dirname, filename), outpath, issues)
if(not data):
continue
testruns.append(data)
for key in desc:
if data[key] not in desc[key]:
desc[key].append(data[key])
- outfile = os.path.join(outpath, 'summary.html')
- pprint('Summary file: %s' % outfile)
+ pprint('Summary files:')
if len(desc['host']) == len(desc['mode']) == len(desc['kernel']) == 1:
title = '%s %s %s' % (desc['host'][0], desc['kernel'][0], desc['mode'][0])
else:
title = inpath
- createHTMLSummarySimple(testruns, outfile, title)
+ createHTMLSummarySimple(testruns, os.path.join(outpath, 'summary.html'), title)
+ pprint(' summary.html - tabular list of test data found')
+ createHTMLDeviceSummary(testruns, os.path.join(outpath, 'summary-devices.html'), title)
+ pprint(' summary-devices.html - kernel device list sorted by total execution time')
+ createHTMLIssuesSummary(testruns, issues, os.path.join(outpath, 'summary-issues.html'), title)
+ pprint(' summary-issues.html - kernel issues found sorted by frequency')
# Function: checkArgBool
# Description:
@@ -5619,7 +6110,7 @@ def checkArgBool(name, value):
# Description:
# Configure the script via the info in a config file
def configFromFile(file):
- Config = ConfigParser.ConfigParser()
+ Config = configparser.ConfigParser()
Config.read(file)
sections = Config.sections()
@@ -5847,6 +6338,7 @@ def printHelp():
' default: suspend-{date}-{time}\n'\
' -rtcwake t Wakeup t seconds after suspend, set t to "off" to disable (default: 15)\n'\
' -addlogs Add the dmesg and ftrace logs to the html output\n'\
+ ' -noturbostat Dont use turbostat in freeze mode (default: disabled)\n'\
' -srgap Add a visible gap in the timeline between sus/res (default: disabled)\n'\
' -skiphtml Run the test and capture the trace logs, but skip the timeline (default: disabled)\n'\
' -result fn Export a results table to a text file for parsing.\n'\
@@ -5868,6 +6360,7 @@ def printHelp():
' be created in a new subdirectory with a summary page.\n'\
' [debug]\n'\
' -f Use ftrace to create device callgraphs (default: disabled)\n'\
+ ' -ftop Use ftrace on the top level call: "%s" (default: disabled)\n'\
' -maxdepth N limit the callgraph data to N call levels (default: 0=all)\n'\
' -expandcg pre-expand the callgraph data in the html output (default: disabled)\n'\
' -fadd file Add functions to be graphed in the timeline from a list in a text file\n'\
@@ -5887,6 +6380,7 @@ def printHelp():
' -status Test to see if the system is enabled to run this tool\n'\
' -fpdt Print out the contents of the ACPI Firmware Performance Data Table\n'\
' -battery Print out battery info (if available)\n'\
+ ' -wifi Print out wifi connection info (if wireless-tools and device exists)\n'\
' -x<mode> Test xset by toggling the given mode (on/off/standby/suspend)\n'\
' -sysinfo Print out system info extracted from BIOS\n'\
' -devinfo Print out the pm settings of all devices which support runtime suspend\n'\
@@ -5896,7 +6390,7 @@ def printHelp():
' [redo]\n'\
' -ftrace ftracefile Create HTML output using ftrace input (used with -dmesg)\n'\
' -dmesg dmesgfile Create HTML output using dmesg (used with -ftrace)\n'\
- '' % (sysvals.title, sysvals.version, sysvals.suspendmode))
+ '' % (sysvals.title, sysvals.version, sysvals.suspendmode, sysvals.ftopfunc))
return True
# ----------------- MAIN --------------------
@@ -5906,7 +6400,7 @@ if __name__ == '__main__':
cmd = ''
simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall',
'-devinfo', '-status', '-battery', '-xon', '-xoff', '-xstandby',
- '-xsuspend', '-xinit', '-xreset', '-xstat']
+ '-xsuspend', '-xinit', '-xreset', '-xstat', '-wifi']
if '-f' in sys.argv:
sysvals.cgskip = sysvals.configFile('cgskip.txt')
# loop through the command line arguments
@@ -5914,7 +6408,7 @@ if __name__ == '__main__':
for arg in args:
if(arg == '-m'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No mode supplied', True)
if val == 'command' and not sysvals.testcommand:
@@ -5938,6 +6432,10 @@ if __name__ == '__main__':
sysvals.postdelay = getArgInt('-postdelay', args, 0, 60000)
elif(arg == '-f'):
sysvals.usecallgraph = True
+ elif(arg == '-ftop'):
+ sysvals.usecallgraph = True
+ sysvals.ftop = True
+ sysvals.usekprobes = False
elif(arg == '-skiphtml'):
sysvals.skiphtml = True
elif(arg == '-cgdump'):
@@ -5948,10 +6446,14 @@ if __name__ == '__main__':
genhtml = True
elif(arg == '-addlogs'):
sysvals.dmesglog = sysvals.ftracelog = True
+ elif(arg == '-nologs'):
+ sysvals.dmesglog = sysvals.ftracelog = False
elif(arg == '-addlogdmesg'):
sysvals.dmesglog = True
elif(arg == '-addlogftrace'):
sysvals.ftracelog = True
+ elif(arg == '-noturbostat'):
+ sysvals.tstat = False
elif(arg == '-verbose'):
sysvals.verbose = True
elif(arg == '-proc'):
@@ -5964,7 +6466,7 @@ if __name__ == '__main__':
sysvals.gzip = True
elif(arg == '-rs'):
try:
- val = args.next()
+ val = next(args)
except:
doError('-rs requires "enable" or "disable"', True)
if val.lower() in switchvalues:
@@ -5976,7 +6478,7 @@ if __name__ == '__main__':
doError('invalid option: %s, use "enable/disable" or "on/off"' % val, True)
elif(arg == '-display'):
try:
- val = args.next()
+ val = next(args)
except:
doError('-display requires an mode value', True)
disopt = ['on', 'off', 'standby', 'suspend']
@@ -5987,7 +6489,7 @@ if __name__ == '__main__':
sysvals.max_graph_depth = getArgInt('-maxdepth', args, 0, 1000)
elif(arg == '-rtcwake'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No rtcwake time supplied', True)
if val.lower() in switchoff:
@@ -6007,7 +6509,7 @@ if __name__ == '__main__':
sysvals.cgtest = getArgInt('-cgtest', args, 0, 1)
elif(arg == '-cgphase'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No phase name supplied', True)
d = Data(0)
@@ -6017,13 +6519,19 @@ if __name__ == '__main__':
sysvals.cgphase = val
elif(arg == '-cgfilter'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No callgraph functions supplied', True)
sysvals.setCallgraphFilter(val)
+ elif(arg == '-skipkprobe'):
+ try:
+ val = next(args)
+ except:
+ doError('No kprobe functions supplied', True)
+ sysvals.skipKprobes(val)
elif(arg == '-cgskip'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No file supplied', True)
if val.lower() in switchoff:
@@ -6038,7 +6546,7 @@ if __name__ == '__main__':
sysvals.callloopmaxlen = getArgFloat('-callloop-maxlen', args, 0.0, 1.0)
elif(arg == '-cmd'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No command string supplied', True)
sysvals.testcommand = val
@@ -6053,13 +6561,13 @@ if __name__ == '__main__':
sysvals.multitest['delay'] = getArgInt('-multi n d (delay between tests)', args, 0, 3600)
elif(arg == '-o'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No subdirectory name supplied', True)
sysvals.outdir = sysvals.setOutputFolder(val)
elif(arg == '-config'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No text file supplied', True)
file = sysvals.configFile(val)
@@ -6068,7 +6576,7 @@ if __name__ == '__main__':
configFromFile(file)
elif(arg == '-fadd'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No text file supplied', True)
file = sysvals.configFile(val)
@@ -6077,7 +6585,7 @@ if __name__ == '__main__':
sysvals.addFtraceFilterFunctions(file)
elif(arg == '-dmesg'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No dmesg file supplied', True)
sysvals.notestrun = True
@@ -6086,7 +6594,7 @@ if __name__ == '__main__':
doError('%s does not exist' % sysvals.dmesgfile)
elif(arg == '-ftrace'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No ftrace file supplied', True)
sysvals.notestrun = True
@@ -6095,7 +6603,7 @@ if __name__ == '__main__':
doError('%s does not exist' % sysvals.ftracefile)
elif(arg == '-summary'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No directory supplied', True)
cmd = 'summary'
@@ -6105,13 +6613,13 @@ if __name__ == '__main__':
doError('%s is not accesible' % val)
elif(arg == '-filter'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No devnames supplied', True)
sysvals.setDeviceFilter(val)
elif(arg == '-result'):
try:
- val = args.next()
+ val = next(args)
except:
doError('No result file supplied', True)
sysvals.result = val
@@ -6159,7 +6667,7 @@ if __name__ == '__main__':
elif(cmd == 'devinfo'):
deviceInfo()
elif(cmd == 'modes'):
- print getModes()
+ pprint(getModes())
elif(cmd == 'flist'):
sysvals.getFtraceFilterFunctions(True)
elif(cmd == 'flistall'):
@@ -6171,11 +6679,18 @@ if __name__ == '__main__':
ret = displayControl(cmd[1:])
elif(cmd == 'xstat'):
pprint('Display Status: %s' % displayControl('stat').upper())
+ elif(cmd == 'wifi'):
+ out = sysvals.checkWifi()
+ if 'device' not in out:
+ pprint('WIFI interface not found')
+ else:
+ for key in sorted(out):
+ pprint('%6s: %s' % (key.upper(), out[key]))
sys.exit(ret)
# if instructed, re-analyze existing data files
if(sysvals.notestrun):
- stamp = rerunTest()
+ stamp = rerunTest(sysvals.outdir)
sysvals.outputResult(stamp)
sys.exit(0)
@@ -6212,7 +6727,7 @@ if __name__ == '__main__':
s = 'suspend-x%d' % sysvals.multitest['count']
sysvals.outdir = datetime.now().strftime(s+'-%y%m%d-%H%M%S')
if not os.path.isdir(sysvals.outdir):
- os.mkdir(sysvals.outdir)
+ os.makedirs(sysvals.outdir)
for i in range(sysvals.multitest['count']):
if(i != 0):
pprint('Waiting %d seconds...' % (sysvals.multitest['delay']))
diff --git a/tools/power/x86/intel-speed-select/.gitignore b/tools/power/x86/intel-speed-select/.gitignore
new file mode 100644
index 000000000000..f61145925ce9
--- /dev/null
+++ b/tools/power/x86/intel-speed-select/.gitignore
@@ -0,0 +1,2 @@
+include/
+intel-speed-select
diff --git a/tools/power/x86/intel-speed-select/Build b/tools/power/x86/intel-speed-select/Build
new file mode 100644
index 000000000000..b61456d75190
--- /dev/null
+++ b/tools/power/x86/intel-speed-select/Build
@@ -0,0 +1 @@
+intel-speed-select-y += isst-config.o isst-core.o isst-display.o
diff --git a/tools/power/x86/intel-speed-select/Makefile b/tools/power/x86/intel-speed-select/Makefile
new file mode 100644
index 000000000000..12c6939dca2a
--- /dev/null
+++ b/tools/power/x86/intel-speed-select/Makefile
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: GPL-2.0
+include ../../../scripts/Makefile.include
+
+bindir ?= /usr/bin
+
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(CURDIR)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+endif
+
+# Do not use make's built-in rules
+# (this improves performance and avoids hard-to-debug behaviour);
+MAKEFLAGS += -r
+
+override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
+
+ALL_TARGETS := intel-speed-select
+ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
+
+all: $(ALL_PROGRAMS)
+
+export srctree OUTPUT CC LD CFLAGS
+include $(srctree)/tools/build/Makefile.include
+
+#
+# We need the following to be outside of kernel tree
+#
+$(OUTPUT)include/linux/isst_if.h: ../../../../include/uapi/linux/isst_if.h
+ mkdir -p $(OUTPUT)include/linux 2>&1 || true
+ ln -sf $(CURDIR)/../../../../include/uapi/linux/isst_if.h $@
+
+prepare: $(OUTPUT)include/linux/isst_if.h
+
+ISST_IN := $(OUTPUT)intel-speed-select-in.o
+
+$(ISST_IN): prepare FORCE
+ $(Q)$(MAKE) $(build)=intel-speed-select
+$(OUTPUT)intel-speed-select: $(ISST_IN)
+ $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
+
+clean:
+ rm -f $(ALL_PROGRAMS)
+ rm -rf $(OUTPUT)include/linux/isst_if.h
+ find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete
+
+install: $(ALL_PROGRAMS)
+ install -d -m 755 $(DESTDIR)$(bindir); \
+ for program in $(ALL_PROGRAMS); do \
+ install $$program $(DESTDIR)$(bindir); \
+ done
+
+FORCE:
+
+.PHONY: all install clean FORCE prepare
diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c
new file mode 100644
index 000000000000..59753b3917bb
--- /dev/null
+++ b/tools/power/x86/intel-speed-select/isst-config.c
@@ -0,0 +1,1612 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Speed Select -- Enumerate and control features
+ * Copyright (c) 2019 Intel Corporation.
+ */
+
+#include <linux/isst_if.h>
+
+#include "isst.h"
+
+struct process_cmd_struct {
+ char *feature;
+ char *command;
+ void (*process_fn)(void);
+};
+
+static const char *version_str = "v1.0";
+static const int supported_api_ver = 1;
+static struct isst_if_platform_info isst_platform_info;
+static char *progname;
+static int debug_flag;
+static FILE *outf;
+
+static int cpu_model;
+
+#define MAX_CPUS_IN_ONE_REQ 64
+static short max_target_cpus;
+static unsigned short target_cpus[MAX_CPUS_IN_ONE_REQ];
+
+static int topo_max_cpus;
+static size_t present_cpumask_size;
+static cpu_set_t *present_cpumask;
+static size_t target_cpumask_size;
+static cpu_set_t *target_cpumask;
+static int tdp_level = 0xFF;
+static int fact_bucket = 0xFF;
+static int fact_avx = 0xFF;
+static unsigned long long fact_trl;
+static int out_format_json;
+static int cmd_help;
+
+/* clos related */
+static int current_clos = -1;
+static int clos_epp = -1;
+static int clos_prop_prio = -1;
+static int clos_min = -1;
+static int clos_max = -1;
+static int clos_desired = -1;
+static int clos_priority_type;
+
+struct _cpu_map {
+ unsigned short core_id;
+ unsigned short pkg_id;
+ unsigned short die_id;
+ unsigned short punit_cpu;
+ unsigned short punit_cpu_core;
+};
+struct _cpu_map *cpu_map;
+
+void debug_printf(const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+
+ if (debug_flag)
+ vprintf(format, args);
+
+ va_end(args);
+}
+
+static void update_cpu_model(void)
+{
+ unsigned int ebx, ecx, edx;
+ unsigned int fms, family;
+
+ __cpuid(1, fms, ebx, ecx, edx);
+ family = (fms >> 8) & 0xf;
+ cpu_model = (fms >> 4) & 0xf;
+ if (family == 6 || family == 0xf)
+ cpu_model += ((fms >> 16) & 0xf) << 4;
+}
+
+/* Open a file, and exit on failure */
+static FILE *fopen_or_exit(const char *path, const char *mode)
+{
+ FILE *filep = fopen(path, mode);
+
+ if (!filep)
+ err(1, "%s: open failed", path);
+
+ return filep;
+}
+
+/* Parse a file containing a single int */
+static int parse_int_file(int fatal, const char *fmt, ...)
+{
+ va_list args;
+ char path[PATH_MAX];
+ FILE *filep;
+ int value;
+
+ va_start(args, fmt);
+ vsnprintf(path, sizeof(path), fmt, args);
+ va_end(args);
+ if (fatal) {
+ filep = fopen_or_exit(path, "r");
+ } else {
+ filep = fopen(path, "r");
+ if (!filep)
+ return -1;
+ }
+ if (fscanf(filep, "%d", &value) != 1)
+ err(1, "%s: failed to parse number from file", path);
+ fclose(filep);
+
+ return value;
+}
+
+int cpufreq_sysfs_present(void)
+{
+ DIR *dir;
+
+ dir = opendir("/sys/devices/system/cpu/cpu0/cpufreq");
+ if (dir) {
+ closedir(dir);
+ return 1;
+ }
+
+ return 0;
+}
+
+int out_format_is_json(void)
+{
+ return out_format_json;
+}
+
+int get_physical_package_id(int cpu)
+{
+ return parse_int_file(
+ 1, "/sys/devices/system/cpu/cpu%d/topology/physical_package_id",
+ cpu);
+}
+
+int get_physical_core_id(int cpu)
+{
+ return parse_int_file(
+ 1, "/sys/devices/system/cpu/cpu%d/topology/core_id", cpu);
+}
+
+int get_physical_die_id(int cpu)
+{
+ int ret;
+
+ ret = parse_int_file(0, "/sys/devices/system/cpu/cpu%d/topology/die_id",
+ cpu);
+ if (ret < 0)
+ ret = 0;
+
+ return ret;
+}
+
+int get_topo_max_cpus(void)
+{
+ return topo_max_cpus;
+}
+
+#define MAX_PACKAGE_COUNT 8
+#define MAX_DIE_PER_PACKAGE 2
+static void for_each_online_package_in_set(void (*callback)(int, void *, void *,
+ void *, void *),
+ void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ int max_packages[MAX_PACKAGE_COUNT * MAX_PACKAGE_COUNT];
+ int pkg_index = 0, i;
+
+ memset(max_packages, 0xff, sizeof(max_packages));
+ for (i = 0; i < topo_max_cpus; ++i) {
+ int j, online, pkg_id, die_id = 0, skip = 0;
+
+ if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask))
+ continue;
+ if (i)
+ online = parse_int_file(
+ 1, "/sys/devices/system/cpu/cpu%d/online", i);
+ else
+ online =
+ 1; /* online entry for CPU 0 needs some special configs */
+
+ die_id = get_physical_die_id(i);
+ if (die_id < 0)
+ die_id = 0;
+ pkg_id = get_physical_package_id(i);
+ /* Create an unique id for package, die combination to store */
+ pkg_id = (MAX_PACKAGE_COUNT * pkg_id + die_id);
+
+ for (j = 0; j < pkg_index; ++j) {
+ if (max_packages[j] == pkg_id) {
+ skip = 1;
+ break;
+ }
+ }
+
+ if (!skip && online && callback) {
+ callback(i, arg1, arg2, arg3, arg4);
+ max_packages[pkg_index++] = pkg_id;
+ }
+ }
+}
+
+static void for_each_online_target_cpu_in_set(
+ void (*callback)(int, void *, void *, void *, void *), void *arg1,
+ void *arg2, void *arg3, void *arg4)
+{
+ int i;
+
+ for (i = 0; i < topo_max_cpus; ++i) {
+ int online;
+
+ if (!CPU_ISSET_S(i, target_cpumask_size, target_cpumask))
+ continue;
+ if (i)
+ online = parse_int_file(
+ 1, "/sys/devices/system/cpu/cpu%d/online", i);
+ else
+ online =
+ 1; /* online entry for CPU 0 needs some special configs */
+
+ if (online && callback)
+ callback(i, arg1, arg2, arg3, arg4);
+ }
+}
+
+#define BITMASK_SIZE 32
+static void set_max_cpu_num(void)
+{
+ FILE *filep;
+ unsigned long dummy;
+
+ topo_max_cpus = 0;
+ filep = fopen_or_exit(
+ "/sys/devices/system/cpu/cpu0/topology/thread_siblings", "r");
+ while (fscanf(filep, "%lx,", &dummy) == 1)
+ topo_max_cpus += BITMASK_SIZE;
+ fclose(filep);
+ topo_max_cpus--; /* 0 based */
+
+ debug_printf("max cpus %d\n", topo_max_cpus);
+}
+
+size_t alloc_cpu_set(cpu_set_t **cpu_set)
+{
+ cpu_set_t *_cpu_set;
+ size_t size;
+
+ _cpu_set = CPU_ALLOC((topo_max_cpus + 1));
+ if (_cpu_set == NULL)
+ err(3, "CPU_ALLOC");
+ size = CPU_ALLOC_SIZE((topo_max_cpus + 1));
+ CPU_ZERO_S(size, _cpu_set);
+
+ *cpu_set = _cpu_set;
+ return size;
+}
+
+void free_cpu_set(cpu_set_t *cpu_set)
+{
+ CPU_FREE(cpu_set);
+}
+
+static int cpu_cnt[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE];
+static void set_cpu_present_cpu_mask(void)
+{
+ size_t size;
+ DIR *dir;
+ int i;
+
+ size = alloc_cpu_set(&present_cpumask);
+ present_cpumask_size = size;
+ for (i = 0; i < topo_max_cpus; ++i) {
+ char buffer[256];
+
+ snprintf(buffer, sizeof(buffer),
+ "/sys/devices/system/cpu/cpu%d", i);
+ dir = opendir(buffer);
+ if (dir) {
+ int pkg_id, die_id;
+
+ CPU_SET_S(i, size, present_cpumask);
+ die_id = get_physical_die_id(i);
+ if (die_id < 0)
+ die_id = 0;
+
+ pkg_id = get_physical_package_id(i);
+ if (pkg_id < MAX_PACKAGE_COUNT &&
+ die_id < MAX_DIE_PER_PACKAGE)
+ cpu_cnt[pkg_id][die_id]++;
+ }
+ closedir(dir);
+ }
+}
+
+int get_cpu_count(int pkg_id, int die_id)
+{
+ if (pkg_id < MAX_PACKAGE_COUNT && die_id < MAX_DIE_PER_PACKAGE)
+ return cpu_cnt[pkg_id][die_id];
+
+ return 0;
+}
+
+static void set_cpu_target_cpu_mask(void)
+{
+ size_t size;
+ int i;
+
+ size = alloc_cpu_set(&target_cpumask);
+ target_cpumask_size = size;
+ for (i = 0; i < max_target_cpus; ++i) {
+ if (!CPU_ISSET_S(target_cpus[i], present_cpumask_size,
+ present_cpumask))
+ continue;
+
+ CPU_SET_S(target_cpus[i], size, target_cpumask);
+ }
+}
+
+static void create_cpu_map(void)
+{
+ const char *pathname = "/dev/isst_interface";
+ int i, fd = 0;
+ struct isst_if_cpu_maps map;
+
+ cpu_map = malloc(sizeof(*cpu_map) * topo_max_cpus);
+ if (!cpu_map)
+ err(3, "cpumap");
+
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ err(-1, "%s open failed", pathname);
+
+ for (i = 0; i < topo_max_cpus; ++i) {
+ if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask))
+ continue;
+
+ map.cmd_count = 1;
+ map.cpu_map[0].logical_cpu = i;
+
+ debug_printf(" map logical_cpu:%d\n",
+ map.cpu_map[0].logical_cpu);
+ if (ioctl(fd, ISST_IF_GET_PHY_ID, &map) == -1) {
+ perror("ISST_IF_GET_PHY_ID");
+ fprintf(outf, "Error: map logical_cpu:%d\n",
+ map.cpu_map[0].logical_cpu);
+ continue;
+ }
+ cpu_map[i].core_id = get_physical_core_id(i);
+ cpu_map[i].pkg_id = get_physical_package_id(i);
+ cpu_map[i].die_id = get_physical_die_id(i);
+ cpu_map[i].punit_cpu = map.cpu_map[0].physical_cpu;
+ cpu_map[i].punit_cpu_core = (map.cpu_map[0].physical_cpu >>
+ 1); // shift to get core id
+
+ debug_printf(
+ "map logical_cpu:%d core: %d die:%d pkg:%d punit_cpu:%d punit_core:%d\n",
+ i, cpu_map[i].core_id, cpu_map[i].die_id,
+ cpu_map[i].pkg_id, cpu_map[i].punit_cpu,
+ cpu_map[i].punit_cpu_core);
+ }
+
+ if (fd)
+ close(fd);
+}
+
+int find_logical_cpu(int pkg_id, int die_id, int punit_core_id)
+{
+ int i;
+
+ for (i = 0; i < topo_max_cpus; ++i) {
+ if (cpu_map[i].pkg_id == pkg_id &&
+ cpu_map[i].die_id == die_id &&
+ cpu_map[i].punit_cpu_core == punit_core_id)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+void set_cpu_mask_from_punit_coremask(int cpu, unsigned long long core_mask,
+ size_t core_cpumask_size,
+ cpu_set_t *core_cpumask, int *cpu_cnt)
+{
+ int i, cnt = 0;
+ int die_id, pkg_id;
+
+ *cpu_cnt = 0;
+ die_id = get_physical_die_id(cpu);
+ pkg_id = get_physical_package_id(cpu);
+
+ for (i = 0; i < 64; ++i) {
+ if (core_mask & BIT(i)) {
+ int j;
+
+ for (j = 0; j < topo_max_cpus; ++j) {
+ if (cpu_map[j].pkg_id == pkg_id &&
+ cpu_map[j].die_id == die_id &&
+ cpu_map[j].punit_cpu_core == i) {
+ CPU_SET_S(j, core_cpumask_size,
+ core_cpumask);
+ ++cnt;
+ }
+ }
+ }
+ }
+
+ *cpu_cnt = cnt;
+}
+
+int find_phy_core_num(int logical_cpu)
+{
+ if (logical_cpu < topo_max_cpus)
+ return cpu_map[logical_cpu].punit_cpu_core;
+
+ return -EINVAL;
+}
+
+static int isst_send_mmio_command(unsigned int cpu, unsigned int reg, int write,
+ unsigned int *value)
+{
+ struct isst_if_io_regs io_regs;
+ const char *pathname = "/dev/isst_interface";
+ int cmd;
+ int fd;
+
+ debug_printf("mmio_cmd cpu:%d reg:%d write:%d\n", cpu, reg, write);
+
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ err(-1, "%s open failed", pathname);
+
+ io_regs.req_count = 1;
+ io_regs.io_reg[0].logical_cpu = cpu;
+ io_regs.io_reg[0].reg = reg;
+ cmd = ISST_IF_IO_CMD;
+ if (write) {
+ io_regs.io_reg[0].read_write = 1;
+ io_regs.io_reg[0].value = *value;
+ } else {
+ io_regs.io_reg[0].read_write = 0;
+ }
+
+ if (ioctl(fd, cmd, &io_regs) == -1) {
+ perror("ISST_IF_IO_CMD");
+ fprintf(outf, "Error: mmio_cmd cpu:%d reg:%x read_write:%x\n",
+ cpu, reg, write);
+ } else {
+ if (!write)
+ *value = io_regs.io_reg[0].value;
+
+ debug_printf(
+ "mmio_cmd response: cpu:%d reg:%x rd_write:%x resp:%x\n",
+ cpu, reg, write, *value);
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+int isst_send_mbox_command(unsigned int cpu, unsigned char command,
+ unsigned char sub_command, unsigned int parameter,
+ unsigned int req_data, unsigned int *resp)
+{
+ const char *pathname = "/dev/isst_interface";
+ int fd;
+ struct isst_if_mbox_cmds mbox_cmds = { 0 };
+
+ debug_printf(
+ "mbox_send: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x\n",
+ cpu, command, sub_command, parameter, req_data);
+
+ if (isst_platform_info.mmio_supported && command == CONFIG_CLOS) {
+ unsigned int value;
+ int write = 0;
+ int clos_id, core_id, ret = 0;
+
+ debug_printf("CLOS %d\n", cpu);
+
+ if (parameter & BIT(MBOX_CMD_WRITE_BIT)) {
+ value = req_data;
+ write = 1;
+ }
+
+ switch (sub_command) {
+ case CLOS_PQR_ASSOC:
+ core_id = parameter & 0xff;
+ ret = isst_send_mmio_command(
+ cpu, PQR_ASSOC_OFFSET + core_id * 4, write,
+ &value);
+ if (!ret && !write)
+ *resp = value;
+ break;
+ case CLOS_PM_CLOS:
+ clos_id = parameter & 0x03;
+ ret = isst_send_mmio_command(
+ cpu, PM_CLOS_OFFSET + clos_id * 4, write,
+ &value);
+ if (!ret && !write)
+ *resp = value;
+ break;
+ case CLOS_PM_QOS_CONFIG:
+ ret = isst_send_mmio_command(cpu, PM_QOS_CONFIG_OFFSET,
+ write, &value);
+ if (!ret && !write)
+ *resp = value;
+ break;
+ case CLOS_STATUS:
+ break;
+ default:
+ break;
+ }
+ return ret;
+ }
+
+ mbox_cmds.cmd_count = 1;
+ mbox_cmds.mbox_cmd[0].logical_cpu = cpu;
+ mbox_cmds.mbox_cmd[0].command = command;
+ mbox_cmds.mbox_cmd[0].sub_command = sub_command;
+ mbox_cmds.mbox_cmd[0].parameter = parameter;
+ mbox_cmds.mbox_cmd[0].req_data = req_data;
+
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ err(-1, "%s open failed", pathname);
+
+ if (ioctl(fd, ISST_IF_MBOX_COMMAND, &mbox_cmds) == -1) {
+ perror("ISST_IF_MBOX_COMMAND");
+ fprintf(outf,
+ "Error: mbox_cmd cpu:%d command:%x sub_command:%x parameter:%x req_data:%x\n",
+ cpu, command, sub_command, parameter, req_data);
+ } else {
+ *resp = mbox_cmds.mbox_cmd[0].resp_data;
+ debug_printf(
+ "mbox_cmd response: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x resp:%x\n",
+ cpu, command, sub_command, parameter, req_data, *resp);
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+int isst_send_msr_command(unsigned int cpu, unsigned int msr, int write,
+ unsigned long long *req_resp)
+{
+ struct isst_if_msr_cmds msr_cmds;
+ const char *pathname = "/dev/isst_interface";
+ int fd;
+
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ err(-1, "%s open failed", pathname);
+
+ msr_cmds.cmd_count = 1;
+ msr_cmds.msr_cmd[0].logical_cpu = cpu;
+ msr_cmds.msr_cmd[0].msr = msr;
+ msr_cmds.msr_cmd[0].read_write = write;
+ if (write)
+ msr_cmds.msr_cmd[0].data = *req_resp;
+
+ if (ioctl(fd, ISST_IF_MSR_COMMAND, &msr_cmds) == -1) {
+ perror("ISST_IF_MSR_COMMAD");
+ fprintf(outf, "Error: msr_cmd cpu:%d msr:%x read_write:%d\n",
+ cpu, msr, write);
+ } else {
+ if (!write)
+ *req_resp = msr_cmds.msr_cmd[0].data;
+
+ debug_printf(
+ "msr_cmd response: cpu:%d msr:%x rd_write:%x resp:%llx %llx\n",
+ cpu, msr, write, *req_resp, msr_cmds.msr_cmd[0].data);
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+static int isst_fill_platform_info(void)
+{
+ const char *pathname = "/dev/isst_interface";
+ int fd;
+
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ err(-1, "%s open failed", pathname);
+
+ if (ioctl(fd, ISST_IF_GET_PLATFORM_INFO, &isst_platform_info) == -1) {
+ perror("ISST_IF_GET_PLATFORM_INFO");
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+
+ if (isst_platform_info.api_version > supported_api_ver) {
+ printf("Incompatible API versions; Upgrade of tool is required\n");
+ return -1;
+ }
+ return 0;
+}
+
+static void isst_print_platform_information(void)
+{
+ struct isst_if_platform_info platform_info;
+ const char *pathname = "/dev/isst_interface";
+ int fd;
+
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ err(-1, "%s open failed", pathname);
+
+ if (ioctl(fd, ISST_IF_GET_PLATFORM_INFO, &platform_info) == -1) {
+ perror("ISST_IF_GET_PLATFORM_INFO");
+ } else {
+ fprintf(outf, "Platform: API version : %d\n",
+ platform_info.api_version);
+ fprintf(outf, "Platform: Driver version : %d\n",
+ platform_info.driver_version);
+ fprintf(outf, "Platform: mbox supported : %d\n",
+ platform_info.mbox_supported);
+ fprintf(outf, "Platform: mmio supported : %d\n",
+ platform_info.mmio_supported);
+ }
+
+ close(fd);
+
+ exit(0);
+}
+
+static void exec_on_get_ctdp_cpu(int cpu, void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ int (*fn_ptr)(int cpu, void *arg);
+ int ret;
+
+ fn_ptr = arg1;
+ ret = fn_ptr(cpu, arg2);
+ if (ret)
+ perror("get_tdp_*");
+ else
+ isst_display_result(cpu, outf, "perf-profile", (char *)arg3,
+ *(unsigned int *)arg4);
+}
+
+#define _get_tdp_level(desc, suffix, object, help) \
+ static void get_tdp_##object(void) \
+ { \
+ struct isst_pkg_ctdp ctdp; \
+\
+ if (cmd_help) { \
+ fprintf(stderr, \
+ "Print %s [No command arguments are required]\n", \
+ help); \
+ exit(0); \
+ } \
+ isst_ctdp_display_information_start(outf); \
+ if (max_target_cpus) \
+ for_each_online_target_cpu_in_set( \
+ exec_on_get_ctdp_cpu, isst_get_ctdp_##suffix, \
+ &ctdp, desc, &ctdp.object); \
+ else \
+ for_each_online_package_in_set(exec_on_get_ctdp_cpu, \
+ isst_get_ctdp_##suffix, \
+ &ctdp, desc, \
+ &ctdp.object); \
+ isst_ctdp_display_information_end(outf); \
+ }
+
+_get_tdp_level("get-config-levels", levels, levels, "TDP levels");
+_get_tdp_level("get-config-version", levels, version, "TDP version");
+_get_tdp_level("get-config-enabled", levels, enabled, "TDP enable status");
+_get_tdp_level("get-config-current_level", levels, current_level,
+ "Current TDP Level");
+_get_tdp_level("get-lock-status", levels, locked, "TDP lock status");
+
+static void dump_isst_config_for_cpu(int cpu, void *arg1, void *arg2,
+ void *arg3, void *arg4)
+{
+ struct isst_pkg_ctdp pkg_dev;
+ int ret;
+
+ memset(&pkg_dev, 0, sizeof(pkg_dev));
+ ret = isst_get_process_ctdp(cpu, tdp_level, &pkg_dev);
+ if (ret) {
+ perror("isst_get_process_ctdp");
+ } else {
+ isst_ctdp_display_information(cpu, outf, tdp_level, &pkg_dev);
+ isst_get_process_ctdp_complete(cpu, &pkg_dev);
+ }
+}
+
+static void dump_isst_config(void)
+{
+ if (cmd_help) {
+ fprintf(stderr,
+ "Print Intel(R) Speed Select Technology Performance profile configuration\n");
+ fprintf(stderr,
+ "including base frequency and turbo frequency configurations\n");
+ fprintf(stderr, "Optional: -l|--level : Specify tdp level\n");
+ fprintf(stderr,
+ "\tIf no arguments, dump information for all TDP levels\n");
+ exit(0);
+ }
+
+ isst_ctdp_display_information_start(outf);
+
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(dump_isst_config_for_cpu,
+ NULL, NULL, NULL, NULL);
+ else
+ for_each_online_package_in_set(dump_isst_config_for_cpu, NULL,
+ NULL, NULL, NULL);
+
+ isst_ctdp_display_information_end(outf);
+}
+
+static void set_tdp_level_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ int ret;
+
+ ret = isst_set_tdp_level(cpu, tdp_level);
+ if (ret)
+ perror("set_tdp_level_for_cpu");
+ else
+ isst_display_result(cpu, outf, "perf-profile", "set_tdp_level",
+ ret);
+}
+
+static void set_tdp_level(void)
+{
+ if (cmd_help) {
+ fprintf(stderr, "Set Config TDP level\n");
+ fprintf(stderr,
+ "\t Arguments: -l|--level : Specify tdp level\n");
+ exit(0);
+ }
+
+ if (tdp_level == 0xff) {
+ fprintf(outf, "Invalid command: specify tdp_level\n");
+ exit(1);
+ }
+ isst_ctdp_display_information_start(outf);
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(set_tdp_level_for_cpu, NULL,
+ NULL, NULL, NULL);
+ else
+ for_each_online_package_in_set(set_tdp_level_for_cpu, NULL,
+ NULL, NULL, NULL);
+ isst_ctdp_display_information_end(outf);
+}
+
+static void dump_pbf_config_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ struct isst_pbf_info pbf_info;
+ int ret;
+
+ ret = isst_get_pbf_info(cpu, tdp_level, &pbf_info);
+ if (ret) {
+ perror("isst_get_pbf_info");
+ } else {
+ isst_pbf_display_information(cpu, outf, tdp_level, &pbf_info);
+ isst_get_pbf_info_complete(&pbf_info);
+ }
+}
+
+static void dump_pbf_config(void)
+{
+ if (cmd_help) {
+ fprintf(stderr,
+ "Print Intel(R) Speed Select Technology base frequency configuration for a TDP level\n");
+ fprintf(stderr,
+ "\tArguments: -l|--level : Specify tdp level\n");
+ exit(0);
+ }
+
+ if (tdp_level == 0xff) {
+ fprintf(outf, "Invalid command: specify tdp_level\n");
+ exit(1);
+ }
+
+ isst_ctdp_display_information_start(outf);
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(dump_pbf_config_for_cpu, NULL,
+ NULL, NULL, NULL);
+ else
+ for_each_online_package_in_set(dump_pbf_config_for_cpu, NULL,
+ NULL, NULL, NULL);
+ isst_ctdp_display_information_end(outf);
+}
+
+static void set_pbf_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ int ret;
+ int status = *(int *)arg4;
+
+ ret = isst_set_pbf_fact_status(cpu, 1, status);
+ if (ret) {
+ perror("isst_set_pbf");
+ } else {
+ if (status)
+ isst_display_result(cpu, outf, "base-freq", "enable",
+ ret);
+ else
+ isst_display_result(cpu, outf, "base-freq", "disable",
+ ret);
+ }
+}
+
+static void set_pbf_enable(void)
+{
+ int status = 1;
+
+ if (cmd_help) {
+ fprintf(stderr,
+ "Enable Intel Speed Select Technology base frequency feature [No command arguments are required]\n");
+ exit(0);
+ }
+
+ isst_ctdp_display_information_start(outf);
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(set_pbf_for_cpu, NULL, NULL,
+ NULL, &status);
+ else
+ for_each_online_package_in_set(set_pbf_for_cpu, NULL, NULL,
+ NULL, &status);
+ isst_ctdp_display_information_end(outf);
+}
+
+static void set_pbf_disable(void)
+{
+ int status = 0;
+
+ if (cmd_help) {
+ fprintf(stderr,
+ "Disable Intel Speed Select Technology base frequency feature [No command arguments are required]\n");
+ exit(0);
+ }
+
+ isst_ctdp_display_information_start(outf);
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(set_pbf_for_cpu, NULL, NULL,
+ NULL, &status);
+ else
+ for_each_online_package_in_set(set_pbf_for_cpu, NULL, NULL,
+ NULL, &status);
+ isst_ctdp_display_information_end(outf);
+}
+
+static void dump_fact_config_for_cpu(int cpu, void *arg1, void *arg2,
+ void *arg3, void *arg4)
+{
+ struct isst_fact_info fact_info;
+ int ret;
+
+ ret = isst_get_fact_info(cpu, tdp_level, &fact_info);
+ if (ret)
+ perror("isst_get_fact_bucket_info");
+ else
+ isst_fact_display_information(cpu, outf, tdp_level, fact_bucket,
+ fact_avx, &fact_info);
+}
+
+static void dump_fact_config(void)
+{
+ if (cmd_help) {
+ fprintf(stderr,
+ "Print complete Intel Speed Select Technology turbo frequency configuration for a TDP level. Other arguments are optional.\n");
+ fprintf(stderr,
+ "\tArguments: -l|--level : Specify tdp level\n");
+ fprintf(stderr,
+ "\tArguments: -b|--bucket : Bucket index to dump\n");
+ fprintf(stderr,
+ "\tArguments: -r|--trl-type : Specify trl type: sse|avx2|avx512\n");
+ exit(0);
+ }
+
+ if (tdp_level == 0xff) {
+ fprintf(outf, "Invalid command: specify tdp_level\n");
+ exit(1);
+ }
+
+ isst_ctdp_display_information_start(outf);
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(dump_fact_config_for_cpu,
+ NULL, NULL, NULL, NULL);
+ else
+ for_each_online_package_in_set(dump_fact_config_for_cpu, NULL,
+ NULL, NULL, NULL);
+ isst_ctdp_display_information_end(outf);
+}
+
+static void set_fact_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ int ret;
+ int status = *(int *)arg4;
+
+ ret = isst_set_pbf_fact_status(cpu, 0, status);
+ if (ret)
+ perror("isst_set_fact");
+ else {
+ if (status) {
+ struct isst_pkg_ctdp pkg_dev;
+
+ ret = isst_get_ctdp_levels(cpu, &pkg_dev);
+ if (ret) {
+ isst_display_result(cpu, outf, "turbo-freq",
+ "enable", ret);
+ return;
+ }
+ ret = isst_set_trl(cpu, fact_trl);
+ isst_display_result(cpu, outf, "turbo-freq", "enable",
+ ret);
+ } else {
+ /* Since we modified TRL during Fact enable, restore it */
+ isst_set_trl_from_current_tdp(cpu, fact_trl);
+ isst_display_result(cpu, outf, "turbo-freq", "disable",
+ ret);
+ }
+ }
+}
+
+static void set_fact_enable(void)
+{
+ int status = 1;
+
+ if (cmd_help) {
+ fprintf(stderr,
+ "Enable Intel Speed Select Technology Turbo frequency feature\n");
+ fprintf(stderr,
+ "Optional: -t|--trl : Specify turbo ratio limit\n");
+ exit(0);
+ }
+
+ isst_ctdp_display_information_start(outf);
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(set_fact_for_cpu, NULL, NULL,
+ NULL, &status);
+ else
+ for_each_online_package_in_set(set_fact_for_cpu, NULL, NULL,
+ NULL, &status);
+ isst_ctdp_display_information_end(outf);
+}
+
+static void set_fact_disable(void)
+{
+ int status = 0;
+
+ if (cmd_help) {
+ fprintf(stderr,
+ "Disable Intel Speed Select Technology turbo frequency feature\n");
+ fprintf(stderr,
+ "Optional: -t|--trl : Specify turbo ratio limit\n");
+ exit(0);
+ }
+
+ isst_ctdp_display_information_start(outf);
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(set_fact_for_cpu, NULL, NULL,
+ NULL, &status);
+ else
+ for_each_online_package_in_set(set_fact_for_cpu, NULL, NULL,
+ NULL, &status);
+ isst_ctdp_display_information_end(outf);
+}
+
+static void enable_clos_qos_config(int cpu, void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ int ret;
+ int status = *(int *)arg4;
+
+ ret = isst_pm_qos_config(cpu, status, clos_priority_type);
+ if (ret) {
+ perror("isst_pm_qos_config");
+ } else {
+ if (status)
+ isst_display_result(cpu, outf, "core-power", "enable",
+ ret);
+ else
+ isst_display_result(cpu, outf, "core-power", "disable",
+ ret);
+ }
+}
+
+static void set_clos_enable(void)
+{
+ int status = 1;
+
+ if (cmd_help) {
+ fprintf(stderr, "Enable core-power for a package/die\n");
+ fprintf(stderr,
+ "\tClos Enable: Specify priority type with [--priority|-p]\n");
+ fprintf(stderr, "\t\t 0: Proportional, 1: Ordered\n");
+ exit(0);
+ }
+
+ if (cpufreq_sysfs_present()) {
+ fprintf(stderr,
+ "cpufreq subsystem and core-power enable will interfere with each other!\n");
+ }
+
+ isst_ctdp_display_information_start(outf);
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(enable_clos_qos_config, NULL,
+ NULL, NULL, &status);
+ else
+ for_each_online_package_in_set(enable_clos_qos_config, NULL,
+ NULL, NULL, &status);
+ isst_ctdp_display_information_end(outf);
+}
+
+static void set_clos_disable(void)
+{
+ int status = 0;
+
+ if (cmd_help) {
+ fprintf(stderr,
+ "Disable core-power: [No command arguments are required]\n");
+ exit(0);
+ }
+
+ isst_ctdp_display_information_start(outf);
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(enable_clos_qos_config, NULL,
+ NULL, NULL, &status);
+ else
+ for_each_online_package_in_set(enable_clos_qos_config, NULL,
+ NULL, NULL, &status);
+ isst_ctdp_display_information_end(outf);
+}
+
+static void dump_clos_config_for_cpu(int cpu, void *arg1, void *arg2,
+ void *arg3, void *arg4)
+{
+ struct isst_clos_config clos_config;
+ int ret;
+
+ ret = isst_pm_get_clos(cpu, current_clos, &clos_config);
+ if (ret)
+ perror("isst_pm_get_clos");
+ else
+ isst_clos_display_information(cpu, outf, current_clos,
+ &clos_config);
+}
+
+static void dump_clos_config(void)
+{
+ if (cmd_help) {
+ fprintf(stderr,
+ "Print Intel Speed Select Technology core power configuration\n");
+ fprintf(stderr,
+ "\tArguments: [-c | --clos]: Specify clos id\n");
+ exit(0);
+ }
+ if (current_clos < 0 || current_clos > 3) {
+ fprintf(stderr, "Invalid clos id\n");
+ exit(0);
+ }
+
+ isst_ctdp_display_information_start(outf);
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(dump_clos_config_for_cpu,
+ NULL, NULL, NULL, NULL);
+ else
+ for_each_online_package_in_set(dump_clos_config_for_cpu, NULL,
+ NULL, NULL, NULL);
+ isst_ctdp_display_information_end(outf);
+}
+
+static void set_clos_config_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ struct isst_clos_config clos_config;
+ int ret;
+
+ clos_config.pkg_id = get_physical_package_id(cpu);
+ clos_config.die_id = get_physical_die_id(cpu);
+
+ clos_config.epp = clos_epp;
+ clos_config.clos_prop_prio = clos_prop_prio;
+ clos_config.clos_min = clos_min;
+ clos_config.clos_max = clos_max;
+ clos_config.clos_desired = clos_desired;
+ ret = isst_set_clos(cpu, current_clos, &clos_config);
+ if (ret)
+ perror("isst_set_clos");
+ else
+ isst_display_result(cpu, outf, "core-power", "config", ret);
+}
+
+static void set_clos_config(void)
+{
+ if (cmd_help) {
+ fprintf(stderr,
+ "Set core-power configuration for one of the four clos ids\n");
+ fprintf(stderr,
+ "\tSpecify targeted clos id with [--clos|-c]\n");
+ fprintf(stderr, "\tSpecify clos EPP with [--epp|-e]\n");
+ fprintf(stderr,
+ "\tSpecify clos Proportional Priority [--weight|-w]\n");
+ fprintf(stderr, "\tSpecify clos min with [--min|-n]\n");
+ fprintf(stderr, "\tSpecify clos max with [--max|-m]\n");
+ fprintf(stderr, "\tSpecify clos desired with [--desired|-d]\n");
+ exit(0);
+ }
+
+ if (current_clos < 0 || current_clos > 3) {
+ fprintf(stderr, "Invalid clos id\n");
+ exit(0);
+ }
+ if (clos_epp < 0 || clos_epp > 0x0F) {
+ fprintf(stderr, "clos epp is not specified, default: 0\n");
+ clos_epp = 0;
+ }
+ if (clos_prop_prio < 0 || clos_prop_prio > 0x0F) {
+ fprintf(stderr,
+ "clos frequency weight is not specified, default: 0\n");
+ clos_prop_prio = 0;
+ }
+ if (clos_min < 0) {
+ fprintf(stderr, "clos min is not specified, default: 0\n");
+ clos_min = 0;
+ }
+ if (clos_max < 0) {
+ fprintf(stderr, "clos max is not specified, default: 0xff\n");
+ clos_max = 0xff;
+ }
+ if (clos_desired < 0) {
+ fprintf(stderr, "clos desired is not specified, default: 0\n");
+ clos_desired = 0x00;
+ }
+
+ isst_ctdp_display_information_start(outf);
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(set_clos_config_for_cpu, NULL,
+ NULL, NULL, NULL);
+ else
+ for_each_online_package_in_set(set_clos_config_for_cpu, NULL,
+ NULL, NULL, NULL);
+ isst_ctdp_display_information_end(outf);
+}
+
+static void set_clos_assoc_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ int ret;
+
+ ret = isst_clos_associate(cpu, current_clos);
+ if (ret)
+ perror("isst_clos_associate");
+ else
+ isst_display_result(cpu, outf, "core-power", "assoc", ret);
+}
+
+static void set_clos_assoc(void)
+{
+ if (cmd_help) {
+ fprintf(stderr, "Associate a clos id to a CPU\n");
+ fprintf(stderr,
+ "\tSpecify targeted clos id with [--clos|-c]\n");
+ exit(0);
+ }
+
+ if (current_clos < 0 || current_clos > 3) {
+ fprintf(stderr, "Invalid clos id\n");
+ exit(0);
+ }
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(set_clos_assoc_for_cpu, NULL,
+ NULL, NULL, NULL);
+ else {
+ fprintf(stderr,
+ "Invalid target cpu. Specify with [-c|--cpu]\n");
+ }
+}
+
+static void get_clos_assoc_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ int clos, ret;
+
+ ret = isst_clos_get_assoc_status(cpu, &clos);
+ if (ret)
+ perror("isst_clos_get_assoc_status");
+ else
+ isst_display_result(cpu, outf, "core-power", "get-assoc", clos);
+}
+
+static void get_clos_assoc(void)
+{
+ if (cmd_help) {
+ fprintf(stderr, "Get associate clos id to a CPU\n");
+ fprintf(stderr, "\tSpecify targeted cpu id with [--cpu|-c]\n");
+ exit(0);
+ }
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(get_clos_assoc_for_cpu, NULL,
+ NULL, NULL, NULL);
+ else {
+ fprintf(stderr,
+ "Invalid target cpu. Specify with [-c|--cpu]\n");
+ }
+}
+
+static struct process_cmd_struct isst_cmds[] = {
+ { "perf-profile", "get-lock-status", get_tdp_locked },
+ { "perf-profile", "get-config-levels", get_tdp_levels },
+ { "perf-profile", "get-config-version", get_tdp_version },
+ { "perf-profile", "get-config-enabled", get_tdp_enabled },
+ { "perf-profile", "get-config-current-level", get_tdp_current_level },
+ { "perf-profile", "set-config-level", set_tdp_level },
+ { "perf-profile", "info", dump_isst_config },
+ { "base-freq", "info", dump_pbf_config },
+ { "base-freq", "enable", set_pbf_enable },
+ { "base-freq", "disable", set_pbf_disable },
+ { "turbo-freq", "info", dump_fact_config },
+ { "turbo-freq", "enable", set_fact_enable },
+ { "turbo-freq", "disable", set_fact_disable },
+ { "core-power", "info", dump_clos_config },
+ { "core-power", "enable", set_clos_enable },
+ { "core-power", "disable", set_clos_disable },
+ { "core-power", "config", set_clos_config },
+ { "core-power", "assoc", set_clos_assoc },
+ { "core-power", "get-assoc", get_clos_assoc },
+ { NULL, NULL, NULL }
+};
+
+/*
+ * parse cpuset with following syntax
+ * 1,2,4..6,8-10 and set bits in cpu_subset
+ */
+void parse_cpu_command(char *optarg)
+{
+ unsigned int start, end;
+ char *next;
+
+ next = optarg;
+
+ while (next && *next) {
+ if (*next == '-') /* no negative cpu numbers */
+ goto error;
+
+ start = strtoul(next, &next, 10);
+
+ if (max_target_cpus < MAX_CPUS_IN_ONE_REQ)
+ target_cpus[max_target_cpus++] = start;
+
+ if (*next == '\0')
+ break;
+
+ if (*next == ',') {
+ next += 1;
+ continue;
+ }
+
+ if (*next == '-') {
+ next += 1; /* start range */
+ } else if (*next == '.') {
+ next += 1;
+ if (*next == '.')
+ next += 1; /* start range */
+ else
+ goto error;
+ }
+
+ end = strtoul(next, &next, 10);
+ if (end <= start)
+ goto error;
+
+ while (++start <= end) {
+ if (max_target_cpus < MAX_CPUS_IN_ONE_REQ)
+ target_cpus[max_target_cpus++] = start;
+ }
+
+ if (*next == ',')
+ next += 1;
+ else if (*next != '\0')
+ goto error;
+ }
+
+#ifdef DEBUG
+ {
+ int i;
+
+ for (i = 0; i < max_target_cpus; ++i)
+ printf("cpu [%d] in arg\n", target_cpus[i]);
+ }
+#endif
+ return;
+
+error:
+ fprintf(stderr, "\"--cpu %s\" malformed\n", optarg);
+ exit(-1);
+}
+
+static void parse_cmd_args(int argc, int start, char **argv)
+{
+ int opt;
+ int option_index;
+
+ static struct option long_options[] = {
+ { "bucket", required_argument, 0, 'b' },
+ { "level", required_argument, 0, 'l' },
+ { "trl-type", required_argument, 0, 'r' },
+ { "trl", required_argument, 0, 't' },
+ { "help", no_argument, 0, 'h' },
+ { "clos", required_argument, 0, 'c' },
+ { "desired", required_argument, 0, 'd' },
+ { "epp", required_argument, 0, 'e' },
+ { "min", required_argument, 0, 'n' },
+ { "max", required_argument, 0, 'm' },
+ { "priority", required_argument, 0, 'p' },
+ { "weight", required_argument, 0, 'w' },
+ { 0, 0, 0, 0 }
+ };
+
+ option_index = start;
+
+ optind = start + 1;
+ while ((opt = getopt_long(argc, argv, "b:l:t:c:d:e:n:m:p:w:h",
+ long_options, &option_index)) != -1) {
+ switch (opt) {
+ case 'b':
+ fact_bucket = atoi(optarg);
+ break;
+ case 'h':
+ cmd_help = 1;
+ break;
+ case 'l':
+ tdp_level = atoi(optarg);
+ break;
+ case 't':
+ sscanf(optarg, "0x%llx", &fact_trl);
+ break;
+ case 'r':
+ if (!strncmp(optarg, "sse", 3)) {
+ fact_avx = 0x01;
+ } else if (!strncmp(optarg, "avx2", 4)) {
+ fact_avx = 0x02;
+ } else if (!strncmp(optarg, "avx512", 4)) {
+ fact_avx = 0x04;
+ } else {
+ fprintf(outf, "Invalid sse,avx options\n");
+ exit(1);
+ }
+ break;
+ /* CLOS related */
+ case 'c':
+ current_clos = atoi(optarg);
+ printf("clos %d\n", current_clos);
+ break;
+ case 'd':
+ clos_desired = atoi(optarg);
+ break;
+ case 'e':
+ clos_epp = atoi(optarg);
+ break;
+ case 'n':
+ clos_min = atoi(optarg);
+ break;
+ case 'm':
+ clos_max = atoi(optarg);
+ break;
+ case 'p':
+ clos_priority_type = atoi(optarg);
+ break;
+ case 'w':
+ clos_prop_prio = atoi(optarg);
+ break;
+ default:
+ printf("no match\n");
+ }
+ }
+}
+
+static void isst_help(void)
+{
+ printf("perf-profile:\tAn architectural mechanism that allows multiple optimized \n\
+ performance profiles per system via static and/or dynamic\n\
+ adjustment of core count, workload, Tjmax, and\n\
+ TDP, etc.\n");
+ printf("\nCommands : For feature=perf-profile\n");
+ printf("\tinfo\n");
+ printf("\tget-lock-status\n");
+ printf("\tget-config-levels\n");
+ printf("\tget-config-version\n");
+ printf("\tget-config-enabled\n");
+ printf("\tget-config-current-level\n");
+ printf("\tset-config-level\n");
+}
+
+static void pbf_help(void)
+{
+ printf("base-freq:\tEnables users to increase guaranteed base frequency\n\
+ on certain cores (high priority cores) in exchange for lower\n\
+ base frequency on remaining cores (low priority cores).\n");
+ printf("\tcommand : info\n");
+ printf("\tcommand : enable\n");
+ printf("\tcommand : disable\n");
+}
+
+static void fact_help(void)
+{
+ printf("turbo-freq:\tEnables the ability to set different turbo ratio\n\
+ limits to cores based on priority.\n");
+ printf("\nCommand: For feature=turbo-freq\n");
+ printf("\tcommand : info\n");
+ printf("\tcommand : enable\n");
+ printf("\tcommand : disable\n");
+}
+
+static void core_power_help(void)
+{
+ printf("core-power:\tInterface that allows user to define per core/tile\n\
+ priority.\n");
+ printf("\nCommands : For feature=core-power\n");
+ printf("\tinfo\n");
+ printf("\tenable\n");
+ printf("\tdisable\n");
+ printf("\tconfig\n");
+ printf("\tassoc\n");
+ printf("\tget-assoc\n");
+}
+
+struct process_cmd_help_struct {
+ char *feature;
+ void (*process_fn)(void);
+};
+
+static struct process_cmd_help_struct isst_help_cmds[] = {
+ { "perf-profile", isst_help },
+ { "base-freq", pbf_help },
+ { "turbo-freq", fact_help },
+ { "core-power", core_power_help },
+ { NULL, NULL }
+};
+
+void process_command(int argc, char **argv)
+{
+ int i = 0, matched = 0;
+ char *feature = argv[optind];
+ char *cmd = argv[optind + 1];
+
+ if (!feature || !cmd)
+ return;
+
+ debug_printf("feature name [%s] command [%s]\n", feature, cmd);
+ if (!strcmp(cmd, "-h") || !strcmp(cmd, "--help")) {
+ while (isst_help_cmds[i].feature) {
+ if (!strcmp(isst_help_cmds[i].feature, feature)) {
+ isst_help_cmds[i].process_fn();
+ exit(0);
+ }
+ ++i;
+ }
+ }
+
+ create_cpu_map();
+
+ i = 0;
+ while (isst_cmds[i].feature) {
+ if (!strcmp(isst_cmds[i].feature, feature) &&
+ !strcmp(isst_cmds[i].command, cmd)) {
+ parse_cmd_args(argc, optind + 1, argv);
+ isst_cmds[i].process_fn();
+ matched = 1;
+ break;
+ }
+ ++i;
+ }
+
+ if (!matched)
+ fprintf(stderr, "Invalid command\n");
+}
+
+static void usage(void)
+{
+ printf("Intel(R) Speed Select Technology\n");
+ printf("\nUsage:\n");
+ printf("intel-speed-select [OPTIONS] FEATURE COMMAND COMMAND_ARGUMENTS\n");
+ printf("\nUse this tool to enumerate and control the Intel Speed Select Technology features,\n");
+ printf("\nFEATURE : [perf-profile|base-freq|turbo-freq|core-power]\n");
+ printf("\nFor help on each feature, use -h|--help\n");
+ printf("\tFor example: intel-speed-select perf-profile -h\n");
+
+ printf("\nFor additional help on each command for a feature, use --h|--help\n");
+ printf("\tFor example: intel-speed-select perf-profile get-lock-status -h\n");
+ printf("\t\t This will print help for the command \"get-lock-status\" for the feature \"perf-profile\"\n");
+
+ printf("\nOPTIONS\n");
+ printf("\t[-c|--cpu] : logical cpu number\n");
+ printf("\t\tDefault: Die scoped for all dies in the system with multiple dies/package\n");
+ printf("\t\t\t Or Package scoped for all Packages when each package contains one die\n");
+ printf("\t[-d|--debug] : Debug mode\n");
+ printf("\t[-h|--help] : Print help\n");
+ printf("\t[-i|--info] : Print platform information\n");
+ printf("\t[-o|--out] : Output file\n");
+ printf("\t\t\tDefault : stderr\n");
+ printf("\t[-f|--format] : output format [json|text]. Default: text\n");
+ printf("\t[-v|--version] : Print version\n");
+
+ printf("\nResult format\n");
+ printf("\tResult display uses a common format for each command:\n");
+ printf("\tResults are formatted in text/JSON with\n");
+ printf("\t\tPackage, Die, CPU, and command specific results.\n");
+ exit(1);
+}
+
+static void print_version(void)
+{
+ fprintf(outf, "Version %s\n", version_str);
+ fprintf(outf, "Build date %s time %s\n", __DATE__, __TIME__);
+ exit(0);
+}
+
+static void cmdline(int argc, char **argv)
+{
+ int opt;
+ int option_index = 0;
+ int ret;
+
+ static struct option long_options[] = {
+ { "cpu", required_argument, 0, 'c' },
+ { "debug", no_argument, 0, 'd' },
+ { "format", required_argument, 0, 'f' },
+ { "help", no_argument, 0, 'h' },
+ { "info", no_argument, 0, 'i' },
+ { "out", required_argument, 0, 'o' },
+ { "version", no_argument, 0, 'v' },
+ { 0, 0, 0, 0 }
+ };
+
+ progname = argv[0];
+ while ((opt = getopt_long_only(argc, argv, "+c:df:hio:v", long_options,
+ &option_index)) != -1) {
+ switch (opt) {
+ case 'c':
+ parse_cpu_command(optarg);
+ break;
+ case 'd':
+ debug_flag = 1;
+ printf("Debug Mode ON\n");
+ break;
+ case 'f':
+ if (!strncmp(optarg, "json", 4))
+ out_format_json = 1;
+ break;
+ case 'h':
+ usage();
+ break;
+ case 'i':
+ isst_print_platform_information();
+ break;
+ case 'o':
+ if (outf)
+ fclose(outf);
+ outf = fopen_or_exit(optarg, "w");
+ break;
+ case 'v':
+ print_version();
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "Must run as root\n");
+ exit(0);
+ }
+
+ if (optind > (argc - 2)) {
+ fprintf(stderr, "Feature name and|or command not specified\n");
+ exit(0);
+ }
+ update_cpu_model();
+ printf("Intel(R) Speed Select Technology\n");
+ printf("Executing on CPU model:%d[0x%x]\n", cpu_model, cpu_model);
+ set_max_cpu_num();
+ set_cpu_present_cpu_mask();
+ set_cpu_target_cpu_mask();
+ ret = isst_fill_platform_info();
+ if (ret)
+ goto out;
+
+ process_command(argc, argv);
+out:
+ free_cpu_set(present_cpumask);
+ free_cpu_set(target_cpumask);
+}
+
+int main(int argc, char **argv)
+{
+ outf = stderr;
+ cmdline(argc, argv);
+ return 0;
+}
diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c
new file mode 100644
index 000000000000..0bf341ad9697
--- /dev/null
+++ b/tools/power/x86/intel-speed-select/isst-core.c
@@ -0,0 +1,743 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Speed Select -- Enumerate and control features
+ * Copyright (c) 2019 Intel Corporation.
+ */
+
+#include "isst.h"
+
+int isst_get_ctdp_levels(int cpu, struct isst_pkg_ctdp *pkg_dev)
+{
+ unsigned int resp;
+ int ret;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP,
+ CONFIG_TDP_GET_LEVELS_INFO, 0, 0, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CONFIG_TDP_GET_LEVELS_INFO resp:%x\n", cpu, resp);
+
+ pkg_dev->version = resp & 0xff;
+ pkg_dev->levels = (resp >> 8) & 0xff;
+ pkg_dev->current_level = (resp >> 16) & 0xff;
+ pkg_dev->locked = !!(resp & BIT(24));
+ pkg_dev->enabled = !!(resp & BIT(31));
+
+ return 0;
+}
+
+int isst_get_ctdp_control(int cpu, int config_index,
+ struct isst_pkg_ctdp_level_info *ctdp_level)
+{
+ unsigned int resp;
+ int ret;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP,
+ CONFIG_TDP_GET_TDP_CONTROL, 0,
+ config_index, &resp);
+ if (ret)
+ return ret;
+
+ ctdp_level->fact_support = resp & BIT(0);
+ ctdp_level->pbf_support = !!(resp & BIT(1));
+ ctdp_level->fact_enabled = !!(resp & BIT(16));
+ ctdp_level->pbf_enabled = !!(resp & BIT(17));
+
+ debug_printf(
+ "cpu:%d CONFIG_TDP_GET_TDP_CONTROL resp:%x fact_support:%d pbf_support: %d fact_enabled:%d pbf_enabled:%d\n",
+ cpu, resp, ctdp_level->fact_support, ctdp_level->pbf_support,
+ ctdp_level->fact_enabled, ctdp_level->pbf_enabled);
+
+ return 0;
+}
+
+int isst_get_tdp_info(int cpu, int config_index,
+ struct isst_pkg_ctdp_level_info *ctdp_level)
+{
+ unsigned int resp;
+ int ret;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP, CONFIG_TDP_GET_TDP_INFO,
+ 0, config_index, &resp);
+ if (ret)
+ return ret;
+
+ ctdp_level->pkg_tdp = resp & GENMASK(14, 0);
+ ctdp_level->tdp_ratio = (resp & GENMASK(23, 16)) >> 16;
+
+ debug_printf(
+ "cpu:%d ctdp:%d CONFIG_TDP_GET_TDP_INFO resp:%x tdp_ratio:%d pkg_tdp:%d\n",
+ cpu, config_index, resp, ctdp_level->tdp_ratio,
+ ctdp_level->pkg_tdp);
+ return 0;
+}
+
+int isst_get_pwr_info(int cpu, int config_index,
+ struct isst_pkg_ctdp_level_info *ctdp_level)
+{
+ unsigned int resp;
+ int ret;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP, CONFIG_TDP_GET_PWR_INFO,
+ 0, config_index, &resp);
+ if (ret)
+ return ret;
+
+ ctdp_level->pkg_max_power = resp & GENMASK(14, 0);
+ ctdp_level->pkg_min_power = (resp & GENMASK(30, 16)) >> 16;
+
+ debug_printf(
+ "cpu:%d ctdp:%d CONFIG_TDP_GET_PWR_INFO resp:%x pkg_max_power:%d pkg_min_power:%d\n",
+ cpu, config_index, resp, ctdp_level->pkg_max_power,
+ ctdp_level->pkg_min_power);
+
+ return 0;
+}
+
+int isst_get_tjmax_info(int cpu, int config_index,
+ struct isst_pkg_ctdp_level_info *ctdp_level)
+{
+ unsigned int resp;
+ int ret;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP, CONFIG_TDP_GET_TJMAX_INFO,
+ 0, config_index, &resp);
+ if (ret)
+ return ret;
+
+ ctdp_level->t_proc_hot = resp & GENMASK(7, 0);
+
+ debug_printf(
+ "cpu:%d ctdp:%d CONFIG_TDP_GET_TJMAX_INFO resp:%x t_proc_hot:%d\n",
+ cpu, config_index, resp, ctdp_level->t_proc_hot);
+
+ return 0;
+}
+
+int isst_get_coremask_info(int cpu, int config_index,
+ struct isst_pkg_ctdp_level_info *ctdp_level)
+{
+ unsigned int resp;
+ int i, ret;
+
+ ctdp_level->cpu_count = 0;
+ for (i = 0; i < 2; ++i) {
+ unsigned long long mask;
+ int cpu_count = 0;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP,
+ CONFIG_TDP_GET_CORE_MASK, 0,
+ (i << 8) | config_index, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf(
+ "cpu:%d ctdp:%d mask:%d CONFIG_TDP_GET_CORE_MASK resp:%x\n",
+ cpu, config_index, i, resp);
+
+ mask = (unsigned long long)resp << (32 * i);
+ set_cpu_mask_from_punit_coremask(cpu, mask,
+ ctdp_level->core_cpumask_size,
+ ctdp_level->core_cpumask,
+ &cpu_count);
+ ctdp_level->cpu_count += cpu_count;
+ debug_printf("cpu:%d ctdp:%d mask:%d cpu count:%d\n", cpu,
+ config_index, i, ctdp_level->cpu_count);
+ }
+
+ return 0;
+}
+
+int isst_get_get_trl(int cpu, int level, int avx_level, int *trl)
+{
+ unsigned int req, resp;
+ int ret;
+
+ req = level | (avx_level << 16);
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP,
+ CONFIG_TDP_GET_TURBO_LIMIT_RATIOS, 0, req,
+ &resp);
+ if (ret)
+ return ret;
+
+ debug_printf(
+ "cpu:%d CONFIG_TDP_GET_TURBO_LIMIT_RATIOS req:%x resp:%x\n",
+ cpu, req, resp);
+
+ trl[0] = resp & GENMASK(7, 0);
+ trl[1] = (resp & GENMASK(15, 8)) >> 8;
+ trl[2] = (resp & GENMASK(23, 16)) >> 16;
+ trl[3] = (resp & GENMASK(31, 24)) >> 24;
+
+ req = level | BIT(8) | (avx_level << 16);
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP,
+ CONFIG_TDP_GET_TURBO_LIMIT_RATIOS, 0, req,
+ &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CONFIG_TDP_GET_TURBO_LIMIT req:%x resp:%x\n", cpu,
+ req, resp);
+
+ trl[4] = resp & GENMASK(7, 0);
+ trl[5] = (resp & GENMASK(15, 8)) >> 8;
+ trl[6] = (resp & GENMASK(23, 16)) >> 16;
+ trl[7] = (resp & GENMASK(31, 24)) >> 24;
+
+ return 0;
+}
+
+int isst_get_trl_bucket_info(int cpu, unsigned long long *buckets_info)
+{
+ int ret;
+
+ debug_printf("cpu:%d bucket info via MSR\n", cpu);
+
+ *buckets_info = 0;
+
+ ret = isst_send_msr_command(cpu, 0x1ae, 0, buckets_info);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d bucket info via MSR successful 0x%llx\n", cpu,
+ *buckets_info);
+
+ return 0;
+}
+
+int isst_set_tdp_level_msr(int cpu, int tdp_level)
+{
+ unsigned long long level = tdp_level;
+ int ret;
+
+ debug_printf("cpu: tdp_level via MSR %d\n", cpu, tdp_level);
+
+ if (isst_get_config_tdp_lock_status(cpu)) {
+ debug_printf("cpu: tdp_locked %d\n", cpu);
+ return -1;
+ }
+
+ if (tdp_level > 2)
+ return -1; /* invalid value */
+
+ ret = isst_send_msr_command(cpu, 0x64b, 1, &level);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu: tdp_level via MSR successful %d\n", cpu, tdp_level);
+
+ return 0;
+}
+
+int isst_set_tdp_level(int cpu, int tdp_level)
+{
+ unsigned int resp;
+ int ret;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP, CONFIG_TDP_SET_LEVEL, 0,
+ tdp_level, &resp);
+ if (ret)
+ return isst_set_tdp_level_msr(cpu, tdp_level);
+
+ return 0;
+}
+
+int isst_get_pbf_info(int cpu, int level, struct isst_pbf_info *pbf_info)
+{
+ unsigned int req, resp;
+ int i, ret;
+
+ pbf_info->core_cpumask_size = alloc_cpu_set(&pbf_info->core_cpumask);
+
+ for (i = 0; i < 2; ++i) {
+ unsigned long long mask;
+ int count;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP,
+ CONFIG_TDP_PBF_GET_CORE_MASK_INFO,
+ 0, (i << 8) | level, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf(
+ "cpu:%d CONFIG_TDP_PBF_GET_CORE_MASK_INFO resp:%x\n",
+ cpu, resp);
+
+ mask = (unsigned long long)resp << (32 * i);
+ set_cpu_mask_from_punit_coremask(cpu, mask,
+ pbf_info->core_cpumask_size,
+ pbf_info->core_cpumask,
+ &count);
+ }
+
+ req = level;
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP,
+ CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO, 0, req,
+ &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO resp:%x\n", cpu,
+ resp);
+
+ pbf_info->p1_low = resp & 0xff;
+ pbf_info->p1_high = (resp & GENMASK(15, 8)) >> 8;
+
+ req = level;
+ ret = isst_send_mbox_command(
+ cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_TDP_INFO, 0, req, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CONFIG_TDP_PBF_GET_TDP_INFO resp:%x\n", cpu, resp);
+
+ pbf_info->tdp = resp & 0xffff;
+
+ req = level;
+ ret = isst_send_mbox_command(
+ cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_TJ_MAX_INFO, 0, req, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CONFIG_TDP_PBF_GET_TJ_MAX_INFO resp:%x\n", cpu,
+ resp);
+ pbf_info->t_control = (resp >> 8) & 0xff;
+ pbf_info->t_prochot = resp & 0xff;
+
+ return 0;
+}
+
+void isst_get_pbf_info_complete(struct isst_pbf_info *pbf_info)
+{
+ free_cpu_set(pbf_info->core_cpumask);
+}
+
+int isst_set_pbf_fact_status(int cpu, int pbf, int enable)
+{
+ struct isst_pkg_ctdp pkg_dev;
+ struct isst_pkg_ctdp_level_info ctdp_level;
+ int current_level;
+ unsigned int req = 0, resp;
+ int ret;
+
+ ret = isst_get_ctdp_levels(cpu, &pkg_dev);
+ if (ret)
+ return ret;
+
+ current_level = pkg_dev.current_level;
+
+ ret = isst_get_ctdp_control(cpu, current_level, &ctdp_level);
+ if (ret)
+ return ret;
+
+ if (pbf) {
+ if (ctdp_level.fact_enabled)
+ req = BIT(16);
+
+ if (enable)
+ req |= BIT(17);
+ else
+ req &= ~BIT(17);
+ } else {
+ if (ctdp_level.pbf_enabled)
+ req = BIT(17);
+
+ if (enable)
+ req |= BIT(16);
+ else
+ req &= ~BIT(16);
+ }
+
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP,
+ CONFIG_TDP_SET_TDP_CONTROL, 0, req, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CONFIG_TDP_SET_TDP_CONTROL pbf/fact:%d req:%x\n",
+ cpu, pbf, req);
+
+ return 0;
+}
+
+int isst_get_fact_bucket_info(int cpu, int level,
+ struct isst_fact_bucket_info *bucket_info)
+{
+ unsigned int resp;
+ int i, k, ret;
+
+ for (i = 0; i < 2; ++i) {
+ int j;
+
+ ret = isst_send_mbox_command(
+ cpu, CONFIG_TDP,
+ CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES, 0,
+ (i << 8) | level, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf(
+ "cpu:%d CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES index:%d level:%d resp:%x\n",
+ cpu, i, level, resp);
+
+ for (j = 0; j < 4; ++j) {
+ bucket_info[j + (i * 4)].high_priority_cores_count =
+ (resp >> (j * 8)) & 0xff;
+ }
+ }
+
+ for (k = 0; k < 3; ++k) {
+ for (i = 0; i < 2; ++i) {
+ int j;
+
+ ret = isst_send_mbox_command(
+ cpu, CONFIG_TDP,
+ CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS, 0,
+ (k << 16) | (i << 8) | level, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf(
+ "cpu:%d CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS index:%d level:%d avx:%d resp:%x\n",
+ cpu, i, level, k, resp);
+
+ for (j = 0; j < 4; ++j) {
+ switch (k) {
+ case 0:
+ bucket_info[j + (i * 4)].sse_trl =
+ (resp >> (j * 8)) & 0xff;
+ break;
+ case 1:
+ bucket_info[j + (i * 4)].avx_trl =
+ (resp >> (j * 8)) & 0xff;
+ break;
+ case 2:
+ bucket_info[j + (i * 4)].avx512_trl =
+ (resp >> (j * 8)) & 0xff;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+int isst_get_fact_info(int cpu, int level, struct isst_fact_info *fact_info)
+{
+ unsigned int resp;
+ int ret;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP,
+ CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO, 0,
+ level, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO resp:%x\n",
+ cpu, resp);
+
+ fact_info->lp_clipping_ratio_license_sse = resp & 0xff;
+ fact_info->lp_clipping_ratio_license_avx2 = (resp >> 8) & 0xff;
+ fact_info->lp_clipping_ratio_license_avx512 = (resp >> 16) & 0xff;
+
+ ret = isst_get_fact_bucket_info(cpu, level, fact_info->bucket_info);
+
+ return ret;
+}
+
+int isst_set_trl(int cpu, unsigned long long trl)
+{
+ int ret;
+
+ if (!trl)
+ trl = 0xFFFFFFFFFFFFFFFFULL;
+
+ ret = isst_send_msr_command(cpu, 0x1AD, 1, &trl);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int isst_set_trl_from_current_tdp(int cpu, unsigned long long trl)
+{
+ unsigned long long msr_trl;
+ int ret;
+
+ if (trl) {
+ msr_trl = trl;
+ } else {
+ struct isst_pkg_ctdp pkg_dev;
+ int trl[8];
+ int i;
+
+ ret = isst_get_ctdp_levels(cpu, &pkg_dev);
+ if (ret)
+ return ret;
+
+ ret = isst_get_get_trl(cpu, pkg_dev.current_level, 0, trl);
+ if (ret)
+ return ret;
+
+ msr_trl = 0;
+ for (i = 0; i < 8; ++i) {
+ unsigned long long _trl = trl[i];
+
+ msr_trl |= (_trl << (i * 8));
+ }
+ }
+ ret = isst_send_msr_command(cpu, 0x1AD, 1, &msr_trl);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* Return 1 if locked */
+int isst_get_config_tdp_lock_status(int cpu)
+{
+ unsigned long long tdp_control = 0;
+ int ret;
+
+ ret = isst_send_msr_command(cpu, 0x64b, 0, &tdp_control);
+ if (ret)
+ return ret;
+
+ ret = !!(tdp_control & BIT(31));
+
+ return ret;
+}
+
+void isst_get_process_ctdp_complete(int cpu, struct isst_pkg_ctdp *pkg_dev)
+{
+ int i;
+
+ if (!pkg_dev->processed)
+ return;
+
+ for (i = 0; i < pkg_dev->levels; ++i) {
+ struct isst_pkg_ctdp_level_info *ctdp_level;
+
+ ctdp_level = &pkg_dev->ctdp_level[i];
+ if (ctdp_level->pbf_support)
+ free_cpu_set(ctdp_level->pbf_info.core_cpumask);
+ free_cpu_set(ctdp_level->core_cpumask);
+ }
+}
+
+int isst_get_process_ctdp(int cpu, int tdp_level, struct isst_pkg_ctdp *pkg_dev)
+{
+ int i, ret;
+
+ if (pkg_dev->processed)
+ return 0;
+
+ ret = isst_get_ctdp_levels(cpu, pkg_dev);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu: %d ctdp enable:%d current level: %d levels:%d\n",
+ cpu, pkg_dev->enabled, pkg_dev->current_level,
+ pkg_dev->levels);
+
+ for (i = 0; i <= pkg_dev->levels; ++i) {
+ struct isst_pkg_ctdp_level_info *ctdp_level;
+
+ if (tdp_level != 0xff && i != tdp_level)
+ continue;
+
+ debug_printf("cpu:%d Get Information for TDP level:%d\n", cpu,
+ i);
+ ctdp_level = &pkg_dev->ctdp_level[i];
+
+ ctdp_level->processed = 1;
+ ctdp_level->level = i;
+ ctdp_level->control_cpu = cpu;
+ ctdp_level->pkg_id = get_physical_package_id(cpu);
+ ctdp_level->die_id = get_physical_die_id(cpu);
+
+ ret = isst_get_ctdp_control(cpu, i, ctdp_level);
+ if (ret)
+ return ret;
+
+ ret = isst_get_tdp_info(cpu, i, ctdp_level);
+ if (ret)
+ return ret;
+
+ ret = isst_get_pwr_info(cpu, i, ctdp_level);
+ if (ret)
+ return ret;
+
+ ret = isst_get_tjmax_info(cpu, i, ctdp_level);
+ if (ret)
+ return ret;
+
+ ctdp_level->core_cpumask_size =
+ alloc_cpu_set(&ctdp_level->core_cpumask);
+ ret = isst_get_coremask_info(cpu, i, ctdp_level);
+ if (ret)
+ return ret;
+
+ ret = isst_get_trl_bucket_info(cpu, &ctdp_level->buckets_info);
+ if (ret)
+ return ret;
+
+ ret = isst_get_get_trl(cpu, i, 0,
+ ctdp_level->trl_sse_active_cores);
+ if (ret)
+ return ret;
+
+ ret = isst_get_get_trl(cpu, i, 1,
+ ctdp_level->trl_avx_active_cores);
+ if (ret)
+ return ret;
+
+ ret = isst_get_get_trl(cpu, i, 2,
+ ctdp_level->trl_avx_512_active_cores);
+ if (ret)
+ return ret;
+
+ if (ctdp_level->pbf_support) {
+ ret = isst_get_pbf_info(cpu, i, &ctdp_level->pbf_info);
+ if (!ret)
+ ctdp_level->pbf_found = 1;
+ }
+
+ if (ctdp_level->fact_support) {
+ ret = isst_get_fact_info(cpu, i,
+ &ctdp_level->fact_info);
+ if (ret)
+ return ret;
+ }
+ }
+
+ pkg_dev->processed = 1;
+
+ return 0;
+}
+
+int isst_pm_qos_config(int cpu, int enable_clos, int priority_type)
+{
+ unsigned int req, resp;
+ int ret;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0,
+ &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CLOS_PM_QOS_CONFIG resp:%x\n", cpu, resp);
+
+ req = resp;
+
+ if (enable_clos)
+ req = req | BIT(1);
+ else
+ req = req & ~BIT(1);
+
+ if (priority_type)
+ req = req | BIT(2);
+ else
+ req = req & ~BIT(2);
+
+ ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG,
+ BIT(MBOX_CMD_WRITE_BIT), req, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CLOS_PM_QOS_CONFIG priority type:%d req:%x\n", cpu,
+ priority_type, req);
+
+ return 0;
+}
+
+int isst_pm_get_clos(int cpu, int clos, struct isst_clos_config *clos_config)
+{
+ unsigned int resp;
+ int ret;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PM_CLOS, clos, 0,
+ &resp);
+ if (ret)
+ return ret;
+
+ clos_config->pkg_id = get_physical_package_id(cpu);
+ clos_config->die_id = get_physical_die_id(cpu);
+
+ clos_config->epp = resp & 0x0f;
+ clos_config->clos_prop_prio = (resp >> 4) & 0x0f;
+ clos_config->clos_min = (resp >> 8) & 0xff;
+ clos_config->clos_max = (resp >> 16) & 0xff;
+ clos_config->clos_desired = (resp >> 24) & 0xff;
+
+ return 0;
+}
+
+int isst_set_clos(int cpu, int clos, struct isst_clos_config *clos_config)
+{
+ unsigned int req, resp;
+ unsigned int param;
+ int ret;
+
+ req = clos_config->epp & 0x0f;
+ req |= (clos_config->clos_prop_prio & 0x0f) << 4;
+ req |= (clos_config->clos_min & 0xff) << 8;
+ req |= (clos_config->clos_max & 0xff) << 16;
+ req |= (clos_config->clos_desired & 0xff) << 24;
+
+ param = BIT(MBOX_CMD_WRITE_BIT) | clos;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PM_CLOS, param, req,
+ &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CLOS_PM_CLOS param:%x req:%x\n", cpu, param, req);
+
+ return 0;
+}
+
+int isst_clos_get_assoc_status(int cpu, int *clos_id)
+{
+ unsigned int resp;
+ unsigned int param;
+ int core_id, ret;
+
+ core_id = find_phy_core_num(cpu);
+ param = core_id;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param, 0,
+ &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CLOS_PQR_ASSOC param:%x resp:%x\n", cpu, param,
+ resp);
+ *clos_id = (resp >> 16) & 0x03;
+
+ return 0;
+}
+
+int isst_clos_associate(int cpu, int clos_id)
+{
+ unsigned int req, resp;
+ unsigned int param;
+ int core_id, ret;
+
+ req = (clos_id & 0x03) << 16;
+ core_id = find_phy_core_num(cpu);
+ param = BIT(MBOX_CMD_WRITE_BIT) | core_id;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param,
+ req, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CLOS_PQR_ASSOC param:%x req:%x\n", cpu, param,
+ req);
+
+ return 0;
+}
diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c
new file mode 100644
index 000000000000..df4aa99c4e92
--- /dev/null
+++ b/tools/power/x86/intel-speed-select/isst-display.c
@@ -0,0 +1,529 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel dynamic_speed_select -- Enumerate and control features
+ * Copyright (c) 2019 Intel Corporation.
+ */
+
+#include "isst.h"
+
+#define DISP_FREQ_MULTIPLIER 100
+
+static void printcpulist(int str_len, char *str, int mask_size,
+ cpu_set_t *cpu_mask)
+{
+ int i, first, curr_index, index;
+
+ if (!CPU_COUNT_S(mask_size, cpu_mask)) {
+ snprintf(str, str_len, "none");
+ return;
+ }
+
+ curr_index = 0;
+ first = 1;
+ for (i = 0; i < get_topo_max_cpus(); ++i) {
+ if (!CPU_ISSET_S(i, mask_size, cpu_mask))
+ continue;
+ if (!first) {
+ index = snprintf(&str[curr_index],
+ str_len - curr_index, ",");
+ curr_index += index;
+ }
+ index = snprintf(&str[curr_index], str_len - curr_index, "%d",
+ i);
+ curr_index += index;
+ first = 0;
+ }
+}
+
+static void printcpumask(int str_len, char *str, int mask_size,
+ cpu_set_t *cpu_mask)
+{
+ int i, max_cpus = get_topo_max_cpus();
+ unsigned int *mask;
+ int size, index, curr_index;
+
+ size = max_cpus / (sizeof(unsigned int) * 8);
+ if (max_cpus % (sizeof(unsigned int) * 8))
+ size++;
+
+ mask = calloc(size, sizeof(unsigned int));
+ if (!mask)
+ return;
+
+ for (i = 0; i < max_cpus; ++i) {
+ int mask_index, bit_index;
+
+ if (!CPU_ISSET_S(i, mask_size, cpu_mask))
+ continue;
+
+ mask_index = i / (sizeof(unsigned int) * 8);
+ bit_index = i % (sizeof(unsigned int) * 8);
+ mask[mask_index] |= BIT(bit_index);
+ }
+
+ curr_index = 0;
+ for (i = size - 1; i >= 0; --i) {
+ index = snprintf(&str[curr_index], str_len - curr_index, "%08x",
+ mask[i]);
+ curr_index += index;
+ if (i) {
+ strncat(&str[curr_index], ",", str_len - curr_index);
+ curr_index++;
+ }
+ }
+
+ free(mask);
+}
+
+static void format_and_print_txt(FILE *outf, int level, char *header,
+ char *value)
+{
+ char *spaces = " ";
+ static char delimiters[256];
+ int i, j = 0;
+
+ if (!level)
+ return;
+
+ if (level == 1) {
+ strcpy(delimiters, " ");
+ } else {
+ for (i = 0; i < level - 1; ++i)
+ j += snprintf(&delimiters[j], sizeof(delimiters) - j,
+ "%s", spaces);
+ }
+
+ if (header && value) {
+ fprintf(outf, "%s", delimiters);
+ fprintf(outf, "%s:%s\n", header, value);
+ } else if (header) {
+ fprintf(outf, "%s", delimiters);
+ fprintf(outf, "%s\n", header);
+ }
+}
+
+static int last_level;
+static void format_and_print(FILE *outf, int level, char *header, char *value)
+{
+ char *spaces = " ";
+ static char delimiters[256];
+ int i;
+
+ if (!out_format_is_json()) {
+ format_and_print_txt(outf, level, header, value);
+ return;
+ }
+
+ if (level == 0) {
+ if (header)
+ fprintf(outf, "{");
+ else
+ fprintf(outf, "\n}\n");
+
+ } else {
+ int j = 0;
+
+ for (i = 0; i < level; ++i)
+ j += snprintf(&delimiters[j], sizeof(delimiters) - j,
+ "%s", spaces);
+
+ if (last_level == level)
+ fprintf(outf, ",\n");
+
+ if (value) {
+ if (last_level != level)
+ fprintf(outf, "\n");
+
+ fprintf(outf, "%s\"%s\": ", delimiters, header);
+ fprintf(outf, "\"%s\"", value);
+ } else {
+ for (i = last_level - 1; i >= level; --i) {
+ int k = 0;
+
+ for (j = i; j > 0; --j)
+ k += snprintf(&delimiters[k],
+ sizeof(delimiters) - k,
+ "%s", spaces);
+ if (i == level && header)
+ fprintf(outf, "\n%s},", delimiters);
+ else
+ fprintf(outf, "\n%s}", delimiters);
+ }
+ if (abs(last_level - level) < 3)
+ fprintf(outf, "\n");
+ if (header)
+ fprintf(outf, "%s\"%s\": {", delimiters,
+ header);
+ }
+ }
+
+ last_level = level;
+}
+
+static void print_package_info(int cpu, FILE *outf)
+{
+ char header[256];
+
+ snprintf(header, sizeof(header), "package-%d",
+ get_physical_package_id(cpu));
+ format_and_print(outf, 1, header, NULL);
+ snprintf(header, sizeof(header), "die-%d", get_physical_die_id(cpu));
+ format_and_print(outf, 2, header, NULL);
+ snprintf(header, sizeof(header), "cpu-%d", cpu);
+ format_and_print(outf, 3, header, NULL);
+}
+
+static void _isst_pbf_display_information(int cpu, FILE *outf, int level,
+ struct isst_pbf_info *pbf_info,
+ int disp_level)
+{
+ char header[256];
+ char value[256];
+
+ snprintf(header, sizeof(header), "speed-select-base-freq");
+ format_and_print(outf, disp_level, header, NULL);
+
+ snprintf(header, sizeof(header), "high-priority-base-frequency(MHz)");
+ snprintf(value, sizeof(value), "%d",
+ pbf_info->p1_high * DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, disp_level + 1, header, value);
+
+ snprintf(header, sizeof(header), "high-priority-cpu-mask");
+ printcpumask(sizeof(value), value, pbf_info->core_cpumask_size,
+ pbf_info->core_cpumask);
+ format_and_print(outf, disp_level + 1, header, value);
+
+ snprintf(header, sizeof(header), "high-priority-cpu-list");
+ printcpulist(sizeof(value), value,
+ pbf_info->core_cpumask_size,
+ pbf_info->core_cpumask);
+ format_and_print(outf, disp_level + 1, header, value);
+
+ snprintf(header, sizeof(header), "low-priority-base-frequency(MHz)");
+ snprintf(value, sizeof(value), "%d",
+ pbf_info->p1_low * DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, disp_level + 1, header, value);
+
+ snprintf(header, sizeof(header), "tjunction-temperature(C)");
+ snprintf(value, sizeof(value), "%d", pbf_info->t_prochot);
+ format_and_print(outf, disp_level + 1, header, value);
+
+ snprintf(header, sizeof(header), "thermal-design-power(W)");
+ snprintf(value, sizeof(value), "%d", pbf_info->tdp);
+ format_and_print(outf, disp_level + 1, header, value);
+}
+
+static void _isst_fact_display_information(int cpu, FILE *outf, int level,
+ int fact_bucket, int fact_avx,
+ struct isst_fact_info *fact_info,
+ int base_level)
+{
+ struct isst_fact_bucket_info *bucket_info = fact_info->bucket_info;
+ char header[256];
+ char value[256];
+ int j;
+
+ snprintf(header, sizeof(header), "speed-select-turbo-freq");
+ format_and_print(outf, base_level, header, NULL);
+ for (j = 0; j < ISST_FACT_MAX_BUCKETS; ++j) {
+ if (fact_bucket != 0xff && fact_bucket != j)
+ continue;
+
+ if (!bucket_info[j].high_priority_cores_count)
+ break;
+
+ snprintf(header, sizeof(header), "bucket-%d", j);
+ format_and_print(outf, base_level + 1, header, NULL);
+
+ snprintf(header, sizeof(header), "high-priority-cores-count");
+ snprintf(value, sizeof(value), "%d",
+ bucket_info[j].high_priority_cores_count);
+ format_and_print(outf, base_level + 2, header, value);
+
+ if (fact_avx & 0x01) {
+ snprintf(header, sizeof(header),
+ "high-priority-max-frequency(MHz)");
+ snprintf(value, sizeof(value), "%d",
+ bucket_info[j].sse_trl * DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, base_level + 2, header, value);
+ }
+
+ if (fact_avx & 0x02) {
+ snprintf(header, sizeof(header),
+ "high-priority-max-avx2-frequency(MHz)");
+ snprintf(value, sizeof(value), "%d",
+ bucket_info[j].avx_trl * DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, base_level + 2, header, value);
+ }
+
+ if (fact_avx & 0x04) {
+ snprintf(header, sizeof(header),
+ "high-priority-max-avx512-frequency(MHz)");
+ snprintf(value, sizeof(value), "%d",
+ bucket_info[j].avx512_trl *
+ DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, base_level + 2, header, value);
+ }
+ }
+ snprintf(header, sizeof(header),
+ "speed-select-turbo-freq-clip-frequencies");
+ format_and_print(outf, base_level + 1, header, NULL);
+ snprintf(header, sizeof(header), "low-priority-max-frequency(MHz)");
+ snprintf(value, sizeof(value), "%d",
+ fact_info->lp_clipping_ratio_license_sse *
+ DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, base_level + 2, header, value);
+ snprintf(header, sizeof(header),
+ "low-priority-max-avx2-frequency(MHz)");
+ snprintf(value, sizeof(value), "%d",
+ fact_info->lp_clipping_ratio_license_avx2 *
+ DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, base_level + 2, header, value);
+ snprintf(header, sizeof(header),
+ "low-priority-max-avx512-frequency(MHz)");
+ snprintf(value, sizeof(value), "%d",
+ fact_info->lp_clipping_ratio_license_avx512 *
+ DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, base_level + 2, header, value);
+}
+
+void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level,
+ struct isst_pkg_ctdp *pkg_dev)
+{
+ char header[256];
+ char value[256];
+ int i, base_level = 1;
+
+ print_package_info(cpu, outf);
+
+ for (i = 0; i <= pkg_dev->levels; ++i) {
+ struct isst_pkg_ctdp_level_info *ctdp_level;
+ int j;
+
+ ctdp_level = &pkg_dev->ctdp_level[i];
+ if (!ctdp_level->processed)
+ continue;
+
+ snprintf(header, sizeof(header), "perf-profile-level-%d",
+ ctdp_level->level);
+ format_and_print(outf, base_level + 3, header, NULL);
+
+ snprintf(header, sizeof(header), "cpu-count");
+ j = get_cpu_count(get_physical_die_id(cpu),
+ get_physical_die_id(cpu));
+ snprintf(value, sizeof(value), "%d", j);
+ format_and_print(outf, base_level + 4, header, value);
+
+ snprintf(header, sizeof(header), "enable-cpu-mask");
+ printcpumask(sizeof(value), value,
+ ctdp_level->core_cpumask_size,
+ ctdp_level->core_cpumask);
+ format_and_print(outf, base_level + 4, header, value);
+
+ snprintf(header, sizeof(header), "enable-cpu-list");
+ printcpulist(sizeof(value), value,
+ ctdp_level->core_cpumask_size,
+ ctdp_level->core_cpumask);
+ format_and_print(outf, base_level + 4, header, value);
+
+ snprintf(header, sizeof(header), "thermal-design-power-ratio");
+ snprintf(value, sizeof(value), "%d", ctdp_level->tdp_ratio);
+ format_and_print(outf, base_level + 4, header, value);
+
+ snprintf(header, sizeof(header), "base-frequency(MHz)");
+ snprintf(value, sizeof(value), "%d",
+ ctdp_level->tdp_ratio * DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, base_level + 4, header, value);
+
+ snprintf(header, sizeof(header),
+ "speed-select-turbo-freq");
+ if (ctdp_level->fact_support) {
+ if (ctdp_level->fact_enabled)
+ snprintf(value, sizeof(value), "enabled");
+ else
+ snprintf(value, sizeof(value), "disabled");
+ } else
+ snprintf(value, sizeof(value), "unsupported");
+ format_and_print(outf, base_level + 4, header, value);
+
+ snprintf(header, sizeof(header),
+ "speed-select-base-freq");
+ if (ctdp_level->pbf_support) {
+ if (ctdp_level->pbf_enabled)
+ snprintf(value, sizeof(value), "enabled");
+ else
+ snprintf(value, sizeof(value), "disabled");
+ } else
+ snprintf(value, sizeof(value), "unsupported");
+ format_and_print(outf, base_level + 4, header, value);
+
+ snprintf(header, sizeof(header), "thermal-design-power(W)");
+ snprintf(value, sizeof(value), "%d", ctdp_level->pkg_tdp);
+ format_and_print(outf, base_level + 4, header, value);
+
+ snprintf(header, sizeof(header), "tjunction-max(C)");
+ snprintf(value, sizeof(value), "%d", ctdp_level->t_proc_hot);
+ format_and_print(outf, base_level + 4, header, value);
+
+ snprintf(header, sizeof(header), "turbo-ratio-limits-sse");
+ format_and_print(outf, base_level + 4, header, NULL);
+ for (j = 0; j < 8; ++j) {
+ snprintf(header, sizeof(header), "bucket-%d", j);
+ format_and_print(outf, base_level + 5, header, NULL);
+
+ snprintf(header, sizeof(header), "core-count");
+ snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff);
+ format_and_print(outf, base_level + 6, header, value);
+
+ snprintf(header, sizeof(header),
+ "max-turbo-frequency(MHz)");
+ snprintf(value, sizeof(value), "%d",
+ ctdp_level->trl_sse_active_cores[j] *
+ DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, base_level + 6, header, value);
+ }
+ snprintf(header, sizeof(header), "turbo-ratio-limits-avx");
+ format_and_print(outf, base_level + 4, header, NULL);
+ for (j = 0; j < 8; ++j) {
+ snprintf(header, sizeof(header), "bucket-%d", j);
+ format_and_print(outf, base_level + 5, header, NULL);
+
+ snprintf(header, sizeof(header), "core-count");
+ snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff);
+ format_and_print(outf, base_level + 6, header, value);
+
+ snprintf(header, sizeof(header),
+ "max-turbo-frequency(MHz)");
+ snprintf(value, sizeof(value), "%d",
+ ctdp_level->trl_avx_active_cores[j] *
+ DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, base_level + 6, header, value);
+ }
+
+ snprintf(header, sizeof(header), "turbo-ratio-limits-avx512");
+ format_and_print(outf, base_level + 4, header, NULL);
+ for (j = 0; j < 8; ++j) {
+ snprintf(header, sizeof(header), "bucket-%d", j);
+ format_and_print(outf, base_level + 5, header, NULL);
+
+ snprintf(header, sizeof(header), "core-count");
+ snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff);
+ format_and_print(outf, base_level + 6, header, value);
+
+ snprintf(header, sizeof(header),
+ "max-turbo-frequency(MHz)");
+ snprintf(value, sizeof(value), "%d",
+ ctdp_level->trl_avx_512_active_cores[j] *
+ DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, base_level + 6, header, value);
+ }
+ if (ctdp_level->pbf_support)
+ _isst_pbf_display_information(cpu, outf, i,
+ &ctdp_level->pbf_info,
+ base_level + 4);
+ if (ctdp_level->fact_support)
+ _isst_fact_display_information(cpu, outf, i, 0xff, 0xff,
+ &ctdp_level->fact_info,
+ base_level + 4);
+ }
+
+ format_and_print(outf, 1, NULL, NULL);
+}
+
+void isst_ctdp_display_information_start(FILE *outf)
+{
+ last_level = 0;
+ format_and_print(outf, 0, "start", NULL);
+}
+
+void isst_ctdp_display_information_end(FILE *outf)
+{
+ format_and_print(outf, 0, NULL, NULL);
+}
+
+void isst_pbf_display_information(int cpu, FILE *outf, int level,
+ struct isst_pbf_info *pbf_info)
+{
+ print_package_info(cpu, outf);
+ _isst_pbf_display_information(cpu, outf, level, pbf_info, 4);
+ format_and_print(outf, 1, NULL, NULL);
+}
+
+void isst_fact_display_information(int cpu, FILE *outf, int level,
+ int fact_bucket, int fact_avx,
+ struct isst_fact_info *fact_info)
+{
+ print_package_info(cpu, outf);
+ _isst_fact_display_information(cpu, outf, level, fact_bucket, fact_avx,
+ fact_info, 4);
+ format_and_print(outf, 1, NULL, NULL);
+}
+
+void isst_clos_display_information(int cpu, FILE *outf, int clos,
+ struct isst_clos_config *clos_config)
+{
+ char header[256];
+ char value[256];
+
+ snprintf(header, sizeof(header), "package-%d",
+ get_physical_package_id(cpu));
+ format_and_print(outf, 1, header, NULL);
+ snprintf(header, sizeof(header), "die-%d", get_physical_die_id(cpu));
+ format_and_print(outf, 2, header, NULL);
+ snprintf(header, sizeof(header), "cpu-%d", cpu);
+ format_and_print(outf, 3, header, NULL);
+
+ snprintf(header, sizeof(header), "core-power");
+ format_and_print(outf, 4, header, NULL);
+
+ snprintf(header, sizeof(header), "clos");
+ snprintf(value, sizeof(value), "%d", clos);
+ format_and_print(outf, 5, header, value);
+
+ snprintf(header, sizeof(header), "epp");
+ snprintf(value, sizeof(value), "%d", clos_config->epp);
+ format_and_print(outf, 5, header, value);
+
+ snprintf(header, sizeof(header), "clos-proportional-priority");
+ snprintf(value, sizeof(value), "%d", clos_config->clos_prop_prio);
+ format_and_print(outf, 5, header, value);
+
+ snprintf(header, sizeof(header), "clos-min");
+ snprintf(value, sizeof(value), "%d", clos_config->clos_min);
+ format_and_print(outf, 5, header, value);
+
+ snprintf(header, sizeof(header), "clos-max");
+ snprintf(value, sizeof(value), "%d", clos_config->clos_max);
+ format_and_print(outf, 5, header, value);
+
+ snprintf(header, sizeof(header), "clos-desired");
+ snprintf(value, sizeof(value), "%d", clos_config->clos_desired);
+ format_and_print(outf, 5, header, value);
+
+ format_and_print(outf, 1, NULL, NULL);
+}
+
+void isst_display_result(int cpu, FILE *outf, char *feature, char *cmd,
+ int result)
+{
+ char header[256];
+ char value[256];
+
+ snprintf(header, sizeof(header), "package-%d",
+ get_physical_package_id(cpu));
+ format_and_print(outf, 1, header, NULL);
+ snprintf(header, sizeof(header), "die-%d", get_physical_die_id(cpu));
+ format_and_print(outf, 2, header, NULL);
+ snprintf(header, sizeof(header), "cpu-%d", cpu);
+ format_and_print(outf, 3, header, NULL);
+ snprintf(header, sizeof(header), "%s", feature);
+ format_and_print(outf, 4, header, NULL);
+ snprintf(header, sizeof(header), "%s", cmd);
+ if (!result)
+ snprintf(value, sizeof(value), "success");
+ else
+ snprintf(value, sizeof(value), "failed(error %d)", result);
+ format_and_print(outf, 5, header, value);
+
+ format_and_print(outf, 1, NULL, NULL);
+}
diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h
new file mode 100644
index 000000000000..2f7f62765eb6
--- /dev/null
+++ b/tools/power/x86/intel-speed-select/isst.h
@@ -0,0 +1,232 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Intel Speed Select -- Enumerate and control features
+ * Copyright (c) 2019 Intel Corporation.
+ */
+
+#ifndef _ISST_H_
+#define _ISST_H_
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sched.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <getopt.h>
+#include <err.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cpuid.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include <stdarg.h>
+#include <sys/ioctl.h>
+
+#define BIT(x) (1 << (x))
+#define GENMASK(h, l) (((~0UL) << (l)) & (~0UL >> (sizeof(long) * 8 - 1 - (h))))
+#define GENMASK_ULL(h, l) \
+ (((~0ULL) << (l)) & (~0ULL >> (sizeof(long long) * 8 - 1 - (h))))
+
+#define CONFIG_TDP 0x7f
+#define CONFIG_TDP_GET_LEVELS_INFO 0x00
+#define CONFIG_TDP_GET_TDP_CONTROL 0x01
+#define CONFIG_TDP_SET_TDP_CONTROL 0x02
+#define CONFIG_TDP_GET_TDP_INFO 0x03
+#define CONFIG_TDP_GET_PWR_INFO 0x04
+#define CONFIG_TDP_GET_TJMAX_INFO 0x05
+#define CONFIG_TDP_GET_CORE_MASK 0x06
+#define CONFIG_TDP_GET_TURBO_LIMIT_RATIOS 0x07
+#define CONFIG_TDP_SET_LEVEL 0x08
+#define CONFIG_TDP_GET_UNCORE_P0_P1_INFO 0X09
+#define CONFIG_TDP_GET_P1_INFO 0x0a
+#define CONFIG_TDP_GET_MEM_FREQ 0x0b
+
+#define CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES 0x10
+#define CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS 0x11
+#define CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO 0x12
+
+#define CONFIG_TDP_PBF_GET_CORE_MASK_INFO 0x20
+#define CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO 0x21
+#define CONFIG_TDP_PBF_GET_TJ_MAX_INFO 0x22
+#define CONFIG_TDP_PBF_GET_TDP_INFO 0X23
+
+#define CONFIG_CLOS 0xd0
+#define CLOS_PQR_ASSOC 0x00
+#define CLOS_PM_CLOS 0x01
+#define CLOS_PM_QOS_CONFIG 0x02
+#define CLOS_STATUS 0x03
+
+#define MBOX_CMD_WRITE_BIT 0x08
+
+#define PM_QOS_INFO_OFFSET 0x00
+#define PM_QOS_CONFIG_OFFSET 0x04
+#define PM_CLOS_OFFSET 0x08
+#define PQR_ASSOC_OFFSET 0x20
+
+struct isst_clos_config {
+ int pkg_id;
+ int die_id;
+ unsigned char epp;
+ unsigned char clos_prop_prio;
+ unsigned char clos_min;
+ unsigned char clos_max;
+ unsigned char clos_desired;
+};
+
+struct isst_fact_bucket_info {
+ int high_priority_cores_count;
+ int sse_trl;
+ int avx_trl;
+ int avx512_trl;
+};
+
+struct isst_pbf_info {
+ int pbf_acticated;
+ int pbf_available;
+ size_t core_cpumask_size;
+ cpu_set_t *core_cpumask;
+ int p1_high;
+ int p1_low;
+ int t_control;
+ int t_prochot;
+ int tdp;
+};
+
+#define ISST_TRL_MAX_ACTIVE_CORES 8
+#define ISST_FACT_MAX_BUCKETS 8
+struct isst_fact_info {
+ int lp_clipping_ratio_license_sse;
+ int lp_clipping_ratio_license_avx2;
+ int lp_clipping_ratio_license_avx512;
+ struct isst_fact_bucket_info bucket_info[ISST_FACT_MAX_BUCKETS];
+};
+
+struct isst_pkg_ctdp_level_info {
+ int processed;
+ int control_cpu;
+ int pkg_id;
+ int die_id;
+ int level;
+ int fact_support;
+ int pbf_support;
+ int fact_enabled;
+ int pbf_enabled;
+ int tdp_ratio;
+ int active;
+ int tdp_control;
+ int pkg_tdp;
+ int pkg_min_power;
+ int pkg_max_power;
+ int fact;
+ int t_proc_hot;
+ int uncore_p0;
+ int uncore_p1;
+ int sse_p1;
+ int avx2_p1;
+ int avx512_p1;
+ int mem_freq;
+ size_t core_cpumask_size;
+ cpu_set_t *core_cpumask;
+ int cpu_count;
+ unsigned long long buckets_info;
+ int trl_sse_active_cores[ISST_TRL_MAX_ACTIVE_CORES];
+ int trl_avx_active_cores[ISST_TRL_MAX_ACTIVE_CORES];
+ int trl_avx_512_active_cores[ISST_TRL_MAX_ACTIVE_CORES];
+ int kobj_bucket_index;
+ int active_bucket;
+ int fact_max_index;
+ int fact_max_config;
+ int pbf_found;
+ int pbf_active;
+ struct isst_pbf_info pbf_info;
+ struct isst_fact_info fact_info;
+};
+
+#define ISST_MAX_TDP_LEVELS (4 + 1) /* +1 for base config */
+struct isst_pkg_ctdp {
+ int locked;
+ int version;
+ int processed;
+ int levels;
+ int current_level;
+ int enabled;
+ struct isst_pkg_ctdp_level_info ctdp_level[ISST_MAX_TDP_LEVELS];
+};
+
+extern int get_topo_max_cpus(void);
+extern int get_cpu_count(int pkg_id, int die_id);
+
+/* Common interfaces */
+extern void debug_printf(const char *format, ...);
+extern int out_format_is_json(void);
+extern int get_physical_package_id(int cpu);
+extern int get_physical_die_id(int cpu);
+extern size_t alloc_cpu_set(cpu_set_t **cpu_set);
+extern void free_cpu_set(cpu_set_t *cpu_set);
+extern int find_logical_cpu(int pkg_id, int die_id, int phy_cpu);
+extern int find_phy_cpu_num(int logical_cpu);
+extern int find_phy_core_num(int logical_cpu);
+extern void set_cpu_mask_from_punit_coremask(int cpu,
+ unsigned long long core_mask,
+ size_t core_cpumask_size,
+ cpu_set_t *core_cpumask,
+ int *cpu_cnt);
+
+extern int isst_send_mbox_command(unsigned int cpu, unsigned char command,
+ unsigned char sub_command,
+ unsigned int write,
+ unsigned int req_data, unsigned int *resp);
+
+extern int isst_send_msr_command(unsigned int cpu, unsigned int command,
+ int write, unsigned long long *req_resp);
+
+extern int isst_get_ctdp_levels(int cpu, struct isst_pkg_ctdp *pkg_dev);
+extern int isst_get_process_ctdp(int cpu, int tdp_level,
+ struct isst_pkg_ctdp *pkg_dev);
+extern void isst_get_process_ctdp_complete(int cpu,
+ struct isst_pkg_ctdp *pkg_dev);
+extern void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level,
+ struct isst_pkg_ctdp *pkg_dev);
+extern void isst_ctdp_display_information_start(FILE *outf);
+extern void isst_ctdp_display_information_end(FILE *outf);
+extern void isst_pbf_display_information(int cpu, FILE *outf, int level,
+ struct isst_pbf_info *info);
+extern int isst_set_tdp_level(int cpu, int tdp_level);
+extern int isst_set_tdp_level_msr(int cpu, int tdp_level);
+extern int isst_set_pbf_fact_status(int cpu, int pbf, int enable);
+extern int isst_get_pbf_info(int cpu, int level,
+ struct isst_pbf_info *pbf_info);
+extern void isst_get_pbf_info_complete(struct isst_pbf_info *pbf_info);
+extern int isst_get_fact_info(int cpu, int level,
+ struct isst_fact_info *fact_info);
+extern int isst_get_fact_bucket_info(int cpu, int level,
+ struct isst_fact_bucket_info *bucket_info);
+extern void isst_fact_display_information(int cpu, FILE *outf, int level,
+ int fact_bucket, int fact_avx,
+ struct isst_fact_info *fact_info);
+extern int isst_set_trl(int cpu, unsigned long long trl);
+extern int isst_set_trl_from_current_tdp(int cpu, unsigned long long trl);
+extern int isst_get_config_tdp_lock_status(int cpu);
+
+extern int isst_pm_qos_config(int cpu, int enable_clos, int priority_type);
+extern int isst_pm_get_clos(int cpu, int clos,
+ struct isst_clos_config *clos_config);
+extern int isst_set_clos(int cpu, int clos,
+ struct isst_clos_config *clos_config);
+extern int isst_clos_associate(int cpu, int clos);
+extern int isst_clos_get_assoc_status(int cpu, int *clos_id);
+extern void isst_clos_display_information(int cpu, FILE *outf, int clos,
+ struct isst_clos_config *clos_config);
+
+extern int isst_read_reg(unsigned short reg, unsigned int *val);
+extern int isst_write_reg(int reg, unsigned int val);
+
+extern void isst_display_result(int cpu, FILE *outf, char *feature, char *cmd,
+ int result);
+#endif
diff --git a/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py b/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py
index 2fa3c5757bcb..2d6d342b148f 100755
--- a/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py
+++ b/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py
@@ -1,4 +1,5 @@
#!/usr/bin/python
+# SPDX-License-Identifier: GPL-2.0-only
# -*- coding: utf-8 -*-
#
""" This utility can be used to debug and tune the performance of the
diff --git a/tools/power/x86/turbostat/Makefile b/tools/power/x86/turbostat/Makefile
index 045f5f7d68ab..13f1e8b9ac52 100644
--- a/tools/power/x86/turbostat/Makefile
+++ b/tools/power/x86/turbostat/Makefile
@@ -9,9 +9,10 @@ ifeq ("$(origin O)", "command line")
endif
turbostat : turbostat.c
-override CFLAGS += -Wall -I../../../include
+override CFLAGS += -O2 -Wall -I../../../include
override CFLAGS += -DMSRHEADER='"../../../../arch/x86/include/asm/msr-index.h"'
override CFLAGS += -DINTEL_FAMILY_HEADER='"../../../../arch/x86/include/asm/intel-family.h"'
+override CFLAGS += -D_FORTIFY_SOURCE=2
%: %.c
@mkdir -p $(BUILD_OUTPUT)
diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c
index c7727be9719f..5d0fddda842c 100644
--- a/tools/power/x86/turbostat/turbostat.c
+++ b/tools/power/x86/turbostat/turbostat.c
@@ -1,22 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* turbostat -- show CPU frequency and C-state residency
* on modern Intel and AMD processors.
*
* Copyright (c) 2013 Intel Corporation.
* Len Brown <len.brown@intel.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _GNU_SOURCE
@@ -51,7 +39,6 @@ FILE *outf;
int *fd_percpu;
struct timeval interval_tv = {5, 0};
struct timespec interval_ts = {5, 0};
-struct timespec one_msec = {0, 1000000};
unsigned int num_iterations;
unsigned int debug;
unsigned int quiet;
@@ -72,6 +59,7 @@ unsigned int do_irtl_hsw;
unsigned int units = 1000000; /* MHz etc */
unsigned int genuine_intel;
unsigned int authentic_amd;
+unsigned int hygon_genuine;
unsigned int max_level, max_extended_level;
unsigned int has_invariant_tsc;
unsigned int do_nhm_platform_info;
@@ -112,6 +100,7 @@ unsigned int has_hwp_epp; /* IA32_HWP_REQUEST[bits 31:24] */
unsigned int has_hwp_pkg; /* IA32_HWP_REQUEST_PKG */
unsigned int has_misc_feature_control;
unsigned int first_counter_read = 1;
+int ignore_stdin;
#define RAPL_PKG (1 << 0)
/* 0x610 MSR_PKG_POWER_LIMIT */
@@ -178,6 +167,7 @@ size_t cpu_present_setsize, cpu_affinity_setsize, cpu_subset_size;
struct thread_data {
struct timeval tv_begin;
struct timeval tv_end;
+ struct timeval tv_delta;
unsigned long long tsc;
unsigned long long aperf;
unsigned long long mperf;
@@ -518,6 +508,7 @@ unsigned long long bic_enabled = (0xFFFFFFFFFFFFFFFFULL & ~BIC_DISABLED_BY_DEFAU
unsigned long long bic_present = BIC_USEC | BIC_TOD | BIC_sysfs | BIC_APIC | BIC_X2APIC;
#define DO_BIC(COUNTER_NAME) (bic_enabled & bic_present & COUNTER_NAME)
+#define DO_BIC_READ(COUNTER_NAME) (bic_present & COUNTER_NAME)
#define ENABLE_BIC(COUNTER_NAME) (bic_enabled |= COUNTER_NAME)
#define BIC_PRESENT(COUNTER_BIT) (bic_present |= COUNTER_BIT)
#define BIC_NOT_PRESENT(COUNTER_BIT) (bic_present &= ~COUNTER_BIT)
@@ -861,7 +852,6 @@ int dump_counters(struct thread_data *t, struct core_data *c,
outp += sprintf(outp, "pc8: %016llX\n", p->pc8);
outp += sprintf(outp, "pc9: %016llX\n", p->pc9);
outp += sprintf(outp, "pc10: %016llX\n", p->pc10);
- outp += sprintf(outp, "pc10: %016llX\n", p->pc10);
outp += sprintf(outp, "cpu_lpi: %016llX\n", p->cpu_lpi);
outp += sprintf(outp, "sys_lpi: %016llX\n", p->sys_lpi);
outp += sprintf(outp, "Joules PKG: %0X\n", p->energy_pkg);
@@ -923,7 +913,7 @@ int format_counters(struct thread_data *t, struct core_data *c,
if (DO_BIC(BIC_TOD))
outp += sprintf(outp, "%10ld.%06ld\t", t->tv_end.tv_sec, t->tv_end.tv_usec);
- interval_float = tv_delta.tv_sec + tv_delta.tv_usec/1000000.0;
+ interval_float = t->tv_delta.tv_sec + t->tv_delta.tv_usec/1000000.0;
tsc = t->tsc * tsc_tweak;
@@ -1299,6 +1289,14 @@ delta_core(struct core_data *new, struct core_data *old)
}
}
+int soft_c1_residency_display(int bic)
+{
+ if (!DO_BIC(BIC_CPU_c1) || use_c1_residency_msr)
+ return 0;
+
+ return DO_BIC_READ(bic);
+}
+
/*
* old = new - old
*/
@@ -1321,6 +1319,7 @@ delta_thread(struct thread_data *new, struct thread_data *old,
* over-write old w/ new so we can print end of interval values
*/
+ timersub(&new->tv_begin, &old->tv_begin, &old->tv_delta);
old->tv_begin = new->tv_begin;
old->tv_end = new->tv_end;
@@ -1334,7 +1333,8 @@ delta_thread(struct thread_data *new, struct thread_data *old,
old->c1 = new->c1 - old->c1;
- if (DO_BIC(BIC_Avg_MHz) || DO_BIC(BIC_Busy) || DO_BIC(BIC_Bzy_MHz)) {
+ if (DO_BIC(BIC_Avg_MHz) || DO_BIC(BIC_Busy) || DO_BIC(BIC_Bzy_MHz) ||
+ soft_c1_residency_display(BIC_Avg_MHz)) {
if ((new->aperf > old->aperf) && (new->mperf > old->mperf)) {
old->aperf = new->aperf - old->aperf;
old->mperf = new->mperf - old->mperf;
@@ -1416,6 +1416,8 @@ void clear_counters(struct thread_data *t, struct core_data *c, struct pkg_data
t->tv_begin.tv_usec = 0;
t->tv_end.tv_sec = 0;
t->tv_end.tv_usec = 0;
+ t->tv_delta.tv_sec = 0;
+ t->tv_delta.tv_usec = 0;
t->tsc = 0;
t->aperf = 0;
@@ -1585,6 +1587,9 @@ void compute_average(struct thread_data *t, struct core_data *c,
for_all_cpus(sum_counters, t, c, p);
+ /* Use the global time delta for the average. */
+ average.threads.tv_delta = tv_delta;
+
average.threads.tsc /= topo.num_cpus;
average.threads.aperf /= topo.num_cpus;
average.threads.mperf /= topo.num_cpus;
@@ -1726,7 +1731,7 @@ void get_apic_id(struct thread_data *t)
if (!DO_BIC(BIC_X2APIC))
return;
- if (authentic_amd) {
+ if (authentic_amd || hygon_genuine) {
unsigned int topology_extensions;
if (max_extended_level < 0x8000001e)
@@ -1774,19 +1779,20 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p)
struct msr_counter *mp;
int i;
- gettimeofday(&t->tv_begin, (struct timezone *)NULL);
-
if (cpu_migrate(cpu)) {
fprintf(outf, "Could not migrate to CPU %d\n", cpu);
return -1;
}
+ gettimeofday(&t->tv_begin, (struct timezone *)NULL);
+
if (first_counter_read)
get_apic_id(t);
retry:
t->tsc = rdtsc(); /* we are running on local CPU of interest */
- if (DO_BIC(BIC_Avg_MHz) || DO_BIC(BIC_Busy) || DO_BIC(BIC_Bzy_MHz)) {
+ if (DO_BIC(BIC_Avg_MHz) || DO_BIC(BIC_Busy) || DO_BIC(BIC_Bzy_MHz) ||
+ soft_c1_residency_display(BIC_Avg_MHz)) {
unsigned long long tsc_before, tsc_between, tsc_after, aperf_time, mperf_time;
/*
@@ -1863,20 +1869,20 @@ retry:
if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE))
goto done;
- if (DO_BIC(BIC_CPU_c3)) {
+ if (DO_BIC(BIC_CPU_c3) || soft_c1_residency_display(BIC_CPU_c3)) {
if (get_msr(cpu, MSR_CORE_C3_RESIDENCY, &c->c3))
return -6;
}
- if (DO_BIC(BIC_CPU_c6) && !do_knl_cstates) {
+ if ((DO_BIC(BIC_CPU_c6) || soft_c1_residency_display(BIC_CPU_c6)) && !do_knl_cstates) {
if (get_msr(cpu, MSR_CORE_C6_RESIDENCY, &c->c6))
return -7;
- } else if (do_knl_cstates) {
+ } else if (do_knl_cstates || soft_c1_residency_display(BIC_CPU_c6)) {
if (get_msr(cpu, MSR_KNL_CORE_C6_RESIDENCY, &c->c6))
return -7;
}
- if (DO_BIC(BIC_CPU_c7))
+ if (DO_BIC(BIC_CPU_c7) || soft_c1_residency_display(BIC_CPU_c7))
if (get_msr(cpu, MSR_CORE_C7_RESIDENCY, &c->c7))
return -8;
@@ -2162,7 +2168,7 @@ int has_turbo_ratio_group_limits(int family, int model)
switch (model) {
case INTEL_FAM6_ATOM_GOLDMONT:
case INTEL_FAM6_SKYLAKE_X:
- case INTEL_FAM6_ATOM_GOLDMONT_X:
+ case INTEL_FAM6_ATOM_GOLDMONT_D:
return 1;
}
return 0;
@@ -2924,6 +2930,7 @@ int snapshot_cpu_lpi_us(void)
if (retval != 1) {
fprintf(stderr, "Disabling Low Power Idle CPU output\n");
BIC_NOT_PRESENT(BIC_CPU_LPI);
+ fclose(fp);
return -1;
}
@@ -2950,6 +2957,7 @@ int snapshot_sys_lpi_us(void)
if (retval != 1) {
fprintf(stderr, "Disabling Low Power Idle System output\n");
BIC_NOT_PRESENT(BIC_SYS_LPI);
+ fclose(fp);
return -1;
}
fclose(fp);
@@ -2997,8 +3005,6 @@ static void signal_handler (int signal)
fprintf(stderr, "SIGUSR1\n");
break;
}
- /* make sure this manually-invoked interval is at least 1ms long */
- nanosleep(&one_msec, NULL);
}
void setup_signal_handler(void)
@@ -3017,29 +3023,38 @@ void setup_signal_handler(void)
void do_sleep(void)
{
- struct timeval select_timeout;
+ struct timeval tout;
+ struct timespec rest;
fd_set readfds;
int retval;
FD_ZERO(&readfds);
FD_SET(0, &readfds);
- if (!isatty(fileno(stdin))) {
+ if (ignore_stdin) {
nanosleep(&interval_ts, NULL);
return;
}
- select_timeout = interval_tv;
- retval = select(1, &readfds, NULL, NULL, &select_timeout);
+ tout = interval_tv;
+ retval = select(1, &readfds, NULL, NULL, &tout);
if (retval == 1) {
switch (getc(stdin)) {
case 'q':
exit_requested = 1;
break;
+ case EOF:
+ /*
+ * 'stdin' is a pipe closed on the other end. There
+ * won't be any further input.
+ */
+ ignore_stdin = 1;
+ /* Sleep the rest of the time */
+ rest.tv_sec = (tout.tv_sec + tout.tv_usec / 1000000);
+ rest.tv_nsec = (tout.tv_usec % 1000000) * 1000;
+ nanosleep(&rest, NULL);
}
- /* make sure this manually-invoked interval is at least 1ms long */
- nanosleep(&one_msec, NULL);
}
}
@@ -3219,14 +3234,15 @@ int probe_nhm_msrs(unsigned int family, unsigned int model)
pkg_cstate_limits = snb_pkg_cstate_limits;
has_misc_feature_control = 1;
break;
- case INTEL_FAM6_HASWELL_CORE: /* HSW */
+ case INTEL_FAM6_HASWELL: /* HSW */
+ case INTEL_FAM6_HASWELL_G: /* HSW */
case INTEL_FAM6_HASWELL_X: /* HSX */
- case INTEL_FAM6_HASWELL_GT3E: /* HSW */
- case INTEL_FAM6_BROADWELL_CORE: /* BDW */
- case INTEL_FAM6_BROADWELL_GT3E: /* BDW */
+ case INTEL_FAM6_HASWELL_L: /* HSW */
+ case INTEL_FAM6_BROADWELL: /* BDW */
+ case INTEL_FAM6_BROADWELL_G: /* BDW */
case INTEL_FAM6_BROADWELL_X: /* BDX */
- case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */
- case INTEL_FAM6_CANNONLAKE_MOBILE: /* CNL */
+ case INTEL_FAM6_SKYLAKE_L: /* SKL */
+ case INTEL_FAM6_CANNONLAKE_L: /* CNL */
pkg_cstate_limits = hsw_pkg_cstate_limits;
has_misc_feature_control = 1;
break;
@@ -3236,7 +3252,7 @@ int probe_nhm_msrs(unsigned int family, unsigned int model)
break;
case INTEL_FAM6_ATOM_SILVERMONT: /* BYT */
no_MSR_MISC_PWR_MGMT = 1;
- case INTEL_FAM6_ATOM_SILVERMONT_X: /* AVN */
+ case INTEL_FAM6_ATOM_SILVERMONT_D: /* AVN */
pkg_cstate_limits = slv_pkg_cstate_limits;
break;
case INTEL_FAM6_ATOM_AIRMONT: /* AMT */
@@ -3248,7 +3264,7 @@ int probe_nhm_msrs(unsigned int family, unsigned int model)
break;
case INTEL_FAM6_ATOM_GOLDMONT: /* BXT */
case INTEL_FAM6_ATOM_GOLDMONT_PLUS:
- case INTEL_FAM6_ATOM_GOLDMONT_X: /* DNV */
+ case INTEL_FAM6_ATOM_GOLDMONT_D: /* DNV */
pkg_cstate_limits = glm_pkg_cstate_limits;
break;
default:
@@ -3291,7 +3307,7 @@ int is_dnv(unsigned int family, unsigned int model)
return 0;
switch (model) {
- case INTEL_FAM6_ATOM_GOLDMONT_X:
+ case INTEL_FAM6_ATOM_GOLDMONT_D:
return 1;
}
return 0;
@@ -3415,14 +3431,15 @@ int has_config_tdp(unsigned int family, unsigned int model)
switch (model) {
case INTEL_FAM6_IVYBRIDGE: /* IVB */
- case INTEL_FAM6_HASWELL_CORE: /* HSW */
+ case INTEL_FAM6_HASWELL: /* HSW */
case INTEL_FAM6_HASWELL_X: /* HSX */
- case INTEL_FAM6_HASWELL_GT3E: /* HSW */
- case INTEL_FAM6_BROADWELL_CORE: /* BDW */
- case INTEL_FAM6_BROADWELL_GT3E: /* BDW */
+ case INTEL_FAM6_HASWELL_L: /* HSW */
+ case INTEL_FAM6_HASWELL_G: /* HSW */
+ case INTEL_FAM6_BROADWELL: /* BDW */
+ case INTEL_FAM6_BROADWELL_G: /* BDW */
case INTEL_FAM6_BROADWELL_X: /* BDX */
- case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */
- case INTEL_FAM6_CANNONLAKE_MOBILE: /* CNL */
+ case INTEL_FAM6_SKYLAKE_L: /* SKL */
+ case INTEL_FAM6_CANNONLAKE_L: /* CNL */
case INTEL_FAM6_SKYLAKE_X: /* SKX */
case INTEL_FAM6_XEON_PHI_KNL: /* Knights Landing */
@@ -3804,7 +3821,7 @@ double get_tdp_intel(unsigned int model)
switch (model) {
case INTEL_FAM6_ATOM_SILVERMONT:
- case INTEL_FAM6_ATOM_SILVERMONT_X:
+ case INTEL_FAM6_ATOM_SILVERMONT_D:
return 30.0;
default:
return 135.0;
@@ -3815,6 +3832,7 @@ double get_tdp_amd(unsigned int family)
{
switch (family) {
case 0x17:
+ case 0x18:
default:
/* This is the max stock TDP of HEDT/Server Fam17h chips */
return 250.0;
@@ -3852,10 +3870,11 @@ void rapl_probe_intel(unsigned int family, unsigned int model)
switch (model) {
case INTEL_FAM6_SANDYBRIDGE:
case INTEL_FAM6_IVYBRIDGE:
- case INTEL_FAM6_HASWELL_CORE: /* HSW */
- case INTEL_FAM6_HASWELL_GT3E: /* HSW */
- case INTEL_FAM6_BROADWELL_CORE: /* BDW */
- case INTEL_FAM6_BROADWELL_GT3E: /* BDW */
+ case INTEL_FAM6_HASWELL: /* HSW */
+ case INTEL_FAM6_HASWELL_L: /* HSW */
+ case INTEL_FAM6_HASWELL_G: /* HSW */
+ case INTEL_FAM6_BROADWELL: /* BDW */
+ case INTEL_FAM6_BROADWELL_G: /* BDW */
do_rapl = RAPL_PKG | RAPL_CORES | RAPL_CORE_POLICY | RAPL_GFX | RAPL_PKG_POWER_INFO;
if (rapl_joules) {
BIC_PRESENT(BIC_Pkg_J);
@@ -3875,8 +3894,8 @@ void rapl_probe_intel(unsigned int family, unsigned int model)
else
BIC_PRESENT(BIC_PkgWatt);
break;
- case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */
- case INTEL_FAM6_CANNONLAKE_MOBILE: /* CNL */
+ case INTEL_FAM6_SKYLAKE_L: /* SKL */
+ case INTEL_FAM6_CANNONLAKE_L: /* CNL */
do_rapl = RAPL_PKG | RAPL_CORES | RAPL_CORE_POLICY | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_PKG_PERF_STATUS | RAPL_GFX | RAPL_PKG_POWER_INFO;
BIC_PRESENT(BIC_PKG__);
BIC_PRESENT(BIC_RAM__);
@@ -3923,7 +3942,7 @@ void rapl_probe_intel(unsigned int family, unsigned int model)
}
break;
case INTEL_FAM6_ATOM_SILVERMONT: /* BYT */
- case INTEL_FAM6_ATOM_SILVERMONT_X: /* AVN */
+ case INTEL_FAM6_ATOM_SILVERMONT_D: /* AVN */
do_rapl = RAPL_PKG | RAPL_CORES;
if (rapl_joules) {
BIC_PRESENT(BIC_Pkg_J);
@@ -3933,7 +3952,7 @@ void rapl_probe_intel(unsigned int family, unsigned int model)
BIC_PRESENT(BIC_CorWatt);
}
break;
- case INTEL_FAM6_ATOM_GOLDMONT_X: /* DNV */
+ case INTEL_FAM6_ATOM_GOLDMONT_D: /* DNV */
do_rapl = RAPL_PKG | RAPL_DRAM | RAPL_DRAM_POWER_INFO | RAPL_DRAM_PERF_STATUS | RAPL_PKG_PERF_STATUS | RAPL_PKG_POWER_INFO | RAPL_CORES_ENERGY_STATUS;
BIC_PRESENT(BIC_PKG__);
BIC_PRESENT(BIC_RAM__);
@@ -3994,6 +4013,7 @@ void rapl_probe_amd(unsigned int family, unsigned int model)
switch (family) {
case 0x17: /* Zen, Zen+ */
+ case 0x18: /* Hygon Dhyana */
do_rapl = RAPL_AMD_F17H | RAPL_PER_CORE_ENERGY;
if (rapl_joules) {
BIC_PRESENT(BIC_Pkg_J);
@@ -4014,7 +4034,7 @@ void rapl_probe_amd(unsigned int family, unsigned int model)
rapl_energy_units = ldexp(1.0, -(msr >> 8 & 0x1f));
rapl_power_units = ldexp(1.0, -(msr & 0xf));
- tdp = get_tdp_amd(model);
+ tdp = get_tdp_amd(family);
rapl_joule_counter_range = 0xFFFFFFFF * rapl_energy_units / tdp;
if (!quiet)
@@ -4030,7 +4050,7 @@ void rapl_probe(unsigned int family, unsigned int model)
{
if (genuine_intel)
rapl_probe_intel(family, model);
- if (authentic_amd)
+ if (authentic_amd || hygon_genuine)
rapl_probe_amd(family, model);
}
@@ -4043,8 +4063,9 @@ void perf_limit_reasons_probe(unsigned int family, unsigned int model)
return;
switch (model) {
- case INTEL_FAM6_HASWELL_CORE: /* HSW */
- case INTEL_FAM6_HASWELL_GT3E: /* HSW */
+ case INTEL_FAM6_HASWELL: /* HSW */
+ case INTEL_FAM6_HASWELL_L: /* HSW */
+ case INTEL_FAM6_HASWELL_G: /* HSW */
do_gfx_perf_limit_reasons = 1;
case INTEL_FAM6_HASWELL_X: /* HSX */
do_core_perf_limit_reasons = 1;
@@ -4259,27 +4280,28 @@ int has_snb_msrs(unsigned int family, unsigned int model)
switch (model) {
case INTEL_FAM6_SANDYBRIDGE:
case INTEL_FAM6_SANDYBRIDGE_X:
- case INTEL_FAM6_IVYBRIDGE: /* IVB */
- case INTEL_FAM6_IVYBRIDGE_X: /* IVB Xeon */
- case INTEL_FAM6_HASWELL_CORE: /* HSW */
- case INTEL_FAM6_HASWELL_X: /* HSW */
- case INTEL_FAM6_HASWELL_GT3E: /* HSW */
- case INTEL_FAM6_BROADWELL_CORE: /* BDW */
- case INTEL_FAM6_BROADWELL_GT3E: /* BDW */
- case INTEL_FAM6_BROADWELL_X: /* BDX */
- case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */
- case INTEL_FAM6_CANNONLAKE_MOBILE: /* CNL */
- case INTEL_FAM6_SKYLAKE_X: /* SKX */
- case INTEL_FAM6_ATOM_GOLDMONT: /* BXT */
+ case INTEL_FAM6_IVYBRIDGE: /* IVB */
+ case INTEL_FAM6_IVYBRIDGE_X: /* IVB Xeon */
+ case INTEL_FAM6_HASWELL: /* HSW */
+ case INTEL_FAM6_HASWELL_X: /* HSW */
+ case INTEL_FAM6_HASWELL_L: /* HSW */
+ case INTEL_FAM6_HASWELL_G: /* HSW */
+ case INTEL_FAM6_BROADWELL: /* BDW */
+ case INTEL_FAM6_BROADWELL_G: /* BDW */
+ case INTEL_FAM6_BROADWELL_X: /* BDX */
+ case INTEL_FAM6_SKYLAKE_L: /* SKL */
+ case INTEL_FAM6_CANNONLAKE_L: /* CNL */
+ case INTEL_FAM6_SKYLAKE_X: /* SKX */
+ case INTEL_FAM6_ATOM_GOLDMONT: /* BXT */
case INTEL_FAM6_ATOM_GOLDMONT_PLUS:
- case INTEL_FAM6_ATOM_GOLDMONT_X: /* DNV */
+ case INTEL_FAM6_ATOM_GOLDMONT_D: /* DNV */
return 1;
}
return 0;
}
/*
- * HSW adds support for additional MSRs:
+ * HSW ULT added support for C8/C9/C10 MSRs:
*
* MSR_PKG_C8_RESIDENCY 0x00000630
* MSR_PKG_C9_RESIDENCY 0x00000631
@@ -4290,16 +4312,16 @@ int has_snb_msrs(unsigned int family, unsigned int model)
* MSR_PKGC10_IRTL 0x00000635
*
*/
-int has_hsw_msrs(unsigned int family, unsigned int model)
+int has_c8910_msrs(unsigned int family, unsigned int model)
{
if (!genuine_intel)
return 0;
switch (model) {
- case INTEL_FAM6_HASWELL_CORE:
- case INTEL_FAM6_BROADWELL_CORE: /* BDW */
- case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */
- case INTEL_FAM6_CANNONLAKE_MOBILE: /* CNL */
+ case INTEL_FAM6_HASWELL_L: /* HSW */
+ case INTEL_FAM6_BROADWELL: /* BDW */
+ case INTEL_FAM6_SKYLAKE_L: /* SKL */
+ case INTEL_FAM6_CANNONLAKE_L: /* CNL */
case INTEL_FAM6_ATOM_GOLDMONT: /* BXT */
case INTEL_FAM6_ATOM_GOLDMONT_PLUS:
return 1;
@@ -4321,8 +4343,8 @@ int has_skl_msrs(unsigned int family, unsigned int model)
return 0;
switch (model) {
- case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */
- case INTEL_FAM6_CANNONLAKE_MOBILE: /* CNL */
+ case INTEL_FAM6_SKYLAKE_L: /* SKL */
+ case INTEL_FAM6_CANNONLAKE_L: /* CNL */
return 1;
}
return 0;
@@ -4334,7 +4356,7 @@ int is_slm(unsigned int family, unsigned int model)
return 0;
switch (model) {
case INTEL_FAM6_ATOM_SILVERMONT: /* BYT */
- case INTEL_FAM6_ATOM_SILVERMONT_X: /* AVN */
+ case INTEL_FAM6_ATOM_SILVERMONT_D: /* AVN */
return 1;
}
return 0;
@@ -4357,7 +4379,7 @@ int is_cnl(unsigned int family, unsigned int model)
return 0;
switch (model) {
- case INTEL_FAM6_CANNONLAKE_MOBILE: /* CNL */
+ case INTEL_FAM6_CANNONLAKE_L: /* CNL */
return 1;
}
@@ -4580,21 +4602,22 @@ unsigned int intel_model_duplicates(unsigned int model)
case INTEL_FAM6_XEON_PHI_KNM:
return INTEL_FAM6_XEON_PHI_KNL;
- case INTEL_FAM6_HASWELL_ULT:
- return INTEL_FAM6_HASWELL_CORE;
-
case INTEL_FAM6_BROADWELL_X:
- case INTEL_FAM6_BROADWELL_XEON_D: /* BDX-DE */
+ case INTEL_FAM6_BROADWELL_D: /* BDX-DE */
return INTEL_FAM6_BROADWELL_X;
- case INTEL_FAM6_SKYLAKE_MOBILE:
- case INTEL_FAM6_SKYLAKE_DESKTOP:
- case INTEL_FAM6_KABYLAKE_MOBILE:
- case INTEL_FAM6_KABYLAKE_DESKTOP:
- return INTEL_FAM6_SKYLAKE_MOBILE;
+ case INTEL_FAM6_SKYLAKE_L:
+ case INTEL_FAM6_SKYLAKE:
+ case INTEL_FAM6_KABYLAKE_L:
+ case INTEL_FAM6_KABYLAKE:
+ return INTEL_FAM6_SKYLAKE_L;
+
+ case INTEL_FAM6_ICELAKE_L:
+ case INTEL_FAM6_ICELAKE_NNPI:
+ return INTEL_FAM6_CANNONLAKE_L;
- case INTEL_FAM6_ICELAKE_MOBILE:
- return INTEL_FAM6_CANNONLAKE_MOBILE;
+ case INTEL_FAM6_ATOM_TREMONT_D:
+ return INTEL_FAM6_ATOM_GOLDMONT_D;
}
return model;
}
@@ -4612,6 +4635,8 @@ void process_cpuid()
genuine_intel = 1;
else if (ebx == 0x68747541 && ecx == 0x444d4163 && edx == 0x69746e65)
authentic_amd = 1;
+ else if (ebx == 0x6f677948 && ecx == 0x656e6975 && edx == 0x6e65476e)
+ hygon_genuine = 1;
if (!quiet)
fprintf(outf, "CPUID(0): %.4s%.4s%.4s ",
@@ -4743,10 +4768,10 @@ void process_cpuid()
if (crystal_hz == 0)
switch(model) {
- case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */
+ case INTEL_FAM6_SKYLAKE_L: /* SKL */
crystal_hz = 24000000; /* 24.0 MHz */
break;
- case INTEL_FAM6_ATOM_GOLDMONT_X: /* DNV */
+ case INTEL_FAM6_ATOM_GOLDMONT_D: /* DNV */
crystal_hz = 25000000; /* 25.0 MHz */
break;
case INTEL_FAM6_ATOM_GOLDMONT: /* BXT */
@@ -4832,12 +4857,12 @@ void process_cpuid()
BIC_NOT_PRESENT(BIC_CPU_c7);
BIC_NOT_PRESENT(BIC_Pkgpc7);
}
- if (has_hsw_msrs(family, model)) {
+ if (has_c8910_msrs(family, model)) {
BIC_PRESENT(BIC_Pkgpc8);
BIC_PRESENT(BIC_Pkgpc9);
BIC_PRESENT(BIC_Pkgpc10);
}
- do_irtl_hsw = has_hsw_msrs(family, model);
+ do_irtl_hsw = has_c8910_msrs(family, model);
if (has_skl_msrs(family, model)) {
BIC_PRESENT(BIC_Totl_c0);
BIC_PRESENT(BIC_Any_c0);
@@ -5135,7 +5160,7 @@ int initialize_counters(int cpu_id)
void allocate_output_buffer()
{
- output_buffer = calloc(1, (1 + topo.num_cpus) * 1024);
+ output_buffer = calloc(1, (1 + topo.num_cpus) * 2048);
outp = output_buffer;
if (outp == NULL)
err(-1, "calloc output buffer");
@@ -5281,7 +5306,7 @@ int get_and_dump_counters(void)
}
void print_version() {
- fprintf(outf, "turbostat version 19.03.20"
+ fprintf(outf, "turbostat version 19.08.31"
" - Len Brown <lenb@kernel.org>\n");
}
diff --git a/tools/power/x86/x86_energy_perf_policy/Makefile b/tools/power/x86/x86_energy_perf_policy/Makefile
index 1fdeef864e7c..666b325a62a2 100644
--- a/tools/power/x86/x86_energy_perf_policy/Makefile
+++ b/tools/power/x86/x86_energy_perf_policy/Makefile
@@ -9,8 +9,9 @@ ifeq ("$(origin O)", "command line")
endif
x86_energy_perf_policy : x86_energy_perf_policy.c
-override CFLAGS += -Wall -I../../../include
+override CFLAGS += -O2 -Wall -I../../../include
override CFLAGS += -DMSRHEADER='"../../../../arch/x86/include/asm/msr-index.h"'
+override CFLAGS += -D_FORTIFY_SOURCE=2
%: %.c
@mkdir -p $(BUILD_OUTPUT)
diff --git a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.8 b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.8
index 17db1c3af4d0..78c6361898b1 100644
--- a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.8
+++ b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.8
@@ -40,7 +40,7 @@ in the same processor package.
Hardware P-States (HWP) are effectively an expansion of hardware
P-state control from the opportunistic turbo-mode P-state range
to include the entire range of available P-states.
-On Broadwell Xeon, the initial HWP implementation, EBP influenced HWP.
+On Broadwell Xeon, the initial HWP implementation, EPB influenced HWP.
That influence was removed in subsequent generations,
where it was moved to the
Energy_Performance_Preference (EPP) field in
diff --git a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c
index 65bbe627a425..3fe1eed900d4 100644
--- a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c
+++ b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* x86_energy_perf_policy -- set the energy versus performance
* policy preference bias on recent X86 processors.
@@ -5,8 +6,6 @@
/*
* Copyright (c) 2010 - 2017 Intel Corporation.
* Len Brown <len.brown@intel.com>
- *
- * This program is released under GPL v2
*/
#define _GNU_SOURCE
@@ -546,7 +545,7 @@ void cmdline(int argc, char **argv)
progname = argv[0];
- while ((opt = getopt_long_only(argc, argv, "+a:c:dD:E:e:f:m:M:rt:u:vw",
+ while ((opt = getopt_long_only(argc, argv, "+a:c:dD:E:e:f:m:M:rt:u:vw:",
long_options, &option_index)) != -1) {
switch (opt) {
case 'a':
@@ -1260,6 +1259,15 @@ void probe_dev_msr(void)
if (system("/sbin/modprobe msr > /dev/null 2>&1"))
err(-5, "no /dev/cpu/0/msr, Try \"# modprobe msr\" ");
}
+
+static void get_cpuid_or_exit(unsigned int leaf,
+ unsigned int *eax, unsigned int *ebx,
+ unsigned int *ecx, unsigned int *edx)
+{
+ if (!__get_cpuid(leaf, eax, ebx, ecx, edx))
+ errx(1, "Processor not supported\n");
+}
+
/*
* early_cpuid()
* initialize turbo_is_enabled, has_hwp, has_epb
@@ -1267,15 +1275,10 @@ void probe_dev_msr(void)
*/
void early_cpuid(void)
{
- unsigned int eax, ebx, ecx, edx, max_level;
+ unsigned int eax, ebx, ecx, edx;
unsigned int fms, family, model;
- __get_cpuid(0, &max_level, &ebx, &ecx, &edx);
-
- if (max_level < 6)
- errx(1, "Processor not supported\n");
-
- __get_cpuid(1, &fms, &ebx, &ecx, &edx);
+ get_cpuid_or_exit(1, &fms, &ebx, &ecx, &edx);
family = (fms >> 8) & 0xf;
model = (fms >> 4) & 0xf;
if (family == 6 || family == 0xf)
@@ -1289,7 +1292,7 @@ void early_cpuid(void)
bdx_highest_ratio = msr & 0xFF;
}
- __get_cpuid(0x6, &eax, &ebx, &ecx, &edx);
+ get_cpuid_or_exit(0x6, &eax, &ebx, &ecx, &edx);
turbo_is_enabled = (eax >> 1) & 1;
has_hwp = (eax >> 7) & 1;
has_epb = (ecx >> 3) & 1;
@@ -1307,7 +1310,7 @@ void parse_cpuid(void)
eax = ebx = ecx = edx = 0;
- __get_cpuid(0, &max_level, &ebx, &ecx, &edx);
+ get_cpuid_or_exit(0, &max_level, &ebx, &ecx, &edx);
if (ebx == 0x756e6547 && edx == 0x49656e69 && ecx == 0x6c65746e)
genuine_intel = 1;
@@ -1316,7 +1319,7 @@ void parse_cpuid(void)
fprintf(stderr, "CPUID(0): %.4s%.4s%.4s ",
(char *)&ebx, (char *)&edx, (char *)&ecx);
- __get_cpuid(1, &fms, &ebx, &ecx, &edx);
+ get_cpuid_or_exit(1, &fms, &ebx, &ecx, &edx);
family = (fms >> 8) & 0xf;
model = (fms >> 4) & 0xf;
stepping = fms & 0xf;
@@ -1341,7 +1344,7 @@ void parse_cpuid(void)
errx(1, "CPUID: no MSR");
- __get_cpuid(0x6, &eax, &ebx, &ecx, &edx);
+ get_cpuid_or_exit(0x6, &eax, &ebx, &ecx, &edx);
/* turbo_is_enabled already set */
/* has_hwp already set */
has_hwp_notify = eax & (1 << 8);
diff --git a/tools/scripts/Makefile.include b/tools/scripts/Makefile.include
index 495066bafbe3..ded7a950dc40 100644
--- a/tools/scripts/Makefile.include
+++ b/tools/scripts/Makefile.include
@@ -32,7 +32,6 @@ EXTRA_WARNINGS += -Wno-system-headers
EXTRA_WARNINGS += -Wold-style-definition
EXTRA_WARNINGS += -Wpacked
EXTRA_WARNINGS += -Wredundant-decls
-EXTRA_WARNINGS += -Wshadow
EXTRA_WARNINGS += -Wstrict-prototypes
EXTRA_WARNINGS += -Wswitch-default
EXTRA_WARNINGS += -Wswitch-enum
@@ -69,8 +68,16 @@ endif
# will do for now and keep the above -Wstrict-aliasing=3 in place
# in newer systems.
# Needed for the __raw_cmpxchg in tools/arch/x86/include/asm/cmpxchg.h
+#
+# See https://lkml.org/lkml/2006/11/28/253 and https://gcc.gnu.org/gcc-4.8/changes.html,
+# that takes into account Linus's comments (search for Wshadow) for the reasoning about
+# -Wshadow not being interesting before gcc 4.8.
+
ifneq ($(filter 3.%,$(MAKE_VERSION)),) # make-3
EXTRA_WARNINGS += -fno-strict-aliasing
+EXTRA_WARNINGS += -Wno-shadow
+else
+EXTRA_WARNINGS += -Wshadow
endif
ifneq ($(findstring $(MAKEFLAGS), w),w)
diff --git a/tools/spi/Makefile b/tools/spi/Makefile
index 815d15589177..5c342e655e55 100644
--- a/tools/spi/Makefile
+++ b/tools/spi/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
include ../scripts/Makefile.include
bindir ?= /usr/bin
diff --git a/tools/spi/spidev_test.c b/tools/spi/spidev_test.c
index 4c12e6aea5d5..3559e7646256 100644
--- a/tools/spi/spidev_test.c
+++ b/tools/spi/spidev_test.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SPI testing utility (using spidev driver)
*
* Copyright (c) 2007 MontaVista Software, Inc.
* Copyright (c) 2007 Anton Vorontsov <avorontsov@ru.mvista.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License.
- *
* Cross-compile with cross-gcc -I/path/to/cross-kernel/include
*/
diff --git a/tools/testing/fault-injection/failcmd.sh b/tools/testing/fault-injection/failcmd.sh
index 29a6c63c5a15..78dac34264be 100644
--- a/tools/testing/fault-injection/failcmd.sh
+++ b/tools/testing/fault-injection/failcmd.sh
@@ -42,7 +42,7 @@ OPTIONS
--interval=value, --space=value, --verbose=value, --task-filter=value,
--stacktrace-depth=value, --require-start=value, --require-end=value,
--reject-start=value, --reject-end=value, --ignore-gfp-wait=value
- See Documentation/fault-injection/fault-injection.txt for more
+ See Documentation/fault-injection/fault-injection.rst for more
information
failslab options:
diff --git a/tools/testing/ktest/config-bisect.pl b/tools/testing/ktest/config-bisect.pl
index b28feea7c363..6fd864935319 100755
--- a/tools/testing/ktest/config-bisect.pl
+++ b/tools/testing/ktest/config-bisect.pl
@@ -1,10 +1,9 @@
#!/usr/bin/perl -w
+# SPDX-License-Identifier: GPL-2.0-only
#
# Copyright 2015 - Steven Rostedt, Red Hat Inc.
# Copyright 2017 - Steven Rostedt, VMware, Inc.
#
-# Licensed under the terms of the GNU GPL License version 2
-#
# usage:
# config-bisect.pl [options] good-config bad-config [good|bad]
@@ -664,7 +663,7 @@ while ($#ARGV >= 0) {
}
else {
- die "Unknow option $opt\n";
+ die "Unknown option $opt\n";
}
}
@@ -733,7 +732,7 @@ if ($start) {
}
}
run_command "cp $good_start $good" or die "failed to copy to $good\n";
- run_command "cp $bad_start $bad" or die "faield to copy to $bad\n";
+ run_command "cp $bad_start $bad" or die "failed to copy to $bad\n";
} else {
if ( ! -f $good ) {
die "Can not find file $good\n";
diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl
index 4711f57e809a..220d04f958a6 100755
--- a/tools/testing/ktest/ktest.pl
+++ b/tools/testing/ktest/ktest.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl -w
+# SPDX-License-Identifier: GPL-2.0-only
#
# Copyright 2010 - Steven Rostedt <srostedt@redhat.com>, Red Hat Inc.
-# Licensed under the terms of the GNU GPL License version 2
#
use strict;
diff --git a/tools/testing/nvdimm/dax-dev.c b/tools/testing/nvdimm/dax-dev.c
index f36e708265b8..7e5d979e73cb 100644
--- a/tools/testing/nvdimm/dax-dev.c
+++ b/tools/testing/nvdimm/dax-dev.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#include "test/nfit_test.h"
#include <linux/mm.h>
diff --git a/tools/testing/nvdimm/dimm_devs.c b/tools/testing/nvdimm/dimm_devs.c
index 2d4baf57822f..57bd27dedf1f 100644
--- a/tools/testing/nvdimm/dimm_devs.c
+++ b/tools/testing/nvdimm/dimm_devs.c
@@ -18,24 +18,13 @@ ssize_t security_show(struct device *dev,
* For the test version we need to poll the "hardware" in order
* to get the updated status for unlock testing.
*/
- nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
- nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, NVDIMM_MASTER);
+ nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
- switch (nvdimm->sec.state) {
- case NVDIMM_SECURITY_DISABLED:
+ if (test_bit(NVDIMM_SECURITY_DISABLED, &nvdimm->sec.flags))
return sprintf(buf, "disabled\n");
- case NVDIMM_SECURITY_UNLOCKED:
+ if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags))
return sprintf(buf, "unlocked\n");
- case NVDIMM_SECURITY_LOCKED:
+ if (test_bit(NVDIMM_SECURITY_LOCKED, &nvdimm->sec.flags))
return sprintf(buf, "locked\n");
- case NVDIMM_SECURITY_FROZEN:
- return sprintf(buf, "frozen\n");
- case NVDIMM_SECURITY_OVERWRITE:
- return sprintf(buf, "overwrite\n");
- default:
- return -ENOTTY;
- }
-
return -ENOTTY;
}
-
diff --git a/tools/testing/nvdimm/pmem-dax.c b/tools/testing/nvdimm/pmem-dax.c
index 2e7fd8227969..af19c85558e7 100644
--- a/tools/testing/nvdimm/pmem-dax.c
+++ b/tools/testing/nvdimm/pmem-dax.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014-2016, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#include "test/nfit_test.h"
#include <linux/blkdev.h>
diff --git a/tools/testing/nvdimm/test/iomap.c b/tools/testing/nvdimm/test/iomap.c
index c6635fee27d8..3f55f2f99112 100644
--- a/tools/testing/nvdimm/test/iomap.c
+++ b/tools/testing/nvdimm/test/iomap.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#include <linux/memremap.h>
#include <linux/rculist.h>
@@ -108,23 +100,59 @@ static void nfit_test_kill(void *_pgmap)
{
struct dev_pagemap *pgmap = _pgmap;
- pgmap->kill(pgmap->ref);
+ WARN_ON(!pgmap || !pgmap->ref);
+
+ if (pgmap->ops && pgmap->ops->kill)
+ pgmap->ops->kill(pgmap);
+ else
+ percpu_ref_kill(pgmap->ref);
+
+ if (pgmap->ops && pgmap->ops->cleanup) {
+ pgmap->ops->cleanup(pgmap);
+ } else {
+ wait_for_completion(&pgmap->done);
+ percpu_ref_exit(pgmap->ref);
+ }
+}
+
+static void dev_pagemap_percpu_release(struct percpu_ref *ref)
+{
+ struct dev_pagemap *pgmap =
+ container_of(ref, struct dev_pagemap, internal_ref);
+
+ complete(&pgmap->done);
}
void *__wrap_devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap)
{
+ int error;
resource_size_t offset = pgmap->res.start;
struct nfit_test_resource *nfit_res = get_nfit_res(offset);
- if (nfit_res) {
- int rc;
-
- rc = devm_add_action_or_reset(dev, nfit_test_kill, pgmap);
- if (rc)
- return ERR_PTR(rc);
- return nfit_res->buf + offset - nfit_res->res.start;
+ if (!nfit_res)
+ return devm_memremap_pages(dev, pgmap);
+
+ if (!pgmap->ref) {
+ if (pgmap->ops && (pgmap->ops->kill || pgmap->ops->cleanup))
+ return ERR_PTR(-EINVAL);
+
+ init_completion(&pgmap->done);
+ error = percpu_ref_init(&pgmap->internal_ref,
+ dev_pagemap_percpu_release, 0, GFP_KERNEL);
+ if (error)
+ return ERR_PTR(error);
+ pgmap->ref = &pgmap->internal_ref;
+ } else {
+ if (!pgmap->ops || !pgmap->ops->kill || !pgmap->ops->cleanup) {
+ WARN(1, "Missing reference count teardown definition\n");
+ return ERR_PTR(-EINVAL);
+ }
}
- return devm_memremap_pages(dev, pgmap);
+
+ error = devm_add_action_or_reset(dev, nfit_test_kill, pgmap);
+ if (error)
+ return ERR_PTR(error);
+ return nfit_res->buf + offset - nfit_res->res.start;
}
EXPORT_SYMBOL_GPL(__wrap_devm_memremap_pages);
diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c
index bb4225cdf666..bf6422a6af7f 100644
--- a/tools/testing/nvdimm/test/nfit.c
+++ b/tools/testing/nvdimm/test/nfit.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/platform_device.h>
@@ -436,10 +428,9 @@ static int nd_intel_test_finish_query(struct nfit_test *t,
dev_dbg(dev, "%s: still verifying\n", __func__);
break;
}
-
dev_dbg(dev, "%s: transition out verify\n", __func__);
fw->state = FW_STATE_UPDATED;
- /* we are going to fall through if it's "done" */
+ /* fall through */
case FW_STATE_UPDATED:
nd_cmd->status = 0;
/* bogus test version */
diff --git a/tools/testing/nvdimm/test/nfit_test.h b/tools/testing/nvdimm/test/nfit_test.h
index ade14fe3837e..448d686da8b1 100644
--- a/tools/testing/nvdimm/test/nfit_test.h
+++ b/tools/testing/nvdimm/test/nfit_test.h
@@ -1,14 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#ifndef __NFIT_TEST_H__
#define __NFIT_TEST_H__
diff --git a/tools/testing/radix-tree/benchmark.c b/tools/testing/radix-tree/benchmark.c
index 7e195ed8e92d..523c79f22ed3 100644
--- a/tools/testing/radix-tree/benchmark.c
+++ b/tools/testing/radix-tree/benchmark.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* benchmark.c:
* Author: Konstantin Khlebnikov <koct9i@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#include <linux/radix-tree.h>
#include <linux/slab.h>
diff --git a/tools/testing/radix-tree/idr-test.c b/tools/testing/radix-tree/idr-test.c
index 1b63bdb7688f..8995092d541e 100644
--- a/tools/testing/radix-tree/idr-test.c
+++ b/tools/testing/radix-tree/idr-test.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* idr-test.c: Test the IDR API
* Copyright (c) 2016 Matthew Wilcox <willy@infradead.org>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#include <linux/bitmap.h>
#include <linux/idr.h>
@@ -287,6 +279,51 @@ static void idr_align_test(struct idr *idr)
}
}
+DEFINE_IDR(find_idr);
+
+static void *idr_throbber(void *arg)
+{
+ time_t start = time(NULL);
+ int id = *(int *)arg;
+
+ rcu_register_thread();
+ do {
+ idr_alloc(&find_idr, xa_mk_value(id), id, id + 1, GFP_KERNEL);
+ idr_remove(&find_idr, id);
+ } while (time(NULL) < start + 10);
+ rcu_unregister_thread();
+
+ return NULL;
+}
+
+void idr_find_test_1(int anchor_id, int throbber_id)
+{
+ pthread_t throbber;
+ time_t start = time(NULL);
+
+ pthread_create(&throbber, NULL, idr_throbber, &throbber_id);
+
+ BUG_ON(idr_alloc(&find_idr, xa_mk_value(anchor_id), anchor_id,
+ anchor_id + 1, GFP_KERNEL) != anchor_id);
+
+ do {
+ int id = 0;
+ void *entry = idr_get_next(&find_idr, &id);
+ BUG_ON(entry != xa_mk_value(id));
+ } while (time(NULL) < start + 11);
+
+ pthread_join(throbber, NULL);
+
+ idr_remove(&find_idr, anchor_id);
+ BUG_ON(!idr_is_empty(&find_idr));
+}
+
+void idr_find_test(void)
+{
+ idr_find_test_1(100000, 0);
+ idr_find_test_1(0, 100000);
+}
+
void idr_checks(void)
{
unsigned long i;
@@ -368,6 +405,7 @@ void idr_checks(void)
idr_u32_test(1);
idr_u32_test(0);
idr_align_test(&idr);
+ idr_find_test();
}
#define module_init(x)
diff --git a/tools/testing/radix-tree/iteration_check.c b/tools/testing/radix-tree/iteration_check.c
index 238db187aa15..e9908bcb06dd 100644
--- a/tools/testing/radix-tree/iteration_check.c
+++ b/tools/testing/radix-tree/iteration_check.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* iteration_check.c: test races having to do with xarray iteration
* Copyright (c) 2016 Intel Corporation
* Author: Ross Zwisler <ross.zwisler@linux.intel.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#include <pthread.h>
#include "test.h"
diff --git a/tools/testing/radix-tree/linux/rcupdate.h b/tools/testing/radix-tree/linux/rcupdate.h
index fd280b070fdb..fed468fb0c78 100644
--- a/tools/testing/radix-tree/linux/rcupdate.h
+++ b/tools/testing/radix-tree/linux/rcupdate.h
@@ -7,6 +7,6 @@
#define rcu_dereference_raw(p) rcu_dereference(p)
#define rcu_dereference_protected(p, cond) rcu_dereference(p)
#define rcu_dereference_check(p, cond) rcu_dereference(p)
-#define RCU_INIT_POINTER(p, v) (p) = (v)
+#define RCU_INIT_POINTER(p, v) do { (p) = (v); } while (0)
#endif
diff --git a/tools/testing/radix-tree/multiorder.c b/tools/testing/radix-tree/multiorder.c
index ff27a74d9762..9eae0fb5a67d 100644
--- a/tools/testing/radix-tree/multiorder.c
+++ b/tools/testing/radix-tree/multiorder.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* multiorder.c: Multi-order radix tree entry testing
* Copyright (c) 2016 Intel Corporation
* Author: Ross Zwisler <ross.zwisler@linux.intel.com>
* Author: Matthew Wilcox <matthew.r.wilcox@intel.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
*/
#include <linux/radix-tree.h>
#include <linux/slab.h>
diff --git a/tools/testing/scatterlist/Makefile b/tools/testing/scatterlist/Makefile
index 933c3a6e4d77..cbb003d9305e 100644
--- a/tools/testing/scatterlist/Makefile
+++ b/tools/testing/scatterlist/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
CFLAGS += -I. -I../../include -g -O2 -Wall -fsanitize=address
LDFLAGS += -fsanitize=address -fsanitize=undefined
TARGETS = main
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 9781ca79794a..25b43a8c2b15 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -74,7 +74,7 @@ endif
# Append kselftest to KBUILD_OUTPUT to avoid cluttering
# KBUILD_OUTPUT with selftest objects and headers installed
# by selftests Makefile or lib.mk.
-ifneq ($(KBUILD_SRC),)
+ifdef building_out_of_srctree
override LDFLAGS =
endif
diff --git a/tools/testing/selftests/android/Makefile b/tools/testing/selftests/android/Makefile
index 72c25a3cb658..7c462714b418 100644
--- a/tools/testing/selftests/android/Makefile
+++ b/tools/testing/selftests/android/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
SUBDIRS := ion
TEST_PROGS := run.sh
diff --git a/tools/testing/selftests/android/ion/Makefile b/tools/testing/selftests/android/ion/Makefile
index 88cfe88e466f..0eb7ab626e1c 100644
--- a/tools/testing/selftests/android/ion/Makefile
+++ b/tools/testing/selftests/android/ion/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
INCLUDEDIR := -I. -I../../../../../drivers/staging/android/uapi/ -I../../../../../usr/include/
CFLAGS := $(CFLAGS) $(INCLUDEDIR) -Wall -O2 -g
diff --git a/tools/testing/selftests/android/ion/ion.h b/tools/testing/selftests/android/ion/ion.h
index f7021ac51335..33db23018abf 100644
--- a/tools/testing/selftests/android/ion/ion.h
+++ b/tools/testing/selftests/android/ion/ion.h
@@ -1,17 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ion.h
*
* Copyright (C) 2011 Google, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
/* This file is copied from drivers/staging/android/uapi/ion.h
diff --git a/tools/testing/selftests/android/ion/ionapp_export.c b/tools/testing/selftests/android/ion/ionapp_export.c
index b5fa0a2dc968..063b7830d1bd 100644
--- a/tools/testing/selftests/android/ion/ionapp_export.c
+++ b/tools/testing/selftests/android/ion/ionapp_export.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ionapp_export.c
*
@@ -7,16 +8,6 @@
* So, this server has to be started first before the client.
*
* Copyright (C) 2017 Pintu Kumar <pintu.ping@gmail.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <stdio.h>
diff --git a/tools/testing/selftests/android/ion/ionapp_import.c b/tools/testing/selftests/android/ion/ionapp_import.c
index ae2d704cfa46..54b580cb04f6 100644
--- a/tools/testing/selftests/android/ion/ionapp_import.c
+++ b/tools/testing/selftests/android/ion/ionapp_import.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ionapp_import.c
*
@@ -6,16 +7,6 @@
* This acts like a client for ionapp_export.
*
* Copyright (C) 2017 Pintu Kumar <pintu.ping@gmail.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <stdio.h>
diff --git a/tools/testing/selftests/arm64/.gitignore b/tools/testing/selftests/arm64/.gitignore
new file mode 100644
index 000000000000..e8fae8d61ed6
--- /dev/null
+++ b/tools/testing/selftests/arm64/.gitignore
@@ -0,0 +1 @@
+tags_test
diff --git a/tools/testing/selftests/arm64/Makefile b/tools/testing/selftests/arm64/Makefile
new file mode 100644
index 000000000000..f9f79fb272f0
--- /dev/null
+++ b/tools/testing/selftests/arm64/Makefile
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# ARCH can be overridden by the user for cross compiling
+ARCH ?= $(shell uname -m 2>/dev/null || echo not)
+
+ifneq (,$(filter $(ARCH),aarch64 arm64))
+CFLAGS += -I../../../../usr/include/
+TEST_GEN_PROGS := tags_test
+TEST_PROGS := run_tags_test.sh
+endif
+
+include ../lib.mk
diff --git a/tools/testing/selftests/arm64/run_tags_test.sh b/tools/testing/selftests/arm64/run_tags_test.sh
new file mode 100755
index 000000000000..745f11379930
--- /dev/null
+++ b/tools/testing/selftests/arm64/run_tags_test.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+
+echo "--------------------"
+echo "running tags test"
+echo "--------------------"
+./tags_test
+if [ $? -ne 0 ]; then
+ echo "[FAIL]"
+else
+ echo "[PASS]"
+fi
diff --git a/tools/testing/selftests/arm64/tags_test.c b/tools/testing/selftests/arm64/tags_test.c
new file mode 100644
index 000000000000..5701163460ef
--- /dev/null
+++ b/tools/testing/selftests/arm64/tags_test.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/prctl.h>
+#include <sys/utsname.h>
+
+#define SHIFT_TAG(tag) ((uint64_t)(tag) << 56)
+#define SET_TAG(ptr, tag) (((uint64_t)(ptr) & ~SHIFT_TAG(0xff)) | \
+ SHIFT_TAG(tag))
+
+int main(void)
+{
+ static int tbi_enabled = 0;
+ unsigned long tag = 0;
+ struct utsname *ptr;
+ int err;
+
+ if (prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0) == 0)
+ tbi_enabled = 1;
+ ptr = (struct utsname *)malloc(sizeof(*ptr));
+ if (tbi_enabled)
+ tag = 0x42;
+ ptr = (struct utsname *)SET_TAG(ptr, tag);
+ err = uname(ptr);
+ free(ptr);
+
+ return err;
+}
diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore
index a877803e4ba8..7470327edcfe 100644
--- a/tools/testing/selftests/bpf/.gitignore
+++ b/tools/testing/selftests/bpf/.gitignore
@@ -22,6 +22,7 @@ test_lirc_mode2_user
get_cgroup_id_user
test_skb_cgroup_id_user
test_socket_cookie
+test_cgroup_attach
test_cgroup_storage
test_select_reuseport
test_flow_dissector
@@ -31,6 +32,10 @@ test_section_names
test_tcpnotify_user
test_libbpf
test_tcp_check_syncookie_user
+test_sysctl
alu32
libbpf.pc
libbpf.so.*
+test_hashmap
+test_btf_dump
+xdping
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 66f2dca1dee1..6889c19a628c 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
+include ../../../../scripts/Kbuild.include
+include ../../../scripts/Makefile.arch
LIBDIR := ../../../lib
BPFDIR := $(LIBDIR)/bpf
@@ -15,19 +17,26 @@ LLC ?= llc
LLVM_OBJCOPY ?= llvm-objcopy
LLVM_READELF ?= llvm-readelf
BTF_PAHOLE ?= pahole
-CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(BPFDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include
+BPF_GCC ?= $(shell command -v bpf-gcc;)
+CFLAGS += -g -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(BPFDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include \
+ -Dbpf_prog_load=bpf_prog_test_load \
+ -Dbpf_load_program=bpf_test_load_program
LDLIBS += -lcap -lelf -lrt -lpthread
# Order correspond to 'make run_tests' order
TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \
test_align test_verifier_log test_dev_cgroup test_tcpbpf_user \
- test_sock test_btf test_sockmap test_lirc_mode2_user get_cgroup_id_user \
- test_socket_cookie test_cgroup_storage test_select_reuseport test_section_names \
- test_netcnt test_tcpnotify_user test_sock_fields test_sysctl
+ test_sock test_btf test_sockmap get_cgroup_id_user test_socket_cookie \
+ test_cgroup_storage test_select_reuseport test_section_names \
+ test_netcnt test_tcpnotify_user test_sock_fields test_sysctl test_hashmap \
+ test_btf_dump test_cgroup_attach xdping
BPF_OBJ_FILES = $(patsubst %.c,%.o, $(notdir $(wildcard progs/*.c)))
TEST_GEN_FILES = $(BPF_OBJ_FILES)
+BTF_C_FILES = $(wildcard progs/btf_dump_test_case_*.c)
+TEST_FILES = $(BTF_C_FILES)
+
# Also test sub-register code-gen if LLVM has eBPF v3 processor support which
# contains both ALU32 and JMP32 instructions.
SUBREG_CODEGEN := $(shell echo "int cal(int a) { return a > 0; }" | \
@@ -38,11 +47,16 @@ ifneq ($(SUBREG_CODEGEN),)
TEST_GEN_FILES += $(patsubst %.o,alu32/%.o, $(BPF_OBJ_FILES))
endif
+ifneq ($(BPF_GCC),)
+TEST_GEN_FILES += $(patsubst %.o,bpf_gcc/%.o, $(BPF_OBJ_FILES))
+endif
+
# Order correspond to 'make run_tests' order
TEST_PROGS := test_kmod.sh \
test_libbpf.sh \
test_xdp_redirect.sh \
test_xdp_meta.sh \
+ test_xdp_veth.sh \
test_offload.py \
test_sock_addr.sh \
test_tunnel.sh \
@@ -50,20 +64,25 @@ TEST_PROGS := test_kmod.sh \
test_lirc_mode2.sh \
test_skb_cgroup_id.sh \
test_flow_dissector.sh \
- test_xdp_vlan.sh \
+ test_xdp_vlan_mode_generic.sh \
+ test_xdp_vlan_mode_native.sh \
test_lwt_ip_encap.sh \
test_tcp_check_syncookie.sh \
test_tc_tunnel.sh \
- test_tc_edt.sh
+ test_tc_edt.sh \
+ test_xdping.sh \
+ test_bpftool_build.sh
TEST_PROGS_EXTENDED := with_addr.sh \
with_tunnels.sh \
tcp_client.py \
- tcp_server.py
+ tcp_server.py \
+ test_xdp_vlan.sh
# Compile but not part of 'make run_tests'
TEST_GEN_PROGS_EXTENDED = test_libbpf_open test_sock_addr test_skb_cgroup_id_user \
- flow_dissector_load test_flow_dissector test_tcp_check_syncookie_user
+ flow_dissector_load test_flow_dissector test_tcp_check_syncookie_user \
+ test_lirc_mode2_user
include ../lib.mk
@@ -74,13 +93,14 @@ all: $(TEST_CUSTOM_PROGS)
$(OUTPUT)/urandom_read: $(OUTPUT)/%: %.c
$(CC) -o $@ $< -Wl,--build-id
-$(OUTPUT)/test_maps: map_tests/*.c
+$(OUTPUT)/test_stub.o: test_stub.c
+ $(CC) $(TEST_PROGS_CFLAGS) $(CFLAGS) -c -o $@ $<
BPFOBJ := $(OUTPUT)/libbpf.a
-$(TEST_GEN_PROGS): $(BPFOBJ)
+$(TEST_GEN_PROGS): $(OUTPUT)/test_stub.o $(BPFOBJ)
-$(TEST_GEN_PROGS_EXTENDED): $(OUTPUT)/libbpf.a
+$(TEST_GEN_PROGS_EXTENDED): $(OUTPUT)/test_stub.o $(OUTPUT)/libbpf.a
$(OUTPUT)/test_dev_cgroup: cgroup_helpers.c
$(OUTPUT)/test_skb_cgroup_id_user: cgroup_helpers.c
@@ -90,12 +110,13 @@ $(OUTPUT)/test_socket_cookie: cgroup_helpers.c
$(OUTPUT)/test_sockmap: cgroup_helpers.c
$(OUTPUT)/test_tcpbpf_user: cgroup_helpers.c
$(OUTPUT)/test_tcpnotify_user: cgroup_helpers.c trace_helpers.c
-$(OUTPUT)/test_progs: trace_helpers.c
+$(OUTPUT)/test_progs: cgroup_helpers.c trace_helpers.c
$(OUTPUT)/get_cgroup_id_user: cgroup_helpers.c
$(OUTPUT)/test_cgroup_storage: cgroup_helpers.c
$(OUTPUT)/test_netcnt: cgroup_helpers.c
$(OUTPUT)/test_sock_fields: cgroup_helpers.c
$(OUTPUT)/test_sysctl: cgroup_helpers.c
+$(OUTPUT)/test_cgroup_attach: cgroup_helpers.c
.PHONY: force
@@ -121,15 +142,19 @@ endif
#
# Use '-idirafter': Don't interfere with include mechanics except where the
# build would have failed anyways.
-CLANG_SYS_INCLUDES := $(shell $(CLANG) -v -E - </dev/null 2>&1 \
+define get_sys_includes
+$(shell $(1) -v -E - </dev/null 2>&1 \
| sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }')
+endef
+CLANG_SYS_INCLUDES = $(call get_sys_includes,$(CLANG))
+BPF_CFLAGS = -I. -I./include/uapi -I../../../include/uapi \
+ -I$(OUTPUT)/../usr/include -D__TARGET_ARCH_$(SRCARCH)
-CLANG_FLAGS = -I. -I./include/uapi -I../../../include/uapi \
- $(CLANG_SYS_INCLUDES) \
- -Wno-compare-distinct-pointer-types
+CLANG_CFLAGS = $(CLANG_SYS_INCLUDES) \
+ -Wno-compare-distinct-pointer-types
-$(OUTPUT)/test_l4lb_noinline.o: CLANG_FLAGS += -fno-inline
-$(OUTPUT)/test_xdp_noinline.o: CLANG_FLAGS += -fno-inline
+$(OUTPUT)/test_l4lb_noinline.o: BPF_CFLAGS += -fno-inline
+$(OUTPUT)/test_xdp_noinline.o: BPF_CFLAGS += -fno-inline
$(OUTPUT)/test_queue_map.o: test_queue_stack_map.h
$(OUTPUT)/test_stack_map.o: test_queue_stack_map.h
@@ -146,12 +171,12 @@ BTF_LLVM_PROBE := $(shell echo "int main() { return 0; }" | \
/bin/rm -f ./llvm_btf_verify.o)
ifneq ($(BTF_LLVM_PROBE),)
- CLANG_FLAGS += -g
+ BPF_CFLAGS += -g
else
ifneq ($(BTF_LLC_PROBE),)
ifneq ($(BTF_PAHOLE_PROBE),)
ifneq ($(BTF_OBJCOPY_PROBE),)
- CLANG_FLAGS += -g
+ BPF_CFLAGS += -g
LLC_FLAGS += -mattr=dwarfris
DWARF2BTF = y
endif
@@ -160,6 +185,7 @@ endif
endif
TEST_PROGS_CFLAGS := -I. -I$(OUTPUT)
+TEST_MAPS_CFLAGS := -I. -I$(OUTPUT)
TEST_VERIFIER_CFLAGS := -I. -I$(OUTPUT) -Iverifier
ifneq ($(SUBREG_CODEGEN),)
@@ -168,24 +194,24 @@ TEST_CUSTOM_PROGS += $(ALU32_BUILD_DIR)/test_progs_32
$(ALU32_BUILD_DIR):
mkdir -p $@
-$(ALU32_BUILD_DIR)/urandom_read: $(OUTPUT)/urandom_read
+$(ALU32_BUILD_DIR)/urandom_read: $(OUTPUT)/urandom_read | $(ALU32_BUILD_DIR)
cp $< $@
$(ALU32_BUILD_DIR)/test_progs_32: test_progs.c $(OUTPUT)/libbpf.a\
- $(ALU32_BUILD_DIR) \
- $(ALU32_BUILD_DIR)/urandom_read
+ $(ALU32_BUILD_DIR)/urandom_read \
+ | $(ALU32_BUILD_DIR)
$(CC) $(TEST_PROGS_CFLAGS) $(CFLAGS) \
-o $(ALU32_BUILD_DIR)/test_progs_32 \
- test_progs.c trace_helpers.c prog_tests/*.c \
+ test_progs.c test_stub.c cgroup_helpers.c trace_helpers.c prog_tests/*.c \
$(OUTPUT)/libbpf.a $(LDLIBS)
$(ALU32_BUILD_DIR)/test_progs_32: $(PROG_TESTS_H)
$(ALU32_BUILD_DIR)/test_progs_32: prog_tests/*.c
-$(ALU32_BUILD_DIR)/%.o: progs/%.c $(ALU32_BUILD_DIR) \
- $(ALU32_BUILD_DIR)/test_progs_32
- $(CLANG) $(CLANG_FLAGS) \
- -O2 -target bpf -emit-llvm -c $< -o - | \
+$(ALU32_BUILD_DIR)/%.o: progs/%.c $(ALU32_BUILD_DIR)/test_progs_32 \
+ | $(ALU32_BUILD_DIR)
+ ($(CLANG) $(BPF_CFLAGS) $(CLANG_CFLAGS) -O2 -target bpf -emit-llvm \
+ -c $< -o - || echo "clang failed") | \
$(LLC) -march=bpf -mattr=+alu32 -mcpu=$(CPU) $(LLC_FLAGS) \
-filetype=obj -o $@
ifeq ($(DWARF2BTF),y)
@@ -193,56 +219,75 @@ ifeq ($(DWARF2BTF),y)
endif
endif
+ifneq ($(BPF_GCC),)
+GCC_SYS_INCLUDES = $(call get_sys_includes,gcc)
+IS_LITTLE_ENDIAN = $(shell $(CC) -dM -E - </dev/null | \
+ grep 'define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__')
+ifeq ($(IS_LITTLE_ENDIAN),)
+MENDIAN=-mbig-endian
+else
+MENDIAN=-mlittle-endian
+endif
+BPF_GCC_CFLAGS = $(GCC_SYS_INCLUDES) $(MENDIAN)
+BPF_GCC_BUILD_DIR = $(OUTPUT)/bpf_gcc
+TEST_CUSTOM_PROGS += $(BPF_GCC_BUILD_DIR)/test_progs_bpf_gcc
+$(BPF_GCC_BUILD_DIR):
+ mkdir -p $@
+
+$(BPF_GCC_BUILD_DIR)/urandom_read: $(OUTPUT)/urandom_read | $(BPF_GCC_BUILD_DIR)
+ cp $< $@
+
+$(BPF_GCC_BUILD_DIR)/test_progs_bpf_gcc: $(OUTPUT)/test_progs \
+ | $(BPF_GCC_BUILD_DIR)
+ cp $< $@
+
+$(BPF_GCC_BUILD_DIR)/%.o: progs/%.c $(BPF_GCC_BUILD_DIR)/test_progs_bpf_gcc \
+ | $(BPF_GCC_BUILD_DIR)
+ $(BPF_GCC) $(BPF_CFLAGS) $(BPF_GCC_CFLAGS) -O2 -c $< -o $@
+endif
+
# Have one program compiled without "-target bpf" to test whether libbpf loads
# it successfully
$(OUTPUT)/test_xdp.o: progs/test_xdp.c
- $(CLANG) $(CLANG_FLAGS) \
- -O2 -emit-llvm -c $< -o - | \
+ ($(CLANG) $(BPF_CFLAGS) $(CLANG_CFLAGS) -O2 -emit-llvm -c $< -o - || \
+ echo "clang failed") | \
$(LLC) -march=bpf -mcpu=$(CPU) $(LLC_FLAGS) -filetype=obj -o $@
ifeq ($(DWARF2BTF),y)
$(BTF_PAHOLE) -J $@
endif
$(OUTPUT)/%.o: progs/%.c
- $(CLANG) $(CLANG_FLAGS) \
- -O2 -target bpf -emit-llvm -c $< -o - | \
+ ($(CLANG) $(BPF_CFLAGS) $(CLANG_CFLAGS) -O2 -target bpf -emit-llvm \
+ -c $< -o - || echo "clang failed") | \
$(LLC) -march=bpf -mcpu=$(CPU) $(LLC_FLAGS) -filetype=obj -o $@
ifeq ($(DWARF2BTF),y)
$(BTF_PAHOLE) -J $@
endif
-PROG_TESTS_H := $(OUTPUT)/prog_tests/tests.h
-test_progs.c: $(PROG_TESTS_H)
-$(OUTPUT)/test_progs: CFLAGS += $(TEST_PROGS_CFLAGS)
-$(OUTPUT)/test_progs: prog_tests/*.c
-
PROG_TESTS_DIR = $(OUTPUT)/prog_tests
$(PROG_TESTS_DIR):
mkdir -p $@
-
+PROG_TESTS_H := $(PROG_TESTS_DIR)/tests.h
PROG_TESTS_FILES := $(wildcard prog_tests/*.c)
-$(PROG_TESTS_H): $(PROG_TESTS_DIR) $(PROG_TESTS_FILES)
+test_progs.c: $(PROG_TESTS_H)
+$(OUTPUT)/test_progs: CFLAGS += $(TEST_PROGS_CFLAGS)
+$(OUTPUT)/test_progs: test_progs.c $(PROG_TESTS_FILES) | $(PROG_TESTS_H)
+$(PROG_TESTS_H): $(PROG_TESTS_FILES) | $(PROG_TESTS_DIR)
$(shell ( cd prog_tests/; \
echo '/* Generated header, do not edit */'; \
- echo '#ifdef DECLARE'; \
ls *.c 2> /dev/null | \
- sed -e 's@\([^\.]*\)\.c@extern void test_\1(void);@'; \
- echo '#endif'; \
- echo '#ifdef CALL'; \
- ls *.c 2> /dev/null | \
- sed -e 's@\([^\.]*\)\.c@test_\1();@'; \
- echo '#endif' \
+ sed -e 's@\([^\.]*\)\.c@DEFINE_TEST(\1)@'; \
) > $(PROG_TESTS_H))
-TEST_MAPS_CFLAGS := -I. -I$(OUTPUT)
MAP_TESTS_DIR = $(OUTPUT)/map_tests
$(MAP_TESTS_DIR):
mkdir -p $@
MAP_TESTS_H := $(MAP_TESTS_DIR)/tests.h
+MAP_TESTS_FILES := $(wildcard map_tests/*.c)
test_maps.c: $(MAP_TESTS_H)
$(OUTPUT)/test_maps: CFLAGS += $(TEST_MAPS_CFLAGS)
-MAP_TESTS_FILES := $(wildcard map_tests/*.c)
-$(MAP_TESTS_H): $(MAP_TESTS_DIR) $(MAP_TESTS_FILES)
+$(OUTPUT)/test_maps: test_maps.c $(MAP_TESTS_FILES) | $(MAP_TESTS_H)
+$(MAP_TESTS_H): $(MAP_TESTS_FILES) | $(MAP_TESTS_DIR)
$(shell ( cd map_tests/; \
echo '/* Generated header, do not edit */'; \
echo '#ifdef DECLARE'; \
@@ -255,16 +300,15 @@ $(MAP_TESTS_H): $(MAP_TESTS_DIR) $(MAP_TESTS_FILES)
echo '#endif' \
) > $(MAP_TESTS_H))
-VERIFIER_TESTS_H := $(OUTPUT)/verifier/tests.h
-test_verifier.c: $(VERIFIER_TESTS_H)
-$(OUTPUT)/test_verifier: CFLAGS += $(TEST_VERIFIER_CFLAGS)
-
VERIFIER_TESTS_DIR = $(OUTPUT)/verifier
$(VERIFIER_TESTS_DIR):
mkdir -p $@
-
+VERIFIER_TESTS_H := $(VERIFIER_TESTS_DIR)/tests.h
VERIFIER_TEST_FILES := $(wildcard verifier/*.c)
-$(OUTPUT)/verifier/tests.h: $(VERIFIER_TESTS_DIR) $(VERIFIER_TEST_FILES)
+test_verifier.c: $(VERIFIER_TESTS_H)
+$(OUTPUT)/test_verifier: CFLAGS += $(TEST_VERIFIER_CFLAGS)
+$(OUTPUT)/test_verifier: test_verifier.c | $(VERIFIER_TEST_FILES) $(VERIFIER_TESTS_H)
+$(VERIFIER_TESTS_H): $(VERIFIER_TEST_FILES) | $(VERIFIER_TESTS_DIR)
$(shell ( cd verifier/; \
echo '/* Generated header, do not edit */'; \
echo '#ifdef FILL_ARRAY'; \
@@ -273,5 +317,6 @@ $(OUTPUT)/verifier/tests.h: $(VERIFIER_TESTS_DIR) $(VERIFIER_TEST_FILES)
echo '#endif' \
) > $(VERIFIER_TESTS_H))
-EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(ALU32_BUILD_DIR) \
- $(VERIFIER_TESTS_H) $(PROG_TESTS_H) $(MAP_TESTS_H)
+EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(ALU32_BUILD_DIR) $(BPF_GCC_BUILD_DIR) \
+ $(VERIFIER_TESTS_H) $(PROG_TESTS_H) $(MAP_TESTS_H) \
+ feature
diff --git a/tools/testing/selftests/bpf/bpf_endian.h b/tools/testing/selftests/bpf/bpf_endian.h
index b25595ea4a78..fbe28008450f 100644
--- a/tools/testing/selftests/bpf/bpf_endian.h
+++ b/tools/testing/selftests/bpf/bpf_endian.h
@@ -1,7 +1,8 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#ifndef __BPF_ENDIAN__
#define __BPF_ENDIAN__
+#include <linux/stddef.h>
#include <linux/swab.h>
/* LLVM's BPF target selects the endianness of the CPU
@@ -28,6 +29,10 @@
# define __bpf_htonl(x) __builtin_bswap32(x)
# define __bpf_constant_ntohl(x) ___constant_swab32(x)
# define __bpf_constant_htonl(x) ___constant_swab32(x)
+# define __bpf_be64_to_cpu(x) __builtin_bswap64(x)
+# define __bpf_cpu_to_be64(x) __builtin_bswap64(x)
+# define __bpf_constant_be64_to_cpu(x) ___constant_swab64(x)
+# define __bpf_constant_cpu_to_be64(x) ___constant_swab64(x)
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
# define __bpf_ntohs(x) (x)
# define __bpf_htons(x) (x)
@@ -37,6 +42,10 @@
# define __bpf_htonl(x) (x)
# define __bpf_constant_ntohl(x) (x)
# define __bpf_constant_htonl(x) (x)
+# define __bpf_be64_to_cpu(x) (x)
+# define __bpf_cpu_to_be64(x) (x)
+# define __bpf_constant_be64_to_cpu(x) (x)
+# define __bpf_constant_cpu_to_be64(x) (x)
#else
# error "Fix your compiler's __BYTE_ORDER__?!"
#endif
@@ -53,5 +62,11 @@
#define bpf_ntohl(x) \
(__builtin_constant_p(x) ? \
__bpf_constant_ntohl(x) : __bpf_ntohl(x))
+#define bpf_cpu_to_be64(x) \
+ (__builtin_constant_p(x) ? \
+ __bpf_constant_cpu_to_be64(x) : __bpf_cpu_to_be64(x))
+#define bpf_be64_to_cpu(x) \
+ (__builtin_constant_p(x) ? \
+ __bpf_constant_be64_to_cpu(x) : __bpf_be64_to_cpu(x))
#endif /* __BPF_ENDIAN__ */
diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h
index 6e80b66d7fb1..54a50699bbfd 100644
--- a/tools/testing/selftests/bpf/bpf_helpers.h
+++ b/tools/testing/selftests/bpf/bpf_helpers.h
@@ -1,6 +1,19 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __BPF_HELPERS_H
-#define __BPF_HELPERS_H
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+#ifndef __BPF_HELPERS__
+#define __BPF_HELPERS__
+
+#define __uint(name, val) int (*name)[val]
+#define __type(name, val) val *name
+
+/* helper macro to print out debug messages */
+#define bpf_printk(fmt, ...) \
+({ \
+ char ____fmt[] = fmt; \
+ bpf_trace_printk(____fmt, sizeof(____fmt), \
+ ##__VA_ARGS__); \
+})
+
+#ifdef __clang__
/* helper macro to place programs, maps, license in
* different sections in elf_bpf file. Section names
@@ -23,7 +36,7 @@ static int (*bpf_map_pop_elem)(void *map, void *value) =
(void *) BPF_FUNC_map_pop_elem;
static int (*bpf_map_peek_elem)(void *map, void *value) =
(void *) BPF_FUNC_map_peek_elem;
-static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr) =
+static int (*bpf_probe_read)(void *dst, int size, const void *unsafe_ptr) =
(void *) BPF_FUNC_probe_read;
static unsigned long long (*bpf_ktime_get_ns)(void) =
(void *) BPF_FUNC_ktime_get_ns;
@@ -54,7 +67,7 @@ static int (*bpf_perf_event_output)(void *ctx, void *map,
(void *) BPF_FUNC_perf_event_output;
static int (*bpf_get_stackid)(void *ctx, void *map, int flags) =
(void *) BPF_FUNC_get_stackid;
-static int (*bpf_probe_write_user)(void *dst, void *src, int size) =
+static int (*bpf_probe_write_user)(void *dst, const void *src, int size) =
(void *) BPF_FUNC_probe_write_user;
static int (*bpf_current_task_under_cgroup)(void *map, int index) =
(void *) BPF_FUNC_current_task_under_cgroup;
@@ -216,6 +229,10 @@ static void *(*bpf_sk_storage_get)(void *map, struct bpf_sock *sk,
(void *) BPF_FUNC_sk_storage_get;
static int (*bpf_sk_storage_delete)(void *map, struct bpf_sock *sk) =
(void *)BPF_FUNC_sk_storage_delete;
+static int (*bpf_send_signal)(unsigned sig) = (void *)BPF_FUNC_send_signal;
+static long long (*bpf_tcp_gen_syncookie)(struct bpf_sock *sk, void *ip,
+ int ip_len, void *tcp, int tcp_len) =
+ (void *) BPF_FUNC_tcp_gen_syncookie;
/* llvm builtin functions that eBPF C program may use to
* emit BPF_LD_ABS and BPF_LD_IND instructions
@@ -241,6 +258,12 @@ struct bpf_map_def {
unsigned int numa_node;
};
+#else
+
+#include <bpf-helpers.h>
+
+#endif
+
#define BPF_ANNOTATE_KV_PAIR(name, type_key, type_val) \
struct ____btf_map_##name { \
type_key key; \
@@ -278,7 +301,7 @@ static int (*bpf_skb_change_type)(void *ctx, __u32 type) =
(void *) BPF_FUNC_skb_change_type;
static unsigned int (*bpf_get_hash_recalc)(void *ctx) =
(void *) BPF_FUNC_get_hash_recalc;
-static unsigned long long (*bpf_get_current_task)(void *ctx) =
+static unsigned long long (*bpf_get_current_task)(void) =
(void *) BPF_FUNC_get_current_task;
static int (*bpf_skb_change_tail)(void *ctx, __u32 len, __u64 flags) =
(void *) BPF_FUNC_skb_change_tail;
@@ -303,8 +326,8 @@ static int (*bpf_skb_adjust_room)(void *ctx, __s32 len_diff, __u32 mode,
#if defined(__TARGET_ARCH_x86)
#define bpf_target_x86
#define bpf_target_defined
-#elif defined(__TARGET_ARCH_s930x)
- #define bpf_target_s930x
+#elif defined(__TARGET_ARCH_s390)
+ #define bpf_target_s390
#define bpf_target_defined
#elif defined(__TARGET_ARCH_arm)
#define bpf_target_arm
@@ -329,8 +352,8 @@ static int (*bpf_skb_adjust_room)(void *ctx, __s32 len_diff, __u32 mode,
#ifndef bpf_target_defined
#if defined(__x86_64__)
#define bpf_target_x86
-#elif defined(__s390x__)
- #define bpf_target_s930x
+#elif defined(__s390__)
+ #define bpf_target_s390
#elif defined(__arm__)
#define bpf_target_arm
#elif defined(__aarch64__)
@@ -346,6 +369,7 @@ static int (*bpf_skb_adjust_room)(void *ctx, __s32 len_diff, __u32 mode,
#if defined(bpf_target_x86)
+#ifdef __KERNEL__
#define PT_REGS_PARM1(x) ((x)->di)
#define PT_REGS_PARM2(x) ((x)->si)
#define PT_REGS_PARM3(x) ((x)->dx)
@@ -356,19 +380,49 @@ static int (*bpf_skb_adjust_room)(void *ctx, __s32 len_diff, __u32 mode,
#define PT_REGS_RC(x) ((x)->ax)
#define PT_REGS_SP(x) ((x)->sp)
#define PT_REGS_IP(x) ((x)->ip)
+#else
+#ifdef __i386__
+/* i386 kernel is built with -mregparm=3 */
+#define PT_REGS_PARM1(x) ((x)->eax)
+#define PT_REGS_PARM2(x) ((x)->edx)
+#define PT_REGS_PARM3(x) ((x)->ecx)
+#define PT_REGS_PARM4(x) 0
+#define PT_REGS_PARM5(x) 0
+#define PT_REGS_RET(x) ((x)->esp)
+#define PT_REGS_FP(x) ((x)->ebp)
+#define PT_REGS_RC(x) ((x)->eax)
+#define PT_REGS_SP(x) ((x)->esp)
+#define PT_REGS_IP(x) ((x)->eip)
+#else
+#define PT_REGS_PARM1(x) ((x)->rdi)
+#define PT_REGS_PARM2(x) ((x)->rsi)
+#define PT_REGS_PARM3(x) ((x)->rdx)
+#define PT_REGS_PARM4(x) ((x)->rcx)
+#define PT_REGS_PARM5(x) ((x)->r8)
+#define PT_REGS_RET(x) ((x)->rsp)
+#define PT_REGS_FP(x) ((x)->rbp)
+#define PT_REGS_RC(x) ((x)->rax)
+#define PT_REGS_SP(x) ((x)->rsp)
+#define PT_REGS_IP(x) ((x)->rip)
+#endif
+#endif
-#elif defined(bpf_target_s390x)
+#elif defined(bpf_target_s390)
-#define PT_REGS_PARM1(x) ((x)->gprs[2])
-#define PT_REGS_PARM2(x) ((x)->gprs[3])
-#define PT_REGS_PARM3(x) ((x)->gprs[4])
-#define PT_REGS_PARM4(x) ((x)->gprs[5])
-#define PT_REGS_PARM5(x) ((x)->gprs[6])
-#define PT_REGS_RET(x) ((x)->gprs[14])
-#define PT_REGS_FP(x) ((x)->gprs[11]) /* Works only with CONFIG_FRAME_POINTER */
-#define PT_REGS_RC(x) ((x)->gprs[2])
-#define PT_REGS_SP(x) ((x)->gprs[15])
-#define PT_REGS_IP(x) ((x)->psw.addr)
+/* s390 provides user_pt_regs instead of struct pt_regs to userspace */
+struct pt_regs;
+#define PT_REGS_S390 const volatile user_pt_regs
+#define PT_REGS_PARM1(x) (((PT_REGS_S390 *)(x))->gprs[2])
+#define PT_REGS_PARM2(x) (((PT_REGS_S390 *)(x))->gprs[3])
+#define PT_REGS_PARM3(x) (((PT_REGS_S390 *)(x))->gprs[4])
+#define PT_REGS_PARM4(x) (((PT_REGS_S390 *)(x))->gprs[5])
+#define PT_REGS_PARM5(x) (((PT_REGS_S390 *)(x))->gprs[6])
+#define PT_REGS_RET(x) (((PT_REGS_S390 *)(x))->gprs[14])
+/* Works only with CONFIG_FRAME_POINTER */
+#define PT_REGS_FP(x) (((PT_REGS_S390 *)(x))->gprs[11])
+#define PT_REGS_RC(x) (((PT_REGS_S390 *)(x))->gprs[2])
+#define PT_REGS_SP(x) (((PT_REGS_S390 *)(x))->gprs[15])
+#define PT_REGS_IP(x) (((PT_REGS_S390 *)(x))->psw.addr)
#elif defined(bpf_target_arm)
@@ -385,16 +439,20 @@ static int (*bpf_skb_adjust_room)(void *ctx, __s32 len_diff, __u32 mode,
#elif defined(bpf_target_arm64)
-#define PT_REGS_PARM1(x) ((x)->regs[0])
-#define PT_REGS_PARM2(x) ((x)->regs[1])
-#define PT_REGS_PARM3(x) ((x)->regs[2])
-#define PT_REGS_PARM4(x) ((x)->regs[3])
-#define PT_REGS_PARM5(x) ((x)->regs[4])
-#define PT_REGS_RET(x) ((x)->regs[30])
-#define PT_REGS_FP(x) ((x)->regs[29]) /* Works only with CONFIG_FRAME_POINTER */
-#define PT_REGS_RC(x) ((x)->regs[0])
-#define PT_REGS_SP(x) ((x)->sp)
-#define PT_REGS_IP(x) ((x)->pc)
+/* arm64 provides struct user_pt_regs instead of struct pt_regs to userspace */
+struct pt_regs;
+#define PT_REGS_ARM64 const volatile struct user_pt_regs
+#define PT_REGS_PARM1(x) (((PT_REGS_ARM64 *)(x))->regs[0])
+#define PT_REGS_PARM2(x) (((PT_REGS_ARM64 *)(x))->regs[1])
+#define PT_REGS_PARM3(x) (((PT_REGS_ARM64 *)(x))->regs[2])
+#define PT_REGS_PARM4(x) (((PT_REGS_ARM64 *)(x))->regs[3])
+#define PT_REGS_PARM5(x) (((PT_REGS_ARM64 *)(x))->regs[4])
+#define PT_REGS_RET(x) (((PT_REGS_ARM64 *)(x))->regs[30])
+/* Works only with CONFIG_FRAME_POINTER */
+#define PT_REGS_FP(x) (((PT_REGS_ARM64 *)(x))->regs[29])
+#define PT_REGS_RC(x) (((PT_REGS_ARM64 *)(x))->regs[0])
+#define PT_REGS_SP(x) (((PT_REGS_ARM64 *)(x))->sp)
+#define PT_REGS_IP(x) (((PT_REGS_ARM64 *)(x))->pc)
#elif defined(bpf_target_mips)
@@ -440,10 +498,10 @@ static int (*bpf_skb_adjust_room)(void *ctx, __s32 len_diff, __u32 mode,
#endif
-#ifdef bpf_target_powerpc
+#if defined(bpf_target_powerpc)
#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = (ctx)->link; })
#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP
-#elif bpf_target_sparc
+#elif defined(bpf_target_sparc)
#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = PT_REGS_RET(ctx); })
#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP
#else
@@ -454,4 +512,24 @@ static int (*bpf_skb_adjust_room)(void *ctx, __s32 len_diff, __u32 mode,
(void *)(PT_REGS_FP(ctx) + sizeof(ip))); })
#endif
+/*
+ * BPF_CORE_READ abstracts away bpf_probe_read() call and captures offset
+ * relocation for source address using __builtin_preserve_access_index()
+ * built-in, provided by Clang.
+ *
+ * __builtin_preserve_access_index() takes as an argument an expression of
+ * taking an address of a field within struct/union. It makes compiler emit
+ * a relocation, which records BTF type ID describing root struct/union and an
+ * accessor string which describes exact embedded field that was used to take
+ * an address. See detailed description of this relocation format and
+ * semantics in comments to struct bpf_offset_reloc in libbpf_internal.h.
+ *
+ * This relocation allows libbpf to adjust BPF instruction to use correct
+ * actual field offset, based on target kernel BTF type that matches original
+ * (local) BTF, used to record relocation.
+ */
+#define BPF_CORE_READ(dst, src) \
+ bpf_probe_read((dst), sizeof(*(src)), \
+ __builtin_preserve_access_index(src))
+
#endif
diff --git a/tools/testing/selftests/bpf/bpf_util.h b/tools/testing/selftests/bpf/bpf_util.h
index a29206ebbd13..ec219f84e041 100644
--- a/tools/testing/selftests/bpf/bpf_util.h
+++ b/tools/testing/selftests/bpf/bpf_util.h
@@ -6,44 +6,17 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <libbpf.h> /* libbpf_num_possible_cpus */
static inline unsigned int bpf_num_possible_cpus(void)
{
- static const char *fcpu = "/sys/devices/system/cpu/possible";
- unsigned int start, end, possible_cpus = 0;
- char buff[128];
- FILE *fp;
- int len, n, i, j = 0;
+ int possible_cpus = libbpf_num_possible_cpus();
- fp = fopen(fcpu, "r");
- if (!fp) {
- printf("Failed to open %s: '%s'!\n", fcpu, strerror(errno));
+ if (possible_cpus < 0) {
+ printf("Failed to get # of possible cpus: '%s'!\n",
+ strerror(-possible_cpus));
exit(1);
}
-
- if (!fgets(buff, sizeof(buff), fp)) {
- printf("Failed to read %s!\n", fcpu);
- exit(1);
- }
-
- len = strlen(buff);
- for (i = 0; i <= len; i++) {
- if (buff[i] == ',' || buff[i] == '\0') {
- buff[i] = '\0';
- n = sscanf(&buff[j], "%u-%u", &start, &end);
- if (n <= 0) {
- printf("Failed to retrieve # possible CPUs!\n");
- exit(1);
- } else if (n == 1) {
- end = start;
- }
- possible_cpus += end - start + 1;
- j = i + 1;
- }
- }
-
- fclose(fp);
-
return possible_cpus;
}
diff --git a/tools/testing/selftests/bpf/cgroup_helpers.c b/tools/testing/selftests/bpf/cgroup_helpers.c
index 6692a40a6979..e95c33e333a4 100644
--- a/tools/testing/selftests/bpf/cgroup_helpers.c
+++ b/tools/testing/selftests/bpf/cgroup_helpers.c
@@ -34,6 +34,60 @@
CGROUP_WORK_DIR, path)
/**
+ * enable_all_controllers() - Enable all available cgroup v2 controllers
+ *
+ * Enable all available cgroup v2 controllers in order to increase
+ * the code coverage.
+ *
+ * If successful, 0 is returned.
+ */
+int enable_all_controllers(char *cgroup_path)
+{
+ char path[PATH_MAX + 1];
+ char buf[PATH_MAX];
+ char *c, *c2;
+ int fd, cfd;
+ ssize_t len;
+
+ snprintf(path, sizeof(path), "%s/cgroup.controllers", cgroup_path);
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ log_err("Opening cgroup.controllers: %s", path);
+ return 1;
+ }
+
+ len = read(fd, buf, sizeof(buf) - 1);
+ if (len < 0) {
+ close(fd);
+ log_err("Reading cgroup.controllers: %s", path);
+ return 1;
+ }
+ buf[len] = 0;
+ close(fd);
+
+ /* No controllers available? We're probably on cgroup v1. */
+ if (len == 0)
+ return 0;
+
+ snprintf(path, sizeof(path), "%s/cgroup.subtree_control", cgroup_path);
+ cfd = open(path, O_RDWR);
+ if (cfd < 0) {
+ log_err("Opening cgroup.subtree_control: %s", path);
+ return 1;
+ }
+
+ for (c = strtok_r(buf, " ", &c2); c; c = strtok_r(NULL, " ", &c2)) {
+ if (dprintf(cfd, "+%s\n", c) <= 0) {
+ log_err("Enabling controller %s: %s", c, path);
+ close(cfd);
+ return 1;
+ }
+ }
+ close(cfd);
+ return 0;
+}
+
+/**
* setup_cgroup_environment() - Setup the cgroup environment
*
* After calling this function, cleanup_cgroup_environment should be called
@@ -71,6 +125,9 @@ int setup_cgroup_environment(void)
return 1;
}
+ if (enable_all_controllers(cgroup_workdir))
+ return 1;
+
return 0;
}
diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config
index f7a0744db31e..5dc109f4c097 100644
--- a/tools/testing/selftests/bpf/config
+++ b/tools/testing/selftests/bpf/config
@@ -34,3 +34,4 @@ CONFIG_NET_MPLS_GSO=m
CONFIG_MPLS_ROUTING=m
CONFIG_MPLS_IPTUNNEL=m
CONFIG_IPV6_SIT=m
+CONFIG_BPF_JIT=y
diff --git a/tools/testing/selftests/bpf/map_tests/.gitignore b/tools/testing/selftests/bpf/map_tests/.gitignore
new file mode 100644
index 000000000000..45984a364647
--- /dev/null
+++ b/tools/testing/selftests/bpf/map_tests/.gitignore
@@ -0,0 +1 @@
+tests.h
diff --git a/tools/testing/selftests/bpf/prog_tests/attach_probe.c b/tools/testing/selftests/bpf/prog_tests/attach_probe.c
new file mode 100644
index 000000000000..5ecc267d98b0
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/attach_probe.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+
+ssize_t get_base_addr() {
+ size_t start;
+ char buf[256];
+ FILE *f;
+
+ f = fopen("/proc/self/maps", "r");
+ if (!f)
+ return -errno;
+
+ while (fscanf(f, "%zx-%*x %s %*s\n", &start, buf) == 2) {
+ if (strcmp(buf, "r-xp") == 0) {
+ fclose(f);
+ return start;
+ }
+ }
+
+ fclose(f);
+ return -EINVAL;
+}
+
+void test_attach_probe(void)
+{
+ const char *kprobe_name = "kprobe/sys_nanosleep";
+ const char *kretprobe_name = "kretprobe/sys_nanosleep";
+ const char *uprobe_name = "uprobe/trigger_func";
+ const char *uretprobe_name = "uretprobe/trigger_func";
+ const int kprobe_idx = 0, kretprobe_idx = 1;
+ const int uprobe_idx = 2, uretprobe_idx = 3;
+ const char *file = "./test_attach_probe.o";
+ struct bpf_program *kprobe_prog, *kretprobe_prog;
+ struct bpf_program *uprobe_prog, *uretprobe_prog;
+ struct bpf_object *obj;
+ int err, prog_fd, duration = 0, res;
+ struct bpf_link *kprobe_link = NULL;
+ struct bpf_link *kretprobe_link = NULL;
+ struct bpf_link *uprobe_link = NULL;
+ struct bpf_link *uretprobe_link = NULL;
+ int results_map_fd;
+ size_t uprobe_offset;
+ ssize_t base_addr;
+
+ base_addr = get_base_addr();
+ if (CHECK(base_addr < 0, "get_base_addr",
+ "failed to find base addr: %zd", base_addr))
+ return;
+ uprobe_offset = (size_t)&get_base_addr - base_addr;
+
+ /* load programs */
+ err = bpf_prog_load(file, BPF_PROG_TYPE_KPROBE, &obj, &prog_fd);
+ if (CHECK(err, "obj_load", "err %d errno %d\n", err, errno))
+ return;
+
+ kprobe_prog = bpf_object__find_program_by_title(obj, kprobe_name);
+ if (CHECK(!kprobe_prog, "find_probe",
+ "prog '%s' not found\n", kprobe_name))
+ goto cleanup;
+ kretprobe_prog = bpf_object__find_program_by_title(obj, kretprobe_name);
+ if (CHECK(!kretprobe_prog, "find_probe",
+ "prog '%s' not found\n", kretprobe_name))
+ goto cleanup;
+ uprobe_prog = bpf_object__find_program_by_title(obj, uprobe_name);
+ if (CHECK(!uprobe_prog, "find_probe",
+ "prog '%s' not found\n", uprobe_name))
+ goto cleanup;
+ uretprobe_prog = bpf_object__find_program_by_title(obj, uretprobe_name);
+ if (CHECK(!uretprobe_prog, "find_probe",
+ "prog '%s' not found\n", uretprobe_name))
+ goto cleanup;
+
+ /* load maps */
+ results_map_fd = bpf_find_map(__func__, obj, "results_map");
+ if (CHECK(results_map_fd < 0, "find_results_map",
+ "err %d\n", results_map_fd))
+ goto cleanup;
+
+ kprobe_link = bpf_program__attach_kprobe(kprobe_prog,
+ false /* retprobe */,
+ SYS_NANOSLEEP_KPROBE_NAME);
+ if (CHECK(IS_ERR(kprobe_link), "attach_kprobe",
+ "err %ld\n", PTR_ERR(kprobe_link))) {
+ kprobe_link = NULL;
+ goto cleanup;
+ }
+ kretprobe_link = bpf_program__attach_kprobe(kretprobe_prog,
+ true /* retprobe */,
+ SYS_NANOSLEEP_KPROBE_NAME);
+ if (CHECK(IS_ERR(kretprobe_link), "attach_kretprobe",
+ "err %ld\n", PTR_ERR(kretprobe_link))) {
+ kretprobe_link = NULL;
+ goto cleanup;
+ }
+ uprobe_link = bpf_program__attach_uprobe(uprobe_prog,
+ false /* retprobe */,
+ 0 /* self pid */,
+ "/proc/self/exe",
+ uprobe_offset);
+ if (CHECK(IS_ERR(uprobe_link), "attach_uprobe",
+ "err %ld\n", PTR_ERR(uprobe_link))) {
+ uprobe_link = NULL;
+ goto cleanup;
+ }
+ uretprobe_link = bpf_program__attach_uprobe(uretprobe_prog,
+ true /* retprobe */,
+ -1 /* any pid */,
+ "/proc/self/exe",
+ uprobe_offset);
+ if (CHECK(IS_ERR(uretprobe_link), "attach_uretprobe",
+ "err %ld\n", PTR_ERR(uretprobe_link))) {
+ uretprobe_link = NULL;
+ goto cleanup;
+ }
+
+ /* trigger & validate kprobe && kretprobe */
+ usleep(1);
+
+ err = bpf_map_lookup_elem(results_map_fd, &kprobe_idx, &res);
+ if (CHECK(err, "get_kprobe_res",
+ "failed to get kprobe res: %d\n", err))
+ goto cleanup;
+ if (CHECK(res != kprobe_idx + 1, "check_kprobe_res",
+ "wrong kprobe res: %d\n", res))
+ goto cleanup;
+
+ err = bpf_map_lookup_elem(results_map_fd, &kretprobe_idx, &res);
+ if (CHECK(err, "get_kretprobe_res",
+ "failed to get kretprobe res: %d\n", err))
+ goto cleanup;
+ if (CHECK(res != kretprobe_idx + 1, "check_kretprobe_res",
+ "wrong kretprobe res: %d\n", res))
+ goto cleanup;
+
+ /* trigger & validate uprobe & uretprobe */
+ get_base_addr();
+
+ err = bpf_map_lookup_elem(results_map_fd, &uprobe_idx, &res);
+ if (CHECK(err, "get_uprobe_res",
+ "failed to get uprobe res: %d\n", err))
+ goto cleanup;
+ if (CHECK(res != uprobe_idx + 1, "check_uprobe_res",
+ "wrong uprobe res: %d\n", res))
+ goto cleanup;
+
+ err = bpf_map_lookup_elem(results_map_fd, &uretprobe_idx, &res);
+ if (CHECK(err, "get_uretprobe_res",
+ "failed to get uretprobe res: %d\n", err))
+ goto cleanup;
+ if (CHECK(res != uretprobe_idx + 1, "check_uretprobe_res",
+ "wrong uretprobe res: %d\n", res))
+ goto cleanup;
+
+cleanup:
+ bpf_link__destroy(kprobe_link);
+ bpf_link__destroy(kretprobe_link);
+ bpf_link__destroy(uprobe_link);
+ bpf_link__destroy(uretprobe_link);
+ bpf_object__close(obj);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_obj_id.c b/tools/testing/selftests/bpf/prog_tests/bpf_obj_id.c
index cb827383db4d..f10029821e16 100644
--- a/tools/testing/selftests/bpf/prog_tests/bpf_obj_id.c
+++ b/tools/testing/selftests/bpf/prog_tests/bpf_obj_id.c
@@ -48,16 +48,17 @@ void test_bpf_obj_id(void)
/* test_obj_id.o is a dumb prog. It should never fail
* to load.
*/
- if (err)
- error_cnt++;
- assert(!err);
+ if (CHECK_FAIL(err))
+ continue;
/* Insert a magic value to the map */
map_fds[i] = bpf_find_map(__func__, objs[i], "test_map_id");
- assert(map_fds[i] >= 0);
+ if (CHECK_FAIL(map_fds[i] < 0))
+ goto done;
err = bpf_map_update_elem(map_fds[i], &array_key,
&array_magic_value, 0);
- assert(!err);
+ if (CHECK_FAIL(err))
+ goto done;
/* Check getting map info */
info_len = sizeof(struct bpf_map_info) * 2;
@@ -96,9 +97,11 @@ void test_bpf_obj_id(void)
prog_infos[i].map_ids = ptr_to_u64(map_ids + i);
prog_infos[i].nr_map_ids = 2;
err = clock_gettime(CLOCK_REALTIME, &real_time_ts);
- assert(!err);
+ if (CHECK_FAIL(err))
+ goto done;
err = clock_gettime(CLOCK_BOOTTIME, &boot_time_ts);
- assert(!err);
+ if (CHECK_FAIL(err))
+ goto done;
err = bpf_obj_get_info_by_fd(prog_fds[i], &prog_infos[i],
&info_len);
load_time = (real_time_ts.tv_sec - boot_time_ts.tv_sec)
@@ -106,8 +109,8 @@ void test_bpf_obj_id(void)
if (CHECK(err ||
prog_infos[i].type != BPF_PROG_TYPE_SOCKET_FILTER ||
info_len != sizeof(struct bpf_prog_info) ||
- (jit_enabled && !prog_infos[i].jited_prog_len) ||
- (jit_enabled &&
+ (env.jit_enabled && !prog_infos[i].jited_prog_len) ||
+ (env.jit_enabled &&
!memcmp(jited_insns, zeros, sizeof(zeros))) ||
!prog_infos[i].xlated_prog_len ||
!memcmp(xlated_insns, zeros, sizeof(zeros)) ||
@@ -121,7 +124,7 @@ void test_bpf_obj_id(void)
err, errno, i,
prog_infos[i].type, BPF_PROG_TYPE_SOCKET_FILTER,
info_len, sizeof(struct bpf_prog_info),
- jit_enabled,
+ env.jit_enabled,
prog_infos[i].jited_prog_len,
prog_infos[i].xlated_prog_len,
!!memcmp(jited_insns, zeros, sizeof(zeros)),
@@ -224,7 +227,8 @@ void test_bpf_obj_id(void)
nr_id_found++;
err = bpf_map_lookup_elem(map_fd, &array_key, &array_value);
- assert(!err);
+ if (CHECK_FAIL(err))
+ goto done;
err = bpf_obj_get_info_by_fd(map_fd, &map_info, &info_len);
CHECK(err || info_len != sizeof(struct bpf_map_info) ||
diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_verif_scale.c b/tools/testing/selftests/bpf/prog_tests/bpf_verif_scale.c
index b74e2f6e96d0..1c01ee2600a9 100644
--- a/tools/testing/selftests/bpf/prog_tests/bpf_verif_scale.c
+++ b/tools/testing/selftests/bpf/prog_tests/bpf_verif_scale.c
@@ -4,15 +4,18 @@
static int libbpf_debug_print(enum libbpf_print_level level,
const char *format, va_list args)
{
- if (level != LIBBPF_DEBUG)
+ if (level != LIBBPF_DEBUG) {
+ vprintf(format, args);
return 0;
+ }
if (!strstr(format, "verifier log"))
return 0;
- return vfprintf(stderr, "%s", args);
+ vprintf("%s", args);
+ return 0;
}
-static int check_load(const char *file)
+static int check_load(const char *file, enum bpf_prog_type type)
{
struct bpf_prog_load_attr attr;
struct bpf_object *obj = NULL;
@@ -20,30 +23,89 @@ static int check_load(const char *file)
memset(&attr, 0, sizeof(struct bpf_prog_load_attr));
attr.file = file;
- attr.prog_type = BPF_PROG_TYPE_SCHED_CLS;
+ attr.prog_type = type;
attr.log_level = 4;
+ attr.prog_flags = BPF_F_TEST_RND_HI32;
err = bpf_prog_load_xattr(&attr, &obj, &prog_fd);
bpf_object__close(obj);
- if (err)
- error_cnt++;
return err;
}
+struct scale_test_def {
+ const char *file;
+ enum bpf_prog_type attach_type;
+ bool fails;
+};
+
void test_bpf_verif_scale(void)
{
- const char *file1 = "./test_verif_scale1.o";
- const char *file2 = "./test_verif_scale2.o";
- const char *file3 = "./test_verif_scale3.o";
- int err;
-
- if (verifier_stats)
- libbpf_set_print(libbpf_debug_print);
-
- err = check_load(file1);
- err |= check_load(file2);
- err |= check_load(file3);
- if (!err)
- printf("test_verif_scale:OK\n");
- else
- printf("test_verif_scale:FAIL\n");
+ struct scale_test_def tests[] = {
+ { "loop3.o", BPF_PROG_TYPE_RAW_TRACEPOINT, true /* fails */ },
+
+ { "test_verif_scale1.o", BPF_PROG_TYPE_SCHED_CLS },
+ { "test_verif_scale2.o", BPF_PROG_TYPE_SCHED_CLS },
+ { "test_verif_scale3.o", BPF_PROG_TYPE_SCHED_CLS },
+
+ /* full unroll by llvm */
+ { "pyperf50.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
+ { "pyperf100.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
+ { "pyperf180.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
+
+ /* partial unroll. llvm will unroll loop ~150 times.
+ * C loop count -> 600.
+ * Asm loop count -> 4.
+ * 16k insns in loop body.
+ * Total of 5 such loops. Total program size ~82k insns.
+ */
+ { "pyperf600.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
+
+ /* no unroll at all.
+ * C loop count -> 600.
+ * ASM loop count -> 600.
+ * ~110 insns in loop body.
+ * Total of 5 such loops. Total program size ~1500 insns.
+ */
+ { "pyperf600_nounroll.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
+
+ { "loop1.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
+ { "loop2.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
+ { "loop4.o", BPF_PROG_TYPE_SCHED_CLS },
+ { "loop5.o", BPF_PROG_TYPE_SCHED_CLS },
+
+ /* partial unroll. 19k insn in a loop.
+ * Total program size 20.8k insn.
+ * ~350k processed_insns
+ */
+ { "strobemeta.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
+
+ /* no unroll, tiny loops */
+ { "strobemeta_nounroll1.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
+ { "strobemeta_nounroll2.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
+
+ { "test_sysctl_loop1.o", BPF_PROG_TYPE_CGROUP_SYSCTL },
+ { "test_sysctl_loop2.o", BPF_PROG_TYPE_CGROUP_SYSCTL },
+
+ { "test_xdp_loop.o", BPF_PROG_TYPE_XDP },
+ { "test_seg6_loop.o", BPF_PROG_TYPE_LWT_SEG6LOCAL },
+ };
+ libbpf_print_fn_t old_print_fn = NULL;
+ int err, i;
+
+ if (env.verifier_stats) {
+ test__force_log();
+ old_print_fn = libbpf_set_print(libbpf_debug_print);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ const struct scale_test_def *test = &tests[i];
+
+ if (!test__start_subtest(test->file))
+ continue;
+
+ err = check_load(test->file, test->attach_type);
+ CHECK_FAIL(err && !test->fails);
+ }
+
+ if (env.verifier_stats)
+ libbpf_set_print(old_print_fn);
}
diff --git a/tools/testing/selftests/bpf/prog_tests/core_reloc.c b/tools/testing/selftests/bpf/prog_tests/core_reloc.c
new file mode 100644
index 000000000000..f3863f976a48
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/core_reloc.c
@@ -0,0 +1,385 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include "progs/core_reloc_types.h"
+
+#define STRUCT_TO_CHAR_PTR(struct_name) (const char *)&(struct struct_name)
+
+#define FLAVORS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
+ .a = 42, \
+ .b = 0xc001, \
+ .c = 0xbeef, \
+}
+
+#define FLAVORS_CASE_COMMON(name) \
+ .case_name = #name, \
+ .bpf_obj_file = "test_core_reloc_flavors.o", \
+ .btf_src_file = "btf__core_reloc_" #name ".o" \
+
+#define FLAVORS_CASE(name) { \
+ FLAVORS_CASE_COMMON(name), \
+ .input = FLAVORS_DATA(core_reloc_##name), \
+ .input_len = sizeof(struct core_reloc_##name), \
+ .output = FLAVORS_DATA(core_reloc_flavors), \
+ .output_len = sizeof(struct core_reloc_flavors), \
+}
+
+#define FLAVORS_ERR_CASE(name) { \
+ FLAVORS_CASE_COMMON(name), \
+ .fails = true, \
+}
+
+#define NESTING_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
+ .a = { .a = { .a = 42 } }, \
+ .b = { .b = { .b = 0xc001 } }, \
+}
+
+#define NESTING_CASE_COMMON(name) \
+ .case_name = #name, \
+ .bpf_obj_file = "test_core_reloc_nesting.o", \
+ .btf_src_file = "btf__core_reloc_" #name ".o"
+
+#define NESTING_CASE(name) { \
+ NESTING_CASE_COMMON(name), \
+ .input = NESTING_DATA(core_reloc_##name), \
+ .input_len = sizeof(struct core_reloc_##name), \
+ .output = NESTING_DATA(core_reloc_nesting), \
+ .output_len = sizeof(struct core_reloc_nesting) \
+}
+
+#define NESTING_ERR_CASE(name) { \
+ NESTING_CASE_COMMON(name), \
+ .fails = true, \
+}
+
+#define ARRAYS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
+ .a = { [2] = 1 }, \
+ .b = { [1] = { [2] = { [3] = 2 } } }, \
+ .c = { [1] = { .c = 3 } }, \
+ .d = { [0] = { [0] = { .d = 4 } } }, \
+}
+
+#define ARRAYS_CASE_COMMON(name) \
+ .case_name = #name, \
+ .bpf_obj_file = "test_core_reloc_arrays.o", \
+ .btf_src_file = "btf__core_reloc_" #name ".o"
+
+#define ARRAYS_CASE(name) { \
+ ARRAYS_CASE_COMMON(name), \
+ .input = ARRAYS_DATA(core_reloc_##name), \
+ .input_len = sizeof(struct core_reloc_##name), \
+ .output = STRUCT_TO_CHAR_PTR(core_reloc_arrays_output) { \
+ .a2 = 1, \
+ .b123 = 2, \
+ .c1c = 3, \
+ .d00d = 4, \
+ }, \
+ .output_len = sizeof(struct core_reloc_arrays_output) \
+}
+
+#define ARRAYS_ERR_CASE(name) { \
+ ARRAYS_CASE_COMMON(name), \
+ .fails = true, \
+}
+
+#define PRIMITIVES_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
+ .a = 1, \
+ .b = 2, \
+ .c = 3, \
+ .d = (void *)4, \
+ .f = (void *)5, \
+}
+
+#define PRIMITIVES_CASE_COMMON(name) \
+ .case_name = #name, \
+ .bpf_obj_file = "test_core_reloc_primitives.o", \
+ .btf_src_file = "btf__core_reloc_" #name ".o"
+
+#define PRIMITIVES_CASE(name) { \
+ PRIMITIVES_CASE_COMMON(name), \
+ .input = PRIMITIVES_DATA(core_reloc_##name), \
+ .input_len = sizeof(struct core_reloc_##name), \
+ .output = PRIMITIVES_DATA(core_reloc_primitives), \
+ .output_len = sizeof(struct core_reloc_primitives), \
+}
+
+#define PRIMITIVES_ERR_CASE(name) { \
+ PRIMITIVES_CASE_COMMON(name), \
+ .fails = true, \
+}
+
+#define MODS_CASE(name) { \
+ .case_name = #name, \
+ .bpf_obj_file = "test_core_reloc_mods.o", \
+ .btf_src_file = "btf__core_reloc_" #name ".o", \
+ .input = STRUCT_TO_CHAR_PTR(core_reloc_##name) { \
+ .a = 1, \
+ .b = 2, \
+ .c = (void *)3, \
+ .d = (void *)4, \
+ .e = { [2] = 5 }, \
+ .f = { [1] = 6 }, \
+ .g = { .x = 7 }, \
+ .h = { .y = 8 }, \
+ }, \
+ .input_len = sizeof(struct core_reloc_##name), \
+ .output = STRUCT_TO_CHAR_PTR(core_reloc_mods_output) { \
+ .a = 1, .b = 2, .c = 3, .d = 4, \
+ .e = 5, .f = 6, .g = 7, .h = 8, \
+ }, \
+ .output_len = sizeof(struct core_reloc_mods_output), \
+}
+
+#define PTR_AS_ARR_CASE(name) { \
+ .case_name = #name, \
+ .bpf_obj_file = "test_core_reloc_ptr_as_arr.o", \
+ .btf_src_file = "btf__core_reloc_" #name ".o", \
+ .input = (const char *)&(struct core_reloc_##name []){ \
+ { .a = 1 }, \
+ { .a = 2 }, \
+ { .a = 3 }, \
+ }, \
+ .input_len = 3 * sizeof(struct core_reloc_##name), \
+ .output = STRUCT_TO_CHAR_PTR(core_reloc_ptr_as_arr) { \
+ .a = 3, \
+ }, \
+ .output_len = sizeof(struct core_reloc_ptr_as_arr), \
+}
+
+#define INTS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
+ .u8_field = 1, \
+ .s8_field = 2, \
+ .u16_field = 3, \
+ .s16_field = 4, \
+ .u32_field = 5, \
+ .s32_field = 6, \
+ .u64_field = 7, \
+ .s64_field = 8, \
+}
+
+#define INTS_CASE_COMMON(name) \
+ .case_name = #name, \
+ .bpf_obj_file = "test_core_reloc_ints.o", \
+ .btf_src_file = "btf__core_reloc_" #name ".o"
+
+#define INTS_CASE(name) { \
+ INTS_CASE_COMMON(name), \
+ .input = INTS_DATA(core_reloc_##name), \
+ .input_len = sizeof(struct core_reloc_##name), \
+ .output = INTS_DATA(core_reloc_ints), \
+ .output_len = sizeof(struct core_reloc_ints), \
+}
+
+#define INTS_ERR_CASE(name) { \
+ INTS_CASE_COMMON(name), \
+ .fails = true, \
+}
+
+struct core_reloc_test_case {
+ const char *case_name;
+ const char *bpf_obj_file;
+ const char *btf_src_file;
+ const char *input;
+ int input_len;
+ const char *output;
+ int output_len;
+ bool fails;
+};
+
+static struct core_reloc_test_case test_cases[] = {
+ /* validate we can find kernel image and use its BTF for relocs */
+ {
+ .case_name = "kernel",
+ .bpf_obj_file = "test_core_reloc_kernel.o",
+ .btf_src_file = NULL, /* load from /lib/modules/$(uname -r) */
+ .input = "",
+ .input_len = 0,
+ .output = "\1", /* true */
+ .output_len = 1,
+ },
+
+ /* validate BPF program can use multiple flavors to match against
+ * single target BTF type
+ */
+ FLAVORS_CASE(flavors),
+
+ FLAVORS_ERR_CASE(flavors__err_wrong_name),
+
+ /* various struct/enum nesting and resolution scenarios */
+ NESTING_CASE(nesting),
+ NESTING_CASE(nesting___anon_embed),
+ NESTING_CASE(nesting___struct_union_mixup),
+ NESTING_CASE(nesting___extra_nesting),
+ NESTING_CASE(nesting___dup_compat_types),
+
+ NESTING_ERR_CASE(nesting___err_missing_field),
+ NESTING_ERR_CASE(nesting___err_array_field),
+ NESTING_ERR_CASE(nesting___err_missing_container),
+ NESTING_ERR_CASE(nesting___err_nonstruct_container),
+ NESTING_ERR_CASE(nesting___err_array_container),
+ NESTING_ERR_CASE(nesting___err_dup_incompat_types),
+ NESTING_ERR_CASE(nesting___err_partial_match_dups),
+ NESTING_ERR_CASE(nesting___err_too_deep),
+
+ /* various array access relocation scenarios */
+ ARRAYS_CASE(arrays),
+ ARRAYS_CASE(arrays___diff_arr_dim),
+ ARRAYS_CASE(arrays___diff_arr_val_sz),
+
+ ARRAYS_ERR_CASE(arrays___err_too_small),
+ ARRAYS_ERR_CASE(arrays___err_too_shallow),
+ ARRAYS_ERR_CASE(arrays___err_non_array),
+ ARRAYS_ERR_CASE(arrays___err_wrong_val_type1),
+ ARRAYS_ERR_CASE(arrays___err_wrong_val_type2),
+
+ /* enum/ptr/int handling scenarios */
+ PRIMITIVES_CASE(primitives),
+ PRIMITIVES_CASE(primitives___diff_enum_def),
+ PRIMITIVES_CASE(primitives___diff_func_proto),
+ PRIMITIVES_CASE(primitives___diff_ptr_type),
+
+ PRIMITIVES_ERR_CASE(primitives___err_non_enum),
+ PRIMITIVES_ERR_CASE(primitives___err_non_int),
+ PRIMITIVES_ERR_CASE(primitives___err_non_ptr),
+
+ /* const/volatile/restrict and typedefs scenarios */
+ MODS_CASE(mods),
+ MODS_CASE(mods___mod_swap),
+ MODS_CASE(mods___typedefs),
+
+ /* handling "ptr is an array" semantics */
+ PTR_AS_ARR_CASE(ptr_as_arr),
+ PTR_AS_ARR_CASE(ptr_as_arr___diff_sz),
+
+ /* int signedness/sizing/bitfield handling */
+ INTS_CASE(ints),
+ INTS_CASE(ints___bool),
+ INTS_CASE(ints___reverse_sign),
+
+ INTS_ERR_CASE(ints___err_bitfield),
+ INTS_ERR_CASE(ints___err_wrong_sz_8),
+ INTS_ERR_CASE(ints___err_wrong_sz_16),
+ INTS_ERR_CASE(ints___err_wrong_sz_32),
+ INTS_ERR_CASE(ints___err_wrong_sz_64),
+
+ /* validate edge cases of capturing relocations */
+ {
+ .case_name = "misc",
+ .bpf_obj_file = "test_core_reloc_misc.o",
+ .btf_src_file = "btf__core_reloc_misc.o",
+ .input = (const char *)&(struct core_reloc_misc_extensible[]){
+ { .a = 1 },
+ { .a = 2 }, /* not read */
+ { .a = 3 },
+ },
+ .input_len = 4 * sizeof(int),
+ .output = STRUCT_TO_CHAR_PTR(core_reloc_misc_output) {
+ .a = 1,
+ .b = 1,
+ .c = 0, /* BUG in clang, should be 3 */
+ },
+ .output_len = sizeof(struct core_reloc_misc_output),
+ },
+};
+
+struct data {
+ char in[256];
+ char out[256];
+};
+
+void test_core_reloc(void)
+{
+ const char *probe_name = "raw_tracepoint/sys_enter";
+ struct bpf_object_load_attr load_attr = {};
+ struct core_reloc_test_case *test_case;
+ int err, duration = 0, i, equal;
+ struct bpf_link *link = NULL;
+ struct bpf_map *data_map;
+ struct bpf_program *prog;
+ struct bpf_object *obj;
+ const int zero = 0;
+ struct data data;
+
+ for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
+ test_case = &test_cases[i];
+
+ if (!test__start_subtest(test_case->case_name))
+ continue;
+
+ obj = bpf_object__open(test_case->bpf_obj_file);
+ if (CHECK(IS_ERR_OR_NULL(obj), "obj_open",
+ "failed to open '%s': %ld\n",
+ test_case->bpf_obj_file, PTR_ERR(obj)))
+ continue;
+
+ prog = bpf_object__find_program_by_title(obj, probe_name);
+ if (CHECK(!prog, "find_probe",
+ "prog '%s' not found\n", probe_name))
+ goto cleanup;
+ bpf_program__set_type(prog, BPF_PROG_TYPE_RAW_TRACEPOINT);
+
+ load_attr.obj = obj;
+ load_attr.log_level = 0;
+ load_attr.target_btf_path = test_case->btf_src_file;
+ err = bpf_object__load_xattr(&load_attr);
+ if (test_case->fails) {
+ CHECK(!err, "obj_load_fail",
+ "should fail to load prog '%s'\n", probe_name);
+ goto cleanup;
+ } else {
+ if (CHECK(err, "obj_load",
+ "failed to load prog '%s': %d\n",
+ probe_name, err))
+ goto cleanup;
+ }
+
+ link = bpf_program__attach_raw_tracepoint(prog, "sys_enter");
+ if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n",
+ PTR_ERR(link)))
+ goto cleanup;
+
+ data_map = bpf_object__find_map_by_name(obj, "test_cor.bss");
+ if (CHECK(!data_map, "find_data_map", "data map not found\n"))
+ goto cleanup;
+
+ memset(&data, 0, sizeof(data));
+ memcpy(data.in, test_case->input, test_case->input_len);
+
+ err = bpf_map_update_elem(bpf_map__fd(data_map),
+ &zero, &data, 0);
+ if (CHECK(err, "update_data_map",
+ "failed to update .data map: %d\n", err))
+ goto cleanup;
+
+ /* trigger test run */
+ usleep(1);
+
+ err = bpf_map_lookup_elem(bpf_map__fd(data_map), &zero, &data);
+ if (CHECK(err, "get_result",
+ "failed to get output data: %d\n", err))
+ goto cleanup;
+
+ equal = memcmp(data.out, test_case->output,
+ test_case->output_len) == 0;
+ if (CHECK(!equal, "check_result",
+ "input/output data don't match\n")) {
+ int j;
+
+ for (j = 0; j < test_case->input_len; j++) {
+ printf("input byte #%d: 0x%02hhx\n",
+ j, test_case->input[j]);
+ }
+ for (j = 0; j < test_case->output_len; j++) {
+ printf("output byte #%d: EXP 0x%02hhx GOT 0x%02hhx\n",
+ j, test_case->output[j], data.out[j]);
+ }
+ goto cleanup;
+ }
+
+cleanup:
+ if (!IS_ERR_OR_NULL(link)) {
+ bpf_link__destroy(link);
+ link = NULL;
+ }
+ bpf_object__close(obj);
+ }
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/flow_dissector.c b/tools/testing/selftests/bpf/prog_tests/flow_dissector.c
index 8b54adfd6264..92563898867c 100644
--- a/tools/testing/selftests/bpf/prog_tests/flow_dissector.c
+++ b/tools/testing/selftests/bpf/prog_tests/flow_dissector.c
@@ -3,6 +3,11 @@
#include <error.h>
#include <linux/if.h>
#include <linux/if_tun.h>
+#include <sys/uio.h>
+
+#ifndef IP_MF
+#define IP_MF 0x2000
+#endif
#define CHECK_FLOW_KEYS(desc, got, expected) \
CHECK_ATTR(memcmp(&got, &expected, sizeof(got)) != 0, \
@@ -15,6 +20,7 @@
"is_encap=%u/%u " \
"ip_proto=0x%x/0x%x " \
"n_proto=0x%x/0x%x " \
+ "flow_label=0x%x/0x%x " \
"sport=%u/%u " \
"dport=%u/%u\n", \
got.nhoff, expected.nhoff, \
@@ -25,6 +31,7 @@
got.is_encap, expected.is_encap, \
got.ip_proto, expected.ip_proto, \
got.n_proto, expected.n_proto, \
+ got.flow_label, expected.flow_label, \
got.sport, expected.sport, \
got.dport, expected.dport)
@@ -34,6 +41,13 @@ struct ipv4_pkt {
struct tcphdr tcp;
} __packed;
+struct ipip_pkt {
+ struct ethhdr eth;
+ struct iphdr iph;
+ struct iphdr iph_inner;
+ struct tcphdr tcp;
+} __packed;
+
struct svlan_ipv4_pkt {
struct ethhdr eth;
__u16 vlan_tci;
@@ -48,6 +62,18 @@ struct ipv6_pkt {
struct tcphdr tcp;
} __packed;
+struct ipv6_frag_pkt {
+ struct ethhdr eth;
+ struct ipv6hdr iph;
+ struct frag_hdr {
+ __u8 nexthdr;
+ __u8 reserved;
+ __be16 frag_off;
+ __be32 identification;
+ } ipf;
+ struct tcphdr tcp;
+} __packed;
+
struct dvlan_ipv6_pkt {
struct ethhdr eth;
__u16 vlan_tci;
@@ -63,10 +89,13 @@ struct test {
union {
struct ipv4_pkt ipv4;
struct svlan_ipv4_pkt svlan_ipv4;
+ struct ipip_pkt ipip;
struct ipv6_pkt ipv6;
+ struct ipv6_frag_pkt ipv6_frag;
struct dvlan_ipv6_pkt dvlan_ipv6;
} pkt;
struct bpf_flow_keys keys;
+ __u32 flags;
};
#define VLAN_HLEN 4
@@ -80,6 +109,8 @@ struct test tests[] = {
.iph.protocol = IPPROTO_TCP,
.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
.tcp.doff = 5,
+ .tcp.source = 80,
+ .tcp.dest = 8080,
},
.keys = {
.nhoff = ETH_HLEN,
@@ -87,6 +118,8 @@ struct test tests[] = {
.addr_proto = ETH_P_IP,
.ip_proto = IPPROTO_TCP,
.n_proto = __bpf_constant_htons(ETH_P_IP),
+ .sport = 80,
+ .dport = 8080,
},
},
{
@@ -96,6 +129,8 @@ struct test tests[] = {
.iph.nexthdr = IPPROTO_TCP,
.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
.tcp.doff = 5,
+ .tcp.source = 80,
+ .tcp.dest = 8080,
},
.keys = {
.nhoff = ETH_HLEN,
@@ -103,6 +138,8 @@ struct test tests[] = {
.addr_proto = ETH_P_IPV6,
.ip_proto = IPPROTO_TCP,
.n_proto = __bpf_constant_htons(ETH_P_IPV6),
+ .sport = 80,
+ .dport = 8080,
},
},
{
@@ -114,6 +151,8 @@ struct test tests[] = {
.iph.protocol = IPPROTO_TCP,
.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
.tcp.doff = 5,
+ .tcp.source = 80,
+ .tcp.dest = 8080,
},
.keys = {
.nhoff = ETH_HLEN + VLAN_HLEN,
@@ -121,6 +160,8 @@ struct test tests[] = {
.addr_proto = ETH_P_IP,
.ip_proto = IPPROTO_TCP,
.n_proto = __bpf_constant_htons(ETH_P_IP),
+ .sport = 80,
+ .dport = 8080,
},
},
{
@@ -132,6 +173,8 @@ struct test tests[] = {
.iph.nexthdr = IPPROTO_TCP,
.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
.tcp.doff = 5,
+ .tcp.source = 80,
+ .tcp.dest = 8080,
},
.keys = {
.nhoff = ETH_HLEN + VLAN_HLEN * 2,
@@ -140,8 +183,205 @@ struct test tests[] = {
.addr_proto = ETH_P_IPV6,
.ip_proto = IPPROTO_TCP,
.n_proto = __bpf_constant_htons(ETH_P_IPV6),
+ .sport = 80,
+ .dport = 8080,
+ },
+ },
+ {
+ .name = "ipv4-frag",
+ .pkt.ipv4 = {
+ .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
+ .iph.ihl = 5,
+ .iph.protocol = IPPROTO_TCP,
+ .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
+ .iph.frag_off = __bpf_constant_htons(IP_MF),
+ .tcp.doff = 5,
+ .tcp.source = 80,
+ .tcp.dest = 8080,
+ },
+ .keys = {
+ .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
+ .nhoff = ETH_HLEN,
+ .thoff = ETH_HLEN + sizeof(struct iphdr),
+ .addr_proto = ETH_P_IP,
+ .ip_proto = IPPROTO_TCP,
+ .n_proto = __bpf_constant_htons(ETH_P_IP),
+ .is_frag = true,
+ .is_first_frag = true,
+ .sport = 80,
+ .dport = 8080,
+ },
+ .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
+ },
+ {
+ .name = "ipv4-no-frag",
+ .pkt.ipv4 = {
+ .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
+ .iph.ihl = 5,
+ .iph.protocol = IPPROTO_TCP,
+ .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
+ .iph.frag_off = __bpf_constant_htons(IP_MF),
+ .tcp.doff = 5,
+ .tcp.source = 80,
+ .tcp.dest = 8080,
+ },
+ .keys = {
+ .nhoff = ETH_HLEN,
+ .thoff = ETH_HLEN + sizeof(struct iphdr),
+ .addr_proto = ETH_P_IP,
+ .ip_proto = IPPROTO_TCP,
+ .n_proto = __bpf_constant_htons(ETH_P_IP),
+ .is_frag = true,
+ .is_first_frag = true,
+ },
+ },
+ {
+ .name = "ipv6-frag",
+ .pkt.ipv6_frag = {
+ .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
+ .iph.nexthdr = IPPROTO_FRAGMENT,
+ .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
+ .ipf.nexthdr = IPPROTO_TCP,
+ .tcp.doff = 5,
+ .tcp.source = 80,
+ .tcp.dest = 8080,
+ },
+ .keys = {
+ .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
+ .nhoff = ETH_HLEN,
+ .thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
+ sizeof(struct frag_hdr),
+ .addr_proto = ETH_P_IPV6,
+ .ip_proto = IPPROTO_TCP,
+ .n_proto = __bpf_constant_htons(ETH_P_IPV6),
+ .is_frag = true,
+ .is_first_frag = true,
+ .sport = 80,
+ .dport = 8080,
+ },
+ .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
+ },
+ {
+ .name = "ipv6-no-frag",
+ .pkt.ipv6_frag = {
+ .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
+ .iph.nexthdr = IPPROTO_FRAGMENT,
+ .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
+ .ipf.nexthdr = IPPROTO_TCP,
+ .tcp.doff = 5,
+ .tcp.source = 80,
+ .tcp.dest = 8080,
+ },
+ .keys = {
+ .nhoff = ETH_HLEN,
+ .thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
+ sizeof(struct frag_hdr),
+ .addr_proto = ETH_P_IPV6,
+ .ip_proto = IPPROTO_TCP,
+ .n_proto = __bpf_constant_htons(ETH_P_IPV6),
+ .is_frag = true,
+ .is_first_frag = true,
+ },
+ },
+ {
+ .name = "ipv6-flow-label",
+ .pkt.ipv6 = {
+ .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
+ .iph.nexthdr = IPPROTO_TCP,
+ .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
+ .iph.flow_lbl = { 0xb, 0xee, 0xef },
+ .tcp.doff = 5,
+ .tcp.source = 80,
+ .tcp.dest = 8080,
+ },
+ .keys = {
+ .nhoff = ETH_HLEN,
+ .thoff = ETH_HLEN + sizeof(struct ipv6hdr),
+ .addr_proto = ETH_P_IPV6,
+ .ip_proto = IPPROTO_TCP,
+ .n_proto = __bpf_constant_htons(ETH_P_IPV6),
+ .sport = 80,
+ .dport = 8080,
+ .flow_label = __bpf_constant_htonl(0xbeeef),
},
},
+ {
+ .name = "ipv6-no-flow-label",
+ .pkt.ipv6 = {
+ .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
+ .iph.nexthdr = IPPROTO_TCP,
+ .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
+ .iph.flow_lbl = { 0xb, 0xee, 0xef },
+ .tcp.doff = 5,
+ .tcp.source = 80,
+ .tcp.dest = 8080,
+ },
+ .keys = {
+ .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
+ .nhoff = ETH_HLEN,
+ .thoff = ETH_HLEN + sizeof(struct ipv6hdr),
+ .addr_proto = ETH_P_IPV6,
+ .ip_proto = IPPROTO_TCP,
+ .n_proto = __bpf_constant_htons(ETH_P_IPV6),
+ .flow_label = __bpf_constant_htonl(0xbeeef),
+ },
+ .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
+ },
+ {
+ .name = "ipip-encap",
+ .pkt.ipip = {
+ .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
+ .iph.ihl = 5,
+ .iph.protocol = IPPROTO_IPIP,
+ .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
+ .iph_inner.ihl = 5,
+ .iph_inner.protocol = IPPROTO_TCP,
+ .iph_inner.tot_len =
+ __bpf_constant_htons(MAGIC_BYTES) -
+ sizeof(struct iphdr),
+ .tcp.doff = 5,
+ .tcp.source = 80,
+ .tcp.dest = 8080,
+ },
+ .keys = {
+ .nhoff = ETH_HLEN,
+ .thoff = ETH_HLEN + sizeof(struct iphdr) +
+ sizeof(struct iphdr),
+ .addr_proto = ETH_P_IP,
+ .ip_proto = IPPROTO_TCP,
+ .n_proto = __bpf_constant_htons(ETH_P_IP),
+ .is_encap = true,
+ .sport = 80,
+ .dport = 8080,
+ },
+ },
+ {
+ .name = "ipip-no-encap",
+ .pkt.ipip = {
+ .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
+ .iph.ihl = 5,
+ .iph.protocol = IPPROTO_IPIP,
+ .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
+ .iph_inner.ihl = 5,
+ .iph_inner.protocol = IPPROTO_TCP,
+ .iph_inner.tot_len =
+ __bpf_constant_htons(MAGIC_BYTES) -
+ sizeof(struct iphdr),
+ .tcp.doff = 5,
+ .tcp.source = 80,
+ .tcp.dest = 8080,
+ },
+ .keys = {
+ .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
+ .nhoff = ETH_HLEN,
+ .thoff = ETH_HLEN + sizeof(struct iphdr),
+ .addr_proto = ETH_P_IP,
+ .ip_proto = IPPROTO_IPIP,
+ .n_proto = __bpf_constant_htons(ETH_P_IP),
+ .is_encap = true,
+ },
+ .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
+ },
};
static int create_tap(const char *ifname)
@@ -211,10 +451,8 @@ void test_flow_dissector(void)
err = bpf_flow_load(&obj, "./bpf_flow.o", "flow_dissector",
"jmp_table", "last_dissection", &prog_fd, &keys_fd);
- if (err) {
- error_cnt++;
+ if (CHECK_FAIL(err))
return;
- }
for (i = 0; i < ARRAY_SIZE(tests); i++) {
struct bpf_flow_keys flow_keys;
@@ -224,6 +462,13 @@ void test_flow_dissector(void)
.data_size_in = sizeof(tests[i].pkt),
.data_out = &flow_keys,
};
+ static struct bpf_flow_keys ctx = {};
+
+ if (tests[i].flags) {
+ tattr.ctx_in = &ctx;
+ tattr.ctx_size_in = sizeof(ctx);
+ ctx.flags = tests[i].flags;
+ }
err = bpf_prog_test_run_xattr(&tattr);
CHECK_ATTR(tattr.data_size_out != sizeof(flow_keys) ||
@@ -242,27 +487,42 @@ void test_flow_dissector(void)
*/
err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
- CHECK(err, "bpf_prog_attach", "err %d errno %d", err, errno);
+ CHECK(err, "bpf_prog_attach", "err %d errno %d\n", err, errno);
tap_fd = create_tap("tap0");
- CHECK(tap_fd < 0, "create_tap", "tap_fd %d errno %d", tap_fd, errno);
+ CHECK(tap_fd < 0, "create_tap", "tap_fd %d errno %d\n", tap_fd, errno);
err = ifup("tap0");
- CHECK(err, "ifup", "err %d errno %d", err, errno);
+ CHECK(err, "ifup", "err %d errno %d\n", err, errno);
for (i = 0; i < ARRAY_SIZE(tests); i++) {
- struct bpf_flow_keys flow_keys = {};
+ /* Keep in sync with 'flags' from eth_get_headlen. */
+ __u32 eth_get_headlen_flags =
+ BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG;
struct bpf_prog_test_run_attr tattr = {};
- __u32 key = 0;
+ struct bpf_flow_keys flow_keys = {};
+ __u32 key = (__u32)(tests[i].keys.sport) << 16 |
+ tests[i].keys.dport;
+
+ /* For skb-less case we can't pass input flags; run
+ * only the tests that have a matching set of flags.
+ */
+
+ if (tests[i].flags != eth_get_headlen_flags)
+ continue;
err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt));
- CHECK(err < 0, "tx_tap", "err %d errno %d", err, errno);
+ CHECK(err < 0, "tx_tap", "err %d errno %d\n", err, errno);
err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys);
CHECK_ATTR(err, tests[i].name, "bpf_map_lookup_elem %d\n", err);
CHECK_ATTR(err, tests[i].name, "skb-less err %d\n", err);
CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
+
+ err = bpf_map_delete_elem(keys_fd, &key);
+ CHECK_ATTR(err, tests[i].name, "bpf_map_delete_elem %d\n", err);
}
+ bpf_prog_detach(prog_fd, BPF_FLOW_DISSECTOR);
bpf_object__close(obj);
}
diff --git a/tools/testing/selftests/bpf/prog_tests/get_stack_raw_tp.c b/tools/testing/selftests/bpf/prog_tests/get_stack_raw_tp.c
index c2a0a9d5591b..eba9a970703b 100644
--- a/tools/testing/selftests/bpf/prog_tests/get_stack_raw_tp.c
+++ b/tools/testing/selftests/bpf/prog_tests/get_stack_raw_tp.c
@@ -1,8 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <pthread.h>
+#include <sched.h>
+#include <sys/socket.h>
#include <test_progs.h>
#define MAX_CNT_RAWTP 10ull
#define MAX_STACK_RAWTP 100
+
+static int duration = 0;
+
struct get_stack_trace_t {
int pid;
int kern_stack_size;
@@ -13,7 +20,7 @@ struct get_stack_trace_t {
struct bpf_stack_build_id user_stack_buildid[MAX_STACK_RAWTP];
};
-static int get_stack_print_output(void *data, int size)
+static void get_stack_print_output(void *ctx, int cpu, void *data, __u32 size)
{
bool good_kern_stack = false, good_user_stack = false;
const char *nonjit_func = "___bpf_prog_run";
@@ -34,7 +41,7 @@ static int get_stack_print_output(void *data, int size)
* just assume it is good if the stack is not empty.
* This could be improved in the future.
*/
- if (jit_enabled) {
+ if (env.jit_enabled) {
found = num_stack > 0;
} else {
for (i = 0; i < num_stack; i++) {
@@ -51,7 +58,7 @@ static int get_stack_print_output(void *data, int size)
}
} else {
num_stack = e->kern_stack_size / sizeof(__u64);
- if (jit_enabled) {
+ if (env.jit_enabled) {
good_kern_stack = num_stack > 0;
} else {
for (i = 0; i < num_stack; i++) {
@@ -65,75 +72,73 @@ static int get_stack_print_output(void *data, int size)
if (e->user_stack_size > 0 && e->user_stack_buildid_size > 0)
good_user_stack = true;
}
- if (!good_kern_stack || !good_user_stack)
- return LIBBPF_PERF_EVENT_ERROR;
- if (cnt == MAX_CNT_RAWTP)
- return LIBBPF_PERF_EVENT_DONE;
-
- return LIBBPF_PERF_EVENT_CONT;
+ if (!good_kern_stack)
+ CHECK(!good_kern_stack, "kern_stack", "corrupted kernel stack\n");
+ if (!good_user_stack)
+ CHECK(!good_user_stack, "user_stack", "corrupted user stack\n");
}
void test_get_stack_raw_tp(void)
{
const char *file = "./test_get_stack_rawtp.o";
- int i, efd, err, prog_fd, pmu_fd, perfmap_fd;
- struct perf_event_attr attr = {};
+ const char *prog_name = "raw_tracepoint/sys_enter";
+ int i, err, prog_fd, exp_cnt = MAX_CNT_RAWTP;
+ struct perf_buffer_opts pb_opts = {};
+ struct perf_buffer *pb = NULL;
+ struct bpf_link *link = NULL;
struct timespec tv = {0, 10};
- __u32 key = 0, duration = 0;
+ struct bpf_program *prog;
struct bpf_object *obj;
+ struct bpf_map *map;
+ cpu_set_t cpu_set;
err = bpf_prog_load(file, BPF_PROG_TYPE_RAW_TRACEPOINT, &obj, &prog_fd);
if (CHECK(err, "prog_load raw tp", "err %d errno %d\n", err, errno))
return;
- efd = bpf_raw_tracepoint_open("sys_enter", prog_fd);
- if (CHECK(efd < 0, "raw_tp_open", "err %d errno %d\n", efd, errno))
+ prog = bpf_object__find_program_by_title(obj, prog_name);
+ if (CHECK(!prog, "find_probe", "prog '%s' not found\n", prog_name))
goto close_prog;
- perfmap_fd = bpf_find_map(__func__, obj, "perfmap");
- if (CHECK(perfmap_fd < 0, "bpf_find_map", "err %d errno %d\n",
- perfmap_fd, errno))
+ map = bpf_object__find_map_by_name(obj, "perfmap");
+ if (CHECK(!map, "bpf_find_map", "not found\n"))
goto close_prog;
err = load_kallsyms();
if (CHECK(err < 0, "load_kallsyms", "err %d errno %d\n", err, errno))
goto close_prog;
- attr.sample_type = PERF_SAMPLE_RAW;
- attr.type = PERF_TYPE_SOFTWARE;
- attr.config = PERF_COUNT_SW_BPF_OUTPUT;
- pmu_fd = syscall(__NR_perf_event_open, &attr, getpid()/*pid*/, -1/*cpu*/,
- -1/*group_fd*/, 0);
- if (CHECK(pmu_fd < 0, "perf_event_open", "err %d errno %d\n", pmu_fd,
- errno))
+ 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 close_prog;
- err = bpf_map_update_elem(perfmap_fd, &key, &pmu_fd, BPF_ANY);
- if (CHECK(err < 0, "bpf_map_update_elem", "err %d errno %d\n", err,
- errno))
+ link = bpf_program__attach_raw_tracepoint(prog, "sys_enter");
+ if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n", PTR_ERR(link)))
goto close_prog;
- err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
- if (CHECK(err < 0, "ioctl PERF_EVENT_IOC_ENABLE", "err %d errno %d\n",
- err, errno))
- goto close_prog;
-
- err = perf_event_mmap(pmu_fd);
- if (CHECK(err < 0, "perf_event_mmap", "err %d errno %d\n", err, errno))
+ pb_opts.sample_cb = get_stack_print_output;
+ pb = perf_buffer__new(bpf_map__fd(map), 8, &pb_opts);
+ if (CHECK(IS_ERR(pb), "perf_buf__new", "err %ld\n", PTR_ERR(pb)))
goto close_prog;
/* trigger some syscall action */
for (i = 0; i < MAX_CNT_RAWTP; i++)
nanosleep(&tv, NULL);
- err = perf_event_poller(pmu_fd, get_stack_print_output);
- if (CHECK(err < 0, "perf_event_poller", "err %d errno %d\n", err, errno))
- goto close_prog;
+ while (exp_cnt > 0) {
+ err = perf_buffer__poll(pb, 100);
+ if (err < 0 && CHECK(err < 0, "pb__poll", "err %d\n", err))
+ goto close_prog;
+ exp_cnt -= err;
+ }
- goto close_prog_noerr;
close_prog:
- error_cnt++;
-close_prog_noerr:
+ if (!IS_ERR_OR_NULL(link))
+ bpf_link__destroy(link);
+ if (!IS_ERR_OR_NULL(pb))
+ perf_buffer__free(pb);
bpf_object__close(obj);
}
diff --git a/tools/testing/selftests/bpf/prog_tests/global_data.c b/tools/testing/selftests/bpf/prog_tests/global_data.c
index d011079fb0bf..c680926fce73 100644
--- a/tools/testing/selftests/bpf/prog_tests/global_data.c
+++ b/tools/testing/selftests/bpf/prog_tests/global_data.c
@@ -7,10 +7,8 @@ static void test_global_data_number(struct bpf_object *obj, __u32 duration)
uint64_t num;
map_fd = bpf_find_map(__func__, obj, "result_number");
- if (map_fd < 0) {
- error_cnt++;
+ if (CHECK_FAIL(map_fd < 0))
return;
- }
struct {
char *name;
@@ -44,10 +42,8 @@ static void test_global_data_string(struct bpf_object *obj, __u32 duration)
char str[32];
map_fd = bpf_find_map(__func__, obj, "result_string");
- if (map_fd < 0) {
- error_cnt++;
+ if (CHECK_FAIL(map_fd < 0))
return;
- }
struct {
char *name;
@@ -81,10 +77,8 @@ static void test_global_data_struct(struct bpf_object *obj, __u32 duration)
struct foo val;
map_fd = bpf_find_map(__func__, obj, "result_struct");
- if (map_fd < 0) {
- error_cnt++;
+ if (CHECK_FAIL(map_fd < 0))
return;
- }
struct {
char *name;
@@ -112,16 +106,12 @@ static void test_global_data_rdonly(struct bpf_object *obj, __u32 duration)
__u8 *buff;
map = bpf_object__find_map_by_name(obj, "test_glo.rodata");
- if (!map || !bpf_map__is_internal(map)) {
- error_cnt++;
+ if (CHECK_FAIL(!map || !bpf_map__is_internal(map)))
return;
- }
map_fd = bpf_map__fd(map);
- if (map_fd < 0) {
- error_cnt++;
+ if (CHECK_FAIL(map_fd < 0))
return;
- }
buff = malloc(bpf_map__def(map)->value_size);
if (buff)
diff --git a/tools/testing/selftests/bpf/prog_tests/l4lb_all.c b/tools/testing/selftests/bpf/prog_tests/l4lb_all.c
index 20ddca830e68..eaf64595be88 100644
--- a/tools/testing/selftests/bpf/prog_tests/l4lb_all.c
+++ b/tools/testing/selftests/bpf/prog_tests/l4lb_all.c
@@ -30,10 +30,8 @@ static void test_l4lb(const char *file)
u32 *magic = (u32 *)buf;
err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd);
- if (err) {
- error_cnt++;
+ if (CHECK_FAIL(err))
return;
- }
map_fd = bpf_find_map(__func__, obj, "vip_map");
if (map_fd < 0)
@@ -72,10 +70,9 @@ static void test_l4lb(const char *file)
bytes += stats[i].bytes;
pkts += stats[i].pkts;
}
- if (bytes != MAGIC_BYTES * NUM_ITER * 2 || pkts != NUM_ITER * 2) {
- error_cnt++;
+ if (CHECK_FAIL(bytes != MAGIC_BYTES * NUM_ITER * 2 ||
+ pkts != NUM_ITER * 2))
printf("test_l4lb:FAIL:stats %lld %lld\n", bytes, pkts);
- }
out:
bpf_object__close(obj);
}
diff --git a/tools/testing/selftests/bpf/prog_tests/map_lock.c b/tools/testing/selftests/bpf/prog_tests/map_lock.c
index ee99368c595c..8f91f1881d11 100644
--- a/tools/testing/selftests/bpf/prog_tests/map_lock.c
+++ b/tools/testing/selftests/bpf/prog_tests/map_lock.c
@@ -8,14 +8,12 @@ static void *parallel_map_access(void *arg)
for (i = 0; i < 10000; i++) {
err = bpf_map_lookup_elem_flags(map_fd, &key, vars, BPF_F_LOCK);
- if (err) {
+ if (CHECK_FAIL(err)) {
printf("lookup failed\n");
- error_cnt++;
goto out;
}
- if (vars[0] != 0) {
+ if (CHECK_FAIL(vars[0] != 0)) {
printf("lookup #%d var[0]=%d\n", i, vars[0]);
- error_cnt++;
goto out;
}
rnd = vars[1];
@@ -24,7 +22,7 @@ static void *parallel_map_access(void *arg)
continue;
printf("lookup #%d var[1]=%d var[%d]=%d\n",
i, rnd, j, vars[j]);
- error_cnt++;
+ CHECK_FAIL(vars[j] != rnd);
goto out;
}
}
@@ -42,34 +40,36 @@ void test_map_lock(void)
void *ret;
err = bpf_prog_load(file, BPF_PROG_TYPE_CGROUP_SKB, &obj, &prog_fd);
- if (err) {
+ if (CHECK_FAIL(err)) {
printf("test_map_lock:bpf_prog_load errno %d\n", errno);
goto close_prog;
}
map_fd[0] = bpf_find_map(__func__, obj, "hash_map");
- if (map_fd[0] < 0)
+ if (CHECK_FAIL(map_fd[0] < 0))
goto close_prog;
map_fd[1] = bpf_find_map(__func__, obj, "array_map");
- if (map_fd[1] < 0)
+ if (CHECK_FAIL(map_fd[1] < 0))
goto close_prog;
bpf_map_update_elem(map_fd[0], &key, vars, BPF_F_LOCK);
for (i = 0; i < 4; i++)
- assert(pthread_create(&thread_id[i], NULL,
- &spin_lock_thread, &prog_fd) == 0);
+ if (CHECK_FAIL(pthread_create(&thread_id[i], NULL,
+ &spin_lock_thread, &prog_fd)))
+ goto close_prog;
for (i = 4; i < 6; i++)
- assert(pthread_create(&thread_id[i], NULL,
- &parallel_map_access, &map_fd[i - 4]) == 0);
+ if (CHECK_FAIL(pthread_create(&thread_id[i], NULL,
+ &parallel_map_access,
+ &map_fd[i - 4])))
+ goto close_prog;
for (i = 0; i < 4; i++)
- assert(pthread_join(thread_id[i], &ret) == 0 &&
- ret == (void *)&prog_fd);
+ if (CHECK_FAIL(pthread_join(thread_id[i], &ret) ||
+ ret != (void *)&prog_fd))
+ goto close_prog;
for (i = 4; i < 6; i++)
- assert(pthread_join(thread_id[i], &ret) == 0 &&
- ret == (void *)&map_fd[i - 4]);
- goto close_prog_noerr;
+ if (CHECK_FAIL(pthread_join(thread_id[i], &ret) ||
+ ret != (void *)&map_fd[i - 4]))
+ goto close_prog;
close_prog:
- error_cnt++;
-close_prog_noerr:
bpf_object__close(obj);
}
diff --git a/tools/testing/selftests/bpf/prog_tests/perf_buffer.c b/tools/testing/selftests/bpf/prog_tests/perf_buffer.c
new file mode 100644
index 000000000000..3003fddc0613
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/perf_buffer.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <pthread.h>
+#include <sched.h>
+#include <sys/socket.h>
+#include <test_progs.h>
+
+static void on_sample(void *ctx, int cpu, void *data, __u32 size)
+{
+ int cpu_data = *(int *)data, duration = 0;
+ cpu_set_t *cpu_seen = ctx;
+
+ if (cpu_data != cpu)
+ CHECK(cpu_data != cpu, "check_cpu_data",
+ "cpu_data %d != cpu %d\n", cpu_data, cpu);
+
+ CPU_SET(cpu, cpu_seen);
+}
+
+void test_perf_buffer(void)
+{
+ int err, prog_fd, nr_cpus, i, duration = 0;
+ const char *prog_name = "kprobe/sys_nanosleep";
+ const char *file = "./test_perf_buffer.o";
+ struct perf_buffer_opts pb_opts = {};
+ struct bpf_map *perf_buf_map;
+ cpu_set_t cpu_set, cpu_seen;
+ struct bpf_program *prog;
+ struct bpf_object *obj;
+ struct perf_buffer *pb;
+ struct bpf_link *link;
+
+ nr_cpus = libbpf_num_possible_cpus();
+ if (CHECK(nr_cpus < 0, "nr_cpus", "err %d\n", nr_cpus))
+ return;
+
+ /* load program */
+ err = bpf_prog_load(file, BPF_PROG_TYPE_KPROBE, &obj, &prog_fd);
+ if (CHECK(err, "obj_load", "err %d errno %d\n", err, errno))
+ return;
+
+ prog = bpf_object__find_program_by_title(obj, prog_name);
+ if (CHECK(!prog, "find_probe", "prog '%s' not found\n", prog_name))
+ goto out_close;
+
+ /* load map */
+ perf_buf_map = bpf_object__find_map_by_name(obj, "perf_buf_map");
+ if (CHECK(!perf_buf_map, "find_perf_buf_map", "not found\n"))
+ goto out_close;
+
+ /* attach kprobe */
+ link = bpf_program__attach_kprobe(prog, false /* retprobe */,
+ SYS_NANOSLEEP_KPROBE_NAME);
+ if (CHECK(IS_ERR(link), "attach_kprobe", "err %ld\n", PTR_ERR(link)))
+ goto out_close;
+
+ /* set up perf buffer */
+ pb_opts.sample_cb = on_sample;
+ pb_opts.ctx = &cpu_seen;
+ pb = perf_buffer__new(bpf_map__fd(perf_buf_map), 1, &pb_opts);
+ if (CHECK(IS_ERR(pb), "perf_buf__new", "err %ld\n", PTR_ERR(pb)))
+ goto out_detach;
+
+ /* trigger kprobe on every CPU */
+ CPU_ZERO(&cpu_seen);
+ for (i = 0; i < nr_cpus; i++) {
+ CPU_ZERO(&cpu_set);
+ CPU_SET(i, &cpu_set);
+
+ err = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set),
+ &cpu_set);
+ if (err && CHECK(err, "set_affinity", "cpu #%d, err %d\n",
+ i, err))
+ goto out_detach;
+
+ usleep(1);
+ }
+
+ /* read perf buffer */
+ err = perf_buffer__poll(pb, 100);
+ if (CHECK(err < 0, "perf_buffer__poll", "err %d\n", err))
+ goto out_free_pb;
+
+ if (CHECK(CPU_COUNT(&cpu_seen) != nr_cpus, "seen_cpu_cnt",
+ "expect %d, seen %d\n", nr_cpus, CPU_COUNT(&cpu_seen)))
+ goto out_free_pb;
+
+out_free_pb:
+ perf_buffer__free(pb);
+out_detach:
+ bpf_link__destroy(link);
+out_close:
+ bpf_object__close(obj);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/pkt_access.c b/tools/testing/selftests/bpf/prog_tests/pkt_access.c
index 4ecfd721a044..a2537dfa899c 100644
--- a/tools/testing/selftests/bpf/prog_tests/pkt_access.c
+++ b/tools/testing/selftests/bpf/prog_tests/pkt_access.c
@@ -9,10 +9,8 @@ void test_pkt_access(void)
int err, prog_fd;
err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd);
- if (err) {
- error_cnt++;
+ if (CHECK_FAIL(err))
return;
- }
err = bpf_prog_test_run(prog_fd, 100000, &pkt_v4, sizeof(pkt_v4),
NULL, NULL, &retval, &duration);
diff --git a/tools/testing/selftests/bpf/prog_tests/pkt_md_access.c b/tools/testing/selftests/bpf/prog_tests/pkt_md_access.c
index ac0d43435806..5f7aea605019 100644
--- a/tools/testing/selftests/bpf/prog_tests/pkt_md_access.c
+++ b/tools/testing/selftests/bpf/prog_tests/pkt_md_access.c
@@ -9,10 +9,8 @@ void test_pkt_md_access(void)
int err, prog_fd;
err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd);
- if (err) {
- error_cnt++;
+ if (CHECK_FAIL(err))
return;
- }
err = bpf_prog_test_run(prog_fd, 10, &pkt_v4, sizeof(pkt_v4),
NULL, NULL, &retval, &duration);
diff --git a/tools/testing/selftests/bpf/prog_tests/queue_stack_map.c b/tools/testing/selftests/bpf/prog_tests/queue_stack_map.c
index e60cd5ff1f55..faccc66f4e39 100644
--- a/tools/testing/selftests/bpf/prog_tests/queue_stack_map.c
+++ b/tools/testing/selftests/bpf/prog_tests/queue_stack_map.c
@@ -27,10 +27,8 @@ static void test_queue_stack_map_by_type(int type)
return;
err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd);
- if (err) {
- error_cnt++;
+ if (CHECK_FAIL(err))
return;
- }
map_in_fd = bpf_find_map(__func__, obj, "map_in");
if (map_in_fd < 0)
@@ -43,10 +41,8 @@ static void test_queue_stack_map_by_type(int type)
/* Push 32 elements to the input map */
for (i = 0; i < MAP_SIZE; i++) {
err = bpf_map_update_elem(map_in_fd, NULL, &vals[i], 0);
- if (err) {
- error_cnt++;
+ if (CHECK_FAIL(err))
goto out;
- }
}
/* The eBPF program pushes iph.saddr in the output map,
diff --git a/tools/testing/selftests/bpf/prog_tests/reference_tracking.c b/tools/testing/selftests/bpf/prog_tests/reference_tracking.c
index 5633be43828f..5c78e2b5a917 100644
--- a/tools/testing/selftests/bpf/prog_tests/reference_tracking.c
+++ b/tools/testing/selftests/bpf/prog_tests/reference_tracking.c
@@ -1,15 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <test_progs.h>
-static int libbpf_debug_print(enum libbpf_print_level level,
- const char *format, va_list args)
-{
- if (level == LIBBPF_DEBUG)
- return 0;
-
- return vfprintf(stderr, format, args);
-}
-
void test_reference_tracking(void)
{
const char *file = "./test_sk_lookup_kern.o";
@@ -19,10 +10,8 @@ void test_reference_tracking(void)
int err = 0;
obj = bpf_object__open(file);
- if (IS_ERR(obj)) {
- error_cnt++;
+ if (CHECK_FAIL(IS_ERR(obj)))
return;
- }
bpf_object__for_each_program(prog, obj) {
const char *title;
@@ -36,9 +25,11 @@ void test_reference_tracking(void)
/* Expect verifier failure if test name has 'fail' */
if (strstr(title, "fail") != NULL) {
- libbpf_set_print(NULL);
+ libbpf_print_fn_t old_print_fn;
+
+ old_print_fn = libbpf_set_print(NULL);
err = !bpf_program__load(prog, "GPL", 0);
- libbpf_set_print(libbpf_debug_print);
+ libbpf_set_print(old_print_fn);
} else {
err = bpf_program__load(prog, "GPL", 0);
}
diff --git a/tools/testing/selftests/bpf/prog_tests/send_signal.c b/tools/testing/selftests/bpf/prog_tests/send_signal.c
new file mode 100644
index 000000000000..b607112c64e7
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/send_signal.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+
+static volatile int sigusr1_received = 0;
+
+static void sigusr1_handler(int signum)
+{
+ sigusr1_received++;
+}
+
+static void test_send_signal_common(struct perf_event_attr *attr,
+ int prog_type,
+ const char *test_name)
+{
+ int err = -1, pmu_fd, prog_fd, info_map_fd, status_map_fd;
+ const char *file = "./test_send_signal_kern.o";
+ struct bpf_object *obj = NULL;
+ int pipe_c2p[2], pipe_p2c[2];
+ __u32 key = 0, duration = 0;
+ char buf[256];
+ pid_t pid;
+ __u64 val;
+
+ if (CHECK(pipe(pipe_c2p), test_name,
+ "pipe pipe_c2p error: %s\n", strerror(errno)))
+ return;
+
+ if (CHECK(pipe(pipe_p2c), test_name,
+ "pipe pipe_p2c error: %s\n", strerror(errno))) {
+ close(pipe_c2p[0]);
+ close(pipe_c2p[1]);
+ return;
+ }
+
+ pid = fork();
+ if (CHECK(pid < 0, test_name, "fork error: %s\n", strerror(errno))) {
+ close(pipe_c2p[0]);
+ close(pipe_c2p[1]);
+ close(pipe_p2c[0]);
+ close(pipe_p2c[1]);
+ return;
+ }
+
+ if (pid == 0) {
+ /* install signal handler and notify parent */
+ signal(SIGUSR1, sigusr1_handler);
+
+ close(pipe_c2p[0]); /* close read */
+ close(pipe_p2c[1]); /* close write */
+
+ /* notify parent signal handler is installed */
+ write(pipe_c2p[1], buf, 1);
+
+ /* make sure parent enabled bpf program to send_signal */
+ read(pipe_p2c[0], buf, 1);
+
+ /* wait a little for signal handler */
+ sleep(1);
+
+ if (sigusr1_received)
+ write(pipe_c2p[1], "2", 1);
+ else
+ write(pipe_c2p[1], "0", 1);
+
+ /* wait for parent notification and exit */
+ read(pipe_p2c[0], buf, 1);
+
+ close(pipe_c2p[1]);
+ close(pipe_p2c[0]);
+ exit(0);
+ }
+
+ close(pipe_c2p[1]); /* close write */
+ close(pipe_p2c[0]); /* close read */
+
+ err = bpf_prog_load(file, prog_type, &obj, &prog_fd);
+ if (CHECK(err < 0, test_name, "bpf_prog_load error: %s\n",
+ strerror(errno)))
+ goto prog_load_failure;
+
+ pmu_fd = syscall(__NR_perf_event_open, attr, pid, -1,
+ -1 /* group id */, 0 /* flags */);
+ if (CHECK(pmu_fd < 0, test_name, "perf_event_open error: %s\n",
+ strerror(errno))) {
+ err = -1;
+ goto close_prog;
+ }
+
+ err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
+ if (CHECK(err < 0, test_name, "ioctl perf_event_ioc_enable error: %s\n",
+ strerror(errno)))
+ goto disable_pmu;
+
+ err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd);
+ if (CHECK(err < 0, test_name, "ioctl perf_event_ioc_set_bpf error: %s\n",
+ strerror(errno)))
+ goto disable_pmu;
+
+ err = -1;
+ info_map_fd = bpf_object__find_map_fd_by_name(obj, "info_map");
+ if (CHECK(info_map_fd < 0, test_name, "find map %s error\n", "info_map"))
+ goto disable_pmu;
+
+ status_map_fd = bpf_object__find_map_fd_by_name(obj, "status_map");
+ if (CHECK(status_map_fd < 0, test_name, "find map %s error\n", "status_map"))
+ goto disable_pmu;
+
+ /* wait until child signal handler installed */
+ read(pipe_c2p[0], buf, 1);
+
+ /* trigger the bpf send_signal */
+ key = 0;
+ val = (((__u64)(SIGUSR1)) << 32) | pid;
+ bpf_map_update_elem(info_map_fd, &key, &val, 0);
+
+ /* notify child that bpf program can send_signal now */
+ write(pipe_p2c[1], buf, 1);
+
+ /* wait for result */
+ err = read(pipe_c2p[0], buf, 1);
+ if (CHECK(err < 0, test_name, "reading pipe error: %s\n", strerror(errno)))
+ goto disable_pmu;
+ if (CHECK(err == 0, test_name, "reading pipe error: size 0\n")) {
+ err = -1;
+ goto disable_pmu;
+ }
+
+ CHECK(buf[0] != '2', test_name, "incorrect result\n");
+
+ /* notify child safe to exit */
+ write(pipe_p2c[1], buf, 1);
+
+disable_pmu:
+ close(pmu_fd);
+close_prog:
+ bpf_object__close(obj);
+prog_load_failure:
+ close(pipe_c2p[0]);
+ close(pipe_p2c[1]);
+ wait(NULL);
+}
+
+static void test_send_signal_tracepoint(void)
+{
+ const char *id_path = "/sys/kernel/debug/tracing/events/syscalls/sys_enter_nanosleep/id";
+ struct perf_event_attr attr = {
+ .type = PERF_TYPE_TRACEPOINT,
+ .sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_CALLCHAIN,
+ .sample_period = 1,
+ .wakeup_events = 1,
+ };
+ __u32 duration = 0;
+ int bytes, efd;
+ char buf[256];
+
+ efd = open(id_path, O_RDONLY, 0);
+ if (CHECK(efd < 0, "tracepoint",
+ "open syscalls/sys_enter_nanosleep/id failure: %s\n",
+ strerror(errno)))
+ return;
+
+ bytes = read(efd, buf, sizeof(buf));
+ close(efd);
+ if (CHECK(bytes <= 0 || bytes >= sizeof(buf), "tracepoint",
+ "read syscalls/sys_enter_nanosleep/id failure: %s\n",
+ strerror(errno)))
+ return;
+
+ attr.config = strtol(buf, NULL, 0);
+
+ test_send_signal_common(&attr, BPF_PROG_TYPE_TRACEPOINT, "tracepoint");
+}
+
+static void test_send_signal_perf(void)
+{
+ struct perf_event_attr attr = {
+ .sample_period = 1,
+ .type = PERF_TYPE_SOFTWARE,
+ .config = PERF_COUNT_SW_CPU_CLOCK,
+ };
+
+ test_send_signal_common(&attr, BPF_PROG_TYPE_PERF_EVENT,
+ "perf_sw_event");
+}
+
+static void test_send_signal_nmi(void)
+{
+ struct perf_event_attr attr = {
+ .sample_freq = 50,
+ .freq = 1,
+ .type = PERF_TYPE_HARDWARE,
+ .config = PERF_COUNT_HW_CPU_CYCLES,
+ };
+ int pmu_fd;
+
+ /* Some setups (e.g. virtual machines) might run with hardware
+ * perf events disabled. If this is the case, skip this test.
+ */
+ pmu_fd = syscall(__NR_perf_event_open, &attr, 0 /* pid */,
+ -1 /* cpu */, -1 /* group_fd */, 0 /* flags */);
+ if (pmu_fd == -1) {
+ if (errno == ENOENT) {
+ printf("%s:SKIP:no PERF_COUNT_HW_CPU_CYCLES\n",
+ __func__);
+ test__skip();
+ return;
+ }
+ /* Let the test fail with a more informative message */
+ } else {
+ close(pmu_fd);
+ }
+
+ test_send_signal_common(&attr, BPF_PROG_TYPE_PERF_EVENT,
+ "perf_hw_event");
+}
+
+void test_send_signal(void)
+{
+ if (test__start_subtest("send_signal_tracepoint"))
+ test_send_signal_tracepoint();
+ if (test__start_subtest("send_signal_perf"))
+ test_send_signal_perf();
+ if (test__start_subtest("send_signal_nmi"))
+ test_send_signal_nmi();
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/sockopt.c b/tools/testing/selftests/bpf/prog_tests/sockopt.c
new file mode 100644
index 000000000000..3e8517a8395a
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/sockopt.c
@@ -0,0 +1,985 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include "cgroup_helpers.h"
+
+static char bpf_log_buf[4096];
+static bool verbose;
+
+enum sockopt_test_error {
+ OK = 0,
+ DENY_LOAD,
+ DENY_ATTACH,
+ EPERM_GETSOCKOPT,
+ EFAULT_GETSOCKOPT,
+ EPERM_SETSOCKOPT,
+ EFAULT_SETSOCKOPT,
+};
+
+static struct sockopt_test {
+ const char *descr;
+ const struct bpf_insn insns[64];
+ enum bpf_attach_type attach_type;
+ enum bpf_attach_type expected_attach_type;
+
+ int set_optname;
+ int set_level;
+ const char set_optval[64];
+ socklen_t set_optlen;
+
+ int get_optname;
+ int get_level;
+ const char get_optval[64];
+ socklen_t get_optlen;
+ socklen_t get_optlen_ret;
+
+ enum sockopt_test_error error;
+} tests[] = {
+
+ /* ==================== getsockopt ==================== */
+
+ {
+ .descr = "getsockopt: no expected_attach_type",
+ .insns = {
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+
+ },
+ .attach_type = BPF_CGROUP_GETSOCKOPT,
+ .expected_attach_type = 0,
+ .error = DENY_LOAD,
+ },
+ {
+ .descr = "getsockopt: wrong expected_attach_type",
+ .insns = {
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+
+ },
+ .attach_type = BPF_CGROUP_GETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+ .error = DENY_ATTACH,
+ },
+ {
+ .descr = "getsockopt: bypass bpf hook",
+ .insns = {
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_GETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
+
+ .get_level = SOL_IP,
+ .set_level = SOL_IP,
+
+ .get_optname = IP_TOS,
+ .set_optname = IP_TOS,
+
+ .set_optval = { 1 << 3 },
+ .set_optlen = 1,
+
+ .get_optval = { 1 << 3 },
+ .get_optlen = 1,
+ },
+ {
+ .descr = "getsockopt: return EPERM from bpf hook",
+ .insns = {
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_GETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
+
+ .get_level = SOL_IP,
+ .get_optname = IP_TOS,
+
+ .get_optlen = 1,
+ .error = EPERM_GETSOCKOPT,
+ },
+ {
+ .descr = "getsockopt: no optval bounds check, deny loading",
+ .insns = {
+ /* r6 = ctx->optval */
+ BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
+ offsetof(struct bpf_sockopt, optval)),
+
+ /* ctx->optval[0] = 0x80 */
+ BPF_MOV64_IMM(BPF_REG_0, 0x80),
+ BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_0, 0),
+
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_GETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
+ .error = DENY_LOAD,
+ },
+ {
+ .descr = "getsockopt: read ctx->level",
+ .insns = {
+ /* r6 = ctx->level */
+ BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
+ offsetof(struct bpf_sockopt, level)),
+
+ /* if (ctx->level == 123) { */
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
+ /* ctx->retval = 0 */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, retval)),
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_JMP_A(1),
+ /* } else { */
+ /* return 0 */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ /* } */
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_GETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
+
+ .get_level = 123,
+
+ .get_optlen = 1,
+ },
+ {
+ .descr = "getsockopt: deny writing to ctx->level",
+ .insns = {
+ /* ctx->level = 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, level)),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_GETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
+
+ .error = DENY_LOAD,
+ },
+ {
+ .descr = "getsockopt: read ctx->optname",
+ .insns = {
+ /* r6 = ctx->optname */
+ BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
+ offsetof(struct bpf_sockopt, optname)),
+
+ /* if (ctx->optname == 123) { */
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
+ /* ctx->retval = 0 */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, retval)),
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_JMP_A(1),
+ /* } else { */
+ /* return 0 */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ /* } */
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_GETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
+
+ .get_optname = 123,
+
+ .get_optlen = 1,
+ },
+ {
+ .descr = "getsockopt: read ctx->retval",
+ .insns = {
+ /* r6 = ctx->retval */
+ BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
+ offsetof(struct bpf_sockopt, retval)),
+
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_GETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
+
+ .get_level = SOL_IP,
+ .get_optname = IP_TOS,
+ .get_optlen = 1,
+ },
+ {
+ .descr = "getsockopt: deny writing to ctx->optname",
+ .insns = {
+ /* ctx->optname = 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, optname)),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_GETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
+
+ .error = DENY_LOAD,
+ },
+ {
+ .descr = "getsockopt: read ctx->optlen",
+ .insns = {
+ /* r6 = ctx->optlen */
+ BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
+ offsetof(struct bpf_sockopt, optlen)),
+
+ /* if (ctx->optlen == 64) { */
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
+ /* ctx->retval = 0 */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, retval)),
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_JMP_A(1),
+ /* } else { */
+ /* return 0 */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ /* } */
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_GETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
+
+ .get_optlen = 64,
+ },
+ {
+ .descr = "getsockopt: deny bigger ctx->optlen",
+ .insns = {
+ /* ctx->optlen = 65 */
+ BPF_MOV64_IMM(BPF_REG_0, 65),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, optlen)),
+
+ /* ctx->retval = 0 */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, retval)),
+
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_GETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
+
+ .get_optlen = 64,
+
+ .error = EFAULT_GETSOCKOPT,
+ },
+ {
+ .descr = "getsockopt: deny arbitrary ctx->retval",
+ .insns = {
+ /* ctx->retval = 123 */
+ BPF_MOV64_IMM(BPF_REG_0, 123),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, retval)),
+
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_GETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
+
+ .get_optlen = 64,
+
+ .error = EFAULT_GETSOCKOPT,
+ },
+ {
+ .descr = "getsockopt: support smaller ctx->optlen",
+ .insns = {
+ /* ctx->optlen = 32 */
+ BPF_MOV64_IMM(BPF_REG_0, 32),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, optlen)),
+ /* ctx->retval = 0 */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, retval)),
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_GETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
+
+ .get_optlen = 64,
+ .get_optlen_ret = 32,
+ },
+ {
+ .descr = "getsockopt: deny writing to ctx->optval",
+ .insns = {
+ /* ctx->optval = 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, optval)),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_GETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
+
+ .error = DENY_LOAD,
+ },
+ {
+ .descr = "getsockopt: deny writing to ctx->optval_end",
+ .insns = {
+ /* ctx->optval_end = 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, optval_end)),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_GETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
+
+ .error = DENY_LOAD,
+ },
+ {
+ .descr = "getsockopt: rewrite value",
+ .insns = {
+ /* r6 = ctx->optval */
+ BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
+ offsetof(struct bpf_sockopt, optval)),
+ /* r2 = ctx->optval */
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
+ /* r6 = ctx->optval + 1 */
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
+
+ /* r7 = ctx->optval_end */
+ BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
+ offsetof(struct bpf_sockopt, optval_end)),
+
+ /* if (ctx->optval + 1 <= ctx->optval_end) { */
+ BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
+ /* ctx->optval[0] = 0xF0 */
+ BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xF0),
+ /* } */
+
+ /* ctx->retval = 0 */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, retval)),
+
+ /* return 1*/
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_GETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
+
+ .get_level = SOL_IP,
+ .get_optname = IP_TOS,
+
+ .get_optval = { 0xF0 },
+ .get_optlen = 1,
+ },
+
+ /* ==================== setsockopt ==================== */
+
+ {
+ .descr = "setsockopt: no expected_attach_type",
+ .insns = {
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = 0,
+ .error = DENY_LOAD,
+ },
+ {
+ .descr = "setsockopt: wrong expected_attach_type",
+ .insns = {
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
+ .error = DENY_ATTACH,
+ },
+ {
+ .descr = "setsockopt: bypass bpf hook",
+ .insns = {
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+
+ .get_level = SOL_IP,
+ .set_level = SOL_IP,
+
+ .get_optname = IP_TOS,
+ .set_optname = IP_TOS,
+
+ .set_optval = { 1 << 3 },
+ .set_optlen = 1,
+
+ .get_optval = { 1 << 3 },
+ .get_optlen = 1,
+ },
+ {
+ .descr = "setsockopt: return EPERM from bpf hook",
+ .insns = {
+ /* return 0 */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+
+ .set_level = SOL_IP,
+ .set_optname = IP_TOS,
+
+ .set_optlen = 1,
+ .error = EPERM_SETSOCKOPT,
+ },
+ {
+ .descr = "setsockopt: no optval bounds check, deny loading",
+ .insns = {
+ /* r6 = ctx->optval */
+ BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
+ offsetof(struct bpf_sockopt, optval)),
+
+ /* r0 = ctx->optval[0] */
+ BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),
+
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+ .error = DENY_LOAD,
+ },
+ {
+ .descr = "setsockopt: read ctx->level",
+ .insns = {
+ /* r6 = ctx->level */
+ BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
+ offsetof(struct bpf_sockopt, level)),
+
+ /* if (ctx->level == 123) { */
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
+ /* ctx->optlen = -1 */
+ BPF_MOV64_IMM(BPF_REG_0, -1),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, optlen)),
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_JMP_A(1),
+ /* } else { */
+ /* return 0 */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ /* } */
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+
+ .set_level = 123,
+
+ .set_optlen = 1,
+ },
+ {
+ .descr = "setsockopt: allow changing ctx->level",
+ .insns = {
+ /* ctx->level = SOL_IP */
+ BPF_MOV64_IMM(BPF_REG_0, SOL_IP),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, level)),
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+
+ .get_level = SOL_IP,
+ .set_level = 234, /* should be rewritten to SOL_IP */
+
+ .get_optname = IP_TOS,
+ .set_optname = IP_TOS,
+
+ .set_optval = { 1 << 3 },
+ .set_optlen = 1,
+ .get_optval = { 1 << 3 },
+ .get_optlen = 1,
+ },
+ {
+ .descr = "setsockopt: read ctx->optname",
+ .insns = {
+ /* r6 = ctx->optname */
+ BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
+ offsetof(struct bpf_sockopt, optname)),
+
+ /* if (ctx->optname == 123) { */
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
+ /* ctx->optlen = -1 */
+ BPF_MOV64_IMM(BPF_REG_0, -1),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, optlen)),
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_JMP_A(1),
+ /* } else { */
+ /* return 0 */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ /* } */
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+
+ .set_optname = 123,
+
+ .set_optlen = 1,
+ },
+ {
+ .descr = "setsockopt: allow changing ctx->optname",
+ .insns = {
+ /* ctx->optname = IP_TOS */
+ BPF_MOV64_IMM(BPF_REG_0, IP_TOS),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, optname)),
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+
+ .get_level = SOL_IP,
+ .set_level = SOL_IP,
+
+ .get_optname = IP_TOS,
+ .set_optname = 456, /* should be rewritten to IP_TOS */
+
+ .set_optval = { 1 << 3 },
+ .set_optlen = 1,
+ .get_optval = { 1 << 3 },
+ .get_optlen = 1,
+ },
+ {
+ .descr = "setsockopt: read ctx->optlen",
+ .insns = {
+ /* r6 = ctx->optlen */
+ BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
+ offsetof(struct bpf_sockopt, optlen)),
+
+ /* if (ctx->optlen == 64) { */
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
+ /* ctx->optlen = -1 */
+ BPF_MOV64_IMM(BPF_REG_0, -1),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, optlen)),
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_JMP_A(1),
+ /* } else { */
+ /* return 0 */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ /* } */
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+
+ .set_optlen = 64,
+ },
+ {
+ .descr = "setsockopt: ctx->optlen == -1 is ok",
+ .insns = {
+ /* ctx->optlen = -1 */
+ BPF_MOV64_IMM(BPF_REG_0, -1),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, optlen)),
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+
+ .set_optlen = 64,
+ },
+ {
+ .descr = "setsockopt: deny ctx->optlen < 0 (except -1)",
+ .insns = {
+ /* ctx->optlen = -2 */
+ BPF_MOV64_IMM(BPF_REG_0, -2),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, optlen)),
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+
+ .set_optlen = 4,
+
+ .error = EFAULT_SETSOCKOPT,
+ },
+ {
+ .descr = "setsockopt: deny ctx->optlen > input optlen",
+ .insns = {
+ /* ctx->optlen = 65 */
+ BPF_MOV64_IMM(BPF_REG_0, 65),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, optlen)),
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+
+ .set_optlen = 64,
+
+ .error = EFAULT_SETSOCKOPT,
+ },
+ {
+ .descr = "setsockopt: allow changing ctx->optlen within bounds",
+ .insns = {
+ /* r6 = ctx->optval */
+ BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
+ offsetof(struct bpf_sockopt, optval)),
+ /* r2 = ctx->optval */
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
+ /* r6 = ctx->optval + 1 */
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
+
+ /* r7 = ctx->optval_end */
+ BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
+ offsetof(struct bpf_sockopt, optval_end)),
+
+ /* if (ctx->optval + 1 <= ctx->optval_end) { */
+ BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
+ /* ctx->optval[0] = 1 << 3 */
+ BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 1 << 3),
+ /* } */
+
+ /* ctx->optlen = 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, optlen)),
+
+ /* return 1*/
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+
+ .get_level = SOL_IP,
+ .set_level = SOL_IP,
+
+ .get_optname = IP_TOS,
+ .set_optname = IP_TOS,
+
+ .set_optval = { 1, 1, 1, 1 },
+ .set_optlen = 4,
+ .get_optval = { 1 << 3 },
+ .get_optlen = 1,
+ },
+ {
+ .descr = "setsockopt: deny write ctx->retval",
+ .insns = {
+ /* ctx->retval = 0 */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, retval)),
+
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+
+ .error = DENY_LOAD,
+ },
+ {
+ .descr = "setsockopt: deny read ctx->retval",
+ .insns = {
+ /* r6 = ctx->retval */
+ BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
+ offsetof(struct bpf_sockopt, retval)),
+
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+
+ .error = DENY_LOAD,
+ },
+ {
+ .descr = "setsockopt: deny writing to ctx->optval",
+ .insns = {
+ /* ctx->optval = 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, optval)),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+
+ .error = DENY_LOAD,
+ },
+ {
+ .descr = "setsockopt: deny writing to ctx->optval_end",
+ .insns = {
+ /* ctx->optval_end = 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
+ offsetof(struct bpf_sockopt, optval_end)),
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+
+ .error = DENY_LOAD,
+ },
+ {
+ .descr = "setsockopt: allow IP_TOS <= 128",
+ .insns = {
+ /* r6 = ctx->optval */
+ BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
+ offsetof(struct bpf_sockopt, optval)),
+ /* r7 = ctx->optval + 1 */
+ BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
+
+ /* r8 = ctx->optval_end */
+ BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
+ offsetof(struct bpf_sockopt, optval_end)),
+
+ /* if (ctx->optval + 1 <= ctx->optval_end) { */
+ BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
+
+ /* r9 = ctx->optval[0] */
+ BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
+
+ /* if (ctx->optval[0] < 128) */
+ BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_JMP_A(1),
+ /* } */
+
+ /* } else { */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ /* } */
+
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+
+ .get_level = SOL_IP,
+ .set_level = SOL_IP,
+
+ .get_optname = IP_TOS,
+ .set_optname = IP_TOS,
+
+ .set_optval = { 0x80 },
+ .set_optlen = 1,
+ .get_optval = { 0x80 },
+ .get_optlen = 1,
+ },
+ {
+ .descr = "setsockopt: deny IP_TOS > 128",
+ .insns = {
+ /* r6 = ctx->optval */
+ BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
+ offsetof(struct bpf_sockopt, optval)),
+ /* r7 = ctx->optval + 1 */
+ BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
+
+ /* r8 = ctx->optval_end */
+ BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
+ offsetof(struct bpf_sockopt, optval_end)),
+
+ /* if (ctx->optval + 1 <= ctx->optval_end) { */
+ BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
+
+ /* r9 = ctx->optval[0] */
+ BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
+
+ /* if (ctx->optval[0] < 128) */
+ BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_JMP_A(1),
+ /* } */
+
+ /* } else { */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ /* } */
+
+ BPF_EXIT_INSN(),
+ },
+ .attach_type = BPF_CGROUP_SETSOCKOPT,
+ .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
+
+ .get_level = SOL_IP,
+ .set_level = SOL_IP,
+
+ .get_optname = IP_TOS,
+ .set_optname = IP_TOS,
+
+ .set_optval = { 0x81 },
+ .set_optlen = 1,
+ .get_optval = { 0x00 },
+ .get_optlen = 1,
+
+ .error = EPERM_SETSOCKOPT,
+ },
+};
+
+static int load_prog(const struct bpf_insn *insns,
+ enum bpf_attach_type expected_attach_type)
+{
+ struct bpf_load_program_attr attr = {
+ .prog_type = BPF_PROG_TYPE_CGROUP_SOCKOPT,
+ .expected_attach_type = expected_attach_type,
+ .insns = insns,
+ .license = "GPL",
+ .log_level = 2,
+ };
+ int fd;
+
+ for (;
+ insns[attr.insns_cnt].code != (BPF_JMP | BPF_EXIT);
+ attr.insns_cnt++) {
+ }
+ attr.insns_cnt++;
+
+ fd = bpf_load_program_xattr(&attr, bpf_log_buf, sizeof(bpf_log_buf));
+ if (verbose && fd < 0)
+ fprintf(stderr, "%s\n", bpf_log_buf);
+
+ return fd;
+}
+
+static int run_test(int cgroup_fd, struct sockopt_test *test)
+{
+ int sock_fd, err, prog_fd;
+ void *optval = NULL;
+ int ret = 0;
+
+ prog_fd = load_prog(test->insns, test->expected_attach_type);
+ if (prog_fd < 0) {
+ if (test->error == DENY_LOAD)
+ return 0;
+
+ log_err("Failed to load BPF program");
+ return -1;
+ }
+
+ err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0);
+ if (err < 0) {
+ if (test->error == DENY_ATTACH)
+ goto close_prog_fd;
+
+ log_err("Failed to attach BPF program");
+ ret = -1;
+ goto close_prog_fd;
+ }
+
+ sock_fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock_fd < 0) {
+ log_err("Failed to create AF_INET socket");
+ ret = -1;
+ goto detach_prog;
+ }
+
+ if (test->set_optlen) {
+ err = setsockopt(sock_fd, test->set_level, test->set_optname,
+ test->set_optval, test->set_optlen);
+ if (err) {
+ if (errno == EPERM && test->error == EPERM_SETSOCKOPT)
+ goto close_sock_fd;
+ if (errno == EFAULT && test->error == EFAULT_SETSOCKOPT)
+ goto free_optval;
+
+ log_err("Failed to call setsockopt");
+ ret = -1;
+ goto close_sock_fd;
+ }
+ }
+
+ if (test->get_optlen) {
+ optval = malloc(test->get_optlen);
+ socklen_t optlen = test->get_optlen;
+ socklen_t expected_get_optlen = test->get_optlen_ret ?:
+ test->get_optlen;
+
+ err = getsockopt(sock_fd, test->get_level, test->get_optname,
+ optval, &optlen);
+ if (err) {
+ if (errno == EPERM && test->error == EPERM_GETSOCKOPT)
+ goto free_optval;
+ if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT)
+ goto free_optval;
+
+ log_err("Failed to call getsockopt");
+ ret = -1;
+ goto free_optval;
+ }
+
+ if (optlen != expected_get_optlen) {
+ errno = 0;
+ log_err("getsockopt returned unexpected optlen");
+ ret = -1;
+ goto free_optval;
+ }
+
+ if (memcmp(optval, test->get_optval, optlen) != 0) {
+ errno = 0;
+ log_err("getsockopt returned unexpected optval");
+ ret = -1;
+ goto free_optval;
+ }
+ }
+
+ ret = test->error != OK;
+
+free_optval:
+ free(optval);
+close_sock_fd:
+ close(sock_fd);
+detach_prog:
+ bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type);
+close_prog_fd:
+ close(prog_fd);
+ return ret;
+}
+
+void test_sockopt(void)
+{
+ int cgroup_fd, i;
+
+ cgroup_fd = test__join_cgroup("/sockopt");
+ if (CHECK_FAIL(cgroup_fd < 0))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ test__start_subtest(tests[i].descr);
+ CHECK_FAIL(run_test(cgroup_fd, &tests[i]));
+ }
+
+ close(cgroup_fd);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/sockopt_inherit.c b/tools/testing/selftests/bpf/prog_tests/sockopt_inherit.c
new file mode 100644
index 000000000000..6cbeea7b4bf1
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/sockopt_inherit.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include "cgroup_helpers.h"
+
+#define SOL_CUSTOM 0xdeadbeef
+#define CUSTOM_INHERIT1 0
+#define CUSTOM_INHERIT2 1
+#define CUSTOM_LISTENER 2
+
+static int connect_to_server(int server_fd)
+{
+ struct sockaddr_storage addr;
+ socklen_t len = sizeof(addr);
+ int fd;
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ log_err("Failed to create client socket");
+ return -1;
+ }
+
+ if (getsockname(server_fd, (struct sockaddr *)&addr, &len)) {
+ log_err("Failed to get server addr");
+ goto out;
+ }
+
+ if (connect(fd, (const struct sockaddr *)&addr, len) < 0) {
+ log_err("Fail to connect to server");
+ goto out;
+ }
+
+ return fd;
+
+out:
+ close(fd);
+ return -1;
+}
+
+static int verify_sockopt(int fd, int optname, const char *msg, char expected)
+{
+ socklen_t optlen = 1;
+ char buf = 0;
+ int err;
+
+ err = getsockopt(fd, SOL_CUSTOM, optname, &buf, &optlen);
+ if (err) {
+ log_err("%s: failed to call getsockopt", msg);
+ return 1;
+ }
+
+ printf("%s %d: got=0x%x ? expected=0x%x\n", msg, optname, buf, expected);
+
+ if (buf != expected) {
+ log_err("%s: unexpected getsockopt value %d != %d", msg,
+ buf, expected);
+ return 1;
+ }
+
+ return 0;
+}
+
+static pthread_mutex_t server_started_mtx = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t server_started = PTHREAD_COND_INITIALIZER;
+
+static void *server_thread(void *arg)
+{
+ struct sockaddr_storage addr;
+ socklen_t len = sizeof(addr);
+ int fd = *(int *)arg;
+ int client_fd;
+ int err = 0;
+
+ err = listen(fd, 1);
+
+ pthread_mutex_lock(&server_started_mtx);
+ pthread_cond_signal(&server_started);
+ pthread_mutex_unlock(&server_started_mtx);
+
+ if (CHECK_FAIL(err < 0)) {
+ perror("Failed to listed on socket");
+ return NULL;
+ }
+
+ err += verify_sockopt(fd, CUSTOM_INHERIT1, "listen", 1);
+ err += verify_sockopt(fd, CUSTOM_INHERIT2, "listen", 1);
+ err += verify_sockopt(fd, CUSTOM_LISTENER, "listen", 1);
+
+ client_fd = accept(fd, (struct sockaddr *)&addr, &len);
+ if (CHECK_FAIL(client_fd < 0)) {
+ perror("Failed to accept client");
+ return NULL;
+ }
+
+ err += verify_sockopt(client_fd, CUSTOM_INHERIT1, "accept", 1);
+ err += verify_sockopt(client_fd, CUSTOM_INHERIT2, "accept", 1);
+ err += verify_sockopt(client_fd, CUSTOM_LISTENER, "accept", 0);
+
+ close(client_fd);
+
+ return (void *)(long)err;
+}
+
+static int start_server(void)
+{
+ struct sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+ };
+ char buf;
+ int err;
+ int fd;
+ int i;
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ log_err("Failed to create server socket");
+ return -1;
+ }
+
+ for (i = CUSTOM_INHERIT1; i <= CUSTOM_LISTENER; i++) {
+ buf = 0x01;
+ err = setsockopt(fd, SOL_CUSTOM, i, &buf, 1);
+ if (err) {
+ log_err("Failed to call setsockopt(%d)", i);
+ close(fd);
+ return -1;
+ }
+ }
+
+ if (bind(fd, (const struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ log_err("Failed to bind socket");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static int prog_attach(struct bpf_object *obj, int cgroup_fd, const char *title)
+{
+ enum bpf_attach_type attach_type;
+ enum bpf_prog_type prog_type;
+ struct bpf_program *prog;
+ int err;
+
+ err = libbpf_prog_type_by_name(title, &prog_type, &attach_type);
+ if (err) {
+ log_err("Failed to deduct types for %s BPF program", title);
+ return -1;
+ }
+
+ prog = bpf_object__find_program_by_title(obj, title);
+ if (!prog) {
+ log_err("Failed to find %s BPF program", title);
+ return -1;
+ }
+
+ err = bpf_prog_attach(bpf_program__fd(prog), cgroup_fd,
+ attach_type, 0);
+ if (err) {
+ log_err("Failed to attach %s BPF program", title);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void run_test(int cgroup_fd)
+{
+ struct bpf_prog_load_attr attr = {
+ .file = "./sockopt_inherit.o",
+ };
+ int server_fd = -1, client_fd;
+ struct bpf_object *obj;
+ void *server_err;
+ pthread_t tid;
+ int ignored;
+ int err;
+
+ err = bpf_prog_load_xattr(&attr, &obj, &ignored);
+ if (CHECK_FAIL(err))
+ return;
+
+ err = prog_attach(obj, cgroup_fd, "cgroup/getsockopt");
+ if (CHECK_FAIL(err))
+ goto close_bpf_object;
+
+ err = prog_attach(obj, cgroup_fd, "cgroup/setsockopt");
+ if (CHECK_FAIL(err))
+ goto close_bpf_object;
+
+ server_fd = start_server();
+ if (CHECK_FAIL(server_fd < 0))
+ goto close_bpf_object;
+
+ if (CHECK_FAIL(pthread_create(&tid, NULL, server_thread,
+ (void *)&server_fd)))
+ goto close_bpf_object;
+
+ pthread_mutex_lock(&server_started_mtx);
+ pthread_cond_wait(&server_started, &server_started_mtx);
+ pthread_mutex_unlock(&server_started_mtx);
+
+ client_fd = connect_to_server(server_fd);
+ if (CHECK_FAIL(client_fd < 0))
+ goto close_server_fd;
+
+ CHECK_FAIL(verify_sockopt(client_fd, CUSTOM_INHERIT1, "connect", 0));
+ CHECK_FAIL(verify_sockopt(client_fd, CUSTOM_INHERIT2, "connect", 0));
+ CHECK_FAIL(verify_sockopt(client_fd, CUSTOM_LISTENER, "connect", 0));
+
+ pthread_join(tid, &server_err);
+
+ err = (int)(long)server_err;
+ CHECK_FAIL(err);
+
+ close(client_fd);
+
+close_server_fd:
+ close(server_fd);
+close_bpf_object:
+ bpf_object__close(obj);
+}
+
+void test_sockopt_inherit(void)
+{
+ int cgroup_fd;
+
+ cgroup_fd = test__join_cgroup("/sockopt_inherit");
+ if (CHECK_FAIL(cgroup_fd < 0))
+ return;
+
+ run_test(cgroup_fd);
+ close(cgroup_fd);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/sockopt_multi.c b/tools/testing/selftests/bpf/prog_tests/sockopt_multi.c
new file mode 100644
index 000000000000..29188d6f5c8d
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/sockopt_multi.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include "cgroup_helpers.h"
+
+static int prog_attach(struct bpf_object *obj, int cgroup_fd, const char *title)
+{
+ enum bpf_attach_type attach_type;
+ enum bpf_prog_type prog_type;
+ struct bpf_program *prog;
+ int err;
+
+ err = libbpf_prog_type_by_name(title, &prog_type, &attach_type);
+ if (err) {
+ log_err("Failed to deduct types for %s BPF program", title);
+ return -1;
+ }
+
+ prog = bpf_object__find_program_by_title(obj, title);
+ if (!prog) {
+ log_err("Failed to find %s BPF program", title);
+ return -1;
+ }
+
+ err = bpf_prog_attach(bpf_program__fd(prog), cgroup_fd,
+ attach_type, BPF_F_ALLOW_MULTI);
+ if (err) {
+ log_err("Failed to attach %s BPF program", title);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int prog_detach(struct bpf_object *obj, int cgroup_fd, const char *title)
+{
+ enum bpf_attach_type attach_type;
+ enum bpf_prog_type prog_type;
+ struct bpf_program *prog;
+ int err;
+
+ err = libbpf_prog_type_by_name(title, &prog_type, &attach_type);
+ if (err)
+ return -1;
+
+ prog = bpf_object__find_program_by_title(obj, title);
+ if (!prog)
+ return -1;
+
+ err = bpf_prog_detach2(bpf_program__fd(prog), cgroup_fd,
+ attach_type);
+ if (err)
+ return -1;
+
+ return 0;
+}
+
+static int run_getsockopt_test(struct bpf_object *obj, int cg_parent,
+ int cg_child, int sock_fd)
+{
+ socklen_t optlen;
+ __u8 buf;
+ int err;
+
+ /* Set IP_TOS to the expected value (0x80). */
+
+ buf = 0x80;
+ err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
+ if (err < 0) {
+ log_err("Failed to call setsockopt(IP_TOS)");
+ goto detach;
+ }
+
+ buf = 0x00;
+ optlen = 1;
+ err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
+ if (err) {
+ log_err("Failed to call getsockopt(IP_TOS)");
+ goto detach;
+ }
+
+ if (buf != 0x80) {
+ log_err("Unexpected getsockopt 0x%x != 0x80 without BPF", buf);
+ err = -1;
+ goto detach;
+ }
+
+ /* Attach child program and make sure it returns new value:
+ * - kernel: -> 0x80
+ * - child: 0x80 -> 0x90
+ */
+
+ err = prog_attach(obj, cg_child, "cgroup/getsockopt/child");
+ if (err)
+ goto detach;
+
+ buf = 0x00;
+ optlen = 1;
+ err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
+ if (err) {
+ log_err("Failed to call getsockopt(IP_TOS)");
+ goto detach;
+ }
+
+ if (buf != 0x90) {
+ log_err("Unexpected getsockopt 0x%x != 0x90", buf);
+ err = -1;
+ goto detach;
+ }
+
+ /* Attach parent program and make sure it returns new value:
+ * - kernel: -> 0x80
+ * - child: 0x80 -> 0x90
+ * - parent: 0x90 -> 0xA0
+ */
+
+ err = prog_attach(obj, cg_parent, "cgroup/getsockopt/parent");
+ if (err)
+ goto detach;
+
+ buf = 0x00;
+ optlen = 1;
+ err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
+ if (err) {
+ log_err("Failed to call getsockopt(IP_TOS)");
+ goto detach;
+ }
+
+ if (buf != 0xA0) {
+ log_err("Unexpected getsockopt 0x%x != 0xA0", buf);
+ err = -1;
+ goto detach;
+ }
+
+ /* Setting unexpected initial sockopt should return EPERM:
+ * - kernel: -> 0x40
+ * - child: unexpected 0x40, EPERM
+ * - parent: unexpected 0x40, EPERM
+ */
+
+ buf = 0x40;
+ if (setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1) < 0) {
+ log_err("Failed to call setsockopt(IP_TOS)");
+ goto detach;
+ }
+
+ buf = 0x00;
+ optlen = 1;
+ err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
+ if (!err) {
+ log_err("Unexpected success from getsockopt(IP_TOS)");
+ goto detach;
+ }
+
+ /* Detach child program and make sure we still get EPERM:
+ * - kernel: -> 0x40
+ * - parent: unexpected 0x40, EPERM
+ */
+
+ err = prog_detach(obj, cg_child, "cgroup/getsockopt/child");
+ if (err) {
+ log_err("Failed to detach child program");
+ goto detach;
+ }
+
+ buf = 0x00;
+ optlen = 1;
+ err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
+ if (!err) {
+ log_err("Unexpected success from getsockopt(IP_TOS)");
+ goto detach;
+ }
+
+ /* Set initial value to the one the parent program expects:
+ * - kernel: -> 0x90
+ * - parent: 0x90 -> 0xA0
+ */
+
+ buf = 0x90;
+ err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
+ if (err < 0) {
+ log_err("Failed to call setsockopt(IP_TOS)");
+ goto detach;
+ }
+
+ buf = 0x00;
+ optlen = 1;
+ err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
+ if (err) {
+ log_err("Failed to call getsockopt(IP_TOS)");
+ goto detach;
+ }
+
+ if (buf != 0xA0) {
+ log_err("Unexpected getsockopt 0x%x != 0xA0", buf);
+ err = -1;
+ goto detach;
+ }
+
+detach:
+ prog_detach(obj, cg_child, "cgroup/getsockopt/child");
+ prog_detach(obj, cg_parent, "cgroup/getsockopt/parent");
+
+ return err;
+}
+
+static int run_setsockopt_test(struct bpf_object *obj, int cg_parent,
+ int cg_child, int sock_fd)
+{
+ socklen_t optlen;
+ __u8 buf;
+ int err;
+
+ /* Set IP_TOS to the expected value (0x80). */
+
+ buf = 0x80;
+ err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
+ if (err < 0) {
+ log_err("Failed to call setsockopt(IP_TOS)");
+ goto detach;
+ }
+
+ buf = 0x00;
+ optlen = 1;
+ err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
+ if (err) {
+ log_err("Failed to call getsockopt(IP_TOS)");
+ goto detach;
+ }
+
+ if (buf != 0x80) {
+ log_err("Unexpected getsockopt 0x%x != 0x80 without BPF", buf);
+ err = -1;
+ goto detach;
+ }
+
+ /* Attach child program and make sure it adds 0x10. */
+
+ err = prog_attach(obj, cg_child, "cgroup/setsockopt");
+ if (err)
+ goto detach;
+
+ buf = 0x80;
+ err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
+ if (err < 0) {
+ log_err("Failed to call setsockopt(IP_TOS)");
+ goto detach;
+ }
+
+ buf = 0x00;
+ optlen = 1;
+ err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
+ if (err) {
+ log_err("Failed to call getsockopt(IP_TOS)");
+ goto detach;
+ }
+
+ if (buf != 0x80 + 0x10) {
+ log_err("Unexpected getsockopt 0x%x != 0x80 + 0x10", buf);
+ err = -1;
+ goto detach;
+ }
+
+ /* Attach parent program and make sure it adds another 0x10. */
+
+ err = prog_attach(obj, cg_parent, "cgroup/setsockopt");
+ if (err)
+ goto detach;
+
+ buf = 0x80;
+ err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
+ if (err < 0) {
+ log_err("Failed to call setsockopt(IP_TOS)");
+ goto detach;
+ }
+
+ buf = 0x00;
+ optlen = 1;
+ err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
+ if (err) {
+ log_err("Failed to call getsockopt(IP_TOS)");
+ goto detach;
+ }
+
+ if (buf != 0x80 + 2 * 0x10) {
+ log_err("Unexpected getsockopt 0x%x != 0x80 + 2 * 0x10", buf);
+ err = -1;
+ goto detach;
+ }
+
+detach:
+ prog_detach(obj, cg_child, "cgroup/setsockopt");
+ prog_detach(obj, cg_parent, "cgroup/setsockopt");
+
+ return err;
+}
+
+void test_sockopt_multi(void)
+{
+ struct bpf_prog_load_attr attr = {
+ .file = "./sockopt_multi.o",
+ };
+ int cg_parent = -1, cg_child = -1;
+ struct bpf_object *obj = NULL;
+ int sock_fd = -1;
+ int err = -1;
+ int ignored;
+
+ cg_parent = test__join_cgroup("/parent");
+ if (CHECK_FAIL(cg_parent < 0))
+ goto out;
+
+ cg_child = test__join_cgroup("/parent/child");
+ if (CHECK_FAIL(cg_child < 0))
+ goto out;
+
+ err = bpf_prog_load_xattr(&attr, &obj, &ignored);
+ if (CHECK_FAIL(err))
+ goto out;
+
+ sock_fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (CHECK_FAIL(sock_fd < 0))
+ goto out;
+
+ CHECK_FAIL(run_getsockopt_test(obj, cg_parent, cg_child, sock_fd));
+ CHECK_FAIL(run_setsockopt_test(obj, cg_parent, cg_child, sock_fd));
+
+out:
+ close(sock_fd);
+ bpf_object__close(obj);
+ close(cg_child);
+ close(cg_parent);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c b/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c
new file mode 100644
index 000000000000..2061a6beac0f
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include "cgroup_helpers.h"
+
+#define SOL_CUSTOM 0xdeadbeef
+
+static int getsetsockopt(void)
+{
+ int fd, err;
+ union {
+ char u8[4];
+ __u32 u32;
+ char cc[16]; /* TCP_CA_NAME_MAX */
+ } buf = {};
+ socklen_t optlen;
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ log_err("Failed to create socket");
+ return -1;
+ }
+
+ /* IP_TOS - BPF bypass */
+
+ buf.u8[0] = 0x08;
+ err = setsockopt(fd, SOL_IP, IP_TOS, &buf, 1);
+ if (err) {
+ log_err("Failed to call setsockopt(IP_TOS)");
+ goto err;
+ }
+
+ buf.u8[0] = 0x00;
+ optlen = 1;
+ err = getsockopt(fd, SOL_IP, IP_TOS, &buf, &optlen);
+ if (err) {
+ log_err("Failed to call getsockopt(IP_TOS)");
+ goto err;
+ }
+
+ if (buf.u8[0] != 0x08) {
+ log_err("Unexpected getsockopt(IP_TOS) buf[0] 0x%02x != 0x08",
+ buf.u8[0]);
+ goto err;
+ }
+
+ /* IP_TTL - EPERM */
+
+ buf.u8[0] = 1;
+ err = setsockopt(fd, SOL_IP, IP_TTL, &buf, 1);
+ if (!err || errno != EPERM) {
+ log_err("Unexpected success from setsockopt(IP_TTL)");
+ goto err;
+ }
+
+ /* SOL_CUSTOM - handled by BPF */
+
+ buf.u8[0] = 0x01;
+ err = setsockopt(fd, SOL_CUSTOM, 0, &buf, 1);
+ if (err) {
+ log_err("Failed to call setsockopt");
+ goto err;
+ }
+
+ buf.u32 = 0x00;
+ optlen = 4;
+ err = getsockopt(fd, SOL_CUSTOM, 0, &buf, &optlen);
+ if (err) {
+ log_err("Failed to call getsockopt");
+ goto err;
+ }
+
+ if (optlen != 1) {
+ log_err("Unexpected optlen %d != 1", optlen);
+ goto err;
+ }
+ if (buf.u8[0] != 0x01) {
+ log_err("Unexpected buf[0] 0x%02x != 0x01", buf.u8[0]);
+ goto err;
+ }
+
+ /* SO_SNDBUF is overwritten */
+
+ buf.u32 = 0x01010101;
+ err = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buf, 4);
+ if (err) {
+ log_err("Failed to call setsockopt(SO_SNDBUF)");
+ goto err;
+ }
+
+ buf.u32 = 0x00;
+ optlen = 4;
+ err = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buf, &optlen);
+ if (err) {
+ log_err("Failed to call getsockopt(SO_SNDBUF)");
+ goto err;
+ }
+
+ if (buf.u32 != 0x55AA*2) {
+ log_err("Unexpected getsockopt(SO_SNDBUF) 0x%x != 0x55AA*2",
+ buf.u32);
+ goto err;
+ }
+
+ /* TCP_CONGESTION can extend the string */
+
+ strcpy(buf.cc, "nv");
+ err = setsockopt(fd, SOL_TCP, TCP_CONGESTION, &buf, strlen("nv"));
+ if (err) {
+ log_err("Failed to call setsockopt(TCP_CONGESTION)");
+ goto err;
+ }
+
+
+ optlen = sizeof(buf.cc);
+ err = getsockopt(fd, SOL_TCP, TCP_CONGESTION, &buf, &optlen);
+ if (err) {
+ log_err("Failed to call getsockopt(TCP_CONGESTION)");
+ goto err;
+ }
+
+ if (strcmp(buf.cc, "cubic") != 0) {
+ log_err("Unexpected getsockopt(TCP_CONGESTION) %s != %s",
+ buf.cc, "cubic");
+ goto err;
+ }
+
+ close(fd);
+ return 0;
+err:
+ close(fd);
+ return -1;
+}
+
+static int prog_attach(struct bpf_object *obj, int cgroup_fd, const char *title)
+{
+ enum bpf_attach_type attach_type;
+ enum bpf_prog_type prog_type;
+ struct bpf_program *prog;
+ int err;
+
+ err = libbpf_prog_type_by_name(title, &prog_type, &attach_type);
+ if (err) {
+ log_err("Failed to deduct types for %s BPF program", title);
+ return -1;
+ }
+
+ prog = bpf_object__find_program_by_title(obj, title);
+ if (!prog) {
+ log_err("Failed to find %s BPF program", title);
+ return -1;
+ }
+
+ err = bpf_prog_attach(bpf_program__fd(prog), cgroup_fd,
+ attach_type, 0);
+ if (err) {
+ log_err("Failed to attach %s BPF program", title);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void run_test(int cgroup_fd)
+{
+ struct bpf_prog_load_attr attr = {
+ .file = "./sockopt_sk.o",
+ };
+ struct bpf_object *obj;
+ int ignored;
+ int err;
+
+ err = bpf_prog_load_xattr(&attr, &obj, &ignored);
+ if (CHECK_FAIL(err))
+ return;
+
+ err = prog_attach(obj, cgroup_fd, "cgroup/getsockopt");
+ if (CHECK_FAIL(err))
+ goto close_bpf_object;
+
+ err = prog_attach(obj, cgroup_fd, "cgroup/setsockopt");
+ if (CHECK_FAIL(err))
+ goto close_bpf_object;
+
+ CHECK_FAIL(getsetsockopt());
+
+close_bpf_object:
+ bpf_object__close(obj);
+}
+
+void test_sockopt_sk(void)
+{
+ int cgroup_fd;
+
+ cgroup_fd = test__join_cgroup("/sockopt_sk");
+ if (CHECK_FAIL(cgroup_fd < 0))
+ return;
+
+ run_test(cgroup_fd);
+ close(cgroup_fd);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/spinlock.c b/tools/testing/selftests/bpf/prog_tests/spinlock.c
index 114ebe6a438e..1ae00cd3174e 100644
--- a/tools/testing/selftests/bpf/prog_tests/spinlock.c
+++ b/tools/testing/selftests/bpf/prog_tests/spinlock.c
@@ -11,19 +11,19 @@ void test_spinlock(void)
void *ret;
err = bpf_prog_load(file, BPF_PROG_TYPE_CGROUP_SKB, &obj, &prog_fd);
- if (err) {
+ if (CHECK_FAIL(err)) {
printf("test_spin_lock:bpf_prog_load errno %d\n", errno);
goto close_prog;
}
for (i = 0; i < 4; i++)
- assert(pthread_create(&thread_id[i], NULL,
- &spin_lock_thread, &prog_fd) == 0);
+ if (CHECK_FAIL(pthread_create(&thread_id[i], NULL,
+ &spin_lock_thread, &prog_fd)))
+ goto close_prog;
+
for (i = 0; i < 4; i++)
- assert(pthread_join(thread_id[i], &ret) == 0 &&
- ret == (void *)&prog_fd);
- goto close_prog_noerr;
+ if (CHECK_FAIL(pthread_join(thread_id[i], &ret) ||
+ ret != (void *)&prog_fd))
+ goto close_prog;
close_prog:
- error_cnt++;
-close_prog_noerr:
bpf_object__close(obj);
}
diff --git a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c
index 3aab2b083c71..d841dced971f 100644
--- a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c
+++ b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c
@@ -4,11 +4,13 @@
void test_stacktrace_build_id(void)
{
int control_map_fd, stackid_hmap_fd, stackmap_fd, stack_amap_fd;
+ const char *prog_name = "tracepoint/random/urandom_read";
const char *file = "./test_stacktrace_build_id.o";
- int bytes, efd, err, pmu_fd, prog_fd, stack_trace_len;
- struct perf_event_attr attr = {};
+ int err, prog_fd, stack_trace_len;
__u32 key, previous_key, val, duration = 0;
+ struct bpf_program *prog;
struct bpf_object *obj;
+ struct bpf_link *link = NULL;
char buf[256];
int i, j;
struct bpf_stack_build_id id_offs[PERF_MAX_STACK_DEPTH];
@@ -18,44 +20,16 @@ void test_stacktrace_build_id(void)
retry:
err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd);
if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno))
- goto out;
+ return;
- /* Get the ID for the sched/sched_switch tracepoint */
- snprintf(buf, sizeof(buf),
- "/sys/kernel/debug/tracing/events/random/urandom_read/id");
- efd = open(buf, O_RDONLY, 0);
- if (CHECK(efd < 0, "open", "err %d errno %d\n", efd, errno))
+ prog = bpf_object__find_program_by_title(obj, prog_name);
+ if (CHECK(!prog, "find_prog", "prog '%s' not found\n", prog_name))
goto close_prog;
- bytes = read(efd, buf, sizeof(buf));
- close(efd);
- if (CHECK(bytes <= 0 || bytes >= sizeof(buf),
- "read", "bytes %d errno %d\n", bytes, errno))
+ link = bpf_program__attach_tracepoint(prog, "random", "urandom_read");
+ if (CHECK(IS_ERR(link), "attach_tp", "err %ld\n", PTR_ERR(link)))
goto close_prog;
- /* Open the perf event and attach bpf progrram */
- attr.config = strtol(buf, NULL, 0);
- attr.type = PERF_TYPE_TRACEPOINT;
- attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_CALLCHAIN;
- attr.sample_period = 1;
- attr.wakeup_events = 1;
- pmu_fd = syscall(__NR_perf_event_open, &attr, -1 /* pid */,
- 0 /* cpu 0 */, -1 /* group id */,
- 0 /* flags */);
- if (CHECK(pmu_fd < 0, "perf_event_open", "err %d errno %d\n",
- pmu_fd, errno))
- goto close_prog;
-
- err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
- if (CHECK(err, "perf_event_ioc_enable", "err %d errno %d\n",
- err, errno))
- goto close_pmu;
-
- err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd);
- if (CHECK(err, "perf_event_ioc_set_bpf", "err %d errno %d\n",
- err, errno))
- goto disable_pmu;
-
/* find map fds */
control_map_fd = bpf_find_map(__func__, obj, "control_map");
if (CHECK(control_map_fd < 0, "bpf_find_map control_map",
@@ -77,9 +51,10 @@ retry:
"err %d errno %d\n", err, errno))
goto disable_pmu;
- assert(system("dd if=/dev/urandom of=/dev/zero count=4 2> /dev/null")
- == 0);
- assert(system("./urandom_read") == 0);
+ if (CHECK_FAIL(system("dd if=/dev/urandom of=/dev/zero count=4 2> /dev/null")))
+ goto disable_pmu;
+ if (CHECK_FAIL(system("./urandom_read")))
+ goto disable_pmu;
/* disable stack trace collection */
key = 0;
val = 1;
@@ -133,8 +108,7 @@ retry:
* try it one more time.
*/
if (build_id_matches < 1 && retry--) {
- ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE);
- close(pmu_fd);
+ bpf_link__destroy(link);
bpf_object__close(obj);
printf("%s:WARN:Didn't find expected build ID from the map, retrying\n",
__func__);
@@ -152,14 +126,8 @@ retry:
"err %d errno %d\n", err, errno);
disable_pmu:
- ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE);
-
-close_pmu:
- close(pmu_fd);
+ bpf_link__destroy(link);
close_prog:
bpf_object__close(obj);
-
-out:
- return;
}
diff --git a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c
index 1c1a2f75f3d8..f62aa0eb959b 100644
--- a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c
+++ b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c
@@ -17,6 +17,7 @@ static __u64 read_perf_max_sample_freq(void)
void test_stacktrace_build_id_nmi(void)
{
int control_map_fd, stackid_hmap_fd, stackmap_fd, stack_amap_fd;
+ const char *prog_name = "tracepoint/random/urandom_read";
const char *file = "./test_stacktrace_build_id.o";
int err, pmu_fd, prog_fd;
struct perf_event_attr attr = {
@@ -25,7 +26,9 @@ void test_stacktrace_build_id_nmi(void)
.config = PERF_COUNT_HW_CPU_CYCLES,
};
__u32 key, previous_key, val, duration = 0;
+ struct bpf_program *prog;
struct bpf_object *obj;
+ struct bpf_link *link;
char buf[256];
int i, j;
struct bpf_stack_build_id id_offs[PERF_MAX_STACK_DEPTH];
@@ -39,6 +42,10 @@ retry:
if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno))
return;
+ prog = bpf_object__find_program_by_title(obj, prog_name);
+ if (CHECK(!prog, "find_prog", "prog '%s' not found\n", prog_name))
+ goto close_prog;
+
pmu_fd = syscall(__NR_perf_event_open, &attr, -1 /* pid */,
0 /* cpu 0 */, -1 /* group id */,
0 /* flags */);
@@ -47,15 +54,12 @@ retry:
pmu_fd, errno))
goto close_prog;
- err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
- if (CHECK(err, "perf_event_ioc_enable", "err %d errno %d\n",
- err, errno))
- goto close_pmu;
-
- err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd);
- if (CHECK(err, "perf_event_ioc_set_bpf", "err %d errno %d\n",
- err, errno))
- goto disable_pmu;
+ link = bpf_program__attach_perf_event(prog, pmu_fd);
+ if (CHECK(IS_ERR(link), "attach_perf_event",
+ "err %ld\n", PTR_ERR(link))) {
+ close(pmu_fd);
+ goto close_prog;
+ }
/* find map fds */
control_map_fd = bpf_find_map(__func__, obj, "control_map");
@@ -78,9 +82,10 @@ retry:
"err %d errno %d\n", err, errno))
goto disable_pmu;
- assert(system("dd if=/dev/urandom of=/dev/zero count=4 2> /dev/null")
- == 0);
- assert(system("taskset 0x1 ./urandom_read 100000") == 0);
+ if (CHECK_FAIL(system("dd if=/dev/urandom of=/dev/zero count=4 2> /dev/null")))
+ goto disable_pmu;
+ if (CHECK_FAIL(system("taskset 0x1 ./urandom_read 100000")))
+ goto disable_pmu;
/* disable stack trace collection */
key = 0;
val = 1;
@@ -134,8 +139,7 @@ retry:
* try it one more time.
*/
if (build_id_matches < 1 && retry--) {
- ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE);
- close(pmu_fd);
+ bpf_link__destroy(link);
bpf_object__close(obj);
printf("%s:WARN:Didn't find expected build ID from the map, retrying\n",
__func__);
@@ -154,11 +158,7 @@ retry:
*/
disable_pmu:
- ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE);
-
-close_pmu:
- close(pmu_fd);
-
+ bpf_link__destroy(link);
close_prog:
bpf_object__close(obj);
}
diff --git a/tools/testing/selftests/bpf/prog_tests/stacktrace_map.c b/tools/testing/selftests/bpf/prog_tests/stacktrace_map.c
index 2bfd50a0d6d1..37269d23df93 100644
--- a/tools/testing/selftests/bpf/prog_tests/stacktrace_map.c
+++ b/tools/testing/selftests/bpf/prog_tests/stacktrace_map.c
@@ -4,65 +4,41 @@
void test_stacktrace_map(void)
{
int control_map_fd, stackid_hmap_fd, stackmap_fd, stack_amap_fd;
+ const char *prog_name = "tracepoint/sched/sched_switch";
+ int err, prog_fd, stack_trace_len;
const char *file = "./test_stacktrace_map.o";
- int bytes, efd, err, pmu_fd, prog_fd, stack_trace_len;
- struct perf_event_attr attr = {};
__u32 key, val, duration = 0;
+ struct bpf_program *prog;
struct bpf_object *obj;
- char buf[256];
+ struct bpf_link *link;
err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd);
if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno))
return;
- /* Get the ID for the sched/sched_switch tracepoint */
- snprintf(buf, sizeof(buf),
- "/sys/kernel/debug/tracing/events/sched/sched_switch/id");
- efd = open(buf, O_RDONLY, 0);
- if (CHECK(efd < 0, "open", "err %d errno %d\n", efd, errno))
+ prog = bpf_object__find_program_by_title(obj, prog_name);
+ if (CHECK(!prog, "find_prog", "prog '%s' not found\n", prog_name))
goto close_prog;
- bytes = read(efd, buf, sizeof(buf));
- close(efd);
- if (bytes <= 0 || bytes >= sizeof(buf))
+ link = bpf_program__attach_tracepoint(prog, "sched", "sched_switch");
+ if (CHECK(IS_ERR(link), "attach_tp", "err %ld\n", PTR_ERR(link)))
goto close_prog;
- /* Open the perf event and attach bpf progrram */
- attr.config = strtol(buf, NULL, 0);
- attr.type = PERF_TYPE_TRACEPOINT;
- attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_CALLCHAIN;
- attr.sample_period = 1;
- attr.wakeup_events = 1;
- pmu_fd = syscall(__NR_perf_event_open, &attr, -1 /* pid */,
- 0 /* cpu 0 */, -1 /* group id */,
- 0 /* flags */);
- if (CHECK(pmu_fd < 0, "perf_event_open", "err %d errno %d\n",
- pmu_fd, errno))
- goto close_prog;
-
- err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
- if (err)
- goto disable_pmu;
-
- err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd);
- if (err)
- goto disable_pmu;
-
/* find map fds */
control_map_fd = bpf_find_map(__func__, obj, "control_map");
- if (control_map_fd < 0)
+ if (CHECK_FAIL(control_map_fd < 0))
goto disable_pmu;
stackid_hmap_fd = bpf_find_map(__func__, obj, "stackid_hmap");
- if (stackid_hmap_fd < 0)
+ if (CHECK_FAIL(stackid_hmap_fd < 0))
goto disable_pmu;
stackmap_fd = bpf_find_map(__func__, obj, "stackmap");
- if (stackmap_fd < 0)
+ if (CHECK_FAIL(stackmap_fd < 0))
goto disable_pmu;
stack_amap_fd = bpf_find_map(__func__, obj, "stack_amap");
- if (stack_amap_fd < 0)
+ if (CHECK_FAIL(stack_amap_fd < 0))
goto disable_pmu;
/* give some time for bpf program run */
@@ -79,25 +55,21 @@ void test_stacktrace_map(void)
err = compare_map_keys(stackid_hmap_fd, stackmap_fd);
if (CHECK(err, "compare_map_keys stackid_hmap vs. stackmap",
"err %d errno %d\n", err, errno))
- goto disable_pmu_noerr;
+ goto disable_pmu;
err = compare_map_keys(stackmap_fd, stackid_hmap_fd);
if (CHECK(err, "compare_map_keys stackmap vs. stackid_hmap",
"err %d errno %d\n", err, errno))
- goto disable_pmu_noerr;
+ goto disable_pmu;
stack_trace_len = PERF_MAX_STACK_DEPTH * sizeof(__u64);
err = compare_stack_ips(stackmap_fd, stack_amap_fd, stack_trace_len);
if (CHECK(err, "compare_stack_ips stackmap vs. stack_amap",
"err %d errno %d\n", err, errno))
- goto disable_pmu_noerr;
+ goto disable_pmu;
- goto disable_pmu_noerr;
disable_pmu:
- error_cnt++;
-disable_pmu_noerr:
- ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE);
- close(pmu_fd);
+ bpf_link__destroy(link);
close_prog:
bpf_object__close(obj);
}
diff --git a/tools/testing/selftests/bpf/prog_tests/stacktrace_map_raw_tp.c b/tools/testing/selftests/bpf/prog_tests/stacktrace_map_raw_tp.c
index 1f8387d80fd7..404a5498e1a3 100644
--- a/tools/testing/selftests/bpf/prog_tests/stacktrace_map_raw_tp.c
+++ b/tools/testing/selftests/bpf/prog_tests/stacktrace_map_raw_tp.c
@@ -3,31 +3,38 @@
void test_stacktrace_map_raw_tp(void)
{
+ const char *prog_name = "tracepoint/sched/sched_switch";
int control_map_fd, stackid_hmap_fd, stackmap_fd;
const char *file = "./test_stacktrace_map.o";
- int efd, err, prog_fd;
__u32 key, val, duration = 0;
+ int err, prog_fd;
+ struct bpf_program *prog;
struct bpf_object *obj;
+ struct bpf_link *link = NULL;
err = bpf_prog_load(file, BPF_PROG_TYPE_RAW_TRACEPOINT, &obj, &prog_fd);
if (CHECK(err, "prog_load raw tp", "err %d errno %d\n", err, errno))
return;
- efd = bpf_raw_tracepoint_open("sched_switch", prog_fd);
- if (CHECK(efd < 0, "raw_tp_open", "err %d errno %d\n", efd, errno))
+ prog = bpf_object__find_program_by_title(obj, prog_name);
+ if (CHECK(!prog, "find_prog", "prog '%s' not found\n", prog_name))
+ goto close_prog;
+
+ link = bpf_program__attach_raw_tracepoint(prog, "sched_switch");
+ if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n", PTR_ERR(link)))
goto close_prog;
/* find map fds */
control_map_fd = bpf_find_map(__func__, obj, "control_map");
- if (control_map_fd < 0)
+ if (CHECK_FAIL(control_map_fd < 0))
goto close_prog;
stackid_hmap_fd = bpf_find_map(__func__, obj, "stackid_hmap");
- if (stackid_hmap_fd < 0)
+ if (CHECK_FAIL(stackid_hmap_fd < 0))
goto close_prog;
stackmap_fd = bpf_find_map(__func__, obj, "stackmap");
- if (stackmap_fd < 0)
+ if (CHECK_FAIL(stackmap_fd < 0))
goto close_prog;
/* give some time for bpf program run */
@@ -51,9 +58,8 @@ void test_stacktrace_map_raw_tp(void)
"err %d errno %d\n", err, errno))
goto close_prog;
- goto close_prog_noerr;
close_prog:
- error_cnt++;
-close_prog_noerr:
+ if (!IS_ERR_OR_NULL(link))
+ bpf_link__destroy(link);
bpf_object__close(obj);
}
diff --git a/tools/testing/selftests/bpf/prog_tests/task_fd_query_rawtp.c b/tools/testing/selftests/bpf/prog_tests/task_fd_query_rawtp.c
index 958a3d88de99..1bdc1d86a50c 100644
--- a/tools/testing/selftests/bpf/prog_tests/task_fd_query_rawtp.c
+++ b/tools/testing/selftests/bpf/prog_tests/task_fd_query_rawtp.c
@@ -70,9 +70,6 @@ void test_task_fd_query_rawtp(void)
if (CHECK(!err, "check_results", "fd_type %d len %u\n", fd_type, len))
goto close_prog;
- goto close_prog_noerr;
close_prog:
- error_cnt++;
-close_prog_noerr:
bpf_object__close(obj);
}
diff --git a/tools/testing/selftests/bpf/prog_tests/task_fd_query_tp.c b/tools/testing/selftests/bpf/prog_tests/task_fd_query_tp.c
index f9b70e81682b..3f131b8fe328 100644
--- a/tools/testing/selftests/bpf/prog_tests/task_fd_query_tp.c
+++ b/tools/testing/selftests/bpf/prog_tests/task_fd_query_tp.c
@@ -62,14 +62,9 @@ static void test_task_fd_query_tp_core(const char *probe_name,
fd_type, buf))
goto close_pmu;
- close(pmu_fd);
- goto close_prog_noerr;
-
close_pmu:
close(pmu_fd);
close_prog:
- error_cnt++;
-close_prog_noerr:
bpf_object__close(obj);
}
diff --git a/tools/testing/selftests/bpf/prog_tests/tcp_estats.c b/tools/testing/selftests/bpf/prog_tests/tcp_estats.c
index bb8759d69099..594307dffd13 100644
--- a/tools/testing/selftests/bpf/prog_tests/tcp_estats.c
+++ b/tools/testing/selftests/bpf/prog_tests/tcp_estats.c
@@ -10,10 +10,8 @@ void test_tcp_estats(void)
err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd);
CHECK(err, "", "err %d errno %d\n", err, errno);
- if (err) {
- error_cnt++;
+ if (err)
return;
- }
bpf_object__close(obj);
}
diff --git a/tools/testing/selftests/bpf/prog_tests/tcp_rtt.c b/tools/testing/selftests/bpf/prog_tests/tcp_rtt.c
new file mode 100644
index 000000000000..fdc0b3614a9e
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/tcp_rtt.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include "cgroup_helpers.h"
+
+struct tcp_rtt_storage {
+ __u32 invoked;
+ __u32 dsack_dups;
+ __u32 delivered;
+ __u32 delivered_ce;
+ __u32 icsk_retransmits;
+};
+
+static void send_byte(int fd)
+{
+ char b = 0x55;
+
+ if (CHECK_FAIL(write(fd, &b, sizeof(b)) != 1))
+ perror("Failed to send single byte");
+}
+
+static int wait_for_ack(int fd, int retries)
+{
+ struct tcp_info info;
+ socklen_t optlen;
+ int i, err;
+
+ for (i = 0; i < retries; i++) {
+ optlen = sizeof(info);
+ err = getsockopt(fd, SOL_TCP, TCP_INFO, &info, &optlen);
+ if (err < 0) {
+ log_err("Failed to lookup TCP stats");
+ return err;
+ }
+
+ if (info.tcpi_unacked == 0)
+ return 0;
+
+ usleep(10);
+ }
+
+ log_err("Did not receive ACK");
+ return -1;
+}
+
+static int verify_sk(int map_fd, int client_fd, const char *msg, __u32 invoked,
+ __u32 dsack_dups, __u32 delivered, __u32 delivered_ce,
+ __u32 icsk_retransmits)
+{
+ int err = 0;
+ struct tcp_rtt_storage val;
+
+ if (CHECK_FAIL(bpf_map_lookup_elem(map_fd, &client_fd, &val) < 0)) {
+ perror("Failed to read socket storage");
+ return -1;
+ }
+
+ if (val.invoked != invoked) {
+ log_err("%s: unexpected bpf_tcp_sock.invoked %d != %d",
+ msg, val.invoked, invoked);
+ err++;
+ }
+
+ if (val.dsack_dups != dsack_dups) {
+ log_err("%s: unexpected bpf_tcp_sock.dsack_dups %d != %d",
+ msg, val.dsack_dups, dsack_dups);
+ err++;
+ }
+
+ if (val.delivered != delivered) {
+ log_err("%s: unexpected bpf_tcp_sock.delivered %d != %d",
+ msg, val.delivered, delivered);
+ err++;
+ }
+
+ if (val.delivered_ce != delivered_ce) {
+ log_err("%s: unexpected bpf_tcp_sock.delivered_ce %d != %d",
+ msg, val.delivered_ce, delivered_ce);
+ err++;
+ }
+
+ if (val.icsk_retransmits != icsk_retransmits) {
+ log_err("%s: unexpected bpf_tcp_sock.icsk_retransmits %d != %d",
+ msg, val.icsk_retransmits, icsk_retransmits);
+ err++;
+ }
+
+ return err;
+}
+
+static int connect_to_server(int server_fd)
+{
+ struct sockaddr_storage addr;
+ socklen_t len = sizeof(addr);
+ int fd;
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ log_err("Failed to create client socket");
+ return -1;
+ }
+
+ if (getsockname(server_fd, (struct sockaddr *)&addr, &len)) {
+ log_err("Failed to get server addr");
+ goto out;
+ }
+
+ if (connect(fd, (const struct sockaddr *)&addr, len) < 0) {
+ log_err("Fail to connect to server");
+ goto out;
+ }
+
+ return fd;
+
+out:
+ close(fd);
+ return -1;
+}
+
+static int run_test(int cgroup_fd, int server_fd)
+{
+ struct bpf_prog_load_attr attr = {
+ .prog_type = BPF_PROG_TYPE_SOCK_OPS,
+ .file = "./tcp_rtt.o",
+ .expected_attach_type = BPF_CGROUP_SOCK_OPS,
+ };
+ struct bpf_object *obj;
+ struct bpf_map *map;
+ int client_fd;
+ int prog_fd;
+ int map_fd;
+ int err;
+
+ err = bpf_prog_load_xattr(&attr, &obj, &prog_fd);
+ if (err) {
+ log_err("Failed to load BPF object");
+ return -1;
+ }
+
+ map = bpf_map__next(NULL, obj);
+ map_fd = bpf_map__fd(map);
+
+ err = bpf_prog_attach(prog_fd, cgroup_fd, BPF_CGROUP_SOCK_OPS, 0);
+ if (err) {
+ log_err("Failed to attach BPF program");
+ goto close_bpf_object;
+ }
+
+ client_fd = connect_to_server(server_fd);
+ if (client_fd < 0) {
+ err = -1;
+ goto close_bpf_object;
+ }
+
+ err += verify_sk(map_fd, client_fd, "syn-ack",
+ /*invoked=*/1,
+ /*dsack_dups=*/0,
+ /*delivered=*/1,
+ /*delivered_ce=*/0,
+ /*icsk_retransmits=*/0);
+
+ send_byte(client_fd);
+ if (wait_for_ack(client_fd, 100) < 0) {
+ err = -1;
+ goto close_client_fd;
+ }
+
+
+ err += verify_sk(map_fd, client_fd, "first payload byte",
+ /*invoked=*/2,
+ /*dsack_dups=*/0,
+ /*delivered=*/2,
+ /*delivered_ce=*/0,
+ /*icsk_retransmits=*/0);
+
+close_client_fd:
+ close(client_fd);
+
+close_bpf_object:
+ bpf_object__close(obj);
+ return err;
+}
+
+static int start_server(void)
+{
+ struct sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+ };
+ int fd;
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ log_err("Failed to create server socket");
+ return -1;
+ }
+
+ if (bind(fd, (const struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ log_err("Failed to bind socket");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static void *server_thread(void *arg)
+{
+ struct sockaddr_storage addr;
+ socklen_t len = sizeof(addr);
+ int fd = *(int *)arg;
+ int client_fd;
+
+ if (CHECK_FAIL(listen(fd, 1)) < 0) {
+ perror("Failed to listed on socket");
+ return NULL;
+ }
+
+ client_fd = accept(fd, (struct sockaddr *)&addr, &len);
+ if (CHECK_FAIL(client_fd < 0)) {
+ perror("Failed to accept client");
+ return NULL;
+ }
+
+ /* Wait for the next connection (that never arrives)
+ * to keep this thread alive to prevent calling
+ * close() on client_fd.
+ */
+ if (CHECK_FAIL(accept(fd, (struct sockaddr *)&addr, &len) >= 0)) {
+ perror("Unexpected success in second accept");
+ return NULL;
+ }
+
+ close(client_fd);
+
+ return NULL;
+}
+
+void test_tcp_rtt(void)
+{
+ int server_fd, cgroup_fd;
+ pthread_t tid;
+
+ cgroup_fd = test__join_cgroup("/tcp_rtt");
+ if (CHECK_FAIL(cgroup_fd < 0))
+ return;
+
+ server_fd = start_server();
+ if (CHECK_FAIL(server_fd < 0))
+ goto close_cgroup_fd;
+
+ pthread_create(&tid, NULL, server_thread, (void *)&server_fd);
+ CHECK_FAIL(run_test(cgroup_fd, server_fd));
+ close(server_fd);
+close_cgroup_fd:
+ close(cgroup_fd);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/xdp.c b/tools/testing/selftests/bpf/prog_tests/xdp.c
index a74167289545..dcb5ecac778e 100644
--- a/tools/testing/selftests/bpf/prog_tests/xdp.c
+++ b/tools/testing/selftests/bpf/prog_tests/xdp.c
@@ -16,10 +16,8 @@ void test_xdp(void)
int err, prog_fd, map_fd;
err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd);
- if (err) {
- error_cnt++;
+ if (CHECK_FAIL(err))
return;
- }
map_fd = bpf_find_map(__func__, obj, "vip2tnl");
if (map_fd < 0)
diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c
index 922aa0a19764..3744196d7cba 100644
--- a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c
+++ b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c
@@ -10,10 +10,8 @@ void test_xdp_adjust_tail(void)
int err, prog_fd;
err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd);
- if (err) {
- error_cnt++;
+ if (CHECK_FAIL(err))
return;
- }
err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
buf, &size, &retval, &duration);
diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_noinline.c b/tools/testing/selftests/bpf/prog_tests/xdp_noinline.c
index 09e6b46f5515..c9404e6b226e 100644
--- a/tools/testing/selftests/bpf/prog_tests/xdp_noinline.c
+++ b/tools/testing/selftests/bpf/prog_tests/xdp_noinline.c
@@ -31,10 +31,8 @@ void test_xdp_noinline(void)
u32 *magic = (u32 *)buf;
err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd);
- if (err) {
- error_cnt++;
+ if (CHECK_FAIL(err))
return;
- }
map_fd = bpf_find_map(__func__, obj, "vip_map");
if (map_fd < 0)
@@ -73,9 +71,10 @@ void test_xdp_noinline(void)
bytes += stats[i].bytes;
pkts += stats[i].pkts;
}
- if (bytes != MAGIC_BYTES * NUM_ITER * 2 || pkts != NUM_ITER * 2) {
- error_cnt++;
- printf("test_xdp_noinline:FAIL:stats %lld %lld\n", bytes, pkts);
+ if (CHECK_FAIL(bytes != MAGIC_BYTES * NUM_ITER * 2 ||
+ pkts != NUM_ITER * 2)) {
+ printf("test_xdp_noinline:FAIL:stats %lld %lld\n",
+ bytes, pkts);
}
out:
bpf_object__close(obj);
diff --git a/tools/testing/selftests/bpf/progs/bpf_flow.c b/tools/testing/selftests/bpf/progs/bpf_flow.c
index 81ad9a0b29d0..040a44206f29 100644
--- a/tools/testing/selftests/bpf/progs/bpf_flow.c
+++ b/tools/testing/selftests/bpf/progs/bpf_flow.c
@@ -57,32 +57,37 @@ struct frag_hdr {
__be32 identification;
};
-struct bpf_map_def SEC("maps") jmp_table = {
- .type = BPF_MAP_TYPE_PROG_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(__u32),
- .max_entries = 8
-};
-
-struct bpf_map_def SEC("maps") last_dissection = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct bpf_flow_keys),
- .max_entries = 1,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
+ __uint(max_entries, 8);
+ __uint(key_size, sizeof(__u32));
+ __uint(value_size, sizeof(__u32));
+} jmp_table SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 1024);
+ __type(key, __u32);
+ __type(value, struct bpf_flow_keys);
+} last_dissection SEC(".maps");
static __always_inline int export_flow_keys(struct bpf_flow_keys *keys,
int ret)
{
- struct bpf_flow_keys *val;
- __u32 key = 0;
+ __u32 key = (__u32)(keys->sport) << 16 | keys->dport;
+ struct bpf_flow_keys val;
- val = bpf_map_lookup_elem(&last_dissection, &key);
- if (val)
- memcpy(val, keys, sizeof(*val));
+ memcpy(&val, keys, sizeof(val));
+ bpf_map_update_elem(&last_dissection, &key, &val, BPF_ANY);
return ret;
}
+#define IPV6_FLOWLABEL_MASK __bpf_constant_htonl(0x000FFFFF)
+static inline __be32 ip6_flowlabel(const struct ipv6hdr *hdr)
+{
+ return *(__be32 *)hdr & IPV6_FLOWLABEL_MASK;
+}
+
static __always_inline void *bpf_flow_dissect_get_header(struct __sk_buff *skb,
__u16 hdr_size,
void *buffer)
@@ -153,7 +158,6 @@ static __always_inline int parse_ip_proto(struct __sk_buff *skb, __u8 proto)
struct tcphdr *tcp, _tcp;
struct udphdr *udp, _udp;
- keys->ip_proto = proto;
switch (proto) {
case IPPROTO_ICMP:
icmp = bpf_flow_dissect_get_header(skb, sizeof(*icmp), &_icmp);
@@ -162,9 +166,15 @@ static __always_inline int parse_ip_proto(struct __sk_buff *skb, __u8 proto)
return export_flow_keys(keys, BPF_OK);
case IPPROTO_IPIP:
keys->is_encap = true;
+ if (keys->flags & BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP)
+ return export_flow_keys(keys, BPF_OK);
+
return parse_eth_proto(skb, bpf_htons(ETH_P_IP));
case IPPROTO_IPV6:
keys->is_encap = true;
+ if (keys->flags & BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP)
+ return export_flow_keys(keys, BPF_OK);
+
return parse_eth_proto(skb, bpf_htons(ETH_P_IPV6));
case IPPROTO_GRE:
gre = bpf_flow_dissect_get_header(skb, sizeof(*gre), &_gre);
@@ -184,6 +194,8 @@ static __always_inline int parse_ip_proto(struct __sk_buff *skb, __u8 proto)
keys->thoff += 4; /* Step over sequence number */
keys->is_encap = true;
+ if (keys->flags & BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP)
+ return export_flow_keys(keys, BPF_OK);
if (gre->proto == bpf_htons(ETH_P_TEB)) {
eth = bpf_flow_dissect_get_header(skb, sizeof(*eth),
@@ -231,7 +243,6 @@ static __always_inline int parse_ipv6_proto(struct __sk_buff *skb, __u8 nexthdr)
{
struct bpf_flow_keys *keys = skb->flow_keys;
- keys->ip_proto = nexthdr;
switch (nexthdr) {
case IPPROTO_HOPOPTS:
case IPPROTO_DSTOPTS:
@@ -266,6 +277,7 @@ PROG(IP)(struct __sk_buff *skb)
keys->addr_proto = ETH_P_IP;
keys->ipv4_src = iph->saddr;
keys->ipv4_dst = iph->daddr;
+ keys->ip_proto = iph->protocol;
keys->thoff += iph->ihl << 2;
if (data + keys->thoff > data_end)
@@ -273,13 +285,20 @@ PROG(IP)(struct __sk_buff *skb)
if (iph->frag_off & bpf_htons(IP_MF | IP_OFFSET)) {
keys->is_frag = true;
- if (iph->frag_off & bpf_htons(IP_OFFSET))
+ if (iph->frag_off & bpf_htons(IP_OFFSET)) {
/* From second fragment on, packets do not have headers
* we can parse.
*/
done = true;
- else
+ } else {
keys->is_first_frag = true;
+ /* No need to parse fragmented packet unless
+ * explicitly asked for.
+ */
+ if (!(keys->flags &
+ BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG))
+ done = true;
+ }
}
if (done)
@@ -301,6 +320,11 @@ PROG(IPV6)(struct __sk_buff *skb)
memcpy(&keys->ipv6_src, &ip6h->saddr, 2*sizeof(ip6h->saddr));
keys->thoff += sizeof(struct ipv6hdr);
+ keys->ip_proto = ip6h->nexthdr;
+ keys->flow_label = ip6_flowlabel(ip6h);
+
+ if (keys->flags & BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL)
+ return export_flow_keys(keys, BPF_OK);
return parse_ipv6_proto(skb, ip6h->nexthdr);
}
@@ -317,7 +341,8 @@ PROG(IPV6OP)(struct __sk_buff *skb)
/* hlen is in 8-octets and does not include the first 8 bytes
* of the header
*/
- skb->flow_keys->thoff += (1 + ip6h->hdrlen) << 3;
+ keys->thoff += (1 + ip6h->hdrlen) << 3;
+ keys->ip_proto = ip6h->nexthdr;
return parse_ipv6_proto(skb, ip6h->nexthdr);
}
@@ -333,9 +358,18 @@ PROG(IPV6FR)(struct __sk_buff *skb)
keys->thoff += sizeof(*fragh);
keys->is_frag = true;
- if (!(fragh->frag_off & bpf_htons(IP6_OFFSET)))
+ keys->ip_proto = fragh->nexthdr;
+
+ if (!(fragh->frag_off & bpf_htons(IP6_OFFSET))) {
keys->is_first_frag = true;
+ /* No need to parse fragmented packet unless
+ * explicitly asked for.
+ */
+ if (!(keys->flags & BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG))
+ return export_flow_keys(keys, BPF_OK);
+ }
+
return parse_ipv6_proto(skb, fragh->nexthdr);
}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays.c
new file mode 100644
index 000000000000..018ed7fbba3a
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_arrays x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___diff_arr_dim.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___diff_arr_dim.c
new file mode 100644
index 000000000000..13d662c57014
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___diff_arr_dim.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_arrays___diff_arr_dim x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___diff_arr_val_sz.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___diff_arr_val_sz.c
new file mode 100644
index 000000000000..a351f418c85d
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___diff_arr_val_sz.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_arrays___diff_arr_val_sz x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_non_array.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_non_array.c
new file mode 100644
index 000000000000..a8735009becc
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_non_array.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_arrays___err_non_array x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_too_shallow.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_too_shallow.c
new file mode 100644
index 000000000000..2a67c28b1e75
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_too_shallow.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_arrays___err_too_shallow x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_too_small.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_too_small.c
new file mode 100644
index 000000000000..1142c08c925f
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_too_small.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_arrays___err_too_small x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_wrong_val_type1.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_wrong_val_type1.c
new file mode 100644
index 000000000000..795a5b729176
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_wrong_val_type1.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_arrays___err_wrong_val_type1 x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_wrong_val_type2.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_wrong_val_type2.c
new file mode 100644
index 000000000000..3af74b837c4d
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_arrays___err_wrong_val_type2.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_arrays___err_wrong_val_type2 x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_flavors.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_flavors.c
new file mode 100644
index 000000000000..b74455b91227
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_flavors.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_flavors x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_flavors__err_wrong_name.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_flavors__err_wrong_name.c
new file mode 100644
index 000000000000..7b6035f86ee6
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_flavors__err_wrong_name.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_flavors__err_wrong_name x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ints.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints.c
new file mode 100644
index 000000000000..7d0f041042c5
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_ints x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___bool.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___bool.c
new file mode 100644
index 000000000000..f9359450186e
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___bool.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_ints___bool x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_bitfield.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_bitfield.c
new file mode 100644
index 000000000000..50369e8320a0
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_bitfield.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_ints___err_bitfield x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_16.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_16.c
new file mode 100644
index 000000000000..823bac13d641
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_16.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_ints___err_wrong_sz_16 x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_32.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_32.c
new file mode 100644
index 000000000000..b44f3be18535
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_32.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_ints___err_wrong_sz_32 x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_64.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_64.c
new file mode 100644
index 000000000000..9a3dd2099c0f
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_64.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_ints___err_wrong_sz_64 x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_8.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_8.c
new file mode 100644
index 000000000000..9f11ef5f6e88
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___err_wrong_sz_8.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_ints___err_wrong_sz_8 x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___reverse_sign.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___reverse_sign.c
new file mode 100644
index 000000000000..aafb1c5819d7
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ints___reverse_sign.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_ints___reverse_sign x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_misc.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_misc.c
new file mode 100644
index 000000000000..ed9ad8b5b4f8
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_misc.c
@@ -0,0 +1,5 @@
+#include "core_reloc_types.h"
+
+void f1(struct core_reloc_misc___a x) {}
+void f2(struct core_reloc_misc___b x) {}
+void f3(struct core_reloc_misc_extensible x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_mods.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_mods.c
new file mode 100644
index 000000000000..124197a2e813
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_mods.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_mods x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_mods___mod_swap.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_mods___mod_swap.c
new file mode 100644
index 000000000000..f8a6592ca75f
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_mods___mod_swap.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_mods___mod_swap x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_mods___typedefs.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_mods___typedefs.c
new file mode 100644
index 000000000000..5c0d73687247
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_mods___typedefs.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_mods___typedefs x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting.c
new file mode 100644
index 000000000000..4480fcc0f183
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___anon_embed.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___anon_embed.c
new file mode 100644
index 000000000000..13e108f76ece
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___anon_embed.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting___anon_embed x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___dup_compat_types.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___dup_compat_types.c
new file mode 100644
index 000000000000..76b54fda5fbb
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___dup_compat_types.c
@@ -0,0 +1,5 @@
+#include "core_reloc_types.h"
+
+void f1(struct core_reloc_nesting___dup_compat_types x) {}
+void f2(struct core_reloc_nesting___dup_compat_types__2 x) {}
+void f3(struct core_reloc_nesting___dup_compat_types__3 x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_container.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_container.c
new file mode 100644
index 000000000000..975fb95db810
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_container.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting___err_array_container x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_field.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_field.c
new file mode 100644
index 000000000000..ad66c67e7980
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_array_field.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting___err_array_field x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_dup_incompat_types.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_dup_incompat_types.c
new file mode 100644
index 000000000000..35c5f8da6812
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_dup_incompat_types.c
@@ -0,0 +1,4 @@
+#include "core_reloc_types.h"
+
+void f1(struct core_reloc_nesting___err_dup_incompat_types__1 x) {}
+void f2(struct core_reloc_nesting___err_dup_incompat_types__2 x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_container.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_container.c
new file mode 100644
index 000000000000..142e332041db
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_container.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting___err_missing_container x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_field.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_field.c
new file mode 100644
index 000000000000..efcae167fab9
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_missing_field.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting___err_missing_field x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_nonstruct_container.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_nonstruct_container.c
new file mode 100644
index 000000000000..97aaaedd8ada
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_nonstruct_container.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting___err_nonstruct_container x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_partial_match_dups.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_partial_match_dups.c
new file mode 100644
index 000000000000..ffde35086e90
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_partial_match_dups.c
@@ -0,0 +1,4 @@
+#include "core_reloc_types.h"
+
+void f1(struct core_reloc_nesting___err_partial_match_dups__a x) {}
+void f2(struct core_reloc_nesting___err_partial_match_dups__b x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_too_deep.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_too_deep.c
new file mode 100644
index 000000000000..39a2fadd8e95
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___err_too_deep.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting___err_too_deep x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___extra_nesting.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___extra_nesting.c
new file mode 100644
index 000000000000..a09d9dfb20df
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___extra_nesting.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting___extra_nesting x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___struct_union_mixup.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___struct_union_mixup.c
new file mode 100644
index 000000000000..3d8a1a74012f
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_nesting___struct_union_mixup.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_nesting___struct_union_mixup x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives.c
new file mode 100644
index 000000000000..96b90e39242a
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_primitives x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_enum_def.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_enum_def.c
new file mode 100644
index 000000000000..6e87233a3ed0
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_enum_def.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_primitives___diff_enum_def x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_func_proto.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_func_proto.c
new file mode 100644
index 000000000000..d9f48e80b9d9
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_func_proto.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_primitives___diff_func_proto x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_ptr_type.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_ptr_type.c
new file mode 100644
index 000000000000..c718f75f8f3b
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___diff_ptr_type.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_primitives___diff_ptr_type x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_enum.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_enum.c
new file mode 100644
index 000000000000..b8a120830891
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_enum.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_primitives___err_non_enum x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_int.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_int.c
new file mode 100644
index 000000000000..ad8b3c9aa76f
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_int.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_primitives___err_non_int x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_ptr.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_ptr.c
new file mode 100644
index 000000000000..e20bc1d42d0a
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_primitives___err_non_ptr.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_primitives___err_non_ptr x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ptr_as_arr.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ptr_as_arr.c
new file mode 100644
index 000000000000..8da52432ba17
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ptr_as_arr.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_ptr_as_arr x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf__core_reloc_ptr_as_arr___diff_sz.c b/tools/testing/selftests/bpf/progs/btf__core_reloc_ptr_as_arr___diff_sz.c
new file mode 100644
index 000000000000..003acfc9a3e7
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf__core_reloc_ptr_as_arr___diff_sz.c
@@ -0,0 +1,3 @@
+#include "core_reloc_types.h"
+
+void f(struct core_reloc_ptr_as_arr___diff_sz x) {}
diff --git a/tools/testing/selftests/bpf/progs/btf_dump_test_case_bitfields.c b/tools/testing/selftests/bpf/progs/btf_dump_test_case_bitfields.c
new file mode 100644
index 000000000000..8f44767a75fa
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf_dump_test_case_bitfields.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+/*
+ * BTF-to-C dumper tests for bitfield.
+ *
+ * Copyright (c) 2019 Facebook
+ */
+#include <stdbool.h>
+
+/* ----- START-EXPECTED-OUTPUT ----- */
+/*
+ *struct bitfields_only_mixed_types {
+ * int a: 3;
+ * long int b: 2;
+ * _Bool c: 1;
+ * enum {
+ * A = 0,
+ * B = 1,
+ * } d: 1;
+ * short e: 5;
+ * int: 20;
+ * unsigned int f: 30;
+ *};
+ *
+ */
+/* ------ END-EXPECTED-OUTPUT ------ */
+
+struct bitfields_only_mixed_types {
+ int a: 3;
+ long int b: 2;
+ bool c: 1; /* it's really a _Bool type */
+ enum {
+ A, /* A = 0, dumper is very explicit */
+ B, /* B = 1, same */
+ } d: 1;
+ short e: 5;
+ /* 20-bit padding here */
+ unsigned f: 30; /* this gets aligned on 4-byte boundary */
+};
+
+/* ----- START-EXPECTED-OUTPUT ----- */
+/*
+ *struct bitfield_mixed_with_others {
+ * char: 4;
+ * int a: 4;
+ * short b;
+ * long int c;
+ * long int d: 8;
+ * int e;
+ * int f;
+ *};
+ *
+ */
+/* ------ END-EXPECTED-OUTPUT ------ */
+struct bitfield_mixed_with_others {
+ long: 4; /* char is enough as a backing field */
+ int a: 4;
+ /* 8-bit implicit padding */
+ short b; /* combined with previous bitfield */
+ /* 4 more bytes of implicit padding */
+ long c;
+ long d: 8;
+ /* 24 bits implicit padding */
+ int e; /* combined with previous bitfield */
+ int f;
+ /* 4 bytes of padding */
+};
+
+/* ----- START-EXPECTED-OUTPUT ----- */
+/*
+ *struct bitfield_flushed {
+ * int a: 4;
+ * long: 60;
+ * long int b: 16;
+ *};
+ *
+ */
+/* ------ END-EXPECTED-OUTPUT ------ */
+struct bitfield_flushed {
+ int a: 4;
+ long: 0; /* flush until next natural alignment boundary */
+ long b: 16;
+};
+
+int f(struct {
+ struct bitfields_only_mixed_types _1;
+ struct bitfield_mixed_with_others _2;
+ struct bitfield_flushed _3;
+} *_)
+{
+ return 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/btf_dump_test_case_multidim.c b/tools/testing/selftests/bpf/progs/btf_dump_test_case_multidim.c
new file mode 100644
index 000000000000..ba97165bdb28
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf_dump_test_case_multidim.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+/*
+ * BTF-to-C dumper test for multi-dimensional array output.
+ *
+ * Copyright (c) 2019 Facebook
+ */
+/* ----- START-EXPECTED-OUTPUT ----- */
+typedef int arr_t[2];
+
+typedef int multiarr_t[3][4][5];
+
+typedef int *ptr_arr_t[6];
+
+typedef int *ptr_multiarr_t[7][8][9][10];
+
+typedef int * (*fn_ptr_arr_t[11])();
+
+typedef int * (*fn_ptr_multiarr_t[12][13])();
+
+struct root_struct {
+ arr_t _1;
+ multiarr_t _2;
+ ptr_arr_t _3;
+ ptr_multiarr_t _4;
+ fn_ptr_arr_t _5;
+ fn_ptr_multiarr_t _6;
+};
+
+/* ------ END-EXPECTED-OUTPUT ------ */
+
+int f(struct root_struct *s)
+{
+ return 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/btf_dump_test_case_namespacing.c b/tools/testing/selftests/bpf/progs/btf_dump_test_case_namespacing.c
new file mode 100644
index 000000000000..92a4ad428710
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf_dump_test_case_namespacing.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+/*
+ * BTF-to-C dumper test validating no name versioning happens between
+ * independent C namespaces (struct/union/enum vs typedef/enum values).
+ *
+ * Copyright (c) 2019 Facebook
+ */
+/* ----- START-EXPECTED-OUTPUT ----- */
+struct S {
+ int S;
+ int U;
+};
+
+typedef struct S S;
+
+union U {
+ int S;
+ int U;
+};
+
+typedef union U U;
+
+enum E {
+ V = 0,
+};
+
+typedef enum E E;
+
+struct A {};
+
+union B {};
+
+enum C {
+ A = 1,
+ B = 2,
+ C = 3,
+};
+
+struct X {};
+
+union Y {};
+
+enum Z;
+
+typedef int X;
+
+typedef int Y;
+
+typedef int Z;
+
+/*------ END-EXPECTED-OUTPUT ------ */
+
+int f(struct {
+ struct S _1;
+ S _2;
+ union U _3;
+ U _4;
+ enum E _5;
+ E _6;
+ struct A a;
+ union B b;
+ enum C c;
+ struct X x;
+ union Y y;
+ enum Z *z;
+ X xx;
+ Y yy;
+ Z zz;
+} *_)
+{
+ return 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/btf_dump_test_case_ordering.c b/tools/testing/selftests/bpf/progs/btf_dump_test_case_ordering.c
new file mode 100644
index 000000000000..7c95702ee4cb
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf_dump_test_case_ordering.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+/*
+ * BTF-to-C dumper test for topological sorting of dependent structs.
+ *
+ * Copyright (c) 2019 Facebook
+ */
+/* ----- START-EXPECTED-OUTPUT ----- */
+struct s1 {};
+
+struct s3;
+
+struct s4;
+
+struct s2 {
+ struct s2 *s2;
+ struct s3 *s3;
+ struct s4 *s4;
+};
+
+struct s3 {
+ struct s1 s1;
+ struct s2 s2;
+};
+
+struct s4 {
+ struct s1 s1;
+ struct s3 s3;
+};
+
+struct list_head {
+ struct list_head *next;
+ struct list_head *prev;
+};
+
+struct hlist_node {
+ struct hlist_node *next;
+ struct hlist_node **pprev;
+};
+
+struct hlist_head {
+ struct hlist_node *first;
+};
+
+struct callback_head {
+ struct callback_head *next;
+ void (*func)(struct callback_head *);
+};
+
+struct root_struct {
+ struct s4 s4;
+ struct list_head l;
+ struct hlist_node n;
+ struct hlist_head h;
+ struct callback_head cb;
+};
+
+/*------ END-EXPECTED-OUTPUT ------ */
+
+int f(struct root_struct *root)
+{
+ return 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/btf_dump_test_case_packing.c b/tools/testing/selftests/bpf/progs/btf_dump_test_case_packing.c
new file mode 100644
index 000000000000..1cef3bec1dc7
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf_dump_test_case_packing.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+/*
+ * BTF-to-C dumper tests for struct packing determination.
+ *
+ * Copyright (c) 2019 Facebook
+ */
+/* ----- START-EXPECTED-OUTPUT ----- */
+struct packed_trailing_space {
+ int a;
+ short b;
+} __attribute__((packed));
+
+struct non_packed_trailing_space {
+ int a;
+ short b;
+};
+
+struct packed_fields {
+ short a;
+ int b;
+} __attribute__((packed));
+
+struct non_packed_fields {
+ short a;
+ int b;
+};
+
+struct nested_packed {
+ char: 4;
+ int a: 4;
+ long int b;
+ struct {
+ char c;
+ int d;
+ } __attribute__((packed)) e;
+} __attribute__((packed));
+
+union union_is_never_packed {
+ int a: 4;
+ char b;
+ char c: 1;
+};
+
+union union_does_not_need_packing {
+ struct {
+ long int a;
+ int b;
+ } __attribute__((packed));
+ int c;
+};
+
+union jump_code_union {
+ char code[5];
+ struct {
+ char jump;
+ int offset;
+ } __attribute__((packed));
+};
+
+/*------ END-EXPECTED-OUTPUT ------ */
+
+int f(struct {
+ struct packed_trailing_space _1;
+ struct non_packed_trailing_space _2;
+ struct packed_fields _3;
+ struct non_packed_fields _4;
+ struct nested_packed _5;
+ union union_is_never_packed _6;
+ union union_does_not_need_packing _7;
+ union jump_code_union _8;
+} *_)
+{
+ return 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/btf_dump_test_case_padding.c b/tools/testing/selftests/bpf/progs/btf_dump_test_case_padding.c
new file mode 100644
index 000000000000..3a62119c7498
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf_dump_test_case_padding.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+/*
+ * BTF-to-C dumper tests for implicit and explicit padding between fields and
+ * at the end of a struct.
+ *
+ * Copyright (c) 2019 Facebook
+ */
+/* ----- START-EXPECTED-OUTPUT ----- */
+struct padded_implicitly {
+ int a;
+ long int b;
+ char c;
+};
+
+/* ------ END-EXPECTED-OUTPUT ------ */
+
+/* ----- START-EXPECTED-OUTPUT ----- */
+/*
+ *struct padded_explicitly {
+ * int a;
+ * int: 32;
+ * int b;
+ *};
+ *
+ */
+/* ------ END-EXPECTED-OUTPUT ------ */
+
+struct padded_explicitly {
+ int a;
+ int: 1; /* algo will explicitly pad with full 32 bits here */
+ int b;
+};
+
+/* ----- START-EXPECTED-OUTPUT ----- */
+/*
+ *struct padded_a_lot {
+ * int a;
+ * long: 32;
+ * long: 64;
+ * long: 64;
+ * int b;
+ *};
+ *
+ */
+/* ------ END-EXPECTED-OUTPUT ------ */
+
+struct padded_a_lot {
+ int a;
+ /* 32 bit of implicit padding here, which algo will make explicit */
+ long: 64;
+ long: 64;
+ int b;
+};
+
+/* ----- START-EXPECTED-OUTPUT ----- */
+/*
+ *struct padded_cache_line {
+ * int a;
+ * long: 32;
+ * long: 64;
+ * long: 64;
+ * long: 64;
+ * int b;
+ *};
+ *
+ */
+/* ------ END-EXPECTED-OUTPUT ------ */
+
+struct padded_cache_line {
+ int a;
+ int b __attribute__((aligned(32)));
+};
+
+/* ----- START-EXPECTED-OUTPUT ----- */
+/*
+ *struct zone_padding {
+ * char x[0];
+ *};
+ *
+ *struct zone {
+ * int a;
+ * short b;
+ * short: 16;
+ * struct zone_padding __pad__;
+ *};
+ *
+ */
+/* ------ END-EXPECTED-OUTPUT ------ */
+
+struct zone_padding {
+ char x[0];
+} __attribute__((__aligned__(8)));
+
+struct zone {
+ int a;
+ short b;
+ short: 16;
+ struct zone_padding __pad__;
+};
+
+int f(struct {
+ struct padded_implicitly _1;
+ struct padded_explicitly _2;
+ struct padded_a_lot _3;
+ struct padded_cache_line _4;
+ struct zone _5;
+} *_)
+{
+ return 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c b/tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c
new file mode 100644
index 000000000000..d4a02fe44a12
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+/*
+ * BTF-to-C dumper test for majority of C syntax quirks.
+ *
+ * Copyright (c) 2019 Facebook
+ */
+/* ----- START-EXPECTED-OUTPUT ----- */
+enum e1 {
+ A = 0,
+ B = 1,
+};
+
+enum e2 {
+ C = 100,
+ D = -100,
+ E = 0,
+};
+
+typedef enum e2 e2_t;
+
+typedef enum {
+ F = 0,
+ G = 1,
+ H = 2,
+} e3_t;
+
+typedef int int_t;
+
+typedef volatile const int * volatile const crazy_ptr_t;
+
+typedef int *****we_need_to_go_deeper_ptr_t;
+
+typedef volatile const we_need_to_go_deeper_ptr_t * restrict * volatile * const * restrict volatile * restrict const * volatile const * restrict volatile const how_about_this_ptr_t;
+
+typedef int *ptr_arr_t[10];
+
+typedef void (*fn_ptr1_t)(int);
+
+typedef void (*printf_fn_t)(const char *, ...);
+
+/* ------ END-EXPECTED-OUTPUT ------ */
+/*
+ * While previous function pointers are pretty trivial (C-syntax-level
+ * trivial), the following are deciphered here for future generations:
+ *
+ * - `fn_ptr2_t`: function, taking anonymous struct as a first arg and pointer
+ * to a function, that takes int and returns int, as a second arg; returning
+ * a pointer to a const pointer to a char. Equivalent to:
+ * typedef struct { int a; } s_t;
+ * typedef int (*fn_t)(int);
+ * typedef char * const * (*fn_ptr2_t)(s_t, fn_t);
+ *
+ * - `fn_complext_t`: pointer to a function returning struct and accepting
+ * union and struct. All structs and enum are anonymous and defined inline.
+ *
+ * - `signal_t: pointer to a function accepting a pointer to a function as an
+ * argument and returning pointer to a function as a result. Sane equivalent:
+ * typedef void (*signal_handler_t)(int);
+ * typedef signal_handler_t (*signal_ptr_t)(int, signal_handler_t);
+ *
+ * - fn_ptr_arr1_t: array of pointers to a function accepting pointer to
+ * a pointer to an int and returning pointer to a char. Easy.
+ *
+ * - fn_ptr_arr2_t: array of const pointers to a function taking no arguments
+ * and returning a const pointer to a function, that takes pointer to a
+ * `int -> char *` function and returns pointer to a char. Equivalent:
+ * typedef char * (*fn_input_t)(int);
+ * typedef char * (*fn_output_outer_t)(fn_input_t);
+ * typedef const fn_output_outer_t (* fn_output_inner_t)();
+ * typedef const fn_output_inner_t fn_ptr_arr2_t[5];
+ */
+/* ----- START-EXPECTED-OUTPUT ----- */
+typedef char * const * (*fn_ptr2_t)(struct {
+ int a;
+}, int (*)(int));
+
+typedef struct {
+ int a;
+ void (*b)(int, struct {
+ int c;
+ }, union {
+ char d;
+ int e[5];
+ });
+} (*fn_complex_t)(union {
+ void *f;
+ char g[16];
+}, struct {
+ int h;
+});
+
+typedef void (* (*signal_t)(int, void (*)(int)))(int);
+
+typedef char * (*fn_ptr_arr1_t[10])(int **);
+
+typedef char * (* const (* const fn_ptr_arr2_t[5])())(char * (*)(int));
+
+struct struct_w_typedefs {
+ int_t a;
+ crazy_ptr_t b;
+ we_need_to_go_deeper_ptr_t c;
+ how_about_this_ptr_t d;
+ ptr_arr_t e;
+ fn_ptr1_t f;
+ printf_fn_t g;
+ fn_ptr2_t h;
+ fn_complex_t i;
+ signal_t j;
+ fn_ptr_arr1_t k;
+ fn_ptr_arr2_t l;
+};
+
+typedef struct {
+ int x;
+ int y;
+ int z;
+} anon_struct_t;
+
+struct struct_fwd;
+
+typedef struct struct_fwd struct_fwd_t;
+
+typedef struct struct_fwd *struct_fwd_ptr_t;
+
+union union_fwd;
+
+typedef union union_fwd union_fwd_t;
+
+typedef union union_fwd *union_fwd_ptr_t;
+
+struct struct_empty {};
+
+struct struct_simple {
+ int a;
+ char b;
+ const int_t *p;
+ struct struct_empty s;
+ enum e2 e;
+ enum {
+ ANON_VAL1 = 1,
+ ANON_VAL2 = 2,
+ } f;
+ int arr1[13];
+ enum e2 arr2[5];
+};
+
+union union_empty {};
+
+union union_simple {
+ void *ptr;
+ int num;
+ int_t num2;
+ union union_empty u;
+};
+
+struct struct_in_struct {
+ struct struct_simple simple;
+ union union_simple also_simple;
+ struct {
+ int a;
+ } not_so_hard_as_well;
+ union {
+ int b;
+ int c;
+ } anon_union_is_good;
+ struct {
+ int d;
+ int e;
+ };
+ union {
+ int f;
+ int g;
+ };
+};
+
+struct struct_with_embedded_stuff {
+ int a;
+ struct {
+ int b;
+ struct {
+ struct struct_with_embedded_stuff *c;
+ const char *d;
+ } e;
+ union {
+ volatile long int f;
+ void * restrict g;
+ };
+ };
+ union {
+ const int_t *h;
+ void (*i)(char, int, void *);
+ } j;
+ enum {
+ K = 100,
+ L = 200,
+ } m;
+ char n[16];
+ struct {
+ char o;
+ int p;
+ void (*q)(int);
+ } r[5];
+ struct struct_in_struct s[10];
+ int t[11];
+};
+
+struct root_struct {
+ enum e1 _1;
+ enum e2 _2;
+ e2_t _2_1;
+ e3_t _2_2;
+ struct struct_w_typedefs _3;
+ anon_struct_t _7;
+ struct struct_fwd *_8;
+ struct_fwd_t *_9;
+ struct_fwd_ptr_t _10;
+ union union_fwd *_11;
+ union_fwd_t *_12;
+ union_fwd_ptr_t _13;
+ struct struct_with_embedded_stuff _14;
+};
+
+/* ------ END-EXPECTED-OUTPUT ------ */
+
+int f(struct root_struct *s)
+{
+ return 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/core_reloc_types.h b/tools/testing/selftests/bpf/progs/core_reloc_types.h
new file mode 100644
index 000000000000..f686a8138d90
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/core_reloc_types.h
@@ -0,0 +1,667 @@
+#include <stdint.h>
+#include <stdbool.h>
+
+/*
+ * FLAVORS
+ */
+struct core_reloc_flavors {
+ int a;
+ int b;
+ int c;
+};
+
+/* this is not a flavor, as it doesn't have triple underscore */
+struct core_reloc_flavors__err_wrong_name {
+ int a;
+ int b;
+ int c;
+};
+
+/*
+ * NESTING
+ */
+/* original set up, used to record relocations in BPF program */
+struct core_reloc_nesting_substruct {
+ int a;
+};
+
+union core_reloc_nesting_subunion {
+ int b;
+};
+
+struct core_reloc_nesting {
+ union {
+ struct core_reloc_nesting_substruct a;
+ } a;
+ struct {
+ union core_reloc_nesting_subunion b;
+ } b;
+};
+
+/* inlined anonymous struct/union instead of named structs in original */
+struct core_reloc_nesting___anon_embed {
+ int __just_for_padding;
+ union {
+ struct {
+ int a;
+ } a;
+ } a;
+ struct {
+ union {
+ int b;
+ } b;
+ } b;
+};
+
+/* different mix of nested structs/unions than in original */
+struct core_reloc_nesting___struct_union_mixup {
+ int __a;
+ struct {
+ int __a;
+ union {
+ char __a;
+ int a;
+ } a;
+ } a;
+ int __b;
+ union {
+ int __b;
+ union {
+ char __b;
+ int b;
+ } b;
+ } b;
+};
+
+/* extra anon structs/unions, but still valid a.a.a and b.b.b accessors */
+struct core_reloc_nesting___extra_nesting {
+ int __padding;
+ struct {
+ struct {
+ struct {
+ struct {
+ union {
+ int a;
+ } a;
+ };
+ };
+ } a;
+ int __some_more;
+ struct {
+ union {
+ union {
+ union {
+ struct {
+ int b;
+ };
+ } b;
+ };
+ } b;
+ };
+ };
+};
+
+/* three flavors of same struct with different structure but same layout for
+ * a.a.a and b.b.b, thus successfully resolved and relocatable */
+struct core_reloc_nesting___dup_compat_types {
+ char __just_for_padding;
+ /* 3 more bytes of padding */
+ struct {
+ struct {
+ int a; /* offset 4 */
+ } a;
+ } a;
+ long long __more_padding;
+ struct {
+ struct {
+ int b; /* offset 16 */
+ } b;
+ } b;
+};
+
+struct core_reloc_nesting___dup_compat_types__2 {
+ int __aligned_padding;
+ struct {
+ int __trickier_noop[0];
+ struct {
+ char __some_more_noops[0];
+ int a; /* offset 4 */
+ } a;
+ } a;
+ int __more_padding;
+ struct {
+ struct {
+ struct {
+ int __critical_padding;
+ int b; /* offset 16 */
+ } b;
+ int __does_not_matter;
+ };
+ } b;
+ int __more_irrelevant_stuff;
+};
+
+struct core_reloc_nesting___dup_compat_types__3 {
+ char __correct_padding[4];
+ struct {
+ struct {
+ int a; /* offset 4 */
+ } a;
+ } a;
+ /* 8 byte padding due to next struct's alignment */
+ struct {
+ struct {
+ int b;
+ } b;
+ } b __attribute__((aligned(16)));
+};
+
+/* b.b.b field is missing */
+struct core_reloc_nesting___err_missing_field {
+ struct {
+ struct {
+ int a;
+ } a;
+ } a;
+ struct {
+ struct {
+ int x;
+ } b;
+ } b;
+};
+
+/* b.b.b field is an array of integers instead of plain int */
+struct core_reloc_nesting___err_array_field {
+ struct {
+ struct {
+ int a;
+ } a;
+ } a;
+ struct {
+ struct {
+ int b[1];
+ } b;
+ } b;
+};
+
+/* middle b container is missing */
+struct core_reloc_nesting___err_missing_container {
+ struct {
+ struct {
+ int a;
+ } a;
+ } a;
+ struct {
+ int x;
+ } b;
+};
+
+/* middle b container is referenced through pointer instead of being embedded */
+struct core_reloc_nesting___err_nonstruct_container {
+ struct {
+ struct {
+ int a;
+ } a;
+ } a;
+ struct {
+ struct {
+ int b;
+ } *b;
+ } b;
+};
+
+/* middle b container is an array of structs instead of plain struct */
+struct core_reloc_nesting___err_array_container {
+ struct {
+ struct {
+ int a;
+ } a;
+ } a;
+ struct {
+ struct {
+ int b;
+ } b[1];
+ } b;
+};
+
+/* two flavors of same struct with incompatible layout for b.b.b */
+struct core_reloc_nesting___err_dup_incompat_types__1 {
+ struct {
+ struct {
+ int a; /* offset 0 */
+ } a;
+ } a;
+ struct {
+ struct {
+ int b; /* offset 4 */
+ } b;
+ } b;
+};
+
+struct core_reloc_nesting___err_dup_incompat_types__2 {
+ struct {
+ struct {
+ int a; /* offset 0 */
+ } a;
+ } a;
+ int __extra_padding;
+ struct {
+ struct {
+ int b; /* offset 8 (!) */
+ } b;
+ } b;
+};
+
+/* two flavors of same struct having one of a.a.a and b.b.b, but not both */
+struct core_reloc_nesting___err_partial_match_dups__a {
+ struct {
+ struct {
+ int a;
+ } a;
+ } a;
+};
+
+struct core_reloc_nesting___err_partial_match_dups__b {
+ struct {
+ struct {
+ int b;
+ } b;
+ } b;
+};
+
+struct core_reloc_nesting___err_too_deep {
+ struct {
+ struct {
+ int a;
+ } a;
+ } a;
+ /* 65 levels of nestedness for b.b.b */
+ struct {
+ struct {
+ struct { struct { struct { struct { struct {
+ struct { struct { struct { struct { struct {
+ struct { struct { struct { struct { struct {
+ struct { struct { struct { struct { struct {
+ struct { struct { struct { struct { struct {
+ struct { struct { struct { struct { struct {
+ struct { struct { struct { struct { struct {
+ struct { struct { struct { struct { struct {
+ struct { struct { struct { struct { struct {
+ struct { struct { struct { struct { struct {
+ struct { struct { struct { struct { struct {
+ struct { struct { struct { struct { struct {
+ /* this one is one too much */
+ struct {
+ int b;
+ };
+ }; }; }; }; };
+ }; }; }; }; };
+ }; }; }; }; };
+ }; }; }; }; };
+ }; }; }; }; };
+ }; }; }; }; };
+ }; }; }; }; };
+ }; }; }; }; };
+ }; }; }; }; };
+ }; }; }; }; };
+ }; }; }; }; };
+ }; }; }; }; };
+ } b;
+ } b;
+};
+
+/*
+ * ARRAYS
+ */
+struct core_reloc_arrays_output {
+ int a2;
+ char b123;
+ int c1c;
+ int d00d;
+};
+
+struct core_reloc_arrays_substruct {
+ int c;
+ int d;
+};
+
+struct core_reloc_arrays {
+ int a[5];
+ char b[2][3][4];
+ struct core_reloc_arrays_substruct c[3];
+ struct core_reloc_arrays_substruct d[1][2];
+};
+
+/* bigger array dimensions */
+struct core_reloc_arrays___diff_arr_dim {
+ int a[7];
+ char b[3][4][5];
+ struct core_reloc_arrays_substruct c[4];
+ struct core_reloc_arrays_substruct d[2][3];
+};
+
+/* different size of array's value (struct) */
+struct core_reloc_arrays___diff_arr_val_sz {
+ int a[5];
+ char b[2][3][4];
+ struct {
+ int __padding1;
+ int c;
+ int __padding2;
+ } c[3];
+ struct {
+ int __padding1;
+ int d;
+ int __padding2;
+ } d[1][2];
+};
+
+struct core_reloc_arrays___err_too_small {
+ int a[2]; /* this one is too small */
+ char b[2][3][4];
+ struct core_reloc_arrays_substruct c[3];
+ struct core_reloc_arrays_substruct d[1][2];
+};
+
+struct core_reloc_arrays___err_too_shallow {
+ int a[5];
+ char b[2][3]; /* this one lacks one dimension */
+ struct core_reloc_arrays_substruct c[3];
+ struct core_reloc_arrays_substruct d[1][2];
+};
+
+struct core_reloc_arrays___err_non_array {
+ int a; /* not an array */
+ char b[2][3][4];
+ struct core_reloc_arrays_substruct c[3];
+ struct core_reloc_arrays_substruct d[1][2];
+};
+
+struct core_reloc_arrays___err_wrong_val_type1 {
+ char a[5]; /* char instead of int */
+ char b[2][3][4];
+ struct core_reloc_arrays_substruct c[3];
+ struct core_reloc_arrays_substruct d[1][2];
+};
+
+struct core_reloc_arrays___err_wrong_val_type2 {
+ int a[5];
+ char b[2][3][4];
+ int c[3]; /* value is not a struct */
+ struct core_reloc_arrays_substruct d[1][2];
+};
+
+/*
+ * PRIMITIVES
+ */
+enum core_reloc_primitives_enum {
+ A = 0,
+ B = 1,
+};
+
+struct core_reloc_primitives {
+ char a;
+ int b;
+ enum core_reloc_primitives_enum c;
+ void *d;
+ int (*f)(const char *);
+};
+
+struct core_reloc_primitives___diff_enum_def {
+ char a;
+ int b;
+ void *d;
+ int (*f)(const char *);
+ enum {
+ X = 100,
+ Y = 200,
+ } c; /* inline enum def with differing set of values */
+};
+
+struct core_reloc_primitives___diff_func_proto {
+ void (*f)(int); /* incompatible function prototype */
+ void *d;
+ enum core_reloc_primitives_enum c;
+ int b;
+ char a;
+};
+
+struct core_reloc_primitives___diff_ptr_type {
+ const char * const d; /* different pointee type + modifiers */
+ char a;
+ int b;
+ enum core_reloc_primitives_enum c;
+ int (*f)(const char *);
+};
+
+struct core_reloc_primitives___err_non_enum {
+ char a[1];
+ int b;
+ int c; /* int instead of enum */
+ void *d;
+ int (*f)(const char *);
+};
+
+struct core_reloc_primitives___err_non_int {
+ char a[1];
+ int *b; /* ptr instead of int */
+ enum core_reloc_primitives_enum c;
+ void *d;
+ int (*f)(const char *);
+};
+
+struct core_reloc_primitives___err_non_ptr {
+ char a[1];
+ int b;
+ enum core_reloc_primitives_enum c;
+ int d; /* int instead of ptr */
+ int (*f)(const char *);
+};
+
+/*
+ * MODS
+ */
+struct core_reloc_mods_output {
+ int a, b, c, d, e, f, g, h;
+};
+
+typedef const int int_t;
+typedef const char *char_ptr_t;
+typedef const int arr_t[7];
+
+struct core_reloc_mods_substruct {
+ int x;
+ int y;
+};
+
+typedef struct {
+ int x;
+ int y;
+} core_reloc_mods_substruct_t;
+
+struct core_reloc_mods {
+ int a;
+ int_t b;
+ char *c;
+ char_ptr_t d;
+ int e[3];
+ arr_t f;
+ struct core_reloc_mods_substruct g;
+ core_reloc_mods_substruct_t h;
+};
+
+/* a/b, c/d, e/f, and g/h pairs are swapped */
+struct core_reloc_mods___mod_swap {
+ int b;
+ int_t a;
+ char *d;
+ char_ptr_t c;
+ int f[3];
+ arr_t e;
+ struct {
+ int y;
+ int x;
+ } h;
+ core_reloc_mods_substruct_t g;
+};
+
+typedef int int1_t;
+typedef int1_t int2_t;
+typedef int2_t int3_t;
+
+typedef int arr1_t[5];
+typedef arr1_t arr2_t;
+typedef arr2_t arr3_t;
+typedef arr3_t arr4_t;
+
+typedef const char * const volatile fancy_char_ptr_t;
+
+typedef core_reloc_mods_substruct_t core_reloc_mods_substruct_tt;
+
+/* we need more typedefs */
+struct core_reloc_mods___typedefs {
+ core_reloc_mods_substruct_tt g;
+ core_reloc_mods_substruct_tt h;
+ arr4_t f;
+ arr4_t e;
+ fancy_char_ptr_t d;
+ fancy_char_ptr_t c;
+ int3_t b;
+ int3_t a;
+};
+
+/*
+ * PTR_AS_ARR
+ */
+struct core_reloc_ptr_as_arr {
+ int a;
+};
+
+struct core_reloc_ptr_as_arr___diff_sz {
+ int :32; /* padding */
+ char __some_more_padding;
+ int a;
+};
+
+/*
+ * INTS
+ */
+struct core_reloc_ints {
+ uint8_t u8_field;
+ int8_t s8_field;
+ uint16_t u16_field;
+ int16_t s16_field;
+ uint32_t u32_field;
+ int32_t s32_field;
+ uint64_t u64_field;
+ int64_t s64_field;
+};
+
+/* signed/unsigned types swap */
+struct core_reloc_ints___reverse_sign {
+ int8_t u8_field;
+ uint8_t s8_field;
+ int16_t u16_field;
+ uint16_t s16_field;
+ int32_t u32_field;
+ uint32_t s32_field;
+ int64_t u64_field;
+ uint64_t s64_field;
+};
+
+struct core_reloc_ints___bool {
+ bool u8_field; /* bool instead of uint8 */
+ int8_t s8_field;
+ uint16_t u16_field;
+ int16_t s16_field;
+ uint32_t u32_field;
+ int32_t s32_field;
+ uint64_t u64_field;
+ int64_t s64_field;
+};
+
+struct core_reloc_ints___err_bitfield {
+ uint8_t u8_field;
+ int8_t s8_field;
+ uint16_t u16_field;
+ int16_t s16_field;
+ uint32_t u32_field: 32; /* bitfields are not supported */
+ int32_t s32_field;
+ uint64_t u64_field;
+ int64_t s64_field;
+};
+
+struct core_reloc_ints___err_wrong_sz_8 {
+ uint16_t u8_field; /* not 8-bit anymore */
+ int16_t s8_field; /* not 8-bit anymore */
+
+ uint16_t u16_field;
+ int16_t s16_field;
+ uint32_t u32_field;
+ int32_t s32_field;
+ uint64_t u64_field;
+ int64_t s64_field;
+};
+
+struct core_reloc_ints___err_wrong_sz_16 {
+ uint8_t u8_field;
+ int8_t s8_field;
+
+ uint32_t u16_field; /* not 16-bit anymore */
+ int32_t s16_field; /* not 16-bit anymore */
+
+ uint32_t u32_field;
+ int32_t s32_field;
+ uint64_t u64_field;
+ int64_t s64_field;
+};
+
+struct core_reloc_ints___err_wrong_sz_32 {
+ uint8_t u8_field;
+ int8_t s8_field;
+ uint16_t u16_field;
+ int16_t s16_field;
+
+ uint64_t u32_field; /* not 32-bit anymore */
+ int64_t s32_field; /* not 32-bit anymore */
+
+ uint64_t u64_field;
+ int64_t s64_field;
+};
+
+struct core_reloc_ints___err_wrong_sz_64 {
+ uint8_t u8_field;
+ int8_t s8_field;
+ uint16_t u16_field;
+ int16_t s16_field;
+ uint32_t u32_field;
+ int32_t s32_field;
+
+ uint32_t u64_field; /* not 64-bit anymore */
+ int32_t s64_field; /* not 64-bit anymore */
+};
+
+/*
+ * MISC
+ */
+struct core_reloc_misc_output {
+ int a, b, c;
+};
+
+struct core_reloc_misc___a {
+ int a1;
+ int a2;
+};
+
+struct core_reloc_misc___b {
+ int b1;
+ int b2;
+};
+
+/* this one extends core_reloc_misc_extensible struct from BPF prog */
+struct core_reloc_misc_extensible {
+ int a;
+ int b;
+ int c;
+ int d;
+};
diff --git a/tools/testing/selftests/bpf/progs/get_cgroup_id_kern.c b/tools/testing/selftests/bpf/progs/get_cgroup_id_kern.c
index 014dba10b8a5..16c54ade6888 100644
--- a/tools/testing/selftests/bpf/progs/get_cgroup_id_kern.c
+++ b/tools/testing/selftests/bpf/progs/get_cgroup_id_kern.c
@@ -4,19 +4,19 @@
#include <linux/bpf.h>
#include "bpf_helpers.h"
-struct bpf_map_def SEC("maps") cg_ids = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(__u64),
- .max_entries = 1,
-};
-
-struct bpf_map_def SEC("maps") pidmap = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(__u32),
- .max_entries = 1,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, __u32);
+ __type(value, __u64);
+} cg_ids SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, __u32);
+ __type(value, __u32);
+} pidmap SEC(".maps");
SEC("tracepoint/syscalls/sys_enter_nanosleep")
int trace(void *ctx)
diff --git a/tools/testing/selftests/bpf/progs/loop1.c b/tools/testing/selftests/bpf/progs/loop1.c
new file mode 100644
index 000000000000..7cdb7f878310
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/loop1.c
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+
+SEC("raw_tracepoint/kfree_skb")
+int nested_loops(volatile struct pt_regs* ctx)
+{
+ int i, j, sum = 0, m;
+
+ for (j = 0; j < 300; j++)
+ for (i = 0; i < j; i++) {
+ if (j & 1)
+ m = PT_REGS_RC(ctx);
+ else
+ m = j;
+ sum += i * m;
+ }
+
+ return sum;
+}
diff --git a/tools/testing/selftests/bpf/progs/loop2.c b/tools/testing/selftests/bpf/progs/loop2.c
new file mode 100644
index 000000000000..9b2f808a2863
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/loop2.c
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+
+SEC("raw_tracepoint/consume_skb")
+int while_true(volatile struct pt_regs* ctx)
+{
+ int i = 0;
+
+ while (true) {
+ if (PT_REGS_RC(ctx) & 1)
+ i += 3;
+ else
+ i += 7;
+ if (i > 40)
+ break;
+ }
+
+ return i;
+}
diff --git a/tools/testing/selftests/bpf/progs/loop3.c b/tools/testing/selftests/bpf/progs/loop3.c
new file mode 100644
index 000000000000..d727657d51e2
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/loop3.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+
+SEC("raw_tracepoint/consume_skb")
+int while_true(volatile struct pt_regs* ctx)
+{
+ __u64 i = 0, sum = 0;
+ do {
+ i++;
+ sum += PT_REGS_RC(ctx);
+ } while (i < 0x100000000ULL);
+ return sum;
+}
diff --git a/tools/testing/selftests/bpf/progs/loop4.c b/tools/testing/selftests/bpf/progs/loop4.c
new file mode 100644
index 000000000000..650859022771
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/loop4.c
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+
+SEC("socket")
+int combinations(volatile struct __sk_buff* skb)
+{
+ int ret = 0, i;
+
+#pragma nounroll
+ for (i = 0; i < 20; i++)
+ if (skb->len)
+ ret |= 1 << i;
+ return ret;
+}
diff --git a/tools/testing/selftests/bpf/progs/loop5.c b/tools/testing/selftests/bpf/progs/loop5.c
new file mode 100644
index 000000000000..28d1d668f07c
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/loop5.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+#define barrier() __asm__ __volatile__("": : :"memory")
+
+char _license[] SEC("license") = "GPL";
+
+SEC("socket")
+int while_true(volatile struct __sk_buff* skb)
+{
+ int i = 0;
+
+ while (1) {
+ if (skb->len)
+ i += 3;
+ else
+ i += 7;
+ if (i == 9)
+ break;
+ barrier();
+ if (i == 10)
+ break;
+ barrier();
+ if (i == 13)
+ break;
+ barrier();
+ if (i == 14)
+ break;
+ }
+ return i;
+}
diff --git a/tools/testing/selftests/bpf/progs/netcnt_prog.c b/tools/testing/selftests/bpf/progs/netcnt_prog.c
index 9f741e69cebe..38a997852cad 100644
--- a/tools/testing/selftests/bpf/progs/netcnt_prog.c
+++ b/tools/testing/selftests/bpf/progs/netcnt_prog.c
@@ -10,23 +10,17 @@
#define REFRESH_TIME_NS 100000000
#define NS_PER_SEC 1000000000
-struct bpf_map_def SEC("maps") percpu_netcnt = {
- .type = BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
- .key_size = sizeof(struct bpf_cgroup_storage_key),
- .value_size = sizeof(struct percpu_net_cnt),
-};
-
-BPF_ANNOTATE_KV_PAIR(percpu_netcnt, struct bpf_cgroup_storage_key,
- struct percpu_net_cnt);
-
-struct bpf_map_def SEC("maps") netcnt = {
- .type = BPF_MAP_TYPE_CGROUP_STORAGE,
- .key_size = sizeof(struct bpf_cgroup_storage_key),
- .value_size = sizeof(struct net_cnt),
-};
-
-BPF_ANNOTATE_KV_PAIR(netcnt, struct bpf_cgroup_storage_key,
- struct net_cnt);
+struct {
+ __uint(type, BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE);
+ __type(key, struct bpf_cgroup_storage_key);
+ __type(value, struct percpu_net_cnt);
+} percpu_netcnt SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_CGROUP_STORAGE);
+ __type(key, struct bpf_cgroup_storage_key);
+ __type(value, struct net_cnt);
+} netcnt SEC(".maps");
SEC("cgroup/skb")
int bpf_nextcnt(struct __sk_buff *skb)
diff --git a/tools/testing/selftests/bpf/progs/pyperf.h b/tools/testing/selftests/bpf/progs/pyperf.h
new file mode 100644
index 000000000000..003fe106fc70
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/pyperf.h
@@ -0,0 +1,263 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+#define FUNCTION_NAME_LEN 64
+#define FILE_NAME_LEN 128
+#define TASK_COMM_LEN 16
+
+typedef struct {
+ int PyThreadState_frame;
+ int PyThreadState_thread;
+ int PyFrameObject_back;
+ int PyFrameObject_code;
+ int PyFrameObject_lineno;
+ int PyCodeObject_filename;
+ int PyCodeObject_name;
+ int String_data;
+ int String_size;
+} OffsetConfig;
+
+typedef struct {
+ uintptr_t current_state_addr;
+ uintptr_t tls_key_addr;
+ OffsetConfig offsets;
+ bool use_tls;
+} PidData;
+
+typedef struct {
+ uint32_t success;
+} Stats;
+
+typedef struct {
+ char name[FUNCTION_NAME_LEN];
+ char file[FILE_NAME_LEN];
+} Symbol;
+
+typedef struct {
+ uint32_t pid;
+ uint32_t tid;
+ char comm[TASK_COMM_LEN];
+ int32_t kernel_stack_id;
+ int32_t user_stack_id;
+ bool thread_current;
+ bool pthread_match;
+ bool stack_complete;
+ int16_t stack_len;
+ int32_t stack[STACK_MAX_LEN];
+
+ int has_meta;
+ int metadata;
+ char dummy_safeguard;
+} Event;
+
+
+typedef int pid_t;
+
+typedef struct {
+ void* f_back; // PyFrameObject.f_back, previous frame
+ void* f_code; // PyFrameObject.f_code, pointer to PyCodeObject
+ void* co_filename; // PyCodeObject.co_filename
+ void* co_name; // PyCodeObject.co_name
+} FrameData;
+
+static __always_inline void *get_thread_state(void *tls_base, PidData *pidData)
+{
+ void* thread_state;
+ int key;
+
+ bpf_probe_read(&key, sizeof(key), (void*)(long)pidData->tls_key_addr);
+ bpf_probe_read(&thread_state, sizeof(thread_state),
+ tls_base + 0x310 + key * 0x10 + 0x08);
+ return thread_state;
+}
+
+static __always_inline bool get_frame_data(void *frame_ptr, PidData *pidData,
+ FrameData *frame, Symbol *symbol)
+{
+ // read data from PyFrameObject
+ bpf_probe_read(&frame->f_back,
+ sizeof(frame->f_back),
+ frame_ptr + pidData->offsets.PyFrameObject_back);
+ bpf_probe_read(&frame->f_code,
+ sizeof(frame->f_code),
+ frame_ptr + pidData->offsets.PyFrameObject_code);
+
+ // read data from PyCodeObject
+ if (!frame->f_code)
+ return false;
+ bpf_probe_read(&frame->co_filename,
+ sizeof(frame->co_filename),
+ frame->f_code + pidData->offsets.PyCodeObject_filename);
+ bpf_probe_read(&frame->co_name,
+ sizeof(frame->co_name),
+ frame->f_code + pidData->offsets.PyCodeObject_name);
+ // read actual names into symbol
+ if (frame->co_filename)
+ bpf_probe_read_str(&symbol->file,
+ sizeof(symbol->file),
+ frame->co_filename + pidData->offsets.String_data);
+ if (frame->co_name)
+ bpf_probe_read_str(&symbol->name,
+ sizeof(symbol->name),
+ frame->co_name + pidData->offsets.String_data);
+ return true;
+}
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 1);
+ __type(key, int);
+ __type(value, PidData);
+} pidmap SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 1);
+ __type(key, int);
+ __type(value, Event);
+} eventmap SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 1);
+ __type(key, Symbol);
+ __type(value, int);
+} symbolmap SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, int);
+ __type(value, Stats);
+} statsmap SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
+ __uint(max_entries, 32);
+ __uint(key_size, sizeof(int));
+ __uint(value_size, sizeof(int));
+} perfmap SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_STACK_TRACE);
+ __uint(max_entries, 1000);
+ __uint(key_size, sizeof(int));
+ __uint(value_size, sizeof(long long) * 127);
+} stackmap SEC(".maps");
+
+static __always_inline int __on_event(struct pt_regs *ctx)
+{
+ uint64_t pid_tgid = bpf_get_current_pid_tgid();
+ pid_t pid = (pid_t)(pid_tgid >> 32);
+ PidData* pidData = bpf_map_lookup_elem(&pidmap, &pid);
+ if (!pidData)
+ return 0;
+
+ int zero = 0;
+ Event* event = bpf_map_lookup_elem(&eventmap, &zero);
+ if (!event)
+ return 0;
+
+ event->pid = pid;
+
+ event->tid = (pid_t)pid_tgid;
+ bpf_get_current_comm(&event->comm, sizeof(event->comm));
+
+ event->user_stack_id = bpf_get_stackid(ctx, &stackmap, BPF_F_USER_STACK);
+ event->kernel_stack_id = bpf_get_stackid(ctx, &stackmap, 0);
+
+ void* thread_state_current = (void*)0;
+ bpf_probe_read(&thread_state_current,
+ sizeof(thread_state_current),
+ (void*)(long)pidData->current_state_addr);
+
+ struct task_struct* task = (struct task_struct*)bpf_get_current_task();
+ void* tls_base = (void*)task;
+
+ void* thread_state = pidData->use_tls ? get_thread_state(tls_base, pidData)
+ : thread_state_current;
+ event->thread_current = thread_state == thread_state_current;
+
+ if (pidData->use_tls) {
+ uint64_t pthread_created;
+ uint64_t pthread_self;
+ bpf_probe_read(&pthread_self, sizeof(pthread_self), tls_base + 0x10);
+
+ bpf_probe_read(&pthread_created,
+ sizeof(pthread_created),
+ thread_state + pidData->offsets.PyThreadState_thread);
+ event->pthread_match = pthread_created == pthread_self;
+ } else {
+ event->pthread_match = 1;
+ }
+
+ if (event->pthread_match || !pidData->use_tls) {
+ void* frame_ptr;
+ FrameData frame;
+ Symbol sym = {};
+ int cur_cpu = bpf_get_smp_processor_id();
+
+ bpf_probe_read(&frame_ptr,
+ sizeof(frame_ptr),
+ thread_state + pidData->offsets.PyThreadState_frame);
+
+ int32_t* symbol_counter = bpf_map_lookup_elem(&symbolmap, &sym);
+ if (symbol_counter == NULL)
+ return 0;
+#ifdef NO_UNROLL
+#pragma clang loop unroll(disable)
+#else
+#pragma clang loop unroll(full)
+#endif
+ /* Unwind python stack */
+ for (int i = 0; i < STACK_MAX_LEN; ++i) {
+ if (frame_ptr && get_frame_data(frame_ptr, pidData, &frame, &sym)) {
+ int32_t new_symbol_id = *symbol_counter * 64 + cur_cpu;
+ int32_t *symbol_id = bpf_map_lookup_elem(&symbolmap, &sym);
+ if (!symbol_id) {
+ bpf_map_update_elem(&symbolmap, &sym, &zero, 0);
+ symbol_id = bpf_map_lookup_elem(&symbolmap, &sym);
+ if (!symbol_id)
+ return 0;
+ }
+ if (*symbol_id == new_symbol_id)
+ (*symbol_counter)++;
+ event->stack[i] = *symbol_id;
+ event->stack_len = i + 1;
+ frame_ptr = frame.f_back;
+ }
+ }
+ event->stack_complete = frame_ptr == NULL;
+ } else {
+ event->stack_complete = 1;
+ }
+
+ Stats* stats = bpf_map_lookup_elem(&statsmap, &zero);
+ if (stats)
+ stats->success++;
+
+ event->has_meta = 0;
+ bpf_perf_event_output(ctx, &perfmap, 0, event, offsetof(Event, metadata));
+ return 0;
+}
+
+SEC("raw_tracepoint/kfree_skb")
+int on_event(struct pt_regs* ctx)
+{
+ int i, ret = 0;
+ ret |= __on_event(ctx);
+ ret |= __on_event(ctx);
+ ret |= __on_event(ctx);
+ ret |= __on_event(ctx);
+ ret |= __on_event(ctx);
+ return ret;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/pyperf100.c b/tools/testing/selftests/bpf/progs/pyperf100.c
new file mode 100644
index 000000000000..29786325db54
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/pyperf100.c
@@ -0,0 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+#define STACK_MAX_LEN 100
+#include "pyperf.h"
diff --git a/tools/testing/selftests/bpf/progs/pyperf180.c b/tools/testing/selftests/bpf/progs/pyperf180.c
new file mode 100644
index 000000000000..c39f559d3100
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/pyperf180.c
@@ -0,0 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+#define STACK_MAX_LEN 180
+#include "pyperf.h"
diff --git a/tools/testing/selftests/bpf/progs/pyperf50.c b/tools/testing/selftests/bpf/progs/pyperf50.c
new file mode 100644
index 000000000000..ef7ce340a292
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/pyperf50.c
@@ -0,0 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+#define STACK_MAX_LEN 50
+#include "pyperf.h"
diff --git a/tools/testing/selftests/bpf/progs/pyperf600.c b/tools/testing/selftests/bpf/progs/pyperf600.c
new file mode 100644
index 000000000000..cb49b89e37cd
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/pyperf600.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+#define STACK_MAX_LEN 600
+/* clang will not unroll the loop 600 times.
+ * Instead it will unroll it to the amount it deemed
+ * appropriate, but the loop will still execute 600 times.
+ * Total program size is around 90k insns
+ */
+#include "pyperf.h"
diff --git a/tools/testing/selftests/bpf/progs/pyperf600_nounroll.c b/tools/testing/selftests/bpf/progs/pyperf600_nounroll.c
new file mode 100644
index 000000000000..6beff7502f4d
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/pyperf600_nounroll.c
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+#define STACK_MAX_LEN 600
+#define NO_UNROLL
+/* clang will not unroll at all.
+ * Total program size is around 2k insns
+ */
+#include "pyperf.h"
diff --git a/tools/testing/selftests/bpf/progs/sendmsg6_prog.c b/tools/testing/selftests/bpf/progs/sendmsg6_prog.c
index 5aeaa284fc47..a68062820410 100644
--- a/tools/testing/selftests/bpf/progs/sendmsg6_prog.c
+++ b/tools/testing/selftests/bpf/progs/sendmsg6_prog.c
@@ -41,8 +41,7 @@ int sendmsg_v6_prog(struct bpf_sock_addr *ctx)
}
/* Rewrite destination. */
- if ((ctx->user_ip6[0] & 0xFFFF) == bpf_htons(0xFACE) &&
- ctx->user_ip6[0] >> 16 == bpf_htons(0xB00C)) {
+ if (ctx->user_ip6[0] == bpf_htonl(0xFACEB00C)) {
ctx->user_ip6[0] = bpf_htonl(DST_REWRITE_IP6_0);
ctx->user_ip6[1] = bpf_htonl(DST_REWRITE_IP6_1);
ctx->user_ip6[2] = bpf_htonl(DST_REWRITE_IP6_2);
diff --git a/tools/testing/selftests/bpf/progs/socket_cookie_prog.c b/tools/testing/selftests/bpf/progs/socket_cookie_prog.c
index 9ff8ac4b0bf6..e4440fdd94cb 100644
--- a/tools/testing/selftests/bpf/progs/socket_cookie_prog.c
+++ b/tools/testing/selftests/bpf/progs/socket_cookie_prog.c
@@ -7,25 +7,33 @@
#include "bpf_helpers.h"
#include "bpf_endian.h"
-struct bpf_map_def SEC("maps") socket_cookies = {
- .type = BPF_MAP_TYPE_HASH,
- .key_size = sizeof(__u64),
- .value_size = sizeof(__u32),
- .max_entries = 1 << 8,
+struct socket_cookie {
+ __u64 cookie_key;
+ __u32 cookie_value;
};
+struct {
+ __uint(type, BPF_MAP_TYPE_SK_STORAGE);
+ __uint(map_flags, BPF_F_NO_PREALLOC);
+ __type(key, int);
+ __type(value, struct socket_cookie);
+} socket_cookies SEC(".maps");
+
SEC("cgroup/connect6")
int set_cookie(struct bpf_sock_addr *ctx)
{
- __u32 cookie_value = 0xFF;
- __u64 cookie_key;
+ struct socket_cookie *p;
if (ctx->family != AF_INET6 || ctx->user_family != AF_INET6)
return 1;
- cookie_key = bpf_get_socket_cookie(ctx);
- if (bpf_map_update_elem(&socket_cookies, &cookie_key, &cookie_value, 0))
- return 0;
+ p = bpf_sk_storage_get(&socket_cookies, ctx->sk, 0,
+ BPF_SK_STORAGE_GET_F_CREATE);
+ if (!p)
+ return 1;
+
+ p->cookie_value = 0xFF;
+ p->cookie_key = bpf_get_socket_cookie(ctx);
return 1;
}
@@ -33,9 +41,8 @@ int set_cookie(struct bpf_sock_addr *ctx)
SEC("sockops")
int update_cookie(struct bpf_sock_ops *ctx)
{
- __u32 new_cookie_value;
- __u32 *cookie_value;
- __u64 cookie_key;
+ struct bpf_sock *sk;
+ struct socket_cookie *p;
if (ctx->family != AF_INET6)
return 1;
@@ -43,14 +50,17 @@ int update_cookie(struct bpf_sock_ops *ctx)
if (ctx->op != BPF_SOCK_OPS_TCP_CONNECT_CB)
return 1;
- cookie_key = bpf_get_socket_cookie(ctx);
+ if (!ctx->sk)
+ return 1;
+
+ p = bpf_sk_storage_get(&socket_cookies, ctx->sk, 0, 0);
+ if (!p)
+ return 1;
- cookie_value = bpf_map_lookup_elem(&socket_cookies, &cookie_key);
- if (!cookie_value)
+ if (p->cookie_key != bpf_get_socket_cookie(ctx))
return 1;
- new_cookie_value = (ctx->local_port << 8) | *cookie_value;
- bpf_map_update_elem(&socket_cookies, &cookie_key, &new_cookie_value, 0);
+ p->cookie_value = (ctx->local_port << 8) | p->cookie_value;
return 1;
}
diff --git a/tools/testing/selftests/bpf/progs/sockmap_parse_prog.c b/tools/testing/selftests/bpf/progs/sockmap_parse_prog.c
index 0f92858f6226..9390e0244259 100644
--- a/tools/testing/selftests/bpf/progs/sockmap_parse_prog.c
+++ b/tools/testing/selftests/bpf/progs/sockmap_parse_prog.c
@@ -1,17 +1,9 @@
#include <linux/bpf.h>
#include "bpf_helpers.h"
-#include "bpf_util.h"
#include "bpf_endian.h"
int _version SEC("version") = 1;
-#define bpf_printk(fmt, ...) \
-({ \
- char ____fmt[] = fmt; \
- bpf_trace_printk(____fmt, sizeof(____fmt), \
- ##__VA_ARGS__); \
-})
-
SEC("sk_skb1")
int bpf_prog1(struct __sk_buff *skb)
{
diff --git a/tools/testing/selftests/bpf/progs/sockmap_tcp_msg_prog.c b/tools/testing/selftests/bpf/progs/sockmap_tcp_msg_prog.c
index 12a7b5c82ed6..e80484d98a1a 100644
--- a/tools/testing/selftests/bpf/progs/sockmap_tcp_msg_prog.c
+++ b/tools/testing/selftests/bpf/progs/sockmap_tcp_msg_prog.c
@@ -1,17 +1,10 @@
#include <linux/bpf.h>
+
#include "bpf_helpers.h"
-#include "bpf_util.h"
#include "bpf_endian.h"
int _version SEC("version") = 1;
-#define bpf_printk(fmt, ...) \
-({ \
- char ____fmt[] = fmt; \
- bpf_trace_printk(____fmt, sizeof(____fmt), \
- ##__VA_ARGS__); \
-})
-
SEC("sk_msg1")
int bpf_prog1(struct sk_msg_md *msg)
{
diff --git a/tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c b/tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c
index 2ce7634a4012..433e23918a62 100644
--- a/tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c
+++ b/tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c
@@ -1,44 +1,36 @@
#include <linux/bpf.h>
#include "bpf_helpers.h"
-#include "bpf_util.h"
#include "bpf_endian.h"
int _version SEC("version") = 1;
-#define bpf_printk(fmt, ...) \
-({ \
- char ____fmt[] = fmt; \
- bpf_trace_printk(____fmt, sizeof(____fmt), \
- ##__VA_ARGS__); \
-})
+struct {
+ __uint(type, BPF_MAP_TYPE_SOCKMAP);
+ __uint(max_entries, 20);
+ __uint(key_size, sizeof(int));
+ __uint(value_size, sizeof(int));
+} sock_map_rx SEC(".maps");
-struct bpf_map_def SEC("maps") sock_map_rx = {
- .type = BPF_MAP_TYPE_SOCKMAP,
- .key_size = sizeof(int),
- .value_size = sizeof(int),
- .max_entries = 20,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_SOCKMAP);
+ __uint(max_entries, 20);
+ __uint(key_size, sizeof(int));
+ __uint(value_size, sizeof(int));
+} sock_map_tx SEC(".maps");
-struct bpf_map_def SEC("maps") sock_map_tx = {
- .type = BPF_MAP_TYPE_SOCKMAP,
- .key_size = sizeof(int),
- .value_size = sizeof(int),
- .max_entries = 20,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_SOCKMAP);
+ __uint(max_entries, 20);
+ __uint(key_size, sizeof(int));
+ __uint(value_size, sizeof(int));
+} sock_map_msg SEC(".maps");
-struct bpf_map_def SEC("maps") sock_map_msg = {
- .type = BPF_MAP_TYPE_SOCKMAP,
- .key_size = sizeof(int),
- .value_size = sizeof(int),
- .max_entries = 20,
-};
-
-struct bpf_map_def SEC("maps") sock_map_break = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(int),
- .value_size = sizeof(int),
- .max_entries = 20,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 20);
+ __type(key, int);
+ __type(value, int);
+} sock_map_break SEC(".maps");
SEC("sk_skb2")
int bpf_prog2(struct __sk_buff *skb)
diff --git a/tools/testing/selftests/bpf/progs/sockopt_inherit.c b/tools/testing/selftests/bpf/progs/sockopt_inherit.c
new file mode 100644
index 000000000000..dede0fcd6102
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/sockopt_inherit.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+__u32 _version SEC("version") = 1;
+
+#define SOL_CUSTOM 0xdeadbeef
+#define CUSTOM_INHERIT1 0
+#define CUSTOM_INHERIT2 1
+#define CUSTOM_LISTENER 2
+
+struct sockopt_inherit {
+ __u8 val;
+};
+
+struct {
+ __uint(type, BPF_MAP_TYPE_SK_STORAGE);
+ __uint(map_flags, BPF_F_NO_PREALLOC | BPF_F_CLONE);
+ __type(key, int);
+ __type(value, struct sockopt_inherit);
+} cloned1_map SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_SK_STORAGE);
+ __uint(map_flags, BPF_F_NO_PREALLOC | BPF_F_CLONE);
+ __type(key, int);
+ __type(value, struct sockopt_inherit);
+} cloned2_map SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_SK_STORAGE);
+ __uint(map_flags, BPF_F_NO_PREALLOC);
+ __type(key, int);
+ __type(value, struct sockopt_inherit);
+} listener_only_map SEC(".maps");
+
+static __inline struct sockopt_inherit *get_storage(struct bpf_sockopt *ctx)
+{
+ if (ctx->optname == CUSTOM_INHERIT1)
+ return bpf_sk_storage_get(&cloned1_map, ctx->sk, 0,
+ BPF_SK_STORAGE_GET_F_CREATE);
+ else if (ctx->optname == CUSTOM_INHERIT2)
+ return bpf_sk_storage_get(&cloned2_map, ctx->sk, 0,
+ BPF_SK_STORAGE_GET_F_CREATE);
+ else
+ return bpf_sk_storage_get(&listener_only_map, ctx->sk, 0,
+ BPF_SK_STORAGE_GET_F_CREATE);
+}
+
+SEC("cgroup/getsockopt")
+int _getsockopt(struct bpf_sockopt *ctx)
+{
+ __u8 *optval_end = ctx->optval_end;
+ struct sockopt_inherit *storage;
+ __u8 *optval = ctx->optval;
+
+ if (ctx->level != SOL_CUSTOM)
+ return 1; /* only interested in SOL_CUSTOM */
+
+ if (optval + 1 > optval_end)
+ return 0; /* EPERM, bounds check */
+
+ storage = get_storage(ctx);
+ if (!storage)
+ return 0; /* EPERM, couldn't get sk storage */
+
+ ctx->retval = 0; /* Reset system call return value to zero */
+
+ optval[0] = storage->val;
+ ctx->optlen = 1;
+
+ return 1;
+}
+
+SEC("cgroup/setsockopt")
+int _setsockopt(struct bpf_sockopt *ctx)
+{
+ __u8 *optval_end = ctx->optval_end;
+ struct sockopt_inherit *storage;
+ __u8 *optval = ctx->optval;
+
+ if (ctx->level != SOL_CUSTOM)
+ return 1; /* only interested in SOL_CUSTOM */
+
+ if (optval + 1 > optval_end)
+ return 0; /* EPERM, bounds check */
+
+ storage = get_storage(ctx);
+ if (!storage)
+ return 0; /* EPERM, couldn't get sk storage */
+
+ storage->val = optval[0];
+ ctx->optlen = -1;
+
+ return 1;
+}
diff --git a/tools/testing/selftests/bpf/progs/sockopt_multi.c b/tools/testing/selftests/bpf/progs/sockopt_multi.c
new file mode 100644
index 000000000000..4afd2595c08e
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/sockopt_multi.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <netinet/in.h>
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+__u32 _version SEC("version") = 1;
+
+SEC("cgroup/getsockopt/child")
+int _getsockopt_child(struct bpf_sockopt *ctx)
+{
+ __u8 *optval_end = ctx->optval_end;
+ __u8 *optval = ctx->optval;
+
+ if (ctx->level != SOL_IP || ctx->optname != IP_TOS)
+ return 1;
+
+ if (optval + 1 > optval_end)
+ return 0; /* EPERM, bounds check */
+
+ if (optval[0] != 0x80)
+ return 0; /* EPERM, unexpected optval from the kernel */
+
+ ctx->retval = 0; /* Reset system call return value to zero */
+
+ optval[0] = 0x90;
+ ctx->optlen = 1;
+
+ return 1;
+}
+
+SEC("cgroup/getsockopt/parent")
+int _getsockopt_parent(struct bpf_sockopt *ctx)
+{
+ __u8 *optval_end = ctx->optval_end;
+ __u8 *optval = ctx->optval;
+
+ if (ctx->level != SOL_IP || ctx->optname != IP_TOS)
+ return 1;
+
+ if (optval + 1 > optval_end)
+ return 0; /* EPERM, bounds check */
+
+ if (optval[0] != 0x90)
+ return 0; /* EPERM, unexpected optval from the kernel */
+
+ ctx->retval = 0; /* Reset system call return value to zero */
+
+ optval[0] = 0xA0;
+ ctx->optlen = 1;
+
+ return 1;
+}
+
+SEC("cgroup/setsockopt")
+int _setsockopt(struct bpf_sockopt *ctx)
+{
+ __u8 *optval_end = ctx->optval_end;
+ __u8 *optval = ctx->optval;
+
+ if (ctx->level != SOL_IP || ctx->optname != IP_TOS)
+ return 1;
+
+ if (optval + 1 > optval_end)
+ return 0; /* EPERM, bounds check */
+
+ optval[0] += 0x10;
+ ctx->optlen = 1;
+
+ return 1;
+}
diff --git a/tools/testing/selftests/bpf/progs/sockopt_sk.c b/tools/testing/selftests/bpf/progs/sockopt_sk.c
new file mode 100644
index 000000000000..9a3d1c79e6fe
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/sockopt_sk.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <string.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+__u32 _version SEC("version") = 1;
+
+#define SOL_CUSTOM 0xdeadbeef
+
+struct sockopt_sk {
+ __u8 val;
+};
+
+struct bpf_map_def SEC("maps") socket_storage_map = {
+ .type = BPF_MAP_TYPE_SK_STORAGE,
+ .key_size = sizeof(int),
+ .value_size = sizeof(struct sockopt_sk),
+ .map_flags = BPF_F_NO_PREALLOC,
+};
+BPF_ANNOTATE_KV_PAIR(socket_storage_map, int, struct sockopt_sk);
+
+SEC("cgroup/getsockopt")
+int _getsockopt(struct bpf_sockopt *ctx)
+{
+ __u8 *optval_end = ctx->optval_end;
+ __u8 *optval = ctx->optval;
+ struct sockopt_sk *storage;
+
+ if (ctx->level == SOL_IP && ctx->optname == IP_TOS)
+ /* Not interested in SOL_IP:IP_TOS;
+ * let next BPF program in the cgroup chain or kernel
+ * handle it.
+ */
+ return 1;
+
+ if (ctx->level == SOL_SOCKET && ctx->optname == SO_SNDBUF) {
+ /* Not interested in SOL_SOCKET:SO_SNDBUF;
+ * let next BPF program in the cgroup chain or kernel
+ * handle it.
+ */
+ return 1;
+ }
+
+ if (ctx->level == SOL_TCP && ctx->optname == TCP_CONGESTION) {
+ /* Not interested in SOL_TCP:TCP_CONGESTION;
+ * let next BPF program in the cgroup chain or kernel
+ * handle it.
+ */
+ return 1;
+ }
+
+ if (ctx->level != SOL_CUSTOM)
+ return 0; /* EPERM, deny everything except custom level */
+
+ if (optval + 1 > optval_end)
+ return 0; /* EPERM, bounds check */
+
+ storage = bpf_sk_storage_get(&socket_storage_map, ctx->sk, 0,
+ BPF_SK_STORAGE_GET_F_CREATE);
+ if (!storage)
+ return 0; /* EPERM, couldn't get sk storage */
+
+ if (!ctx->retval)
+ return 0; /* EPERM, kernel should not have handled
+ * SOL_CUSTOM, something is wrong!
+ */
+ ctx->retval = 0; /* Reset system call return value to zero */
+
+ optval[0] = storage->val;
+ ctx->optlen = 1;
+
+ return 1;
+}
+
+SEC("cgroup/setsockopt")
+int _setsockopt(struct bpf_sockopt *ctx)
+{
+ __u8 *optval_end = ctx->optval_end;
+ __u8 *optval = ctx->optval;
+ struct sockopt_sk *storage;
+
+ if (ctx->level == SOL_IP && ctx->optname == IP_TOS)
+ /* Not interested in SOL_IP:IP_TOS;
+ * let next BPF program in the cgroup chain or kernel
+ * handle it.
+ */
+ return 1;
+
+ if (ctx->level == SOL_SOCKET && ctx->optname == SO_SNDBUF) {
+ /* Overwrite SO_SNDBUF value */
+
+ if (optval + sizeof(__u32) > optval_end)
+ return 0; /* EPERM, bounds check */
+
+ *(__u32 *)optval = 0x55AA;
+ ctx->optlen = 4;
+
+ return 1;
+ }
+
+ if (ctx->level == SOL_TCP && ctx->optname == TCP_CONGESTION) {
+ /* Always use cubic */
+
+ if (optval + 5 > optval_end)
+ return 0; /* EPERM, bounds check */
+
+ memcpy(optval, "cubic", 5);
+ ctx->optlen = 5;
+
+ return 1;
+ }
+
+ if (ctx->level != SOL_CUSTOM)
+ return 0; /* EPERM, deny everything except custom level */
+
+ if (optval + 1 > optval_end)
+ return 0; /* EPERM, bounds check */
+
+ storage = bpf_sk_storage_get(&socket_storage_map, ctx->sk, 0,
+ BPF_SK_STORAGE_GET_F_CREATE);
+ if (!storage)
+ return 0; /* EPERM, couldn't get sk storage */
+
+ storage->val = optval[0];
+ ctx->optlen = -1; /* BPF has consumed this option, don't call kernel
+ * setsockopt handler.
+ */
+
+ return 1;
+}
diff --git a/tools/testing/selftests/bpf/progs/strobemeta.c b/tools/testing/selftests/bpf/progs/strobemeta.c
new file mode 100644
index 000000000000..d3df3d86f092
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/strobemeta.c
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+// Copyright (c) 2019 Facebook
+
+#define STROBE_MAX_INTS 2
+#define STROBE_MAX_STRS 25
+#define STROBE_MAX_MAPS 100
+#define STROBE_MAX_MAP_ENTRIES 20
+/* full unroll by llvm #undef NO_UNROLL */
+#include "strobemeta.h"
+
diff --git a/tools/testing/selftests/bpf/progs/strobemeta.h b/tools/testing/selftests/bpf/progs/strobemeta.h
new file mode 100644
index 000000000000..8a399bdfd920
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/strobemeta.h
@@ -0,0 +1,530 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <linux/bpf.h>
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include "bpf_helpers.h"
+
+typedef uint32_t pid_t;
+struct task_struct {};
+
+#define TASK_COMM_LEN 16
+#define PERF_MAX_STACK_DEPTH 127
+
+#define STROBE_TYPE_INVALID 0
+#define STROBE_TYPE_INT 1
+#define STROBE_TYPE_STR 2
+#define STROBE_TYPE_MAP 3
+
+#define STACK_TABLE_EPOCH_SHIFT 20
+#define STROBE_MAX_STR_LEN 1
+#define STROBE_MAX_CFGS 32
+#define STROBE_MAX_PAYLOAD \
+ (STROBE_MAX_STRS * STROBE_MAX_STR_LEN + \
+ STROBE_MAX_MAPS * (1 + STROBE_MAX_MAP_ENTRIES * 2) * STROBE_MAX_STR_LEN)
+
+struct strobe_value_header {
+ /*
+ * meaning depends on type:
+ * 1. int: 0, if value not set, 1 otherwise
+ * 2. str: 1 always, whether value is set or not is determined by ptr
+ * 3. map: 1 always, pointer points to additional struct with number
+ * of entries (up to STROBE_MAX_MAP_ENTRIES)
+ */
+ uint16_t len;
+ /*
+ * _reserved might be used for some future fields/flags, but we always
+ * want to keep strobe_value_header to be 8 bytes, so BPF can read 16
+ * bytes in one go and get both header and value
+ */
+ uint8_t _reserved[6];
+};
+
+/*
+ * strobe_value_generic is used from BPF probe only, but needs to be a union
+ * of strobe_value_int/strobe_value_str/strobe_value_map
+ */
+struct strobe_value_generic {
+ struct strobe_value_header header;
+ union {
+ int64_t val;
+ void *ptr;
+ };
+};
+
+struct strobe_value_int {
+ struct strobe_value_header header;
+ int64_t value;
+};
+
+struct strobe_value_str {
+ struct strobe_value_header header;
+ const char* value;
+};
+
+struct strobe_value_map {
+ struct strobe_value_header header;
+ const struct strobe_map_raw* value;
+};
+
+struct strobe_map_entry {
+ const char* key;
+ const char* val;
+};
+
+/*
+ * Map of C-string key/value pairs with fixed maximum capacity. Each map has
+ * corresponding int64 ID, which application can use (or ignore) in whatever
+ * way appropriate. Map is "write-only", there is no way to get data out of
+ * map. Map is intended to be used to provide metadata for profilers and is
+ * not to be used for internal in-app communication. All methods are
+ * thread-safe.
+ */
+struct strobe_map_raw {
+ /*
+ * general purpose unique ID that's up to application to decide
+ * whether and how to use; for request metadata use case id is unique
+ * request ID that's used to match metadata with stack traces on
+ * Strobelight backend side
+ */
+ int64_t id;
+ /* number of used entries in map */
+ int64_t cnt;
+ /*
+ * having volatile doesn't change anything on BPF side, but clang
+ * emits warnings for passing `volatile const char *` into
+ * bpf_probe_read_str that expects just `const char *`
+ */
+ const char* tag;
+ /*
+ * key/value entries, each consisting of 2 pointers to key and value
+ * C strings
+ */
+ struct strobe_map_entry entries[STROBE_MAX_MAP_ENTRIES];
+};
+
+/* Following values define supported values of TLS mode */
+#define TLS_NOT_SET -1
+#define TLS_LOCAL_EXEC 0
+#define TLS_IMM_EXEC 1
+#define TLS_GENERAL_DYN 2
+
+/*
+ * structure that universally represents TLS location (both for static
+ * executables and shared libraries)
+ */
+struct strobe_value_loc {
+ /*
+ * tls_mode defines what TLS mode was used for particular metavariable:
+ * - -1 (TLS_NOT_SET) - no metavariable;
+ * - 0 (TLS_LOCAL_EXEC) - Local Executable mode;
+ * - 1 (TLS_IMM_EXEC) - Immediate Executable mode;
+ * - 2 (TLS_GENERAL_DYN) - General Dynamic mode;
+ * Local Dynamic mode is not yet supported, because never seen in
+ * practice. Mode defines how offset field is interpreted. See
+ * calc_location() in below for details.
+ */
+ int64_t tls_mode;
+ /*
+ * TLS_LOCAL_EXEC: offset from thread pointer (fs:0 for x86-64,
+ * tpidr_el0 for aarch64).
+ * TLS_IMM_EXEC: absolute address of GOT entry containing offset
+ * from thread pointer;
+ * TLS_GENERAL_DYN: absolute addres of double GOT entry
+ * containing tls_index_t struct;
+ */
+ int64_t offset;
+};
+
+struct strobemeta_cfg {
+ int64_t req_meta_idx;
+ struct strobe_value_loc int_locs[STROBE_MAX_INTS];
+ struct strobe_value_loc str_locs[STROBE_MAX_STRS];
+ struct strobe_value_loc map_locs[STROBE_MAX_MAPS];
+};
+
+struct strobe_map_descr {
+ uint64_t id;
+ int16_t tag_len;
+ /*
+ * cnt <0 - map value isn't set;
+ * 0 - map has id set, but no key/value entries
+ */
+ int16_t cnt;
+ /*
+ * both key_lens[i] and val_lens[i] should be >0 for present key/value
+ * entry
+ */
+ uint16_t key_lens[STROBE_MAX_MAP_ENTRIES];
+ uint16_t val_lens[STROBE_MAX_MAP_ENTRIES];
+};
+
+struct strobemeta_payload {
+ /* req_id has valid request ID, if req_meta_valid == 1 */
+ int64_t req_id;
+ uint8_t req_meta_valid;
+ /*
+ * mask has Nth bit set to 1, if Nth metavar was present and
+ * successfully read
+ */
+ uint64_t int_vals_set_mask;
+ int64_t int_vals[STROBE_MAX_INTS];
+ /* len is >0 for present values */
+ uint16_t str_lens[STROBE_MAX_STRS];
+ /* if map_descrs[i].cnt == -1, metavar is not present/set */
+ struct strobe_map_descr map_descrs[STROBE_MAX_MAPS];
+ /*
+ * payload has compactly packed values of str and map variables in the
+ * form: strval1\0strval2\0map1key1\0map1val1\0map2key1\0map2val1\0
+ * (and so on); str_lens[i], key_lens[i] and val_lens[i] determines
+ * value length
+ */
+ char payload[STROBE_MAX_PAYLOAD];
+};
+
+struct strobelight_bpf_sample {
+ uint64_t ktime;
+ char comm[TASK_COMM_LEN];
+ pid_t pid;
+ int user_stack_id;
+ int kernel_stack_id;
+ int has_meta;
+ struct strobemeta_payload metadata;
+ /*
+ * makes it possible to pass (<real payload size> + 1) as data size to
+ * perf_submit() to avoid perf_submit's paranoia about passing zero as
+ * size, as it deduces that <real payload size> might be
+ * **theoretically** zero
+ */
+ char dummy_safeguard;
+};
+
+struct {
+ __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
+ __uint(max_entries, 32);
+ __uint(key_size, sizeof(int));
+ __uint(value_size, sizeof(int));
+} samples SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_STACK_TRACE);
+ __uint(max_entries, 16);
+ __uint(key_size, sizeof(uint32_t));
+ __uint(value_size, sizeof(uint64_t) * PERF_MAX_STACK_DEPTH);
+} stacks_0 SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_STACK_TRACE);
+ __uint(max_entries, 16);
+ __uint(key_size, sizeof(uint32_t));
+ __uint(value_size, sizeof(uint64_t) * PERF_MAX_STACK_DEPTH);
+} stacks_1 SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, uint32_t);
+ __type(value, struct strobelight_bpf_sample);
+} sample_heap SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+ __uint(max_entries, STROBE_MAX_CFGS);
+ __type(key, pid_t);
+ __type(value, struct strobemeta_cfg);
+} strobemeta_cfgs SEC(".maps");
+
+/* Type for the dtv. */
+/* https://github.com/lattera/glibc/blob/master/nptl/sysdeps/x86_64/tls.h#L34 */
+typedef union dtv {
+ size_t counter;
+ struct {
+ void* val;
+ bool is_static;
+ } pointer;
+} dtv_t;
+
+/* Partial definition for tcbhead_t */
+/* https://github.com/bminor/glibc/blob/master/sysdeps/x86_64/nptl/tls.h#L42 */
+struct tcbhead {
+ void* tcb;
+ dtv_t* dtv;
+};
+
+/*
+ * TLS module/offset information for shared library case.
+ * For x86-64, this is mapped onto two entries in GOT.
+ * For aarch64, this is pointed to by second GOT entry.
+ */
+struct tls_index {
+ uint64_t module;
+ uint64_t offset;
+};
+
+static __always_inline void *calc_location(struct strobe_value_loc *loc,
+ void *tls_base)
+{
+ /*
+ * tls_mode value is:
+ * - -1 (TLS_NOT_SET), if no metavar is present;
+ * - 0 (TLS_LOCAL_EXEC), if metavar uses Local Executable mode of TLS
+ * (offset from fs:0 for x86-64 or tpidr_el0 for aarch64);
+ * - 1 (TLS_IMM_EXEC), if metavar uses Immediate Executable mode of TLS;
+ * - 2 (TLS_GENERAL_DYN), if metavar uses General Dynamic mode of TLS;
+ * This schema allows to use something like:
+ * (tls_mode + 1) * (tls_base + offset)
+ * to get NULL for "no metavar" location, or correct pointer for local
+ * executable mode without doing extra ifs.
+ */
+ if (loc->tls_mode <= TLS_LOCAL_EXEC) {
+ /* static executable is simple, we just have offset from
+ * tls_base */
+ void *addr = tls_base + loc->offset;
+ /* multiply by (tls_mode + 1) to get NULL, if we have no
+ * metavar in this slot */
+ return (void *)((loc->tls_mode + 1) * (int64_t)addr);
+ }
+ /*
+ * Other modes are more complicated, we need to jump through few hoops.
+ *
+ * For immediate executable mode (currently supported only for aarch64):
+ * - loc->offset is pointing to a GOT entry containing fixed offset
+ * relative to tls_base;
+ *
+ * For general dynamic mode:
+ * - loc->offset is pointing to a beginning of double GOT entries;
+ * - (for aarch64 only) second entry points to tls_index_t struct;
+ * - (for x86-64 only) two GOT entries are already tls_index_t;
+ * - tls_index_t->module is used to find start of TLS section in
+ * which variable resides;
+ * - tls_index_t->offset provides offset within that TLS section,
+ * pointing to value of variable.
+ */
+ struct tls_index tls_index;
+ dtv_t *dtv;
+ void *tls_ptr;
+
+ bpf_probe_read(&tls_index, sizeof(struct tls_index),
+ (void *)loc->offset);
+ /* valid module index is always positive */
+ if (tls_index.module > 0) {
+ /* dtv = ((struct tcbhead *)tls_base)->dtv[tls_index.module] */
+ bpf_probe_read(&dtv, sizeof(dtv),
+ &((struct tcbhead *)tls_base)->dtv);
+ dtv += tls_index.module;
+ } else {
+ dtv = NULL;
+ }
+ bpf_probe_read(&tls_ptr, sizeof(void *), dtv);
+ /* if pointer has (void *)-1 value, then TLS wasn't initialized yet */
+ return tls_ptr && tls_ptr != (void *)-1
+ ? tls_ptr + tls_index.offset
+ : NULL;
+}
+
+static __always_inline void read_int_var(struct strobemeta_cfg *cfg,
+ size_t idx, void *tls_base,
+ struct strobe_value_generic *value,
+ struct strobemeta_payload *data)
+{
+ void *location = calc_location(&cfg->int_locs[idx], tls_base);
+ if (!location)
+ return;
+
+ bpf_probe_read(value, sizeof(struct strobe_value_generic), location);
+ data->int_vals[idx] = value->val;
+ if (value->header.len)
+ data->int_vals_set_mask |= (1 << idx);
+}
+
+static __always_inline uint64_t read_str_var(struct strobemeta_cfg *cfg,
+ size_t idx, void *tls_base,
+ struct strobe_value_generic *value,
+ struct strobemeta_payload *data,
+ void *payload)
+{
+ void *location;
+ uint32_t len;
+
+ data->str_lens[idx] = 0;
+ location = calc_location(&cfg->str_locs[idx], tls_base);
+ if (!location)
+ return 0;
+
+ bpf_probe_read(value, sizeof(struct strobe_value_generic), location);
+ len = bpf_probe_read_str(payload, STROBE_MAX_STR_LEN, value->ptr);
+ /*
+ * if bpf_probe_read_str returns error (<0), due to casting to
+ * unsinged int, it will become big number, so next check is
+ * sufficient to check for errors AND prove to BPF verifier, that
+ * bpf_probe_read_str won't return anything bigger than
+ * STROBE_MAX_STR_LEN
+ */
+ if (len > STROBE_MAX_STR_LEN)
+ return 0;
+
+ data->str_lens[idx] = len;
+ return len;
+}
+
+static __always_inline void *read_map_var(struct strobemeta_cfg *cfg,
+ size_t idx, void *tls_base,
+ struct strobe_value_generic *value,
+ struct strobemeta_payload *data,
+ void *payload)
+{
+ struct strobe_map_descr* descr = &data->map_descrs[idx];
+ struct strobe_map_raw map;
+ void *location;
+ uint32_t len;
+ int i;
+
+ descr->tag_len = 0; /* presume no tag is set */
+ descr->cnt = -1; /* presume no value is set */
+
+ location = calc_location(&cfg->map_locs[idx], tls_base);
+ if (!location)
+ return payload;
+
+ bpf_probe_read(value, sizeof(struct strobe_value_generic), location);
+ if (bpf_probe_read(&map, sizeof(struct strobe_map_raw), value->ptr))
+ return payload;
+
+ descr->id = map.id;
+ descr->cnt = map.cnt;
+ if (cfg->req_meta_idx == idx) {
+ data->req_id = map.id;
+ data->req_meta_valid = 1;
+ }
+
+ len = bpf_probe_read_str(payload, STROBE_MAX_STR_LEN, map.tag);
+ if (len <= STROBE_MAX_STR_LEN) {
+ descr->tag_len = len;
+ payload += len;
+ }
+
+#ifdef NO_UNROLL
+#pragma clang loop unroll(disable)
+#else
+#pragma unroll
+#endif
+ for (int i = 0; i < STROBE_MAX_MAP_ENTRIES && i < map.cnt; ++i) {
+ descr->key_lens[i] = 0;
+ len = bpf_probe_read_str(payload, STROBE_MAX_STR_LEN,
+ map.entries[i].key);
+ if (len <= STROBE_MAX_STR_LEN) {
+ descr->key_lens[i] = len;
+ payload += len;
+ }
+ descr->val_lens[i] = 0;
+ len = bpf_probe_read_str(payload, STROBE_MAX_STR_LEN,
+ map.entries[i].val);
+ if (len <= STROBE_MAX_STR_LEN) {
+ descr->val_lens[i] = len;
+ payload += len;
+ }
+ }
+
+ return payload;
+}
+
+/*
+ * read_strobe_meta returns NULL, if no metadata was read; otherwise returns
+ * pointer to *right after* payload ends
+ */
+static __always_inline void *read_strobe_meta(struct task_struct *task,
+ struct strobemeta_payload *data)
+{
+ pid_t pid = bpf_get_current_pid_tgid() >> 32;
+ struct strobe_value_generic value = {0};
+ struct strobemeta_cfg *cfg;
+ void *tls_base, *payload;
+
+ cfg = bpf_map_lookup_elem(&strobemeta_cfgs, &pid);
+ if (!cfg)
+ return NULL;
+
+ data->int_vals_set_mask = 0;
+ data->req_meta_valid = 0;
+ payload = data->payload;
+ /*
+ * we don't have struct task_struct definition, it should be:
+ * tls_base = (void *)task->thread.fsbase;
+ */
+ tls_base = (void *)task;
+
+#ifdef NO_UNROLL
+#pragma clang loop unroll(disable)
+#else
+#pragma unroll
+#endif
+ for (int i = 0; i < STROBE_MAX_INTS; ++i) {
+ read_int_var(cfg, i, tls_base, &value, data);
+ }
+#ifdef NO_UNROLL
+#pragma clang loop unroll(disable)
+#else
+#pragma unroll
+#endif
+ for (int i = 0; i < STROBE_MAX_STRS; ++i) {
+ payload += read_str_var(cfg, i, tls_base, &value, data, payload);
+ }
+#ifdef NO_UNROLL
+#pragma clang loop unroll(disable)
+#else
+#pragma unroll
+#endif
+ for (int i = 0; i < STROBE_MAX_MAPS; ++i) {
+ payload = read_map_var(cfg, i, tls_base, &value, data, payload);
+ }
+ /*
+ * return pointer right after end of payload, so it's possible to
+ * calculate exact amount of useful data that needs to be sent
+ */
+ return payload;
+}
+
+SEC("raw_tracepoint/kfree_skb")
+int on_event(struct pt_regs *ctx) {
+ pid_t pid = bpf_get_current_pid_tgid() >> 32;
+ struct strobelight_bpf_sample* sample;
+ struct task_struct *task;
+ uint32_t zero = 0;
+ uint64_t ktime_ns;
+ void *sample_end;
+
+ sample = bpf_map_lookup_elem(&sample_heap, &zero);
+ if (!sample)
+ return 0; /* this will never happen */
+
+ sample->pid = pid;
+ bpf_get_current_comm(&sample->comm, TASK_COMM_LEN);
+ ktime_ns = bpf_ktime_get_ns();
+ sample->ktime = ktime_ns;
+
+ task = (struct task_struct *)bpf_get_current_task();
+ sample_end = read_strobe_meta(task, &sample->metadata);
+ sample->has_meta = sample_end != NULL;
+ sample_end = sample_end ? : &sample->metadata;
+
+ if ((ktime_ns >> STACK_TABLE_EPOCH_SHIFT) & 1) {
+ sample->kernel_stack_id = bpf_get_stackid(ctx, &stacks_1, 0);
+ sample->user_stack_id = bpf_get_stackid(ctx, &stacks_1, BPF_F_USER_STACK);
+ } else {
+ sample->kernel_stack_id = bpf_get_stackid(ctx, &stacks_0, 0);
+ sample->user_stack_id = bpf_get_stackid(ctx, &stacks_0, BPF_F_USER_STACK);
+ }
+
+ uint64_t sample_size = sample_end - (void *)sample;
+ /* should always be true */
+ if (sample_size < sizeof(struct strobelight_bpf_sample))
+ bpf_perf_event_output(ctx, &samples, 0, sample, 1 + sample_size);
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/strobemeta_nounroll1.c b/tools/testing/selftests/bpf/progs/strobemeta_nounroll1.c
new file mode 100644
index 000000000000..f0a1669e11d6
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/strobemeta_nounroll1.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+// Copyright (c) 2019 Facebook
+
+#define STROBE_MAX_INTS 2
+#define STROBE_MAX_STRS 25
+#define STROBE_MAX_MAPS 13
+#define STROBE_MAX_MAP_ENTRIES 20
+#define NO_UNROLL
+#include "strobemeta.h"
diff --git a/tools/testing/selftests/bpf/progs/strobemeta_nounroll2.c b/tools/testing/selftests/bpf/progs/strobemeta_nounroll2.c
new file mode 100644
index 000000000000..4291a7d642e7
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/strobemeta_nounroll2.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+// Copyright (c) 2019 Facebook
+
+#define STROBE_MAX_INTS 2
+#define STROBE_MAX_STRS 25
+#define STROBE_MAX_MAPS 30
+#define STROBE_MAX_MAP_ENTRIES 20
+#define NO_UNROLL
+#include "strobemeta.h"
diff --git a/tools/testing/selftests/bpf/progs/tcp_rtt.c b/tools/testing/selftests/bpf/progs/tcp_rtt.c
new file mode 100644
index 000000000000..233bdcb1659e
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/tcp_rtt.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+__u32 _version SEC("version") = 1;
+
+struct tcp_rtt_storage {
+ __u32 invoked;
+ __u32 dsack_dups;
+ __u32 delivered;
+ __u32 delivered_ce;
+ __u32 icsk_retransmits;
+};
+
+struct bpf_map_def SEC("maps") socket_storage_map = {
+ .type = BPF_MAP_TYPE_SK_STORAGE,
+ .key_size = sizeof(int),
+ .value_size = sizeof(struct tcp_rtt_storage),
+ .map_flags = BPF_F_NO_PREALLOC,
+};
+BPF_ANNOTATE_KV_PAIR(socket_storage_map, int, struct tcp_rtt_storage);
+
+SEC("sockops")
+int _sockops(struct bpf_sock_ops *ctx)
+{
+ struct tcp_rtt_storage *storage;
+ struct bpf_tcp_sock *tcp_sk;
+ int op = (int) ctx->op;
+ struct bpf_sock *sk;
+
+ sk = ctx->sk;
+ if (!sk)
+ return 1;
+
+ storage = bpf_sk_storage_get(&socket_storage_map, sk, 0,
+ BPF_SK_STORAGE_GET_F_CREATE);
+ if (!storage)
+ return 1;
+
+ if (op == BPF_SOCK_OPS_TCP_CONNECT_CB) {
+ bpf_sock_ops_cb_flags_set(ctx, BPF_SOCK_OPS_RTT_CB_FLAG);
+ return 1;
+ }
+
+ if (op != BPF_SOCK_OPS_RTT_CB)
+ return 1;
+
+ tcp_sk = bpf_tcp_sock(sk);
+ if (!tcp_sk)
+ return 1;
+
+ storage->invoked++;
+
+ storage->dsack_dups = tcp_sk->dsack_dups;
+ storage->delivered = tcp_sk->delivered;
+ storage->delivered_ce = tcp_sk->delivered_ce;
+ storage->icsk_retransmits = tcp_sk->icsk_retransmits;
+
+ return 1;
+}
diff --git a/tools/testing/selftests/bpf/progs/test_attach_probe.c b/tools/testing/selftests/bpf/progs/test_attach_probe.c
new file mode 100644
index 000000000000..63a8dfef893b
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_attach_probe.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017 Facebook
+
+#include <linux/ptrace.h>
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 4);
+ __type(key, int);
+ __type(value, int);
+} results_map SEC(".maps");
+
+SEC("kprobe/sys_nanosleep")
+int handle_sys_nanosleep_entry(struct pt_regs *ctx)
+{
+ const int key = 0, value = 1;
+
+ bpf_map_update_elem(&results_map, &key, &value, 0);
+ return 0;
+}
+
+SEC("kretprobe/sys_nanosleep")
+int handle_sys_getpid_return(struct pt_regs *ctx)
+{
+ const int key = 1, value = 2;
+
+ bpf_map_update_elem(&results_map, &key, &value, 0);
+ return 0;
+}
+
+SEC("uprobe/trigger_func")
+int handle_uprobe_entry(struct pt_regs *ctx)
+{
+ const int key = 2, value = 3;
+
+ bpf_map_update_elem(&results_map, &key, &value, 0);
+ return 0;
+}
+
+SEC("uretprobe/trigger_func")
+int handle_uprobe_return(struct pt_regs *ctx)
+{
+ const int key = 3, value = 4;
+
+ bpf_map_update_elem(&results_map, &key, &value, 0);
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
+__u32 _version SEC("version") = 1;
diff --git a/tools/testing/selftests/bpf/progs/test_btf_newkv.c b/tools/testing/selftests/bpf/progs/test_btf_newkv.c
new file mode 100644
index 000000000000..5ee3622ddebb
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_btf_newkv.c
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018 Facebook */
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+int _version SEC("version") = 1;
+
+struct ipv_counts {
+ unsigned int v4;
+ unsigned int v6;
+};
+
+/* just to validate we can handle maps in multiple sections */
+struct bpf_map_def SEC("maps") btf_map_legacy = {
+ .type = BPF_MAP_TYPE_ARRAY,
+ .key_size = sizeof(int),
+ .value_size = sizeof(long long),
+ .max_entries = 4,
+};
+
+BPF_ANNOTATE_KV_PAIR(btf_map_legacy, int, struct ipv_counts);
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 4);
+ __type(key, int);
+ __type(value, struct ipv_counts);
+} btf_map SEC(".maps");
+
+struct dummy_tracepoint_args {
+ unsigned long long pad;
+ struct sock *sock;
+};
+
+__attribute__((noinline))
+static int test_long_fname_2(struct dummy_tracepoint_args *arg)
+{
+ struct ipv_counts *counts;
+ int key = 0;
+
+ if (!arg->sock)
+ return 0;
+
+ counts = bpf_map_lookup_elem(&btf_map, &key);
+ if (!counts)
+ return 0;
+
+ counts->v6++;
+
+ /* just verify we can reference both maps */
+ counts = bpf_map_lookup_elem(&btf_map_legacy, &key);
+ if (!counts)
+ return 0;
+
+ return 0;
+}
+
+__attribute__((noinline))
+static int test_long_fname_1(struct dummy_tracepoint_args *arg)
+{
+ return test_long_fname_2(arg);
+}
+
+SEC("dummy_tracepoint")
+int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
+{
+ return test_long_fname_1(arg);
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_arrays.c b/tools/testing/selftests/bpf/progs/test_core_reloc_arrays.c
new file mode 100644
index 000000000000..bf67f0fdf743
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_core_reloc_arrays.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+
+#include <linux/bpf.h>
+#include <stdint.h>
+#include "bpf_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+
+static volatile struct data {
+ char in[256];
+ char out[256];
+} data;
+
+struct core_reloc_arrays_output {
+ int a2;
+ char b123;
+ int c1c;
+ int d00d;
+};
+
+struct core_reloc_arrays_substruct {
+ int c;
+ int d;
+};
+
+struct core_reloc_arrays {
+ int a[5];
+ char b[2][3][4];
+ struct core_reloc_arrays_substruct c[3];
+ struct core_reloc_arrays_substruct d[1][2];
+};
+
+SEC("raw_tracepoint/sys_enter")
+int test_core_arrays(void *ctx)
+{
+ struct core_reloc_arrays *in = (void *)&data.in;
+ struct core_reloc_arrays_output *out = (void *)&data.out;
+
+ /* in->a[2] */
+ if (BPF_CORE_READ(&out->a2, &in->a[2]))
+ return 1;
+ /* in->b[1][2][3] */
+ if (BPF_CORE_READ(&out->b123, &in->b[1][2][3]))
+ return 1;
+ /* in->c[1].c */
+ if (BPF_CORE_READ(&out->c1c, &in->c[1].c))
+ return 1;
+ /* in->d[0][0].d */
+ if (BPF_CORE_READ(&out->d00d, &in->d[0][0].d))
+ return 1;
+
+ return 0;
+}
+
diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_flavors.c b/tools/testing/selftests/bpf/progs/test_core_reloc_flavors.c
new file mode 100644
index 000000000000..9fda73e87972
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_core_reloc_flavors.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+
+#include <linux/bpf.h>
+#include <stdint.h>
+#include "bpf_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+
+static volatile struct data {
+ char in[256];
+ char out[256];
+} data;
+
+struct core_reloc_flavors {
+ int a;
+ int b;
+ int c;
+};
+
+/* local flavor with reversed layout */
+struct core_reloc_flavors___reversed {
+ int c;
+ int b;
+ int a;
+};
+
+/* local flavor with nested/overlapping layout */
+struct core_reloc_flavors___weird {
+ struct {
+ int b;
+ };
+ /* a and c overlap in local flavor, but this should still work
+ * correctly with target original flavor
+ */
+ union {
+ int a;
+ int c;
+ };
+};
+
+SEC("raw_tracepoint/sys_enter")
+int test_core_flavors(void *ctx)
+{
+ struct core_reloc_flavors *in_orig = (void *)&data.in;
+ struct core_reloc_flavors___reversed *in_rev = (void *)&data.in;
+ struct core_reloc_flavors___weird *in_weird = (void *)&data.in;
+ struct core_reloc_flavors *out = (void *)&data.out;
+
+ /* read a using weird layout */
+ if (BPF_CORE_READ(&out->a, &in_weird->a))
+ return 1;
+ /* read b using reversed layout */
+ if (BPF_CORE_READ(&out->b, &in_rev->b))
+ return 1;
+ /* read c using original layout */
+ if (BPF_CORE_READ(&out->c, &in_orig->c))
+ return 1;
+
+ return 0;
+}
+
diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_ints.c b/tools/testing/selftests/bpf/progs/test_core_reloc_ints.c
new file mode 100644
index 000000000000..d99233c8008a
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_core_reloc_ints.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+
+#include <linux/bpf.h>
+#include <stdint.h>
+#include "bpf_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+
+static volatile struct data {
+ char in[256];
+ char out[256];
+} data;
+
+struct core_reloc_ints {
+ uint8_t u8_field;
+ int8_t s8_field;
+ uint16_t u16_field;
+ int16_t s16_field;
+ uint32_t u32_field;
+ int32_t s32_field;
+ uint64_t u64_field;
+ int64_t s64_field;
+};
+
+SEC("raw_tracepoint/sys_enter")
+int test_core_ints(void *ctx)
+{
+ struct core_reloc_ints *in = (void *)&data.in;
+ struct core_reloc_ints *out = (void *)&data.out;
+
+ if (BPF_CORE_READ(&out->u8_field, &in->u8_field) ||
+ BPF_CORE_READ(&out->s8_field, &in->s8_field) ||
+ BPF_CORE_READ(&out->u16_field, &in->u16_field) ||
+ BPF_CORE_READ(&out->s16_field, &in->s16_field) ||
+ BPF_CORE_READ(&out->u32_field, &in->u32_field) ||
+ BPF_CORE_READ(&out->s32_field, &in->s32_field) ||
+ BPF_CORE_READ(&out->u64_field, &in->u64_field) ||
+ BPF_CORE_READ(&out->s64_field, &in->s64_field))
+ return 1;
+
+ return 0;
+}
+
diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_kernel.c b/tools/testing/selftests/bpf/progs/test_core_reloc_kernel.c
new file mode 100644
index 000000000000..37e02aa3f0c8
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_core_reloc_kernel.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+
+#include <linux/bpf.h>
+#include <stdint.h>
+#include "bpf_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+
+static volatile struct data {
+ char in[256];
+ char out[256];
+} data;
+
+struct task_struct {
+ int pid;
+ int tgid;
+};
+
+SEC("raw_tracepoint/sys_enter")
+int test_core_kernel(void *ctx)
+{
+ struct task_struct *task = (void *)bpf_get_current_task();
+ uint64_t pid_tgid = bpf_get_current_pid_tgid();
+ int pid, tgid;
+
+ if (BPF_CORE_READ(&pid, &task->pid) ||
+ BPF_CORE_READ(&tgid, &task->tgid))
+ return 1;
+
+ /* validate pid + tgid matches */
+ data.out[0] = (((uint64_t)pid << 32) | tgid) == pid_tgid;
+
+ return 0;
+}
+
diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_misc.c b/tools/testing/selftests/bpf/progs/test_core_reloc_misc.c
new file mode 100644
index 000000000000..c59984bd3e23
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_core_reloc_misc.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+
+#include <linux/bpf.h>
+#include <stdint.h>
+#include "bpf_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+
+static volatile struct data {
+ char in[256];
+ char out[256];
+} data;
+
+struct core_reloc_misc_output {
+ int a, b, c;
+};
+
+struct core_reloc_misc___a {
+ int a1;
+ int a2;
+};
+
+struct core_reloc_misc___b {
+ int b1;
+ int b2;
+};
+
+/* fixed two first members, can be extended with new fields */
+struct core_reloc_misc_extensible {
+ int a;
+ int b;
+};
+
+SEC("raw_tracepoint/sys_enter")
+int test_core_misc(void *ctx)
+{
+ struct core_reloc_misc___a *in_a = (void *)&data.in;
+ struct core_reloc_misc___b *in_b = (void *)&data.in;
+ struct core_reloc_misc_extensible *in_ext = (void *)&data.in;
+ struct core_reloc_misc_output *out = (void *)&data.out;
+
+ /* record two different relocations with the same accessor string */
+ if (BPF_CORE_READ(&out->a, &in_a->a1) || /* accessor: 0:0 */
+ BPF_CORE_READ(&out->b, &in_b->b1)) /* accessor: 0:0 */
+ return 1;
+
+ /* Validate relocations capture array-only accesses for structs with
+ * fixed header, but with potentially extendable tail. This will read
+ * first 4 bytes of 2nd element of in_ext array of potentially
+ * variably sized struct core_reloc_misc_extensible. */
+ if (BPF_CORE_READ(&out->c, &in_ext[2])) /* accessor: 2 */
+ return 1;
+
+ return 0;
+}
+
diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_mods.c b/tools/testing/selftests/bpf/progs/test_core_reloc_mods.c
new file mode 100644
index 000000000000..f98b942c062b
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_core_reloc_mods.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+
+#include <linux/bpf.h>
+#include <stdint.h>
+#include "bpf_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+
+static volatile struct data {
+ char in[256];
+ char out[256];
+} data;
+
+struct core_reloc_mods_output {
+ int a, b, c, d, e, f, g, h;
+};
+
+typedef const int int_t;
+typedef const char *char_ptr_t;
+typedef const int arr_t[7];
+
+struct core_reloc_mods_substruct {
+ int x;
+ int y;
+};
+
+typedef struct {
+ int x;
+ int y;
+} core_reloc_mods_substruct_t;
+
+struct core_reloc_mods {
+ int a;
+ int_t b;
+ char *c;
+ char_ptr_t d;
+ int e[3];
+ arr_t f;
+ struct core_reloc_mods_substruct g;
+ core_reloc_mods_substruct_t h;
+};
+
+SEC("raw_tracepoint/sys_enter")
+int test_core_mods(void *ctx)
+{
+ struct core_reloc_mods *in = (void *)&data.in;
+ struct core_reloc_mods_output *out = (void *)&data.out;
+
+ if (BPF_CORE_READ(&out->a, &in->a) ||
+ BPF_CORE_READ(&out->b, &in->b) ||
+ BPF_CORE_READ(&out->c, &in->c) ||
+ BPF_CORE_READ(&out->d, &in->d) ||
+ BPF_CORE_READ(&out->e, &in->e[2]) ||
+ BPF_CORE_READ(&out->f, &in->f[1]) ||
+ BPF_CORE_READ(&out->g, &in->g.x) ||
+ BPF_CORE_READ(&out->h, &in->h.y))
+ return 1;
+
+ return 0;
+}
+
diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_nesting.c b/tools/testing/selftests/bpf/progs/test_core_reloc_nesting.c
new file mode 100644
index 000000000000..3ca30cec2b39
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_core_reloc_nesting.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+
+#include <linux/bpf.h>
+#include <stdint.h>
+#include "bpf_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+
+static volatile struct data {
+ char in[256];
+ char out[256];
+} data;
+
+struct core_reloc_nesting_substruct {
+ int a;
+};
+
+union core_reloc_nesting_subunion {
+ int b;
+};
+
+/* int a.a.a and b.b.b accesses */
+struct core_reloc_nesting {
+ union {
+ struct core_reloc_nesting_substruct a;
+ } a;
+ struct {
+ union core_reloc_nesting_subunion b;
+ } b;
+};
+
+SEC("raw_tracepoint/sys_enter")
+int test_core_nesting(void *ctx)
+{
+ struct core_reloc_nesting *in = (void *)&data.in;
+ struct core_reloc_nesting *out = (void *)&data.out;
+
+ if (BPF_CORE_READ(&out->a.a.a, &in->a.a.a))
+ return 1;
+ if (BPF_CORE_READ(&out->b.b.b, &in->b.b.b))
+ return 1;
+
+ return 0;
+}
+
diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_primitives.c b/tools/testing/selftests/bpf/progs/test_core_reloc_primitives.c
new file mode 100644
index 000000000000..add52f23ab35
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_core_reloc_primitives.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+
+#include <linux/bpf.h>
+#include <stdint.h>
+#include "bpf_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+
+static volatile struct data {
+ char in[256];
+ char out[256];
+} data;
+
+enum core_reloc_primitives_enum {
+ A = 0,
+ B = 1,
+};
+
+struct core_reloc_primitives {
+ char a;
+ int b;
+ enum core_reloc_primitives_enum c;
+ void *d;
+ int (*f)(const char *);
+};
+
+SEC("raw_tracepoint/sys_enter")
+int test_core_primitives(void *ctx)
+{
+ struct core_reloc_primitives *in = (void *)&data.in;
+ struct core_reloc_primitives *out = (void *)&data.out;
+
+ if (BPF_CORE_READ(&out->a, &in->a) ||
+ BPF_CORE_READ(&out->b, &in->b) ||
+ BPF_CORE_READ(&out->c, &in->c) ||
+ BPF_CORE_READ(&out->d, &in->d) ||
+ BPF_CORE_READ(&out->f, &in->f))
+ return 1;
+
+ return 0;
+}
+
diff --git a/tools/testing/selftests/bpf/progs/test_core_reloc_ptr_as_arr.c b/tools/testing/selftests/bpf/progs/test_core_reloc_ptr_as_arr.c
new file mode 100644
index 000000000000..526b7ddc7ea1
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_core_reloc_ptr_as_arr.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+
+#include <linux/bpf.h>
+#include <stdint.h>
+#include "bpf_helpers.h"
+
+char _license[] SEC("license") = "GPL";
+
+static volatile struct data {
+ char in[256];
+ char out[256];
+} data;
+
+struct core_reloc_ptr_as_arr {
+ int a;
+};
+
+SEC("raw_tracepoint/sys_enter")
+int test_core_ptr_as_arr(void *ctx)
+{
+ struct core_reloc_ptr_as_arr *in = (void *)&data.in;
+ struct core_reloc_ptr_as_arr *out = (void *)&data.out;
+
+ if (BPF_CORE_READ(&out->a, &in[2].a))
+ return 1;
+
+ return 0;
+}
+
diff --git a/tools/testing/selftests/bpf/progs/test_get_stack_rawtp.c b/tools/testing/selftests/bpf/progs/test_get_stack_rawtp.c
index f6d9f238e00a..f8ffa3f3d44b 100644
--- a/tools/testing/selftests/bpf/progs/test_get_stack_rawtp.c
+++ b/tools/testing/selftests/bpf/progs/test_get_stack_rawtp.c
@@ -15,19 +15,19 @@ struct stack_trace_t {
struct bpf_stack_build_id user_stack_buildid[MAX_STACK_RAWTP];
};
-struct bpf_map_def SEC("maps") perfmap = {
- .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
- .key_size = sizeof(int),
- .value_size = sizeof(__u32),
- .max_entries = 2,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
+ __uint(max_entries, 2);
+ __uint(key_size, sizeof(int));
+ __uint(value_size, sizeof(__u32));
+} perfmap SEC(".maps");
-struct bpf_map_def SEC("maps") stackdata_map = {
- .type = BPF_MAP_TYPE_PERCPU_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct stack_trace_t),
- .max_entries = 1,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, __u32);
+ __type(value, struct stack_trace_t);
+} stackdata_map SEC(".maps");
/* Allocate per-cpu space twice the needed. For the code below
* usize = bpf_get_stack(ctx, raw_data, max_len, BPF_F_USER_STACK);
@@ -47,14 +47,15 @@ struct bpf_map_def SEC("maps") stackdata_map = {
* issue and avoid complicated C programming massaging.
* This is an acceptable workaround since there is one entry here.
*/
-struct bpf_map_def SEC("maps") rawdata_map = {
- .type = BPF_MAP_TYPE_PERCPU_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = MAX_STACK_RAWTP * sizeof(__u64) * 2,
- .max_entries = 1,
-};
+typedef __u64 raw_stack_trace_t[2 * MAX_STACK_RAWTP];
+struct {
+ __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, __u32);
+ __type(value, raw_stack_trace_t);
+} rawdata_map SEC(".maps");
-SEC("tracepoint/raw_syscalls/sys_enter")
+SEC("raw_tracepoint/sys_enter")
int bpf_prog1(void *ctx)
{
int max_len, max_buildid_len, usize, ksize, total_size;
diff --git a/tools/testing/selftests/bpf/progs/test_global_data.c b/tools/testing/selftests/bpf/progs/test_global_data.c
index 5ab14e941980..32a6073acb99 100644
--- a/tools/testing/selftests/bpf/progs/test_global_data.c
+++ b/tools/testing/selftests/bpf/progs/test_global_data.c
@@ -7,19 +7,19 @@
#include "bpf_helpers.h"
-struct bpf_map_def SEC("maps") result_number = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(__u64),
- .max_entries = 11,
-};
-
-struct bpf_map_def SEC("maps") result_string = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = 32,
- .max_entries = 5,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 11);
+ __type(key, __u32);
+ __type(value, __u64);
+} result_number SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 5);
+ __type(key, __u32);
+ const char (*value)[32];
+} result_string SEC(".maps");
struct foo {
__u8 a;
@@ -27,12 +27,12 @@ struct foo {
__u64 c;
};
-struct bpf_map_def SEC("maps") result_struct = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct foo),
- .max_entries = 5,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 5);
+ __type(key, __u32);
+ __type(value, struct foo);
+} result_struct SEC(".maps");
/* Relocation tests for __u64s. */
static __u64 num0;
diff --git a/tools/testing/selftests/bpf/progs/test_jhash.h b/tools/testing/selftests/bpf/progs/test_jhash.h
index 3d12c11a8d47..c300734d26f6 100644
--- a/tools/testing/selftests/bpf/progs/test_jhash.h
+++ b/tools/testing/selftests/bpf/progs/test_jhash.h
@@ -1,9 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019 Facebook
+#include <features.h>
typedef unsigned int u32;
-static __attribute__((always_inline)) u32 rol32(u32 word, unsigned int shift)
+static __always_inline u32 rol32(u32 word, unsigned int shift)
{
return (word << shift) | (word >> ((-shift) & 31));
}
diff --git a/tools/testing/selftests/bpf/progs/test_l4lb.c b/tools/testing/selftests/bpf/progs/test_l4lb.c
index 1e10c9590991..1d652ee8e73d 100644
--- a/tools/testing/selftests/bpf/progs/test_l4lb.c
+++ b/tools/testing/selftests/bpf/progs/test_l4lb.c
@@ -169,40 +169,40 @@ struct eth_hdr {
unsigned short eth_proto;
};
-struct bpf_map_def SEC("maps") vip_map = {
- .type = BPF_MAP_TYPE_HASH,
- .key_size = sizeof(struct vip),
- .value_size = sizeof(struct vip_meta),
- .max_entries = MAX_VIPS,
-};
-
-struct bpf_map_def SEC("maps") ch_rings = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(__u32),
- .max_entries = CH_RINGS_SIZE,
-};
-
-struct bpf_map_def SEC("maps") reals = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct real_definition),
- .max_entries = MAX_REALS,
-};
-
-struct bpf_map_def SEC("maps") stats = {
- .type = BPF_MAP_TYPE_PERCPU_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct vip_stats),
- .max_entries = MAX_VIPS,
-};
-
-struct bpf_map_def SEC("maps") ctl_array = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct ctl_value),
- .max_entries = CTL_MAP_SIZE,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, MAX_VIPS);
+ __type(key, struct vip);
+ __type(value, struct vip_meta);
+} vip_map SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, CH_RINGS_SIZE);
+ __type(key, __u32);
+ __type(value, __u32);
+} ch_rings SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, MAX_REALS);
+ __type(key, __u32);
+ __type(value, struct real_definition);
+} reals SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+ __uint(max_entries, MAX_VIPS);
+ __type(key, __u32);
+ __type(value, struct vip_stats);
+} stats SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, CTL_MAP_SIZE);
+ __type(key, __u32);
+ __type(value, struct ctl_value);
+} ctl_array SEC(".maps");
static __always_inline __u32 get_packet_hash(struct packet_description *pckt,
bool ipv6)
diff --git a/tools/testing/selftests/bpf/progs/test_l4lb_noinline.c b/tools/testing/selftests/bpf/progs/test_l4lb_noinline.c
index ba44a14e6dc4..2e4efe70b1e5 100644
--- a/tools/testing/selftests/bpf/progs/test_l4lb_noinline.c
+++ b/tools/testing/selftests/bpf/progs/test_l4lb_noinline.c
@@ -165,40 +165,40 @@ struct eth_hdr {
unsigned short eth_proto;
};
-struct bpf_map_def SEC("maps") vip_map = {
- .type = BPF_MAP_TYPE_HASH,
- .key_size = sizeof(struct vip),
- .value_size = sizeof(struct vip_meta),
- .max_entries = MAX_VIPS,
-};
-
-struct bpf_map_def SEC("maps") ch_rings = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(__u32),
- .max_entries = CH_RINGS_SIZE,
-};
-
-struct bpf_map_def SEC("maps") reals = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct real_definition),
- .max_entries = MAX_REALS,
-};
-
-struct bpf_map_def SEC("maps") stats = {
- .type = BPF_MAP_TYPE_PERCPU_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct vip_stats),
- .max_entries = MAX_VIPS,
-};
-
-struct bpf_map_def SEC("maps") ctl_array = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct ctl_value),
- .max_entries = CTL_MAP_SIZE,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, MAX_VIPS);
+ __type(key, struct vip);
+ __type(value, struct vip_meta);
+} vip_map SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, CH_RINGS_SIZE);
+ __type(key, __u32);
+ __type(value, __u32);
+} ch_rings SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, MAX_REALS);
+ __type(key, __u32);
+ __type(value, struct real_definition);
+} reals SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+ __uint(max_entries, MAX_VIPS);
+ __type(key, __u32);
+ __type(value, struct vip_stats);
+} stats SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, CTL_MAP_SIZE);
+ __type(key, __u32);
+ __type(value, struct ctl_value);
+} ctl_array SEC(".maps");
static __u32 get_packet_hash(struct packet_description *pckt,
bool ipv6)
diff --git a/tools/testing/selftests/bpf/progs/test_lwt_seg6local.c b/tools/testing/selftests/bpf/progs/test_lwt_seg6local.c
index 0575751bc1bc..41a3ebcd593d 100644
--- a/tools/testing/selftests/bpf/progs/test_lwt_seg6local.c
+++ b/tools/testing/selftests/bpf/progs/test_lwt_seg6local.c
@@ -6,23 +6,12 @@
#include "bpf_helpers.h"
#include "bpf_endian.h"
-#define bpf_printk(fmt, ...) \
-({ \
- char ____fmt[] = fmt; \
- bpf_trace_printk(____fmt, sizeof(____fmt), \
- ##__VA_ARGS__); \
-})
-
/* Packet parsing state machine helpers. */
#define cursor_advance(_cursor, _len) \
({ void *_tmp = _cursor; _cursor += _len; _tmp; })
#define SR6_FLAG_ALERT (1 << 4)
-#define htonll(x) ((bpf_htonl(1)) == 1 ? (x) : ((uint64_t)bpf_htonl((x) & \
- 0xFFFFFFFF) << 32) | bpf_htonl((x) >> 32))
-#define ntohll(x) ((bpf_ntohl(1)) == 1 ? (x) : ((uint64_t)bpf_ntohl((x) & \
- 0xFFFFFFFF) << 32) | bpf_ntohl((x) >> 32))
#define BPF_PACKET_HEADER __attribute__((packed))
struct ip6_t {
@@ -61,7 +50,7 @@ struct sr6_tlv_t {
unsigned char value[0];
} BPF_PACKET_HEADER;
-__attribute__((always_inline)) struct ip6_srh_t *get_srh(struct __sk_buff *skb)
+static __always_inline struct ip6_srh_t *get_srh(struct __sk_buff *skb)
{
void *cursor, *data_end;
struct ip6_srh_t *srh;
@@ -95,7 +84,7 @@ __attribute__((always_inline)) struct ip6_srh_t *get_srh(struct __sk_buff *skb)
return srh;
}
-__attribute__((always_inline))
+static __always_inline
int update_tlv_pad(struct __sk_buff *skb, uint32_t new_pad,
uint32_t old_pad, uint32_t pad_off)
{
@@ -125,7 +114,7 @@ int update_tlv_pad(struct __sk_buff *skb, uint32_t new_pad,
return 0;
}
-__attribute__((always_inline))
+static __always_inline
int is_valid_tlv_boundary(struct __sk_buff *skb, struct ip6_srh_t *srh,
uint32_t *tlv_off, uint32_t *pad_size,
uint32_t *pad_off)
@@ -184,7 +173,7 @@ int is_valid_tlv_boundary(struct __sk_buff *skb, struct ip6_srh_t *srh,
return 0;
}
-__attribute__((always_inline))
+static __always_inline
int add_tlv(struct __sk_buff *skb, struct ip6_srh_t *srh, uint32_t tlv_off,
struct sr6_tlv_t *itlv, uint8_t tlv_size)
{
@@ -228,7 +217,7 @@ int add_tlv(struct __sk_buff *skb, struct ip6_srh_t *srh, uint32_t tlv_off,
return update_tlv_pad(skb, new_pad, pad_size, pad_off);
}
-__attribute__((always_inline))
+static __always_inline
int delete_tlv(struct __sk_buff *skb, struct ip6_srh_t *srh,
uint32_t tlv_off)
{
@@ -266,7 +255,7 @@ int delete_tlv(struct __sk_buff *skb, struct ip6_srh_t *srh,
return update_tlv_pad(skb, new_pad, pad_size, pad_off);
}
-__attribute__((always_inline))
+static __always_inline
int has_egr_tlv(struct __sk_buff *skb, struct ip6_srh_t *srh)
{
int tlv_offset = sizeof(struct ip6_t) + sizeof(struct ip6_srh_t) +
@@ -283,8 +272,8 @@ int has_egr_tlv(struct __sk_buff *skb, struct ip6_srh_t *srh)
return 0;
// check if egress TLV value is correct
- if (ntohll(egr_addr.hi) == 0xfd00000000000000 &&
- ntohll(egr_addr.lo) == 0x4)
+ if (bpf_be64_to_cpu(egr_addr.hi) == 0xfd00000000000000 &&
+ bpf_be64_to_cpu(egr_addr.lo) == 0x4)
return 1;
}
@@ -315,8 +304,8 @@ int __encap_srh(struct __sk_buff *skb)
#pragma clang loop unroll(full)
for (unsigned long long lo = 0; lo < 4; lo++) {
- seg->lo = htonll(4 - lo);
- seg->hi = htonll(hi);
+ seg->lo = bpf_cpu_to_be64(4 - lo);
+ seg->hi = bpf_cpu_to_be64(hi);
seg = (struct ip6_addr_t *)((char *)seg + sizeof(*seg));
}
@@ -356,8 +345,8 @@ int __add_egr_x(struct __sk_buff *skb)
if (err)
return BPF_DROP;
- addr.lo = htonll(lo);
- addr.hi = htonll(hi);
+ addr.lo = bpf_cpu_to_be64(lo);
+ addr.hi = bpf_cpu_to_be64(hi);
err = bpf_lwt_seg6_action(skb, SEG6_LOCAL_ACTION_END_X,
(void *)&addr, sizeof(addr));
if (err)
diff --git a/tools/testing/selftests/bpf/progs/test_map_in_map.c b/tools/testing/selftests/bpf/progs/test_map_in_map.c
index 2985f262846e..113226115365 100644
--- a/tools/testing/selftests/bpf/progs/test_map_in_map.c
+++ b/tools/testing/selftests/bpf/progs/test_map_in_map.c
@@ -5,23 +5,23 @@
#include <linux/types.h>
#include "bpf_helpers.h"
-struct bpf_map_def SEC("maps") mim_array = {
- .type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
- .key_size = sizeof(int),
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
+ __uint(max_entries, 1);
+ __uint(map_flags, 0);
+ __uint(key_size, sizeof(__u32));
/* must be sizeof(__u32) for map in map */
- .value_size = sizeof(__u32),
- .max_entries = 1,
- .map_flags = 0,
-};
-
-struct bpf_map_def SEC("maps") mim_hash = {
- .type = BPF_MAP_TYPE_HASH_OF_MAPS,
- .key_size = sizeof(int),
+ __uint(value_size, sizeof(__u32));
+} mim_array SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
+ __uint(max_entries, 1);
+ __uint(map_flags, 0);
+ __uint(key_size, sizeof(int));
/* must be sizeof(__u32) for map in map */
- .value_size = sizeof(__u32),
- .max_entries = 1,
- .map_flags = 0,
-};
+ __uint(value_size, sizeof(__u32));
+} mim_hash SEC(".maps");
SEC("xdp_mimtest")
int xdp_mimtest0(struct xdp_md *ctx)
diff --git a/tools/testing/selftests/bpf/progs/test_map_lock.c b/tools/testing/selftests/bpf/progs/test_map_lock.c
index af8cc68ed2f9..bb7ce35f691b 100644
--- a/tools/testing/selftests/bpf/progs/test_map_lock.c
+++ b/tools/testing/selftests/bpf/progs/test_map_lock.c
@@ -11,28 +11,24 @@ struct hmap_elem {
int var[VAR_NUM];
};
-struct bpf_map_def SEC("maps") hash_map = {
- .type = BPF_MAP_TYPE_HASH,
- .key_size = sizeof(int),
- .value_size = sizeof(struct hmap_elem),
- .max_entries = 1,
-};
-
-BPF_ANNOTATE_KV_PAIR(hash_map, int, struct hmap_elem);
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 1);
+ __type(key, __u32);
+ __type(value, struct hmap_elem);
+} hash_map SEC(".maps");
struct array_elem {
struct bpf_spin_lock lock;
int var[VAR_NUM];
};
-struct bpf_map_def SEC("maps") array_map = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(int),
- .value_size = sizeof(struct array_elem),
- .max_entries = 1,
-};
-
-BPF_ANNOTATE_KV_PAIR(array_map, int, struct array_elem);
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, int);
+ __type(value, struct array_elem);
+} array_map SEC(".maps");
SEC("map_lock_demo")
int bpf_map_lock_test(struct __sk_buff *skb)
diff --git a/tools/testing/selftests/bpf/progs/test_obj_id.c b/tools/testing/selftests/bpf/progs/test_obj_id.c
index 880d2963b472..3d30c02bdae9 100644
--- a/tools/testing/selftests/bpf/progs/test_obj_id.c
+++ b/tools/testing/selftests/bpf/progs/test_obj_id.c
@@ -1,8 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2017 Facebook
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
*/
#include <stddef.h>
#include <linux/bpf.h>
@@ -16,12 +13,12 @@
int _version SEC("version") = 1;
-struct bpf_map_def SEC("maps") test_map_id = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(__u64),
- .max_entries = 1,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, __u32);
+ __type(value, __u64);
+} test_map_id SEC(".maps");
SEC("test_obj_id_dummy")
int test_obj_id(struct __sk_buff *skb)
diff --git a/tools/testing/selftests/bpf/progs/test_perf_buffer.c b/tools/testing/selftests/bpf/progs/test_perf_buffer.c
new file mode 100644
index 000000000000..876c27deb65a
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_perf_buffer.c
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+
+#include <linux/ptrace.h>
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+struct {
+ __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
+ __uint(key_size, sizeof(int));
+ __uint(value_size, sizeof(int));
+} perf_buf_map SEC(".maps");
+
+SEC("kprobe/sys_nanosleep")
+int handle_sys_nanosleep_entry(struct pt_regs *ctx)
+{
+ int cpu = bpf_get_smp_processor_id();
+
+ bpf_perf_event_output(ctx, &perf_buf_map, BPF_F_CURRENT_CPU,
+ &cpu, sizeof(cpu));
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
+__u32 _version SEC("version") = 1;
diff --git a/tools/testing/selftests/bpf/progs/test_pkt_access.c b/tools/testing/selftests/bpf/progs/test_pkt_access.c
index 6e11ba11709e..7cf42d14103f 100644
--- a/tools/testing/selftests/bpf/progs/test_pkt_access.c
+++ b/tools/testing/selftests/bpf/progs/test_pkt_access.c
@@ -1,8 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2017 Facebook
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
*/
#include <stddef.h>
#include <string.h>
diff --git a/tools/testing/selftests/bpf/progs/test_pkt_md_access.c b/tools/testing/selftests/bpf/progs/test_pkt_md_access.c
index 7956302ecdf2..3d039e18bf82 100644
--- a/tools/testing/selftests/bpf/progs/test_pkt_md_access.c
+++ b/tools/testing/selftests/bpf/progs/test_pkt_md_access.c
@@ -1,8 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2017 Facebook
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
*/
#include <stddef.h>
#include <string.h>
diff --git a/tools/testing/selftests/bpf/progs/test_seg6_loop.c b/tools/testing/selftests/bpf/progs/test_seg6_loop.c
new file mode 100644
index 000000000000..c4d104428643
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_seg6_loop.c
@@ -0,0 +1,258 @@
+#include <stddef.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <linux/seg6_local.h>
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+#include "bpf_endian.h"
+
+/* Packet parsing state machine helpers. */
+#define cursor_advance(_cursor, _len) \
+ ({ void *_tmp = _cursor; _cursor += _len; _tmp; })
+
+#define SR6_FLAG_ALERT (1 << 4)
+
+#define BPF_PACKET_HEADER __attribute__((packed))
+
+struct ip6_t {
+ unsigned int ver:4;
+ unsigned int priority:8;
+ unsigned int flow_label:20;
+ unsigned short payload_len;
+ unsigned char next_header;
+ unsigned char hop_limit;
+ unsigned long long src_hi;
+ unsigned long long src_lo;
+ unsigned long long dst_hi;
+ unsigned long long dst_lo;
+} BPF_PACKET_HEADER;
+
+struct ip6_addr_t {
+ unsigned long long hi;
+ unsigned long long lo;
+} BPF_PACKET_HEADER;
+
+struct ip6_srh_t {
+ unsigned char nexthdr;
+ unsigned char hdrlen;
+ unsigned char type;
+ unsigned char segments_left;
+ unsigned char first_segment;
+ unsigned char flags;
+ unsigned short tag;
+
+ struct ip6_addr_t segments[0];
+} BPF_PACKET_HEADER;
+
+struct sr6_tlv_t {
+ unsigned char type;
+ unsigned char len;
+ unsigned char value[0];
+} BPF_PACKET_HEADER;
+
+static __always_inline struct ip6_srh_t *get_srh(struct __sk_buff *skb)
+{
+ void *cursor, *data_end;
+ struct ip6_srh_t *srh;
+ struct ip6_t *ip;
+ uint8_t *ipver;
+
+ data_end = (void *)(long)skb->data_end;
+ cursor = (void *)(long)skb->data;
+ ipver = (uint8_t *)cursor;
+
+ if ((void *)ipver + sizeof(*ipver) > data_end)
+ return NULL;
+
+ if ((*ipver >> 4) != 6)
+ return NULL;
+
+ ip = cursor_advance(cursor, sizeof(*ip));
+ if ((void *)ip + sizeof(*ip) > data_end)
+ return NULL;
+
+ if (ip->next_header != 43)
+ return NULL;
+
+ srh = cursor_advance(cursor, sizeof(*srh));
+ if ((void *)srh + sizeof(*srh) > data_end)
+ return NULL;
+
+ if (srh->type != 4)
+ return NULL;
+
+ return srh;
+}
+
+static __always_inline int update_tlv_pad(struct __sk_buff *skb,
+ uint32_t new_pad, uint32_t old_pad,
+ uint32_t pad_off)
+{
+ int err;
+
+ if (new_pad != old_pad) {
+ err = bpf_lwt_seg6_adjust_srh(skb, pad_off,
+ (int) new_pad - (int) old_pad);
+ if (err)
+ return err;
+ }
+
+ if (new_pad > 0) {
+ char pad_tlv_buf[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0};
+ struct sr6_tlv_t *pad_tlv = (struct sr6_tlv_t *) pad_tlv_buf;
+
+ pad_tlv->type = SR6_TLV_PADDING;
+ pad_tlv->len = new_pad - 2;
+
+ err = bpf_lwt_seg6_store_bytes(skb, pad_off,
+ (void *)pad_tlv_buf, new_pad);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static __always_inline int is_valid_tlv_boundary(struct __sk_buff *skb,
+ struct ip6_srh_t *srh,
+ uint32_t *tlv_off,
+ uint32_t *pad_size,
+ uint32_t *pad_off)
+{
+ uint32_t srh_off, cur_off;
+ int offset_valid = 0;
+ int err;
+
+ srh_off = (char *)srh - (char *)(long)skb->data;
+ // cur_off = end of segments, start of possible TLVs
+ cur_off = srh_off + sizeof(*srh) +
+ sizeof(struct ip6_addr_t) * (srh->first_segment + 1);
+
+ *pad_off = 0;
+
+ // we can only go as far as ~10 TLVs due to the BPF max stack size
+ #pragma clang loop unroll(disable)
+ for (int i = 0; i < 100; i++) {
+ struct sr6_tlv_t tlv;
+
+ if (cur_off == *tlv_off)
+ offset_valid = 1;
+
+ if (cur_off >= srh_off + ((srh->hdrlen + 1) << 3))
+ break;
+
+ err = bpf_skb_load_bytes(skb, cur_off, &tlv, sizeof(tlv));
+ if (err)
+ return err;
+
+ if (tlv.type == SR6_TLV_PADDING) {
+ *pad_size = tlv.len + sizeof(tlv);
+ *pad_off = cur_off;
+
+ if (*tlv_off == srh_off) {
+ *tlv_off = cur_off;
+ offset_valid = 1;
+ }
+ break;
+
+ } else if (tlv.type == SR6_TLV_HMAC) {
+ break;
+ }
+
+ cur_off += sizeof(tlv) + tlv.len;
+ } // we reached the padding or HMAC TLVs, or the end of the SRH
+
+ if (*pad_off == 0)
+ *pad_off = cur_off;
+
+ if (*tlv_off == -1)
+ *tlv_off = cur_off;
+ else if (!offset_valid)
+ return -EINVAL;
+
+ return 0;
+}
+
+static __always_inline int add_tlv(struct __sk_buff *skb,
+ struct ip6_srh_t *srh, uint32_t tlv_off,
+ struct sr6_tlv_t *itlv, uint8_t tlv_size)
+{
+ uint32_t srh_off = (char *)srh - (char *)(long)skb->data;
+ uint8_t len_remaining, new_pad;
+ uint32_t pad_off = 0;
+ uint32_t pad_size = 0;
+ uint32_t partial_srh_len;
+ int err;
+
+ if (tlv_off != -1)
+ tlv_off += srh_off;
+
+ if (itlv->type == SR6_TLV_PADDING || itlv->type == SR6_TLV_HMAC)
+ return -EINVAL;
+
+ err = is_valid_tlv_boundary(skb, srh, &tlv_off, &pad_size, &pad_off);
+ if (err)
+ return err;
+
+ err = bpf_lwt_seg6_adjust_srh(skb, tlv_off, sizeof(*itlv) + itlv->len);
+ if (err)
+ return err;
+
+ err = bpf_lwt_seg6_store_bytes(skb, tlv_off, (void *)itlv, tlv_size);
+ if (err)
+ return err;
+
+ // the following can't be moved inside update_tlv_pad because the
+ // bpf verifier has some issues with it
+ pad_off += sizeof(*itlv) + itlv->len;
+ partial_srh_len = pad_off - srh_off;
+ len_remaining = partial_srh_len % 8;
+ new_pad = 8 - len_remaining;
+
+ if (new_pad == 1) // cannot pad for 1 byte only
+ new_pad = 9;
+ else if (new_pad == 8)
+ new_pad = 0;
+
+ return update_tlv_pad(skb, new_pad, pad_size, pad_off);
+}
+
+// Add an Egress TLV fc00::4, add the flag A,
+// and apply End.X action to fc42::1
+SEC("lwt_seg6local")
+int __add_egr_x(struct __sk_buff *skb)
+{
+ unsigned long long hi = 0xfc42000000000000;
+ unsigned long long lo = 0x1;
+ struct ip6_srh_t *srh = get_srh(skb);
+ uint8_t new_flags = SR6_FLAG_ALERT;
+ struct ip6_addr_t addr;
+ int err, offset;
+
+ if (srh == NULL)
+ return BPF_DROP;
+
+ uint8_t tlv[20] = {2, 18, 0, 0, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4};
+
+ err = add_tlv(skb, srh, (srh->hdrlen+1) << 3,
+ (struct sr6_tlv_t *)&tlv, 20);
+ if (err)
+ return BPF_DROP;
+
+ offset = sizeof(struct ip6_t) + offsetof(struct ip6_srh_t, flags);
+ err = bpf_lwt_seg6_store_bytes(skb, offset,
+ (void *)&new_flags, sizeof(new_flags));
+ if (err)
+ return BPF_DROP;
+
+ addr.lo = bpf_cpu_to_be64(lo);
+ addr.hi = bpf_cpu_to_be64(hi);
+ err = bpf_lwt_seg6_action(skb, SEG6_LOCAL_ACTION_END_X,
+ (void *)&addr, sizeof(addr));
+ if (err)
+ return BPF_DROP;
+ return BPF_REDIRECT;
+}
+char __license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c b/tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c
index 5b54ec637ada..ea7d84f01235 100644
--- a/tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c
+++ b/tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c
@@ -21,40 +21,40 @@ int _version SEC("version") = 1;
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
-struct bpf_map_def SEC("maps") outer_map = {
- .type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
- .key_size = sizeof(__u32),
- .value_size = sizeof(__u32),
- .max_entries = 1,
-};
-
-struct bpf_map_def SEC("maps") result_map = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(__u32),
- .max_entries = NR_RESULTS,
-};
-
-struct bpf_map_def SEC("maps") tmp_index_ovr_map = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(int),
- .max_entries = 1,
-};
-
-struct bpf_map_def SEC("maps") linum_map = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(__u32),
- .max_entries = 1,
-};
-
-struct bpf_map_def SEC("maps") data_check_map = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct data_check),
- .max_entries = 1,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
+ __uint(max_entries, 1);
+ __uint(key_size, sizeof(__u32));
+ __uint(value_size, sizeof(__u32));
+} outer_map SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, NR_RESULTS);
+ __type(key, __u32);
+ __type(value, __u32);
+} result_map SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, __u32);
+ __type(value, int);
+} tmp_index_ovr_map SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, __u32);
+ __type(value, __u32);
+} linum_map SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, __u32);
+ __type(value, struct data_check);
+} data_check_map SEC(".maps");
#define GOTO_DONE(_result) ({ \
result = (_result); \
diff --git a/tools/testing/selftests/bpf/progs/test_send_signal_kern.c b/tools/testing/selftests/bpf/progs/test_send_signal_kern.c
new file mode 100644
index 000000000000..0e6be01157e6
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_send_signal_kern.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+#include <linux/bpf.h>
+#include <linux/version.h>
+#include "bpf_helpers.h"
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, __u32);
+ __type(value, __u64);
+} info_map SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, __u32);
+ __type(value, __u64);
+} status_map SEC(".maps");
+
+SEC("send_signal_demo")
+int bpf_send_signal_test(void *ctx)
+{
+ __u64 *info_val, *status_val;
+ __u32 key = 0, pid, sig;
+ int ret;
+
+ status_val = bpf_map_lookup_elem(&status_map, &key);
+ if (!status_val || *status_val != 0)
+ return 0;
+
+ info_val = bpf_map_lookup_elem(&info_map, &key);
+ if (!info_val || *info_val == 0)
+ return 0;
+
+ sig = *info_val >> 32;
+ pid = *info_val & 0xffffFFFF;
+
+ if ((bpf_get_current_pid_tgid() >> 32) == pid) {
+ ret = bpf_send_signal(sig);
+ if (ret == 0)
+ *status_val = 1;
+ }
+
+ return 0;
+}
+char __license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/test_sock_fields_kern.c b/tools/testing/selftests/bpf/progs/test_sock_fields_kern.c
index 1c39e4ccb7f1..a47b003623ef 100644
--- a/tools/testing/selftests/bpf/progs/test_sock_fields_kern.c
+++ b/tools/testing/selftests/bpf/progs/test_sock_fields_kern.c
@@ -27,58 +27,52 @@ enum bpf_linum_array_idx {
__NR_BPF_LINUM_ARRAY_IDX,
};
-struct bpf_map_def SEC("maps") addr_map = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct sockaddr_in6),
- .max_entries = __NR_BPF_ADDR_ARRAY_IDX,
-};
-
-struct bpf_map_def SEC("maps") sock_result_map = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct bpf_sock),
- .max_entries = __NR_BPF_RESULT_ARRAY_IDX,
-};
-
-struct bpf_map_def SEC("maps") tcp_sock_result_map = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct bpf_tcp_sock),
- .max_entries = __NR_BPF_RESULT_ARRAY_IDX,
-};
-
-struct bpf_map_def SEC("maps") linum_map = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(__u32),
- .max_entries = __NR_BPF_LINUM_ARRAY_IDX,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, __NR_BPF_ADDR_ARRAY_IDX);
+ __type(key, __u32);
+ __type(value, struct sockaddr_in6);
+} addr_map SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, __NR_BPF_RESULT_ARRAY_IDX);
+ __type(key, __u32);
+ __type(value, struct bpf_sock);
+} sock_result_map SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, __NR_BPF_RESULT_ARRAY_IDX);
+ __type(key, __u32);
+ __type(value, struct bpf_tcp_sock);
+} tcp_sock_result_map SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, __NR_BPF_LINUM_ARRAY_IDX);
+ __type(key, __u32);
+ __type(value, __u32);
+} linum_map SEC(".maps");
struct bpf_spinlock_cnt {
struct bpf_spin_lock lock;
__u32 cnt;
};
-struct bpf_map_def SEC("maps") sk_pkt_out_cnt = {
- .type = BPF_MAP_TYPE_SK_STORAGE,
- .key_size = sizeof(int),
- .value_size = sizeof(struct bpf_spinlock_cnt),
- .max_entries = 0,
- .map_flags = BPF_F_NO_PREALLOC,
-};
-
-BPF_ANNOTATE_KV_PAIR(sk_pkt_out_cnt, int, struct bpf_spinlock_cnt);
-
-struct bpf_map_def SEC("maps") sk_pkt_out_cnt10 = {
- .type = BPF_MAP_TYPE_SK_STORAGE,
- .key_size = sizeof(int),
- .value_size = sizeof(struct bpf_spinlock_cnt),
- .max_entries = 0,
- .map_flags = BPF_F_NO_PREALLOC,
-};
-
-BPF_ANNOTATE_KV_PAIR(sk_pkt_out_cnt10, int, struct bpf_spinlock_cnt);
+struct {
+ __uint(type, BPF_MAP_TYPE_SK_STORAGE);
+ __uint(map_flags, BPF_F_NO_PREALLOC);
+ __type(key, int);
+ __type(value, struct bpf_spinlock_cnt);
+} sk_pkt_out_cnt SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_SK_STORAGE);
+ __uint(map_flags, BPF_F_NO_PREALLOC);
+ __type(key, int);
+ __type(value, struct bpf_spinlock_cnt);
+} sk_pkt_out_cnt10 SEC(".maps");
static bool is_loopback6(__u32 *a6)
{
diff --git a/tools/testing/selftests/bpf/progs/test_spin_lock.c b/tools/testing/selftests/bpf/progs/test_spin_lock.c
index 40f904312090..a43b999c8da2 100644
--- a/tools/testing/selftests/bpf/progs/test_spin_lock.c
+++ b/tools/testing/selftests/bpf/progs/test_spin_lock.c
@@ -10,29 +10,23 @@ struct hmap_elem {
int test_padding;
};
-struct bpf_map_def SEC("maps") hmap = {
- .type = BPF_MAP_TYPE_HASH,
- .key_size = sizeof(int),
- .value_size = sizeof(struct hmap_elem),
- .max_entries = 1,
-};
-
-BPF_ANNOTATE_KV_PAIR(hmap, int, struct hmap_elem);
-
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 1);
+ __type(key, int);
+ __type(value, struct hmap_elem);
+} hmap SEC(".maps");
struct cls_elem {
struct bpf_spin_lock lock;
volatile int cnt;
};
-struct bpf_map_def SEC("maps") cls_map = {
- .type = BPF_MAP_TYPE_CGROUP_STORAGE,
- .key_size = sizeof(struct bpf_cgroup_storage_key),
- .value_size = sizeof(struct cls_elem),
-};
-
-BPF_ANNOTATE_KV_PAIR(cls_map, struct bpf_cgroup_storage_key,
- struct cls_elem);
+struct {
+ __uint(type, BPF_MAP_TYPE_CGROUP_STORAGE);
+ __type(key, struct bpf_cgroup_storage_key);
+ __type(value, struct cls_elem);
+} cls_map SEC(".maps");
struct bpf_vqueue {
struct bpf_spin_lock lock;
@@ -42,14 +36,13 @@ struct bpf_vqueue {
unsigned int rate;
};
-struct bpf_map_def SEC("maps") vqueue = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(int),
- .value_size = sizeof(struct bpf_vqueue),
- .max_entries = 1,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, int);
+ __type(value, struct bpf_vqueue);
+} vqueue SEC(".maps");
-BPF_ANNOTATE_KV_PAIR(vqueue, int, struct bpf_vqueue);
#define CREDIT_PER_NS(delta, rate) (((delta) * rate) >> 20)
SEC("spin_lock_demo")
diff --git a/tools/testing/selftests/bpf/progs/test_stacktrace_build_id.c b/tools/testing/selftests/bpf/progs/test_stacktrace_build_id.c
index d86c281e957f..f5638e26865d 100644
--- a/tools/testing/selftests/bpf/progs/test_stacktrace_build_id.c
+++ b/tools/testing/selftests/bpf/progs/test_stacktrace_build_id.c
@@ -8,36 +8,36 @@
#define PERF_MAX_STACK_DEPTH 127
#endif
-struct bpf_map_def SEC("maps") control_map = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(__u32),
- .max_entries = 1,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, __u32);
+ __type(value, __u32);
+} control_map SEC(".maps");
-struct bpf_map_def SEC("maps") stackid_hmap = {
- .type = BPF_MAP_TYPE_HASH,
- .key_size = sizeof(__u32),
- .value_size = sizeof(__u32),
- .max_entries = 16384,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 16384);
+ __type(key, __u32);
+ __type(value, __u32);
+} stackid_hmap SEC(".maps");
-struct bpf_map_def SEC("maps") stackmap = {
- .type = BPF_MAP_TYPE_STACK_TRACE,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct bpf_stack_build_id)
- * PERF_MAX_STACK_DEPTH,
- .max_entries = 128,
- .map_flags = BPF_F_STACK_BUILD_ID,
-};
+typedef struct bpf_stack_build_id stack_trace_t[PERF_MAX_STACK_DEPTH];
-struct bpf_map_def SEC("maps") stack_amap = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct bpf_stack_build_id)
- * PERF_MAX_STACK_DEPTH,
- .max_entries = 128,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_STACK_TRACE);
+ __uint(max_entries, 128);
+ __uint(map_flags, BPF_F_STACK_BUILD_ID);
+ __uint(key_size, sizeof(__u32));
+ __uint(value_size, sizeof(stack_trace_t));
+} stackmap SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 128);
+ __type(key, __u32);
+ __type(value, stack_trace_t);
+} stack_amap SEC(".maps");
/* taken from /sys/kernel/debug/tracing/events/random/urandom_read/format */
struct random_urandom_args {
diff --git a/tools/testing/selftests/bpf/progs/test_stacktrace_map.c b/tools/testing/selftests/bpf/progs/test_stacktrace_map.c
index af111af7ca1a..fa0be3e10a10 100644
--- a/tools/testing/selftests/bpf/progs/test_stacktrace_map.c
+++ b/tools/testing/selftests/bpf/progs/test_stacktrace_map.c
@@ -8,33 +8,35 @@
#define PERF_MAX_STACK_DEPTH 127
#endif
-struct bpf_map_def SEC("maps") control_map = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(__u32),
- .max_entries = 1,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, __u32);
+ __type(value, __u32);
+} control_map SEC(".maps");
-struct bpf_map_def SEC("maps") stackid_hmap = {
- .type = BPF_MAP_TYPE_HASH,
- .key_size = sizeof(__u32),
- .value_size = sizeof(__u32),
- .max_entries = 16384,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 16384);
+ __type(key, __u32);
+ __type(value, __u32);
+} stackid_hmap SEC(".maps");
-struct bpf_map_def SEC("maps") stackmap = {
- .type = BPF_MAP_TYPE_STACK_TRACE,
- .key_size = sizeof(__u32),
- .value_size = sizeof(__u64) * PERF_MAX_STACK_DEPTH,
- .max_entries = 16384,
-};
+typedef __u64 stack_trace_t[PERF_MAX_STACK_DEPTH];
-struct bpf_map_def SEC("maps") stack_amap = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(__u64) * PERF_MAX_STACK_DEPTH,
- .max_entries = 16384,
-};
+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_ARRAY);
+ __uint(max_entries, 16384);
+ __type(key, __u32);
+ __type(value, stack_trace_t);
+} stack_amap SEC(".maps");
/* taken from /sys/kernel/debug/tracing/events/sched/sched_switch/format */
struct sched_switch_args {
diff --git a/tools/testing/selftests/bpf/progs/test_sysctl_loop1.c b/tools/testing/selftests/bpf/progs/test_sysctl_loop1.c
new file mode 100644
index 000000000000..608a06871572
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_sysctl_loop1.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+
+#include <stdint.h>
+#include <string.h>
+
+#include <linux/stddef.h>
+#include <linux/bpf.h>
+
+#include "bpf_helpers.h"
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+/* tcp_mem sysctl has only 3 ints, but this test is doing TCP_MEM_LOOPS */
+#define TCP_MEM_LOOPS 28 /* because 30 doesn't fit into 512 bytes of stack */
+#define MAX_ULONG_STR_LEN 7
+#define MAX_VALUE_STR_LEN (TCP_MEM_LOOPS * MAX_ULONG_STR_LEN)
+
+static __always_inline int is_tcp_mem(struct bpf_sysctl *ctx)
+{
+ volatile char tcp_mem_name[] = "net/ipv4/tcp_mem/very_very_very_very_long_pointless_string";
+ unsigned char i;
+ char name[64];
+ int ret;
+
+ memset(name, 0, sizeof(name));
+ ret = bpf_sysctl_get_name(ctx, name, sizeof(name), 0);
+ if (ret < 0 || ret != sizeof(tcp_mem_name) - 1)
+ return 0;
+
+#pragma clang loop unroll(disable)
+ for (i = 0; i < sizeof(tcp_mem_name); ++i)
+ if (name[i] != tcp_mem_name[i])
+ return 0;
+
+ return 1;
+}
+
+SEC("cgroup/sysctl")
+int sysctl_tcp_mem(struct bpf_sysctl *ctx)
+{
+ unsigned long tcp_mem[TCP_MEM_LOOPS] = {};
+ char value[MAX_VALUE_STR_LEN];
+ unsigned char i, off = 0;
+ int ret;
+
+ if (ctx->write)
+ return 0;
+
+ if (!is_tcp_mem(ctx))
+ return 0;
+
+ ret = bpf_sysctl_get_current_value(ctx, value, MAX_VALUE_STR_LEN);
+ if (ret < 0 || ret >= MAX_VALUE_STR_LEN)
+ return 0;
+
+#pragma clang loop unroll(disable)
+ for (i = 0; i < ARRAY_SIZE(tcp_mem); ++i) {
+ ret = bpf_strtoul(value + off, MAX_ULONG_STR_LEN, 0,
+ tcp_mem + i);
+ if (ret <= 0 || ret > MAX_ULONG_STR_LEN)
+ return 0;
+ off += ret & MAX_ULONG_STR_LEN;
+ }
+
+ return tcp_mem[0] < tcp_mem[1] && tcp_mem[1] < tcp_mem[2];
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/test_sysctl_loop2.c b/tools/testing/selftests/bpf/progs/test_sysctl_loop2.c
new file mode 100644
index 000000000000..cb201cbe11e7
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_sysctl_loop2.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+
+#include <stdint.h>
+#include <string.h>
+
+#include <linux/stddef.h>
+#include <linux/bpf.h>
+
+#include "bpf_helpers.h"
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+/* tcp_mem sysctl has only 3 ints, but this test is doing TCP_MEM_LOOPS */
+#define TCP_MEM_LOOPS 20 /* because 30 doesn't fit into 512 bytes of stack */
+#define MAX_ULONG_STR_LEN 7
+#define MAX_VALUE_STR_LEN (TCP_MEM_LOOPS * MAX_ULONG_STR_LEN)
+
+static __attribute__((noinline)) int is_tcp_mem(struct bpf_sysctl *ctx)
+{
+ volatile char tcp_mem_name[] = "net/ipv4/tcp_mem/very_very_very_very_long_pointless_string_to_stress_byte_loop";
+ unsigned char i;
+ char name[64];
+ int ret;
+
+ memset(name, 0, sizeof(name));
+ ret = bpf_sysctl_get_name(ctx, name, sizeof(name), 0);
+ if (ret < 0 || ret != sizeof(tcp_mem_name) - 1)
+ return 0;
+
+#pragma clang loop unroll(disable)
+ for (i = 0; i < sizeof(tcp_mem_name); ++i)
+ if (name[i] != tcp_mem_name[i])
+ return 0;
+
+ return 1;
+}
+
+
+SEC("cgroup/sysctl")
+int sysctl_tcp_mem(struct bpf_sysctl *ctx)
+{
+ unsigned long tcp_mem[TCP_MEM_LOOPS] = {};
+ char value[MAX_VALUE_STR_LEN];
+ unsigned char i, off = 0;
+ int ret;
+
+ if (ctx->write)
+ return 0;
+
+ if (!is_tcp_mem(ctx))
+ return 0;
+
+ ret = bpf_sysctl_get_current_value(ctx, value, MAX_VALUE_STR_LEN);
+ if (ret < 0 || ret >= MAX_VALUE_STR_LEN)
+ return 0;
+
+#pragma clang loop unroll(disable)
+ for (i = 0; i < ARRAY_SIZE(tcp_mem); ++i) {
+ ret = bpf_strtoul(value + off, MAX_ULONG_STR_LEN, 0,
+ tcp_mem + i);
+ if (ret <= 0 || ret > MAX_ULONG_STR_LEN)
+ return 0;
+ off += ret & MAX_ULONG_STR_LEN;
+ }
+
+ return tcp_mem[0] < tcp_mem[1] && tcp_mem[1] < tcp_mem[2];
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/test_sysctl_prog.c b/tools/testing/selftests/bpf/progs/test_sysctl_prog.c
index a295cad805d7..5cbbff416998 100644
--- a/tools/testing/selftests/bpf/progs/test_sysctl_prog.c
+++ b/tools/testing/selftests/bpf/progs/test_sysctl_prog.c
@@ -8,7 +8,6 @@
#include <linux/bpf.h>
#include "bpf_helpers.h"
-#include "bpf_util.h"
/* Max supported length of a string with unsigned long in base 10 (pow2 - 1). */
#define MAX_ULONG_STR_LEN 0xF
@@ -16,6 +15,10 @@
/* Max supported length of sysctl value string (pow2). */
#define MAX_VALUE_STR_LEN 0x40
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
static __always_inline int is_tcp_mem(struct bpf_sysctl *ctx)
{
char tcp_mem_name[] = "net/ipv4/tcp_mem";
diff --git a/tools/testing/selftests/bpf/progs/test_tc_edt.c b/tools/testing/selftests/bpf/progs/test_tc_edt.c
index 3af64c470d64..0961415ba477 100644
--- a/tools/testing/selftests/bpf/progs/test_tc_edt.c
+++ b/tools/testing/selftests/bpf/progs/test_tc_edt.c
@@ -2,6 +2,7 @@
#include <stdint.h>
#include <linux/bpf.h>
#include <linux/if_ether.h>
+#include <linux/stddef.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/pkt_cls.h>
diff --git a/tools/testing/selftests/bpf/progs/test_tcp_check_syncookie_kern.c b/tools/testing/selftests/bpf/progs/test_tcp_check_syncookie_kern.c
index 1ab095bcacd8..d8803dfa8d32 100644
--- a/tools/testing/selftests/bpf/progs/test_tcp_check_syncookie_kern.c
+++ b/tools/testing/selftests/bpf/progs/test_tcp_check_syncookie_kern.c
@@ -19,10 +19,29 @@
struct bpf_map_def SEC("maps") results = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(__u32),
- .value_size = sizeof(__u64),
- .max_entries = 1,
+ .value_size = sizeof(__u32),
+ .max_entries = 3,
};
+static __always_inline __s64 gen_syncookie(void *data_end, struct bpf_sock *sk,
+ void *iph, __u32 ip_size,
+ struct tcphdr *tcph)
+{
+ __u32 thlen = tcph->doff * 4;
+
+ if (tcph->syn && !tcph->ack) {
+ // packet should only have an MSS option
+ if (thlen != 24)
+ return 0;
+
+ if ((void *)tcph + thlen > data_end)
+ return 0;
+
+ return bpf_tcp_gen_syncookie(sk, iph, ip_size, tcph, thlen);
+ }
+ return 0;
+}
+
static __always_inline void check_syncookie(void *ctx, void *data,
void *data_end)
{
@@ -33,8 +52,10 @@ static __always_inline void check_syncookie(void *ctx, void *data,
struct ipv6hdr *ipv6h;
struct tcphdr *tcph;
int ret;
+ __u32 key_mss = 2;
+ __u32 key_gen = 1;
__u32 key = 0;
- __u64 value = 1;
+ __s64 seq_mss;
ethh = data;
if (ethh + 1 > data_end)
@@ -66,6 +87,9 @@ static __always_inline void check_syncookie(void *ctx, void *data,
if (sk->state != BPF_TCP_LISTEN)
goto release;
+ seq_mss = gen_syncookie(data_end, sk, ipv4h, sizeof(*ipv4h),
+ tcph);
+
ret = bpf_tcp_check_syncookie(sk, ipv4h, sizeof(*ipv4h),
tcph, sizeof(*tcph));
break;
@@ -95,6 +119,9 @@ static __always_inline void check_syncookie(void *ctx, void *data,
if (sk->state != BPF_TCP_LISTEN)
goto release;
+ seq_mss = gen_syncookie(data_end, sk, ipv6h, sizeof(*ipv6h),
+ tcph);
+
ret = bpf_tcp_check_syncookie(sk, ipv6h, sizeof(*ipv6h),
tcph, sizeof(*tcph));
break;
@@ -103,8 +130,19 @@ static __always_inline void check_syncookie(void *ctx, void *data,
return;
}
- if (ret == 0)
- bpf_map_update_elem(&results, &key, &value, 0);
+ if (seq_mss > 0) {
+ __u32 cookie = (__u32)seq_mss;
+ __u32 mss = seq_mss >> 32;
+
+ bpf_map_update_elem(&results, &key_gen, &cookie, 0);
+ bpf_map_update_elem(&results, &key_mss, &mss, 0);
+ }
+
+ if (ret == 0) {
+ __u32 cookie = bpf_ntohl(tcph->ack_seq) - 1;
+
+ bpf_map_update_elem(&results, &key, &cookie, 0);
+ }
release:
bpf_sk_release(sk);
diff --git a/tools/testing/selftests/bpf/progs/test_tcp_estats.c b/tools/testing/selftests/bpf/progs/test_tcp_estats.c
index bee3bbecc0c4..c8c595da38d4 100644
--- a/tools/testing/selftests/bpf/progs/test_tcp_estats.c
+++ b/tools/testing/selftests/bpf/progs/test_tcp_estats.c
@@ -148,12 +148,12 @@ struct tcp_estats_basic_event {
struct tcp_estats_conn_id conn_id;
};
-struct bpf_map_def SEC("maps") ev_record_map = {
- .type = BPF_MAP_TYPE_HASH,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct tcp_estats_basic_event),
- .max_entries = 1024,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 1024);
+ __type(key, __u32);
+ __type(value, struct tcp_estats_basic_event);
+} ev_record_map SEC(".maps");
struct dummy_tracepoint_args {
unsigned long long pad;
diff --git a/tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c b/tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c
index c7c3240e0dd4..2e233613d1fc 100644
--- a/tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c
+++ b/tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c
@@ -14,19 +14,19 @@
#include "bpf_endian.h"
#include "test_tcpbpf.h"
-struct bpf_map_def SEC("maps") global_map = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct tcpbpf_globals),
- .max_entries = 4,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 4);
+ __type(key, __u32);
+ __type(value, struct tcpbpf_globals);
+} global_map SEC(".maps");
-struct bpf_map_def SEC("maps") sockopt_results = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(int),
- .max_entries = 2,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 2);
+ __type(key, __u32);
+ __type(value, int);
+} sockopt_results SEC(".maps");
static inline void update_event_map(int event)
{
diff --git a/tools/testing/selftests/bpf/progs/test_tcpnotify_kern.c b/tools/testing/selftests/bpf/progs/test_tcpnotify_kern.c
index ec6db6e64c41..08346e7765d5 100644
--- a/tools/testing/selftests/bpf/progs/test_tcpnotify_kern.c
+++ b/tools/testing/selftests/bpf/progs/test_tcpnotify_kern.c
@@ -14,19 +14,19 @@
#include "bpf_endian.h"
#include "test_tcpnotify.h"
-struct bpf_map_def SEC("maps") global_map = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct tcpnotify_globals),
- .max_entries = 4,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 4);
+ __type(key, __u32);
+ __type(value, struct tcpnotify_globals);
+} global_map SEC(".maps");
-struct bpf_map_def SEC("maps") perf_event_map = {
- .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
- .key_size = sizeof(int),
- .value_size = sizeof(__u32),
- .max_entries = 2,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
+ __uint(max_entries, 2);
+ __uint(key_size, sizeof(int));
+ __uint(value_size, sizeof(__u32));
+} perf_event_map SEC(".maps");
int _version SEC("version") = 1;
diff --git a/tools/testing/selftests/bpf/progs/test_verif_scale2.c b/tools/testing/selftests/bpf/progs/test_verif_scale2.c
index 77830693eccb..9897150ed516 100644
--- a/tools/testing/selftests/bpf/progs/test_verif_scale2.c
+++ b/tools/testing/selftests/bpf/progs/test_verif_scale2.c
@@ -2,7 +2,7 @@
// Copyright (c) 2019 Facebook
#include <linux/bpf.h>
#include "bpf_helpers.h"
-#define ATTR __attribute__((always_inline))
+#define ATTR __always_inline
#include "test_jhash.h"
SEC("scale90_inline")
diff --git a/tools/testing/selftests/bpf/progs/test_xdp.c b/tools/testing/selftests/bpf/progs/test_xdp.c
index 5e7df8bb5b5d..0941c655b07b 100644
--- a/tools/testing/selftests/bpf/progs/test_xdp.c
+++ b/tools/testing/selftests/bpf/progs/test_xdp.c
@@ -22,19 +22,19 @@
int _version SEC("version") = 1;
-struct bpf_map_def SEC("maps") rxcnt = {
- .type = BPF_MAP_TYPE_PERCPU_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(__u64),
- .max_entries = 256,
-};
-
-struct bpf_map_def SEC("maps") vip2tnl = {
- .type = BPF_MAP_TYPE_HASH,
- .key_size = sizeof(struct vip),
- .value_size = sizeof(struct iptnl_info),
- .max_entries = MAX_IPTNL_ENTRIES,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+ __uint(max_entries, 256);
+ __type(key, __u32);
+ __type(value, __u64);
+} rxcnt SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, MAX_IPTNL_ENTRIES);
+ __type(key, struct vip);
+ __type(value, struct iptnl_info);
+} vip2tnl SEC(".maps");
static __always_inline void count_tx(__u32 protocol)
{
diff --git a/tools/testing/selftests/bpf/progs/test_xdp_loop.c b/tools/testing/selftests/bpf/progs/test_xdp_loop.c
new file mode 100644
index 000000000000..97175f73c3fe
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_xdp_loop.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+#include <stddef.h>
+#include <string.h>
+#include <linux/bpf.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/in.h>
+#include <linux/udp.h>
+#include <linux/tcp.h>
+#include <linux/pkt_cls.h>
+#include <sys/socket.h>
+#include "bpf_helpers.h"
+#include "bpf_endian.h"
+#include "test_iptunnel_common.h"
+
+int _version SEC("version") = 1;
+
+struct {
+ __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+ __uint(max_entries, 256);
+ __type(key, __u32);
+ __type(value, __u64);
+} rxcnt SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, MAX_IPTNL_ENTRIES);
+ __type(key, struct vip);
+ __type(value, struct iptnl_info);
+} vip2tnl SEC(".maps");
+
+static __always_inline void count_tx(__u32 protocol)
+{
+ __u64 *rxcnt_count;
+
+ rxcnt_count = bpf_map_lookup_elem(&rxcnt, &protocol);
+ if (rxcnt_count)
+ *rxcnt_count += 1;
+}
+
+static __always_inline int get_dport(void *trans_data, void *data_end,
+ __u8 protocol)
+{
+ struct tcphdr *th;
+ struct udphdr *uh;
+
+ switch (protocol) {
+ case IPPROTO_TCP:
+ th = (struct tcphdr *)trans_data;
+ if (th + 1 > data_end)
+ return -1;
+ return th->dest;
+ case IPPROTO_UDP:
+ uh = (struct udphdr *)trans_data;
+ if (uh + 1 > data_end)
+ return -1;
+ return uh->dest;
+ default:
+ return 0;
+ }
+}
+
+static __always_inline void set_ethhdr(struct ethhdr *new_eth,
+ const struct ethhdr *old_eth,
+ const struct iptnl_info *tnl,
+ __be16 h_proto)
+{
+ memcpy(new_eth->h_source, old_eth->h_dest, sizeof(new_eth->h_source));
+ memcpy(new_eth->h_dest, tnl->dmac, sizeof(new_eth->h_dest));
+ new_eth->h_proto = h_proto;
+}
+
+static __always_inline int handle_ipv4(struct xdp_md *xdp)
+{
+ void *data_end = (void *)(long)xdp->data_end;
+ void *data = (void *)(long)xdp->data;
+ struct iptnl_info *tnl;
+ struct ethhdr *new_eth;
+ struct ethhdr *old_eth;
+ struct iphdr *iph = data + sizeof(struct ethhdr);
+ __u16 *next_iph;
+ __u16 payload_len;
+ struct vip vip = {};
+ int dport;
+ __u32 csum = 0;
+ int i;
+
+ if (iph + 1 > data_end)
+ return XDP_DROP;
+
+ dport = get_dport(iph + 1, data_end, iph->protocol);
+ if (dport == -1)
+ return XDP_DROP;
+
+ vip.protocol = iph->protocol;
+ vip.family = AF_INET;
+ vip.daddr.v4 = iph->daddr;
+ vip.dport = dport;
+ payload_len = bpf_ntohs(iph->tot_len);
+
+ tnl = bpf_map_lookup_elem(&vip2tnl, &vip);
+ /* It only does v4-in-v4 */
+ if (!tnl || tnl->family != AF_INET)
+ return XDP_PASS;
+
+ if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct iphdr)))
+ return XDP_DROP;
+
+ data = (void *)(long)xdp->data;
+ data_end = (void *)(long)xdp->data_end;
+
+ new_eth = data;
+ iph = data + sizeof(*new_eth);
+ old_eth = data + sizeof(*iph);
+
+ if (new_eth + 1 > data_end ||
+ old_eth + 1 > data_end ||
+ iph + 1 > data_end)
+ return XDP_DROP;
+
+ set_ethhdr(new_eth, old_eth, tnl, bpf_htons(ETH_P_IP));
+
+ iph->version = 4;
+ iph->ihl = sizeof(*iph) >> 2;
+ iph->frag_off = 0;
+ iph->protocol = IPPROTO_IPIP;
+ iph->check = 0;
+ iph->tos = 0;
+ iph->tot_len = bpf_htons(payload_len + sizeof(*iph));
+ iph->daddr = tnl->daddr.v4;
+ iph->saddr = tnl->saddr.v4;
+ iph->ttl = 8;
+
+ next_iph = (__u16 *)iph;
+#pragma clang loop unroll(disable)
+ for (i = 0; i < sizeof(*iph) >> 1; i++)
+ csum += *next_iph++;
+
+ iph->check = ~((csum & 0xffff) + (csum >> 16));
+
+ count_tx(vip.protocol);
+
+ return XDP_TX;
+}
+
+static __always_inline int handle_ipv6(struct xdp_md *xdp)
+{
+ void *data_end = (void *)(long)xdp->data_end;
+ void *data = (void *)(long)xdp->data;
+ struct iptnl_info *tnl;
+ struct ethhdr *new_eth;
+ struct ethhdr *old_eth;
+ struct ipv6hdr *ip6h = data + sizeof(struct ethhdr);
+ __u16 payload_len;
+ struct vip vip = {};
+ int dport;
+
+ if (ip6h + 1 > data_end)
+ return XDP_DROP;
+
+ dport = get_dport(ip6h + 1, data_end, ip6h->nexthdr);
+ if (dport == -1)
+ return XDP_DROP;
+
+ vip.protocol = ip6h->nexthdr;
+ vip.family = AF_INET6;
+ memcpy(vip.daddr.v6, ip6h->daddr.s6_addr32, sizeof(vip.daddr));
+ vip.dport = dport;
+ payload_len = ip6h->payload_len;
+
+ tnl = bpf_map_lookup_elem(&vip2tnl, &vip);
+ /* It only does v6-in-v6 */
+ if (!tnl || tnl->family != AF_INET6)
+ return XDP_PASS;
+
+ if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct ipv6hdr)))
+ return XDP_DROP;
+
+ data = (void *)(long)xdp->data;
+ data_end = (void *)(long)xdp->data_end;
+
+ new_eth = data;
+ ip6h = data + sizeof(*new_eth);
+ old_eth = data + sizeof(*ip6h);
+
+ if (new_eth + 1 > data_end || old_eth + 1 > data_end ||
+ ip6h + 1 > data_end)
+ return XDP_DROP;
+
+ set_ethhdr(new_eth, old_eth, tnl, bpf_htons(ETH_P_IPV6));
+
+ ip6h->version = 6;
+ ip6h->priority = 0;
+ memset(ip6h->flow_lbl, 0, sizeof(ip6h->flow_lbl));
+ ip6h->payload_len = bpf_htons(bpf_ntohs(payload_len) + sizeof(*ip6h));
+ ip6h->nexthdr = IPPROTO_IPV6;
+ ip6h->hop_limit = 8;
+ memcpy(ip6h->saddr.s6_addr32, tnl->saddr.v6, sizeof(tnl->saddr.v6));
+ memcpy(ip6h->daddr.s6_addr32, tnl->daddr.v6, sizeof(tnl->daddr.v6));
+
+ count_tx(vip.protocol);
+
+ return XDP_TX;
+}
+
+SEC("xdp_tx_iptunnel")
+int _xdp_tx_iptunnel(struct xdp_md *xdp)
+{
+ void *data_end = (void *)(long)xdp->data_end;
+ void *data = (void *)(long)xdp->data;
+ struct ethhdr *eth = data;
+ __u16 h_proto;
+
+ if (eth + 1 > data_end)
+ return XDP_DROP;
+
+ h_proto = eth->h_proto;
+
+ if (h_proto == bpf_htons(ETH_P_IP))
+ return handle_ipv4(xdp);
+ else if (h_proto == bpf_htons(ETH_P_IPV6))
+
+ return handle_ipv6(xdp);
+ else
+ return XDP_DROP;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/test_xdp_noinline.c b/tools/testing/selftests/bpf/progs/test_xdp_noinline.c
index 5e4aac74f9d0..e88d7b9d65ab 100644
--- a/tools/testing/selftests/bpf/progs/test_xdp_noinline.c
+++ b/tools/testing/selftests/bpf/progs/test_xdp_noinline.c
@@ -14,13 +14,7 @@
#include <linux/tcp.h>
#include <linux/udp.h>
#include "bpf_helpers.h"
-
-#define bpf_printk(fmt, ...) \
-({ \
- char ____fmt[] = fmt; \
- bpf_trace_printk(____fmt, sizeof(____fmt), \
- ##__VA_ARGS__); \
-})
+#include "bpf_endian.h"
static __u32 rol32(__u32 word, unsigned int shift)
{
@@ -170,53 +164,48 @@ struct lb_stats {
__u64 v1;
};
-struct bpf_map_def __attribute__ ((section("maps"), used)) vip_map = {
- .type = BPF_MAP_TYPE_HASH,
- .key_size = sizeof(struct vip_definition),
- .value_size = sizeof(struct vip_meta),
- .max_entries = 512,
- .map_flags = 0,
-};
-
-struct bpf_map_def __attribute__ ((section("maps"), used)) lru_cache = {
- .type = BPF_MAP_TYPE_LRU_HASH,
- .key_size = sizeof(struct flow_key),
- .value_size = sizeof(struct real_pos_lru),
- .max_entries = 300,
- .map_flags = 1U << 1,
-};
-
-struct bpf_map_def __attribute__ ((section("maps"), used)) ch_rings = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(__u32),
- .max_entries = 12 * 655,
- .map_flags = 0,
-};
-
-struct bpf_map_def __attribute__ ((section("maps"), used)) reals = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct real_definition),
- .max_entries = 40,
- .map_flags = 0,
-};
-
-struct bpf_map_def __attribute__ ((section("maps"), used)) stats = {
- .type = BPF_MAP_TYPE_PERCPU_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct lb_stats),
- .max_entries = 515,
- .map_flags = 0,
-};
-
-struct bpf_map_def __attribute__ ((section("maps"), used)) ctl_array = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct ctl_value),
- .max_entries = 16,
- .map_flags = 0,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 512);
+ __type(key, struct vip_definition);
+ __type(value, struct vip_meta);
+} vip_map SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_LRU_HASH);
+ __uint(max_entries, 300);
+ __uint(map_flags, 1U << 1);
+ __type(key, struct flow_key);
+ __type(value, struct real_pos_lru);
+} lru_cache SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 12 * 655);
+ __type(key, __u32);
+ __type(value, __u32);
+} ch_rings SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 40);
+ __type(key, __u32);
+ __type(value, struct real_definition);
+} reals SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+ __uint(max_entries, 515);
+ __type(key, __u32);
+ __type(value, struct lb_stats);
+} stats SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 16);
+ __type(key, __u32);
+ __type(value, struct ctl_value);
+} ctl_array SEC(".maps");
struct eth_hdr {
unsigned char eth_dest[6];
@@ -317,7 +306,7 @@ bool encap_v6(struct xdp_md *xdp, struct ctl_value *cval,
ip6h->nexthdr = IPPROTO_IPV6;
ip_suffix = pckt->flow.srcv6[3] ^ pckt->flow.port16[0];
ip6h->payload_len =
- __builtin_bswap16(pkt_bytes + sizeof(struct ipv6hdr));
+ bpf_htons(pkt_bytes + sizeof(struct ipv6hdr));
ip6h->hop_limit = 4;
ip6h->saddr.in6_u.u6_addr32[0] = 1;
@@ -334,7 +323,7 @@ bool encap_v4(struct xdp_md *xdp, struct ctl_value *cval,
struct real_definition *dst, __u32 pkt_bytes)
{
- __u32 ip_suffix = __builtin_bswap16(pckt->flow.port16[0]);
+ __u32 ip_suffix = bpf_ntohs(pckt->flow.port16[0]);
struct eth_hdr *new_eth;
struct eth_hdr *old_eth;
__u16 *next_iph_u16;
@@ -364,7 +353,7 @@ bool encap_v4(struct xdp_md *xdp, struct ctl_value *cval,
iph->protocol = IPPROTO_IPIP;
iph->check = 0;
iph->tos = 1;
- iph->tot_len = __builtin_bswap16(pkt_bytes + sizeof(struct iphdr));
+ iph->tot_len = bpf_htons(pkt_bytes + sizeof(struct iphdr));
/* don't update iph->daddr, since it will overwrite old eth_proto
* and multiple iterations of bpf_prog_run() will fail
*/
@@ -651,7 +640,7 @@ static int process_l3_headers_v6(struct packet_description *pckt,
iph_len = sizeof(struct ipv6hdr);
*protocol = ip6h->nexthdr;
pckt->flow.proto = *protocol;
- *pkt_bytes = __builtin_bswap16(ip6h->payload_len);
+ *pkt_bytes = bpf_ntohs(ip6h->payload_len);
off += iph_len;
if (*protocol == 45) {
return XDP_DROP;
@@ -683,7 +672,7 @@ static int process_l3_headers_v4(struct packet_description *pckt,
return XDP_DROP;
*protocol = iph->protocol;
pckt->flow.proto = *protocol;
- *pkt_bytes = __builtin_bswap16(iph->tot_len);
+ *pkt_bytes = bpf_ntohs(iph->tot_len);
off += 20;
if (iph->frag_off & 65343)
return XDP_DROP;
@@ -820,10 +809,10 @@ int balancer_ingress(struct xdp_md *ctx)
nh_off = sizeof(struct eth_hdr);
if (data + nh_off > data_end)
return XDP_DROP;
- eth_proto = eth->eth_proto;
- if (eth_proto == 8)
+ eth_proto = bpf_ntohs(eth->eth_proto);
+ if (eth_proto == ETH_P_IP)
return process_packet(data, nh_off, data_end, 0, ctx);
- else if (eth_proto == 56710)
+ else if (eth_proto == ETH_P_IPV6)
return process_packet(data, nh_off, data_end, 1, ctx);
else
return XDP_DROP;
diff --git a/tools/testing/selftests/bpf/progs/xdp_redirect_map.c b/tools/testing/selftests/bpf/progs/xdp_redirect_map.c
new file mode 100644
index 000000000000..1c5f298d7196
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/xdp_redirect_map.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+struct {
+ __uint(type, BPF_MAP_TYPE_DEVMAP);
+ __uint(max_entries, 8);
+ __uint(key_size, sizeof(int));
+ __uint(value_size, sizeof(int));
+} tx_port SEC(".maps");
+
+SEC("redirect_map_0")
+int xdp_redirect_map_0(struct xdp_md *xdp)
+{
+ return bpf_redirect_map(&tx_port, 0, 0);
+}
+
+SEC("redirect_map_1")
+int xdp_redirect_map_1(struct xdp_md *xdp)
+{
+ return bpf_redirect_map(&tx_port, 1, 0);
+}
+
+SEC("redirect_map_2")
+int xdp_redirect_map_2(struct xdp_md *xdp)
+{
+ return bpf_redirect_map(&tx_port, 2, 0);
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/xdp_tx.c b/tools/testing/selftests/bpf/progs/xdp_tx.c
new file mode 100644
index 000000000000..57912e7c94b0
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/xdp_tx.c
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+SEC("tx")
+int xdp_tx(struct xdp_md *xdp)
+{
+ return XDP_TX;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/xdping_kern.c b/tools/testing/selftests/bpf/progs/xdping_kern.c
new file mode 100644
index 000000000000..112a2857f4e2
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/xdping_kern.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. */
+
+#define KBUILD_MODNAME "foo"
+#include <stddef.h>
+#include <string.h>
+#include <linux/bpf.h>
+#include <linux/icmp.h>
+#include <linux/in.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <linux/if_vlan.h>
+#include <linux/ip.h>
+
+#include "bpf_helpers.h"
+#include "bpf_endian.h"
+
+#include "xdping.h"
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 256);
+ __type(key, __u32);
+ __type(value, struct pinginfo);
+} ping_map SEC(".maps");
+
+static __always_inline void swap_src_dst_mac(void *data)
+{
+ unsigned short *p = data;
+ unsigned short dst[3];
+
+ dst[0] = p[0];
+ dst[1] = p[1];
+ dst[2] = p[2];
+ p[0] = p[3];
+ p[1] = p[4];
+ p[2] = p[5];
+ p[3] = dst[0];
+ p[4] = dst[1];
+ p[5] = dst[2];
+}
+
+static __always_inline __u16 csum_fold_helper(__wsum sum)
+{
+ sum = (sum & 0xffff) + (sum >> 16);
+ return ~((sum & 0xffff) + (sum >> 16));
+}
+
+static __always_inline __u16 ipv4_csum(void *data_start, int data_size)
+{
+ __wsum sum;
+
+ sum = bpf_csum_diff(0, 0, data_start, data_size, 0);
+ return csum_fold_helper(sum);
+}
+
+#define ICMP_ECHO_LEN 64
+
+static __always_inline int icmp_check(struct xdp_md *ctx, int type)
+{
+ void *data_end = (void *)(long)ctx->data_end;
+ void *data = (void *)(long)ctx->data;
+ struct ethhdr *eth = data;
+ struct icmphdr *icmph;
+ struct iphdr *iph;
+
+ if (data + sizeof(*eth) + sizeof(*iph) + ICMP_ECHO_LEN > data_end)
+ return XDP_PASS;
+
+ if (eth->h_proto != bpf_htons(ETH_P_IP))
+ return XDP_PASS;
+
+ iph = data + sizeof(*eth);
+
+ if (iph->protocol != IPPROTO_ICMP)
+ return XDP_PASS;
+
+ if (bpf_ntohs(iph->tot_len) - sizeof(*iph) != ICMP_ECHO_LEN)
+ return XDP_PASS;
+
+ icmph = data + sizeof(*eth) + sizeof(*iph);
+
+ if (icmph->type != type)
+ return XDP_PASS;
+
+ return XDP_TX;
+}
+
+SEC("xdpclient")
+int xdping_client(struct xdp_md *ctx)
+{
+ void *data_end = (void *)(long)ctx->data_end;
+ void *data = (void *)(long)ctx->data;
+ struct pinginfo *pinginfo = NULL;
+ struct ethhdr *eth = data;
+ struct icmphdr *icmph;
+ struct iphdr *iph;
+ __u64 recvtime;
+ __be32 raddr;
+ __be16 seq;
+ int ret;
+ __u8 i;
+
+ ret = icmp_check(ctx, ICMP_ECHOREPLY);
+
+ if (ret != XDP_TX)
+ return ret;
+
+ iph = data + sizeof(*eth);
+ icmph = data + sizeof(*eth) + sizeof(*iph);
+ raddr = iph->saddr;
+
+ /* Record time reply received. */
+ recvtime = bpf_ktime_get_ns();
+ pinginfo = bpf_map_lookup_elem(&ping_map, &raddr);
+ if (!pinginfo || pinginfo->seq != icmph->un.echo.sequence)
+ return XDP_PASS;
+
+ if (pinginfo->start) {
+#pragma clang loop unroll(full)
+ for (i = 0; i < XDPING_MAX_COUNT; i++) {
+ if (pinginfo->times[i] == 0)
+ break;
+ }
+ /* verifier is fussy here... */
+ if (i < XDPING_MAX_COUNT) {
+ pinginfo->times[i] = recvtime -
+ pinginfo->start;
+ pinginfo->start = 0;
+ i++;
+ }
+ /* No more space for values? */
+ if (i == pinginfo->count || i == XDPING_MAX_COUNT)
+ return XDP_PASS;
+ }
+
+ /* Now convert reply back into echo request. */
+ swap_src_dst_mac(data);
+ iph->saddr = iph->daddr;
+ iph->daddr = raddr;
+ icmph->type = ICMP_ECHO;
+ seq = bpf_htons(bpf_ntohs(icmph->un.echo.sequence) + 1);
+ icmph->un.echo.sequence = seq;
+ icmph->checksum = 0;
+ icmph->checksum = ipv4_csum(icmph, ICMP_ECHO_LEN);
+
+ pinginfo->seq = seq;
+ pinginfo->start = bpf_ktime_get_ns();
+
+ return XDP_TX;
+}
+
+SEC("xdpserver")
+int xdping_server(struct xdp_md *ctx)
+{
+ void *data_end = (void *)(long)ctx->data_end;
+ void *data = (void *)(long)ctx->data;
+ struct ethhdr *eth = data;
+ struct icmphdr *icmph;
+ struct iphdr *iph;
+ __be32 raddr;
+ int ret;
+
+ ret = icmp_check(ctx, ICMP_ECHO);
+
+ if (ret != XDP_TX)
+ return ret;
+
+ iph = data + sizeof(*eth);
+ icmph = data + sizeof(*eth) + sizeof(*iph);
+ raddr = iph->saddr;
+
+ /* Now convert request into echo reply. */
+ swap_src_dst_mac(data);
+ iph->saddr = iph->daddr;
+ iph->daddr = raddr;
+ icmph->type = ICMP_ECHOREPLY;
+ icmph->checksum = 0;
+ icmph->checksum = ipv4_csum(icmph, ICMP_ECHO_LEN);
+
+ return XDP_TX;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/test_align.c b/tools/testing/selftests/bpf/test_align.c
index 3c789d03b629..0262f7b374f9 100644
--- a/tools/testing/selftests/bpf/test_align.c
+++ b/tools/testing/selftests/bpf/test_align.c
@@ -180,7 +180,7 @@ static struct bpf_align_test tests[] = {
},
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
.matches = {
- {7, "R0=pkt(id=0,off=8,r=8,imm=0)"},
+ {7, "R0_w=pkt(id=0,off=8,r=8,imm=0)"},
{7, "R3_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
{8, "R3_w=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"},
{9, "R3_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
@@ -315,7 +315,7 @@ static struct bpf_align_test tests[] = {
/* Calculated offset in R6 has unknown value, but known
* alignment of 4.
*/
- {8, "R2=pkt(id=0,off=0,r=8,imm=0)"},
+ {8, "R2_w=pkt(id=0,off=0,r=8,imm=0)"},
{8, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
/* Offset is added to packet pointer R5, resulting in
* known fixed offset, and variable offset from R6.
@@ -405,7 +405,7 @@ static struct bpf_align_test tests[] = {
/* Calculated offset in R6 has unknown value, but known
* alignment of 4.
*/
- {8, "R2=pkt(id=0,off=0,r=8,imm=0)"},
+ {8, "R2_w=pkt(id=0,off=0,r=8,imm=0)"},
{8, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
/* Adding 14 makes R6 be (4n+2) */
{9, "R6_w=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},
@@ -473,12 +473,12 @@ static struct bpf_align_test tests[] = {
/* (4n) + 14 == (4n+2). We blow our bounds, because
* the add could overflow.
*/
- {7, "R5=inv(id=0,var_off=(0x2; 0xfffffffffffffffc))"},
+ {7, "R5_w=inv(id=0,var_off=(0x2; 0xfffffffffffffffc))"},
/* Checked s>=0 */
{9, "R5=inv(id=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
/* packet pointer + nonnegative (4n+2) */
{11, "R6_w=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
- {13, "R4=pkt(id=1,off=4,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
+ {13, "R4_w=pkt(id=1,off=4,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
/* NET_IP_ALIGN + (4n+2) == (4n), alignment is fine.
* We checked the bounds, but it might have been able
* to overflow if the packet pointer started in the
@@ -486,7 +486,7 @@ static struct bpf_align_test tests[] = {
* So we did not get a 'range' on R6, and the access
* attempt will fail.
*/
- {15, "R6=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
+ {15, "R6_w=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
}
},
{
@@ -521,7 +521,7 @@ static struct bpf_align_test tests[] = {
/* Calculated offset in R6 has unknown value, but known
* alignment of 4.
*/
- {7, "R2=pkt(id=0,off=0,r=8,imm=0)"},
+ {7, "R2_w=pkt(id=0,off=0,r=8,imm=0)"},
{9, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
/* Adding 14 makes R6 be (4n+2) */
{10, "R6_w=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},
@@ -574,7 +574,7 @@ static struct bpf_align_test tests[] = {
/* Calculated offset in R6 has unknown value, but known
* alignment of 4.
*/
- {7, "R2=pkt(id=0,off=0,r=8,imm=0)"},
+ {7, "R2_w=pkt(id=0,off=0,r=8,imm=0)"},
{10, "R6_w=inv(id=0,umax_value=60,var_off=(0x0; 0x3c))"},
/* Adding 14 makes R6 be (4n+2) */
{11, "R6_w=inv(id=0,umin_value=14,umax_value=74,var_off=(0x2; 0x7c))"},
diff --git a/tools/testing/selftests/bpf/test_bpftool_build.sh b/tools/testing/selftests/bpf/test_bpftool_build.sh
new file mode 100755
index 000000000000..4ba5a34bff56
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_bpftool_build.sh
@@ -0,0 +1,143 @@
+#!/bin/bash
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+
+ERROR=0
+TMPDIR=
+
+# If one build fails, continue but return non-0 on exit.
+return_value() {
+ if [ -d "$TMPDIR" ] ; then
+ rm -rf -- $TMPDIR
+ fi
+ exit $ERROR
+}
+trap return_value EXIT
+
+case $1 in
+ -h|--help)
+ echo -e "$0 [-j <n>]"
+ echo -e "\tTest the different ways of building bpftool."
+ echo -e ""
+ echo -e "\tOptions:"
+ echo -e "\t\t-j <n>:\tPass -j flag to 'make'."
+ exit
+ ;;
+esac
+
+J=$*
+
+# Assume script is located under tools/testing/selftests/bpf/. We want to start
+# build attempts from the top of kernel repository.
+SCRIPT_REL_PATH=$(realpath --relative-to=$PWD $0)
+SCRIPT_REL_DIR=$(dirname $SCRIPT_REL_PATH)
+KDIR_ROOT_DIR=$(realpath $PWD/$SCRIPT_REL_DIR/../../../../)
+cd $KDIR_ROOT_DIR
+
+check() {
+ local dir=$(realpath $1)
+
+ echo -n "binary: "
+ # Returns non-null if file is found (and "false" is run)
+ find $dir -type f -executable -name bpftool -print -exec false {} + && \
+ ERROR=1 && printf "FAILURE: Did not find bpftool\n"
+}
+
+make_and_clean() {
+ echo -e "\$PWD: $PWD"
+ echo -e "command: make -s $* >/dev/null"
+ make $J -s $* >/dev/null
+ if [ $? -ne 0 ] ; then
+ ERROR=1
+ fi
+ if [ $# -ge 1 ] ; then
+ check ${@: -1}
+ else
+ check .
+ fi
+ (
+ if [ $# -ge 1 ] ; then
+ cd ${@: -1}
+ fi
+ make -s clean
+ )
+ echo
+}
+
+make_with_tmpdir() {
+ local ARGS
+
+ TMPDIR=$(mktemp -d)
+ if [ $# -ge 2 ] ; then
+ ARGS=${@:1:(($# - 1))}
+ fi
+ echo -e "\$PWD: $PWD"
+ echo -e "command: make -s $ARGS ${@: -1}=$TMPDIR/ >/dev/null"
+ make $J -s $ARGS ${@: -1}=$TMPDIR/ >/dev/null
+ if [ $? -ne 0 ] ; then
+ ERROR=1
+ fi
+ check $TMPDIR
+ rm -rf -- $TMPDIR
+ echo
+}
+
+echo "Trying to build bpftool"
+echo -e "... through kbuild\n"
+
+if [ -f ".config" ] ; then
+ make_and_clean tools/bpf
+
+ ## $OUTPUT is overwritten in kbuild Makefile, and thus cannot be passed
+ ## down from toplevel Makefile to bpftool's Makefile.
+
+ # make_with_tmpdir tools/bpf OUTPUT
+ echo -e "skip: make tools/bpf OUTPUT=<dir> (not supported)\n"
+
+ make_with_tmpdir tools/bpf O
+else
+ echo -e "skip: make tools/bpf (no .config found)\n"
+ echo -e "skip: make tools/bpf OUTPUT=<dir> (not supported)\n"
+ echo -e "skip: make tools/bpf O=<dir> (no .config found)\n"
+fi
+
+echo -e "... from kernel source tree\n"
+
+make_and_clean -C tools/bpf/bpftool
+
+make_with_tmpdir -C tools/bpf/bpftool OUTPUT
+
+make_with_tmpdir -C tools/bpf/bpftool O
+
+echo -e "... from tools/\n"
+cd tools/
+
+make_and_clean bpf
+
+## In tools/bpf/Makefile, function "descend" is called and passes $(O) and
+## $(OUTPUT). We would like $(OUTPUT) to have "bpf/bpftool/" appended before
+## calling bpftool's Makefile, but this is not the case as the "descend"
+## function focuses on $(O)/$(subdir). However, in the present case, updating
+## $(O) to have $(OUTPUT) recomputed from it in bpftool's Makefile does not
+## work, because $(O) is not defined from command line and $(OUTPUT) is not
+## updated in tools/scripts/Makefile.include.
+##
+## Workarounds would require to a) edit "descend" or use an alternative way to
+## call bpftool's Makefile, b) modify the conditions to update $(OUTPUT) and
+## other variables in tools/scripts/Makefile.include (at the risk of breaking
+## the build of other tools), or c) append manually the "bpf/bpftool" suffix to
+## $(OUTPUT) in bpf's Makefile, which may break if targets for other directories
+## use "descend" in the future.
+
+# make_with_tmpdir bpf OUTPUT
+echo -e "skip: make bpf OUTPUT=<dir> (not supported)\n"
+
+make_with_tmpdir bpf O
+
+echo -e "... from bpftool's dir\n"
+cd bpf/bpftool
+
+make_and_clean
+
+make_with_tmpdir OUTPUT
+
+make_with_tmpdir O
diff --git a/tools/testing/selftests/bpf/test_btf.c b/tools/testing/selftests/bpf/test_btf.c
index 42c1ce988945..3d617e806054 100644
--- a/tools/testing/selftests/bpf/test_btf.c
+++ b/tools/testing/selftests/bpf/test_btf.c
@@ -3417,6 +3417,94 @@ static struct btf_raw_test raw_tests[] = {
.value_type_id = 1,
.max_entries = 4,
},
+/*
+ * typedef int arr_t[16];
+ * struct s {
+ * arr_t *a;
+ * };
+ */
+{
+ .descr = "struct->ptr->typedef->array->int size resolution",
+ .raw_types = {
+ BTF_STRUCT_ENC(NAME_TBD, 1, 8), /* [1] */
+ BTF_MEMBER_ENC(NAME_TBD, 2, 0),
+ BTF_PTR_ENC(3), /* [2] */
+ BTF_TYPEDEF_ENC(NAME_TBD, 4), /* [3] */
+ BTF_TYPE_ARRAY_ENC(5, 5, 16), /* [4] */
+ BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [5] */
+ BTF_END_RAW,
+ },
+ BTF_STR_SEC("\0s\0a\0arr_t"),
+ .map_type = BPF_MAP_TYPE_ARRAY,
+ .map_name = "ptr_mod_chain_size_resolve_map",
+ .key_size = sizeof(int),
+ .value_size = sizeof(int) * 16,
+ .key_type_id = 5 /* int */,
+ .value_type_id = 3 /* arr_t */,
+ .max_entries = 4,
+},
+/*
+ * typedef int arr_t[16][8][4];
+ * struct s {
+ * arr_t *a;
+ * };
+ */
+{
+ .descr = "struct->ptr->typedef->multi-array->int size resolution",
+ .raw_types = {
+ BTF_STRUCT_ENC(NAME_TBD, 1, 8), /* [1] */
+ BTF_MEMBER_ENC(NAME_TBD, 2, 0),
+ BTF_PTR_ENC(3), /* [2] */
+ BTF_TYPEDEF_ENC(NAME_TBD, 4), /* [3] */
+ BTF_TYPE_ARRAY_ENC(5, 7, 16), /* [4] */
+ BTF_TYPE_ARRAY_ENC(6, 7, 8), /* [5] */
+ BTF_TYPE_ARRAY_ENC(7, 7, 4), /* [6] */
+ BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [7] */
+ BTF_END_RAW,
+ },
+ BTF_STR_SEC("\0s\0a\0arr_t"),
+ .map_type = BPF_MAP_TYPE_ARRAY,
+ .map_name = "multi_arr_size_resolve_map",
+ .key_size = sizeof(int),
+ .value_size = sizeof(int) * 16 * 8 * 4,
+ .key_type_id = 7 /* int */,
+ .value_type_id = 3 /* arr_t */,
+ .max_entries = 4,
+},
+/*
+ * typedef int int_t;
+ * typedef int_t arr3_t[4];
+ * typedef arr3_t arr2_t[8];
+ * typedef arr2_t arr1_t[16];
+ * struct s {
+ * arr1_t *a;
+ * };
+ */
+{
+ .descr = "typedef/multi-arr mix size resolution",
+ .raw_types = {
+ BTF_STRUCT_ENC(NAME_TBD, 1, 8), /* [1] */
+ BTF_MEMBER_ENC(NAME_TBD, 2, 0),
+ BTF_PTR_ENC(3), /* [2] */
+ BTF_TYPEDEF_ENC(NAME_TBD, 4), /* [3] */
+ BTF_TYPE_ARRAY_ENC(5, 10, 16), /* [4] */
+ BTF_TYPEDEF_ENC(NAME_TBD, 6), /* [5] */
+ BTF_TYPE_ARRAY_ENC(7, 10, 8), /* [6] */
+ BTF_TYPEDEF_ENC(NAME_TBD, 8), /* [7] */
+ BTF_TYPE_ARRAY_ENC(9, 10, 4), /* [8] */
+ BTF_TYPEDEF_ENC(NAME_TBD, 10), /* [9] */
+ BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [10] */
+ BTF_END_RAW,
+ },
+ BTF_STR_SEC("\0s\0a\0arr1_t\0arr2_t\0arr3_t\0int_t"),
+ .map_type = BPF_MAP_TYPE_ARRAY,
+ .map_name = "typedef_arra_mix_size_resolve_map",
+ .key_size = sizeof(int),
+ .value_size = sizeof(int) * 16 * 8 * 4,
+ .key_type_id = 10 /* int */,
+ .value_type_id = 3 /* arr_t */,
+ .max_entries = 4,
+},
}; /* struct btf_raw_test raw_tests[] */
@@ -4016,71 +4104,18 @@ struct btf_file_test {
};
static struct btf_file_test file_tests[] = {
-{
- .file = "test_btf_haskv.o",
-},
-{
- .file = "test_btf_nokv.o",
- .btf_kv_notfound = true,
-},
+ { .file = "test_btf_haskv.o", },
+ { .file = "test_btf_newkv.o", },
+ { .file = "test_btf_nokv.o", .btf_kv_notfound = true, },
};
-static int file_has_btf_elf(const char *fn, bool *has_btf_ext)
-{
- Elf_Scn *scn = NULL;
- GElf_Ehdr ehdr;
- int ret = 0;
- int elf_fd;
- Elf *elf;
-
- if (CHECK(elf_version(EV_CURRENT) == EV_NONE,
- "elf_version(EV_CURRENT) == EV_NONE"))
- return -1;
-
- elf_fd = open(fn, O_RDONLY);
- if (CHECK(elf_fd == -1, "open(%s): errno:%d", fn, errno))
- return -1;
-
- elf = elf_begin(elf_fd, ELF_C_READ, NULL);
- if (CHECK(!elf, "elf_begin(%s): %s", fn, elf_errmsg(elf_errno()))) {
- ret = -1;
- goto done;
- }
-
- if (CHECK(!gelf_getehdr(elf, &ehdr), "!gelf_getehdr(%s)", fn)) {
- ret = -1;
- goto done;
- }
-
- while ((scn = elf_nextscn(elf, scn))) {
- const char *sh_name;
- GElf_Shdr sh;
-
- if (CHECK(gelf_getshdr(scn, &sh) != &sh,
- "file:%s gelf_getshdr != &sh", fn)) {
- ret = -1;
- goto done;
- }
-
- sh_name = elf_strptr(elf, ehdr.e_shstrndx, sh.sh_name);
- if (!strcmp(sh_name, BTF_ELF_SEC))
- ret = 1;
- if (!strcmp(sh_name, BTF_EXT_ELF_SEC))
- *has_btf_ext = true;
- }
-
-done:
- close(elf_fd);
- elf_end(elf);
- return ret;
-}
-
static int do_test_file(unsigned int test_num)
{
const struct btf_file_test *test = &file_tests[test_num - 1];
const char *expected_fnames[] = {"_dummy_tracepoint",
"test_long_fname_1",
"test_long_fname_2"};
+ struct btf_ext *btf_ext = NULL;
struct bpf_prog_info info = {};
struct bpf_object *obj = NULL;
struct bpf_func_info *finfo;
@@ -4095,15 +4130,19 @@ static int do_test_file(unsigned int test_num)
fprintf(stderr, "BTF libbpf test[%u] (%s): ", test_num,
test->file);
- err = file_has_btf_elf(test->file, &has_btf_ext);
- if (err == -1)
- return err;
-
- if (err == 0) {
- fprintf(stderr, "SKIP. No ELF %s found", BTF_ELF_SEC);
- skip_cnt++;
- return 0;
+ btf = btf__parse_elf(test->file, &btf_ext);
+ if (IS_ERR(btf)) {
+ if (PTR_ERR(btf) == -ENOENT) {
+ fprintf(stderr, "SKIP. No ELF %s found", BTF_ELF_SEC);
+ skip_cnt++;
+ return 0;
+ }
+ return PTR_ERR(btf);
}
+ btf__free(btf);
+
+ has_btf_ext = btf_ext != NULL;
+ btf_ext__free(btf_ext);
obj = bpf_object__open(test->file);
if (CHECK(IS_ERR(obj), "obj: %ld", PTR_ERR(obj)))
diff --git a/tools/testing/selftests/bpf/test_btf_dump.c b/tools/testing/selftests/bpf/test_btf_dump.c
new file mode 100644
index 000000000000..6e75dd3cb14f
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_btf_dump.c
@@ -0,0 +1,150 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <linux/err.h>
+#include <btf.h>
+
+#define CHECK(condition, format...) ({ \
+ int __ret = !!(condition); \
+ if (__ret) { \
+ fprintf(stderr, "%s:%d:FAIL ", __func__, __LINE__); \
+ fprintf(stderr, format); \
+ } \
+ __ret; \
+})
+
+void btf_dump_printf(void *ctx, const char *fmt, va_list args)
+{
+ vfprintf(ctx, fmt, args);
+}
+
+struct btf_dump_test_case {
+ const char *name;
+ struct btf_dump_opts opts;
+} btf_dump_test_cases[] = {
+ {.name = "btf_dump_test_case_syntax", .opts = {}},
+ {.name = "btf_dump_test_case_ordering", .opts = {}},
+ {.name = "btf_dump_test_case_padding", .opts = {}},
+ {.name = "btf_dump_test_case_packing", .opts = {}},
+ {.name = "btf_dump_test_case_bitfields", .opts = {}},
+ {.name = "btf_dump_test_case_multidim", .opts = {}},
+ {.name = "btf_dump_test_case_namespacing", .opts = {}},
+};
+
+static int btf_dump_all_types(const struct btf *btf,
+ const struct btf_dump_opts *opts)
+{
+ size_t type_cnt = btf__get_nr_types(btf);
+ struct btf_dump *d;
+ int err = 0, id;
+
+ d = btf_dump__new(btf, NULL, opts, btf_dump_printf);
+ if (IS_ERR(d))
+ return PTR_ERR(d);
+
+ for (id = 1; id <= type_cnt; id++) {
+ err = btf_dump__dump_type(d, id);
+ if (err)
+ goto done;
+ }
+
+done:
+ btf_dump__free(d);
+ return err;
+}
+
+int test_btf_dump_case(int n, struct btf_dump_test_case *test_case)
+{
+ char test_file[256], out_file[256], diff_cmd[1024];
+ struct btf *btf = NULL;
+ int err = 0, fd = -1;
+ FILE *f = NULL;
+
+ fprintf(stderr, "Test case #%d (%s): ", n, test_case->name);
+
+ snprintf(test_file, sizeof(test_file), "%s.o", test_case->name);
+
+ btf = btf__parse_elf(test_file, NULL);
+ if (CHECK(IS_ERR(btf),
+ "failed to load test BTF: %ld\n", PTR_ERR(btf))) {
+ err = -PTR_ERR(btf);
+ btf = NULL;
+ goto done;
+ }
+
+ snprintf(out_file, sizeof(out_file),
+ "/tmp/%s.output.XXXXXX", test_case->name);
+ fd = mkstemp(out_file);
+ if (CHECK(fd < 0, "failed to create temp output file: %d\n", fd)) {
+ err = fd;
+ goto done;
+ }
+ f = fdopen(fd, "w");
+ if (CHECK(f == NULL, "failed to open temp output file: %s(%d)\n",
+ strerror(errno), errno)) {
+ close(fd);
+ goto done;
+ }
+
+ test_case->opts.ctx = f;
+ err = btf_dump_all_types(btf, &test_case->opts);
+ fclose(f);
+ close(fd);
+ if (CHECK(err, "failure during C dumping: %d\n", err)) {
+ goto done;
+ }
+
+ snprintf(test_file, sizeof(test_file), "progs/%s.c", test_case->name);
+ if (access(test_file, R_OK) == -1)
+ /*
+ * When the test is run with O=, kselftest copies TEST_FILES
+ * without preserving the directory structure.
+ */
+ snprintf(test_file, sizeof(test_file), "%s.c",
+ test_case->name);
+ /*
+ * Diff test output and expected test output, contained between
+ * START-EXPECTED-OUTPUT and END-EXPECTED-OUTPUT lines in test case.
+ * For expected output lines, everything before '*' is stripped out.
+ * Also lines containing comment start and comment end markers are
+ * ignored.
+ */
+ snprintf(diff_cmd, sizeof(diff_cmd),
+ "awk '/START-EXPECTED-OUTPUT/{out=1;next} "
+ "/END-EXPECTED-OUTPUT/{out=0} "
+ "/\\/\\*|\\*\\//{next} " /* ignore comment start/end lines */
+ "out {sub(/^[ \\t]*\\*/, \"\"); print}' '%s' | diff -u - '%s'",
+ test_file, out_file);
+ err = system(diff_cmd);
+ if (CHECK(err,
+ "differing test output, output=%s, err=%d, diff cmd:\n%s\n",
+ out_file, err, diff_cmd))
+ goto done;
+
+ remove(out_file);
+ fprintf(stderr, "OK\n");
+
+done:
+ btf__free(btf);
+ return err;
+}
+
+int main() {
+ int test_case_cnt, i, err, failed = 0;
+
+ test_case_cnt = sizeof(btf_dump_test_cases) /
+ sizeof(btf_dump_test_cases[0]);
+
+ for (i = 0; i < test_case_cnt; i++) {
+ err = test_btf_dump_case(i, &btf_dump_test_cases[i]);
+ if (err)
+ failed++;
+ }
+
+ fprintf(stderr, "%d tests succeeded, %d tests failed.\n",
+ test_case_cnt - failed, failed);
+
+ return failed;
+}
diff --git a/tools/testing/selftests/bpf/test_cgroup_attach.c b/tools/testing/selftests/bpf/test_cgroup_attach.c
new file mode 100644
index 000000000000..7671909ee1cb
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_cgroup_attach.c
@@ -0,0 +1,571 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* eBPF example program:
+ *
+ * - Creates arraymap in kernel with 4 bytes keys and 8 byte values
+ *
+ * - Loads eBPF program
+ *
+ * The eBPF program accesses the map passed in to store two pieces of
+ * information. The number of invocations of the program, which maps
+ * to the number of packets received, is stored to key 0. Key 1 is
+ * incremented on each iteration by the number of bytes stored in
+ * the skb. The program also stores the number of received bytes
+ * in the cgroup storage.
+ *
+ * - Attaches the new program to a cgroup using BPF_PROG_ATTACH
+ *
+ * - Every second, reads map[0] and map[1] to see how many bytes and
+ * packets were seen on any socket of tasks in the given cgroup.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <linux/filter.h>
+
+#include <linux/bpf.h>
+#include <bpf/bpf.h>
+
+#include "bpf_util.h"
+#include "bpf_rlimit.h"
+#include "cgroup_helpers.h"
+
+#define FOO "/foo"
+#define BAR "/foo/bar/"
+#define PING_CMD "ping -q -c1 -w1 127.0.0.1 > /dev/null"
+
+char bpf_log_buf[BPF_LOG_BUF_SIZE];
+
+#ifdef DEBUG
+#define debug(args...) printf(args)
+#else
+#define debug(args...)
+#endif
+
+static int prog_load(int verdict)
+{
+ int ret;
+ struct bpf_insn prog[] = {
+ BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */
+ BPF_EXIT_INSN(),
+ };
+ size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
+
+ ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
+ prog, insns_cnt, "GPL", 0,
+ bpf_log_buf, BPF_LOG_BUF_SIZE);
+
+ if (ret < 0) {
+ log_err("Loading program");
+ printf("Output from verifier:\n%s\n-------\n", bpf_log_buf);
+ return 0;
+ }
+ return ret;
+}
+
+static int test_foo_bar(void)
+{
+ int drop_prog, allow_prog, foo = 0, bar = 0, rc = 0;
+
+ allow_prog = prog_load(1);
+ if (!allow_prog)
+ goto err;
+
+ drop_prog = prog_load(0);
+ if (!drop_prog)
+ goto err;
+
+ if (setup_cgroup_environment())
+ goto err;
+
+ /* Create cgroup /foo, get fd, and join it */
+ foo = create_and_get_cgroup(FOO);
+ if (foo < 0)
+ goto err;
+
+ if (join_cgroup(FOO))
+ goto err;
+
+ if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS,
+ BPF_F_ALLOW_OVERRIDE)) {
+ log_err("Attaching prog to /foo");
+ goto err;
+ }
+
+ debug("Attached DROP prog. This ping in cgroup /foo should fail...\n");
+ assert(system(PING_CMD) != 0);
+
+ /* Create cgroup /foo/bar, get fd, and join it */
+ bar = create_and_get_cgroup(BAR);
+ if (bar < 0)
+ goto err;
+
+ if (join_cgroup(BAR))
+ goto err;
+
+ debug("Attached DROP prog. This ping in cgroup /foo/bar should fail...\n");
+ assert(system(PING_CMD) != 0);
+
+ if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
+ BPF_F_ALLOW_OVERRIDE)) {
+ log_err("Attaching prog to /foo/bar");
+ goto err;
+ }
+
+ debug("Attached PASS prog. This ping in cgroup /foo/bar should pass...\n");
+ assert(system(PING_CMD) == 0);
+
+ if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) {
+ log_err("Detaching program from /foo/bar");
+ goto err;
+ }
+
+ debug("Detached PASS from /foo/bar while DROP is attached to /foo.\n"
+ "This ping in cgroup /foo/bar should fail...\n");
+ assert(system(PING_CMD) != 0);
+
+ if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
+ BPF_F_ALLOW_OVERRIDE)) {
+ log_err("Attaching prog to /foo/bar");
+ goto err;
+ }
+
+ if (bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) {
+ log_err("Detaching program from /foo");
+ goto err;
+ }
+
+ debug("Attached PASS from /foo/bar and detached DROP from /foo.\n"
+ "This ping in cgroup /foo/bar should pass...\n");
+ assert(system(PING_CMD) == 0);
+
+ if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
+ BPF_F_ALLOW_OVERRIDE)) {
+ log_err("Attaching prog to /foo/bar");
+ goto err;
+ }
+
+ if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) {
+ errno = 0;
+ log_err("Unexpected success attaching prog to /foo/bar");
+ goto err;
+ }
+
+ if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) {
+ log_err("Detaching program from /foo/bar");
+ goto err;
+ }
+
+ if (!bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) {
+ errno = 0;
+ log_err("Unexpected success in double detach from /foo");
+ goto err;
+ }
+
+ if (bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) {
+ log_err("Attaching non-overridable prog to /foo");
+ goto err;
+ }
+
+ if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) {
+ errno = 0;
+ log_err("Unexpected success attaching non-overridable prog to /foo/bar");
+ goto err;
+ }
+
+ if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
+ BPF_F_ALLOW_OVERRIDE)) {
+ errno = 0;
+ log_err("Unexpected success attaching overridable prog to /foo/bar");
+ goto err;
+ }
+
+ if (!bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS,
+ BPF_F_ALLOW_OVERRIDE)) {
+ errno = 0;
+ log_err("Unexpected success attaching overridable prog to /foo");
+ goto err;
+ }
+
+ if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) {
+ log_err("Attaching different non-overridable prog to /foo");
+ goto err;
+ }
+
+ goto out;
+
+err:
+ rc = 1;
+
+out:
+ close(foo);
+ close(bar);
+ cleanup_cgroup_environment();
+ if (!rc)
+ printf("#override:PASS\n");
+ else
+ printf("#override:FAIL\n");
+ return rc;
+}
+
+static int map_fd = -1;
+
+static int prog_load_cnt(int verdict, int val)
+{
+ int cgroup_storage_fd, percpu_cgroup_storage_fd;
+
+ if (map_fd < 0)
+ map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, 8, 1, 0);
+ if (map_fd < 0) {
+ printf("failed to create map '%s'\n", strerror(errno));
+ return -1;
+ }
+
+ cgroup_storage_fd = bpf_create_map(BPF_MAP_TYPE_CGROUP_STORAGE,
+ sizeof(struct bpf_cgroup_storage_key), 8, 0, 0);
+ if (cgroup_storage_fd < 0) {
+ printf("failed to create map '%s'\n", strerror(errno));
+ return -1;
+ }
+
+ percpu_cgroup_storage_fd = bpf_create_map(
+ BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
+ sizeof(struct bpf_cgroup_storage_key), 8, 0, 0);
+ if (percpu_cgroup_storage_fd < 0) {
+ printf("failed to create map '%s'\n", strerror(errno));
+ return -1;
+ }
+
+ struct bpf_insn prog[] = {
+ BPF_MOV32_IMM(BPF_REG_0, 0),
+ BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */
+ BPF_LD_MAP_FD(BPF_REG_1, map_fd),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
+ BPF_MOV64_IMM(BPF_REG_1, val), /* r1 = 1 */
+ BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */
+
+ BPF_LD_MAP_FD(BPF_REG_1, cgroup_storage_fd),
+ BPF_MOV64_IMM(BPF_REG_2, 0),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_local_storage),
+ BPF_MOV64_IMM(BPF_REG_1, val),
+ BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_W, BPF_REG_0, BPF_REG_1, 0, 0),
+
+ BPF_LD_MAP_FD(BPF_REG_1, percpu_cgroup_storage_fd),
+ BPF_MOV64_IMM(BPF_REG_2, 0),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_local_storage),
+ BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 0x1),
+ BPF_STX_MEM(BPF_W, BPF_REG_0, BPF_REG_3, 0),
+
+ BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */
+ BPF_EXIT_INSN(),
+ };
+ size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
+ int ret;
+
+ ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
+ prog, insns_cnt, "GPL", 0,
+ bpf_log_buf, BPF_LOG_BUF_SIZE);
+
+ if (ret < 0) {
+ log_err("Loading program");
+ printf("Output from verifier:\n%s\n-------\n", bpf_log_buf);
+ return 0;
+ }
+ close(cgroup_storage_fd);
+ return ret;
+}
+
+
+static int test_multiprog(void)
+{
+ __u32 prog_ids[4], prog_cnt = 0, attach_flags, saved_prog_id;
+ int cg1 = 0, cg2 = 0, cg3 = 0, cg4 = 0, cg5 = 0, key = 0;
+ int drop_prog, allow_prog[6] = {}, rc = 0;
+ unsigned long long value;
+ int i = 0;
+
+ for (i = 0; i < 6; i++) {
+ allow_prog[i] = prog_load_cnt(1, 1 << i);
+ if (!allow_prog[i])
+ goto err;
+ }
+ drop_prog = prog_load_cnt(0, 1);
+ if (!drop_prog)
+ goto err;
+
+ if (setup_cgroup_environment())
+ goto err;
+
+ cg1 = create_and_get_cgroup("/cg1");
+ if (cg1 < 0)
+ goto err;
+ cg2 = create_and_get_cgroup("/cg1/cg2");
+ if (cg2 < 0)
+ goto err;
+ cg3 = create_and_get_cgroup("/cg1/cg2/cg3");
+ if (cg3 < 0)
+ goto err;
+ cg4 = create_and_get_cgroup("/cg1/cg2/cg3/cg4");
+ if (cg4 < 0)
+ goto err;
+ cg5 = create_and_get_cgroup("/cg1/cg2/cg3/cg4/cg5");
+ if (cg5 < 0)
+ goto err;
+
+ if (join_cgroup("/cg1/cg2/cg3/cg4/cg5"))
+ goto err;
+
+ if (bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS,
+ BPF_F_ALLOW_MULTI)) {
+ log_err("Attaching prog to cg1");
+ goto err;
+ }
+ if (!bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS,
+ BPF_F_ALLOW_MULTI)) {
+ log_err("Unexpected success attaching the same prog to cg1");
+ goto err;
+ }
+ if (bpf_prog_attach(allow_prog[1], cg1, BPF_CGROUP_INET_EGRESS,
+ BPF_F_ALLOW_MULTI)) {
+ log_err("Attaching prog2 to cg1");
+ goto err;
+ }
+ if (bpf_prog_attach(allow_prog[2], cg2, BPF_CGROUP_INET_EGRESS,
+ BPF_F_ALLOW_OVERRIDE)) {
+ log_err("Attaching prog to cg2");
+ goto err;
+ }
+ if (bpf_prog_attach(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS,
+ BPF_F_ALLOW_MULTI)) {
+ log_err("Attaching prog to cg3");
+ goto err;
+ }
+ if (bpf_prog_attach(allow_prog[4], cg4, BPF_CGROUP_INET_EGRESS,
+ BPF_F_ALLOW_OVERRIDE)) {
+ log_err("Attaching prog to cg4");
+ goto err;
+ }
+ if (bpf_prog_attach(allow_prog[5], cg5, BPF_CGROUP_INET_EGRESS, 0)) {
+ log_err("Attaching prog to cg5");
+ goto err;
+ }
+ assert(system(PING_CMD) == 0);
+ assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
+ assert(value == 1 + 2 + 8 + 32);
+
+ /* query the number of effective progs in cg5 */
+ assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
+ NULL, NULL, &prog_cnt) == 0);
+ assert(prog_cnt == 4);
+ /* retrieve prog_ids of effective progs in cg5 */
+ assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
+ &attach_flags, prog_ids, &prog_cnt) == 0);
+ assert(prog_cnt == 4);
+ assert(attach_flags == 0);
+ saved_prog_id = prog_ids[0];
+ /* check enospc handling */
+ prog_ids[0] = 0;
+ prog_cnt = 2;
+ assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
+ &attach_flags, prog_ids, &prog_cnt) == -1 &&
+ errno == ENOSPC);
+ assert(prog_cnt == 4);
+ /* check that prog_ids are returned even when buffer is too small */
+ assert(prog_ids[0] == saved_prog_id);
+ /* retrieve prog_id of single attached prog in cg5 */
+ prog_ids[0] = 0;
+ assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0,
+ NULL, prog_ids, &prog_cnt) == 0);
+ assert(prog_cnt == 1);
+ assert(prog_ids[0] == saved_prog_id);
+
+ /* detach bottom program and ping again */
+ if (bpf_prog_detach2(-1, cg5, BPF_CGROUP_INET_EGRESS)) {
+ log_err("Detaching prog from cg5");
+ goto err;
+ }
+ value = 0;
+ assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0);
+ assert(system(PING_CMD) == 0);
+ assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
+ assert(value == 1 + 2 + 8 + 16);
+
+ /* detach 3rd from bottom program and ping again */
+ errno = 0;
+ if (!bpf_prog_detach2(0, cg3, BPF_CGROUP_INET_EGRESS)) {
+ log_err("Unexpected success on detach from cg3");
+ goto err;
+ }
+ if (bpf_prog_detach2(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS)) {
+ log_err("Detaching from cg3");
+ goto err;
+ }
+ value = 0;
+ assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0);
+ assert(system(PING_CMD) == 0);
+ assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
+ assert(value == 1 + 2 + 16);
+
+ /* detach 2nd from bottom program and ping again */
+ if (bpf_prog_detach2(-1, cg4, BPF_CGROUP_INET_EGRESS)) {
+ log_err("Detaching prog from cg4");
+ goto err;
+ }
+ value = 0;
+ assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0);
+ assert(system(PING_CMD) == 0);
+ assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
+ assert(value == 1 + 2 + 4);
+
+ prog_cnt = 4;
+ assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
+ &attach_flags, prog_ids, &prog_cnt) == 0);
+ assert(prog_cnt == 3);
+ assert(attach_flags == 0);
+ assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0,
+ NULL, prog_ids, &prog_cnt) == 0);
+ assert(prog_cnt == 0);
+ goto out;
+err:
+ rc = 1;
+
+out:
+ for (i = 0; i < 6; i++)
+ if (allow_prog[i] > 0)
+ close(allow_prog[i]);
+ close(cg1);
+ close(cg2);
+ close(cg3);
+ close(cg4);
+ close(cg5);
+ cleanup_cgroup_environment();
+ if (!rc)
+ printf("#multi:PASS\n");
+ else
+ printf("#multi:FAIL\n");
+ return rc;
+}
+
+static int test_autodetach(void)
+{
+ __u32 prog_cnt = 4, attach_flags;
+ int allow_prog[2] = {0};
+ __u32 prog_ids[2] = {0};
+ int cg = 0, i, rc = -1;
+ void *ptr = NULL;
+ int attempts;
+
+ for (i = 0; i < ARRAY_SIZE(allow_prog); i++) {
+ allow_prog[i] = prog_load_cnt(1, 1 << i);
+ if (!allow_prog[i])
+ goto err;
+ }
+
+ if (setup_cgroup_environment())
+ goto err;
+
+ /* create a cgroup, attach two programs and remember their ids */
+ cg = create_and_get_cgroup("/cg_autodetach");
+ if (cg < 0)
+ goto err;
+
+ if (join_cgroup("/cg_autodetach"))
+ goto err;
+
+ for (i = 0; i < ARRAY_SIZE(allow_prog); i++) {
+ if (bpf_prog_attach(allow_prog[i], cg, BPF_CGROUP_INET_EGRESS,
+ BPF_F_ALLOW_MULTI)) {
+ log_err("Attaching prog[%d] to cg:egress", i);
+ goto err;
+ }
+ }
+
+ /* make sure that programs are attached and run some traffic */
+ assert(bpf_prog_query(cg, BPF_CGROUP_INET_EGRESS, 0, &attach_flags,
+ prog_ids, &prog_cnt) == 0);
+ assert(system(PING_CMD) == 0);
+
+ /* allocate some memory (4Mb) to pin the original cgroup */
+ ptr = malloc(4 * (1 << 20));
+ if (!ptr)
+ goto err;
+
+ /* close programs and cgroup fd */
+ for (i = 0; i < ARRAY_SIZE(allow_prog); i++) {
+ close(allow_prog[i]);
+ allow_prog[i] = 0;
+ }
+
+ close(cg);
+ cg = 0;
+
+ /* leave the cgroup and remove it. don't detach programs */
+ cleanup_cgroup_environment();
+
+ /* wait for the asynchronous auto-detachment.
+ * wait for no more than 5 sec and give up.
+ */
+ for (i = 0; i < ARRAY_SIZE(prog_ids); i++) {
+ for (attempts = 5; attempts >= 0; attempts--) {
+ int fd = bpf_prog_get_fd_by_id(prog_ids[i]);
+
+ if (fd < 0)
+ break;
+
+ /* don't leave the fd open */
+ close(fd);
+
+ if (!attempts)
+ goto err;
+
+ sleep(1);
+ }
+ }
+
+ rc = 0;
+err:
+ for (i = 0; i < ARRAY_SIZE(allow_prog); i++)
+ if (allow_prog[i] > 0)
+ close(allow_prog[i]);
+ if (cg)
+ close(cg);
+ free(ptr);
+ cleanup_cgroup_environment();
+ if (!rc)
+ printf("#autodetach:PASS\n");
+ else
+ printf("#autodetach:FAIL\n");
+ return rc;
+}
+
+int main(void)
+{
+ int (*tests[])(void) = {
+ test_foo_bar,
+ test_multiprog,
+ test_autodetach,
+ };
+ int errors = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tests); i++)
+ if (tests[i]())
+ errors++;
+
+ if (errors)
+ printf("test_cgroup_attach:FAIL\n");
+ else
+ printf("test_cgroup_attach:PASS\n");
+
+ return errors ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/tools/testing/selftests/bpf/test_cgroup_storage.c b/tools/testing/selftests/bpf/test_cgroup_storage.c
index 2fc4625c1a15..655729004391 100644
--- a/tools/testing/selftests/bpf/test_cgroup_storage.c
+++ b/tools/testing/selftests/bpf/test_cgroup_storage.c
@@ -20,9 +20,9 @@ int main(int argc, char **argv)
BPF_MOV64_IMM(BPF_REG_2, 0), /* flags, not used */
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
BPF_FUNC_get_local_storage),
- BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0),
+ BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_0, 0),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 0x1),
- BPF_STX_MEM(BPF_W, BPF_REG_0, BPF_REG_3, 0),
+ BPF_STX_MEM(BPF_DW, BPF_REG_0, BPF_REG_3, 0),
BPF_LD_MAP_FD(BPF_REG_1, 0), /* map fd */
BPF_MOV64_IMM(BPF_REG_2, 0), /* flags, not used */
@@ -30,7 +30,7 @@ int main(int argc, char **argv)
BPF_FUNC_get_local_storage),
BPF_MOV64_IMM(BPF_REG_1, 1),
BPF_STX_XADD(BPF_DW, BPF_REG_0, BPF_REG_1, 0),
- BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0),
+ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 0),
BPF_ALU64_IMM(BPF_AND, BPF_REG_1, 0x1),
BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
BPF_EXIT_INSN(),
diff --git a/tools/testing/selftests/bpf/test_dev_cgroup.c b/tools/testing/selftests/bpf/test_dev_cgroup.c
index 76e4993b7c16..d850fb9076b5 100644
--- a/tools/testing/selftests/bpf/test_dev_cgroup.c
+++ b/tools/testing/selftests/bpf/test_dev_cgroup.c
@@ -1,8 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2017 Facebook
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
*/
#include <stdio.h>
diff --git a/tools/testing/selftests/bpf/test_hashmap.c b/tools/testing/selftests/bpf/test_hashmap.c
new file mode 100644
index 000000000000..b64094c981e3
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_hashmap.c
@@ -0,0 +1,382 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+/*
+ * Tests for libbpf's hashmap.
+ *
+ * Copyright (c) 2019 Facebook
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <linux/err.h>
+#include "hashmap.h"
+
+#define CHECK(condition, format...) ({ \
+ int __ret = !!(condition); \
+ if (__ret) { \
+ fprintf(stderr, "%s:%d:FAIL ", __func__, __LINE__); \
+ fprintf(stderr, format); \
+ } \
+ __ret; \
+})
+
+size_t hash_fn(const void *k, void *ctx)
+{
+ return (long)k;
+}
+
+bool equal_fn(const void *a, const void *b, void *ctx)
+{
+ return (long)a == (long)b;
+}
+
+static inline size_t next_pow_2(size_t n)
+{
+ size_t r = 1;
+
+ while (r < n)
+ r <<= 1;
+ return r;
+}
+
+static inline size_t exp_cap(size_t sz)
+{
+ size_t r = next_pow_2(sz);
+
+ if (sz * 4 / 3 > r)
+ r <<= 1;
+ return r;
+}
+
+#define ELEM_CNT 62
+
+int test_hashmap_generic(void)
+{
+ struct hashmap_entry *entry, *tmp;
+ int err, bkt, found_cnt, i;
+ long long found_msk;
+ struct hashmap *map;
+
+ fprintf(stderr, "%s: ", __func__);
+
+ map = hashmap__new(hash_fn, equal_fn, NULL);
+ if (CHECK(IS_ERR(map), "failed to create map: %ld\n", PTR_ERR(map)))
+ return 1;
+
+ for (i = 0; i < ELEM_CNT; i++) {
+ const void *oldk, *k = (const void *)(long)i;
+ void *oldv, *v = (void *)(long)(1024 + i);
+
+ err = hashmap__update(map, k, v, &oldk, &oldv);
+ if (CHECK(err != -ENOENT, "unexpected result: %d\n", err))
+ return 1;
+
+ if (i % 2) {
+ err = hashmap__add(map, k, v);
+ } else {
+ err = hashmap__set(map, k, v, &oldk, &oldv);
+ if (CHECK(oldk != NULL || oldv != NULL,
+ "unexpected k/v: %p=%p\n", oldk, oldv))
+ return 1;
+ }
+
+ if (CHECK(err, "failed to add k/v %ld = %ld: %d\n",
+ (long)k, (long)v, err))
+ return 1;
+
+ if (CHECK(!hashmap__find(map, k, &oldv),
+ "failed to find key %ld\n", (long)k))
+ return 1;
+ if (CHECK(oldv != v, "found value is wrong: %ld\n", (long)oldv))
+ return 1;
+ }
+
+ if (CHECK(hashmap__size(map) != ELEM_CNT,
+ "invalid map size: %zu\n", hashmap__size(map)))
+ return 1;
+ if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
+ "unexpected map capacity: %zu\n", hashmap__capacity(map)))
+ return 1;
+
+ found_msk = 0;
+ hashmap__for_each_entry(map, entry, bkt) {
+ long k = (long)entry->key;
+ long v = (long)entry->value;
+
+ found_msk |= 1ULL << k;
+ if (CHECK(v - k != 1024, "invalid k/v pair: %ld = %ld\n", k, v))
+ return 1;
+ }
+ if (CHECK(found_msk != (1ULL << ELEM_CNT) - 1,
+ "not all keys iterated: %llx\n", found_msk))
+ return 1;
+
+ for (i = 0; i < ELEM_CNT; i++) {
+ const void *oldk, *k = (const void *)(long)i;
+ void *oldv, *v = (void *)(long)(256 + i);
+
+ err = hashmap__add(map, k, v);
+ if (CHECK(err != -EEXIST, "unexpected add result: %d\n", err))
+ return 1;
+
+ if (i % 2)
+ err = hashmap__update(map, k, v, &oldk, &oldv);
+ else
+ err = hashmap__set(map, k, v, &oldk, &oldv);
+
+ if (CHECK(err, "failed to update k/v %ld = %ld: %d\n",
+ (long)k, (long)v, err))
+ return 1;
+ if (CHECK(!hashmap__find(map, k, &oldv),
+ "failed to find key %ld\n", (long)k))
+ return 1;
+ if (CHECK(oldv != v, "found value is wrong: %ld\n", (long)oldv))
+ return 1;
+ }
+
+ if (CHECK(hashmap__size(map) != ELEM_CNT,
+ "invalid updated map size: %zu\n", hashmap__size(map)))
+ return 1;
+ if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
+ "unexpected map capacity: %zu\n", hashmap__capacity(map)))
+ return 1;
+
+ found_msk = 0;
+ hashmap__for_each_entry_safe(map, entry, tmp, bkt) {
+ long k = (long)entry->key;
+ long v = (long)entry->value;
+
+ found_msk |= 1ULL << k;
+ if (CHECK(v - k != 256,
+ "invalid updated k/v pair: %ld = %ld\n", k, v))
+ return 1;
+ }
+ if (CHECK(found_msk != (1ULL << ELEM_CNT) - 1,
+ "not all keys iterated after update: %llx\n", found_msk))
+ return 1;
+
+ found_cnt = 0;
+ hashmap__for_each_key_entry(map, entry, (void *)0) {
+ found_cnt++;
+ }
+ if (CHECK(!found_cnt, "didn't find any entries for key 0\n"))
+ return 1;
+
+ found_msk = 0;
+ found_cnt = 0;
+ hashmap__for_each_key_entry_safe(map, entry, tmp, (void *)0) {
+ const void *oldk, *k;
+ void *oldv, *v;
+
+ k = entry->key;
+ v = entry->value;
+
+ found_cnt++;
+ found_msk |= 1ULL << (long)k;
+
+ if (CHECK(!hashmap__delete(map, k, &oldk, &oldv),
+ "failed to delete k/v %ld = %ld\n",
+ (long)k, (long)v))
+ return 1;
+ if (CHECK(oldk != k || oldv != v,
+ "invalid deleted k/v: expected %ld = %ld, got %ld = %ld\n",
+ (long)k, (long)v, (long)oldk, (long)oldv))
+ return 1;
+ if (CHECK(hashmap__delete(map, k, &oldk, &oldv),
+ "unexpectedly deleted k/v %ld = %ld\n",
+ (long)oldk, (long)oldv))
+ return 1;
+ }
+
+ if (CHECK(!found_cnt || !found_msk,
+ "didn't delete any key entries\n"))
+ return 1;
+ if (CHECK(hashmap__size(map) != ELEM_CNT - found_cnt,
+ "invalid updated map size (already deleted: %d): %zu\n",
+ found_cnt, hashmap__size(map)))
+ return 1;
+ if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
+ "unexpected map capacity: %zu\n", hashmap__capacity(map)))
+ return 1;
+
+ hashmap__for_each_entry_safe(map, entry, tmp, bkt) {
+ const void *oldk, *k;
+ void *oldv, *v;
+
+ k = entry->key;
+ v = entry->value;
+
+ found_cnt++;
+ found_msk |= 1ULL << (long)k;
+
+ if (CHECK(!hashmap__delete(map, k, &oldk, &oldv),
+ "failed to delete k/v %ld = %ld\n",
+ (long)k, (long)v))
+ return 1;
+ if (CHECK(oldk != k || oldv != v,
+ "invalid old k/v: expect %ld = %ld, got %ld = %ld\n",
+ (long)k, (long)v, (long)oldk, (long)oldv))
+ return 1;
+ if (CHECK(hashmap__delete(map, k, &oldk, &oldv),
+ "unexpectedly deleted k/v %ld = %ld\n",
+ (long)k, (long)v))
+ return 1;
+ }
+
+ if (CHECK(found_cnt != ELEM_CNT || found_msk != (1ULL << ELEM_CNT) - 1,
+ "not all keys were deleted: found_cnt:%d, found_msk:%llx\n",
+ found_cnt, found_msk))
+ return 1;
+ if (CHECK(hashmap__size(map) != 0,
+ "invalid updated map size (already deleted: %d): %zu\n",
+ found_cnt, hashmap__size(map)))
+ return 1;
+
+ found_cnt = 0;
+ hashmap__for_each_entry(map, entry, bkt) {
+ CHECK(false, "unexpected map entries left: %ld = %ld\n",
+ (long)entry->key, (long)entry->value);
+ return 1;
+ }
+
+ hashmap__free(map);
+ hashmap__for_each_entry(map, entry, bkt) {
+ CHECK(false, "unexpected map entries left: %ld = %ld\n",
+ (long)entry->key, (long)entry->value);
+ return 1;
+ }
+
+ fprintf(stderr, "OK\n");
+ return 0;
+}
+
+size_t collision_hash_fn(const void *k, void *ctx)
+{
+ return 0;
+}
+
+int test_hashmap_multimap(void)
+{
+ void *k1 = (void *)0, *k2 = (void *)1;
+ struct hashmap_entry *entry;
+ struct hashmap *map;
+ long found_msk;
+ int err, bkt;
+
+ fprintf(stderr, "%s: ", __func__);
+
+ /* force collisions */
+ map = hashmap__new(collision_hash_fn, equal_fn, NULL);
+ if (CHECK(IS_ERR(map), "failed to create map: %ld\n", PTR_ERR(map)))
+ return 1;
+
+
+ /* set up multimap:
+ * [0] -> 1, 2, 4;
+ * [1] -> 8, 16, 32;
+ */
+ err = hashmap__append(map, k1, (void *)1);
+ if (CHECK(err, "failed to add k/v: %d\n", err))
+ return 1;
+ err = hashmap__append(map, k1, (void *)2);
+ if (CHECK(err, "failed to add k/v: %d\n", err))
+ return 1;
+ err = hashmap__append(map, k1, (void *)4);
+ if (CHECK(err, "failed to add k/v: %d\n", err))
+ return 1;
+
+ err = hashmap__append(map, k2, (void *)8);
+ if (CHECK(err, "failed to add k/v: %d\n", err))
+ return 1;
+ err = hashmap__append(map, k2, (void *)16);
+ if (CHECK(err, "failed to add k/v: %d\n", err))
+ return 1;
+ err = hashmap__append(map, k2, (void *)32);
+ if (CHECK(err, "failed to add k/v: %d\n", err))
+ return 1;
+
+ if (CHECK(hashmap__size(map) != 6,
+ "invalid map size: %zu\n", hashmap__size(map)))
+ return 1;
+
+ /* verify global iteration still works and sees all values */
+ found_msk = 0;
+ hashmap__for_each_entry(map, entry, bkt) {
+ found_msk |= (long)entry->value;
+ }
+ if (CHECK(found_msk != (1 << 6) - 1,
+ "not all keys iterated: %lx\n", found_msk))
+ return 1;
+
+ /* iterate values for key 1 */
+ found_msk = 0;
+ hashmap__for_each_key_entry(map, entry, k1) {
+ found_msk |= (long)entry->value;
+ }
+ if (CHECK(found_msk != (1 | 2 | 4),
+ "invalid k1 values: %lx\n", found_msk))
+ return 1;
+
+ /* iterate values for key 2 */
+ found_msk = 0;
+ hashmap__for_each_key_entry(map, entry, k2) {
+ found_msk |= (long)entry->value;
+ }
+ if (CHECK(found_msk != (8 | 16 | 32),
+ "invalid k2 values: %lx\n", found_msk))
+ return 1;
+
+ fprintf(stderr, "OK\n");
+ return 0;
+}
+
+int test_hashmap_empty()
+{
+ struct hashmap_entry *entry;
+ int bkt;
+ struct hashmap *map;
+ void *k = (void *)0;
+
+ fprintf(stderr, "%s: ", __func__);
+
+ /* force collisions */
+ map = hashmap__new(hash_fn, equal_fn, NULL);
+ if (CHECK(IS_ERR(map), "failed to create map: %ld\n", PTR_ERR(map)))
+ return 1;
+
+ if (CHECK(hashmap__size(map) != 0,
+ "invalid map size: %zu\n", hashmap__size(map)))
+ return 1;
+ if (CHECK(hashmap__capacity(map) != 0,
+ "invalid map capacity: %zu\n", hashmap__capacity(map)))
+ return 1;
+ if (CHECK(hashmap__find(map, k, NULL), "unexpected find\n"))
+ return 1;
+ if (CHECK(hashmap__delete(map, k, NULL, NULL), "unexpected delete\n"))
+ return 1;
+
+ hashmap__for_each_entry(map, entry, bkt) {
+ CHECK(false, "unexpected iterated entry\n");
+ return 1;
+ }
+ hashmap__for_each_key_entry(map, entry, k) {
+ CHECK(false, "unexpected key entry\n");
+ return 1;
+ }
+
+ fprintf(stderr, "OK\n");
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ bool failed = false;
+
+ if (test_hashmap_generic())
+ failed = true;
+ if (test_hashmap_multimap())
+ failed = true;
+ if (test_hashmap_empty())
+ failed = true;
+
+ return failed;
+}
diff --git a/tools/testing/selftests/bpf/test_iptunnel_common.h b/tools/testing/selftests/bpf/test_iptunnel_common.h
index e4cd252a1b20..1d5ba839ddea 100644
--- a/tools/testing/selftests/bpf/test_iptunnel_common.h
+++ b/tools/testing/selftests/bpf/test_iptunnel_common.h
@@ -1,8 +1,5 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2016 Facebook
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
*/
#ifndef _TEST_IPTNL_COMMON_H
#define _TEST_IPTNL_COMMON_H
diff --git a/tools/testing/selftests/bpf/test_lpm_map.c b/tools/testing/selftests/bpf/test_lpm_map.c
index 02d7c871862a..006be3963977 100644
--- a/tools/testing/selftests/bpf/test_lpm_map.c
+++ b/tools/testing/selftests/bpf/test_lpm_map.c
@@ -573,13 +573,13 @@ static void test_lpm_get_next_key(void)
/* add one more element (total two) */
key_p->prefixlen = 24;
- inet_pton(AF_INET, "192.168.0.0", key_p->data);
+ inet_pton(AF_INET, "192.168.128.0", key_p->data);
assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0);
memset(key_p, 0, key_size);
assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0);
assert(key_p->prefixlen == 24 && key_p->data[0] == 192 &&
- key_p->data[1] == 168 && key_p->data[2] == 0);
+ key_p->data[1] == 168 && key_p->data[2] == 128);
memset(next_key_p, 0, key_size);
assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
@@ -592,7 +592,7 @@ static void test_lpm_get_next_key(void)
/* Add one more element (total three) */
key_p->prefixlen = 24;
- inet_pton(AF_INET, "192.168.128.0", key_p->data);
+ inet_pton(AF_INET, "192.168.0.0", key_p->data);
assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0);
memset(key_p, 0, key_size);
@@ -643,6 +643,41 @@ static void test_lpm_get_next_key(void)
assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 &&
errno == ENOENT);
+ /* Add one more element (total five) */
+ key_p->prefixlen = 28;
+ inet_pton(AF_INET, "192.168.1.128", key_p->data);
+ assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0);
+
+ memset(key_p, 0, key_size);
+ assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0);
+ assert(key_p->prefixlen == 24 && key_p->data[0] == 192 &&
+ key_p->data[1] == 168 && key_p->data[2] == 0);
+
+ memset(next_key_p, 0, key_size);
+ assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
+ assert(next_key_p->prefixlen == 28 && next_key_p->data[0] == 192 &&
+ next_key_p->data[1] == 168 && next_key_p->data[2] == 1 &&
+ next_key_p->data[3] == 128);
+
+ memcpy(key_p, next_key_p, key_size);
+ assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
+ assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 &&
+ next_key_p->data[1] == 168 && next_key_p->data[2] == 1);
+
+ memcpy(key_p, next_key_p, key_size);
+ assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
+ assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 &&
+ next_key_p->data[1] == 168 && next_key_p->data[2] == 128);
+
+ memcpy(key_p, next_key_p, key_size);
+ assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
+ assert(next_key_p->prefixlen == 16 && next_key_p->data[0] == 192 &&
+ next_key_p->data[1] == 168);
+
+ memcpy(key_p, next_key_p, key_size);
+ assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 &&
+ errno == ENOENT);
+
/* no exact matching key should return the first one in post order */
key_p->prefixlen = 22;
inet_pton(AF_INET, "192.168.1.0", key_p->data);
diff --git a/tools/testing/selftests/bpf/test_lru_map.c b/tools/testing/selftests/bpf/test_lru_map.c
index 781c7de343be..6a5349f9eb14 100644
--- a/tools/testing/selftests/bpf/test_lru_map.c
+++ b/tools/testing/selftests/bpf/test_lru_map.c
@@ -1,9 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016 Facebook
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
*/
#define _GNU_SOURCE
#include <stdio.h>
@@ -18,9 +15,11 @@
#include <sys/wait.h>
#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
#include "bpf_util.h"
#include "bpf_rlimit.h"
+#include "../../../include/linux/filter.h"
#define LOCAL_FREE_TARGET (128)
#define PERCPU_FREE_TARGET (4)
@@ -40,6 +39,68 @@ static int create_map(int map_type, int map_flags, unsigned int size)
return map_fd;
}
+static int bpf_map_lookup_elem_with_ref_bit(int fd, unsigned long long key,
+ void *value)
+{
+ struct bpf_load_program_attr prog;
+ struct bpf_create_map_attr map;
+ struct bpf_insn insns[] = {
+ BPF_LD_MAP_VALUE(BPF_REG_9, 0, 0),
+ BPF_LD_MAP_FD(BPF_REG_1, fd),
+ BPF_LD_IMM64(BPF_REG_3, key),
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+ BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0),
+ BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4),
+ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 0),
+ BPF_STX_MEM(BPF_DW, BPF_REG_9, BPF_REG_1, 0),
+ BPF_MOV64_IMM(BPF_REG_0, 42),
+ BPF_JMP_IMM(BPF_JA, 0, 0, 1),
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ };
+ __u8 data[64] = {};
+ int mfd, pfd, ret, zero = 0;
+ __u32 retval = 0;
+
+ memset(&map, 0, sizeof(map));
+ map.map_type = BPF_MAP_TYPE_ARRAY;
+ map.key_size = sizeof(int);
+ map.value_size = sizeof(unsigned long long);
+ map.max_entries = 1;
+
+ mfd = bpf_create_map_xattr(&map);
+ if (mfd < 0)
+ return -1;
+
+ insns[0].imm = mfd;
+
+ memset(&prog, 0, sizeof(prog));
+ prog.prog_type = BPF_PROG_TYPE_SCHED_CLS;
+ prog.insns = insns;
+ prog.insns_cnt = ARRAY_SIZE(insns);
+ prog.license = "GPL";
+
+ pfd = bpf_load_program_xattr(&prog, NULL, 0);
+ if (pfd < 0) {
+ close(mfd);
+ return -1;
+ }
+
+ ret = bpf_prog_test_run(pfd, 1, data, sizeof(data),
+ NULL, NULL, &retval, NULL);
+ if (ret < 0 || retval != 42) {
+ ret = -1;
+ } else {
+ assert(!bpf_map_lookup_elem(mfd, &zero, value));
+ ret = 0;
+ }
+ close(pfd);
+ close(mfd);
+ return ret;
+}
+
static int map_subset(int map0, int map1)
{
unsigned long long next_key = 0;
@@ -87,7 +148,7 @@ static int sched_next_online(int pid, int *next_to_try)
return ret;
}
-/* Size of the LRU amp is 2
+/* Size of the LRU map is 2
* Add key=1 (+1 key)
* Add key=2 (+1 key)
* Lookup Key=1
@@ -157,7 +218,7 @@ static void test_lru_sanity0(int map_type, int map_flags)
* stop LRU from removing key=1
*/
key = 1;
- assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
+ assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
assert(value[0] == 1234);
key = 3;
@@ -167,7 +228,8 @@ static void test_lru_sanity0(int map_type, int map_flags)
/* key=2 has been removed from the LRU */
key = 2;
- assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1);
+ assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
+ errno == ENOENT);
assert(map_equal(lru_map_fd, expected_map_fd));
@@ -221,7 +283,7 @@ static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free)
/* Lookup 1 to tgt_free/2 */
end_key = 1 + batch_size;
for (key = 1; key < end_key; key++) {
- assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
+ assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
assert(!bpf_map_update_elem(expected_map_fd, &key, value,
BPF_NOEXIST));
}
@@ -322,10 +384,11 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free)
end_key = 1 + batch_size;
value[0] = 4321;
for (key = 1; key < end_key; key++) {
- assert(bpf_map_lookup_elem(lru_map_fd, &key, value));
+ assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
+ errno == ENOENT);
assert(!bpf_map_update_elem(lru_map_fd, &key, value,
BPF_NOEXIST));
- assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
+ assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
assert(value[0] == 4321);
assert(!bpf_map_update_elem(expected_map_fd, &key, value,
BPF_NOEXIST));
@@ -404,7 +467,7 @@ static void test_lru_sanity3(int map_type, int map_flags, unsigned int tgt_free)
/* Lookup key 1 to tgt_free*3/2 */
end_key = tgt_free + batch_size;
for (key = 1; key < end_key; key++) {
- assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
+ assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
assert(!bpf_map_update_elem(expected_map_fd, &key, value,
BPF_NOEXIST));
}
@@ -463,7 +526,7 @@ static void test_lru_sanity4(int map_type, int map_flags, unsigned int tgt_free)
assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
for (key = 1; key <= tgt_free; key++) {
- assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
+ assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
assert(!bpf_map_update_elem(expected_map_fd, &key, value,
BPF_NOEXIST));
}
@@ -494,16 +557,16 @@ static void do_test_lru_sanity5(unsigned long long last_key, int map_fd)
unsigned long long key, value[nr_cpus];
/* Ensure the last key inserted by previous CPU can be found */
- assert(!bpf_map_lookup_elem(map_fd, &last_key, value));
-
+ assert(!bpf_map_lookup_elem_with_ref_bit(map_fd, last_key, value));
value[0] = 1234;
key = last_key + 1;
assert(!bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST));
- assert(!bpf_map_lookup_elem(map_fd, &key, value));
+ assert(!bpf_map_lookup_elem_with_ref_bit(map_fd, key, value));
/* Cannot find the last key because it was removed by LRU */
- assert(bpf_map_lookup_elem(map_fd, &last_key, value));
+ assert(bpf_map_lookup_elem(map_fd, &last_key, value) == -1 &&
+ errno == ENOENT);
}
/* Test map with only one element */
@@ -590,8 +653,8 @@ static void test_lru_sanity6(int map_type, int map_flags, int tgt_free)
/* Make ref bit sticky for key: [1, tgt_free] */
for (stable_key = 1; stable_key <= tgt_free; stable_key++) {
/* Mark the ref bit */
- assert(!bpf_map_lookup_elem(lru_map_fd, &stable_key,
- value));
+ assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd,
+ stable_key, value));
}
assert(!bpf_map_update_elem(lru_map_fd, &key, value,
BPF_NOEXIST));
@@ -612,6 +675,198 @@ static void test_lru_sanity6(int map_type, int map_flags, int tgt_free)
printf("Pass\n");
}
+/* Size of the LRU map is 2
+ * Add key=1 (+1 key)
+ * Add key=2 (+1 key)
+ * Lookup Key=1 (datapath)
+ * Lookup Key=2 (syscall)
+ * Add Key=3
+ * => Key=2 will be removed by LRU
+ * Iterate map. Only found key=1 and key=3
+ */
+static void test_lru_sanity7(int map_type, int map_flags)
+{
+ unsigned long long key, value[nr_cpus];
+ int lru_map_fd, expected_map_fd;
+ int next_cpu = 0;
+
+ printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
+ map_flags);
+
+ assert(sched_next_online(0, &next_cpu) != -1);
+
+ if (map_flags & BPF_F_NO_COMMON_LRU)
+ lru_map_fd = create_map(map_type, map_flags, 2 * nr_cpus);
+ else
+ lru_map_fd = create_map(map_type, map_flags, 2);
+ assert(lru_map_fd != -1);
+
+ expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, 2);
+ assert(expected_map_fd != -1);
+
+ value[0] = 1234;
+
+ /* insert key=1 element */
+
+ key = 1;
+ assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
+ assert(!bpf_map_update_elem(expected_map_fd, &key, value,
+ BPF_NOEXIST));
+
+ /* BPF_NOEXIST means: add new element if it doesn't exist */
+ assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST) == -1
+ /* key=1 already exists */
+ && errno == EEXIST);
+
+ /* insert key=2 element */
+
+ /* check that key=2 is not found */
+ key = 2;
+ assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
+ errno == ENOENT);
+
+ /* BPF_EXIST means: update existing element */
+ assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_EXIST) == -1 &&
+ /* key=2 is not there */
+ errno == ENOENT);
+
+ assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
+
+ /* insert key=3 element */
+
+ /* check that key=3 is not found */
+ key = 3;
+ assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
+ errno == ENOENT);
+
+ /* check that key=1 can be found and mark the ref bit to
+ * stop LRU from removing key=1
+ */
+ key = 1;
+ assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
+ assert(value[0] == 1234);
+
+ /* check that key=2 can be found and do _not_ mark ref bit.
+ * this will be evicted on next update.
+ */
+ key = 2;
+ assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
+ assert(value[0] == 1234);
+
+ key = 3;
+ assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
+ assert(!bpf_map_update_elem(expected_map_fd, &key, value,
+ BPF_NOEXIST));
+
+ /* key=2 has been removed from the LRU */
+ key = 2;
+ assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
+ errno == ENOENT);
+
+ assert(map_equal(lru_map_fd, expected_map_fd));
+
+ close(expected_map_fd);
+ close(lru_map_fd);
+
+ printf("Pass\n");
+}
+
+/* Size of the LRU map is 2
+ * Add key=1 (+1 key)
+ * Add key=2 (+1 key)
+ * Lookup Key=1 (syscall)
+ * Lookup Key=2 (datapath)
+ * Add Key=3
+ * => Key=1 will be removed by LRU
+ * Iterate map. Only found key=2 and key=3
+ */
+static void test_lru_sanity8(int map_type, int map_flags)
+{
+ unsigned long long key, value[nr_cpus];
+ int lru_map_fd, expected_map_fd;
+ int next_cpu = 0;
+
+ printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
+ map_flags);
+
+ assert(sched_next_online(0, &next_cpu) != -1);
+
+ if (map_flags & BPF_F_NO_COMMON_LRU)
+ lru_map_fd = create_map(map_type, map_flags, 2 * nr_cpus);
+ else
+ lru_map_fd = create_map(map_type, map_flags, 2);
+ assert(lru_map_fd != -1);
+
+ expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, 2);
+ assert(expected_map_fd != -1);
+
+ value[0] = 1234;
+
+ /* insert key=1 element */
+
+ key = 1;
+ assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
+
+ /* BPF_NOEXIST means: add new element if it doesn't exist */
+ assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST) == -1
+ /* key=1 already exists */
+ && errno == EEXIST);
+
+ /* insert key=2 element */
+
+ /* check that key=2 is not found */
+ key = 2;
+ assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
+ errno == ENOENT);
+
+ /* BPF_EXIST means: update existing element */
+ assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_EXIST) == -1 &&
+ /* key=2 is not there */
+ errno == ENOENT);
+
+ assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
+ assert(!bpf_map_update_elem(expected_map_fd, &key, value,
+ BPF_NOEXIST));
+
+ /* insert key=3 element */
+
+ /* check that key=3 is not found */
+ key = 3;
+ assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
+ errno == ENOENT);
+
+ /* check that key=1 can be found and do _not_ mark ref bit.
+ * this will be evicted on next update.
+ */
+ key = 1;
+ assert(!bpf_map_lookup_elem(lru_map_fd, &key, value));
+ assert(value[0] == 1234);
+
+ /* check that key=2 can be found and mark the ref bit to
+ * stop LRU from removing key=2
+ */
+ key = 2;
+ assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value));
+ assert(value[0] == 1234);
+
+ key = 3;
+ assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST));
+ assert(!bpf_map_update_elem(expected_map_fd, &key, value,
+ BPF_NOEXIST));
+
+ /* key=1 has been removed from the LRU */
+ key = 1;
+ assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 &&
+ errno == ENOENT);
+
+ assert(map_equal(lru_map_fd, expected_map_fd));
+
+ close(expected_map_fd);
+ close(lru_map_fd);
+
+ printf("Pass\n");
+}
+
int main(int argc, char **argv)
{
int map_types[] = {BPF_MAP_TYPE_LRU_HASH,
@@ -637,6 +892,8 @@ int main(int argc, char **argv)
test_lru_sanity4(map_types[t], map_flags[f], tgt_free);
test_lru_sanity5(map_types[t], map_flags[f]);
test_lru_sanity6(map_types[t], map_flags[f], tgt_free);
+ test_lru_sanity7(map_types[t], map_flags[f]);
+ test_lru_sanity8(map_types[t], map_flags[f]);
printf("\n");
}
diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c
index 246f745cb006..e1f1becda529 100644
--- a/tools/testing/selftests/bpf/test_maps.c
+++ b/tools/testing/selftests/bpf/test_maps.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Testsuite for eBPF maps
*
* Copyright (c) 2014 PLUMgrid, http://plumgrid.com
* Copyright (c) 2016 Facebook
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
*/
#include <stdio.h>
@@ -511,6 +508,21 @@ static void test_devmap(unsigned int task, void *data)
close(fd);
}
+static void test_devmap_hash(unsigned int task, void *data)
+{
+ int fd;
+ __u32 key, value;
+
+ fd = bpf_create_map(BPF_MAP_TYPE_DEVMAP_HASH, sizeof(key), sizeof(value),
+ 2, 0);
+ if (fd < 0) {
+ printf("Failed to create devmap_hash '%s'!\n", strerror(errno));
+ exit(1);
+ }
+
+ close(fd);
+}
+
static void test_queuemap(unsigned int task, void *data)
{
const int MAP_SIZE = 32;
@@ -1421,7 +1433,7 @@ static void test_map_wronly(void)
assert(bpf_map_get_next_key(fd, &key, &value) == -1 && errno == EPERM);
}
-static void prepare_reuseport_grp(int type, int map_fd,
+static void prepare_reuseport_grp(int type, int map_fd, size_t map_elem_size,
__s64 *fds64, __u64 *sk_cookies,
unsigned int n)
{
@@ -1431,6 +1443,8 @@ static void prepare_reuseport_grp(int type, int map_fd,
const int optval = 1;
unsigned int i;
u64 sk_cookie;
+ void *value;
+ __s32 fd32;
__s64 fd64;
int err;
@@ -1452,8 +1466,14 @@ static void prepare_reuseport_grp(int type, int map_fd,
"err:%d errno:%d\n", err, errno);
/* reuseport_array does not allow unbound sk */
- err = bpf_map_update_elem(map_fd, &index0, &fd64,
- BPF_ANY);
+ if (map_elem_size == sizeof(__u64))
+ value = &fd64;
+ else {
+ assert(map_elem_size == sizeof(__u32));
+ fd32 = (__s32)fd64;
+ value = &fd32;
+ }
+ err = bpf_map_update_elem(map_fd, &index0, value, BPF_ANY);
CHECK(err != -1 || errno != EINVAL,
"reuseport array update unbound sk",
"sock_type:%d err:%d errno:%d\n",
@@ -1481,7 +1501,7 @@ static void prepare_reuseport_grp(int type, int map_fd,
* reuseport_array does not allow
* non-listening tcp sk.
*/
- err = bpf_map_update_elem(map_fd, &index0, &fd64,
+ err = bpf_map_update_elem(map_fd, &index0, value,
BPF_ANY);
CHECK(err != -1 || errno != EINVAL,
"reuseport array update non-listening sk",
@@ -1544,7 +1564,7 @@ static void test_reuseport_array(void)
for (t = 0; t < ARRAY_SIZE(types); t++) {
type = types[t];
- prepare_reuseport_grp(type, map_fd, grpa_fds64,
+ prepare_reuseport_grp(type, map_fd, sizeof(__u64), grpa_fds64,
grpa_cookies, ARRAY_SIZE(grpa_fds64));
/* Test BPF_* update flags */
@@ -1652,7 +1672,8 @@ static void test_reuseport_array(void)
sizeof(__u32), sizeof(__u32), array_size, 0);
CHECK(map_fd == -1, "reuseport array create",
"map_fd:%d, errno:%d\n", map_fd, errno);
- prepare_reuseport_grp(SOCK_STREAM, map_fd, &fd64, &sk_cookie, 1);
+ prepare_reuseport_grp(SOCK_STREAM, map_fd, sizeof(__u32), &fd64,
+ &sk_cookie, 1);
fd = fd64;
err = bpf_map_update_elem(map_fd, &index3, &fd, BPF_NOEXIST);
CHECK(err == -1, "reuseport array update 32 bit fd",
@@ -1678,6 +1699,7 @@ static void run_all_tests(void)
test_arraymap_percpu_many_keys();
test_devmap(0, NULL);
+ test_devmap_hash(0, NULL);
test_sockmap(0, NULL);
test_map_large();
diff --git a/tools/testing/selftests/bpf/test_offload.py b/tools/testing/selftests/bpf/test_offload.py
index 425f9ed27c3b..15a666329a34 100755
--- a/tools/testing/selftests/bpf/test_offload.py
+++ b/tools/testing/selftests/bpf/test_offload.py
@@ -1353,7 +1353,7 @@ try:
bpftool_prog_list_wait(expected=1)
ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
- fail(ifnameB != simB1['ifname'], "program not bound to originial device")
+ fail(ifnameB != simB1['ifname'], "program not bound to original device")
simB1.remove()
bpftool_prog_list_wait(expected=1)
diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c
index bf5c90998916..af75a1c7a458 100644
--- a/tools/testing/selftests/bpf/test_progs.c
+++ b/tools/testing/selftests/bpf/test_progs.c
@@ -1,15 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2017 Facebook
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
*/
#include "test_progs.h"
+#include "cgroup_helpers.h"
#include "bpf_rlimit.h"
+#include <argp.h>
+#include <string.h>
-int error_cnt, pass_cnt;
-bool jit_enabled;
-bool verifier_stats = false;
+/* defined in test_progs.h */
+struct test_env env;
+
+struct prog_test_def {
+ const char *test_name;
+ int test_num;
+ void (*run_test)(void);
+ bool force_log;
+ int error_cnt;
+ int skip_cnt;
+ bool tested;
+ bool need_cgroup_cleanup;
+
+ const char *subtest_name;
+ int subtest_num;
+
+ /* store counts before subtest started */
+ int old_error_cnt;
+};
+
+static bool should_run(struct test_selector *sel, int num, const char *name)
+{
+ if (sel->name && sel->name[0] && !strstr(name, sel->name))
+ return false;
+
+ if (!sel->num_set)
+ return true;
+
+ return num < sel->num_set_len && sel->num_set[num];
+}
+
+static void dump_test_log(const struct prog_test_def *test, bool failed)
+{
+ if (stdout == env.stdout)
+ return;
+
+ fflush(stdout); /* exports env.log_buf & env.log_cnt */
+
+ if (env.verbose || test->force_log || failed) {
+ if (env.log_cnt) {
+ env.log_buf[env.log_cnt] = '\0';
+ fprintf(env.stdout, "%s", env.log_buf);
+ if (env.log_buf[env.log_cnt - 1] != '\n')
+ fprintf(env.stdout, "\n");
+ }
+ }
+
+ fseeko(stdout, 0, SEEK_SET); /* rewind */
+}
+
+static void skip_account(void)
+{
+ if (env.test->skip_cnt) {
+ env.skip_cnt++;
+ env.test->skip_cnt = 0;
+ }
+}
+
+void test__end_subtest()
+{
+ struct prog_test_def *test = env.test;
+ int sub_error_cnt = test->error_cnt - test->old_error_cnt;
+
+ if (sub_error_cnt)
+ env.fail_cnt++;
+ else
+ env.sub_succ_cnt++;
+ skip_account();
+
+ dump_test_log(test, sub_error_cnt);
+
+ fprintf(env.stdout, "#%d/%d %s:%s\n",
+ test->test_num, test->subtest_num,
+ test->subtest_name, sub_error_cnt ? "FAIL" : "OK");
+}
+
+bool test__start_subtest(const char *name)
+{
+ struct prog_test_def *test = env.test;
+
+ if (test->subtest_name) {
+ test__end_subtest();
+ test->subtest_name = NULL;
+ }
+
+ test->subtest_num++;
+
+ if (!name || !name[0]) {
+ fprintf(env.stderr,
+ "Subtest #%d didn't provide sub-test name!\n",
+ test->subtest_num);
+ return false;
+ }
+
+ if (!should_run(&env.subtest_selector, test->subtest_num, name))
+ return false;
+
+ test->subtest_name = name;
+ env.test->old_error_cnt = env.test->error_cnt;
+
+ return true;
+}
+
+void test__force_log() {
+ env.test->force_log = true;
+}
+
+void test__skip(void)
+{
+ env.test->skip_cnt++;
+}
+
+void test__fail(void)
+{
+ env.test->error_cnt++;
+}
+
+int test__join_cgroup(const char *path)
+{
+ int fd;
+
+ if (!env.test->need_cgroup_cleanup) {
+ if (setup_cgroup_environment()) {
+ fprintf(stderr,
+ "#%d %s: Failed to setup cgroup environment\n",
+ env.test->test_num, env.test->test_name);
+ return -1;
+ }
+
+ env.test->need_cgroup_cleanup = true;
+ }
+
+ fd = create_and_get_cgroup(path);
+ if (fd < 0) {
+ fprintf(stderr,
+ "#%d %s: Failed to create cgroup '%s' (errno=%d)\n",
+ env.test->test_num, env.test->test_name, path, errno);
+ return fd;
+ }
+
+ if (join_cgroup(path)) {
+ fprintf(stderr,
+ "#%d %s: Failed to join cgroup '%s' (errno=%d)\n",
+ env.test->test_num, env.test->test_name, path, errno);
+ return -1;
+ }
+
+ return fd;
+}
struct ipv4_packet pkt_v4 = {
.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
@@ -35,7 +181,7 @@ int bpf_find_map(const char *test, struct bpf_object *obj, const char *name)
map = bpf_object__find_map_by_name(obj, name);
if (!map) {
printf("%s:FAIL:map '%s' not found\n", test, name);
- error_cnt++;
+ test__fail();
return -1;
}
return bpf_map__fd(map);
@@ -159,23 +305,276 @@ void *spin_lock_thread(void *arg)
pthread_exit(arg);
}
-#define DECLARE
+/* extern declarations for test funcs */
+#define DEFINE_TEST(name) extern void test_##name();
#include <prog_tests/tests.h>
-#undef DECLARE
+#undef DEFINE_TEST
+
+static struct prog_test_def prog_test_defs[] = {
+#define DEFINE_TEST(name) { \
+ .test_name = #name, \
+ .run_test = &test_##name, \
+},
+#include <prog_tests/tests.h>
+#undef DEFINE_TEST
+};
+const int prog_test_cnt = ARRAY_SIZE(prog_test_defs);
+
+const char *argp_program_version = "test_progs 0.1";
+const char *argp_program_bug_address = "<bpf@vger.kernel.org>";
+const char argp_program_doc[] = "BPF selftests test runner";
+
+enum ARG_KEYS {
+ ARG_TEST_NUM = 'n',
+ ARG_TEST_NAME = 't',
+ ARG_VERIFIER_STATS = 's',
+ ARG_VERBOSE = 'v',
+};
+
+static const struct argp_option opts[] = {
+ { "num", ARG_TEST_NUM, "NUM", 0,
+ "Run test number NUM only " },
+ { "name", ARG_TEST_NAME, "NAME", 0,
+ "Run tests with names containing NAME" },
+ { "verifier-stats", ARG_VERIFIER_STATS, NULL, 0,
+ "Output verifier statistics", },
+ { "verbose", ARG_VERBOSE, "LEVEL", OPTION_ARG_OPTIONAL,
+ "Verbose output (use -vv for extra verbose output)" },
+ {},
+};
-int main(int ac, char **av)
+static int libbpf_print_fn(enum libbpf_print_level level,
+ const char *format, va_list args)
{
+ if (!env.very_verbose && level == LIBBPF_DEBUG)
+ return 0;
+ vprintf(format, args);
+ return 0;
+}
+
+int parse_num_list(const char *s, struct test_selector *sel)
+{
+ int i, set_len = 0, num, start = 0, end = -1;
+ bool *set = NULL, *tmp, parsing_end = false;
+ char *next;
+
+ while (s[0]) {
+ errno = 0;
+ num = strtol(s, &next, 10);
+ if (errno)
+ return -errno;
+
+ if (parsing_end)
+ end = num;
+ else
+ start = num;
+
+ if (!parsing_end && *next == '-') {
+ s = next + 1;
+ parsing_end = true;
+ continue;
+ } else if (*next == ',') {
+ parsing_end = false;
+ s = next + 1;
+ end = num;
+ } else if (*next == '\0') {
+ parsing_end = false;
+ s = next;
+ end = num;
+ } else {
+ return -EINVAL;
+ }
+
+ if (start > end)
+ return -EINVAL;
+
+ if (end + 1 > set_len) {
+ set_len = end + 1;
+ tmp = realloc(set, set_len);
+ if (!tmp) {
+ free(set);
+ return -ENOMEM;
+ }
+ set = tmp;
+ }
+ for (i = start; i <= end; i++) {
+ set[i] = true;
+ }
+
+ }
+
+ if (!set)
+ return -EINVAL;
+
+ sel->num_set = set;
+ sel->num_set_len = set_len;
+
+ return 0;
+}
+
+static error_t parse_arg(int key, char *arg, struct argp_state *state)
+{
+ struct test_env *env = state->input;
+
+ switch (key) {
+ case ARG_TEST_NUM: {
+ char *subtest_str = strchr(arg, '/');
+
+ if (subtest_str) {
+ *subtest_str = '\0';
+ if (parse_num_list(subtest_str + 1,
+ &env->subtest_selector)) {
+ fprintf(stderr,
+ "Failed to parse subtest numbers.\n");
+ return -EINVAL;
+ }
+ }
+ if (parse_num_list(arg, &env->test_selector)) {
+ fprintf(stderr, "Failed to parse test numbers.\n");
+ return -EINVAL;
+ }
+ break;
+ }
+ case ARG_TEST_NAME: {
+ char *subtest_str = strchr(arg, '/');
+
+ if (subtest_str) {
+ *subtest_str = '\0';
+ env->subtest_selector.name = strdup(subtest_str + 1);
+ if (!env->subtest_selector.name)
+ return -ENOMEM;
+ }
+ env->test_selector.name = strdup(arg);
+ if (!env->test_selector.name)
+ return -ENOMEM;
+ break;
+ }
+ case ARG_VERIFIER_STATS:
+ env->verifier_stats = true;
+ break;
+ case ARG_VERBOSE:
+ if (arg) {
+ if (strcmp(arg, "v") == 0) {
+ env->very_verbose = true;
+ } else {
+ fprintf(stderr,
+ "Unrecognized verbosity setting ('%s'), only -v and -vv are supported\n",
+ arg);
+ return -EINVAL;
+ }
+ }
+ env->verbose = true;
+ break;
+ case ARGP_KEY_ARG:
+ argp_usage(state);
+ break;
+ case ARGP_KEY_END:
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static void stdio_hijack(void)
+{
+#ifdef __GLIBC__
+ env.stdout = stdout;
+ env.stderr = stderr;
+
+ if (env.verbose) {
+ /* nothing to do, output to stdout by default */
+ return;
+ }
+
+ /* stdout and stderr -> buffer */
+ fflush(stdout);
+
+ stdout = open_memstream(&env.log_buf, &env.log_cnt);
+ if (!stdout) {
+ stdout = env.stdout;
+ perror("open_memstream");
+ return;
+ }
+
+ stderr = stdout;
+#endif
+}
+
+static void stdio_restore(void)
+{
+#ifdef __GLIBC__
+ if (stdout == env.stdout)
+ return;
+
+ fclose(stdout);
+ free(env.log_buf);
+
+ env.log_buf = NULL;
+ env.log_cnt = 0;
+
+ stdout = env.stdout;
+ stderr = env.stderr;
+#endif
+}
+
+int main(int argc, char **argv)
+{
+ static const struct argp argp = {
+ .options = opts,
+ .parser = parse_arg,
+ .doc = argp_program_doc,
+ };
+ int err, i;
+
+ err = argp_parse(&argp, argc, argv, 0, NULL, &env);
+ if (err)
+ return err;
+
+ libbpf_set_print(libbpf_print_fn);
+
srand(time(NULL));
- jit_enabled = is_jit_enabled();
+ env.jit_enabled = is_jit_enabled();
- if (ac == 2 && strcmp(av[1], "-s") == 0)
- verifier_stats = true;
+ stdio_hijack();
+ for (i = 0; i < prog_test_cnt; i++) {
+ struct prog_test_def *test = &prog_test_defs[i];
-#define CALL
-#include <prog_tests/tests.h>
-#undef CALL
+ env.test = test;
+ test->test_num = i + 1;
+
+ if (!should_run(&env.test_selector,
+ test->test_num, test->test_name))
+ continue;
+
+ test->run_test();
+ /* ensure last sub-test is finalized properly */
+ if (test->subtest_name)
+ test__end_subtest();
+
+ test->tested = true;
+ if (test->error_cnt)
+ env.fail_cnt++;
+ else
+ env.succ_cnt++;
+ skip_account();
+
+ dump_test_log(test, test->error_cnt);
+
+ fprintf(env.stdout, "#%d %s:%s\n",
+ test->test_num, test->test_name,
+ test->error_cnt ? "FAIL" : "OK");
+
+ if (test->need_cgroup_cleanup)
+ cleanup_cgroup_environment();
+ }
+ stdio_restore();
+ printf("Summary: %d/%d PASSED, %d SKIPPED, %d FAILED\n",
+ env.succ_cnt, env.sub_succ_cnt, env.skip_cnt, env.fail_cnt);
+
+ free(env.test_selector.num_set);
+ free(env.subtest_selector.num_set);
- printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt);
- return error_cnt ? EXIT_FAILURE : EXIT_SUCCESS;
+ return env.fail_cnt ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/tools/testing/selftests/bpf/test_progs.h b/tools/testing/selftests/bpf/test_progs.h
index f095e1d4c657..0c48f64f732b 100644
--- a/tools/testing/selftests/bpf/test_progs.h
+++ b/tools/testing/selftests/bpf/test_progs.h
@@ -16,9 +16,10 @@ typedef __u16 __sum16;
#include <linux/if_packet.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
-#include <linux/tcp.h>
+#include <netinet/tcp.h>
#include <linux/filter.h>
#include <linux/perf_event.h>
+#include <linux/socket.h>
#include <linux/unistd.h>
#include <sys/ioctl.h>
@@ -38,9 +39,40 @@ typedef __u16 __sum16;
#include "trace_helpers.h"
#include "flow_dissector_load.h"
-extern int error_cnt, pass_cnt;
-extern bool jit_enabled;
-extern bool verifier_stats;
+struct test_selector {
+ const char *name;
+ bool *num_set;
+ int num_set_len;
+};
+
+struct test_env {
+ struct test_selector test_selector;
+ struct test_selector subtest_selector;
+ bool verifier_stats;
+ bool verbose;
+ bool very_verbose;
+
+ bool jit_enabled;
+
+ struct prog_test_def *test;
+ FILE *stdout;
+ FILE *stderr;
+ char *log_buf;
+ size_t log_cnt;
+
+ int succ_cnt; /* successful tests */
+ int sub_succ_cnt; /* successful sub-tests */
+ int fail_cnt; /* total failed tests + sub-tests */
+ int skip_cnt; /* skipped tests */
+};
+
+extern struct test_env env;
+
+extern void test__force_log();
+extern bool test__start_subtest(const char *name);
+extern void test__skip(void);
+extern void test__fail(void);
+extern int test__join_cgroup(const char *path);
#define MAGIC_BYTES 123
@@ -63,12 +95,21 @@ extern struct ipv6_packet pkt_v6;
#define _CHECK(condition, tag, duration, format...) ({ \
int __ret = !!(condition); \
if (__ret) { \
- error_cnt++; \
+ test__fail(); \
printf("%s:FAIL:%s ", __func__, tag); \
printf(format); \
} else { \
- pass_cnt++; \
- printf("%s:PASS:%s %d nsec\n", __func__, tag, duration);\
+ printf("%s:PASS:%s %d nsec\n", \
+ __func__, tag, duration); \
+ } \
+ __ret; \
+})
+
+#define CHECK_FAIL(condition) ({ \
+ int __ret = !!(condition); \
+ if (__ret) { \
+ test__fail(); \
+ printf("%s:FAIL:%d\n", __func__, __LINE__); \
} \
__ret; \
})
@@ -92,3 +133,11 @@ int compare_map_keys(int map1_fd, int map2_fd);
int compare_stack_ips(int smap_fd, int amap_fd, int stack_trace_len);
int extract_build_id(char *build_id, size_t size);
void *spin_lock_thread(void *arg);
+
+#ifdef __x86_64__
+#define SYS_NANOSLEEP_KPROBE_NAME "__x64_sys_nanosleep"
+#elif defined(__s390x__)
+#define SYS_NANOSLEEP_KPROBE_NAME "__s390x_sys_nanosleep"
+#else
+#define SYS_NANOSLEEP_KPROBE_NAME "sys_nanosleep"
+#endif
diff --git a/tools/testing/selftests/bpf/test_queue_stack_map.h b/tools/testing/selftests/bpf/test_queue_stack_map.h
index 295b9b3bc5c7..0e014d3b2b36 100644
--- a/tools/testing/selftests/bpf/test_queue_stack_map.h
+++ b/tools/testing/selftests/bpf/test_queue_stack_map.h
@@ -10,21 +10,21 @@
int _version SEC("version") = 1;
-struct bpf_map_def __attribute__ ((section("maps"), used)) map_in = {
- .type = MAP_TYPE,
- .key_size = 0,
- .value_size = sizeof(__u32),
- .max_entries = 32,
- .map_flags = 0,
-};
-
-struct bpf_map_def __attribute__ ((section("maps"), used)) map_out = {
- .type = MAP_TYPE,
- .key_size = 0,
- .value_size = sizeof(__u32),
- .max_entries = 32,
- .map_flags = 0,
-};
+struct {
+ __uint(type, MAP_TYPE);
+ __uint(max_entries, 32);
+ __uint(map_flags, 0);
+ __uint(key_size, 0);
+ __uint(value_size, sizeof(__u32));
+} map_in SEC(".maps");
+
+struct {
+ __uint(type, MAP_TYPE);
+ __uint(max_entries, 32);
+ __uint(map_flags, 0);
+ __uint(key_size, 0);
+ __uint(value_size, sizeof(__u32));
+} map_out SEC(".maps");
SEC("test")
int _test(struct __sk_buff *skb)
diff --git a/tools/testing/selftests/bpf/test_section_names.c b/tools/testing/selftests/bpf/test_section_names.c
index bebd4fbca1f4..29833aeaf0de 100644
--- a/tools/testing/selftests/bpf/test_section_names.c
+++ b/tools/testing/selftests/bpf/test_section_names.c
@@ -120,10 +120,30 @@ static struct sec_name_test tests[] = {
{0, BPF_CGROUP_UDP6_SENDMSG},
},
{
+ "cgroup/recvmsg4",
+ {0, BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_UDP4_RECVMSG},
+ {0, BPF_CGROUP_UDP4_RECVMSG},
+ },
+ {
+ "cgroup/recvmsg6",
+ {0, BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_UDP6_RECVMSG},
+ {0, BPF_CGROUP_UDP6_RECVMSG},
+ },
+ {
"cgroup/sysctl",
{0, BPF_PROG_TYPE_CGROUP_SYSCTL, BPF_CGROUP_SYSCTL},
{0, BPF_CGROUP_SYSCTL},
},
+ {
+ "cgroup/getsockopt",
+ {0, BPF_PROG_TYPE_CGROUP_SOCKOPT, BPF_CGROUP_GETSOCKOPT},
+ {0, BPF_CGROUP_GETSOCKOPT},
+ },
+ {
+ "cgroup/setsockopt",
+ {0, BPF_PROG_TYPE_CGROUP_SOCKOPT, BPF_CGROUP_SETSOCKOPT},
+ {0, BPF_CGROUP_SETSOCKOPT},
+ },
};
static int test_prog_type_by_name(const struct sec_name_test *test)
diff --git a/tools/testing/selftests/bpf/test_select_reuseport.c b/tools/testing/selftests/bpf/test_select_reuseport.c
index 75646d9b34aa..7566c13eb51a 100644
--- a/tools/testing/selftests/bpf/test_select_reuseport.c
+++ b/tools/testing/selftests/bpf/test_select_reuseport.c
@@ -523,6 +523,58 @@ static void test_pass_on_err(int type, sa_family_t family)
printf("OK\n");
}
+static void test_detach_bpf(int type, sa_family_t family)
+{
+#ifdef SO_DETACH_REUSEPORT_BPF
+ __u32 nr_run_before = 0, nr_run_after = 0, tmp, i;
+ struct epoll_event ev;
+ int cli_fd, err, nev;
+ struct cmd cmd = {};
+ int optvalue = 0;
+
+ printf("%s: ", __func__);
+ err = setsockopt(sk_fds[0], SOL_SOCKET, SO_DETACH_REUSEPORT_BPF,
+ &optvalue, sizeof(optvalue));
+ CHECK(err == -1, "setsockopt(SO_DETACH_REUSEPORT_BPF)",
+ "err:%d errno:%d\n", err, errno);
+
+ err = setsockopt(sk_fds[1], SOL_SOCKET, SO_DETACH_REUSEPORT_BPF,
+ &optvalue, sizeof(optvalue));
+ CHECK(err == 0 || errno != ENOENT, "setsockopt(SO_DETACH_REUSEPORT_BPF)",
+ "err:%d errno:%d\n", err, errno);
+
+ for (i = 0; i < NR_RESULTS; i++) {
+ err = bpf_map_lookup_elem(result_map, &i, &tmp);
+ CHECK(err == -1, "lookup_elem(result_map)",
+ "i:%u err:%d errno:%d\n", i, err, errno);
+ nr_run_before += tmp;
+ }
+
+ cli_fd = send_data(type, family, &cmd, sizeof(cmd), PASS);
+ nev = epoll_wait(epfd, &ev, 1, 5);
+ CHECK(nev <= 0, "nev <= 0",
+ "nev:%d expected:1 type:%d family:%d data:(0, 0)\n",
+ nev, type, family);
+
+ for (i = 0; i < NR_RESULTS; i++) {
+ err = bpf_map_lookup_elem(result_map, &i, &tmp);
+ CHECK(err == -1, "lookup_elem(result_map)",
+ "i:%u err:%d errno:%d\n", i, err, errno);
+ nr_run_after += tmp;
+ }
+
+ CHECK(nr_run_before != nr_run_after,
+ "nr_run_before != nr_run_after",
+ "nr_run_before:%u nr_run_after:%u\n",
+ nr_run_before, nr_run_after);
+
+ printf("OK\n");
+ close(cli_fd);
+#else
+ printf("%s: SKIP\n", __func__);
+#endif
+}
+
static void prepare_sk_fds(int type, sa_family_t family, bool inany)
{
const int first = REUSEPORT_ARRAY_SIZE - 1;
@@ -664,6 +716,8 @@ static void test_all(void)
test_pass(type, family);
test_syncookie(type, family);
test_pass_on_err(type, family);
+ /* Must be the last test */
+ test_detach_bpf(type, family);
cleanup_per_test();
printf("\n");
diff --git a/tools/testing/selftests/bpf/test_sock.c b/tools/testing/selftests/bpf/test_sock.c
index fb679ac3d4b0..0e6652733462 100644
--- a/tools/testing/selftests/bpf/test_sock.c
+++ b/tools/testing/selftests/bpf/test_sock.c
@@ -13,6 +13,7 @@
#include <bpf/bpf.h>
#include "cgroup_helpers.h"
+#include "bpf_endian.h"
#include "bpf_rlimit.h"
#include "bpf_util.h"
@@ -232,7 +233,8 @@ static struct sock_test tests[] = {
/* if (ip == expected && port == expected) */
BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
offsetof(struct bpf_sock, src_ip6[3])),
- BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x01000000, 4),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
+ __bpf_constant_ntohl(0x00000001), 4),
BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
offsetof(struct bpf_sock, src_port)),
BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2),
@@ -261,7 +263,8 @@ static struct sock_test tests[] = {
/* if (ip == expected && port == expected) */
BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
offsetof(struct bpf_sock, src_ip4)),
- BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x0100007F, 4),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
+ __bpf_constant_ntohl(0x7F000001), 4),
BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
offsetof(struct bpf_sock, src_port)),
BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
diff --git a/tools/testing/selftests/bpf/test_sock_addr.c b/tools/testing/selftests/bpf/test_sock_addr.c
index 3f110eaaf29c..61fd95b89af8 100644
--- a/tools/testing/selftests/bpf/test_sock_addr.c
+++ b/tools/testing/selftests/bpf/test_sock_addr.c
@@ -76,6 +76,7 @@ struct sock_addr_test {
enum {
LOAD_REJECT,
ATTACH_REJECT,
+ ATTACH_OKAY,
SYSCALL_EPERM,
SYSCALL_ENOTSUPP,
SUCCESS,
@@ -88,9 +89,13 @@ static int connect4_prog_load(const struct sock_addr_test *test);
static int connect6_prog_load(const struct sock_addr_test *test);
static int sendmsg_allow_prog_load(const struct sock_addr_test *test);
static int sendmsg_deny_prog_load(const struct sock_addr_test *test);
+static int recvmsg_allow_prog_load(const struct sock_addr_test *test);
+static int recvmsg_deny_prog_load(const struct sock_addr_test *test);
static int sendmsg4_rw_asm_prog_load(const struct sock_addr_test *test);
+static int recvmsg4_rw_asm_prog_load(const struct sock_addr_test *test);
static int sendmsg4_rw_c_prog_load(const struct sock_addr_test *test);
static int sendmsg6_rw_asm_prog_load(const struct sock_addr_test *test);
+static int recvmsg6_rw_asm_prog_load(const struct sock_addr_test *test);
static int sendmsg6_rw_c_prog_load(const struct sock_addr_test *test);
static int sendmsg6_rw_v4mapped_prog_load(const struct sock_addr_test *test);
static int sendmsg6_rw_wildcard_prog_load(const struct sock_addr_test *test);
@@ -507,6 +512,92 @@ static struct sock_addr_test tests[] = {
SRC6_REWRITE_IP,
SYSCALL_EPERM,
},
+
+ /* recvmsg */
+ {
+ "recvmsg4: return code ok",
+ recvmsg_allow_prog_load,
+ BPF_CGROUP_UDP4_RECVMSG,
+ BPF_CGROUP_UDP4_RECVMSG,
+ AF_INET,
+ SOCK_DGRAM,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ ATTACH_OKAY,
+ },
+ {
+ "recvmsg4: return code !ok",
+ recvmsg_deny_prog_load,
+ BPF_CGROUP_UDP4_RECVMSG,
+ BPF_CGROUP_UDP4_RECVMSG,
+ AF_INET,
+ SOCK_DGRAM,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ LOAD_REJECT,
+ },
+ {
+ "recvmsg6: return code ok",
+ recvmsg_allow_prog_load,
+ BPF_CGROUP_UDP6_RECVMSG,
+ BPF_CGROUP_UDP6_RECVMSG,
+ AF_INET6,
+ SOCK_DGRAM,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ ATTACH_OKAY,
+ },
+ {
+ "recvmsg6: return code !ok",
+ recvmsg_deny_prog_load,
+ BPF_CGROUP_UDP6_RECVMSG,
+ BPF_CGROUP_UDP6_RECVMSG,
+ AF_INET6,
+ SOCK_DGRAM,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ LOAD_REJECT,
+ },
+ {
+ "recvmsg4: rewrite IP & port (asm)",
+ recvmsg4_rw_asm_prog_load,
+ BPF_CGROUP_UDP4_RECVMSG,
+ BPF_CGROUP_UDP4_RECVMSG,
+ AF_INET,
+ SOCK_DGRAM,
+ SERV4_REWRITE_IP,
+ SERV4_REWRITE_PORT,
+ SERV4_REWRITE_IP,
+ SERV4_REWRITE_PORT,
+ SERV4_IP,
+ SUCCESS,
+ },
+ {
+ "recvmsg6: rewrite IP & port (asm)",
+ recvmsg6_rw_asm_prog_load,
+ BPF_CGROUP_UDP6_RECVMSG,
+ BPF_CGROUP_UDP6_RECVMSG,
+ AF_INET6,
+ SOCK_DGRAM,
+ SERV6_REWRITE_IP,
+ SERV6_REWRITE_PORT,
+ SERV6_REWRITE_IP,
+ SERV6_REWRITE_PORT,
+ SERV6_IP,
+ SUCCESS,
+ },
};
static int mk_sockaddr(int domain, const char *ip, unsigned short port,
@@ -745,6 +836,7 @@ static int load_path(const struct sock_addr_test *test, const char *path)
attr.file = path;
attr.prog_type = BPF_PROG_TYPE_CGROUP_SOCK_ADDR;
attr.expected_attach_type = test->expected_attach_type;
+ attr.prog_flags = BPF_F_TEST_RND_HI32;
if (bpf_prog_load_xattr(&attr, &obj, &prog_fd)) {
if (test->expected_result != LOAD_REJECT)
@@ -765,8 +857,8 @@ static int connect6_prog_load(const struct sock_addr_test *test)
return load_path(test, CONNECT6_PROG_PATH);
}
-static int sendmsg_ret_only_prog_load(const struct sock_addr_test *test,
- int32_t rc)
+static int xmsg_ret_only_prog_load(const struct sock_addr_test *test,
+ int32_t rc)
{
struct bpf_insn insns[] = {
/* return rc */
@@ -778,12 +870,22 @@ static int sendmsg_ret_only_prog_load(const struct sock_addr_test *test,
static int sendmsg_allow_prog_load(const struct sock_addr_test *test)
{
- return sendmsg_ret_only_prog_load(test, /*rc*/ 1);
+ return xmsg_ret_only_prog_load(test, /*rc*/ 1);
}
static int sendmsg_deny_prog_load(const struct sock_addr_test *test)
{
- return sendmsg_ret_only_prog_load(test, /*rc*/ 0);
+ return xmsg_ret_only_prog_load(test, /*rc*/ 0);
+}
+
+static int recvmsg_allow_prog_load(const struct sock_addr_test *test)
+{
+ return xmsg_ret_only_prog_load(test, /*rc*/ 1);
+}
+
+static int recvmsg_deny_prog_load(const struct sock_addr_test *test)
+{
+ return xmsg_ret_only_prog_load(test, /*rc*/ 0);
}
static int sendmsg4_rw_asm_prog_load(const struct sock_addr_test *test)
@@ -838,6 +940,47 @@ static int sendmsg4_rw_asm_prog_load(const struct sock_addr_test *test)
return load_insns(test, insns, sizeof(insns) / sizeof(struct bpf_insn));
}
+static int recvmsg4_rw_asm_prog_load(const struct sock_addr_test *test)
+{
+ struct sockaddr_in src4_rw_addr;
+
+ if (mk_sockaddr(AF_INET, SERV4_IP, SERV4_PORT,
+ (struct sockaddr *)&src4_rw_addr,
+ sizeof(src4_rw_addr)) == -1)
+ return -1;
+
+ struct bpf_insn insns[] = {
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
+
+ /* if (sk.family == AF_INET && */
+ BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
+ offsetof(struct bpf_sock_addr, family)),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_7, AF_INET, 6),
+
+ /* sk.type == SOCK_DGRAM) { */
+ BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
+ offsetof(struct bpf_sock_addr, type)),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_7, SOCK_DGRAM, 4),
+
+ /* user_ip4 = src4_rw_addr.sin_addr */
+ BPF_MOV32_IMM(BPF_REG_7, src4_rw_addr.sin_addr.s_addr),
+ BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7,
+ offsetof(struct bpf_sock_addr, user_ip4)),
+
+ /* user_port = src4_rw_addr.sin_port */
+ BPF_MOV32_IMM(BPF_REG_7, src4_rw_addr.sin_port),
+ BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7,
+ offsetof(struct bpf_sock_addr, user_port)),
+ /* } */
+
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ };
+
+ return load_insns(test, insns, sizeof(insns) / sizeof(struct bpf_insn));
+}
+
static int sendmsg4_rw_c_prog_load(const struct sock_addr_test *test)
{
return load_path(test, SENDMSG4_PROG_PATH);
@@ -901,6 +1044,39 @@ static int sendmsg6_rw_asm_prog_load(const struct sock_addr_test *test)
return sendmsg6_rw_dst_asm_prog_load(test, SERV6_REWRITE_IP);
}
+static int recvmsg6_rw_asm_prog_load(const struct sock_addr_test *test)
+{
+ struct sockaddr_in6 src6_rw_addr;
+
+ if (mk_sockaddr(AF_INET6, SERV6_IP, SERV6_PORT,
+ (struct sockaddr *)&src6_rw_addr,
+ sizeof(src6_rw_addr)) == -1)
+ return -1;
+
+ struct bpf_insn insns[] = {
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
+
+ /* if (sk.family == AF_INET6) { */
+ BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
+ offsetof(struct bpf_sock_addr, family)),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_7, AF_INET6, 10),
+
+ STORE_IPV6(user_ip6, src6_rw_addr.sin6_addr.s6_addr32),
+
+ /* user_port = dst6_rw_addr.sin6_port */
+ BPF_MOV32_IMM(BPF_REG_7, src6_rw_addr.sin6_port),
+ BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7,
+ offsetof(struct bpf_sock_addr, user_port)),
+ /* } */
+
+ /* return 1 */
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ };
+
+ return load_insns(test, insns, sizeof(insns) / sizeof(struct bpf_insn));
+}
+
static int sendmsg6_rw_v4mapped_prog_load(const struct sock_addr_test *test)
{
return sendmsg6_rw_dst_asm_prog_load(test, SERV6_V4MAPPED_IP);
@@ -1282,13 +1458,13 @@ out:
return err;
}
-static int run_sendmsg_test_case(const struct sock_addr_test *test)
+static int run_xmsg_test_case(const struct sock_addr_test *test, int max_cmsg)
{
socklen_t addr_len = sizeof(struct sockaddr_storage);
- struct sockaddr_storage expected_src_addr;
- struct sockaddr_storage requested_addr;
struct sockaddr_storage expected_addr;
- struct sockaddr_storage real_src_addr;
+ struct sockaddr_storage server_addr;
+ struct sockaddr_storage sendmsg_addr;
+ struct sockaddr_storage recvmsg_addr;
int clientfd = -1;
int servfd = -1;
int set_cmsg;
@@ -1297,20 +1473,19 @@ static int run_sendmsg_test_case(const struct sock_addr_test *test)
if (test->type != SOCK_DGRAM)
goto err;
- if (init_addrs(test, &requested_addr, &expected_addr,
- &expected_src_addr))
+ if (init_addrs(test, &sendmsg_addr, &server_addr, &expected_addr))
goto err;
/* Prepare server to sendmsg to */
- servfd = start_server(test->type, &expected_addr, addr_len);
+ servfd = start_server(test->type, &server_addr, addr_len);
if (servfd == -1)
goto err;
- for (set_cmsg = 0; set_cmsg <= 1; ++set_cmsg) {
+ for (set_cmsg = 0; set_cmsg <= max_cmsg; ++set_cmsg) {
if (clientfd >= 0)
close(clientfd);
- clientfd = sendmsg_to_server(test->type, &requested_addr,
+ clientfd = sendmsg_to_server(test->type, &sendmsg_addr,
addr_len, set_cmsg, /*flags*/0,
&err);
if (err)
@@ -1330,10 +1505,10 @@ static int run_sendmsg_test_case(const struct sock_addr_test *test)
* specific packet may differ from the one used by default and
* returned by getsockname(2).
*/
- if (recvmsg_from_client(servfd, &real_src_addr) == -1)
+ if (recvmsg_from_client(servfd, &recvmsg_addr) == -1)
goto err;
- if (cmp_addr(&real_src_addr, &expected_src_addr, /*cmp_port*/0))
+ if (cmp_addr(&recvmsg_addr, &expected_addr, /*cmp_port*/0))
goto err;
}
@@ -1366,6 +1541,9 @@ static int run_test_case(int cgfd, const struct sock_addr_test *test)
goto out;
} else if (test->expected_result == ATTACH_REJECT || err) {
goto err;
+ } else if (test->expected_result == ATTACH_OKAY) {
+ err = 0;
+ goto out;
}
switch (test->attach_type) {
@@ -1379,7 +1557,11 @@ static int run_test_case(int cgfd, const struct sock_addr_test *test)
break;
case BPF_CGROUP_UDP4_SENDMSG:
case BPF_CGROUP_UDP6_SENDMSG:
- err = run_sendmsg_test_case(test);
+ err = run_xmsg_test_case(test, 1);
+ break;
+ case BPF_CGROUP_UDP4_RECVMSG:
+ case BPF_CGROUP_UDP6_RECVMSG:
+ err = run_xmsg_test_case(test, 0);
break;
default:
goto err;
diff --git a/tools/testing/selftests/bpf/test_sock_fields.c b/tools/testing/selftests/bpf/test_sock_fields.c
index e089477fa0a3..f0fc103261a4 100644
--- a/tools/testing/selftests/bpf/test_sock_fields.c
+++ b/tools/testing/selftests/bpf/test_sock_fields.c
@@ -414,6 +414,7 @@ int main(int argc, char **argv)
struct bpf_prog_load_attr attr = {
.file = "test_sock_fields_kern.o",
.prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .prog_flags = BPF_F_TEST_RND_HI32,
};
int cgroup_fd, egress_fd, ingress_fd, err;
struct bpf_program *ingress_prog;
diff --git a/tools/testing/selftests/bpf/test_socket_cookie.c b/tools/testing/selftests/bpf/test_socket_cookie.c
index e51d63786ff8..15653b0e26eb 100644
--- a/tools/testing/selftests/bpf/test_socket_cookie.c
+++ b/tools/testing/selftests/bpf/test_socket_cookie.c
@@ -18,6 +18,11 @@
#define CG_PATH "/foo"
#define SOCKET_COOKIE_PROG "./socket_cookie_prog.o"
+struct socket_cookie {
+ __u64 cookie_key;
+ __u32 cookie_value;
+};
+
static int start_server(void)
{
struct sockaddr_in6 addr;
@@ -89,8 +94,7 @@ static int validate_map(struct bpf_map *map, int client_fd)
__u32 cookie_expected_value;
struct sockaddr_in6 addr;
socklen_t len = sizeof(addr);
- __u32 cookie_value;
- __u64 cookie_key;
+ struct socket_cookie val;
int err = 0;
int map_fd;
@@ -101,17 +105,7 @@ static int validate_map(struct bpf_map *map, int client_fd)
map_fd = bpf_map__fd(map);
- err = bpf_map_get_next_key(map_fd, NULL, &cookie_key);
- if (err) {
- log_err("Can't get cookie key from map");
- goto out;
- }
-
- err = bpf_map_lookup_elem(map_fd, &cookie_key, &cookie_value);
- if (err) {
- log_err("Can't get cookie value from map");
- goto out;
- }
+ err = bpf_map_lookup_elem(map_fd, &client_fd, &val);
err = getsockname(client_fd, (struct sockaddr *)&addr, &len);
if (err) {
@@ -120,8 +114,8 @@ static int validate_map(struct bpf_map *map, int client_fd)
}
cookie_expected_value = (ntohs(addr.sin6_port) << 8) | 0xFF;
- if (cookie_value != cookie_expected_value) {
- log_err("Unexpected value in map: %x != %x", cookie_value,
+ if (val.cookie_value != cookie_expected_value) {
+ log_err("Unexpected value in map: %x != %x", val.cookie_value,
cookie_expected_value);
goto err;
}
@@ -148,6 +142,7 @@ static int run_test(int cgfd)
memset(&attr, 0, sizeof(attr));
attr.file = SOCKET_COOKIE_PROG;
attr.prog_type = BPF_PROG_TYPE_UNSPEC;
+ attr.prog_flags = BPF_F_TEST_RND_HI32;
err = bpf_prog_load_xattr(&attr, &pobj, &prog_fd);
if (err) {
diff --git a/tools/testing/selftests/bpf/test_sockmap_kern.h b/tools/testing/selftests/bpf/test_sockmap_kern.h
index e7639f66a941..d008b41b7d8d 100644
--- a/tools/testing/selftests/bpf/test_sockmap_kern.h
+++ b/tools/testing/selftests/bpf/test_sockmap_kern.h
@@ -28,68 +28,61 @@
* are established and verdicts are decided.
*/
-#define bpf_printk(fmt, ...) \
-({ \
- char ____fmt[] = fmt; \
- bpf_trace_printk(____fmt, sizeof(____fmt), \
- ##__VA_ARGS__); \
-})
-
-struct bpf_map_def SEC("maps") sock_map = {
- .type = TEST_MAP_TYPE,
- .key_size = sizeof(int),
- .value_size = sizeof(int),
- .max_entries = 20,
-};
-
-struct bpf_map_def SEC("maps") sock_map_txmsg = {
- .type = TEST_MAP_TYPE,
- .key_size = sizeof(int),
- .value_size = sizeof(int),
- .max_entries = 20,
-};
-
-struct bpf_map_def SEC("maps") sock_map_redir = {
- .type = TEST_MAP_TYPE,
- .key_size = sizeof(int),
- .value_size = sizeof(int),
- .max_entries = 20,
-};
-
-struct bpf_map_def SEC("maps") sock_apply_bytes = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(int),
- .value_size = sizeof(int),
- .max_entries = 1
-};
-
-struct bpf_map_def SEC("maps") sock_cork_bytes = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(int),
- .value_size = sizeof(int),
- .max_entries = 1
-};
-
-struct bpf_map_def SEC("maps") sock_bytes = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(int),
- .value_size = sizeof(int),
- .max_entries = 6
-};
-
-struct bpf_map_def SEC("maps") sock_redir_flags = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(int),
- .value_size = sizeof(int),
- .max_entries = 1
-};
-
-struct bpf_map_def SEC("maps") sock_skb_opts = {
- .type = BPF_MAP_TYPE_ARRAY,
- .key_size = sizeof(int),
- .value_size = sizeof(int),
- .max_entries = 1
-};
+struct {
+ __uint(type, TEST_MAP_TYPE);
+ __uint(max_entries, 20);
+ __uint(key_size, sizeof(int));
+ __uint(value_size, sizeof(int));
+} sock_map SEC(".maps");
+
+struct {
+ __uint(type, TEST_MAP_TYPE);
+ __uint(max_entries, 20);
+ __uint(key_size, sizeof(int));
+ __uint(value_size, sizeof(int));
+} sock_map_txmsg SEC(".maps");
+
+struct {
+ __uint(type, TEST_MAP_TYPE);
+ __uint(max_entries, 20);
+ __uint(key_size, sizeof(int));
+ __uint(value_size, sizeof(int));
+} sock_map_redir SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, int);
+ __type(value, int);
+} sock_apply_bytes SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, int);
+ __type(value, int);
+} sock_cork_bytes SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 6);
+ __type(key, int);
+ __type(value, int);
+} sock_bytes SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, int);
+ __type(value, int);
+} sock_redir_flags SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, int);
+ __type(value, int);
+} sock_skb_opts SEC(".maps");
SEC("sk_skb1")
int bpf_prog1(struct __sk_buff *skb)
diff --git a/tools/testing/selftests/bpf/test_stub.c b/tools/testing/selftests/bpf/test_stub.c
new file mode 100644
index 000000000000..84e81a89e2f9
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_stub.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright (C) 2019 Netronome Systems, Inc. */
+
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+#include <string.h>
+
+int bpf_prog_test_load(const char *file, enum bpf_prog_type type,
+ struct bpf_object **pobj, int *prog_fd)
+{
+ struct bpf_prog_load_attr attr;
+
+ memset(&attr, 0, sizeof(struct bpf_prog_load_attr));
+ attr.file = file;
+ attr.prog_type = type;
+ attr.expected_attach_type = 0;
+ attr.prog_flags = BPF_F_TEST_RND_HI32;
+
+ return bpf_prog_load_xattr(&attr, pobj, prog_fd);
+}
+
+int bpf_test_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
+ size_t insns_cnt, const char *license,
+ __u32 kern_version, char *log_buf,
+ size_t log_buf_sz)
+{
+ struct bpf_load_program_attr load_attr;
+
+ memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
+ load_attr.prog_type = type;
+ load_attr.expected_attach_type = 0;
+ load_attr.name = NULL;
+ load_attr.insns = insns;
+ load_attr.insns_cnt = insns_cnt;
+ load_attr.license = license;
+ load_attr.kern_version = kern_version;
+ load_attr.prog_flags = BPF_F_TEST_RND_HI32;
+
+ return bpf_load_program_xattr(&load_attr, log_buf, log_buf_sz);
+}
diff --git a/tools/testing/selftests/bpf/test_sysctl.c b/tools/testing/selftests/bpf/test_sysctl.c
index a3bebd7c68dd..4f8ec1f10a80 100644
--- a/tools/testing/selftests/bpf/test_sysctl.c
+++ b/tools/testing/selftests/bpf/test_sysctl.c
@@ -13,6 +13,7 @@
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
+#include "bpf_endian.h"
#include "bpf_rlimit.h"
#include "bpf_util.h"
#include "cgroup_helpers.h"
@@ -31,6 +32,7 @@ struct sysctl_test {
enum bpf_attach_type attach_type;
const char *sysctl;
int open_flags;
+ int seek;
const char *newval;
const char *oldval;
enum {
@@ -100,7 +102,7 @@ static struct sysctl_test tests[] = {
.descr = "ctx:write sysctl:write read ok",
.insns = {
/* If (write) */
- BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_1,
+ BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1,
offsetof(struct bpf_sysctl, write)),
BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 1, 2),
@@ -139,7 +141,7 @@ static struct sysctl_test tests[] = {
/* If (file_pos == X) */
BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1,
offsetof(struct bpf_sysctl, file_pos)),
- BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0, 2),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 3, 2),
/* return ALLOW; */
BPF_MOV64_IMM(BPF_REG_0, 1),
@@ -152,6 +154,7 @@ static struct sysctl_test tests[] = {
.attach_type = BPF_CGROUP_SYSCTL,
.sysctl = "kernel/ostype",
.open_flags = O_RDONLY,
+ .seek = 3,
.result = SUCCESS,
},
{
@@ -214,7 +217,8 @@ static struct sysctl_test tests[] = {
/* if (ret == expected && */
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, sizeof("tcp_mem") - 1, 6),
/* buf == "tcp_mem\0") */
- BPF_LD_IMM64(BPF_REG_8, 0x006d656d5f706374ULL),
+ BPF_LD_IMM64(BPF_REG_8,
+ bpf_be64_to_cpu(0x7463705f6d656d00ULL)),
BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 2),
@@ -255,7 +259,8 @@ static struct sysctl_test tests[] = {
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, -E2BIG, 6),
/* buf[0:7] == "tcp_me\0") */
- BPF_LD_IMM64(BPF_REG_8, 0x00656d5f706374ULL),
+ BPF_LD_IMM64(BPF_REG_8,
+ bpf_be64_to_cpu(0x7463705f6d650000ULL)),
BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 2),
@@ -298,12 +303,14 @@ static struct sysctl_test tests[] = {
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 16, 14),
/* buf[0:8] == "net/ipv4" && */
- BPF_LD_IMM64(BPF_REG_8, 0x347670692f74656eULL),
+ BPF_LD_IMM64(BPF_REG_8,
+ bpf_be64_to_cpu(0x6e65742f69707634ULL)),
BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 10),
/* buf[8:16] == "/tcp_mem" && */
- BPF_LD_IMM64(BPF_REG_8, 0x6d656d5f7063742fULL),
+ BPF_LD_IMM64(BPF_REG_8,
+ bpf_be64_to_cpu(0x2f7463705f6d656dULL)),
BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 8),
BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 6),
@@ -350,12 +357,14 @@ static struct sysctl_test tests[] = {
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, -E2BIG, 10),
/* buf[0:8] == "net/ipv4" && */
- BPF_LD_IMM64(BPF_REG_8, 0x347670692f74656eULL),
+ BPF_LD_IMM64(BPF_REG_8,
+ bpf_be64_to_cpu(0x6e65742f69707634ULL)),
BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 6),
/* buf[8:16] == "/tcp_me\0") */
- BPF_LD_IMM64(BPF_REG_8, 0x00656d5f7063742fULL),
+ BPF_LD_IMM64(BPF_REG_8,
+ bpf_be64_to_cpu(0x2f7463705f6d6500ULL)),
BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 8),
BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 2),
@@ -396,7 +405,8 @@ static struct sysctl_test tests[] = {
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, -E2BIG, 6),
/* buf[0:8] == "net/ip\0") */
- BPF_LD_IMM64(BPF_REG_8, 0x000070692f74656eULL),
+ BPF_LD_IMM64(BPF_REG_8,
+ bpf_be64_to_cpu(0x6e65742f69700000ULL)),
BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 2),
@@ -431,7 +441,8 @@ static struct sysctl_test tests[] = {
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 6, 6),
/* buf[0:6] == "Linux\n\0") */
- BPF_LD_IMM64(BPF_REG_8, 0x000a78756e694cULL),
+ BPF_LD_IMM64(BPF_REG_8,
+ bpf_be64_to_cpu(0x4c696e75780a0000ULL)),
BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 2),
@@ -469,7 +480,8 @@ static struct sysctl_test tests[] = {
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 6, 6),
/* buf[0:6] == "Linux\n\0") */
- BPF_LD_IMM64(BPF_REG_8, 0x000a78756e694cULL),
+ BPF_LD_IMM64(BPF_REG_8,
+ bpf_be64_to_cpu(0x4c696e75780a0000ULL)),
BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 2),
@@ -507,7 +519,8 @@ static struct sysctl_test tests[] = {
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, -E2BIG, 6),
/* buf[0:6] == "Linux\0") */
- BPF_LD_IMM64(BPF_REG_8, 0x000078756e694cULL),
+ BPF_LD_IMM64(BPF_REG_8,
+ bpf_be64_to_cpu(0x4c696e7578000000ULL)),
BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 2),
@@ -650,7 +663,8 @@ static struct sysctl_test tests[] = {
/* buf[0:4] == "606\0") */
BPF_LDX_MEM(BPF_W, BPF_REG_9, BPF_REG_7, 0),
- BPF_JMP_IMM(BPF_JNE, BPF_REG_9, 0x00363036, 2),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_9,
+ bpf_ntohl(0x36303600), 2),
/* return DENY; */
BPF_MOV64_IMM(BPF_REG_0, 0),
@@ -685,17 +699,20 @@ static struct sysctl_test tests[] = {
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 23, 14),
/* buf[0:8] == "3000000 " && */
- BPF_LD_IMM64(BPF_REG_8, 0x2030303030303033ULL),
+ BPF_LD_IMM64(BPF_REG_8,
+ bpf_be64_to_cpu(0x3330303030303020ULL)),
BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 10),
/* buf[8:16] == "4000000 " && */
- BPF_LD_IMM64(BPF_REG_8, 0x2030303030303034ULL),
+ BPF_LD_IMM64(BPF_REG_8,
+ bpf_be64_to_cpu(0x3430303030303020ULL)),
BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 8),
BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 6),
/* buf[16:24] == "6000000\0") */
- BPF_LD_IMM64(BPF_REG_8, 0x0030303030303036ULL),
+ BPF_LD_IMM64(BPF_REG_8,
+ bpf_be64_to_cpu(0x3630303030303000ULL)),
BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 16),
BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 2),
@@ -735,7 +752,8 @@ static struct sysctl_test tests[] = {
/* buf[0:3] == "60\0") */
BPF_LDX_MEM(BPF_W, BPF_REG_9, BPF_REG_7, 0),
- BPF_JMP_IMM(BPF_JNE, BPF_REG_9, 0x003036, 2),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_9,
+ bpf_ntohl(0x36300000), 2),
/* return DENY; */
BPF_MOV64_IMM(BPF_REG_0, 0),
@@ -757,7 +775,8 @@ static struct sysctl_test tests[] = {
/* sysctl_set_new_value arg2 (buf) */
BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
- BPF_MOV64_IMM(BPF_REG_0, 0x00303036),
+ BPF_MOV64_IMM(BPF_REG_0,
+ bpf_ntohl(0x36303000)),
BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
@@ -791,7 +810,7 @@ static struct sysctl_test tests[] = {
/* sysctl_set_new_value arg2 (buf) */
BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
- BPF_MOV64_IMM(BPF_REG_0, FIXUP_SYSCTL_VALUE),
+ BPF_LD_IMM64(BPF_REG_0, FIXUP_SYSCTL_VALUE),
BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
@@ -825,8 +844,9 @@ static struct sysctl_test tests[] = {
/* arg1 (buf) */
BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
- BPF_MOV64_IMM(BPF_REG_0, 0x00303036),
- BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
+ BPF_MOV64_IMM(BPF_REG_0,
+ bpf_ntohl(0x36303000)),
+ BPF_STX_MEM(BPF_W, BPF_REG_7, BPF_REG_0, 0),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
@@ -869,7 +889,8 @@ static struct sysctl_test tests[] = {
BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
/* "600 602\0" */
- BPF_LD_IMM64(BPF_REG_0, 0x0032303620303036ULL),
+ BPF_LD_IMM64(BPF_REG_0,
+ bpf_be64_to_cpu(0x3630302036303200ULL)),
BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
@@ -937,7 +958,8 @@ static struct sysctl_test tests[] = {
/* arg1 (buf) */
BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
- BPF_MOV64_IMM(BPF_REG_0, 0x00303036),
+ BPF_MOV64_IMM(BPF_REG_0,
+ bpf_ntohl(0x36303000)),
BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
@@ -969,8 +991,9 @@ static struct sysctl_test tests[] = {
/* arg1 (buf) */
BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
- BPF_MOV64_IMM(BPF_REG_0, 0x00373730),
- BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
+ BPF_MOV64_IMM(BPF_REG_0,
+ bpf_ntohl(0x30373700)),
+ BPF_STX_MEM(BPF_W, BPF_REG_7, BPF_REG_0, 0),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
@@ -1012,7 +1035,8 @@ static struct sysctl_test tests[] = {
/* arg1 (buf) */
BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
- BPF_MOV64_IMM(BPF_REG_0, 0x00303036),
+ BPF_MOV64_IMM(BPF_REG_0,
+ bpf_ntohl(0x36303000)),
BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
@@ -1052,7 +1076,8 @@ static struct sysctl_test tests[] = {
/* arg1 (buf) */
BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
- BPF_MOV64_IMM(BPF_REG_0, 0x090a0c0d),
+ BPF_MOV64_IMM(BPF_REG_0,
+ bpf_ntohl(0x0d0c0a09)),
BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
@@ -1092,7 +1117,9 @@ static struct sysctl_test tests[] = {
/* arg1 (buf) */
BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
- BPF_MOV64_IMM(BPF_REG_0, 0x00362d0a), /* " -6\0" */
+ /* " -6\0" */
+ BPF_MOV64_IMM(BPF_REG_0,
+ bpf_ntohl(0x0a2d3600)),
BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
@@ -1132,8 +1159,10 @@ static struct sysctl_test tests[] = {
/* arg1 (buf) */
BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
- BPF_MOV64_IMM(BPF_REG_0, 0x00362d0a), /* " -6\0" */
- BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
+ /* " -6\0" */
+ BPF_MOV64_IMM(BPF_REG_0,
+ bpf_ntohl(0x0a2d3600)),
+ BPF_STX_MEM(BPF_W, BPF_REG_7, BPF_REG_0, 0),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
@@ -1175,8 +1204,10 @@ static struct sysctl_test tests[] = {
/* arg1 (buf) */
BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
- BPF_MOV64_IMM(BPF_REG_0, 0x65667830), /* "0xfe" */
- BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
+ /* "0xfe" */
+ BPF_MOV64_IMM(BPF_REG_0,
+ bpf_ntohl(0x30786665)),
+ BPF_STX_MEM(BPF_W, BPF_REG_7, BPF_REG_0, 0),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
@@ -1218,11 +1249,14 @@ static struct sysctl_test tests[] = {
/* arg1 (buf) 9223372036854775807 */
BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -24),
- BPF_LD_IMM64(BPF_REG_0, 0x3032373333323239ULL),
+ BPF_LD_IMM64(BPF_REG_0,
+ bpf_be64_to_cpu(0x3932323333373230ULL)),
BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
- BPF_LD_IMM64(BPF_REG_0, 0x3537373435383633ULL),
+ BPF_LD_IMM64(BPF_REG_0,
+ bpf_be64_to_cpu(0x3336383534373735ULL)),
BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 8),
- BPF_LD_IMM64(BPF_REG_0, 0x0000000000373038ULL),
+ BPF_LD_IMM64(BPF_REG_0,
+ bpf_be64_to_cpu(0x3830370000000000ULL)),
BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 16),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
@@ -1266,11 +1300,14 @@ static struct sysctl_test tests[] = {
/* arg1 (buf) 9223372036854775808 */
BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -24),
- BPF_LD_IMM64(BPF_REG_0, 0x3032373333323239ULL),
+ BPF_LD_IMM64(BPF_REG_0,
+ bpf_be64_to_cpu(0x3932323333373230ULL)),
BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
- BPF_LD_IMM64(BPF_REG_0, 0x3537373435383633ULL),
+ BPF_LD_IMM64(BPF_REG_0,
+ bpf_be64_to_cpu(0x3336383534373735ULL)),
BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 8),
- BPF_LD_IMM64(BPF_REG_0, 0x0000000000383038ULL),
+ BPF_LD_IMM64(BPF_REG_0,
+ bpf_be64_to_cpu(0x3830380000000000ULL)),
BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 16),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
@@ -1344,20 +1381,24 @@ static size_t probe_prog_length(const struct bpf_insn *fp)
static int fixup_sysctl_value(const char *buf, size_t buf_len,
struct bpf_insn *prog, size_t insn_num)
{
- uint32_t value_num = 0;
+ union {
+ uint8_t raw[sizeof(uint64_t)];
+ uint64_t num;
+ } value = {};
uint8_t c, i;
- if (buf_len > sizeof(value_num)) {
+ if (buf_len > sizeof(value)) {
log_err("Value is too big (%zd) to use in fixup", buf_len);
return -1;
}
-
- for (i = 0; i < buf_len; ++i) {
- c = buf[i];
- value_num |= (c << i * 8);
+ if (prog[insn_num].code != (BPF_LD | BPF_DW | BPF_IMM)) {
+ log_err("Can fixup only BPF_LD_IMM64 insns");
+ return -1;
}
- prog[insn_num].imm = value_num;
+ memcpy(value.raw, buf, buf_len);
+ prog[insn_num].imm = (uint32_t)value.num;
+ prog[insn_num + 1].imm = (uint32_t)(value.num >> 32);
return 0;
}
@@ -1442,6 +1483,11 @@ static int access_sysctl(const char *sysctl_path,
if (fd < 0)
return fd;
+ if (test->seek && lseek(fd, test->seek, SEEK_SET) == -1) {
+ log_err("lseek(%d) failed", test->seek);
+ goto err;
+ }
+
if (test->open_flags == O_RDONLY) {
char buf[128];
@@ -1499,6 +1545,7 @@ static int run_test_case(int cgfd, struct sysctl_test *test)
goto err;
}
+ errno = 0;
if (access_sysctl(sysctl_path, test) == -1) {
if (test->result == OP_EPERM && errno == EPERM)
goto out;
@@ -1507,7 +1554,7 @@ static int run_test_case(int cgfd, struct sysctl_test *test)
}
if (test->result != SUCCESS) {
- log_err("Unexpected failure");
+ log_err("Unexpected success");
goto err;
}
diff --git a/tools/testing/selftests/bpf/test_tcp_check_syncookie.sh b/tools/testing/selftests/bpf/test_tcp_check_syncookie.sh
index d48e51716d19..9b3617d770a5 100755
--- a/tools/testing/selftests/bpf/test_tcp_check_syncookie.sh
+++ b/tools/testing/selftests/bpf/test_tcp_check_syncookie.sh
@@ -37,6 +37,9 @@ setup()
ns1_exec ip link set lo up
ns1_exec sysctl -w net.ipv4.tcp_syncookies=2
+ ns1_exec sysctl -w net.ipv4.tcp_window_scaling=0
+ ns1_exec sysctl -w net.ipv4.tcp_timestamps=0
+ ns1_exec sysctl -w net.ipv4.tcp_sack=0
wait_for_ip 127.0.0.1
wait_for_ip ::1
diff --git a/tools/testing/selftests/bpf/test_tcp_check_syncookie_user.c b/tools/testing/selftests/bpf/test_tcp_check_syncookie_user.c
index 87829c86c746..b9e991d43155 100644
--- a/tools/testing/selftests/bpf/test_tcp_check_syncookie_user.c
+++ b/tools/testing/selftests/bpf/test_tcp_check_syncookie_user.c
@@ -2,6 +2,7 @@
// Copyright (c) 2018 Facebook
// Copyright (c) 2019 Cloudflare
+#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
@@ -77,7 +78,7 @@ out:
return fd;
}
-static int get_map_fd_by_prog_id(int prog_id)
+static int get_map_fd_by_prog_id(int prog_id, bool *xdp)
{
struct bpf_prog_info info = {};
__u32 info_len = sizeof(info);
@@ -104,6 +105,8 @@ static int get_map_fd_by_prog_id(int prog_id)
goto err;
}
+ *xdp = info.type == BPF_PROG_TYPE_XDP;
+
map_fd = bpf_map_get_fd_by_id(map_ids[0]);
if (map_fd < 0)
log_err("Failed to get fd by map id %d", map_ids[0]);
@@ -113,18 +116,32 @@ err:
return map_fd;
}
-static int run_test(int server_fd, int results_fd)
+static int run_test(int server_fd, int results_fd, bool xdp)
{
int client = -1, srv_client = -1;
int ret = 0;
__u32 key = 0;
- __u64 value = 0;
+ __u32 key_gen = 1;
+ __u32 key_mss = 2;
+ __u32 value = 0;
+ __u32 value_gen = 0;
+ __u32 value_mss = 0;
if (bpf_map_update_elem(results_fd, &key, &value, 0) < 0) {
log_err("Can't clear results");
goto err;
}
+ if (bpf_map_update_elem(results_fd, &key_gen, &value_gen, 0) < 0) {
+ log_err("Can't clear results");
+ goto err;
+ }
+
+ if (bpf_map_update_elem(results_fd, &key_mss, &value_mss, 0) < 0) {
+ log_err("Can't clear results");
+ goto err;
+ }
+
client = connect_to_server(server_fd);
if (client == -1)
goto err;
@@ -140,8 +157,35 @@ static int run_test(int server_fd, int results_fd)
goto err;
}
- if (value != 1) {
- log_err("Didn't match syncookie: %llu", value);
+ if (value == 0) {
+ log_err("Didn't match syncookie: %u", value);
+ goto err;
+ }
+
+ if (bpf_map_lookup_elem(results_fd, &key_gen, &value_gen) < 0) {
+ log_err("Can't lookup result");
+ goto err;
+ }
+
+ if (xdp && value_gen == 0) {
+ // SYN packets do not get passed through generic XDP, skip the
+ // rest of the test.
+ printf("Skipping XDP cookie check\n");
+ goto out;
+ }
+
+ if (bpf_map_lookup_elem(results_fd, &key_mss, &value_mss) < 0) {
+ log_err("Can't lookup result");
+ goto err;
+ }
+
+ if (value != value_gen) {
+ log_err("BPF generated cookie does not match kernel one");
+ goto err;
+ }
+
+ if (value_mss < 536 || value_mss > USHRT_MAX) {
+ log_err("Unexpected MSS retrieved");
goto err;
}
@@ -163,13 +207,14 @@ int main(int argc, char **argv)
int server_v6 = -1;
int results = -1;
int err = 0;
+ bool xdp;
if (argc < 2) {
fprintf(stderr, "Usage: %s prog_id\n", argv[0]);
exit(1);
}
- results = get_map_fd_by_prog_id(atoi(argv[1]));
+ results = get_map_fd_by_prog_id(atoi(argv[1]), &xdp);
if (results < 0) {
log_err("Can't get map");
goto err;
@@ -194,10 +239,10 @@ int main(int argc, char **argv)
if (server_v6 == -1)
goto err;
- if (run_test(server, results))
+ if (run_test(server, results, xdp))
goto err;
- if (run_test(server_v6, results))
+ if (run_test(server_v6, results, xdp))
goto err;
printf("ok\n");
diff --git a/tools/testing/selftests/bpf/test_tcpnotify_user.c b/tools/testing/selftests/bpf/test_tcpnotify_user.c
index 86152d9ae95b..f9765ddf0761 100644
--- a/tools/testing/selftests/bpf/test_tcpnotify_user.c
+++ b/tools/testing/selftests/bpf/test_tcpnotify_user.c
@@ -17,6 +17,7 @@
#include <linux/rtnetlink.h>
#include <signal.h>
#include <linux/perf_event.h>
+#include <linux/err.h>
#include "bpf_rlimit.h"
#include "bpf_util.h"
@@ -30,28 +31,34 @@
pthread_t tid;
int rx_callbacks;
-static int dummyfn(void *data, int size)
+static void dummyfn(void *ctx, int cpu, void *data, __u32 size)
{
struct tcp_notifier *t = data;
if (t->type != 0xde || t->subtype != 0xad ||
t->source != 0xbe || t->hash != 0xef)
- return 1;
+ return;
rx_callbacks++;
- return 0;
}
-void tcp_notifier_poller(int fd)
+void tcp_notifier_poller(struct perf_buffer *pb)
{
- while (1)
- perf_event_poller(fd, dummyfn);
+ int err;
+
+ while (1) {
+ err = perf_buffer__poll(pb, 100);
+ if (err < 0 && err != -EINTR) {
+ printf("failed perf_buffer__poll: %d\n", err);
+ return;
+ }
+ }
}
static void *poller_thread(void *arg)
{
- int fd = *(int *)arg;
+ struct perf_buffer *pb = arg;
- tcp_notifier_poller(fd);
+ tcp_notifier_poller(pb);
return arg;
}
@@ -60,52 +67,20 @@ int verify_result(const struct tcpnotify_globals *result)
return (result->ncalls > 0 && result->ncalls == rx_callbacks ? 0 : 1);
}
-static int bpf_find_map(const char *test, struct bpf_object *obj,
- const char *name)
-{
- struct bpf_map *map;
-
- map = bpf_object__find_map_by_name(obj, name);
- if (!map) {
- printf("%s:FAIL:map '%s' not found\n", test, name);
- return -1;
- }
- return bpf_map__fd(map);
-}
-
-static int setup_bpf_perf_event(int mapfd)
-{
- struct perf_event_attr attr = {
- .sample_type = PERF_SAMPLE_RAW,
- .type = PERF_TYPE_SOFTWARE,
- .config = PERF_COUNT_SW_BPF_OUTPUT,
- };
- int key = 0;
- int pmu_fd;
-
- pmu_fd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, 0);
- if (pmu_fd < 0)
- return pmu_fd;
- bpf_map_update_elem(mapfd, &key, &pmu_fd, BPF_ANY);
-
- ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
- return pmu_fd;
-}
-
int main(int argc, char **argv)
{
const char *file = "test_tcpnotify_kern.o";
- int prog_fd, map_fd, perf_event_fd;
+ struct bpf_map *perf_map, *global_map;
+ struct perf_buffer_opts pb_opts = {};
struct tcpnotify_globals g = {0};
+ struct perf_buffer *pb = NULL;
const char *cg_path = "/foo";
+ int prog_fd, rv, cg_fd = -1;
int error = EXIT_FAILURE;
struct bpf_object *obj;
- int cg_fd = -1;
- __u32 key = 0;
- int rv;
char test_script[80];
- int pmu_fd;
cpu_set_t cpuset;
+ __u32 key = 0;
CPU_ZERO(&cpuset);
CPU_SET(0, &cpuset);
@@ -133,19 +108,24 @@ int main(int argc, char **argv)
goto err;
}
- perf_event_fd = bpf_find_map(__func__, obj, "perf_event_map");
- if (perf_event_fd < 0)
+ perf_map = bpf_object__find_map_by_name(obj, "perf_event_map");
+ if (!perf_map) {
+ printf("FAIL:map '%s' not found\n", "perf_event_map");
goto err;
+ }
- map_fd = bpf_find_map(__func__, obj, "global_map");
- if (map_fd < 0)
- goto err;
+ global_map = bpf_object__find_map_by_name(obj, "global_map");
+ if (!global_map) {
+ printf("FAIL:map '%s' not found\n", "global_map");
+ return -1;
+ }
- pmu_fd = setup_bpf_perf_event(perf_event_fd);
- if (pmu_fd < 0 || perf_event_mmap(pmu_fd) < 0)
+ pb_opts.sample_cb = dummyfn;
+ pb = perf_buffer__new(bpf_map__fd(perf_map), 8, &pb_opts);
+ if (IS_ERR(pb))
goto err;
- pthread_create(&tid, NULL, poller_thread, (void *)&pmu_fd);
+ pthread_create(&tid, NULL, poller_thread, pb);
sprintf(test_script,
"iptables -A INPUT -p tcp --dport %d -j DROP",
@@ -162,7 +142,7 @@ int main(int argc, char **argv)
TESTPORT);
system(test_script);
- rv = bpf_map_lookup_elem(map_fd, &key, &g);
+ rv = bpf_map_lookup_elem(bpf_map__fd(global_map), &key, &g);
if (rv != 0) {
printf("FAILED: bpf_map_lookup_elem returns %d\n", rv);
goto err;
@@ -182,5 +162,7 @@ err:
bpf_prog_detach(cg_fd, BPF_CGROUP_SOCK_OPS);
close(cg_fd);
cleanup_cgroup_environment();
+ if (!IS_ERR_OR_NULL(pb))
+ perf_buffer__free(pb);
return error;
}
diff --git a/tools/testing/selftests/bpf/test_tunnel.sh b/tools/testing/selftests/bpf/test_tunnel.sh
index 546aee3e9fb4..bd12ec97a44d 100755
--- a/tools/testing/selftests/bpf/test_tunnel.sh
+++ b/tools/testing/selftests/bpf/test_tunnel.sh
@@ -696,30 +696,57 @@ check_err()
bpf_tunnel_test()
{
+ local errors=0
+
echo "Testing GRE tunnel..."
test_gre
+ errors=$(( $errors + $? ))
+
echo "Testing IP6GRE tunnel..."
test_ip6gre
+ errors=$(( $errors + $? ))
+
echo "Testing IP6GRETAP tunnel..."
test_ip6gretap
+ errors=$(( $errors + $? ))
+
echo "Testing ERSPAN tunnel..."
test_erspan v2
+ errors=$(( $errors + $? ))
+
echo "Testing IP6ERSPAN tunnel..."
test_ip6erspan v2
+ errors=$(( $errors + $? ))
+
echo "Testing VXLAN tunnel..."
test_vxlan
+ errors=$(( $errors + $? ))
+
echo "Testing IP6VXLAN tunnel..."
test_ip6vxlan
+ errors=$(( $errors + $? ))
+
echo "Testing GENEVE tunnel..."
test_geneve
+ errors=$(( $errors + $? ))
+
echo "Testing IP6GENEVE tunnel..."
test_ip6geneve
+ errors=$(( $errors + $? ))
+
echo "Testing IPIP tunnel..."
test_ipip
+ errors=$(( $errors + $? ))
+
echo "Testing IPIP6 tunnel..."
test_ipip6
+ errors=$(( $errors + $? ))
+
echo "Testing IPSec tunnel..."
test_xfrm_tunnel
+ errors=$(( $errors + $? ))
+
+ return $errors
}
trap cleanup 0 3 6
@@ -728,4 +755,9 @@ trap cleanup_exit 2 9
cleanup
bpf_tunnel_test
+if [ $? -ne 0 ]; then
+ echo -e "$(basename $0): ${RED}FAIL${NC}"
+ exit 1
+fi
+echo -e "$(basename $0): ${GREEN}PASS${NC}"
exit 0
diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c
index ccd896b98cac..d27fd929abb9 100644
--- a/tools/testing/selftests/bpf/test_verifier.c
+++ b/tools/testing/selftests/bpf/test_verifier.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Testsuite for eBPF verifier
*
* Copyright (c) 2014 PLUMgrid, http://plumgrid.com
* Copyright (c) 2017 Facebook
* Copyright (c) 2018 Covalent IO, Inc. http://covalent.io
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
*/
#include <endian.h>
@@ -53,7 +50,7 @@
#define MAX_INSNS BPF_MAXINSNS
#define MAX_TEST_INSNS 1000000
#define MAX_FIXUPS 8
-#define MAX_NR_MAPS 18
+#define MAX_NR_MAPS 19
#define MAX_TEST_RUNS 8
#define POINTER_VALUE 0xcafe4all
#define TEST_DATA_LEN 64
@@ -64,6 +61,7 @@
#define UNPRIV_SYSCTL "kernel/unprivileged_bpf_disabled"
static bool unpriv_disabled = false;
static int skips;
+static bool verbose = false;
struct bpf_test {
const char *descr;
@@ -87,27 +85,34 @@ struct bpf_test {
int fixup_map_array_wo[MAX_FIXUPS];
int fixup_map_array_small[MAX_FIXUPS];
int fixup_sk_storage_map[MAX_FIXUPS];
+ int fixup_map_event_output[MAX_FIXUPS];
const char *errstr;
const char *errstr_unpriv;
- uint32_t retval, retval_unpriv, insn_processed;
+ uint32_t insn_processed;
int prog_len;
enum {
UNDEF,
ACCEPT,
- REJECT
+ REJECT,
+ VERBOSE_ACCEPT,
} result, result_unpriv;
enum bpf_prog_type prog_type;
uint8_t flags;
- __u8 data[TEST_DATA_LEN];
void (*fill_helper)(struct bpf_test *self);
uint8_t runs;
- struct {
- uint32_t retval, retval_unpriv;
- union {
- __u8 data[TEST_DATA_LEN];
- __u64 data64[TEST_DATA_LEN / 8];
- };
- } retvals[MAX_TEST_RUNS];
+#define bpf_testdata_struct_t \
+ struct { \
+ uint32_t retval, retval_unpriv; \
+ union { \
+ __u8 data[TEST_DATA_LEN]; \
+ __u64 data64[TEST_DATA_LEN / 8]; \
+ }; \
+ }
+ union {
+ bpf_testdata_struct_t;
+ bpf_testdata_struct_t retvals[MAX_TEST_RUNS];
+ };
+ enum bpf_attach_type expected_attach_type;
};
/* Note we want this to be 64 bit aligned so that the end of our array is
@@ -138,32 +143,36 @@ static void bpf_fill_ld_abs_vlan_push_pop(struct bpf_test *self)
loop:
for (j = 0; j < PUSH_CNT; j++) {
insn[i++] = BPF_LD_ABS(BPF_B, 0);
- insn[i] = BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0x34, len - i - 2);
+ /* jump to error label */
+ insn[i] = BPF_JMP32_IMM(BPF_JNE, BPF_REG_0, 0x34, len - i - 3);
i++;
insn[i++] = BPF_MOV64_REG(BPF_REG_1, BPF_REG_6);
insn[i++] = BPF_MOV64_IMM(BPF_REG_2, 1);
insn[i++] = BPF_MOV64_IMM(BPF_REG_3, 2);
insn[i++] = BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
BPF_FUNC_skb_vlan_push),
- insn[i] = BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, len - i - 2);
+ insn[i] = BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, len - i - 3);
i++;
}
for (j = 0; j < PUSH_CNT; j++) {
insn[i++] = BPF_LD_ABS(BPF_B, 0);
- insn[i] = BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0x34, len - i - 2);
+ insn[i] = BPF_JMP32_IMM(BPF_JNE, BPF_REG_0, 0x34, len - i - 3);
i++;
insn[i++] = BPF_MOV64_REG(BPF_REG_1, BPF_REG_6);
insn[i++] = BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
BPF_FUNC_skb_vlan_pop),
- insn[i] = BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, len - i - 2);
+ insn[i] = BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, len - i - 3);
i++;
}
if (++k < 5)
goto loop;
- for (; i < len - 1; i++)
- insn[i] = BPF_ALU32_IMM(BPF_MOV, BPF_REG_0, 0xbef);
+ for (; i < len - 3; i++)
+ insn[i] = BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 0xbef);
+ insn[len - 3] = BPF_JMP_A(1);
+ /* error label */
+ insn[len - 2] = BPF_MOV32_IMM(BPF_REG_0, 0);
insn[len - 1] = BPF_EXIT_INSN();
self->prog_len = len;
}
@@ -171,8 +180,13 @@ loop:
static void bpf_fill_jump_around_ld_abs(struct bpf_test *self)
{
struct bpf_insn *insn = self->fill_insns;
- /* jump range is limited to 16 bit. every ld_abs is replaced by 6 insns */
- unsigned int len = (1 << 15) / 6;
+ /* jump range is limited to 16 bit. every ld_abs is replaced by 6 insns,
+ * but on arches like arm, ppc etc, there will be one BPF_ZEXT inserted
+ * to extend the error value of the inlined ld_abs sequence which then
+ * contains 7 insns. so, set the dividend to 7 so the testcase could
+ * work on all arches.
+ */
+ unsigned int len = (1 << 15) / 7;
int i = 0;
insn[i++] = BPF_MOV64_REG(BPF_REG_6, BPF_REG_1);
@@ -210,33 +224,35 @@ static void bpf_fill_rand_ld_dw(struct bpf_test *self)
self->retval = (uint32_t)res;
}
-/* test the sequence of 1k jumps */
+#define MAX_JMP_SEQ 8192
+
+/* test the sequence of 8k jumps */
static void bpf_fill_scale1(struct bpf_test *self)
{
struct bpf_insn *insn = self->fill_insns;
int i = 0, k = 0;
insn[i++] = BPF_MOV64_REG(BPF_REG_6, BPF_REG_1);
- /* test to check that the sequence of 1024 jumps is acceptable */
- while (k++ < 1024) {
+ /* test to check that the long sequence of jumps is acceptable */
+ while (k++ < MAX_JMP_SEQ) {
insn[i++] = BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
BPF_FUNC_get_prandom_u32);
- insn[i++] = BPF_JMP_IMM(BPF_JGT, BPF_REG_0, bpf_semi_rand_get(), 2);
+ insn[i++] = BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, bpf_semi_rand_get(), 2);
insn[i++] = BPF_MOV64_REG(BPF_REG_1, BPF_REG_10);
insn[i++] = BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6,
-8 * (k % 64 + 1));
}
- /* every jump adds 1024 steps to insn_processed, so to stay exactly
- * within 1m limit add MAX_TEST_INSNS - 1025 MOVs and 1 EXIT
+ /* is_state_visited() doesn't allocate state for pruning for every jump.
+ * Hence multiply jmps by 4 to accommodate that heuristic
*/
- while (i < MAX_TEST_INSNS - 1025)
- insn[i++] = BPF_ALU32_IMM(BPF_MOV, BPF_REG_0, 42);
+ while (i < MAX_TEST_INSNS - MAX_JMP_SEQ * 4)
+ insn[i++] = BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 42);
insn[i] = BPF_EXIT_INSN();
self->prog_len = i + 1;
self->retval = 42;
}
-/* test the sequence of 1k jumps in inner most function (function depth 8)*/
+/* test the sequence of 8k jumps in inner most function (function depth 8)*/
static void bpf_fill_scale2(struct bpf_test *self)
{
struct bpf_insn *insn = self->fill_insns;
@@ -248,20 +264,18 @@ static void bpf_fill_scale2(struct bpf_test *self)
insn[i++] = BPF_EXIT_INSN();
}
insn[i++] = BPF_MOV64_REG(BPF_REG_6, BPF_REG_1);
- /* test to check that the sequence of 1024 jumps is acceptable */
- while (k++ < 1024) {
+ /* test to check that the long sequence of jumps is acceptable */
+ k = 0;
+ while (k++ < MAX_JMP_SEQ) {
insn[i++] = BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
BPF_FUNC_get_prandom_u32);
- insn[i++] = BPF_JMP_IMM(BPF_JGT, BPF_REG_0, bpf_semi_rand_get(), 2);
+ insn[i++] = BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, bpf_semi_rand_get(), 2);
insn[i++] = BPF_MOV64_REG(BPF_REG_1, BPF_REG_10);
insn[i++] = BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6,
-8 * (k % (64 - 4 * FUNC_NEST) + 1));
}
- /* every jump adds 1024 steps to insn_processed, so to stay exactly
- * within 1m limit add MAX_TEST_INSNS - 1025 MOVs and 1 EXIT
- */
- while (i < MAX_TEST_INSNS - 1025)
- insn[i++] = BPF_ALU32_IMM(BPF_MOV, BPF_REG_0, 42);
+ while (i < MAX_TEST_INSNS - MAX_JMP_SEQ * 4)
+ insn[i++] = BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 42);
insn[i] = BPF_EXIT_INSN();
self->prog_len = i + 1;
self->retval = 42;
@@ -621,6 +635,7 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type,
int *fixup_map_array_wo = test->fixup_map_array_wo;
int *fixup_map_array_small = test->fixup_map_array_small;
int *fixup_sk_storage_map = test->fixup_sk_storage_map;
+ int *fixup_map_event_output = test->fixup_map_event_output;
if (test->fill_helper) {
test->fill_insns = calloc(MAX_TEST_INSNS, sizeof(struct bpf_insn));
@@ -782,6 +797,14 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type,
fixup_sk_storage_map++;
} while (*fixup_sk_storage_map);
}
+ if (*fixup_map_event_output) {
+ map_fds[18] = __create_map(BPF_MAP_TYPE_PERF_EVENT_ARRAY,
+ sizeof(int), sizeof(int), 1, 0);
+ do {
+ prog[*fixup_map_event_output].imm = map_fds[18];
+ fixup_map_event_output++;
+ } while (*fixup_map_event_output);
+ }
}
static int set_admin(bool admin)
@@ -838,12 +861,43 @@ static int do_prog_test_run(int fd_prog, bool unpriv, uint32_t expected_val,
return 0;
}
+static bool cmp_str_seq(const char *log, const char *exp)
+{
+ char needle[80];
+ const char *p, *q;
+ int len;
+
+ do {
+ p = strchr(exp, '\t');
+ if (!p)
+ p = exp + strlen(exp);
+
+ len = p - exp;
+ if (len >= sizeof(needle) || !len) {
+ printf("FAIL\nTestcase bug\n");
+ return false;
+ }
+ strncpy(needle, exp, len);
+ needle[len] = 0;
+ q = strstr(log, needle);
+ if (!q) {
+ printf("FAIL\nUnexpected verifier log in successful load!\n"
+ "EXP: %s\nRES:\n", needle);
+ return false;
+ }
+ log = q + len;
+ exp = p + 1;
+ } while (*p);
+ return true;
+}
+
static void do_test_single(struct bpf_test *test, bool unpriv,
int *passes, int *errors)
{
int fd_prog, expected_ret, alignment_prevented_execution;
int prog_len, prog_type = test->prog_type;
struct bpf_insn *prog = test->insns;
+ struct bpf_load_program_attr attr;
int run_errs, run_successes;
int map_fds[MAX_NR_MAPS];
const char *expected_err;
@@ -870,27 +924,37 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
if (fixup_skips != skips)
return;
- pflags = 0;
+ pflags = BPF_F_TEST_RND_HI32;
if (test->flags & F_LOAD_WITH_STRICT_ALIGNMENT)
pflags |= BPF_F_STRICT_ALIGNMENT;
if (test->flags & F_NEEDS_EFFICIENT_UNALIGNED_ACCESS)
pflags |= BPF_F_ANY_ALIGNMENT;
- fd_prog = bpf_verify_program(prog_type, prog, prog_len, pflags,
- "GPL", 0, bpf_vlog, sizeof(bpf_vlog), 4);
- if (fd_prog < 0 && !bpf_probe_prog_type(prog_type, 0)) {
- printf("SKIP (unsupported program type %d)\n", prog_type);
- skips++;
- goto close_fds;
- }
+ if (test->flags & ~3)
+ pflags |= test->flags;
expected_ret = unpriv && test->result_unpriv != UNDEF ?
test->result_unpriv : test->result;
expected_err = unpriv && test->errstr_unpriv ?
test->errstr_unpriv : test->errstr;
+ memset(&attr, 0, sizeof(attr));
+ attr.prog_type = prog_type;
+ attr.expected_attach_type = test->expected_attach_type;
+ attr.insns = prog;
+ attr.insns_cnt = prog_len;
+ attr.license = "GPL";
+ attr.log_level = verbose || expected_ret == VERBOSE_ACCEPT ? 1 : 4;
+ attr.prog_flags = pflags;
+
+ fd_prog = bpf_load_program_xattr(&attr, bpf_vlog, sizeof(bpf_vlog));
+ if (fd_prog < 0 && !bpf_probe_prog_type(prog_type, 0)) {
+ printf("SKIP (unsupported program type %d)\n", prog_type);
+ skips++;
+ goto close_fds;
+ }
alignment_prevented_execution = 0;
- if (expected_ret == ACCEPT) {
+ if (expected_ret == ACCEPT || expected_ret == VERBOSE_ACCEPT) {
if (fd_prog < 0) {
printf("FAIL\nFailed to load prog '%s'!\n",
strerror(errno));
@@ -901,12 +965,15 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
(test->flags & F_NEEDS_EFFICIENT_UNALIGNED_ACCESS))
alignment_prevented_execution = 1;
#endif
+ if (expected_ret == VERBOSE_ACCEPT && !cmp_str_seq(bpf_vlog, expected_err)) {
+ goto fail_log;
+ }
} else {
if (fd_prog >= 0) {
printf("FAIL\nUnexpected success to load!\n");
goto fail_log;
}
- if (!strstr(bpf_vlog, expected_err)) {
+ if (!expected_err || !strstr(bpf_vlog, expected_err)) {
printf("FAIL\nUnexpected error message!\n\tEXP: %s\n\tRES: %s\n",
expected_err, bpf_vlog);
goto fail_log;
@@ -926,23 +993,17 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
}
}
+ if (verbose)
+ printf(", verifier log:\n%s", bpf_vlog);
+
run_errs = 0;
run_successes = 0;
if (!alignment_prevented_execution && fd_prog >= 0) {
uint32_t expected_val;
int i;
- if (!test->runs) {
- expected_val = unpriv && test->retval_unpriv ?
- test->retval_unpriv : test->retval;
-
- err = do_prog_test_run(fd_prog, unpriv, expected_val,
- test->data, sizeof(test->data));
- if (err)
- run_errs++;
- else
- run_successes++;
- }
+ if (!test->runs)
+ test->runs = 1;
for (i = 0; i < test->runs; i++) {
if (unpriv && test->retvals[i].retval_unpriv)
@@ -1075,17 +1136,24 @@ int main(int argc, char **argv)
{
unsigned int from = 0, to = ARRAY_SIZE(tests);
bool unpriv = !is_admin();
+ int arg = 1;
+
+ if (argc > 1 && strcmp(argv[1], "-v") == 0) {
+ arg++;
+ verbose = true;
+ argc--;
+ }
if (argc == 3) {
- unsigned int l = atoi(argv[argc - 2]);
- unsigned int u = atoi(argv[argc - 1]);
+ unsigned int l = atoi(argv[arg]);
+ unsigned int u = atoi(argv[arg + 1]);
if (l < to && u < to) {
from = l;
to = u + 1;
}
} else if (argc == 2) {
- unsigned int t = atoi(argv[argc - 1]);
+ unsigned int t = atoi(argv[arg]);
if (t < to) {
from = t;
diff --git a/tools/testing/selftests/bpf/test_xdp_veth.sh b/tools/testing/selftests/bpf/test_xdp_veth.sh
new file mode 100755
index 000000000000..ba8ffcdaac30
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_xdp_veth.sh
@@ -0,0 +1,118 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+#
+# Create 3 namespaces with 3 veth peers, and
+# forward packets in-between using native XDP
+#
+# XDP_TX
+# NS1(veth11) NS2(veth22) NS3(veth33)
+# | | |
+# | | |
+# (veth1, (veth2, (veth3,
+# id:111) id:122) id:133)
+# ^ | ^ | ^ |
+# | | XDP_REDIRECT | | XDP_REDIRECT | |
+# | ------------------ ------------------ |
+# -----------------------------------------
+# XDP_REDIRECT
+
+# Kselftest framework requirement - SKIP code is 4.
+ksft_skip=4
+
+TESTNAME=xdp_veth
+BPF_FS=$(awk '$3 == "bpf" {print $2; exit}' /proc/mounts)
+BPF_DIR=$BPF_FS/test_$TESTNAME
+
+_cleanup()
+{
+ set +e
+ ip link del veth1 2> /dev/null
+ ip link del veth2 2> /dev/null
+ ip link del veth3 2> /dev/null
+ ip netns del ns1 2> /dev/null
+ ip netns del ns2 2> /dev/null
+ ip netns del ns3 2> /dev/null
+ rm -rf $BPF_DIR 2> /dev/null
+}
+
+cleanup_skip()
+{
+ echo "selftests: $TESTNAME [SKIP]"
+ _cleanup
+
+ exit $ksft_skip
+}
+
+cleanup()
+{
+ if [ "$?" = 0 ]; then
+ echo "selftests: $TESTNAME [PASS]"
+ else
+ echo "selftests: $TESTNAME [FAILED]"
+ fi
+ _cleanup
+}
+
+if [ $(id -u) -ne 0 ]; then
+ echo "selftests: $TESTNAME [SKIP] Need root privileges"
+ exit $ksft_skip
+fi
+
+if ! ip link set dev lo xdp off > /dev/null 2>&1; then
+ echo "selftests: $TESTNAME [SKIP] Could not run test without the ip xdp support"
+ exit $ksft_skip
+fi
+
+if [ -z "$BPF_FS" ]; then
+ echo "selftests: $TESTNAME [SKIP] Could not run test without bpffs mounted"
+ exit $ksft_skip
+fi
+
+if ! bpftool version > /dev/null 2>&1; then
+ echo "selftests: $TESTNAME [SKIP] Could not run test without bpftool"
+ exit $ksft_skip
+fi
+
+set -e
+
+trap cleanup_skip EXIT
+
+ip netns add ns1
+ip netns add ns2
+ip netns add ns3
+
+ip link add veth1 index 111 type veth peer name veth11 netns ns1
+ip link add veth2 index 122 type veth peer name veth22 netns ns2
+ip link add veth3 index 133 type veth peer name veth33 netns ns3
+
+ip link set veth1 up
+ip link set veth2 up
+ip link set veth3 up
+
+ip -n ns1 addr add 10.1.1.11/24 dev veth11
+ip -n ns3 addr add 10.1.1.33/24 dev veth33
+
+ip -n ns1 link set dev veth11 up
+ip -n ns2 link set dev veth22 up
+ip -n ns3 link set dev veth33 up
+
+mkdir $BPF_DIR
+bpftool prog loadall \
+ xdp_redirect_map.o $BPF_DIR/progs type xdp \
+ pinmaps $BPF_DIR/maps
+bpftool map update pinned $BPF_DIR/maps/tx_port key 0 0 0 0 value 122 0 0 0
+bpftool map update pinned $BPF_DIR/maps/tx_port key 1 0 0 0 value 133 0 0 0
+bpftool map update pinned $BPF_DIR/maps/tx_port key 2 0 0 0 value 111 0 0 0
+ip link set dev veth1 xdp pinned $BPF_DIR/progs/redirect_map_0
+ip link set dev veth2 xdp pinned $BPF_DIR/progs/redirect_map_1
+ip link set dev veth3 xdp pinned $BPF_DIR/progs/redirect_map_2
+
+ip -n ns1 link set dev veth11 xdp obj xdp_dummy.o sec xdp_dummy
+ip -n ns2 link set dev veth22 xdp obj xdp_tx.o sec tx
+ip -n ns3 link set dev veth33 xdp obj xdp_dummy.o sec xdp_dummy
+
+trap cleanup EXIT
+
+ip netns exec ns1 ping -c 1 -W 1 10.1.1.33
+
+exit 0
diff --git a/tools/testing/selftests/bpf/test_xdp_vlan.sh b/tools/testing/selftests/bpf/test_xdp_vlan.sh
index 51a3a31d1aac..bb8b0da91686 100755
--- a/tools/testing/selftests/bpf/test_xdp_vlan.sh
+++ b/tools/testing/selftests/bpf/test_xdp_vlan.sh
@@ -1,6 +1,14 @@
#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Author: Jesper Dangaard Brouer <hawk@kernel.org>
-TESTNAME=xdp_vlan
+# Allow wrapper scripts to name test
+if [ -z "$TESTNAME" ]; then
+ TESTNAME=xdp_vlan
+fi
+
+# Default XDP mode
+XDP_MODE=xdpgeneric
usage() {
echo "Testing XDP + TC eBPF VLAN manipulations: $TESTNAME"
@@ -9,9 +17,23 @@ usage() {
echo " -v | --verbose : Verbose"
echo " --flush : Flush before starting (e.g. after --interactive)"
echo " --interactive : Keep netns setup running after test-run"
+ echo " --mode=XXX : Choose XDP mode (xdp | xdpgeneric | xdpdrv)"
echo ""
}
+valid_xdp_mode()
+{
+ local mode=$1
+
+ case "$mode" in
+ xdpgeneric | xdpdrv | xdp)
+ return 0
+ ;;
+ *)
+ return 1
+ esac
+}
+
cleanup()
{
local status=$?
@@ -37,7 +59,7 @@ cleanup()
# Using external program "getopt" to get --long-options
OPTIONS=$(getopt -o hvfi: \
- --long verbose,flush,help,interactive,debug -- "$@")
+ --long verbose,flush,help,interactive,debug,mode: -- "$@")
if (( $? != 0 )); then
usage
echo "selftests: $TESTNAME [FAILED] Error calling getopt, unknown option?"
@@ -60,6 +82,11 @@ while true; do
cleanup
shift
;;
+ --mode )
+ shift
+ XDP_MODE=$1
+ shift
+ ;;
-- )
shift
break
@@ -81,8 +108,14 @@ if [ "$EUID" -ne 0 ]; then
exit 1
fi
-ip link set dev lo xdp off 2>/dev/null > /dev/null
-if [ $? -ne 0 ];then
+valid_xdp_mode $XDP_MODE
+if [ $? -ne 0 ]; then
+ echo "selftests: $TESTNAME [FAILED] unknown XDP mode ($XDP_MODE)"
+ exit 1
+fi
+
+ip link set dev lo xdpgeneric off 2>/dev/null > /dev/null
+if [ $? -ne 0 ]; then
echo "selftests: $TESTNAME [SKIP] need ip xdp support"
exit 0
fi
@@ -155,7 +188,7 @@ ip netns exec ns2 ip link set lo up
# At this point, the hosts cannot reach each-other,
# because ns2 are using VLAN tags on the packets.
-ip netns exec ns2 sh -c 'ping -W 1 -c 1 100.64.41.1 || echo "Okay ping fails"'
+ip netns exec ns2 sh -c 'ping -W 1 -c 1 100.64.41.1 || echo "Success: First ping must fail"'
# Now we can use the test_xdp_vlan.c program to pop/push these VLAN tags
@@ -166,7 +199,7 @@ export FILE=test_xdp_vlan.o
# First test: Remove VLAN by setting VLAN ID 0, using "xdp_vlan_change"
export XDP_PROG=xdp_vlan_change
-ip netns exec ns1 ip link set $DEVNS1 xdp object $FILE section $XDP_PROG
+ip netns exec ns1 ip link set $DEVNS1 $XDP_MODE object $FILE section $XDP_PROG
# In ns1: egress use TC to add back VLAN tag 4011
# (del cmd)
@@ -177,8 +210,8 @@ ip netns exec ns1 tc filter add dev $DEVNS1 egress \
prio 1 handle 1 bpf da obj $FILE sec tc_vlan_push
# Now the namespaces can reach each-other, test with ping:
-ip netns exec ns2 ping -W 2 -c 3 $IPADDR1
-ip netns exec ns1 ping -W 2 -c 3 $IPADDR2
+ip netns exec ns2 ping -i 0.2 -W 2 -c 2 $IPADDR1
+ip netns exec ns1 ping -i 0.2 -W 2 -c 2 $IPADDR2
# Second test: Replace xdp prog, that fully remove vlan header
#
@@ -187,9 +220,9 @@ ip netns exec ns1 ping -W 2 -c 3 $IPADDR2
# ETH_P_8021Q indication, and this cause overwriting of our changes.
#
export XDP_PROG=xdp_vlan_remove_outer2
-ip netns exec ns1 ip link set $DEVNS1 xdp off
-ip netns exec ns1 ip link set $DEVNS1 xdp object $FILE section $XDP_PROG
+ip netns exec ns1 ip link set $DEVNS1 $XDP_MODE off
+ip netns exec ns1 ip link set $DEVNS1 $XDP_MODE object $FILE section $XDP_PROG
# Now the namespaces should still be able reach each-other, test with ping:
-ip netns exec ns2 ping -W 2 -c 3 $IPADDR1
-ip netns exec ns1 ping -W 2 -c 3 $IPADDR2
+ip netns exec ns2 ping -i 0.2 -W 2 -c 2 $IPADDR1
+ip netns exec ns1 ping -i 0.2 -W 2 -c 2 $IPADDR2
diff --git a/tools/testing/selftests/bpf/test_xdp_vlan_mode_generic.sh b/tools/testing/selftests/bpf/test_xdp_vlan_mode_generic.sh
new file mode 100755
index 000000000000..c515326d6d59
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_xdp_vlan_mode_generic.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# Exit on failure
+set -e
+
+# Wrapper script to test generic-XDP
+export TESTNAME=xdp_vlan_mode_generic
+./test_xdp_vlan.sh --mode=xdpgeneric
diff --git a/tools/testing/selftests/bpf/test_xdp_vlan_mode_native.sh b/tools/testing/selftests/bpf/test_xdp_vlan_mode_native.sh
new file mode 100755
index 000000000000..5cf7ce1f16c1
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_xdp_vlan_mode_native.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# Exit on failure
+set -e
+
+# Wrapper script to test native-XDP
+export TESTNAME=xdp_vlan_mode_native
+./test_xdp_vlan.sh --mode=xdpdrv
diff --git a/tools/testing/selftests/bpf/test_xdping.sh b/tools/testing/selftests/bpf/test_xdping.sh
new file mode 100755
index 000000000000..c2f0ddb45531
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_xdping.sh
@@ -0,0 +1,99 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# xdping tests
+# Here we setup and teardown configuration required to run
+# xdping, exercising its options.
+#
+# Setup is similar to test_tunnel tests but without the tunnel.
+#
+# Topology:
+# ---------
+# root namespace | tc_ns0 namespace
+# |
+# ---------- | ----------
+# | veth1 | --------- | veth0 |
+# ---------- peer ----------
+#
+# Device Configuration
+# --------------------
+# Root namespace with BPF
+# Device names and addresses:
+# veth1 IP: 10.1.1.200
+# xdp added to veth1, xdpings originate from here.
+#
+# Namespace tc_ns0 with BPF
+# Device names and addresses:
+# veth0 IPv4: 10.1.1.100
+# For some tests xdping run in server mode here.
+#
+
+readonly TARGET_IP="10.1.1.100"
+readonly TARGET_NS="xdp_ns0"
+
+readonly LOCAL_IP="10.1.1.200"
+
+setup()
+{
+ ip netns add $TARGET_NS
+ ip link add veth0 type veth peer name veth1
+ ip link set veth0 netns $TARGET_NS
+ ip netns exec $TARGET_NS ip addr add ${TARGET_IP}/24 dev veth0
+ ip addr add ${LOCAL_IP}/24 dev veth1
+ ip netns exec $TARGET_NS ip link set veth0 up
+ ip link set veth1 up
+}
+
+cleanup()
+{
+ set +e
+ ip netns delete $TARGET_NS 2>/dev/null
+ ip link del veth1 2>/dev/null
+ if [[ $server_pid -ne 0 ]]; then
+ kill -TERM $server_pid
+ fi
+}
+
+test()
+{
+ client_args="$1"
+ server_args="$2"
+
+ echo "Test client args '$client_args'; server args '$server_args'"
+
+ server_pid=0
+ if [[ -n "$server_args" ]]; then
+ ip netns exec $TARGET_NS ./xdping $server_args &
+ server_pid=$!
+ sleep 10
+ fi
+ ./xdping $client_args $TARGET_IP
+
+ if [[ $server_pid -ne 0 ]]; then
+ kill -TERM $server_pid
+ server_pid=0
+ fi
+
+ echo "Test client args '$client_args'; server args '$server_args': PASS"
+}
+
+set -e
+
+server_pid=0
+
+trap cleanup EXIT
+
+setup
+
+for server_args in "" "-I veth0 -s -S" ; do
+ # client in skb mode
+ client_args="-I veth1 -S"
+ test "$client_args" "$server_args"
+
+ # client with count of 10 RTT measurements.
+ client_args="-I veth1 -S -c 10"
+ test "$client_args" "$server_args"
+done
+
+echo "OK. All tests passed"
+exit 0
diff --git a/tools/testing/selftests/bpf/trace_helpers.c b/tools/testing/selftests/bpf/trace_helpers.c
index 9a9fc6c9b70b..7f989b3e4e22 100644
--- a/tools/testing/selftests/bpf/trace_helpers.c
+++ b/tools/testing/selftests/bpf/trace_helpers.c
@@ -30,9 +30,7 @@ int load_kallsyms(void)
if (!f)
return -ENOENT;
- while (!feof(f)) {
- if (!fgets(buf, sizeof(buf), f))
- break;
+ while (fgets(buf, sizeof(buf), f)) {
if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3)
break;
if (!addr)
@@ -88,128 +86,3 @@ long ksym_get_addr(const char *name)
return 0;
}
-
-static int page_size;
-static int page_cnt = 8;
-static struct perf_event_mmap_page *header;
-
-int perf_event_mmap_header(int fd, struct perf_event_mmap_page **header)
-{
- void *base;
- int mmap_size;
-
- page_size = getpagesize();
- mmap_size = page_size * (page_cnt + 1);
-
- base = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (base == MAP_FAILED) {
- printf("mmap err\n");
- return -1;
- }
-
- *header = base;
- return 0;
-}
-
-int perf_event_mmap(int fd)
-{
- return perf_event_mmap_header(fd, &header);
-}
-
-static int perf_event_poll(int fd)
-{
- struct pollfd pfd = { .fd = fd, .events = POLLIN };
-
- return poll(&pfd, 1, 1000);
-}
-
-struct perf_event_sample {
- struct perf_event_header header;
- __u32 size;
- char data[];
-};
-
-static enum bpf_perf_event_ret
-bpf_perf_event_print(struct perf_event_header *hdr, void *private_data)
-{
- struct perf_event_sample *e = (struct perf_event_sample *)hdr;
- perf_event_print_fn fn = private_data;
- int ret;
-
- if (e->header.type == PERF_RECORD_SAMPLE) {
- ret = fn(e->data, e->size);
- if (ret != LIBBPF_PERF_EVENT_CONT)
- return ret;
- } else if (e->header.type == PERF_RECORD_LOST) {
- struct {
- struct perf_event_header header;
- __u64 id;
- __u64 lost;
- } *lost = (void *) e;
- printf("lost %lld events\n", lost->lost);
- } else {
- printf("unknown event type=%d size=%d\n",
- e->header.type, e->header.size);
- }
-
- return LIBBPF_PERF_EVENT_CONT;
-}
-
-int perf_event_poller(int fd, perf_event_print_fn output_fn)
-{
- enum bpf_perf_event_ret ret;
- void *buf = NULL;
- size_t len = 0;
-
- for (;;) {
- perf_event_poll(fd);
- ret = bpf_perf_event_read_simple(header, page_cnt * page_size,
- page_size, &buf, &len,
- bpf_perf_event_print,
- output_fn);
- if (ret != LIBBPF_PERF_EVENT_CONT)
- break;
- }
- free(buf);
-
- return ret;
-}
-
-int perf_event_poller_multi(int *fds, struct perf_event_mmap_page **headers,
- int num_fds, perf_event_print_fn output_fn)
-{
- enum bpf_perf_event_ret ret;
- struct pollfd *pfds;
- void *buf = NULL;
- size_t len = 0;
- int i;
-
- pfds = calloc(num_fds, sizeof(*pfds));
- if (!pfds)
- return LIBBPF_PERF_EVENT_ERROR;
-
- for (i = 0; i < num_fds; i++) {
- pfds[i].fd = fds[i];
- pfds[i].events = POLLIN;
- }
-
- for (;;) {
- poll(pfds, num_fds, 1000);
- for (i = 0; i < num_fds; i++) {
- if (!pfds[i].revents)
- continue;
-
- ret = bpf_perf_event_read_simple(headers[i],
- page_cnt * page_size,
- page_size, &buf, &len,
- bpf_perf_event_print,
- output_fn);
- if (ret != LIBBPF_PERF_EVENT_CONT)
- break;
- }
- }
- free(buf);
- free(pfds);
-
- return ret;
-}
diff --git a/tools/testing/selftests/bpf/trace_helpers.h b/tools/testing/selftests/bpf/trace_helpers.h
index 18924f23db1b..aa4dcfe18050 100644
--- a/tools/testing/selftests/bpf/trace_helpers.h
+++ b/tools/testing/selftests/bpf/trace_helpers.h
@@ -3,7 +3,6 @@
#define __TRACE_HELPER_H
#include <libbpf.h>
-#include <linux/perf_event.h>
struct ksym {
long addr;
@@ -14,12 +13,4 @@ int load_kallsyms(void);
struct ksym *ksym_search(long key);
long ksym_get_addr(const char *name);
-typedef enum bpf_perf_event_ret (*perf_event_print_fn)(void *data, int size);
-
-int perf_event_mmap(int fd);
-int perf_event_mmap_header(int fd, struct perf_event_mmap_page **header);
-/* return LIBBPF_PERF_EVENT_DONE or LIBBPF_PERF_EVENT_ERROR */
-int perf_event_poller(int fd, perf_event_print_fn output_fn);
-int perf_event_poller_multi(int *fds, struct perf_event_mmap_page **headers,
- int num_fds, perf_event_print_fn output_fn);
#endif
diff --git a/tools/testing/selftests/bpf/verifier/array_access.c b/tools/testing/selftests/bpf/verifier/array_access.c
index bcb83196e459..f3c33e128709 100644
--- a/tools/testing/selftests/bpf/verifier/array_access.c
+++ b/tools/testing/selftests/bpf/verifier/array_access.c
@@ -226,7 +226,7 @@
BPF_LD_MAP_FD(BPF_REG_1, 0),
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1),
- BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0),
+ BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, 0),
BPF_EXIT_INSN(),
},
.fixup_map_array_ro = { 3 },
diff --git a/tools/testing/selftests/bpf/verifier/basic_instr.c b/tools/testing/selftests/bpf/verifier/basic_instr.c
index ed91a7b9a456..071dbc889e8c 100644
--- a/tools/testing/selftests/bpf/verifier/basic_instr.c
+++ b/tools/testing/selftests/bpf/verifier/basic_instr.c
@@ -91,6 +91,91 @@
.result = ACCEPT,
},
{
+ "lsh64 by 0 imm",
+ .insns = {
+ BPF_LD_IMM64(BPF_REG_0, 1),
+ BPF_LD_IMM64(BPF_REG_1, 1),
+ BPF_ALU64_IMM(BPF_LSH, BPF_REG_1, 0),
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 1, 1),
+ BPF_MOV64_IMM(BPF_REG_0, 2),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 1,
+},
+{
+ "rsh64 by 0 imm",
+ .insns = {
+ BPF_LD_IMM64(BPF_REG_0, 1),
+ BPF_LD_IMM64(BPF_REG_1, 0x100000000LL),
+ BPF_ALU64_REG(BPF_MOV, BPF_REG_2, BPF_REG_1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_1, 0),
+ BPF_JMP_REG(BPF_JEQ, BPF_REG_1, BPF_REG_2, 1),
+ BPF_MOV64_IMM(BPF_REG_0, 2),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 1,
+},
+{
+ "arsh64 by 0 imm",
+ .insns = {
+ BPF_LD_IMM64(BPF_REG_0, 1),
+ BPF_LD_IMM64(BPF_REG_1, 0x100000000LL),
+ BPF_ALU64_REG(BPF_MOV, BPF_REG_2, BPF_REG_1),
+ BPF_ALU64_IMM(BPF_ARSH, BPF_REG_1, 0),
+ BPF_JMP_REG(BPF_JEQ, BPF_REG_1, BPF_REG_2, 1),
+ BPF_MOV64_IMM(BPF_REG_0, 2),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 1,
+},
+{
+ "lsh64 by 0 reg",
+ .insns = {
+ BPF_LD_IMM64(BPF_REG_0, 1),
+ BPF_LD_IMM64(BPF_REG_1, 1),
+ BPF_LD_IMM64(BPF_REG_2, 0),
+ BPF_ALU64_REG(BPF_LSH, BPF_REG_1, BPF_REG_2),
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 1, 1),
+ BPF_MOV64_IMM(BPF_REG_0, 2),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 1,
+},
+{
+ "rsh64 by 0 reg",
+ .insns = {
+ BPF_LD_IMM64(BPF_REG_0, 1),
+ BPF_LD_IMM64(BPF_REG_1, 0x100000000LL),
+ BPF_ALU64_REG(BPF_MOV, BPF_REG_2, BPF_REG_1),
+ BPF_LD_IMM64(BPF_REG_3, 0),
+ BPF_ALU64_REG(BPF_RSH, BPF_REG_1, BPF_REG_3),
+ BPF_JMP_REG(BPF_JEQ, BPF_REG_1, BPF_REG_2, 1),
+ BPF_MOV64_IMM(BPF_REG_0, 2),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 1,
+},
+{
+ "arsh64 by 0 reg",
+ .insns = {
+ BPF_LD_IMM64(BPF_REG_0, 1),
+ BPF_LD_IMM64(BPF_REG_1, 0x100000000LL),
+ BPF_ALU64_REG(BPF_MOV, BPF_REG_2, BPF_REG_1),
+ BPF_LD_IMM64(BPF_REG_3, 0),
+ BPF_ALU64_REG(BPF_ARSH, BPF_REG_1, BPF_REG_3),
+ BPF_JMP_REG(BPF_JEQ, BPF_REG_1, BPF_REG_2, 1),
+ BPF_MOV64_IMM(BPF_REG_0, 2),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 1,
+},
+{
"invalid 64-bit BPF_END",
.insns = {
BPF_MOV32_IMM(BPF_REG_0, 0),
diff --git a/tools/testing/selftests/bpf/verifier/calls.c b/tools/testing/selftests/bpf/verifier/calls.c
index 9093a8f64dc6..2d752c4f8d9d 100644
--- a/tools/testing/selftests/bpf/verifier/calls.c
+++ b/tools/testing/selftests/bpf/verifier/calls.c
@@ -215,9 +215,11 @@
BPF_MOV64_IMM(BPF_REG_0, 3),
BPF_JMP_IMM(BPF_JA, 0, 0, -6),
},
- .prog_type = BPF_PROG_TYPE_TRACEPOINT,
- .errstr = "back-edge from insn",
- .result = REJECT,
+ .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
+ .errstr_unpriv = "back-edge from insn",
+ .result_unpriv = REJECT,
+ .result = ACCEPT,
+ .retval = 1,
},
{
"calls: conditional call 4",
@@ -250,22 +252,24 @@
BPF_MOV64_IMM(BPF_REG_0, 3),
BPF_EXIT_INSN(),
},
- .prog_type = BPF_PROG_TYPE_TRACEPOINT,
- .errstr = "back-edge from insn",
- .result = REJECT,
+ .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+ .result = ACCEPT,
+ .retval = 1,
},
{
"calls: conditional call 6",
.insns = {
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2),
- BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, -2),
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, -3),
BPF_EXIT_INSN(),
BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
offsetof(struct __sk_buff, mark)),
BPF_EXIT_INSN(),
},
- .prog_type = BPF_PROG_TYPE_TRACEPOINT,
- .errstr = "back-edge from insn",
+ .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+ .errstr = "infinite loop detected",
.result = REJECT,
},
{
diff --git a/tools/testing/selftests/bpf/verifier/cfg.c b/tools/testing/selftests/bpf/verifier/cfg.c
index 349c0862fb4c..4eb76ed739ce 100644
--- a/tools/testing/selftests/bpf/verifier/cfg.c
+++ b/tools/testing/selftests/bpf/verifier/cfg.c
@@ -41,7 +41,8 @@
BPF_JMP_IMM(BPF_JA, 0, 0, -1),
BPF_EXIT_INSN(),
},
- .errstr = "back-edge",
+ .errstr = "unreachable insn 1",
+ .errstr_unpriv = "back-edge",
.result = REJECT,
},
{
@@ -53,18 +54,20 @@
BPF_JMP_IMM(BPF_JA, 0, 0, -4),
BPF_EXIT_INSN(),
},
- .errstr = "back-edge",
+ .errstr = "unreachable insn 4",
+ .errstr_unpriv = "back-edge",
.result = REJECT,
},
{
"conditional loop",
.insns = {
- BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+ BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
BPF_MOV64_REG(BPF_REG_2, BPF_REG_0),
BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, -3),
BPF_EXIT_INSN(),
},
- .errstr = "back-edge",
+ .errstr = "infinite loop detected",
+ .errstr_unpriv = "back-edge",
.result = REJECT,
},
diff --git a/tools/testing/selftests/bpf/verifier/ctx_skb.c b/tools/testing/selftests/bpf/verifier/ctx_skb.c
index b0fda2877119..d438193804b2 100644
--- a/tools/testing/selftests/bpf/verifier/ctx_skb.c
+++ b/tools/testing/selftests/bpf/verifier/ctx_skb.c
@@ -975,6 +975,17 @@
.prog_type = BPF_PROG_TYPE_CGROUP_SKB,
},
{
+ "read gso_segs from CGROUP_SKB",
+ .insns = {
+ BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
+ offsetof(struct __sk_buff, gso_segs)),
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+},
+{
"write gso_segs from CGROUP_SKB",
.insns = {
BPF_MOV64_IMM(BPF_REG_0, 0),
diff --git a/tools/testing/selftests/bpf/verifier/direct_packet_access.c b/tools/testing/selftests/bpf/verifier/direct_packet_access.c
index d5c596fdc4b9..2c5fbe7bcd27 100644
--- a/tools/testing/selftests/bpf/verifier/direct_packet_access.c
+++ b/tools/testing/selftests/bpf/verifier/direct_packet_access.c
@@ -511,7 +511,8 @@
offsetof(struct __sk_buff, data)),
BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
offsetof(struct __sk_buff, data_end)),
- BPF_MOV64_IMM(BPF_REG_0, 0xffffffff),
+ BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
+ offsetof(struct __sk_buff, mark)),
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8),
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8),
BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffff),
diff --git a/tools/testing/selftests/bpf/verifier/div_overflow.c b/tools/testing/selftests/bpf/verifier/div_overflow.c
index bd3f38dbe796..acab4f00819f 100644
--- a/tools/testing/selftests/bpf/verifier/div_overflow.c
+++ b/tools/testing/selftests/bpf/verifier/div_overflow.c
@@ -29,8 +29,11 @@
"DIV64 overflow, check 1",
.insns = {
BPF_MOV64_IMM(BPF_REG_1, -1),
- BPF_LD_IMM64(BPF_REG_0, LLONG_MIN),
- BPF_ALU64_REG(BPF_DIV, BPF_REG_0, BPF_REG_1),
+ BPF_LD_IMM64(BPF_REG_2, LLONG_MIN),
+ BPF_ALU64_REG(BPF_DIV, BPF_REG_2, BPF_REG_1),
+ BPF_MOV32_IMM(BPF_REG_0, 0),
+ BPF_JMP_REG(BPF_JEQ, BPF_REG_0, BPF_REG_2, 1),
+ BPF_MOV32_IMM(BPF_REG_0, 1),
BPF_EXIT_INSN(),
},
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
@@ -40,8 +43,11 @@
{
"DIV64 overflow, check 2",
.insns = {
- BPF_LD_IMM64(BPF_REG_0, LLONG_MIN),
- BPF_ALU64_IMM(BPF_DIV, BPF_REG_0, -1),
+ BPF_LD_IMM64(BPF_REG_1, LLONG_MIN),
+ BPF_ALU64_IMM(BPF_DIV, BPF_REG_1, -1),
+ BPF_MOV32_IMM(BPF_REG_0, 0),
+ BPF_JMP_REG(BPF_JEQ, BPF_REG_0, BPF_REG_1, 1),
+ BPF_MOV32_IMM(BPF_REG_0, 1),
BPF_EXIT_INSN(),
},
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
diff --git a/tools/testing/selftests/bpf/verifier/event_output.c b/tools/testing/selftests/bpf/verifier/event_output.c
new file mode 100644
index 000000000000..130553e19eca
--- /dev/null
+++ b/tools/testing/selftests/bpf/verifier/event_output.c
@@ -0,0 +1,94 @@
+/* instructions used to output a skb based software event, produced
+ * from code snippet:
+ * struct TMP {
+ * uint64_t tmp;
+ * } tt;
+ * tt.tmp = 5;
+ * bpf_perf_event_output(skb, &connection_tracking_event_map, 0,
+ * &tt, sizeof(tt));
+ * return 1;
+ *
+ * the bpf assembly from llvm is:
+ * 0: b7 02 00 00 05 00 00 00 r2 = 5
+ * 1: 7b 2a f8 ff 00 00 00 00 *(u64 *)(r10 - 8) = r2
+ * 2: bf a4 00 00 00 00 00 00 r4 = r10
+ * 3: 07 04 00 00 f8 ff ff ff r4 += -8
+ * 4: 18 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r2 = 0ll
+ * 6: b7 03 00 00 00 00 00 00 r3 = 0
+ * 7: b7 05 00 00 08 00 00 00 r5 = 8
+ * 8: 85 00 00 00 19 00 00 00 call 25
+ * 9: b7 00 00 00 01 00 00 00 r0 = 1
+ * 10: 95 00 00 00 00 00 00 00 exit
+ *
+ * The reason I put the code here instead of fill_helpers is that map fixup
+ * is against the insns, instead of filled prog.
+ */
+
+#define __PERF_EVENT_INSNS__ \
+ BPF_MOV64_IMM(BPF_REG_2, 5), \
+ BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, -8), \
+ BPF_MOV64_REG(BPF_REG_4, BPF_REG_10), \
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, -8), \
+ BPF_LD_MAP_FD(BPF_REG_2, 0), \
+ BPF_MOV64_IMM(BPF_REG_3, 0), \
+ BPF_MOV64_IMM(BPF_REG_5, 8), \
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, \
+ BPF_FUNC_perf_event_output), \
+ BPF_MOV64_IMM(BPF_REG_0, 1), \
+ BPF_EXIT_INSN(),
+{
+ "perfevent for sockops",
+ .insns = { __PERF_EVENT_INSNS__ },
+ .prog_type = BPF_PROG_TYPE_SOCK_OPS,
+ .fixup_map_event_output = { 4 },
+ .result = ACCEPT,
+ .retval = 1,
+},
+{
+ "perfevent for tc",
+ .insns = { __PERF_EVENT_INSNS__ },
+ .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+ .fixup_map_event_output = { 4 },
+ .result = ACCEPT,
+ .retval = 1,
+},
+{
+ "perfevent for lwt out",
+ .insns = { __PERF_EVENT_INSNS__ },
+ .prog_type = BPF_PROG_TYPE_LWT_OUT,
+ .fixup_map_event_output = { 4 },
+ .result = ACCEPT,
+ .retval = 1,
+},
+{
+ "perfevent for xdp",
+ .insns = { __PERF_EVENT_INSNS__ },
+ .prog_type = BPF_PROG_TYPE_XDP,
+ .fixup_map_event_output = { 4 },
+ .result = ACCEPT,
+ .retval = 1,
+},
+{
+ "perfevent for socket filter",
+ .insns = { __PERF_EVENT_INSNS__ },
+ .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
+ .fixup_map_event_output = { 4 },
+ .result = ACCEPT,
+ .retval = 1,
+},
+{
+ "perfevent for sk_skb",
+ .insns = { __PERF_EVENT_INSNS__ },
+ .prog_type = BPF_PROG_TYPE_SK_SKB,
+ .fixup_map_event_output = { 4 },
+ .result = ACCEPT,
+ .retval = 1,
+},
+{
+ "perfevent for cgroup skb",
+ .insns = { __PERF_EVENT_INSNS__ },
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .fixup_map_event_output = { 4 },
+ .result = ACCEPT,
+ .retval = 1,
+},
diff --git a/tools/testing/selftests/bpf/verifier/helper_access_var_len.c b/tools/testing/selftests/bpf/verifier/helper_access_var_len.c
index 1f39d845c64f..67ab12410050 100644
--- a/tools/testing/selftests/bpf/verifier/helper_access_var_len.c
+++ b/tools/testing/selftests/bpf/verifier/helper_access_var_len.c
@@ -29,9 +29,9 @@
{
"helper access to variable memory: stack, bitwise AND, zero included",
.insns = {
+ BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, 8),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64),
- BPF_MOV64_IMM(BPF_REG_2, 16),
BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -128),
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -128),
BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 64),
@@ -46,9 +46,9 @@
{
"helper access to variable memory: stack, bitwise AND + JMP, wrong max",
.insns = {
+ BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, 8),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64),
- BPF_MOV64_IMM(BPF_REG_2, 16),
BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -128),
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -128),
BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 65),
@@ -122,9 +122,9 @@
{
"helper access to variable memory: stack, JMP, bounds + offset",
.insns = {
+ BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, 8),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64),
- BPF_MOV64_IMM(BPF_REG_2, 16),
BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -128),
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -128),
BPF_JMP_IMM(BPF_JGT, BPF_REG_2, 64, 5),
@@ -143,9 +143,9 @@
{
"helper access to variable memory: stack, JMP, wrong max",
.insns = {
+ BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, 8),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64),
- BPF_MOV64_IMM(BPF_REG_2, 16),
BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -128),
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -128),
BPF_JMP_IMM(BPF_JGT, BPF_REG_2, 65, 4),
@@ -163,9 +163,9 @@
{
"helper access to variable memory: stack, JMP, no max check",
.insns = {
+ BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, 8),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64),
- BPF_MOV64_IMM(BPF_REG_2, 16),
BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -128),
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -128),
BPF_MOV64_IMM(BPF_REG_4, 0),
@@ -183,9 +183,9 @@
{
"helper access to variable memory: stack, JMP, no min check",
.insns = {
+ BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, 8),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64),
- BPF_MOV64_IMM(BPF_REG_2, 16),
BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -128),
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -128),
BPF_JMP_IMM(BPF_JGT, BPF_REG_2, 64, 3),
@@ -201,9 +201,9 @@
{
"helper access to variable memory: stack, JMP (signed), no min check",
.insns = {
+ BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, 8),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64),
- BPF_MOV64_IMM(BPF_REG_2, 16),
BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -128),
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -128),
BPF_JMP_IMM(BPF_JSGT, BPF_REG_2, 64, 3),
@@ -244,6 +244,7 @@
{
"helper access to variable memory: map, JMP, wrong max",
.insns = {
+ BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 8),
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),
@@ -251,7 +252,7 @@
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 10),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
- BPF_MOV64_IMM(BPF_REG_2, sizeof(struct test_val)),
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, -128),
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -128),
BPF_JMP_IMM(BPF_JSGT, BPF_REG_2, sizeof(struct test_val) + 1, 4),
@@ -262,7 +263,7 @@
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
},
- .fixup_map_hash_48b = { 3 },
+ .fixup_map_hash_48b = { 4 },
.errstr = "invalid access to map value, value_size=48 off=0 size=49",
.result = REJECT,
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
@@ -296,6 +297,7 @@
{
"helper access to variable memory: map adjusted, JMP, wrong max",
.insns = {
+ BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 8),
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),
@@ -304,7 +306,7 @@
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 11),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 20),
- BPF_MOV64_IMM(BPF_REG_2, sizeof(struct test_val)),
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, -128),
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -128),
BPF_JMP_IMM(BPF_JSGT, BPF_REG_2, sizeof(struct test_val) - 19, 4),
@@ -315,7 +317,7 @@
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
},
- .fixup_map_hash_48b = { 3 },
+ .fixup_map_hash_48b = { 4 },
.errstr = "R1 min value is outside of the array range",
.result = REJECT,
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
@@ -337,8 +339,8 @@
{
"helper access to variable memory: size > 0 not allowed on NULL (ARG_PTR_TO_MEM_OR_NULL)",
.insns = {
+ BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 0),
BPF_MOV64_IMM(BPF_REG_1, 0),
- BPF_MOV64_IMM(BPF_REG_2, 1),
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, -128),
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -128),
BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 64),
@@ -562,6 +564,7 @@
{
"helper access to variable memory: 8 bytes leak",
.insns = {
+ BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, 8),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64),
BPF_MOV64_IMM(BPF_REG_0, 0),
@@ -572,7 +575,6 @@
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -24),
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16),
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8),
- BPF_MOV64_IMM(BPF_REG_2, 1),
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, -128),
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -128),
BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 63),
diff --git a/tools/testing/selftests/bpf/verifier/loops1.c b/tools/testing/selftests/bpf/verifier/loops1.c
new file mode 100644
index 000000000000..1fc4e61e9f9f
--- /dev/null
+++ b/tools/testing/selftests/bpf/verifier/loops1.c
@@ -0,0 +1,189 @@
+{
+ "bounded loop, count to 4",
+ .insns = {
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+ BPF_JMP_IMM(BPF_JLT, BPF_REG_0, 4, -2),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+ .retval = 4,
+},
+{
+ "bounded loop, count to 20",
+ .insns = {
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 3),
+ BPF_JMP_IMM(BPF_JLT, BPF_REG_0, 20, -2),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+},
+{
+ "bounded loop, count from positive unknown to 4",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_JMP_IMM(BPF_JSLT, BPF_REG_0, 0, 2),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+ BPF_JMP_IMM(BPF_JLT, BPF_REG_0, 4, -2),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+ .retval = 4,
+},
+{
+ "bounded loop, count from totally unknown to 4",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+ BPF_JMP_IMM(BPF_JLT, BPF_REG_0, 4, -2),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+},
+{
+ "bounded loop, count to 4 with equality",
+ .insns = {
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 4, -2),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+},
+{
+ "bounded loop, start in the middle",
+ .insns = {
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_JMP_A(1),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+ BPF_JMP_IMM(BPF_JLT, BPF_REG_0, 4, -2),
+ BPF_EXIT_INSN(),
+ },
+ .result = REJECT,
+ .errstr = "back-edge",
+ .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+ .retval = 4,
+},
+{
+ "bounded loop containing a forward jump",
+ .insns = {
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+ BPF_JMP_REG(BPF_JEQ, BPF_REG_0, BPF_REG_0, 0),
+ BPF_JMP_IMM(BPF_JLT, BPF_REG_0, 4, -3),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+ .retval = 4,
+},
+{
+ "bounded loop that jumps out rather than in",
+ .insns = {
+ BPF_MOV64_IMM(BPF_REG_6, 0),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
+ BPF_JMP_IMM(BPF_JGT, BPF_REG_6, 10000, 2),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_JMP_A(-4),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+},
+{
+ "infinite loop after a conditional jump",
+ .insns = {
+ BPF_MOV64_IMM(BPF_REG_0, 5),
+ BPF_JMP_IMM(BPF_JLT, BPF_REG_0, 4, 2),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+ BPF_JMP_A(-2),
+ BPF_EXIT_INSN(),
+ },
+ .result = REJECT,
+ .errstr = "program is too large",
+ .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+},
+{
+ "bounded recursion",
+ .insns = {
+ BPF_MOV64_IMM(BPF_REG_1, 0),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1),
+ BPF_EXIT_INSN(),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 1),
+ BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
+ BPF_JMP_IMM(BPF_JLT, BPF_REG_1, 4, 1),
+ BPF_EXIT_INSN(),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, -5),
+ BPF_EXIT_INSN(),
+ },
+ .result = REJECT,
+ .errstr = "back-edge",
+ .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+},
+{
+ "infinite loop in two jumps",
+ .insns = {
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_JMP_A(0),
+ BPF_JMP_IMM(BPF_JLT, BPF_REG_0, 4, -2),
+ BPF_EXIT_INSN(),
+ },
+ .result = REJECT,
+ .errstr = "loop detected",
+ .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+},
+{
+ "infinite loop: three-jump trick",
+ .insns = {
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+ BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 1),
+ BPF_JMP_IMM(BPF_JLT, BPF_REG_0, 2, 1),
+ BPF_EXIT_INSN(),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+ BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 1),
+ BPF_JMP_IMM(BPF_JLT, BPF_REG_0, 2, 1),
+ BPF_EXIT_INSN(),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+ BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 1),
+ BPF_JMP_IMM(BPF_JLT, BPF_REG_0, 2, -11),
+ BPF_EXIT_INSN(),
+ },
+ .result = REJECT,
+ .errstr = "loop detected",
+ .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+},
+{
+ "not-taken loop with back jump to 1st insn",
+ .insns = {
+ BPF_MOV64_IMM(BPF_REG_0, 123),
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 4, -2),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .prog_type = BPF_PROG_TYPE_XDP,
+ .retval = 123,
+},
+{
+ "taken loop with back jump to 1st insn",
+ .insns = {
+ BPF_MOV64_IMM(BPF_REG_1, 10),
+ BPF_MOV64_IMM(BPF_REG_2, 0),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 1),
+ BPF_EXIT_INSN(),
+ BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_1),
+ BPF_ALU64_IMM(BPF_SUB, BPF_REG_1, 1),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, -3),
+ BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .prog_type = BPF_PROG_TYPE_XDP,
+ .retval = 55,
+},
diff --git a/tools/testing/selftests/bpf/verifier/precise.c b/tools/testing/selftests/bpf/verifier/precise.c
new file mode 100644
index 000000000000..02151f8c940f
--- /dev/null
+++ b/tools/testing/selftests/bpf/verifier/precise.c
@@ -0,0 +1,194 @@
+{
+ "precise: test 1",
+ .insns = {
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_LD_MAP_FD(BPF_REG_6, 0),
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_FP),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+ BPF_ST_MEM(BPF_DW, BPF_REG_FP, -8, 0),
+ BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+ BPF_EXIT_INSN(),
+
+ BPF_MOV64_REG(BPF_REG_9, BPF_REG_0),
+
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_FP),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+ BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+ BPF_EXIT_INSN(),
+
+ BPF_MOV64_REG(BPF_REG_8, BPF_REG_0),
+
+ BPF_ALU64_REG(BPF_SUB, BPF_REG_9, BPF_REG_8), /* map_value_ptr -= map_value_ptr */
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_9),
+ BPF_JMP_IMM(BPF_JLT, BPF_REG_2, 8, 1),
+ BPF_EXIT_INSN(),
+
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1), /* R2=inv(umin=1, umax=8) */
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_FP),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8),
+ BPF_MOV64_IMM(BPF_REG_3, 0),
+ BPF_EMIT_CALL(BPF_FUNC_probe_read),
+ BPF_EXIT_INSN(),
+ },
+ .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+ .fixup_map_array_48b = { 1 },
+ .result = VERBOSE_ACCEPT,
+ .errstr =
+ "26: (85) call bpf_probe_read#4\
+ last_idx 26 first_idx 20\
+ regs=4 stack=0 before 25\
+ regs=4 stack=0 before 24\
+ regs=4 stack=0 before 23\
+ regs=4 stack=0 before 22\
+ regs=4 stack=0 before 20\
+ parent didn't have regs=4 stack=0 marks\
+ last_idx 19 first_idx 10\
+ regs=4 stack=0 before 19\
+ regs=200 stack=0 before 18\
+ regs=300 stack=0 before 17\
+ regs=201 stack=0 before 15\
+ regs=201 stack=0 before 14\
+ regs=200 stack=0 before 13\
+ regs=200 stack=0 before 12\
+ regs=200 stack=0 before 11\
+ regs=200 stack=0 before 10\
+ parent already had regs=0 stack=0 marks",
+},
+{
+ "precise: test 2",
+ .insns = {
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_LD_MAP_FD(BPF_REG_6, 0),
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_FP),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+ BPF_ST_MEM(BPF_DW, BPF_REG_FP, -8, 0),
+ BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+ BPF_EXIT_INSN(),
+
+ BPF_MOV64_REG(BPF_REG_9, BPF_REG_0),
+
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_FP),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+ BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+ BPF_EXIT_INSN(),
+
+ BPF_MOV64_REG(BPF_REG_8, BPF_REG_0),
+
+ BPF_ALU64_REG(BPF_SUB, BPF_REG_9, BPF_REG_8), /* map_value_ptr -= map_value_ptr */
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_9),
+ BPF_JMP_IMM(BPF_JLT, BPF_REG_2, 8, 1),
+ BPF_EXIT_INSN(),
+
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1), /* R2=inv(umin=1, umax=8) */
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_FP),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8),
+ BPF_MOV64_IMM(BPF_REG_3, 0),
+ BPF_EMIT_CALL(BPF_FUNC_probe_read),
+ BPF_EXIT_INSN(),
+ },
+ .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+ .fixup_map_array_48b = { 1 },
+ .result = VERBOSE_ACCEPT,
+ .flags = BPF_F_TEST_STATE_FREQ,
+ .errstr =
+ "26: (85) call bpf_probe_read#4\
+ last_idx 26 first_idx 22\
+ regs=4 stack=0 before 25\
+ regs=4 stack=0 before 24\
+ regs=4 stack=0 before 23\
+ regs=4 stack=0 before 22\
+ parent didn't have regs=4 stack=0 marks\
+ last_idx 20 first_idx 20\
+ regs=4 stack=0 before 20\
+ parent didn't have regs=4 stack=0 marks\
+ last_idx 19 first_idx 17\
+ regs=4 stack=0 before 19\
+ regs=200 stack=0 before 18\
+ regs=300 stack=0 before 17\
+ parent already had regs=0 stack=0 marks",
+},
+{
+ "precise: cross frame pruning",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_MOV64_IMM(BPF_REG_8, 0),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+ BPF_MOV64_IMM(BPF_REG_8, 1),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_MOV64_IMM(BPF_REG_9, 0),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+ BPF_MOV64_IMM(BPF_REG_9, 1),
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 4),
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_8, 1, 1),
+ BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_2, 0),
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .prog_type = BPF_PROG_TYPE_XDP,
+ .flags = BPF_F_TEST_STATE_FREQ,
+ .errstr = "!read_ok",
+ .result = REJECT,
+},
+{
+ "precise: ST insn causing spi > allocated_stack",
+ .insns = {
+ BPF_MOV64_REG(BPF_REG_3, BPF_REG_10),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_3, 123, 0),
+ BPF_ST_MEM(BPF_DW, BPF_REG_3, -8, 0),
+ BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -8),
+ BPF_MOV64_IMM(BPF_REG_0, -1),
+ BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .prog_type = BPF_PROG_TYPE_XDP,
+ .flags = BPF_F_TEST_STATE_FREQ,
+ .errstr = "5: (2d) if r4 > r0 goto pc+0\
+ last_idx 5 first_idx 5\
+ parent didn't have regs=10 stack=0 marks\
+ last_idx 4 first_idx 2\
+ regs=10 stack=0 before 4\
+ regs=10 stack=0 before 3\
+ regs=0 stack=1 before 2\
+ last_idx 5 first_idx 5\
+ parent didn't have regs=1 stack=0 marks",
+ .result = VERBOSE_ACCEPT,
+ .retval = -1,
+},
+{
+ "precise: STX insn causing spi > allocated_stack",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_MOV64_REG(BPF_REG_3, BPF_REG_10),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_3, 123, 0),
+ BPF_STX_MEM(BPF_DW, BPF_REG_3, BPF_REG_0, -8),
+ BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -8),
+ BPF_MOV64_IMM(BPF_REG_0, -1),
+ BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .prog_type = BPF_PROG_TYPE_XDP,
+ .flags = BPF_F_TEST_STATE_FREQ,
+ .errstr = "last_idx 6 first_idx 6\
+ parent didn't have regs=10 stack=0 marks\
+ last_idx 5 first_idx 3\
+ regs=10 stack=0 before 5\
+ regs=10 stack=0 before 4\
+ regs=0 stack=1 before 3\
+ last_idx 6 first_idx 6\
+ parent didn't have regs=1 stack=0 marks\
+ last_idx 5 first_idx 3\
+ regs=1 stack=0 before 5",
+ .result = VERBOSE_ACCEPT,
+ .retval = -1,
+},
diff --git a/tools/testing/selftests/bpf/verifier/prevent_map_lookup.c b/tools/testing/selftests/bpf/verifier/prevent_map_lookup.c
index bbdba990fefb..da7a4b37cb98 100644
--- a/tools/testing/selftests/bpf/verifier/prevent_map_lookup.c
+++ b/tools/testing/selftests/bpf/verifier/prevent_map_lookup.c
@@ -29,21 +29,6 @@
.prog_type = BPF_PROG_TYPE_SOCK_OPS,
},
{
- "prevent map lookup in xskmap",
- .insns = {
- BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
- BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
- BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
- BPF_LD_MAP_FD(BPF_REG_1, 0),
- BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
- BPF_EXIT_INSN(),
- },
- .fixup_map_xskmap = { 3 },
- .result = REJECT,
- .errstr = "cannot pass map_type 17 into func bpf_map_lookup_elem",
- .prog_type = BPF_PROG_TYPE_XDP,
-},
-{
"prevent map lookup in stack trace",
.insns = {
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
diff --git a/tools/testing/selftests/bpf/verifier/sock.c b/tools/testing/selftests/bpf/verifier/sock.c
index b31cd2cf50d0..9ed192e14f5f 100644
--- a/tools/testing/selftests/bpf/verifier/sock.c
+++ b/tools/testing/selftests/bpf/verifier/sock.c
@@ -498,3 +498,21 @@
.result = REJECT,
.errstr = "cannot pass map_type 24 into func bpf_map_lookup_elem",
},
+{
+ "bpf_map_lookup_elem(xskmap, &key); xs->queue_id",
+ .insns = {
+ BPF_ST_MEM(BPF_W, BPF_REG_10, -8, 0),
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+ BPF_LD_MAP_FD(BPF_REG_1, 0),
+ BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+ BPF_EXIT_INSN(),
+ BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_xdp_sock, queue_id)),
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .fixup_map_xskmap = { 3 },
+ .prog_type = BPF_PROG_TYPE_XDP,
+ .result = ACCEPT,
+},
diff --git a/tools/testing/selftests/bpf/verifier/subreg.c b/tools/testing/selftests/bpf/verifier/subreg.c
new file mode 100644
index 000000000000..4c4133c80440
--- /dev/null
+++ b/tools/testing/selftests/bpf/verifier/subreg.c
@@ -0,0 +1,533 @@
+/* This file contains sub-register zero extension checks for insns defining
+ * sub-registers, meaning:
+ * - All insns under BPF_ALU class. Their BPF_ALU32 variants or narrow width
+ * forms (BPF_END) could define sub-registers.
+ * - Narrow direct loads, BPF_B/H/W | BPF_LDX.
+ * - BPF_LD is not exposed to JIT back-ends, so no need for testing.
+ *
+ * "get_prandom_u32" is used to initialize low 32-bit of some registers to
+ * prevent potential optimizations done by verifier or JIT back-ends which could
+ * optimize register back into constant when range info shows one register is a
+ * constant.
+ */
+{
+ "add32 reg zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+ BPF_LD_IMM64(BPF_REG_0, 0x100000000ULL),
+ BPF_ALU32_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "add32 imm zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ /* An insn could have no effect on the low 32-bit, for example:
+ * a = a + 0
+ * a = a | 0
+ * a = a & -1
+ * But, they should still zero high 32-bit.
+ */
+ BPF_ALU32_IMM(BPF_ADD, BPF_REG_0, 0),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_ADD, BPF_REG_0, -2),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_6),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "sub32 reg zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+ BPF_LD_IMM64(BPF_REG_0, 0x1ffffffffULL),
+ BPF_ALU32_REG(BPF_SUB, BPF_REG_0, BPF_REG_1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "sub32 imm zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_SUB, BPF_REG_0, 0),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_SUB, BPF_REG_0, 1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_6),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "mul32 reg zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+ BPF_LD_IMM64(BPF_REG_0, 0x100000001ULL),
+ BPF_ALU32_REG(BPF_MUL, BPF_REG_0, BPF_REG_1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "mul32 imm zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_MUL, BPF_REG_0, 1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_MUL, BPF_REG_0, -1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_6),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "div32 reg zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+ BPF_MOV64_IMM(BPF_REG_0, -1),
+ BPF_ALU32_REG(BPF_DIV, BPF_REG_0, BPF_REG_1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "div32 imm zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_DIV, BPF_REG_0, 1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_DIV, BPF_REG_0, 2),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_6),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "or32 reg zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+ BPF_LD_IMM64(BPF_REG_0, 0x100000001ULL),
+ BPF_ALU32_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "or32 imm zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_OR, BPF_REG_0, 0),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_OR, BPF_REG_0, 1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_6),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "and32 reg zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x100000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_1, BPF_REG_0),
+ BPF_LD_IMM64(BPF_REG_0, 0x1ffffffffULL),
+ BPF_ALU32_REG(BPF_AND, BPF_REG_0, BPF_REG_1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "and32 imm zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_AND, BPF_REG_0, -1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_AND, BPF_REG_0, -2),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_6),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "lsh32 reg zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x100000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_MOV64_IMM(BPF_REG_1, 1),
+ BPF_ALU32_REG(BPF_LSH, BPF_REG_0, BPF_REG_1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "lsh32 imm zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_LSH, BPF_REG_0, 0),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_LSH, BPF_REG_0, 1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_6),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "rsh32 reg zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_MOV64_IMM(BPF_REG_1, 1),
+ BPF_ALU32_REG(BPF_RSH, BPF_REG_0, BPF_REG_1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "rsh32 imm zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_RSH, BPF_REG_0, 0),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_RSH, BPF_REG_0, 1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_6),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "neg32 reg zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_NEG, BPF_REG_0, 0),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "mod32 reg zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+ BPF_MOV64_IMM(BPF_REG_0, -1),
+ BPF_ALU32_REG(BPF_MOD, BPF_REG_0, BPF_REG_1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "mod32 imm zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_MOD, BPF_REG_0, 1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_MOD, BPF_REG_0, 2),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_6),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "xor32 reg zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+ BPF_LD_IMM64(BPF_REG_0, 0x100000000ULL),
+ BPF_ALU32_REG(BPF_XOR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "xor32 imm zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_XOR, BPF_REG_0, 1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "mov32 reg zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x100000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_1, BPF_REG_0),
+ BPF_LD_IMM64(BPF_REG_0, 0x100000000ULL),
+ BPF_MOV32_REG(BPF_REG_0, BPF_REG_1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "mov32 imm zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_MOV32_IMM(BPF_REG_0, 0),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_MOV32_IMM(BPF_REG_0, 1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_6),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "arsh32 reg zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_MOV64_IMM(BPF_REG_1, 1),
+ BPF_ALU32_REG(BPF_ARSH, BPF_REG_0, BPF_REG_1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "arsh32 imm zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_ARSH, BPF_REG_0, 0),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_ALU32_IMM(BPF_ARSH, BPF_REG_0, 1),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_6),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "end16 (to_le) reg zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+ BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 32),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_6),
+ BPF_ENDIAN(BPF_TO_LE, BPF_REG_0, 16),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "end32 (to_le) reg zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+ BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 32),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_6),
+ BPF_ENDIAN(BPF_TO_LE, BPF_REG_0, 32),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "end16 (to_be) reg zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+ BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 32),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_6),
+ BPF_ENDIAN(BPF_TO_BE, BPF_REG_0, 16),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "end32 (to_be) reg zero extend check",
+ .insns = {
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+ BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 32),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_6),
+ BPF_ENDIAN(BPF_TO_BE, BPF_REG_0, 32),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "ldx_b zero extend check",
+ .insns = {
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_10),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -4),
+ BPF_ST_MEM(BPF_W, BPF_REG_6, 0, 0xfaceb00c),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6, 0),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "ldx_h zero extend check",
+ .insns = {
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_10),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -4),
+ BPF_ST_MEM(BPF_W, BPF_REG_6, 0, 0xfaceb00c),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_6, 0),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
+{
+ "ldx_w zero extend check",
+ .insns = {
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_10),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -4),
+ BPF_ST_MEM(BPF_W, BPF_REG_6, 0, 0xfaceb00c),
+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+ BPF_LD_IMM64(BPF_REG_1, 0x1000000000ULL),
+ BPF_ALU64_REG(BPF_OR, BPF_REG_0, BPF_REG_1),
+ BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_0, 32),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .retval = 0,
+},
diff --git a/tools/testing/selftests/bpf/verifier/value_ptr_arith.c b/tools/testing/selftests/bpf/verifier/value_ptr_arith.c
index c3de1a2c9dc5..a53d99cebd9f 100644
--- a/tools/testing/selftests/bpf/verifier/value_ptr_arith.c
+++ b/tools/testing/selftests/bpf/verifier/value_ptr_arith.c
@@ -183,7 +183,7 @@
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
BPF_EXIT_INSN(),
- BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0),
+ BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0),
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 3),
BPF_MOV64_IMM(BPF_REG_2, 0),
BPF_MOV64_IMM(BPF_REG_3, 0x100000),
diff --git a/tools/testing/selftests/bpf/verifier/wide_access.c b/tools/testing/selftests/bpf/verifier/wide_access.c
new file mode 100644
index 000000000000..ccade9312d21
--- /dev/null
+++ b/tools/testing/selftests/bpf/verifier/wide_access.c
@@ -0,0 +1,73 @@
+#define BPF_SOCK_ADDR_STORE(field, off, res, err) \
+{ \
+ "wide store to bpf_sock_addr." #field "[" #off "]", \
+ .insns = { \
+ BPF_MOV64_IMM(BPF_REG_0, 1), \
+ BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, \
+ offsetof(struct bpf_sock_addr, field[off])), \
+ BPF_EXIT_INSN(), \
+ }, \
+ .result = res, \
+ .prog_type = BPF_PROG_TYPE_CGROUP_SOCK_ADDR, \
+ .expected_attach_type = BPF_CGROUP_UDP6_SENDMSG, \
+ .errstr = err, \
+}
+
+/* user_ip6[0] is u64 aligned */
+BPF_SOCK_ADDR_STORE(user_ip6, 0, ACCEPT,
+ NULL),
+BPF_SOCK_ADDR_STORE(user_ip6, 1, REJECT,
+ "invalid bpf_context access off=12 size=8"),
+BPF_SOCK_ADDR_STORE(user_ip6, 2, ACCEPT,
+ NULL),
+BPF_SOCK_ADDR_STORE(user_ip6, 3, REJECT,
+ "invalid bpf_context access off=20 size=8"),
+
+/* msg_src_ip6[0] is _not_ u64 aligned */
+BPF_SOCK_ADDR_STORE(msg_src_ip6, 0, REJECT,
+ "invalid bpf_context access off=44 size=8"),
+BPF_SOCK_ADDR_STORE(msg_src_ip6, 1, ACCEPT,
+ NULL),
+BPF_SOCK_ADDR_STORE(msg_src_ip6, 2, REJECT,
+ "invalid bpf_context access off=52 size=8"),
+BPF_SOCK_ADDR_STORE(msg_src_ip6, 3, REJECT,
+ "invalid bpf_context access off=56 size=8"),
+
+#undef BPF_SOCK_ADDR_STORE
+
+#define BPF_SOCK_ADDR_LOAD(field, off, res, err) \
+{ \
+ "wide load from bpf_sock_addr." #field "[" #off "]", \
+ .insns = { \
+ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, \
+ offsetof(struct bpf_sock_addr, field[off])), \
+ BPF_MOV64_IMM(BPF_REG_0, 1), \
+ BPF_EXIT_INSN(), \
+ }, \
+ .result = res, \
+ .prog_type = BPF_PROG_TYPE_CGROUP_SOCK_ADDR, \
+ .expected_attach_type = BPF_CGROUP_UDP6_SENDMSG, \
+ .errstr = err, \
+}
+
+/* user_ip6[0] is u64 aligned */
+BPF_SOCK_ADDR_LOAD(user_ip6, 0, ACCEPT,
+ NULL),
+BPF_SOCK_ADDR_LOAD(user_ip6, 1, REJECT,
+ "invalid bpf_context access off=12 size=8"),
+BPF_SOCK_ADDR_LOAD(user_ip6, 2, ACCEPT,
+ NULL),
+BPF_SOCK_ADDR_LOAD(user_ip6, 3, REJECT,
+ "invalid bpf_context access off=20 size=8"),
+
+/* msg_src_ip6[0] is _not_ u64 aligned */
+BPF_SOCK_ADDR_LOAD(msg_src_ip6, 0, REJECT,
+ "invalid bpf_context access off=44 size=8"),
+BPF_SOCK_ADDR_LOAD(msg_src_ip6, 1, ACCEPT,
+ NULL),
+BPF_SOCK_ADDR_LOAD(msg_src_ip6, 2, REJECT,
+ "invalid bpf_context access off=52 size=8"),
+BPF_SOCK_ADDR_LOAD(msg_src_ip6, 3, REJECT,
+ "invalid bpf_context access off=56 size=8"),
+
+#undef BPF_SOCK_ADDR_LOAD
diff --git a/tools/testing/selftests/bpf/xdping.c b/tools/testing/selftests/bpf/xdping.c
new file mode 100644
index 000000000000..d60a343b1371
--- /dev/null
+++ b/tools/testing/selftests/bpf/xdping.c
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. */
+
+#include <linux/bpf.h>
+#include <linux/if_link.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <sys/resource.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include "bpf/bpf.h"
+#include "bpf/libbpf.h"
+
+#include "xdping.h"
+
+static int ifindex;
+static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
+
+static void cleanup(int sig)
+{
+ bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
+ if (sig)
+ exit(1);
+}
+
+static int get_stats(int fd, __u16 count, __u32 raddr)
+{
+ struct pinginfo pinginfo = { 0 };
+ char inaddrbuf[INET_ADDRSTRLEN];
+ struct in_addr inaddr;
+ __u16 i;
+
+ inaddr.s_addr = raddr;
+
+ printf("\nXDP RTT data:\n");
+
+ if (bpf_map_lookup_elem(fd, &raddr, &pinginfo)) {
+ perror("bpf_map_lookup elem: ");
+ return 1;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (pinginfo.times[i] == 0)
+ break;
+
+ printf("64 bytes from %s: icmp_seq=%d ttl=64 time=%#.5f ms\n",
+ inet_ntop(AF_INET, &inaddr, inaddrbuf,
+ sizeof(inaddrbuf)),
+ count + i + 1,
+ (double)pinginfo.times[i]/1000000);
+ }
+
+ if (i < count) {
+ fprintf(stderr, "Expected %d samples, got %d.\n", count, i);
+ return 1;
+ }
+
+ bpf_map_delete_elem(fd, &raddr);
+
+ return 0;
+}
+
+static void show_usage(const char *prog)
+{
+ fprintf(stderr,
+ "usage: %s [OPTS] -I interface destination\n\n"
+ "OPTS:\n"
+ " -c count Stop after sending count requests\n"
+ " (default %d, max %d)\n"
+ " -I interface interface name\n"
+ " -N Run in driver mode\n"
+ " -s Server mode\n"
+ " -S Run in skb mode\n",
+ prog, XDPING_DEFAULT_COUNT, XDPING_MAX_COUNT);
+}
+
+int main(int argc, char **argv)
+{
+ __u32 mode_flags = XDP_FLAGS_DRV_MODE | XDP_FLAGS_SKB_MODE;
+ struct addrinfo *a, hints = { .ai_family = AF_INET };
+ struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
+ __u16 count = XDPING_DEFAULT_COUNT;
+ struct pinginfo pinginfo = { 0 };
+ const char *optstr = "c:I:NsS";
+ struct bpf_program *main_prog;
+ int prog_fd = -1, map_fd = -1;
+ struct sockaddr_in rin;
+ struct bpf_object *obj;
+ struct bpf_map *map;
+ char *ifname = NULL;
+ char filename[256];
+ int opt, ret = 1;
+ __u32 raddr = 0;
+ int server = 0;
+ char cmd[256];
+
+ while ((opt = getopt(argc, argv, optstr)) != -1) {
+ switch (opt) {
+ case 'c':
+ count = atoi(optarg);
+ if (count < 1 || count > XDPING_MAX_COUNT) {
+ fprintf(stderr,
+ "min count is 1, max count is %d\n",
+ XDPING_MAX_COUNT);
+ return 1;
+ }
+ break;
+ case 'I':
+ ifname = optarg;
+ ifindex = if_nametoindex(ifname);
+ if (!ifindex) {
+ fprintf(stderr, "Could not get interface %s\n",
+ ifname);
+ return 1;
+ }
+ break;
+ case 'N':
+ xdp_flags |= XDP_FLAGS_DRV_MODE;
+ break;
+ case 's':
+ /* use server program */
+ server = 1;
+ break;
+ case 'S':
+ xdp_flags |= XDP_FLAGS_SKB_MODE;
+ break;
+ default:
+ show_usage(basename(argv[0]));
+ return 1;
+ }
+ }
+
+ if (!ifname) {
+ show_usage(basename(argv[0]));
+ return 1;
+ }
+ if (!server && optind == argc) {
+ show_usage(basename(argv[0]));
+ return 1;
+ }
+
+ if ((xdp_flags & mode_flags) == mode_flags) {
+ fprintf(stderr, "-N or -S can be specified, not both.\n");
+ show_usage(basename(argv[0]));
+ return 1;
+ }
+
+ if (!server) {
+ /* Only supports IPv4; see hints initiailization above. */
+ if (getaddrinfo(argv[optind], NULL, &hints, &a) || !a) {
+ fprintf(stderr, "Could not resolve %s\n", argv[optind]);
+ return 1;
+ }
+ memcpy(&rin, a->ai_addr, sizeof(rin));
+ raddr = rin.sin_addr.s_addr;
+ freeaddrinfo(a);
+ }
+
+ if (setrlimit(RLIMIT_MEMLOCK, &r)) {
+ perror("setrlimit(RLIMIT_MEMLOCK)");
+ return 1;
+ }
+
+ snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
+
+ if (bpf_prog_load(filename, BPF_PROG_TYPE_XDP, &obj, &prog_fd)) {
+ fprintf(stderr, "load of %s failed\n", filename);
+ return 1;
+ }
+
+ main_prog = bpf_object__find_program_by_title(obj,
+ server ? "xdpserver" :
+ "xdpclient");
+ if (main_prog)
+ prog_fd = bpf_program__fd(main_prog);
+ if (!main_prog || prog_fd < 0) {
+ fprintf(stderr, "could not find xdping program");
+ return 1;
+ }
+
+ map = bpf_map__next(NULL, obj);
+ if (map)
+ map_fd = bpf_map__fd(map);
+ if (!map || map_fd < 0) {
+ fprintf(stderr, "Could not find ping map");
+ goto done;
+ }
+
+ signal(SIGINT, cleanup);
+ signal(SIGTERM, cleanup);
+
+ printf("Setting up XDP for %s, please wait...\n", ifname);
+
+ printf("XDP setup disrupts network connectivity, hit Ctrl+C to quit\n");
+
+ if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) {
+ fprintf(stderr, "Link set xdp fd failed for %s\n", ifname);
+ goto done;
+ }
+
+ if (server) {
+ close(prog_fd);
+ close(map_fd);
+ printf("Running server on %s; press Ctrl+C to exit...\n",
+ ifname);
+ do { } while (1);
+ }
+
+ /* Start xdping-ing from last regular ping reply, e.g. for a count
+ * of 10 ICMP requests, we start xdping-ing using reply with seq number
+ * 10. The reason the last "real" ping RTT is much higher is that
+ * the ping program sees the ICMP reply associated with the last
+ * XDP-generated packet, so ping doesn't get a reply until XDP is done.
+ */
+ pinginfo.seq = htons(count);
+ pinginfo.count = count;
+
+ if (bpf_map_update_elem(map_fd, &raddr, &pinginfo, BPF_ANY)) {
+ fprintf(stderr, "could not communicate with BPF map: %s\n",
+ strerror(errno));
+ cleanup(0);
+ goto done;
+ }
+
+ /* We need to wait for XDP setup to complete. */
+ sleep(10);
+
+ snprintf(cmd, sizeof(cmd), "ping -c %d -I %s %s",
+ count, ifname, argv[optind]);
+
+ printf("\nNormal ping RTT data\n");
+ printf("[Ignore final RTT; it is distorted by XDP using the reply]\n");
+
+ ret = system(cmd);
+
+ if (!ret)
+ ret = get_stats(map_fd, count, raddr);
+
+ cleanup(0);
+
+done:
+ if (prog_fd > 0)
+ close(prog_fd);
+ if (map_fd > 0)
+ close(map_fd);
+
+ return ret;
+}
diff --git a/tools/testing/selftests/bpf/xdping.h b/tools/testing/selftests/bpf/xdping.h
new file mode 100644
index 000000000000..afc578df77be
--- /dev/null
+++ b/tools/testing/selftests/bpf/xdping.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. */
+
+#define XDPING_MAX_COUNT 10
+#define XDPING_DEFAULT_COUNT 4
+
+struct pinginfo {
+ __u64 start;
+ __be16 seq;
+ __u16 count;
+ __u32 pad;
+ __u64 times[XDPING_MAX_COUNT];
+};
diff --git a/tools/testing/selftests/breakpoints/breakpoint_test.c b/tools/testing/selftests/breakpoints/breakpoint_test.c
index 8f3655e59020..3266cc9293fe 100644
--- a/tools/testing/selftests/breakpoints/breakpoint_test.c
+++ b/tools/testing/selftests/breakpoints/breakpoint_test.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 Red Hat, Inc., Frederic Weisbecker <fweisbec@redhat.com>
*
- * Licensed under the terms of the GNU GPL License version 2
- *
* Selftests for breakpoints (and more generally the do_debug() path) in x86.
*/
diff --git a/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c b/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c
index ab59d814341a..58ed5eeab709 100644
--- a/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c
+++ b/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c
@@ -1,20 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2016 Google, Inc.
*
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* Original Code by Pavel Labath <labath@google.com>
*
* Code modified by Pratyush Anand <panand@redhat.com>
* for testing different byte select for each access size.
- *
*/
#define _GNU_SOURCE
diff --git a/tools/testing/selftests/breakpoints/step_after_suspend_test.c b/tools/testing/selftests/breakpoints/step_after_suspend_test.c
index cf868b5e00f7..b3ead29c6089 100644
--- a/tools/testing/selftests/breakpoints/step_after_suspend_test.c
+++ b/tools/testing/selftests/breakpoints/step_after_suspend_test.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2016 Google, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#define _GNU_SOURCE
diff --git a/tools/testing/selftests/cgroup/cgroup_util.c b/tools/testing/selftests/cgroup/cgroup_util.c
index 4c223266299a..bdb69599c4bd 100644
--- a/tools/testing/selftests/cgroup/cgroup_util.c
+++ b/tools/testing/selftests/cgroup/cgroup_util.c
@@ -191,8 +191,7 @@ int cg_find_unified_root(char *root, size_t len)
strtok(NULL, delim);
strtok(NULL, delim);
- if (strcmp(fs, "cgroup") == 0 &&
- strcmp(type, "cgroup2") == 0) {
+ if (strcmp(type, "cgroup2") == 0) {
strncpy(root, mount, len);
return 0;
}
diff --git a/tools/testing/selftests/cgroup/test_core.c b/tools/testing/selftests/cgroup/test_core.c
index be59f9c34ea2..79053a4f4783 100644
--- a/tools/testing/selftests/cgroup/test_core.c
+++ b/tools/testing/selftests/cgroup/test_core.c
@@ -198,7 +198,7 @@ static int test_cgcore_no_internal_process_constraint_on_threads(const char *roo
char *parent = NULL, *child = NULL;
if (cg_read_strstr(root, "cgroup.controllers", "cpu") ||
- cg_read_strstr(root, "cgroup.subtree_control", "cpu")) {
+ cg_write(root, "cgroup.subtree_control", "+cpu")) {
ret = KSFT_SKIP;
goto cleanup;
}
@@ -376,6 +376,11 @@ int main(int argc, char *argv[])
if (cg_find_unified_root(root, sizeof(root)))
ksft_exit_skip("cgroup v2 isn't mounted\n");
+
+ if (cg_read_strstr(root, "cgroup.subtree_control", "memory"))
+ if (cg_write(root, "cgroup.subtree_control", "+memory"))
+ ksft_exit_skip("Failed to set memory controller\n");
+
for (i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:
diff --git a/tools/testing/selftests/cgroup/test_freezer.c b/tools/testing/selftests/cgroup/test_freezer.c
index 2bfddb6d6d3b..0fc1b6d4b0f9 100644
--- a/tools/testing/selftests/cgroup/test_freezer.c
+++ b/tools/testing/selftests/cgroup/test_freezer.c
@@ -11,7 +11,6 @@
#include <stdlib.h>
#include <sys/inotify.h>
#include <string.h>
-#include <sys/types.h>
#include <sys/wait.h>
#include "../kselftest.h"
@@ -449,6 +448,59 @@ cleanup:
}
/*
+ * The test creates a cgroups and freezes it. Then it creates a child cgroup
+ * and populates it with a task. After that it checks that the child cgroup
+ * is frozen and the parent cgroup remains frozen too.
+ */
+static int test_cgfreezer_mkdir(const char *root)
+{
+ int ret = KSFT_FAIL;
+ char *parent, *child = NULL;
+ int pid;
+
+ parent = cg_name(root, "cg_test_mkdir_A");
+ if (!parent)
+ goto cleanup;
+
+ child = cg_name(parent, "cg_test_mkdir_B");
+ if (!child)
+ goto cleanup;
+
+ if (cg_create(parent))
+ goto cleanup;
+
+ if (cg_freeze_wait(parent, true))
+ goto cleanup;
+
+ if (cg_create(child))
+ goto cleanup;
+
+ pid = cg_run_nowait(child, child_fn, NULL);
+ if (pid < 0)
+ goto cleanup;
+
+ if (cg_wait_for_proc_count(child, 1))
+ goto cleanup;
+
+ if (cg_check_frozen(child, true))
+ goto cleanup;
+
+ if (cg_check_frozen(parent, true))
+ goto cleanup;
+
+ ret = KSFT_PASS;
+
+cleanup:
+ if (child)
+ cg_destroy(child);
+ free(child);
+ if (parent)
+ cg_destroy(parent);
+ free(parent);
+ return ret;
+}
+
+/*
* The test creates two nested cgroups, freezes the parent
* and removes the child. Then it checks that the parent cgroup
* remains frozen and it's possible to create a new child
@@ -816,6 +868,7 @@ struct cgfreezer_test {
T(test_cgfreezer_simple),
T(test_cgfreezer_tree),
T(test_cgfreezer_forkbomb),
+ T(test_cgfreezer_mkdir),
T(test_cgfreezer_rmdir),
T(test_cgfreezer_migrate),
T(test_cgfreezer_ptrace),
diff --git a/tools/testing/selftests/cgroup/test_memcontrol.c b/tools/testing/selftests/cgroup/test_memcontrol.c
index 6f339882a6ca..c19a97dd02d4 100644
--- a/tools/testing/selftests/cgroup/test_memcontrol.c
+++ b/tools/testing/selftests/cgroup/test_memcontrol.c
@@ -1205,6 +1205,10 @@ int main(int argc, char **argv)
if (cg_read_strstr(root, "cgroup.controllers", "memory"))
ksft_exit_skip("memory controller isn't available\n");
+ if (cg_read_strstr(root, "cgroup.subtree_control", "memory"))
+ if (cg_write(root, "cgroup.subtree_control", "+memory"))
+ ksft_exit_skip("Failed to set memory controller\n");
+
for (i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:
diff --git a/tools/testing/selftests/drivers/dma-buf/Makefile b/tools/testing/selftests/drivers/dma-buf/Makefile
index f22c3f7cf612..79cb16b4e01a 100644
--- a/tools/testing/selftests/drivers/dma-buf/Makefile
+++ b/tools/testing/selftests/drivers/dma-buf/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
CFLAGS += -I../../../../../usr/include/
TEST_GEN_PROGS := udmabuf
diff --git a/tools/testing/selftests/drivers/dma-buf/config b/tools/testing/selftests/drivers/dma-buf/config
new file mode 100644
index 000000000000..d708515cff1b
--- /dev/null
+++ b/tools/testing/selftests/drivers/dma-buf/config
@@ -0,0 +1 @@
+CONFIG_UDMABUF=y
diff --git a/tools/testing/selftests/drivers/net/mlxsw/devlink_trap.sh b/tools/testing/selftests/drivers/net/mlxsw/devlink_trap.sh
new file mode 100755
index 000000000000..89b55e946eed
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/mlxsw/devlink_trap.sh
@@ -0,0 +1,129 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test generic devlink-trap functionality over mlxsw. These tests are not
+# specific to a single trap, but do not check the devlink-trap common
+# infrastructure either.
+
+lib_dir=$(dirname $0)/../../../net/forwarding
+
+ALL_TESTS="
+ dev_del_test
+"
+NUM_NETIFS=4
+source $lib_dir/tc_common.sh
+source $lib_dir/lib.sh
+source $lib_dir/devlink_lib.sh
+
+h1_create()
+{
+ simple_if_init $h1
+}
+
+h1_destroy()
+{
+ simple_if_fini $h1
+}
+
+h2_create()
+{
+ simple_if_init $h2
+}
+
+h2_destroy()
+{
+ simple_if_fini $h2
+}
+
+switch_create()
+{
+ ip link add dev br0 type bridge vlan_filtering 1 mcast_snooping 0
+
+ ip link set dev $swp1 master br0
+ ip link set dev $swp2 master br0
+
+ ip link set dev br0 up
+ ip link set dev $swp1 up
+ ip link set dev $swp2 up
+}
+
+switch_destroy()
+{
+ ip link set dev $swp2 down
+ ip link set dev $swp1 down
+
+ ip link del dev br0
+}
+
+setup_prepare()
+{
+ h1=${NETIFS[p1]}
+ swp1=${NETIFS[p2]}
+
+ swp2=${NETIFS[p3]}
+ h2=${NETIFS[p4]}
+
+ vrf_prepare
+
+ h1_create
+ h2_create
+
+ switch_create
+}
+
+cleanup()
+{
+ pre_cleanup
+
+ switch_destroy
+
+ h2_destroy
+ h1_destroy
+
+ vrf_cleanup
+}
+
+dev_del_test()
+{
+ local trap_name="source_mac_is_multicast"
+ local smac=01:02:03:04:05:06
+ local num_iter=5
+ local mz_pid
+ local i
+
+ $MZ $h1 -c 0 -p 100 -a $smac -b bcast -t ip -q &
+ mz_pid=$!
+
+ # The purpose of this test is to make sure we correctly dismantle a
+ # port while packets are trapped from it. This is done by reloading the
+ # the driver while the 'ingress_smac_mc_drop' trap is triggered.
+ RET=0
+
+ for i in $(seq 1 $num_iter); do
+ log_info "Iteration $i / $num_iter"
+
+ devlink_trap_action_set $trap_name "trap"
+ sleep 1
+
+ devlink_reload
+ # Allow netdevices to be re-created following the reload
+ sleep 20
+
+ cleanup
+ setup_prepare
+ setup_wait
+ done
+
+ log_test "Device delete"
+
+ kill $mz_pid && wait $mz_pid &> /dev/null
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/drivers/net/mlxsw/devlink_trap_l2_drops.sh b/tools/testing/selftests/drivers/net/mlxsw/devlink_trap_l2_drops.sh
new file mode 100755
index 000000000000..5dcdfa20fc6c
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/mlxsw/devlink_trap_l2_drops.sh
@@ -0,0 +1,484 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test devlink-trap L2 drops functionality over mlxsw. Each registered L2 drop
+# packet trap is tested to make sure it is triggered under the right
+# conditions.
+
+lib_dir=$(dirname $0)/../../../net/forwarding
+
+ALL_TESTS="
+ source_mac_is_multicast_test
+ vlan_tag_mismatch_test
+ ingress_vlan_filter_test
+ ingress_stp_filter_test
+ port_list_is_empty_test
+ port_loopback_filter_test
+"
+NUM_NETIFS=4
+source $lib_dir/tc_common.sh
+source $lib_dir/lib.sh
+source $lib_dir/devlink_lib.sh
+
+h1_create()
+{
+ simple_if_init $h1
+}
+
+h1_destroy()
+{
+ simple_if_fini $h1
+}
+
+h2_create()
+{
+ simple_if_init $h2
+}
+
+h2_destroy()
+{
+ simple_if_fini $h2
+}
+
+switch_create()
+{
+ ip link add dev br0 type bridge vlan_filtering 1 mcast_snooping 0
+
+ ip link set dev $swp1 master br0
+ ip link set dev $swp2 master br0
+
+ ip link set dev br0 up
+ ip link set dev $swp1 up
+ ip link set dev $swp2 up
+
+ tc qdisc add dev $swp2 clsact
+}
+
+switch_destroy()
+{
+ tc qdisc del dev $swp2 clsact
+
+ ip link set dev $swp2 down
+ ip link set dev $swp1 down
+
+ ip link del dev br0
+}
+
+setup_prepare()
+{
+ h1=${NETIFS[p1]}
+ swp1=${NETIFS[p2]}
+
+ swp2=${NETIFS[p3]}
+ h2=${NETIFS[p4]}
+
+ vrf_prepare
+
+ h1_create
+ h2_create
+
+ switch_create
+}
+
+cleanup()
+{
+ pre_cleanup
+
+ switch_destroy
+
+ h2_destroy
+ h1_destroy
+
+ vrf_cleanup
+}
+
+l2_drops_test()
+{
+ local trap_name=$1; shift
+ local group_name=$1; shift
+
+ # This is the common part of all the tests. It checks that stats are
+ # initially idle, then non-idle after changing the trap action and
+ # finally idle again. It also makes sure the packets are dropped and
+ # never forwarded.
+ devlink_trap_stats_idle_test $trap_name
+ check_err $? "Trap stats not idle with initial drop action"
+ devlink_trap_group_stats_idle_test $group_name
+ check_err $? "Trap group stats not idle with initial drop action"
+
+ devlink_trap_action_set $trap_name "trap"
+
+ devlink_trap_stats_idle_test $trap_name
+ check_fail $? "Trap stats idle after setting action to trap"
+ devlink_trap_group_stats_idle_test $group_name
+ check_fail $? "Trap group stats idle after setting action to trap"
+
+ devlink_trap_action_set $trap_name "drop"
+
+ devlink_trap_stats_idle_test $trap_name
+ check_err $? "Trap stats not idle after setting action to drop"
+ devlink_trap_group_stats_idle_test $group_name
+ check_err $? "Trap group stats not idle after setting action to drop"
+
+ tc_check_packets "dev $swp2 egress" 101 0
+ check_err $? "Packets were not dropped"
+}
+
+l2_drops_cleanup()
+{
+ local mz_pid=$1; shift
+
+ kill $mz_pid && wait $mz_pid &> /dev/null
+ tc filter del dev $swp2 egress protocol ip pref 1 handle 101 flower
+}
+
+source_mac_is_multicast_test()
+{
+ local trap_name="source_mac_is_multicast"
+ local smac=01:02:03:04:05:06
+ local group_name="l2_drops"
+ local mz_pid
+
+ tc filter add dev $swp2 egress protocol ip pref 1 handle 101 \
+ flower src_mac $smac action drop
+
+ $MZ $h1 -c 0 -p 100 -a $smac -b bcast -t ip -d 1msec -q &
+ mz_pid=$!
+
+ RET=0
+
+ l2_drops_test $trap_name $group_name
+
+ log_test "Source MAC is multicast"
+
+ l2_drops_cleanup $mz_pid
+}
+
+__vlan_tag_mismatch_test()
+{
+ local trap_name="vlan_tag_mismatch"
+ local dmac=de:ad:be:ef:13:37
+ local group_name="l2_drops"
+ local opt=$1; shift
+ local mz_pid
+
+ # Remove PVID flag. This should prevent untagged and prio-tagged
+ # packets from entering the bridge.
+ bridge vlan add vid 1 dev $swp1 untagged master
+
+ tc filter add dev $swp2 egress protocol ip pref 1 handle 101 \
+ flower dst_mac $dmac action drop
+
+ $MZ $h1 "$opt" -c 0 -p 100 -a own -b $dmac -t ip -d 1msec -q &
+ mz_pid=$!
+
+ l2_drops_test $trap_name $group_name
+
+ # Add PVID and make sure packets are no longer dropped.
+ bridge vlan add vid 1 dev $swp1 pvid untagged master
+ devlink_trap_action_set $trap_name "trap"
+
+ devlink_trap_stats_idle_test $trap_name
+ check_err $? "Trap stats not idle when packets should not be dropped"
+ devlink_trap_group_stats_idle_test $group_name
+ check_err $? "Trap group stats not idle with when packets should not be dropped"
+
+ tc_check_packets "dev $swp2 egress" 101 0
+ check_fail $? "Packets not forwarded when should"
+
+ devlink_trap_action_set $trap_name "drop"
+
+ l2_drops_cleanup $mz_pid
+}
+
+vlan_tag_mismatch_untagged_test()
+{
+ RET=0
+
+ __vlan_tag_mismatch_test
+
+ log_test "VLAN tag mismatch - untagged packets"
+}
+
+vlan_tag_mismatch_vid_0_test()
+{
+ RET=0
+
+ __vlan_tag_mismatch_test "-Q 0"
+
+ log_test "VLAN tag mismatch - prio-tagged packets"
+}
+
+vlan_tag_mismatch_test()
+{
+ vlan_tag_mismatch_untagged_test
+ vlan_tag_mismatch_vid_0_test
+}
+
+ingress_vlan_filter_test()
+{
+ local trap_name="ingress_vlan_filter"
+ local dmac=de:ad:be:ef:13:37
+ local group_name="l2_drops"
+ local mz_pid
+ local vid=10
+
+ bridge vlan add vid $vid dev $swp2 master
+ # During initialization the firmware enables all the VLAN filters and
+ # the driver does not turn them off since the traffic will be discarded
+ # by the STP filter whose default is DISCARD state. Add the VID on the
+ # ingress bridge port and then remove it to make sure it is not member
+ # in the VLAN.
+ bridge vlan add vid $vid dev $swp1 master
+ bridge vlan del vid $vid dev $swp1 master
+
+ RET=0
+
+ tc filter add dev $swp2 egress protocol ip pref 1 handle 101 \
+ flower dst_mac $dmac action drop
+
+ $MZ $h1 -Q $vid -c 0 -p 100 -a own -b $dmac -t ip -d 1msec -q &
+ mz_pid=$!
+
+ l2_drops_test $trap_name $group_name
+
+ # Add the VLAN on the bridge port and make sure packets are no longer
+ # dropped.
+ bridge vlan add vid $vid dev $swp1 master
+ devlink_trap_action_set $trap_name "trap"
+
+ devlink_trap_stats_idle_test $trap_name
+ check_err $? "Trap stats not idle when packets should not be dropped"
+ devlink_trap_group_stats_idle_test $group_name
+ check_err $? "Trap group stats not idle with when packets should not be dropped"
+
+ tc_check_packets "dev $swp2 egress" 101 0
+ check_fail $? "Packets not forwarded when should"
+
+ devlink_trap_action_set $trap_name "drop"
+
+ log_test "Ingress VLAN filter"
+
+ l2_drops_cleanup $mz_pid
+
+ bridge vlan del vid $vid dev $swp1 master
+ bridge vlan del vid $vid dev $swp2 master
+}
+
+__ingress_stp_filter_test()
+{
+ local trap_name="ingress_spanning_tree_filter"
+ local dmac=de:ad:be:ef:13:37
+ local group_name="l2_drops"
+ local state=$1; shift
+ local mz_pid
+ local vid=20
+
+ bridge vlan add vid $vid dev $swp2 master
+ bridge vlan add vid $vid dev $swp1 master
+ ip link set dev $swp1 type bridge_slave state $state
+
+ tc filter add dev $swp2 egress protocol ip pref 1 handle 101 \
+ flower dst_mac $dmac action drop
+
+ $MZ $h1 -Q $vid -c 0 -p 100 -a own -b $dmac -t ip -d 1msec -q &
+ mz_pid=$!
+
+ l2_drops_test $trap_name $group_name
+
+ # Change STP state to forwarding and make sure packets are no longer
+ # dropped.
+ ip link set dev $swp1 type bridge_slave state 3
+ devlink_trap_action_set $trap_name "trap"
+
+ devlink_trap_stats_idle_test $trap_name
+ check_err $? "Trap stats not idle when packets should not be dropped"
+ devlink_trap_group_stats_idle_test $group_name
+ check_err $? "Trap group stats not idle with when packets should not be dropped"
+
+ tc_check_packets "dev $swp2 egress" 101 0
+ check_fail $? "Packets not forwarded when should"
+
+ devlink_trap_action_set $trap_name "drop"
+
+ l2_drops_cleanup $mz_pid
+
+ bridge vlan del vid $vid dev $swp1 master
+ bridge vlan del vid $vid dev $swp2 master
+}
+
+ingress_stp_filter_listening_test()
+{
+ local state=$1; shift
+
+ RET=0
+
+ __ingress_stp_filter_test $state
+
+ log_test "Ingress STP filter - listening state"
+}
+
+ingress_stp_filter_learning_test()
+{
+ local state=$1; shift
+
+ RET=0
+
+ __ingress_stp_filter_test $state
+
+ log_test "Ingress STP filter - learning state"
+}
+
+ingress_stp_filter_test()
+{
+ ingress_stp_filter_listening_test 1
+ ingress_stp_filter_learning_test 2
+}
+
+port_list_is_empty_uc_test()
+{
+ local trap_name="port_list_is_empty"
+ local dmac=de:ad:be:ef:13:37
+ local group_name="l2_drops"
+ local mz_pid
+
+ # Disable unicast flooding on both ports, so that packets cannot egress
+ # any port.
+ ip link set dev $swp1 type bridge_slave flood off
+ ip link set dev $swp2 type bridge_slave flood off
+
+ RET=0
+
+ tc filter add dev $swp2 egress protocol ip pref 1 handle 101 \
+ flower dst_mac $dmac action drop
+
+ $MZ $h1 -c 0 -p 100 -a own -b $dmac -t ip -d 1msec -q &
+ mz_pid=$!
+
+ l2_drops_test $trap_name $group_name
+
+ # Allow packets to be flooded to one port.
+ ip link set dev $swp2 type bridge_slave flood on
+ devlink_trap_action_set $trap_name "trap"
+
+ devlink_trap_stats_idle_test $trap_name
+ check_err $? "Trap stats not idle when packets should not be dropped"
+ devlink_trap_group_stats_idle_test $group_name
+ check_err $? "Trap group stats not idle with when packets should not be dropped"
+
+ tc_check_packets "dev $swp2 egress" 101 0
+ check_fail $? "Packets not forwarded when should"
+
+ devlink_trap_action_set $trap_name "drop"
+
+ log_test "Port list is empty - unicast"
+
+ l2_drops_cleanup $mz_pid
+
+ ip link set dev $swp1 type bridge_slave flood on
+}
+
+port_list_is_empty_mc_test()
+{
+ local trap_name="port_list_is_empty"
+ local dmac=01:00:5e:00:00:01
+ local group_name="l2_drops"
+ local dip=239.0.0.1
+ local mz_pid
+
+ # Disable multicast flooding on both ports, so that packets cannot
+ # egress any port. We also need to flush IP addresses from the bridge
+ # in order to prevent packets from being flooded to the router port.
+ ip link set dev $swp1 type bridge_slave mcast_flood off
+ ip link set dev $swp2 type bridge_slave mcast_flood off
+ ip address flush dev br0
+
+ RET=0
+
+ tc filter add dev $swp2 egress protocol ip pref 1 handle 101 \
+ flower dst_mac $dmac action drop
+
+ $MZ $h1 -c 0 -p 100 -a own -b $dmac -t ip -B $dip -d 1msec -q &
+ mz_pid=$!
+
+ l2_drops_test $trap_name $group_name
+
+ # Allow packets to be flooded to one port.
+ ip link set dev $swp2 type bridge_slave mcast_flood on
+ devlink_trap_action_set $trap_name "trap"
+
+ devlink_trap_stats_idle_test $trap_name
+ check_err $? "Trap stats not idle when packets should not be dropped"
+ devlink_trap_group_stats_idle_test $group_name
+ check_err $? "Trap group stats not idle with when packets should not be dropped"
+
+ tc_check_packets "dev $swp2 egress" 101 0
+ check_fail $? "Packets not forwarded when should"
+
+ devlink_trap_action_set $trap_name "drop"
+
+ log_test "Port list is empty - multicast"
+
+ l2_drops_cleanup $mz_pid
+
+ ip link set dev $swp1 type bridge_slave mcast_flood on
+}
+
+port_list_is_empty_test()
+{
+ port_list_is_empty_uc_test
+ port_list_is_empty_mc_test
+}
+
+port_loopback_filter_uc_test()
+{
+ local trap_name="port_loopback_filter"
+ local dmac=de:ad:be:ef:13:37
+ local group_name="l2_drops"
+ local mz_pid
+
+ # Make sure packets can only egress the input port.
+ ip link set dev $swp2 type bridge_slave flood off
+
+ RET=0
+
+ tc filter add dev $swp2 egress protocol ip pref 1 handle 101 \
+ flower dst_mac $dmac action drop
+
+ $MZ $h1 -c 0 -p 100 -a own -b $dmac -t ip -d 1msec -q &
+ mz_pid=$!
+
+ l2_drops_test $trap_name $group_name
+
+ # Allow packets to be flooded.
+ ip link set dev $swp2 type bridge_slave flood on
+ devlink_trap_action_set $trap_name "trap"
+
+ devlink_trap_stats_idle_test $trap_name
+ check_err $? "Trap stats not idle when packets should not be dropped"
+ devlink_trap_group_stats_idle_test $group_name
+ check_err $? "Trap group stats not idle with when packets should not be dropped"
+
+ tc_check_packets "dev $swp2 egress" 101 0
+ check_fail $? "Packets not forwarded when should"
+
+ devlink_trap_action_set $trap_name "drop"
+
+ log_test "Port loopback filter - unicast"
+
+ l2_drops_cleanup $mz_pid
+}
+
+port_loopback_filter_test()
+{
+ port_loopback_filter_uc_test
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/drivers/net/mlxsw/fib_offload.sh b/tools/testing/selftests/drivers/net/mlxsw/fib_offload.sh
new file mode 100755
index 000000000000..e99ae500f387
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/mlxsw/fib_offload.sh
@@ -0,0 +1,349 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test unicast FIB offload indication.
+
+lib_dir=$(dirname $0)/../../../net/forwarding
+
+ALL_TESTS="
+ ipv6_route_add
+ ipv6_route_replace
+ ipv6_route_nexthop_group_share
+ ipv6_route_rate
+"
+NUM_NETIFS=4
+source $lib_dir/lib.sh
+source $lib_dir/devlink_lib.sh
+
+tor1_create()
+{
+ simple_if_init $tor1_p1 2001:db8:1::2/128 2001:db8:1::3/128
+}
+
+tor1_destroy()
+{
+ simple_if_fini $tor1_p1 2001:db8:1::2/128 2001:db8:1::3/128
+}
+
+tor2_create()
+{
+ simple_if_init $tor2_p1 2001:db8:2::2/128 2001:db8:2::3/128
+}
+
+tor2_destroy()
+{
+ simple_if_fini $tor2_p1 2001:db8:2::2/128 2001:db8:2::3/128
+}
+
+spine_create()
+{
+ ip link set dev $spine_p1 up
+ ip link set dev $spine_p2 up
+
+ __addr_add_del $spine_p1 add 2001:db8:1::1/64
+ __addr_add_del $spine_p2 add 2001:db8:2::1/64
+}
+
+spine_destroy()
+{
+ __addr_add_del $spine_p2 del 2001:db8:2::1/64
+ __addr_add_del $spine_p1 del 2001:db8:1::1/64
+
+ ip link set dev $spine_p2 down
+ ip link set dev $spine_p1 down
+}
+
+ipv6_offload_check()
+{
+ local pfx="$1"; shift
+ local expected_num=$1; shift
+ local num
+
+ # Try to avoid races with route offload
+ sleep .1
+
+ num=$(ip -6 route show match ${pfx} | grep "offload" | wc -l)
+
+ if [ $num -eq $expected_num ]; then
+ return 0
+ fi
+
+ return 1
+}
+
+ipv6_route_add_prefix()
+{
+ RET=0
+
+ # Add a prefix route and check that it is offloaded.
+ ip -6 route add 2001:db8:3::/64 dev $spine_p1 metric 100
+ ipv6_offload_check "2001:db8:3::/64 dev $spine_p1 metric 100" 1
+ check_err $? "prefix route not offloaded"
+
+ # Append an identical prefix route with an higher metric and check that
+ # offload indication did not change.
+ ip -6 route append 2001:db8:3::/64 dev $spine_p1 metric 200
+ ipv6_offload_check "2001:db8:3::/64 dev $spine_p1 metric 100" 1
+ check_err $? "lowest metric not offloaded after append"
+ ipv6_offload_check "2001:db8:3::/64 dev $spine_p1 metric 200" 0
+ check_err $? "highest metric offloaded when should not"
+
+ # Prepend an identical prefix route with lower metric and check that
+ # it is offloaded and the others are not.
+ ip -6 route append 2001:db8:3::/64 dev $spine_p1 metric 10
+ ipv6_offload_check "2001:db8:3::/64 dev $spine_p1 metric 10" 1
+ check_err $? "lowest metric not offloaded after prepend"
+ ipv6_offload_check "2001:db8:3::/64 dev $spine_p1 metric 100" 0
+ check_err $? "mid metric offloaded when should not"
+ ipv6_offload_check "2001:db8:3::/64 dev $spine_p1 metric 200" 0
+ check_err $? "highest metric offloaded when should not"
+
+ # Delete the routes and add the same route with a different nexthop
+ # device. Check that it is offloaded.
+ ip -6 route flush 2001:db8:3::/64 dev $spine_p1
+ ip -6 route add 2001:db8:3::/64 dev $spine_p2
+ ipv6_offload_check "2001:db8:3::/64 dev $spine_p2" 1
+
+ log_test "IPv6 prefix route add"
+
+ ip -6 route flush 2001:db8:3::/64
+}
+
+ipv6_route_add_mpath()
+{
+ RET=0
+
+ # Add a multipath route and check that it is offloaded.
+ ip -6 route add 2001:db8:3::/64 metric 100 \
+ nexthop via 2001:db8:1::2 dev $spine_p1 \
+ nexthop via 2001:db8:2::2 dev $spine_p2
+ ipv6_offload_check "2001:db8:3::/64 metric 100" 2
+ check_err $? "multipath route not offloaded when should"
+
+ # Append another nexthop and check that it is offloaded as well.
+ ip -6 route append 2001:db8:3::/64 metric 100 \
+ nexthop via 2001:db8:1::3 dev $spine_p1
+ ipv6_offload_check "2001:db8:3::/64 metric 100" 3
+ check_err $? "appended nexthop not offloaded when should"
+
+ # Mimic route replace by removing the route and adding it back with
+ # only two nexthops.
+ ip -6 route del 2001:db8:3::/64
+ ip -6 route add 2001:db8:3::/64 metric 100 \
+ nexthop via 2001:db8:1::2 dev $spine_p1 \
+ nexthop via 2001:db8:2::2 dev $spine_p2
+ ipv6_offload_check "2001:db8:3::/64 metric 100" 2
+ check_err $? "multipath route not offloaded after delete & add"
+
+ # Append a nexthop with an higher metric and check that the offload
+ # indication did not change.
+ ip -6 route append 2001:db8:3::/64 metric 200 \
+ nexthop via 2001:db8:1::3 dev $spine_p1
+ ipv6_offload_check "2001:db8:3::/64 metric 100" 2
+ check_err $? "lowest metric not offloaded after append"
+ ipv6_offload_check "2001:db8:3::/64 metric 200" 0
+ check_err $? "highest metric offloaded when should not"
+
+ # Prepend a nexthop with a lower metric and check that it is offloaded
+ # and the others are not.
+ ip -6 route append 2001:db8:3::/64 metric 10 \
+ nexthop via 2001:db8:1::3 dev $spine_p1
+ ipv6_offload_check "2001:db8:3::/64 metric 10" 1
+ check_err $? "lowest metric not offloaded after prepend"
+ ipv6_offload_check "2001:db8:3::/64 metric 100" 0
+ check_err $? "mid metric offloaded when should not"
+ ipv6_offload_check "2001:db8:3::/64 metric 200" 0
+ check_err $? "highest metric offloaded when should not"
+
+ log_test "IPv6 multipath route add"
+
+ ip -6 route flush 2001:db8:3::/64
+}
+
+ipv6_route_add()
+{
+ ipv6_route_add_prefix
+ ipv6_route_add_mpath
+}
+
+ipv6_route_replace()
+{
+ RET=0
+
+ # Replace prefix route with prefix route.
+ ip -6 route add 2001:db8:3::/64 metric 100 dev $spine_p1
+ ipv6_offload_check "2001:db8:3::/64 metric 100" 1
+ check_err $? "prefix route not offloaded when should"
+ ip -6 route replace 2001:db8:3::/64 metric 100 dev $spine_p2
+ ipv6_offload_check "2001:db8:3::/64 metric 100" 1
+ check_err $? "prefix route not offloaded after replace"
+
+ # Replace prefix route with multipath route.
+ ip -6 route replace 2001:db8:3::/64 metric 100 \
+ nexthop via 2001:db8:1::2 dev $spine_p1 \
+ nexthop via 2001:db8:2::2 dev $spine_p2
+ ipv6_offload_check "2001:db8:3::/64 metric 100" 2
+ check_err $? "multipath route not offloaded after replace"
+
+ # Replace multipath route with prefix route. A prefix route cannot
+ # replace a multipath route, so it is appended.
+ ip -6 route replace 2001:db8:3::/64 metric 100 dev $spine_p1
+ ipv6_offload_check "2001:db8:3::/64 metric 100 dev $spine_p1" 0
+ check_err $? "prefix route offloaded after 'replacing' multipath route"
+ ipv6_offload_check "2001:db8:3::/64 metric 100" 2
+ check_err $? "multipath route not offloaded after being 'replaced' by prefix route"
+
+ # Replace multipath route with multipath route.
+ ip -6 route replace 2001:db8:3::/64 metric 100 \
+ nexthop via 2001:db8:1::3 dev $spine_p1 \
+ nexthop via 2001:db8:2::3 dev $spine_p2
+ ipv6_offload_check "2001:db8:3::/64 metric 100" 2
+ check_err $? "multipath route not offloaded after replacing multipath route"
+
+ # Replace a non-existing multipath route with a multipath route and
+ # check that it is appended and not offloaded.
+ ip -6 route replace 2001:db8:3::/64 metric 200 \
+ nexthop via 2001:db8:1::3 dev $spine_p1 \
+ nexthop via 2001:db8:2::3 dev $spine_p2
+ ipv6_offload_check "2001:db8:3::/64 metric 100" 2
+ check_err $? "multipath route not offloaded after non-existing route was 'replaced'"
+ ipv6_offload_check "2001:db8:3::/64 metric 200" 0
+ check_err $? "multipath route offloaded after 'replacing' non-existing route"
+
+ log_test "IPv6 route replace"
+
+ ip -6 route flush 2001:db8:3::/64
+}
+
+ipv6_route_nexthop_group_share()
+{
+ RET=0
+
+ # The driver consolidates identical nexthop groups in order to reduce
+ # the resource usage in its adjacency table. Check that the deletion
+ # of one multipath route using the group does not affect the other.
+ ip -6 route add 2001:db8:3::/64 \
+ nexthop via 2001:db8:1::2 dev $spine_p1 \
+ nexthop via 2001:db8:2::2 dev $spine_p2
+ ip -6 route add 2001:db8:4::/64 \
+ nexthop via 2001:db8:1::2 dev $spine_p1 \
+ nexthop via 2001:db8:2::2 dev $spine_p2
+ ipv6_offload_check "2001:db8:3::/64" 2
+ check_err $? "multipath route not offloaded when should"
+ ipv6_offload_check "2001:db8:4::/64" 2
+ check_err $? "multipath route not offloaded when should"
+ ip -6 route del 2001:db8:3::/64
+ ipv6_offload_check "2001:db8:4::/64" 2
+ check_err $? "multipath route not offloaded after deletion of route sharing the nexthop group"
+
+ # Check that after unsharing a nexthop group the routes are still
+ # marked as offloaded.
+ ip -6 route add 2001:db8:3::/64 \
+ nexthop via 2001:db8:1::2 dev $spine_p1 \
+ nexthop via 2001:db8:2::2 dev $spine_p2
+ ip -6 route del 2001:db8:4::/64 \
+ nexthop via 2001:db8:1::2 dev $spine_p1
+ ipv6_offload_check "2001:db8:4::/64" 1
+ check_err $? "singlepath route not offloaded after unsharing the nexthop group"
+ ipv6_offload_check "2001:db8:3::/64" 2
+ check_err $? "multipath route not offloaded after unsharing the nexthop group"
+
+ log_test "IPv6 nexthop group sharing"
+
+ ip -6 route flush 2001:db8:3::/64
+ ip -6 route flush 2001:db8:4::/64
+}
+
+ipv6_route_rate()
+{
+ local batch_dir=$(mktemp -d)
+ local num_rts=$((40 * 1024))
+ local num_nhs=16
+ local total
+ local start
+ local diff
+ local end
+ local nhs
+ local i
+
+ RET=0
+
+ # Prepare 40K /64 multipath routes with 16 nexthops each and check how
+ # long it takes to add them. A limit of 60 seconds is set. It is much
+ # higher than insertion should take and meant to flag a serious
+ # regression.
+ total=$((nums_nhs * num_rts))
+
+ for i in $(seq 1 $num_nhs); do
+ ip -6 address add 2001:db8:1::10:$i/128 dev $tor1_p1
+ nexthops+=" nexthop via 2001:db8:1::10:$i dev $spine_p1"
+ done
+
+ for i in $(seq 1 $num_rts); do
+ echo "route add 2001:db8:8:$(printf "%x" $i)::/64$nexthops" \
+ >> $batch_dir/add.batch
+ echo "route del 2001:db8:8:$(printf "%x" $i)::/64$nexthops" \
+ >> $batch_dir/del.batch
+ done
+
+ start=$(date +%s.%N)
+
+ ip -batch $batch_dir/add.batch
+ count=$(ip -6 route show | grep offload | wc -l)
+ while [ $count -lt $total ]; do
+ sleep .01
+ count=$(ip -6 route show | grep offload | wc -l)
+ done
+
+ end=$(date +%s.%N)
+
+ diff=$(echo "$end - $start" | bc -l)
+ test "$(echo "$diff > 60" | bc -l)" -eq 0
+ check_err $? "route insertion took too long"
+ log_info "inserted $num_rts routes in $diff seconds"
+
+ log_test "IPv6 routes insertion rate"
+
+ ip -batch $batch_dir/del.batch
+ for i in $(seq 1 $num_nhs); do
+ ip -6 address del 2001:db8:1::10:$i/128 dev $tor1_p1
+ done
+ rm -rf $batch_dir
+}
+
+setup_prepare()
+{
+ spine_p1=${NETIFS[p1]}
+ tor1_p1=${NETIFS[p2]}
+
+ spine_p2=${NETIFS[p3]}
+ tor2_p1=${NETIFS[p4]}
+
+ vrf_prepare
+ forwarding_enable
+
+ tor1_create
+ tor2_create
+ spine_create
+}
+
+cleanup()
+{
+ pre_cleanup
+
+ spine_destroy
+ tor2_destroy
+ tor1_destroy
+
+ forwarding_restore
+ vrf_cleanup
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh b/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh
index 40f16f2a3afd..5cbff8038f84 100755
--- a/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh
+++ b/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_bridge.sh
@@ -36,8 +36,6 @@ source $lib_dir/lib.sh
h1_create()
{
- local dscp;
-
simple_if_init $h1 192.0.2.1/28
tc qdisc add dev $h1 clsact
dscp_capture_install $h1 10
@@ -67,6 +65,7 @@ h2_destroy()
dscp_map()
{
local base=$1; shift
+ local prio
for prio in {0..7}; do
echo app=$prio,5,$((base + prio))
@@ -138,6 +137,7 @@ dscp_ping_test()
local prio=$1; shift
local dev_10=$1; shift
local dev_20=$1; shift
+ local key
local dscp_10=$(((prio + 10) << 2))
local dscp_20=$(((prio + 20) << 2))
@@ -175,6 +175,8 @@ dscp_ping_test()
test_dscp()
{
+ local prio
+
for prio in {0..7}; do
dscp_ping_test v$h1 192.0.2.1 192.0.2.2 $prio $h1 $h2
done
diff --git a/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_router.sh b/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_router.sh
index 9faf02e32627..c745ce3befee 100755
--- a/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_router.sh
+++ b/tools/testing/selftests/drivers/net/mlxsw/qos_dscp_router.sh
@@ -31,6 +31,7 @@ ALL_TESTS="
ping_ipv4
test_update
test_no_update
+ test_dscp_leftover
"
lib_dir=$(dirname $0)/../../../net/forwarding
@@ -50,10 +51,13 @@ reprioritize()
echo ${reprio[$in]}
}
-h1_create()
+zero()
{
- local dscp;
+ echo 0
+}
+h1_create()
+{
simple_if_init $h1 192.0.2.1/28
tc qdisc add dev $h1 clsact
dscp_capture_install $h1 0
@@ -87,6 +91,7 @@ h2_destroy()
dscp_map()
{
local base=$1; shift
+ local prio
for prio in {0..7}; do
echo app=$prio,5,$((base + prio))
@@ -156,6 +161,7 @@ dscp_ping_test()
local reprio=$1; shift
local dev1=$1; shift
local dev2=$1; shift
+ local i
local prio2=$($reprio $prio) # ICMP Request egress prio
local prio3=$($reprio $prio2) # ICMP Response egress prio
@@ -205,6 +211,7 @@ __test_update()
{
local update=$1; shift
local reprio=$1; shift
+ local prio
sysctl_restore net.ipv4.ip_forward_update_priority
sysctl_set net.ipv4.ip_forward_update_priority $update
@@ -224,6 +231,19 @@ test_no_update()
__test_update 0 echo
}
+# Test that when the last APP rule is removed, the prio->DSCP map is properly
+# set to zeroes, and that the last APP rule does not stay active in the ASIC.
+test_dscp_leftover()
+{
+ lldptool -T -i $swp2 -V APP -d $(dscp_map 0) >/dev/null
+ lldpad_app_wait_del
+
+ __test_update 0 zero
+
+ lldptool -T -i $swp2 -V APP $(dscp_map 0) >/dev/null
+ lldpad_app_wait_set $swp2
+}
+
trap cleanup EXIT
setup_prepare
diff --git a/tools/testing/selftests/drivers/net/mlxsw/qos_mc_aware.sh b/tools/testing/selftests/drivers/net/mlxsw/qos_mc_aware.sh
index 71231ad2dbfb..47315fe48d5a 100755
--- a/tools/testing/selftests/drivers/net/mlxsw/qos_mc_aware.sh
+++ b/tools/testing/selftests/drivers/net/mlxsw/qos_mc_aware.sh
@@ -262,7 +262,7 @@ test_mc_aware()
stop_traffic
- log_test "UC performace under MC overload"
+ log_test "UC performance under MC overload"
echo "UC-only throughput $(humanize $ucth1)"
echo "UC+MC throughput $(humanize $ucth2)"
@@ -316,7 +316,7 @@ test_uc_aware()
stop_traffic
- log_test "MC performace under UC overload"
+ log_test "MC performance under UC overload"
echo " ingress UC throughput $(humanize ${uc_ir})"
echo " egress UC throughput $(humanize ${uc_er})"
echo " sent $attempts BC ARPs, got $passes responses"
diff --git a/tools/testing/selftests/drivers/net/mlxsw/rtnetlink.sh b/tools/testing/selftests/drivers/net/mlxsw/rtnetlink.sh
index 1c30f302a1e7..5c39e5f6a480 100755
--- a/tools/testing/selftests/drivers/net/mlxsw/rtnetlink.sh
+++ b/tools/testing/selftests/drivers/net/mlxsw/rtnetlink.sh
@@ -28,6 +28,7 @@ ALL_TESTS="
vlan_interface_uppers_test
bridge_extern_learn_test
neigh_offload_test
+ nexthop_offload_test
devlink_reload_test
"
NUM_NETIFS=2
@@ -607,6 +608,52 @@ neigh_offload_test()
ip -4 address del 192.0.2.1/24 dev $swp1
}
+nexthop_offload_test()
+{
+ # Test that IPv4 and IPv6 nexthops are marked as offloaded
+ RET=0
+
+ sysctl_set net.ipv6.conf.$swp2.keep_addr_on_down 1
+ simple_if_init $swp1 192.0.2.1/24 2001:db8:1::1/64
+ simple_if_init $swp2 192.0.2.2/24 2001:db8:1::2/64
+ setup_wait
+
+ ip -4 route add 198.51.100.0/24 vrf v$swp1 \
+ nexthop via 192.0.2.2 dev $swp1
+ ip -6 route add 2001:db8:2::/64 vrf v$swp1 \
+ nexthop via 2001:db8:1::2 dev $swp1
+
+ ip -4 route show 198.51.100.0/24 vrf v$swp1 | grep -q offload
+ check_err $? "ipv4 nexthop not marked as offloaded when should"
+ ip -6 route show 2001:db8:2::/64 vrf v$swp1 | grep -q offload
+ check_err $? "ipv6 nexthop not marked as offloaded when should"
+
+ ip link set dev $swp2 down
+ sleep 1
+
+ ip -4 route show 198.51.100.0/24 vrf v$swp1 | grep -q offload
+ check_fail $? "ipv4 nexthop marked as offloaded when should not"
+ ip -6 route show 2001:db8:2::/64 vrf v$swp1 | grep -q offload
+ check_fail $? "ipv6 nexthop marked as offloaded when should not"
+
+ ip link set dev $swp2 up
+ setup_wait
+
+ ip -4 route show 198.51.100.0/24 vrf v$swp1 | grep -q offload
+ check_err $? "ipv4 nexthop not marked as offloaded after neigh add"
+ ip -6 route show 2001:db8:2::/64 vrf v$swp1 | grep -q offload
+ check_err $? "ipv6 nexthop not marked as offloaded after neigh add"
+
+ log_test "nexthop offload indication"
+
+ ip -6 route del 2001:db8:2::/64 vrf v$swp1
+ ip -4 route del 198.51.100.0/24 vrf v$swp1
+
+ simple_if_fini $swp2 192.0.2.2/24 2001:db8:1::2/64
+ simple_if_fini $swp1 192.0.2.1/24 2001:db8:1::1/64
+ sysctl_restore net.ipv6.conf.$swp2.keep_addr_on_down
+}
+
devlink_reload_test()
{
# Test that after executing all the above configuration tests, a
diff --git a/tools/testing/selftests/drivers/net/netdevsim/devlink.sh b/tools/testing/selftests/drivers/net/netdevsim/devlink.sh
new file mode 100755
index 000000000000..115837355eaf
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/netdevsim/devlink.sh
@@ -0,0 +1,165 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+lib_dir=$(dirname $0)/../../../net/forwarding
+
+ALL_TESTS="fw_flash_test params_test regions_test"
+NUM_NETIFS=0
+source $lib_dir/lib.sh
+
+BUS_ADDR=10
+PORT_COUNT=4
+DEV_NAME=netdevsim$BUS_ADDR
+SYSFS_NET_DIR=/sys/bus/netdevsim/devices/$DEV_NAME/net/
+DEBUGFS_DIR=/sys/kernel/debug/netdevsim/$DEV_NAME/
+DL_HANDLE=netdevsim/$DEV_NAME
+
+fw_flash_test()
+{
+ RET=0
+
+ devlink dev flash $DL_HANDLE file dummy
+ check_err $? "Failed to flash with status updates on"
+
+ echo "n"> $DEBUGFS_DIR/fw_update_status
+ check_err $? "Failed to disable status updates"
+
+ devlink dev flash $DL_HANDLE file dummy
+ check_err $? "Failed to flash with status updates off"
+
+ log_test "fw flash test"
+}
+
+param_get()
+{
+ local name=$1
+
+ cmd_jq "devlink dev param show $DL_HANDLE name $name -j" \
+ '.[][][].values[] | select(.cmode == "driverinit").value'
+}
+
+param_set()
+{
+ local name=$1
+ local value=$2
+
+ devlink dev param set $DL_HANDLE name $name cmode driverinit value $value
+}
+
+check_value()
+{
+ local name=$1
+ local phase_name=$2
+ local expected_param_value=$3
+ local expected_debugfs_value=$4
+ local value
+
+ value=$(param_get $name)
+ check_err $? "Failed to get $name param value"
+ [ "$value" == "$expected_param_value" ]
+ check_err $? "Unexpected $phase_name $name param value"
+ value=$(<$DEBUGFS_DIR/$name)
+ check_err $? "Failed to get $name debugfs value"
+ [ "$value" == "$expected_debugfs_value" ]
+ check_err $? "Unexpected $phase_name $name debugfs value"
+}
+
+params_test()
+{
+ RET=0
+
+ local max_macs
+ local test1
+
+ check_value max_macs initial 32 32
+ check_value test1 initial true Y
+
+ param_set max_macs 16
+ check_err $? "Failed to set max_macs param value"
+ param_set test1 false
+ check_err $? "Failed to set test1 param value"
+
+ check_value max_macs post-set 16 32
+ check_value test1 post-set false Y
+
+ devlink dev reload $DL_HANDLE
+
+ check_value max_macs post-reload 16 16
+ check_value test1 post-reload false N
+
+ log_test "params test"
+}
+
+check_region_size()
+{
+ local name=$1
+ local size
+
+ size=$(devlink region show $DL_HANDLE/$name -j | jq -e -r '.[][].size')
+ check_err $? "Failed to get $name region size"
+ [ $size -eq 32768 ]
+ check_err $? "Invalid $name region size"
+}
+
+check_region_snapshot_count()
+{
+ local name=$1
+ local phase_name=$2
+ local expected_count=$3
+ local count
+
+ count=$(devlink region show $DL_HANDLE/$name -j | jq -e -r '.[][].snapshot | length')
+ [ $count -eq $expected_count ]
+ check_err $? "Unexpected $phase_name snapshot count"
+}
+
+regions_test()
+{
+ RET=0
+
+ local count
+
+ check_region_size dummy
+ check_region_snapshot_count dummy initial 0
+
+ echo ""> $DEBUGFS_DIR/take_snapshot
+ check_err $? "Failed to take first dummy region snapshot"
+ check_region_snapshot_count dummy post-first-snapshot 1
+
+ echo ""> $DEBUGFS_DIR/take_snapshot
+ check_err $? "Failed to take second dummy region snapshot"
+ check_region_snapshot_count dummy post-second-snapshot 2
+
+ echo ""> $DEBUGFS_DIR/take_snapshot
+ check_err $? "Failed to take third dummy region snapshot"
+ check_region_snapshot_count dummy post-third-snapshot 3
+
+ devlink region del $DL_HANDLE/dummy snapshot 1
+ check_err $? "Failed to delete first dummy region snapshot"
+
+ check_region_snapshot_count dummy post-first-delete 2
+
+ log_test "regions test"
+}
+
+setup_prepare()
+{
+ modprobe netdevsim
+ echo "$BUS_ADDR $PORT_COUNT" > /sys/bus/netdevsim/new_device
+ while [ ! -d $SYSFS_NET_DIR ] ; do :; done
+}
+
+cleanup()
+{
+ pre_cleanup
+ echo "$BUS_ADDR" > /sys/bus/netdevsim/del_device
+ modprobe -r netdevsim
+}
+
+trap cleanup EXIT
+
+setup_prepare
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/drivers/net/netdevsim/devlink_trap.sh b/tools/testing/selftests/drivers/net/netdevsim/devlink_trap.sh
new file mode 100755
index 000000000000..f101ab9441e2
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/netdevsim/devlink_trap.sh
@@ -0,0 +1,364 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# This test is for checking devlink-trap functionality. It makes use of
+# netdevsim which implements the required callbacks.
+
+lib_dir=$(dirname $0)/../../../net/forwarding
+
+ALL_TESTS="
+ init_test
+ trap_action_test
+ trap_metadata_test
+ bad_trap_test
+ bad_trap_action_test
+ trap_stats_test
+ trap_group_action_test
+ bad_trap_group_test
+ trap_group_stats_test
+ port_del_test
+ dev_del_test
+"
+NETDEVSIM_PATH=/sys/bus/netdevsim/
+DEV_ADDR=1337
+DEV=netdevsim${DEV_ADDR}
+DEVLINK_DEV=netdevsim/${DEV}
+SLEEP_TIME=1
+NETDEV=""
+NUM_NETIFS=0
+source $lib_dir/lib.sh
+source $lib_dir/devlink_lib.sh
+
+require_command udevadm
+
+modprobe netdevsim &> /dev/null
+if [ ! -d "$NETDEVSIM_PATH" ]; then
+ echo "SKIP: No netdevsim support"
+ exit 1
+fi
+
+if [ -d "${NETDEVSIM_PATH}/devices/netdevsim${DEV_ADDR}" ]; then
+ echo "SKIP: Device netdevsim${DEV_ADDR} already exists"
+ exit 1
+fi
+
+init_test()
+{
+ RET=0
+
+ test $(devlink_traps_num_get) -ne 0
+ check_err $? "No traps were registered"
+
+ log_test "Initialization"
+}
+
+trap_action_test()
+{
+ local orig_action
+ local trap_name
+ local action
+
+ RET=0
+
+ for trap_name in $(devlink_traps_get); do
+ # The action of non-drop traps cannot be changed.
+ if [ $(devlink_trap_type_get $trap_name) = "drop" ]; then
+ devlink_trap_action_set $trap_name "trap"
+ action=$(devlink_trap_action_get $trap_name)
+ if [ $action != "trap" ]; then
+ check_err 1 "Trap $trap_name did not change action to trap"
+ fi
+
+ devlink_trap_action_set $trap_name "drop"
+ action=$(devlink_trap_action_get $trap_name)
+ if [ $action != "drop" ]; then
+ check_err 1 "Trap $trap_name did not change action to drop"
+ fi
+ else
+ orig_action=$(devlink_trap_action_get $trap_name)
+
+ devlink_trap_action_set $trap_name "trap"
+ action=$(devlink_trap_action_get $trap_name)
+ if [ $action != $orig_action ]; then
+ check_err 1 "Trap $trap_name changed action when should not"
+ fi
+
+ devlink_trap_action_set $trap_name "drop"
+ action=$(devlink_trap_action_get $trap_name)
+ if [ $action != $orig_action ]; then
+ check_err 1 "Trap $trap_name changed action when should not"
+ fi
+ fi
+ done
+
+ log_test "Trap action"
+}
+
+trap_metadata_test()
+{
+ local trap_name
+
+ RET=0
+
+ for trap_name in $(devlink_traps_get); do
+ devlink_trap_metadata_test $trap_name "input_port"
+ check_err $? "Input port not reported as metadata of trap $trap_name"
+ done
+
+ log_test "Trap metadata"
+}
+
+bad_trap_test()
+{
+ RET=0
+
+ devlink_trap_action_set "made_up_trap" "drop"
+ check_fail $? "Did not get an error for non-existing trap"
+
+ log_test "Non-existing trap"
+}
+
+bad_trap_action_test()
+{
+ local traps_arr
+ local trap_name
+
+ RET=0
+
+ # Pick first trap.
+ traps_arr=($(devlink_traps_get))
+ trap_name=${traps_arr[0]}
+
+ devlink_trap_action_set $trap_name "made_up_action"
+ check_fail $? "Did not get an error for non-existing trap action"
+
+ log_test "Non-existing trap action"
+}
+
+trap_stats_test()
+{
+ local trap_name
+
+ RET=0
+
+ for trap_name in $(devlink_traps_get); do
+ devlink_trap_stats_idle_test $trap_name
+ check_err $? "Stats of trap $trap_name not idle when netdev down"
+
+ ip link set dev $NETDEV up
+
+ if [ $(devlink_trap_type_get $trap_name) = "drop" ]; then
+ devlink_trap_action_set $trap_name "trap"
+ devlink_trap_stats_idle_test $trap_name
+ check_fail $? "Stats of trap $trap_name idle when action is trap"
+
+ devlink_trap_action_set $trap_name "drop"
+ devlink_trap_stats_idle_test $trap_name
+ check_err $? "Stats of trap $trap_name not idle when action is drop"
+ else
+ devlink_trap_stats_idle_test $trap_name
+ check_fail $? "Stats of non-drop trap $trap_name idle when should not"
+ fi
+
+ ip link set dev $NETDEV down
+ done
+
+ log_test "Trap statistics"
+}
+
+trap_group_action_test()
+{
+ local curr_group group_name
+ local trap_name
+ local trap_type
+ local action
+
+ RET=0
+
+ for group_name in $(devlink_trap_groups_get); do
+ devlink_trap_group_action_set $group_name "trap"
+
+ for trap_name in $(devlink_traps_get); do
+ curr_group=$(devlink_trap_group_get $trap_name)
+ if [ $curr_group != $group_name ]; then
+ continue
+ fi
+
+ trap_type=$(devlink_trap_type_get $trap_name)
+ if [ $trap_type != "drop" ]; then
+ continue
+ fi
+
+ action=$(devlink_trap_action_get $trap_name)
+ if [ $action != "trap" ]; then
+ check_err 1 "Trap $trap_name did not change action to trap"
+ fi
+ done
+
+ devlink_trap_group_action_set $group_name "drop"
+
+ for trap_name in $(devlink_traps_get); do
+ curr_group=$(devlink_trap_group_get $trap_name)
+ if [ $curr_group != $group_name ]; then
+ continue
+ fi
+
+ trap_type=$(devlink_trap_type_get $trap_name)
+ if [ $trap_type != "drop" ]; then
+ continue
+ fi
+
+ action=$(devlink_trap_action_get $trap_name)
+ if [ $action != "drop" ]; then
+ check_err 1 "Trap $trap_name did not change action to drop"
+ fi
+ done
+ done
+
+ log_test "Trap group action"
+}
+
+bad_trap_group_test()
+{
+ RET=0
+
+ devlink_trap_group_action_set "made_up_trap_group" "drop"
+ check_fail $? "Did not get an error for non-existing trap group"
+
+ log_test "Non-existing trap group"
+}
+
+trap_group_stats_test()
+{
+ local group_name
+
+ RET=0
+
+ for group_name in $(devlink_trap_groups_get); do
+ devlink_trap_group_stats_idle_test $group_name
+ check_err $? "Stats of trap group $group_name not idle when netdev down"
+
+ ip link set dev $NETDEV up
+
+ devlink_trap_group_action_set $group_name "trap"
+ devlink_trap_group_stats_idle_test $group_name
+ check_fail $? "Stats of trap group $group_name idle when action is trap"
+
+ devlink_trap_group_action_set $group_name "drop"
+ ip link set dev $NETDEV down
+ done
+
+ log_test "Trap group statistics"
+}
+
+port_del_test()
+{
+ local group_name
+ local i
+
+ # The test never fails. It is meant to exercise different code paths
+ # and make sure we properly dismantle a port while packets are
+ # in-flight.
+ RET=0
+
+ devlink_traps_enable_all
+
+ for i in $(seq 1 10); do
+ ip link set dev $NETDEV up
+
+ sleep $SLEEP_TIME
+
+ netdevsim_port_destroy
+ netdevsim_port_create
+ udevadm settle
+ done
+
+ devlink_traps_disable_all
+
+ log_test "Port delete"
+}
+
+dev_del_test()
+{
+ local group_name
+ local i
+
+ # The test never fails. It is meant to exercise different code paths
+ # and make sure we properly unregister traps while packets are
+ # in-flight.
+ RET=0
+
+ devlink_traps_enable_all
+
+ for i in $(seq 1 10); do
+ ip link set dev $NETDEV up
+
+ sleep $SLEEP_TIME
+
+ cleanup
+ setup_prepare
+ done
+
+ devlink_traps_disable_all
+
+ log_test "Device delete"
+}
+
+netdevsim_dev_create()
+{
+ echo "$DEV_ADDR 0" > ${NETDEVSIM_PATH}/new_device
+}
+
+netdevsim_dev_destroy()
+{
+ echo "$DEV_ADDR" > ${NETDEVSIM_PATH}/del_device
+}
+
+netdevsim_port_create()
+{
+ echo 1 > ${NETDEVSIM_PATH}/devices/${DEV}/new_port
+}
+
+netdevsim_port_destroy()
+{
+ echo 1 > ${NETDEVSIM_PATH}/devices/${DEV}/del_port
+}
+
+setup_prepare()
+{
+ local netdev
+
+ netdevsim_dev_create
+
+ if [ ! -d "${NETDEVSIM_PATH}/devices/${DEV}" ]; then
+ echo "Failed to create netdevsim device"
+ exit 1
+ fi
+
+ netdevsim_port_create
+
+ if [ ! -d "${NETDEVSIM_PATH}/devices/${DEV}/net/" ]; then
+ echo "Failed to create netdevsim port"
+ exit 1
+ fi
+
+ # Wait for udev to rename newly created netdev.
+ udevadm settle
+
+ NETDEV=$(ls ${NETDEVSIM_PATH}/devices/${DEV}/net/)
+}
+
+cleanup()
+{
+ pre_cleanup
+ netdevsim_port_destroy
+ netdevsim_dev_destroy
+}
+
+trap cleanup EXIT
+
+setup_prepare
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/efivarfs/Makefile b/tools/testing/selftests/efivarfs/Makefile
index c49dcea69319..e3181338ba5e 100644
--- a/tools/testing/selftests/efivarfs/Makefile
+++ b/tools/testing/selftests/efivarfs/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
CFLAGS = -Wall
TEST_GEN_FILES := open-unlink create-read
diff --git a/tools/testing/selftests/exec/execveat.c b/tools/testing/selftests/exec/execveat.c
index 47cbf54d0801..cbb6efbdb786 100644
--- a/tools/testing/selftests/exec/execveat.c
+++ b/tools/testing/selftests/exec/execveat.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014 Google, Inc.
*
- * Licensed under the terms of the GNU GPL License version 2
- *
* Selftests for execveat(2).
*/
diff --git a/tools/testing/selftests/firmware/Makefile b/tools/testing/selftests/firmware/Makefile
index 261c81f08606..012b2cf69c11 100644
--- a/tools/testing/selftests/firmware/Makefile
+++ b/tools/testing/selftests/firmware/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# Makefile for firmware loading selftests
# No binaries, but make sure arg-less "make" doesn't trigger "run_tests"
diff --git a/tools/testing/selftests/firmware/fw_filesystem.sh b/tools/testing/selftests/firmware/fw_filesystem.sh
index a4320c4b44dc..56894477c8bd 100755
--- a/tools/testing/selftests/firmware/fw_filesystem.sh
+++ b/tools/testing/selftests/firmware/fw_filesystem.sh
@@ -116,6 +116,16 @@ config_set_name()
echo -n $1 > $DIR/config_name
}
+config_set_into_buf()
+{
+ echo 1 > $DIR/config_into_buf
+}
+
+config_unset_into_buf()
+{
+ echo 0 > $DIR/config_into_buf
+}
+
config_set_sync_direct()
{
echo 1 > $DIR/config_sync_direct
@@ -153,13 +163,21 @@ config_set_read_fw_idx()
read_firmwares()
{
+ if [ "$(cat $DIR/config_into_buf)" == "1" ]; then
+ fwfile="$FW_INTO_BUF"
+ else
+ fwfile="$FW"
+ fi
+ if [ "$1" = "xzonly" ]; then
+ fwfile="${fwfile}-orig"
+ fi
for i in $(seq 0 3); do
config_set_read_fw_idx $i
# Verify the contents are what we expect.
# -Z required for now -- check for yourself, md5sum
# on $FW and DIR/read_firmware will yield the same. Even
# cmp agrees, so something is off.
- if ! diff -q -Z "$FW" $DIR/read_firmware 2>/dev/null ; then
+ if ! diff -q -Z "$fwfile" $DIR/read_firmware 2>/dev/null ; then
echo "request #$i: firmware was not loaded" >&2
exit 1
fi
@@ -189,6 +207,18 @@ test_batched_request_firmware_nofile()
echo "OK"
}
+test_batched_request_firmware_into_buf_nofile()
+{
+ echo -n "Batched request_firmware_into_buf() nofile try #$1: "
+ config_reset
+ config_set_name nope-test-firmware.bin
+ config_set_into_buf
+ config_trigger_sync
+ read_firmwares_expect_nofile
+ release_all_firmware
+ echo "OK"
+}
+
test_batched_request_firmware_direct_nofile()
{
echo -n "Batched request_firmware_direct() nofile try #$1: "
@@ -246,17 +276,29 @@ test_request_firmware_nowait_custom_nofile()
test_batched_request_firmware()
{
- echo -n "Batched request_firmware() try #$1: "
+ echo -n "Batched request_firmware() $2 try #$1: "
+ config_reset
+ config_trigger_sync
+ read_firmwares $2
+ release_all_firmware
+ echo "OK"
+}
+
+test_batched_request_firmware_into_buf()
+{
+ echo -n "Batched request_firmware_into_buf() $2 try #$1: "
config_reset
+ config_set_name $TEST_FIRMWARE_INTO_BUF_FILENAME
+ config_set_into_buf
config_trigger_sync
- read_firmwares
+ read_firmwares $2
release_all_firmware
echo "OK"
}
test_batched_request_firmware_direct()
{
- echo -n "Batched request_firmware_direct() try #$1: "
+ echo -n "Batched request_firmware_direct() $2 try #$1: "
config_reset
config_set_sync_direct
config_trigger_sync
@@ -266,7 +308,7 @@ test_batched_request_firmware_direct()
test_request_firmware_nowait_uevent()
{
- echo -n "Batched request_firmware_nowait(uevent=true) try #$1: "
+ echo -n "Batched request_firmware_nowait(uevent=true) $2 try #$1: "
config_reset
config_trigger_async
release_all_firmware
@@ -275,11 +317,16 @@ test_request_firmware_nowait_uevent()
test_request_firmware_nowait_custom()
{
- echo -n "Batched request_firmware_nowait(uevent=false) try #$1: "
+ echo -n "Batched request_firmware_nowait(uevent=false) $2 try #$1: "
config_reset
config_unset_uevent
RANDOM_FILE_PATH=$(setup_random_file)
RANDOM_FILE="$(basename $RANDOM_FILE_PATH)"
+ if [ "$2" = "both" ]; then
+ xz -9 -C crc32 -k $RANDOM_FILE_PATH
+ elif [ "$2" = "xzonly" ]; then
+ xz -9 -C crc32 $RANDOM_FILE_PATH
+ fi
config_set_name $RANDOM_FILE
config_trigger_async
release_all_firmware
@@ -294,19 +341,23 @@ test_config_present
echo
echo "Testing with the file present..."
for i in $(seq 1 5); do
- test_batched_request_firmware $i
+ test_batched_request_firmware $i normal
+done
+
+for i in $(seq 1 5); do
+ test_batched_request_firmware_into_buf $i normal
done
for i in $(seq 1 5); do
- test_batched_request_firmware_direct $i
+ test_batched_request_firmware_direct $i normal
done
for i in $(seq 1 5); do
- test_request_firmware_nowait_uevent $i
+ test_request_firmware_nowait_uevent $i normal
done
for i in $(seq 1 5); do
- test_request_firmware_nowait_custom $i
+ test_request_firmware_nowait_custom $i normal
done
# Test for file not found, errors are expected, the failure would be
@@ -318,6 +369,10 @@ for i in $(seq 1 5); do
done
for i in $(seq 1 5); do
+ test_batched_request_firmware_into_buf_nofile $i
+done
+
+for i in $(seq 1 5); do
test_batched_request_firmware_direct_nofile $i
done
@@ -329,4 +384,55 @@ for i in $(seq 1 5); do
test_request_firmware_nowait_custom_nofile $i
done
+test "$HAS_FW_LOADER_COMPRESS" != "yes" && exit 0
+
+# test with both files present
+xz -9 -C crc32 -k $FW
+config_set_name $NAME
+echo
+echo "Testing with both plain and xz files present..."
+for i in $(seq 1 5); do
+ test_batched_request_firmware $i both
+done
+
+for i in $(seq 1 5); do
+ test_batched_request_firmware_into_buf $i both
+done
+
+for i in $(seq 1 5); do
+ test_batched_request_firmware_direct $i both
+done
+
+for i in $(seq 1 5); do
+ test_request_firmware_nowait_uevent $i both
+done
+
+for i in $(seq 1 5); do
+ test_request_firmware_nowait_custom $i both
+done
+
+# test with only xz file present
+mv "$FW" "${FW}-orig"
+echo
+echo "Testing with only xz file present..."
+for i in $(seq 1 5); do
+ test_batched_request_firmware $i xzonly
+done
+
+for i in $(seq 1 5); do
+ test_batched_request_firmware_into_buf $i xzonly
+done
+
+for i in $(seq 1 5); do
+ test_batched_request_firmware_direct $i xzonly
+done
+
+for i in $(seq 1 5); do
+ test_request_firmware_nowait_uevent $i xzonly
+done
+
+for i in $(seq 1 5); do
+ test_request_firmware_nowait_custom $i xzonly
+done
+
exit 0
diff --git a/tools/testing/selftests/firmware/fw_lib.sh b/tools/testing/selftests/firmware/fw_lib.sh
index 1cbb12e284a6..b879305a766d 100755
--- a/tools/testing/selftests/firmware/fw_lib.sh
+++ b/tools/testing/selftests/firmware/fw_lib.sh
@@ -9,6 +9,12 @@ DIR=/sys/devices/virtual/misc/test_firmware
PROC_CONFIG="/proc/config.gz"
TEST_DIR=$(dirname $0)
+# We need to load a different file to test request_firmware_into_buf
+# I believe the issue is firmware loaded cached vs. non-cached
+# with same filename is bungled.
+# To reproduce rename this to test-firmware.bin
+TEST_FIRMWARE_INTO_BUF_FILENAME=test-firmware-into-buf.bin
+
# Kselftest framework requirement - SKIP code is 4.
ksft_skip=4
@@ -50,6 +56,7 @@ check_setup()
{
HAS_FW_LOADER_USER_HELPER="$(kconfig_has CONFIG_FW_LOADER_USER_HELPER=y)"
HAS_FW_LOADER_USER_HELPER_FALLBACK="$(kconfig_has CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y)"
+ HAS_FW_LOADER_COMPRESS="$(kconfig_has CONFIG_FW_LOADER_COMPRESS=y)"
PROC_FW_IGNORE_SYSFS_FALLBACK="0"
PROC_FW_FORCE_SYSFS_FALLBACK="0"
@@ -84,6 +91,12 @@ check_setup()
fi
OLD_FWPATH="$(cat /sys/module/firmware_class/parameters/path)"
+
+ if [ "$HAS_FW_LOADER_COMPRESS" = "yes" ]; then
+ if ! which xz 2> /dev/null > /dev/null; then
+ HAS_FW_LOADER_COMPRESS=""
+ fi
+ fi
}
verify_reqs()
@@ -101,6 +114,8 @@ setup_tmp_file()
FWPATH=$(mktemp -d)
FW="$FWPATH/test-firmware.bin"
echo "ABCD0123" >"$FW"
+ FW_INTO_BUF="$FWPATH/$TEST_FIRMWARE_INTO_BUF_FILENAME"
+ echo "EFGH4567" >"$FW_INTO_BUF"
NAME=$(basename "$FW")
if [ "$TEST_REQS_FW_SET_CUSTOM_PATH" = "yes" ]; then
echo -n "$FWPATH" >/sys/module/firmware_class/parameters/path
@@ -168,6 +183,9 @@ test_finish()
if [ -f $FW ]; then
rm -f "$FW"
fi
+ if [ -f $FW_INTO_BUF ]; then
+ rm -f "$FW_INTO_BUF"
+ fi
if [ -d $FWPATH ]; then
rm -rf "$FWPATH"
fi
diff --git a/tools/testing/selftests/firmware/fw_run_tests.sh b/tools/testing/selftests/firmware/fw_run_tests.sh
index cffdd4eb0a57..8e14d555c197 100755
--- a/tools/testing/selftests/firmware/fw_run_tests.sh
+++ b/tools/testing/selftests/firmware/fw_run_tests.sh
@@ -11,6 +11,7 @@ source $TEST_DIR/fw_lib.sh
export HAS_FW_LOADER_USER_HELPER=""
export HAS_FW_LOADER_USER_HELPER_FALLBACK=""
+export HAS_FW_LOADER_COMPRESS=""
run_tests()
{
diff --git a/tools/testing/selftests/ftrace/ftracetest b/tools/testing/selftests/ftrace/ftracetest
index 136387422b00..063ecb290a5a 100755
--- a/tools/testing/selftests/ftrace/ftracetest
+++ b/tools/testing/selftests/ftrace/ftracetest
@@ -1,11 +1,11 @@
#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
# ftracetest - Ftrace test shell scripts
#
# Copyright (C) Hitachi Ltd., 2014
# Written by Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
#
-# Released under the terms of the GPL v2.
usage() { # errno [message]
[ ! -z "$2" ] && echo $2
@@ -23,9 +23,15 @@ echo " If <dir> is -, all logs output in console only"
exit $1
}
+# default error
+err_ret=1
+
+# kselftest skip code is 4
+err_skip=4
+
errexit() { # message
echo "Error: $1" 1>&2
- exit 1
+ exit $err_ret
}
# Ensuring user privilege
@@ -116,11 +122,31 @@ parse_opts() { # opts
}
# Parameters
-DEBUGFS_DIR=`grep debugfs /proc/mounts | cut -f2 -d' ' | head -1`
-if [ -z "$DEBUGFS_DIR" ]; then
- TRACING_DIR=`grep tracefs /proc/mounts | cut -f2 -d' ' | head -1`
-else
- TRACING_DIR=$DEBUGFS_DIR/tracing
+TRACING_DIR=`grep tracefs /proc/mounts | cut -f2 -d' ' | head -1`
+if [ -z "$TRACING_DIR" ]; then
+ DEBUGFS_DIR=`grep debugfs /proc/mounts | cut -f2 -d' ' | head -1`
+ if [ -z "$DEBUGFS_DIR" ]; then
+ # If tracefs exists, then so does /sys/kernel/tracing
+ if [ -d "/sys/kernel/tracing" ]; then
+ mount -t tracefs nodev /sys/kernel/tracing ||
+ errexit "Failed to mount /sys/kernel/tracing"
+ TRACING_DIR="/sys/kernel/tracing"
+ # If debugfs exists, then so does /sys/kernel/debug
+ elif [ -d "/sys/kernel/debug" ]; then
+ mount -t debugfs nodev /sys/kernel/debug ||
+ errexit "Failed to mount /sys/kernel/debug"
+ TRACING_DIR="/sys/kernel/debug/tracing"
+ else
+ err_ret=$err_skip
+ errexit "debugfs and tracefs are not configured in this kernel"
+ fi
+ else
+ TRACING_DIR="$DEBUGFS_DIR/tracing"
+ fi
+fi
+if [ ! -d "$TRACING_DIR" ]; then
+ err_ret=$err_skip
+ errexit "ftrace is not configured in this kernel"
fi
TOP_DIR=`absdir $0`
@@ -318,6 +344,7 @@ run_test() { # testfile
local testlog=/proc/self/fd/1
fi
export TMPDIR=`mktemp -d /tmp/ftracetest-dir.XXXXXX`
+ export FTRACETEST_ROOT=$TOP_DIR
echo "execute$INSTANCE: "$1 > $testlog
SIG_RESULT=0
if [ $VERBOSE -eq -1 ]; then
diff --git a/tools/testing/selftests/ftrace/test.d/functions b/tools/testing/selftests/ftrace/test.d/functions
index 779ec11f61bd..86986c4bba54 100644
--- a/tools/testing/selftests/ftrace/test.d/functions
+++ b/tools/testing/selftests/ftrace/test.d/functions
@@ -91,8 +91,8 @@ initialize_ftrace() { # Reset ftrace to initial-state
reset_events_filter
reset_ftrace_filter
disable_events
- echo > set_event_pid # event tracer is always on
- echo > set_ftrace_pid
+ [ -f set_event_pid ] && echo > set_event_pid
+ [ -f set_ftrace_pid ] && echo > set_ftrace_pid
[ -f set_ftrace_filter ] && echo | tee set_ftrace_*
[ -f set_graph_function ] && echo | tee set_graph_*
[ -f stack_trace_filter ] && echo > stack_trace_filter
@@ -115,7 +115,7 @@ ftrace_errlog_check() { # err-prefix command-with-error-pos-by-^ command-file
command=$(echo "$2" | tr -d ^)
echo "Test command: $command"
echo > error_log
- (! echo "$command" > "$3" ) 2> /dev/null
+ (! echo "$command" >> "$3" ) 2> /dev/null
grep "$1: error:" -A 3 error_log
N=$(tail -n 1 error_log | wc -c)
# " Command: " and "^\n" => 13
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_user.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_user.tc
new file mode 100644
index 000000000000..0f60087583d8
--- /dev/null
+++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_user.tc
@@ -0,0 +1,32 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# description: Kprobe event user-memory access
+
+[ -f kprobe_events ] || exit_unsupported # this is configurable
+
+grep -q '\$arg<N>' README || exit_unresolved # depends on arch
+grep -A10 "fetcharg:" README | grep -q 'ustring' || exit_unsupported
+grep -A10 "fetcharg:" README | grep -q '\[u\]<offset>' || exit_unsupported
+
+:;: "user-memory access syntax and ustring working on user memory";:
+echo 'p:myevent do_sys_open path=+0($arg2):ustring path2=+u0($arg2):string' \
+ > kprobe_events
+
+grep myevent kprobe_events | \
+ grep -q 'path=+0($arg2):ustring path2=+u0($arg2):string'
+echo 1 > events/kprobes/myevent/enable
+echo > /dev/null
+echo 0 > events/kprobes/myevent/enable
+
+grep myevent trace | grep -q 'path="/dev/null" path2="/dev/null"'
+
+:;: "user-memory access syntax and ustring not working with kernel memory";:
+echo 'p:myevent vfs_symlink path=+0($arg3):ustring path2=+u0($arg3):string' \
+ > kprobe_events
+echo 1 > events/kprobes/myevent/enable
+ln -s foo $TMPDIR/bar
+echo 0 > events/kprobes/myevent/enable
+
+grep myevent trace | grep -q 'path=(fault) path2=(fault)'
+
+exit 0
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc
index 3fb70e01b1fe..3ff236719b6e 100644
--- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc
+++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc
@@ -24,7 +24,21 @@ test -d events/kprobes2/event2 || exit_failure
:;: "Add an event on dot function without name" ;:
-FUNC=`grep -m 10 " [tT] .*\.isra\..*$" /proc/kallsyms | tail -n 1 | cut -f 3 -d " "`
+find_dot_func() {
+ if [ ! -f available_filter_functions ]; then
+ grep -m 10 " [tT] .*\.isra\..*$" /proc/kallsyms | tail -n 1 | cut -f 3 -d " "
+ return;
+ fi
+
+ grep " [tT] .*\.isra\..*" /proc/kallsyms | cut -f 3 -d " " | while read f; do
+ if grep -s $f available_filter_functions; then
+ echo $f
+ break
+ fi
+ done
+}
+
+FUNC=`find_dot_func | tail -n 1`
[ "x" != "x$FUNC" ] || exit_unresolved
echo "p $FUNC" > kprobe_events
EVENT=`grep $FUNC kprobe_events | cut -f 1 -d " " | cut -f 2 -d:`
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_ftrace.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_ftrace.tc
index 492426e95e09..7650a82db3f5 100644
--- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_ftrace.tc
+++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_ftrace.tc
@@ -3,7 +3,7 @@
# description: Kprobe dynamic event with function tracer
[ -f kprobe_events ] || exit_unsupported # this is configurable
-grep function available_tracers || exit_unsupported # this is configurable
+grep "function" available_tracers || exit_unsupported # this is configurable
# prepare
echo nop > current_tracer
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_multiprobe.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_multiprobe.tc
new file mode 100644
index 000000000000..44494bac86d1
--- /dev/null
+++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_multiprobe.tc
@@ -0,0 +1,35 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# description: Create/delete multiprobe on kprobe event
+
+[ -f kprobe_events ] || exit_unsupported
+
+grep -q "Create/append/" README || exit_unsupported
+
+# Choose 2 symbols for target
+SYM1=_do_fork
+SYM2=do_exit
+EVENT_NAME=kprobes/testevent
+
+DEF1="p:$EVENT_NAME $SYM1"
+DEF2="p:$EVENT_NAME $SYM2"
+
+:;: "Define an event which has 2 probes" ;:
+echo $DEF1 >> kprobe_events
+echo $DEF2 >> kprobe_events
+cat kprobe_events | grep "$DEF1"
+cat kprobe_events | grep "$DEF2"
+
+:;: "Remove the event by name (should remove both)" ;:
+echo "-:$EVENT_NAME" >> kprobe_events
+test `cat kprobe_events | wc -l` -eq 0
+
+:;: "Remove just 1 event" ;:
+echo $DEF1 >> kprobe_events
+echo $DEF2 >> kprobe_events
+echo "-:$EVENT_NAME $SYM1" >> kprobe_events
+! cat kprobe_events | grep "$DEF1"
+cat kprobe_events | grep "$DEF2"
+
+:;: "Appending different type must fail" ;:
+! echo "$DEF1 \$stack" >> kprobe_events
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_syntax_errors.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_syntax_errors.tc
index 29faaec942c6..8a4025e912cb 100644
--- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_syntax_errors.tc
+++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_syntax_errors.tc
@@ -41,6 +41,11 @@ check_error 'p vfs_read ^%none_reg' # BAD_REG_NAME
check_error 'p vfs_read ^@12345678abcde' # BAD_MEM_ADDR
check_error 'p vfs_read ^@+10' # FILE_ON_KPROBE
+grep -q "imm-value" README && \
+check_error 'p vfs_read arg1=\^x' # BAD_IMM
+grep -q "imm-string" README && \
+check_error 'p vfs_read arg1=\"abcd^' # IMMSTR_NO_CLOSE
+
check_error 'p vfs_read ^+0@0)' # DEREF_NEED_BRACE
check_error 'p vfs_read ^+0ab1(@0)' # BAD_DEREF_OFFS
check_error 'p vfs_read +0(+0(@0^)' # DEREF_OPEN_BRACE
@@ -82,4 +87,15 @@ case $(uname -m) in
;;
esac
+# multiprobe errors
+if grep -q "Create/append/" README && grep -q "imm-value" README; then
+echo 'p:kprobes/testevent _do_fork' > kprobe_events
+check_error '^r:kprobes/testevent do_exit' # DIFF_PROBE_TYPE
+echo 'p:kprobes/testevent _do_fork abcd=\1' > kprobe_events
+check_error 'p:kprobes/testevent _do_fork ^bcd=\1' # DIFF_ARG_TYPE
+check_error 'p:kprobes/testevent _do_fork ^abcd=\1:u8' # DIFF_ARG_TYPE
+check_error 'p:kprobes/testevent _do_fork ^abcd=\"foo"' # DIFF_ARG_TYPE
+check_error '^p:kprobes/testevent _do_fork' # SAME_PROBE
+fi
+
exit 0
diff --git a/tools/testing/selftests/ftrace/test.d/selftest/bashisms.tc b/tools/testing/selftests/ftrace/test.d/selftest/bashisms.tc
new file mode 100644
index 000000000000..1b081e910e14
--- /dev/null
+++ b/tools/testing/selftests/ftrace/test.d/selftest/bashisms.tc
@@ -0,0 +1,21 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# description: Meta-selftest: Checkbashisms
+
+if [ ! -f $FTRACETEST_ROOT/ftracetest ]; then
+ echo "Hmm, we can not find ftracetest"
+ exit_unresolved
+fi
+
+if ! which checkbashisms > /dev/null 2>&1 ; then
+ echo "No checkbashisms found. skipped."
+ exit_unresolved
+fi
+
+checkbashisms $FTRACETEST_ROOT/ftracetest
+checkbashisms $FTRACETEST_ROOT/test.d/functions
+for t in $(find $FTRACETEST_ROOT/test.d -name \*.tc); do
+ checkbashisms $t
+done
+
+exit 0
diff --git a/tools/testing/selftests/futex/functional/futex_requeue_pi.c b/tools/testing/selftests/futex/functional/futex_requeue_pi.c
index 8d20957f7586..1ee5518ee6b7 100644
--- a/tools/testing/selftests/futex/functional/futex_requeue_pi.c
+++ b/tools/testing/selftests/futex/functional/futex_requeue_pi.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/******************************************************************************
*
* Copyright © International Business Machines Corp., 2006-2008
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
* DESCRIPTION
* This test excercises the futex syscall op codes needed for requeuing
* priority inheritance aware POSIX condition variables and mutexes.
diff --git a/tools/testing/selftests/futex/functional/futex_requeue_pi_mismatched_ops.c b/tools/testing/selftests/futex/functional/futex_requeue_pi_mismatched_ops.c
index 742624c59ba7..d0a4d332ea44 100644
--- a/tools/testing/selftests/futex/functional/futex_requeue_pi_mismatched_ops.c
+++ b/tools/testing/selftests/futex/functional/futex_requeue_pi_mismatched_ops.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/******************************************************************************
*
* Copyright © International Business Machines Corp., 2009
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
* DESCRIPTION
* 1. Block a thread using FUTEX_WAIT
* 2. Attempt to use FUTEX_CMP_REQUEUE_PI on the futex from 1.
diff --git a/tools/testing/selftests/futex/functional/futex_requeue_pi_signal_restart.c b/tools/testing/selftests/futex/functional/futex_requeue_pi_signal_restart.c
index a0f5934707ff..f8c43ce8fe66 100644
--- a/tools/testing/selftests/futex/functional/futex_requeue_pi_signal_restart.c
+++ b/tools/testing/selftests/futex/functional/futex_requeue_pi_signal_restart.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/******************************************************************************
*
* Copyright © International Business Machines Corp., 2006-2008
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
* DESCRIPTION
* This test exercises the futex_wait_requeue_pi() signal handling both
* before and after the requeue. The first should be restarted by the
diff --git a/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c b/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c
index a458d42ff86e..fb4148f23fa3 100644
--- a/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c
+++ b/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/******************************************************************************
*
* Copyright FUJITSU LIMITED 2010
* Copyright KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
* DESCRIPTION
* Internally, Futex has two handling mode, anon and file. The private file
* mapping is special. At first it behave as file, but after write anything
diff --git a/tools/testing/selftests/futex/functional/futex_wait_timeout.c b/tools/testing/selftests/futex/functional/futex_wait_timeout.c
index 04b95478059c..ee55e6d389a3 100644
--- a/tools/testing/selftests/futex/functional/futex_wait_timeout.c
+++ b/tools/testing/selftests/futex/functional/futex_wait_timeout.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/******************************************************************************
*
* Copyright © International Business Machines Corp., 2009
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
* DESCRIPTION
* Block on a futex and wait for timeout.
*
diff --git a/tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c b/tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c
index 3a1d12a14921..ed9cd07e31c1 100644
--- a/tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c
+++ b/tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/******************************************************************************
*
* Copyright FUJITSU LIMITED 2010
* Copyright KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
* DESCRIPTION
* Wait on uninitialized heap. It shold be zero and FUTEX_WAIT should
* return immediately. This test is intent to test zero page handling in
diff --git a/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
index a34a6bbc30ce..0ae390ff8164 100644
--- a/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
+++ b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/******************************************************************************
*
* Copyright © International Business Machines Corp., 2009
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
* DESCRIPTION
* Test if FUTEX_WAIT op returns -EWOULDBLOCK if the futex value differs
* from the expected one.
diff --git a/tools/testing/selftests/futex/functional/run.sh b/tools/testing/selftests/futex/functional/run.sh
index 7ff002eed624..1acb6ace1680 100755
--- a/tools/testing/selftests/futex/functional/run.sh
+++ b/tools/testing/selftests/futex/functional/run.sh
@@ -1,14 +1,10 @@
#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
###############################################################################
#
# Copyright © International Business Machines Corp., 2009
#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
# DESCRIPTION
# Run tests in the current directory.
#
diff --git a/tools/testing/selftests/futex/include/atomic.h b/tools/testing/selftests/futex/include/atomic.h
index f861da3e31ab..428bcd921bb5 100644
--- a/tools/testing/selftests/futex/include/atomic.h
+++ b/tools/testing/selftests/futex/include/atomic.h
@@ -1,12 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/******************************************************************************
*
* Copyright © International Business Machines Corp., 2009
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
* DESCRIPTION
* GCC atomic builtin wrappers
* http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html
diff --git a/tools/testing/selftests/futex/include/futextest.h b/tools/testing/selftests/futex/include/futextest.h
index b98c3aba7102..ddbcfc9b7bac 100644
--- a/tools/testing/selftests/futex/include/futextest.h
+++ b/tools/testing/selftests/futex/include/futextest.h
@@ -1,12 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/******************************************************************************
*
* Copyright © International Business Machines Corp., 2009
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
* DESCRIPTION
* Glibc independent futex library for testing kernel functionality.
*
diff --git a/tools/testing/selftests/futex/include/logging.h b/tools/testing/selftests/futex/include/logging.h
index 01989644e50a..874c69ce5cce 100644
--- a/tools/testing/selftests/futex/include/logging.h
+++ b/tools/testing/selftests/futex/include/logging.h
@@ -1,12 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/******************************************************************************
*
* Copyright © International Business Machines Corp., 2009
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
* DESCRIPTION
* Glibc independent futex library for testing kernel functionality.
*
diff --git a/tools/testing/selftests/futex/run.sh b/tools/testing/selftests/futex/run.sh
index 88bcb1767362..5e76ea18f9fa 100755
--- a/tools/testing/selftests/futex/run.sh
+++ b/tools/testing/selftests/futex/run.sh
@@ -1,14 +1,10 @@
#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
###############################################################################
#
# Copyright © International Business Machines Corp., 2009
#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
# DESCRIPTION
# Run all tests under the functional, performance, and stress directories.
# Format and summarize the results.
diff --git a/tools/testing/selftests/gpio/gpio-mockup-chardev.c b/tools/testing/selftests/gpio/gpio-mockup-chardev.c
index d587c814a9ca..73ead8828d3a 100644
--- a/tools/testing/selftests/gpio/gpio-mockup-chardev.c
+++ b/tools/testing/selftests/gpio/gpio-mockup-chardev.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* GPIO chardev test helper
*
* Copyright (C) 2016 Bamvor Jian Zhang
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
*/
#define _GNU_SOURCE
diff --git a/tools/testing/selftests/ia64/aliasing-test.c b/tools/testing/selftests/ia64/aliasing-test.c
index 62a190d45f38..1ad6896f10f7 100644
--- a/tools/testing/selftests/ia64/aliasing-test.c
+++ b/tools/testing/selftests/ia64/aliasing-test.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Exercise /dev/mem mmap cases that have been troublesome in the past
*
* (c) Copyright 2007 Hewlett-Packard Development Company, L.P.
* Bjorn Helgaas <bjorn.helgaas@hp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <stdlib.h>
diff --git a/tools/testing/selftests/ir/ir_loopback.c b/tools/testing/selftests/ir/ir_loopback.c
index e700e09e3682..af7f9c7d59bc 100644
--- a/tools/testing/selftests/ir/ir_loopback.c
+++ b/tools/testing/selftests/ir/ir_loopback.c
@@ -54,9 +54,9 @@ static const struct {
{ RC_PROTO_RC6_MCE, "rc-6-mce", 0x00007fff, "rc-6" },
{ RC_PROTO_SHARP, "sharp", 0x1fff, "sharp" },
{ RC_PROTO_IMON, "imon", 0x7fffffff, "imon" },
- { RC_PROTO_RCMM12, "rcmm-12", 0x00000fff, "rcmm" },
- { RC_PROTO_RCMM24, "rcmm-24", 0x00ffffff, "rcmm" },
- { RC_PROTO_RCMM32, "rcmm-32", 0xffffffff, "rcmm" },
+ { RC_PROTO_RCMM12, "rcmm-12", 0x00000fff, "rc-mm" },
+ { RC_PROTO_RCMM24, "rcmm-24", 0x00ffffff, "rc-mm" },
+ { RC_PROTO_RCMM32, "rcmm-32", 0xffffffff, "rc-mm" },
};
int lirc_open(const char *rc)
diff --git a/tools/testing/selftests/kcmp/Makefile b/tools/testing/selftests/kcmp/Makefile
index 47aa9887f9d4..b4d39f6b5124 100644
--- a/tools/testing/selftests/kcmp/Makefile
+++ b/tools/testing/selftests/kcmp/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
CFLAGS += -I../../../../usr/include/
TEST_GEN_PROGS := kcmp_test
diff --git a/tools/testing/selftests/kexec/Makefile b/tools/testing/selftests/kexec/Makefile
index 8e9b27a7452f..aa91d2063249 100644
--- a/tools/testing/selftests/kexec/Makefile
+++ b/tools/testing/selftests/kexec/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# Makefile for kexec tests
uname_M := $(shell uname -m 2>/dev/null || echo not)
diff --git a/tools/testing/selftests/kmod/Makefile b/tools/testing/selftests/kmod/Makefile
index fa2ccc5fb3de..5b3e746a0bee 100644
--- a/tools/testing/selftests/kmod/Makefile
+++ b/tools/testing/selftests/kmod/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# Makefile for kmod loading selftests
# No binaries, but make sure arg-less "make" doesn't trigger "run_tests"
diff --git a/tools/testing/selftests/kmod/kmod.sh b/tools/testing/selftests/kmod/kmod.sh
index 0a76314b4414..8b944cf042f6 100755
--- a/tools/testing/selftests/kmod/kmod.sh
+++ b/tools/testing/selftests/kmod/kmod.sh
@@ -28,7 +28,7 @@
# override by exporting to your environment prior running this script.
# For instance this script assumes you do not have xfs loaded upon boot.
# If this is false, export DEFAULT_KMOD_FS="ext4" prior to running this
-# script if the filesyste module you don't have loaded upon bootup
+# script if the filesystem module you don't have loaded upon bootup
# is ext4 instead. Refer to allow_user_defaults() for a list of user
# override variables possible.
#
@@ -263,7 +263,7 @@ config_get_test_result()
config_reset()
{
if ! echo -n "1" >"$DIR"/reset; then
- echo "$0: reset shuld have worked" >&2
+ echo "$0: reset should have worked" >&2
exit 1
fi
}
@@ -488,7 +488,7 @@ usage()
echo Example uses:
echo
echo "${TEST_NAME}.sh -- executes all tests"
- echo "${TEST_NAME}.sh -t 0008 -- Executes test ID 0008 number of times is recomended"
+ echo "${TEST_NAME}.sh -t 0008 -- Executes test ID 0008 number of times is recommended"
echo "${TEST_NAME}.sh -w 0008 -- Watch test ID 0008 run until an error occurs"
echo "${TEST_NAME}.sh -s 0008 -- Run test ID 0008 once"
echo "${TEST_NAME}.sh -c 0008 3 -- Run test ID 0008 three times"
diff --git a/tools/testing/selftests/kselftest.h b/tools/testing/selftests/kselftest.h
index ec15c4f6af55..0ac49d91a260 100644
--- a/tools/testing/selftests/kselftest.h
+++ b/tools/testing/selftests/kselftest.h
@@ -10,6 +10,7 @@
#ifndef __KSELFTEST_H
#define __KSELFTEST_H
+#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
@@ -81,58 +82,68 @@ static inline void ksft_print_cnts(void)
static inline void ksft_print_msg(const char *msg, ...)
{
+ int saved_errno = errno;
va_list args;
va_start(args, msg);
printf("# ");
+ errno = saved_errno;
vprintf(msg, args);
va_end(args);
}
static inline void ksft_test_result_pass(const char *msg, ...)
{
+ int saved_errno = errno;
va_list args;
ksft_cnt.ksft_pass++;
va_start(args, msg);
printf("ok %d ", ksft_test_num());
+ errno = saved_errno;
vprintf(msg, args);
va_end(args);
}
static inline void ksft_test_result_fail(const char *msg, ...)
{
+ int saved_errno = errno;
va_list args;
ksft_cnt.ksft_fail++;
va_start(args, msg);
printf("not ok %d ", ksft_test_num());
+ errno = saved_errno;
vprintf(msg, args);
va_end(args);
}
static inline void ksft_test_result_skip(const char *msg, ...)
{
+ int saved_errno = errno;
va_list args;
ksft_cnt.ksft_xskip++;
va_start(args, msg);
printf("not ok %d # SKIP ", ksft_test_num());
+ errno = saved_errno;
vprintf(msg, args);
va_end(args);
}
static inline void ksft_test_result_error(const char *msg, ...)
{
+ int saved_errno = errno;
va_list args;
ksft_cnt.ksft_error++;
va_start(args, msg);
printf("not ok %d # error ", ksft_test_num());
+ errno = saved_errno;
vprintf(msg, args);
va_end(args);
}
@@ -152,10 +163,12 @@ static inline int ksft_exit_fail(void)
static inline int ksft_exit_fail_msg(const char *msg, ...)
{
+ int saved_errno = errno;
va_list args;
va_start(args, msg);
printf("Bail out! ");
+ errno = saved_errno;
vprintf(msg, args);
va_end(args);
@@ -178,10 +191,12 @@ static inline int ksft_exit_xpass(void)
static inline int ksft_exit_skip(const char *msg, ...)
{
if (msg) {
+ int saved_errno = errno;
va_list args;
va_start(args, msg);
printf("not ok %d # SKIP ", 1 + ksft_test_num());
+ errno = saved_errno;
vprintf(msg, args);
va_end(args);
} else {
diff --git a/tools/testing/selftests/kselftest/runner.sh b/tools/testing/selftests/kselftest/runner.sh
index eff3ee303d0d..00c9020bdda8 100644
--- a/tools/testing/selftests/kselftest/runner.sh
+++ b/tools/testing/selftests/kselftest/runner.sh
@@ -24,16 +24,6 @@ tap_prefix()
fi
}
-# If stdbuf is unavailable, we must fall back to line-at-a-time piping.
-tap_unbuffer()
-{
- if ! which stdbuf >/dev/null ; then
- "$@"
- else
- stdbuf -i0 -o0 -e0 "$@"
- fi
-}
-
run_one()
{
DIR="$1"
@@ -54,7 +44,7 @@ run_one()
echo "not ok $test_num $TEST_HDR_MSG"
else
cd `dirname $TEST` > /dev/null
- (((((tap_unbuffer ./$BASENAME_TEST 2>&1; echo $? >&3) |
+ (((((./$BASENAME_TEST 2>&1; echo $? >&3) |
tap_prefix >&4) 3>&1) |
(read xs; exit $xs)) 4>>"$logfile" &&
echo "ok $test_num $TEST_HDR_MSG") ||
diff --git a/tools/testing/selftests/kselftest_harness.h b/tools/testing/selftests/kselftest_harness.h
index 941d9391377f..5336b26506ab 100644
--- a/tools/testing/selftests/kselftest_harness.h
+++ b/tools/testing/selftests/kselftest_harness.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by the GPLv2 license.
*
* kselftest_harness.h: simple C unit test helper.
*
@@ -62,6 +62,7 @@
#include <sys/wait.h>
#include <unistd.h>
+#define TEST_TIMEOUT_DEFAULT 30
/* Utilities exposed to the test definitions */
#ifndef TH_LOG_STREAM
@@ -169,7 +170,8 @@
static void test_name(struct __test_metadata *_metadata); \
static struct __test_metadata _##test_name##_object = \
{ .name = "global." #test_name, \
- .fn = &test_name, .termsig = _signal }; \
+ .fn = &test_name, .termsig = _signal, \
+ .timeout = TEST_TIMEOUT_DEFAULT, }; \
static void __attribute__((constructor)) _register_##test_name(void) \
{ \
__register_test(&_##test_name##_object); \
@@ -280,12 +282,15 @@
*/
/* TODO(wad) register fixtures on dedicated test lists. */
#define TEST_F(fixture_name, test_name) \
- __TEST_F_IMPL(fixture_name, test_name, -1)
+ __TEST_F_IMPL(fixture_name, test_name, -1, TEST_TIMEOUT_DEFAULT)
#define TEST_F_SIGNAL(fixture_name, test_name, signal) \
- __TEST_F_IMPL(fixture_name, test_name, signal)
+ __TEST_F_IMPL(fixture_name, test_name, signal, TEST_TIMEOUT_DEFAULT)
-#define __TEST_F_IMPL(fixture_name, test_name, signal) \
+#define TEST_F_TIMEOUT(fixture_name, test_name, timeout) \
+ __TEST_F_IMPL(fixture_name, test_name, -1, timeout)
+
+#define __TEST_F_IMPL(fixture_name, test_name, signal, tmout) \
static void fixture_name##_##test_name( \
struct __test_metadata *_metadata, \
FIXTURE_DATA(fixture_name) *self); \
@@ -307,6 +312,7 @@
.name = #fixture_name "." #test_name, \
.fn = &wrapper_##fixture_name##_##test_name, \
.termsig = signal, \
+ .timeout = tmout, \
}; \
static void __attribute__((constructor)) \
_register_##fixture_name##_##test_name(void) \
@@ -632,6 +638,7 @@ struct __test_metadata {
int termsig;
int passed;
int trigger; /* extra handler after the evaluation */
+ int timeout;
__u8 step;
bool no_print; /* manual trigger when TH_LOG_STREAM is not available */
struct __test_metadata *prev, *next;
@@ -696,7 +703,7 @@ void __run_test(struct __test_metadata *t)
t->passed = 1;
t->trigger = 0;
printf("[ RUN ] %s\n", t->name);
- alarm(30);
+ alarm(t->timeout);
child_pid = fork();
if (child_pid < 0) {
printf("ERROR SPAWNING TEST CHILD\n");
diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
index df1bf9230a74..b35da375530a 100644
--- a/tools/testing/selftests/kvm/.gitignore
+++ b/tools/testing/selftests/kvm/.gitignore
@@ -1,7 +1,8 @@
+/s390x/sync_regs_test
/x86_64/cr4_cpuid_sync_test
/x86_64/evmcs_test
/x86_64/hyperv_cpuid
-/x86_64/kvm_create_max_vcpus
+/x86_64/mmio_warning_test
/x86_64/platform_info_test
/x86_64/set_sregs_test
/x86_64/smm_test
@@ -12,3 +13,4 @@
/x86_64/vmx_tsc_adjust_test
/clear_dirty_log_test
/dirty_log_test
+/kvm_create_max_vcpus
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 79c524395ebe..62c591f87dab 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
include ../../../../scripts/Kbuild.include
all:
@@ -6,27 +7,35 @@ top_srcdir = ../../../..
KSFT_KHDR_INSTALL := 1
UNAME_M := $(shell uname -m)
-LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/ucall.c lib/sparsebit.c
-LIBKVM_x86_64 = lib/x86_64/processor.c lib/x86_64/vmx.c
-LIBKVM_aarch64 = lib/aarch64/processor.c
+LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/sparsebit.c
+LIBKVM_x86_64 = lib/x86_64/processor.c lib/x86_64/vmx.c lib/x86_64/ucall.c
+LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c
+LIBKVM_s390x = lib/s390x/processor.c lib/s390x/ucall.c
-TEST_GEN_PROGS_x86_64 = x86_64/platform_info_test
-TEST_GEN_PROGS_x86_64 += x86_64/set_sregs_test
-TEST_GEN_PROGS_x86_64 += x86_64/sync_regs_test
-TEST_GEN_PROGS_x86_64 += x86_64/vmx_tsc_adjust_test
-TEST_GEN_PROGS_x86_64 += x86_64/cr4_cpuid_sync_test
-TEST_GEN_PROGS_x86_64 += x86_64/state_test
+TEST_GEN_PROGS_x86_64 = x86_64/cr4_cpuid_sync_test
TEST_GEN_PROGS_x86_64 += x86_64/evmcs_test
TEST_GEN_PROGS_x86_64 += x86_64/hyperv_cpuid
-TEST_GEN_PROGS_x86_64 += x86_64/vmx_close_while_nested_test
+TEST_GEN_PROGS_x86_64 += x86_64/mmio_warning_test
+TEST_GEN_PROGS_x86_64 += x86_64/platform_info_test
+TEST_GEN_PROGS_x86_64 += x86_64/set_sregs_test
TEST_GEN_PROGS_x86_64 += x86_64/smm_test
-TEST_GEN_PROGS_x86_64 += x86_64/kvm_create_max_vcpus
+TEST_GEN_PROGS_x86_64 += x86_64/state_test
+TEST_GEN_PROGS_x86_64 += x86_64/sync_regs_test
+TEST_GEN_PROGS_x86_64 += x86_64/vmx_close_while_nested_test
TEST_GEN_PROGS_x86_64 += x86_64/vmx_set_nested_state_test
-TEST_GEN_PROGS_x86_64 += dirty_log_test
+TEST_GEN_PROGS_x86_64 += x86_64/vmx_tsc_adjust_test
TEST_GEN_PROGS_x86_64 += clear_dirty_log_test
+TEST_GEN_PROGS_x86_64 += dirty_log_test
+TEST_GEN_PROGS_x86_64 += kvm_create_max_vcpus
-TEST_GEN_PROGS_aarch64 += dirty_log_test
TEST_GEN_PROGS_aarch64 += clear_dirty_log_test
+TEST_GEN_PROGS_aarch64 += dirty_log_test
+TEST_GEN_PROGS_aarch64 += kvm_create_max_vcpus
+
+TEST_GEN_PROGS_s390x = s390x/memop
+TEST_GEN_PROGS_s390x += s390x/sync_regs_test
+TEST_GEN_PROGS_s390x += dirty_log_test
+TEST_GEN_PROGS_s390x += kvm_create_max_vcpus
TEST_GEN_PROGS += $(TEST_GEN_PROGS_$(UNAME_M))
LIBKVM += $(LIBKVM_$(UNAME_M))
@@ -34,12 +43,19 @@ LIBKVM += $(LIBKVM_$(UNAME_M))
INSTALL_HDR_PATH = $(top_srcdir)/usr
LINUX_HDR_PATH = $(INSTALL_HDR_PATH)/include/
LINUX_TOOL_INCLUDE = $(top_srcdir)/tools/include
-CFLAGS += -O2 -g -std=gnu99 -fno-stack-protector -fno-PIE -I$(LINUX_TOOL_INCLUDE) -I$(LINUX_HDR_PATH) -Iinclude -I$(<D) -Iinclude/$(UNAME_M) -I..
+CFLAGS += -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \
+ -fno-stack-protector -fno-PIE -I$(LINUX_TOOL_INCLUDE) \
+ -I$(LINUX_HDR_PATH) -Iinclude -I$(<D) -Iinclude/$(UNAME_M) -I..
no-pie-option := $(call try-run, echo 'int main() { return 0; }' | \
$(CC) -Werror $(KBUILD_CPPFLAGS) $(CC_OPTION_CFLAGS) -no-pie -x c - -o "$$TMP", -no-pie)
-LDFLAGS += -pthread $(no-pie-option)
+# On s390, build the testcases KVM-enabled
+pgste-option = $(call try-run, echo 'int main() { return 0; }' | \
+ $(CC) -Werror -Wl$(comma)--s390-pgste -x c - -o "$$TMP",-Wl$(comma)--s390-pgste)
+
+
+LDFLAGS += -pthread $(no-pie-option) $(pgste-option)
# After inclusion, $(OUTPUT) is defined and
# $(TEST_GEN_PROGS) starts with $(OUTPUT)/
diff --git a/tools/testing/selftests/kvm/config b/tools/testing/selftests/kvm/config
new file mode 100644
index 000000000000..63ed533f73d6
--- /dev/null
+++ b/tools/testing/selftests/kvm/config
@@ -0,0 +1,3 @@
+CONFIG_KVM=y
+CONFIG_KVM_INTEL=y
+CONFIG_KVM_AMD=y
diff --git a/tools/testing/selftests/kvm/dirty_log_test.c b/tools/testing/selftests/kvm/dirty_log_test.c
index f50a15c38f9b..dc3346e090f5 100644
--- a/tools/testing/selftests/kvm/dirty_log_test.c
+++ b/tools/testing/selftests/kvm/dirty_log_test.c
@@ -26,8 +26,8 @@
/* The memory slot index to track dirty pages */
#define TEST_MEM_SLOT_INDEX 1
-/* Default guest test memory offset, 1G */
-#define DEFAULT_GUEST_TEST_MEM 0x40000000
+/* Default guest test virtual memory offset */
+#define DEFAULT_GUEST_TEST_MEM 0xc0000000
/* How many pages to dirty for each guest loop */
#define TEST_PAGES_PER_LOOP 1024
@@ -38,6 +38,27 @@
/* Interval for each host loop (ms) */
#define TEST_HOST_LOOP_INTERVAL 10UL
+/* Dirty bitmaps are always little endian, so we need to swap on big endian */
+#if defined(__s390x__)
+# define BITOP_LE_SWIZZLE ((BITS_PER_LONG-1) & ~0x7)
+# define test_bit_le(nr, addr) \
+ test_bit((nr) ^ BITOP_LE_SWIZZLE, addr)
+# define set_bit_le(nr, addr) \
+ set_bit((nr) ^ BITOP_LE_SWIZZLE, addr)
+# define clear_bit_le(nr, addr) \
+ clear_bit((nr) ^ BITOP_LE_SWIZZLE, addr)
+# define test_and_set_bit_le(nr, addr) \
+ test_and_set_bit((nr) ^ BITOP_LE_SWIZZLE, addr)
+# define test_and_clear_bit_le(nr, addr) \
+ test_and_clear_bit((nr) ^ BITOP_LE_SWIZZLE, addr)
+#else
+# define test_bit_le test_bit
+# define set_bit_le set_bit
+# define clear_bit_le clear_bit
+# define test_and_set_bit_le test_and_set_bit
+# define test_and_clear_bit_le test_and_clear_bit
+#endif
+
/*
* Guest/Host shared variables. Ensure addr_gva2hva() and/or
* sync_global_to/from_guest() are used when accessing from
@@ -69,11 +90,23 @@ static uint64_t guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM;
*/
static void guest_code(void)
{
+ uint64_t addr;
int i;
+ /*
+ * On s390x, all pages of a 1M segment are initially marked as dirty
+ * when a page of the segment is written to for the very first time.
+ * To compensate this specialty in this test, we need to touch all
+ * pages during the first iteration.
+ */
+ for (i = 0; i < guest_num_pages; i++) {
+ addr = guest_test_virt_mem + i * guest_page_size;
+ *(uint64_t *)addr = READ_ONCE(iteration);
+ }
+
while (true) {
for (i = 0; i < TEST_PAGES_PER_LOOP; i++) {
- uint64_t addr = guest_test_virt_mem;
+ addr = guest_test_virt_mem;
addr += (READ_ONCE(random_array[i]) % guest_num_pages)
* guest_page_size;
addr &= ~(host_page_size - 1);
@@ -121,7 +154,6 @@ static void *vcpu_worker(void *data)
uint64_t *guest_array;
uint64_t pages_count = 0;
struct kvm_run *run;
- struct ucall uc;
run = vcpu_state(vm, VCPU_ID);
@@ -131,7 +163,8 @@ static void *vcpu_worker(void *data)
while (!READ_ONCE(host_quit)) {
/* Let the guest dirty the random pages */
ret = _vcpu_run(vm, VCPU_ID);
- if (get_ucall(vm, VCPU_ID, &uc) == UCALL_SYNC) {
+ TEST_ASSERT(ret == 0, "vcpu_run failed: %d\n", ret);
+ if (get_ucall(vm, VCPU_ID, NULL) == UCALL_SYNC) {
pages_count += TEST_PAGES_PER_LOOP;
generate_random_array(guest_array, TEST_PAGES_PER_LOOP);
} else {
@@ -158,15 +191,15 @@ static void vm_dirty_log_verify(unsigned long *bmap)
value_ptr = host_test_mem + page * host_page_size;
/* If this is a special page that we were tracking... */
- if (test_and_clear_bit(page, host_bmap_track)) {
+ if (test_and_clear_bit_le(page, host_bmap_track)) {
host_track_next_count++;
- TEST_ASSERT(test_bit(page, bmap),
+ TEST_ASSERT(test_bit_le(page, bmap),
"Page %"PRIu64" should have its dirty bit "
"set in this iteration but it is missing",
page);
}
- if (test_bit(page, bmap)) {
+ if (test_bit_le(page, bmap)) {
host_dirty_count++;
/*
* If the bit is set, the value written onto
@@ -209,7 +242,7 @@ static void vm_dirty_log_verify(unsigned long *bmap)
* should report its dirtyness in the
* next run
*/
- set_bit(page, host_bmap_track);
+ set_bit_le(page, host_bmap_track);
}
}
}
@@ -292,7 +325,11 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
* A little more than 1G of guest page sized pages. Cover the
* case where the size is not aligned to 64 pages.
*/
- guest_num_pages = (1ul << (30 - guest_page_shift)) + 3;
+ guest_num_pages = (1ul << (30 - guest_page_shift)) + 16;
+#ifdef __s390x__
+ /* Round up to multiple of 1M (segment size) */
+ guest_num_pages = (guest_num_pages + 0xff) & ~0xffUL;
+#endif
host_page_size = getpagesize();
host_num_pages = (guest_num_pages * guest_page_size) / host_page_size +
!!((guest_num_pages * guest_page_size) % host_page_size);
@@ -304,6 +341,11 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
guest_test_phys_mem = phys_offset;
}
+#ifdef __s390x__
+ /* Align to 1M (segment size) */
+ guest_test_phys_mem &= ~((1 << 20) - 1);
+#endif
+
DEBUG("guest physical test memory offset: 0x%lx\n", guest_test_phys_mem);
bmap = bitmap_alloc(host_num_pages);
@@ -337,7 +379,7 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid());
#endif
#ifdef __aarch64__
- ucall_init(vm, UCALL_MMIO, NULL);
+ ucall_init(vm, NULL);
#endif
/* Export the shared variables to the guest */
@@ -426,8 +468,11 @@ int main(int argc, char *argv[])
unsigned long interval = TEST_HOST_LOOP_INTERVAL;
bool mode_selected = false;
uint64_t phys_offset = 0;
- unsigned int mode, host_ipa_limit;
+ unsigned int mode;
int opt, i;
+#ifdef __aarch64__
+ unsigned int host_ipa_limit;
+#endif
#ifdef USE_CLEAR_DIRTY_LOG
if (!kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2)) {
@@ -451,6 +496,9 @@ int main(int argc, char *argv[])
vm_guest_mode_params_init(VM_MODE_P48V48_64K, true, true);
}
#endif
+#ifdef __s390x__
+ vm_guest_mode_params_init(VM_MODE_P40V48_4K, true, true);
+#endif
while ((opt = getopt(argc, argv, "hi:I:p:m:")) != -1) {
switch (opt) {
diff --git a/tools/testing/selftests/kvm/include/aarch64/processor.h b/tools/testing/selftests/kvm/include/aarch64/processor.h
index 9ef2ab1a0c08..b7fa0c8551db 100644
--- a/tools/testing/selftests/kvm/include/aarch64/processor.h
+++ b/tools/testing/selftests/kvm/include/aarch64/processor.h
@@ -52,4 +52,8 @@ static inline void set_reg(struct kvm_vm *vm, uint32_t vcpuid, uint64_t id, uint
vcpu_ioctl(vm, vcpuid, KVM_SET_ONE_REG, &reg);
}
+void aarch64_vcpu_setup(struct kvm_vm *vm, int vcpuid, struct kvm_vcpu_init *init);
+void aarch64_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid,
+ struct kvm_vcpu_init *init, void *guest_code);
+
#endif /* SELFTEST_KVM_PROCESSOR_H */
diff --git a/tools/testing/selftests/kvm/include/evmcs.h b/tools/testing/selftests/kvm/include/evmcs.h
index 4059014d93ea..4912d23844bc 100644
--- a/tools/testing/selftests/kvm/include/evmcs.h
+++ b/tools/testing/selftests/kvm/include/evmcs.h
@@ -220,6 +220,8 @@ struct hv_enlightened_vmcs {
struct hv_enlightened_vmcs *current_evmcs;
struct hv_vp_assist_page *current_vp_assist;
+int vcpu_enable_evmcs(struct kvm_vm *vm, int vcpu_id);
+
static inline int enable_vp_assist(uint64_t vp_assist_pa, void *vp_assist)
{
u64 val = (vp_assist_pa & HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_MASK) |
diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
index 8c6b9619797d..5463b7896a0a 100644
--- a/tools/testing/selftests/kvm/include/kvm_util.h
+++ b/tools/testing/selftests/kvm/include/kvm_util.h
@@ -1,10 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tools/testing/selftests/kvm/include/kvm_util.h
*
* Copyright (C) 2018, Google LLC.
- *
- * This work is licensed under the terms of the GNU GPL, version 2.
- *
*/
#ifndef SELFTEST_KVM_UTIL_H
#define SELFTEST_KVM_UTIL_H
@@ -43,6 +41,12 @@ enum vm_guest_mode {
NUM_VM_MODES,
};
+#ifdef __aarch64__
+#define VM_MODE_DEFAULT VM_MODE_P40V48_4K
+#else
+#define VM_MODE_DEFAULT VM_MODE_P52V48_4K
+#endif
+
#define vm_guest_mode_string(m) vm_guest_mode_string[m]
extern const char * const vm_guest_mode_string[];
@@ -88,8 +92,7 @@ int _vcpu_ioctl(struct kvm_vm *vm, uint32_t vcpuid, unsigned long ioctl,
void *arg);
void vm_ioctl(struct kvm_vm *vm, unsigned long ioctl, void *arg);
void vm_mem_region_set_flags(struct kvm_vm *vm, uint32_t slot, uint32_t flags);
-void vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpuid, int pgd_memslot,
- int gdt_memslot);
+void vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpuid);
vm_vaddr_t vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min,
uint32_t data_memslot, uint32_t pgd_memslot);
void virt_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr,
@@ -114,14 +117,18 @@ void vcpu_sregs_set(struct kvm_vm *vm, uint32_t vcpuid,
struct kvm_sregs *sregs);
int _vcpu_sregs_set(struct kvm_vm *vm, uint32_t vcpuid,
struct kvm_sregs *sregs);
+#ifdef __KVM_HAVE_VCPU_EVENTS
void vcpu_events_get(struct kvm_vm *vm, uint32_t vcpuid,
struct kvm_vcpu_events *events);
void vcpu_events_set(struct kvm_vm *vm, uint32_t vcpuid,
struct kvm_vcpu_events *events);
+#endif
+#ifdef __x86_64__
void vcpu_nested_state_get(struct kvm_vm *vm, uint32_t vcpuid,
struct kvm_nested_state *state);
int vcpu_nested_state_set(struct kvm_vm *vm, uint32_t vcpuid,
struct kvm_nested_state *state, bool ignore_error);
+#endif
const char *exit_reason_str(unsigned int exit_reason);
@@ -137,6 +144,8 @@ struct kvm_vm *vm_create_default(uint32_t vcpuid, uint64_t extra_mem_size,
void *guest_code);
void vm_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, void *guest_code);
+bool vm_is_unrestricted_guest(struct kvm_vm *vm);
+
struct kvm_userspace_memory_region *
kvm_userspace_memory_region_find(struct kvm_vm *vm, uint64_t start,
uint64_t end);
@@ -156,12 +165,6 @@ int vm_create_device(struct kvm_vm *vm, struct kvm_create_device *cd);
memcpy(&(g), _p, sizeof(g)); \
})
-/* ucall implementation types */
-typedef enum {
- UCALL_PIO,
- UCALL_MMIO,
-} ucall_type_t;
-
/* Common ucalls */
enum {
UCALL_NONE,
@@ -177,7 +180,7 @@ struct ucall {
uint64_t args[UCALL_MAX_ARGS];
};
-void ucall_init(struct kvm_vm *vm, ucall_type_t type, void *arg);
+void ucall_init(struct kvm_vm *vm, void *arg);
void ucall_uninit(struct kvm_vm *vm);
void ucall(uint64_t cmd, int nargs, ...);
uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc);
diff --git a/tools/testing/selftests/kvm/include/s390x/processor.h b/tools/testing/selftests/kvm/include/s390x/processor.h
new file mode 100644
index 000000000000..e0e96a5f608c
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/s390x/processor.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * s390x processor specific defines
+ */
+#ifndef SELFTEST_KVM_PROCESSOR_H
+#define SELFTEST_KVM_PROCESSOR_H
+
+/* Bits in the region/segment table entry */
+#define REGION_ENTRY_ORIGIN ~0xfffUL /* region/segment table origin */
+#define REGION_ENTRY_PROTECT 0x200 /* region protection bit */
+#define REGION_ENTRY_NOEXEC 0x100 /* region no-execute bit */
+#define REGION_ENTRY_OFFSET 0xc0 /* region table offset */
+#define REGION_ENTRY_INVALID 0x20 /* invalid region table entry */
+#define REGION_ENTRY_TYPE 0x0c /* region/segment table type mask */
+#define REGION_ENTRY_LENGTH 0x03 /* region third length */
+
+/* Bits in the page table entry */
+#define PAGE_INVALID 0x400 /* HW invalid bit */
+#define PAGE_PROTECT 0x200 /* HW read-only bit */
+#define PAGE_NOEXEC 0x100 /* HW no-execute bit */
+
+#endif
diff --git a/tools/testing/selftests/kvm/include/sparsebit.h b/tools/testing/selftests/kvm/include/sparsebit.h
index 31e030915c1f..12a9a4b9cead 100644
--- a/tools/testing/selftests/kvm/include/sparsebit.h
+++ b/tools/testing/selftests/kvm/include/sparsebit.h
@@ -1,11 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tools/testing/selftests/kvm/include/sparsebit.h
*
* Copyright (C) 2018, Google LLC.
*
- * This work is licensed under the terms of the GNU GPL, version 2.
- *
- *
* Header file that describes API to the sparsebit library.
* This library provides a memory efficient means of storing
* the settings of bits indexed via a uint64_t. Memory usage
diff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h
index c7dafe8bd02c..a41db6fb7e24 100644
--- a/tools/testing/selftests/kvm/include/test_util.h
+++ b/tools/testing/selftests/kvm/include/test_util.h
@@ -1,10 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tools/testing/selftests/kvm/include/test_util.h
*
* Copyright (C) 2018, Google LLC.
- *
- * This work is licensed under the terms of the GNU GPL, version 2.
- *
*/
#ifndef SELFTEST_KVM_TEST_UTIL_H
diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h
index 6063d5b2f356..80d19740d2dc 100644
--- a/tools/testing/selftests/kvm/include/x86_64/processor.h
+++ b/tools/testing/selftests/kvm/include/x86_64/processor.h
@@ -1,10 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tools/testing/selftests/kvm/include/x86_64/processor.h
*
* Copyright (C) 2018, Google LLC.
- *
- * This work is licensed under the terms of the GNU GPL, version 2.
- *
*/
#ifndef SELFTEST_KVM_PROCESSOR_H
@@ -303,6 +301,8 @@ static inline unsigned long get_xmm(int n)
return 0;
}
+bool is_intel_cpu(void);
+
struct kvm_x86_state;
struct kvm_x86_state *vcpu_save_state(struct kvm_vm *vm, uint32_t vcpuid);
void vcpu_load_state(struct kvm_vm *vm, uint32_t vcpuid,
diff --git a/tools/testing/selftests/kvm/include/x86_64/vmx.h b/tools/testing/selftests/kvm/include/x86_64/vmx.h
index c9bd935b939c..69b17055f63d 100644
--- a/tools/testing/selftests/kvm/include/x86_64/vmx.h
+++ b/tools/testing/selftests/kvm/include/x86_64/vmx.h
@@ -1,10 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tools/testing/selftests/kvm/include/x86_64/vmx.h
*
* Copyright (C) 2018, Google LLC.
- *
- * This work is licensed under the terms of the GNU GPL, version 2.
- *
*/
#ifndef SELFTEST_KVM_VMX_H
diff --git a/tools/testing/selftests/kvm/x86_64/kvm_create_max_vcpus.c b/tools/testing/selftests/kvm/kvm_create_max_vcpus.c
index 50e92996f918..231d79e57774 100644
--- a/tools/testing/selftests/kvm/x86_64/kvm_create_max_vcpus.c
+++ b/tools/testing/selftests/kvm/kvm_create_max_vcpus.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* kvm_create_max_vcpus
*
* Copyright (C) 2019, Google LLC.
*
- * This work is licensed under the terms of the GNU GPL, version 2.
- *
* Test for KVM_CAP_MAX_VCPUS and KVM_CAP_MAX_VCPU_ID.
*/
@@ -28,13 +27,13 @@ void test_vcpu_creation(int first_vcpu_id, int num_vcpus)
printf("Testing creating %d vCPUs, with IDs %d...%d.\n",
num_vcpus, first_vcpu_id, first_vcpu_id + num_vcpus - 1);
- vm = vm_create(VM_MODE_P52V48_4K, DEFAULT_GUEST_PHY_PAGES, O_RDWR);
+ vm = vm_create(VM_MODE_DEFAULT, DEFAULT_GUEST_PHY_PAGES, O_RDWR);
for (i = 0; i < num_vcpus; i++) {
int vcpu_id = first_vcpu_id + i;
/* This asserts that the vCPU was created. */
- vm_vcpu_add(vm, vcpu_id, 0, 0);
+ vm_vcpu_add(vm, vcpu_id);
}
kvm_vm_free(vm);
diff --git a/tools/testing/selftests/kvm/lib/aarch64/processor.c b/tools/testing/selftests/kvm/lib/aarch64/processor.c
index e8c42506a09d..486400a97374 100644
--- a/tools/testing/selftests/kvm/lib/aarch64/processor.c
+++ b/tools/testing/selftests/kvm/lib/aarch64/processor.c
@@ -7,6 +7,8 @@
#define _GNU_SOURCE /* for program_invocation_name */
+#include <linux/compiler.h>
+
#include "kvm_util.h"
#include "../kvm_util_internal.h"
#include "processor.h"
@@ -67,15 +69,13 @@ static uint64_t ptrs_per_pgd(struct kvm_vm *vm)
return 1 << (vm->va_bits - shift);
}
-static uint64_t ptrs_per_pte(struct kvm_vm *vm)
+static uint64_t __maybe_unused ptrs_per_pte(struct kvm_vm *vm)
{
return 1 << (vm->page_shift - 3);
}
void virt_pgd_alloc(struct kvm_vm *vm, uint32_t pgd_memslot)
{
- int rc;
-
if (!vm->pgd_created) {
vm_paddr_t paddr = vm_phy_pages_alloc(vm,
page_align(vm, ptrs_per_pgd(vm) * 8) / vm->page_size,
@@ -181,6 +181,7 @@ vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva)
unmapped_gva:
TEST_ASSERT(false, "No mapping for vm virtual address, "
"gva: 0x%lx", gva);
+ exit(1);
}
static void pte_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent, uint64_t page, int level)
@@ -226,7 +227,7 @@ struct kvm_vm *vm_create_default(uint32_t vcpuid, uint64_t extra_mem_pages,
uint64_t extra_pg_pages = (extra_mem_pages / ptrs_per_4k_pte) * 2;
struct kvm_vm *vm;
- vm = vm_create(VM_MODE_P52V48_4K, DEFAULT_GUEST_PHY_PAGES + extra_pg_pages, O_RDWR);
+ vm = vm_create(VM_MODE_DEFAULT, DEFAULT_GUEST_PHY_PAGES + extra_pg_pages, O_RDWR);
kvm_vm_elf_load(vm, program_invocation_name, 0, 0);
vm_vcpu_add_default(vm, vcpuid, guest_code);
@@ -234,28 +235,21 @@ struct kvm_vm *vm_create_default(uint32_t vcpuid, uint64_t extra_mem_pages,
return vm;
}
-void vm_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, void *guest_code)
+void aarch64_vcpu_setup(struct kvm_vm *vm, int vcpuid, struct kvm_vcpu_init *init)
{
- size_t stack_size = vm->page_size == 4096 ?
- DEFAULT_STACK_PGS * vm->page_size :
- vm->page_size;
- uint64_t stack_vaddr = vm_vaddr_alloc(vm, stack_size,
- DEFAULT_ARM64_GUEST_STACK_VADDR_MIN, 0, 0);
-
- vm_vcpu_add(vm, vcpuid, 0, 0);
+ struct kvm_vcpu_init default_init = { .target = -1, };
+ uint64_t sctlr_el1, tcr_el1;
- set_reg(vm, vcpuid, ARM64_CORE_REG(sp_el1), stack_vaddr + stack_size);
- set_reg(vm, vcpuid, ARM64_CORE_REG(regs.pc), (uint64_t)guest_code);
-}
+ if (!init)
+ init = &default_init;
-void vcpu_setup(struct kvm_vm *vm, int vcpuid, int pgd_memslot, int gdt_memslot)
-{
- struct kvm_vcpu_init init;
- uint64_t sctlr_el1, tcr_el1;
+ if (init->target == -1) {
+ struct kvm_vcpu_init preferred;
+ vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &preferred);
+ init->target = preferred.target;
+ }
- memset(&init, 0, sizeof(init));
- init.target = KVM_ARM_TARGET_GENERIC_V8;
- vcpu_ioctl(vm, vcpuid, KVM_ARM_VCPU_INIT, &init);
+ vcpu_ioctl(vm, vcpuid, KVM_ARM_VCPU_INIT, init);
/*
* Enable FP/ASIMD to avoid trapping when accessing Q0-Q15
@@ -312,6 +306,27 @@ void vcpu_dump(FILE *stream, struct kvm_vm *vm, uint32_t vcpuid, uint8_t indent)
get_reg(vm, vcpuid, ARM64_CORE_REG(regs.pstate), &pstate);
get_reg(vm, vcpuid, ARM64_CORE_REG(regs.pc), &pc);
- fprintf(stream, "%*spstate: 0x%.16llx pc: 0x%.16llx\n",
+ fprintf(stream, "%*spstate: 0x%.16lx pc: 0x%.16lx\n",
indent, "", pstate, pc);
}
+
+void aarch64_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid,
+ struct kvm_vcpu_init *init, void *guest_code)
+{
+ size_t stack_size = vm->page_size == 4096 ?
+ DEFAULT_STACK_PGS * vm->page_size :
+ vm->page_size;
+ uint64_t stack_vaddr = vm_vaddr_alloc(vm, stack_size,
+ DEFAULT_ARM64_GUEST_STACK_VADDR_MIN, 0, 0);
+
+ vm_vcpu_add(vm, vcpuid);
+ aarch64_vcpu_setup(vm, vcpuid, init);
+
+ set_reg(vm, vcpuid, ARM64_CORE_REG(sp_el1), stack_vaddr + stack_size);
+ set_reg(vm, vcpuid, ARM64_CORE_REG(regs.pc), (uint64_t)guest_code);
+}
+
+void vm_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, void *guest_code)
+{
+ aarch64_vcpu_add_default(vm, vcpuid, NULL, guest_code);
+}
diff --git a/tools/testing/selftests/kvm/lib/aarch64/ucall.c b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
new file mode 100644
index 000000000000..6cd91970fbad
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ucall support. A ucall is a "hypercall to userspace".
+ *
+ * Copyright (C) 2018, Red Hat, Inc.
+ */
+#include "kvm_util.h"
+#include "../kvm_util_internal.h"
+
+static vm_vaddr_t *ucall_exit_mmio_addr;
+
+static bool ucall_mmio_init(struct kvm_vm *vm, vm_paddr_t gpa)
+{
+ if (kvm_userspace_memory_region_find(vm, gpa, gpa + 1))
+ return false;
+
+ virt_pg_map(vm, gpa, gpa, 0);
+
+ ucall_exit_mmio_addr = (vm_vaddr_t *)gpa;
+ sync_global_to_guest(vm, ucall_exit_mmio_addr);
+
+ return true;
+}
+
+void ucall_init(struct kvm_vm *vm, void *arg)
+{
+ vm_paddr_t gpa, start, end, step, offset;
+ unsigned int bits;
+ bool ret;
+
+ if (arg) {
+ gpa = (vm_paddr_t)arg;
+ ret = ucall_mmio_init(vm, gpa);
+ TEST_ASSERT(ret, "Can't set ucall mmio address to %lx", gpa);
+ return;
+ }
+
+ /*
+ * Find an address within the allowed physical and virtual address
+ * spaces, that does _not_ have a KVM memory region associated with
+ * it. Identity mapping an address like this allows the guest to
+ * access it, but as KVM doesn't know what to do with it, it
+ * will assume it's something userspace handles and exit with
+ * KVM_EXIT_MMIO. Well, at least that's how it works for AArch64.
+ * Here we start with a guess that the addresses around 5/8th
+ * of the allowed space are unmapped and then work both down and
+ * up from there in 1/16th allowed space sized steps.
+ *
+ * Note, we need to use VA-bits - 1 when calculating the allowed
+ * virtual address space for an identity mapping because the upper
+ * half of the virtual address space is the two's complement of the
+ * lower and won't match physical addresses.
+ */
+ bits = vm->va_bits - 1;
+ bits = vm->pa_bits < bits ? vm->pa_bits : bits;
+ end = 1ul << bits;
+ start = end * 5 / 8;
+ step = end / 16;
+ for (offset = 0; offset < end - start; offset += step) {
+ if (ucall_mmio_init(vm, start - offset))
+ return;
+ if (ucall_mmio_init(vm, start + offset))
+ return;
+ }
+ TEST_ASSERT(false, "Can't find a ucall mmio address");
+}
+
+void ucall_uninit(struct kvm_vm *vm)
+{
+ ucall_exit_mmio_addr = 0;
+ sync_global_to_guest(vm, ucall_exit_mmio_addr);
+}
+
+void ucall(uint64_t cmd, int nargs, ...)
+{
+ struct ucall uc = {
+ .cmd = cmd,
+ };
+ va_list va;
+ int i;
+
+ nargs = nargs <= UCALL_MAX_ARGS ? nargs : UCALL_MAX_ARGS;
+
+ va_start(va, nargs);
+ for (i = 0; i < nargs; ++i)
+ uc.args[i] = va_arg(va, uint64_t);
+ va_end(va);
+
+ *ucall_exit_mmio_addr = (vm_vaddr_t)&uc;
+}
+
+uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc)
+{
+ struct kvm_run *run = vcpu_state(vm, vcpu_id);
+ struct ucall ucall = {};
+
+ if (run->exit_reason == KVM_EXIT_MMIO &&
+ run->mmio.phys_addr == (uint64_t)ucall_exit_mmio_addr) {
+ vm_vaddr_t gva;
+
+ TEST_ASSERT(run->mmio.is_write && run->mmio.len == 8,
+ "Unexpected ucall exit mmio address access");
+ memcpy(&gva, run->mmio.data, sizeof(gva));
+ memcpy(&ucall, addr_gva2hva(vm, gva), sizeof(ucall));
+
+ vcpu_run_complete_io(vm, vcpu_id);
+ if (uc)
+ memcpy(uc, &ucall, sizeof(ucall));
+ }
+
+ return ucall.cmd;
+}
diff --git a/tools/testing/selftests/kvm/lib/assert.c b/tools/testing/selftests/kvm/lib/assert.c
index 6398efe67885..4911fc77d0f6 100644
--- a/tools/testing/selftests/kvm/lib/assert.c
+++ b/tools/testing/selftests/kvm/lib/assert.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tools/testing/selftests/kvm/lib/assert.c
*
* Copyright (C) 2018, Google LLC.
- *
- * This work is licensed under the terms of the GNU GPL, version 2.
*/
#define _GNU_SOURCE /* for getline(3) and strchrnul(3)*/
diff --git a/tools/testing/selftests/kvm/lib/elf.c b/tools/testing/selftests/kvm/lib/elf.c
index 5eb857584aa3..bc75a91e00a6 100644
--- a/tools/testing/selftests/kvm/lib/elf.c
+++ b/tools/testing/selftests/kvm/lib/elf.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tools/testing/selftests/kvm/lib/elf.c
*
* Copyright (C) 2018, Google LLC.
- *
- * This work is licensed under the terms of the GNU GPL, version 2.
*/
#include "test_util.h"
diff --git a/tools/testing/selftests/kvm/lib/io.c b/tools/testing/selftests/kvm/lib/io.c
index cff869ffe6ee..eaf351cc7e7f 100644
--- a/tools/testing/selftests/kvm/lib/io.c
+++ b/tools/testing/selftests/kvm/lib/io.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tools/testing/selftests/kvm/lib/io.c
*
* Copyright (C) 2018, Google LLC.
- *
- * This work is licensed under the terms of the GNU GPL, version 2.
*/
#include "test_util.h"
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index e9113857f44e..6e49bb039376 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tools/testing/selftests/kvm/lib/kvm_util.c
*
* Copyright (C) 2018, Google LLC.
- *
- * This work is licensed under the terms of the GNU GPL, version 2.
*/
#include "test_util.h"
@@ -135,7 +134,6 @@ struct kvm_vm *_vm_create(enum vm_guest_mode mode, uint64_t phy_pages,
int perm, unsigned long type)
{
struct kvm_vm *vm;
- int kvm_fd;
vm = calloc(1, sizeof(*vm));
TEST_ASSERT(vm != NULL, "Insufficient Memory");
@@ -556,9 +554,9 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm,
uint32_t flags)
{
int ret;
- unsigned long pmem_size = 0;
struct userspace_mem_region *region;
size_t huge_page_size = KVM_UTIL_PGS_PER_HUGEPG * vm->page_size;
+ size_t alignment;
TEST_ASSERT((guest_paddr % vm->page_size) == 0, "Guest physical "
"address not on a page boundary.\n"
@@ -608,9 +606,20 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm,
TEST_ASSERT(region != NULL, "Insufficient Memory");
region->mmap_size = npages * vm->page_size;
- /* Enough memory to align up to a huge page. */
+#ifdef __s390x__
+ /* On s390x, the host address must be aligned to 1M (due to PGSTEs) */
+ alignment = 0x100000;
+#else
+ alignment = 1;
+#endif
+
if (src_type == VM_MEM_SRC_ANONYMOUS_THP)
- region->mmap_size += huge_page_size;
+ alignment = max(huge_page_size, alignment);
+
+ /* Add enough memory to align up if necessary */
+ if (alignment > 1)
+ region->mmap_size += alignment;
+
region->mmap_start = mmap(NULL, region->mmap_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS
@@ -620,9 +629,8 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm,
"test_malloc failed, mmap_start: %p errno: %i",
region->mmap_start, errno);
- /* Align THP allocation up to start of a huge page. */
- region->host_mem = align(region->mmap_start,
- src_type == VM_MEM_SRC_ANONYMOUS_THP ? huge_page_size : 1);
+ /* Align host address */
+ region->host_mem = align(region->mmap_start, alignment);
/* As needed perform madvise */
if (src_type == VM_MEM_SRC_ANONYMOUS || src_type == VM_MEM_SRC_ANONYMOUS_THP) {
@@ -766,11 +774,10 @@ static int vcpu_mmap_sz(void)
*
* Return: None
*
- * Creates and adds to the VM specified by vm and virtual CPU with
- * the ID given by vcpuid.
+ * Adds a virtual CPU to the VM specified by vm with the ID given by vcpuid.
+ * No additional VCPU setup is done.
*/
-void vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpuid, int pgd_memslot,
- int gdt_memslot)
+void vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpuid)
{
struct vcpu *vcpu;
@@ -804,8 +811,6 @@ void vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpuid, int pgd_memslot,
vm->vcpu_head->prev = vcpu;
vcpu->next = vm->vcpu_head;
vm->vcpu_head = vcpu;
-
- vcpu_setup(vm, vcpuid, pgd_memslot, gdt_memslot);
}
/*
@@ -1224,6 +1229,7 @@ void vcpu_regs_set(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_regs *regs)
ret, errno);
}
+#ifdef __KVM_HAVE_VCPU_EVENTS
void vcpu_events_get(struct kvm_vm *vm, uint32_t vcpuid,
struct kvm_vcpu_events *events)
{
@@ -1249,7 +1255,9 @@ void vcpu_events_set(struct kvm_vm *vm, uint32_t vcpuid,
TEST_ASSERT(ret == 0, "KVM_SET_VCPU_EVENTS, failed, rc: %i errno: %i",
ret, errno);
}
+#endif
+#ifdef __x86_64__
void vcpu_nested_state_get(struct kvm_vm *vm, uint32_t vcpuid,
struct kvm_nested_state *state)
{
@@ -1281,6 +1289,7 @@ int vcpu_nested_state_set(struct kvm_vm *vm, uint32_t vcpuid,
return ret;
}
+#endif
/*
* VM VCPU System Regs Get
@@ -1334,7 +1343,6 @@ void vcpu_sregs_set(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_sregs *sregs)
int _vcpu_sregs_set(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_sregs *sregs)
{
struct vcpu *vcpu = vcpu_find(vm, vcpuid);
- int ret;
TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid);
@@ -1584,3 +1592,39 @@ void *addr_gva2hva(struct kvm_vm *vm, vm_vaddr_t gva)
{
return addr_gpa2hva(vm, addr_gva2gpa(vm, gva));
}
+
+/*
+ * Is Unrestricted Guest
+ *
+ * Input Args:
+ * vm - Virtual Machine
+ *
+ * Output Args: None
+ *
+ * Return: True if the unrestricted guest is set to 'Y', otherwise return false.
+ *
+ * Check if the unrestricted guest flag is enabled.
+ */
+bool vm_is_unrestricted_guest(struct kvm_vm *vm)
+{
+ char val = 'N';
+ size_t count;
+ FILE *f;
+
+ if (vm == NULL) {
+ /* Ensure that the KVM vendor-specific module is loaded. */
+ f = fopen(KVM_DEV_PATH, "r");
+ TEST_ASSERT(f != NULL, "Error in opening KVM dev file: %d",
+ errno);
+ fclose(f);
+ }
+
+ f = fopen("/sys/module/kvm_intel/parameters/unrestricted_guest", "r");
+ if (f) {
+ count = fread(&val, sizeof(char), 1, f);
+ TEST_ASSERT(count == 1, "Unable to read from param file.");
+ fclose(f);
+ }
+
+ return val == 'Y';
+}
diff --git a/tools/testing/selftests/kvm/lib/kvm_util_internal.h b/tools/testing/selftests/kvm/lib/kvm_util_internal.h
index 4595e42c6e29..f36262e0f655 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util_internal.h
+++ b/tools/testing/selftests/kvm/lib/kvm_util_internal.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tools/testing/selftests/kvm/lib/kvm_util_internal.h
*
* Copyright (C) 2018, Google LLC.
- *
- * This work is licensed under the terms of the GNU GPL, version 2.
*/
#ifndef SELFTEST_KVM_UTIL_INTERNAL_H
@@ -65,8 +64,6 @@ struct kvm_vm {
};
struct vcpu *vcpu_find(struct kvm_vm *vm, uint32_t vcpuid);
-void vcpu_setup(struct kvm_vm *vm, int vcpuid, int pgd_memslot,
- int gdt_memslot);
void virt_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent);
void regs_dump(FILE *stream, struct kvm_regs *regs, uint8_t indent);
void sregs_dump(FILE *stream, struct kvm_sregs *sregs, uint8_t indent);
diff --git a/tools/testing/selftests/kvm/lib/s390x/processor.c b/tools/testing/selftests/kvm/lib/s390x/processor.c
new file mode 100644
index 000000000000..32a02360b1eb
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/s390x/processor.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * KVM selftest s390x library code - CPU-related functions (page tables...)
+ *
+ * Copyright (C) 2019, Red Hat, Inc.
+ */
+
+#define _GNU_SOURCE /* for program_invocation_name */
+
+#include "processor.h"
+#include "kvm_util.h"
+#include "../kvm_util_internal.h"
+
+#define KVM_GUEST_PAGE_TABLE_MIN_PADDR 0x180000
+
+#define PAGES_PER_REGION 4
+
+void virt_pgd_alloc(struct kvm_vm *vm, uint32_t memslot)
+{
+ vm_paddr_t paddr;
+
+ TEST_ASSERT(vm->page_size == 4096, "Unsupported page size: 0x%x",
+ vm->page_size);
+
+ if (vm->pgd_created)
+ return;
+
+ paddr = vm_phy_pages_alloc(vm, PAGES_PER_REGION,
+ KVM_GUEST_PAGE_TABLE_MIN_PADDR, memslot);
+ memset(addr_gpa2hva(vm, paddr), 0xff, PAGES_PER_REGION * vm->page_size);
+
+ vm->pgd = paddr;
+ vm->pgd_created = true;
+}
+
+/*
+ * Allocate 4 pages for a region/segment table (ri < 4), or one page for
+ * a page table (ri == 4). Returns a suitable region/segment table entry
+ * which points to the freshly allocated pages.
+ */
+static uint64_t virt_alloc_region(struct kvm_vm *vm, int ri, uint32_t memslot)
+{
+ uint64_t taddr;
+
+ taddr = vm_phy_pages_alloc(vm, ri < 4 ? PAGES_PER_REGION : 1,
+ KVM_GUEST_PAGE_TABLE_MIN_PADDR, memslot);
+ memset(addr_gpa2hva(vm, taddr), 0xff, PAGES_PER_REGION * vm->page_size);
+
+ return (taddr & REGION_ENTRY_ORIGIN)
+ | (((4 - ri) << 2) & REGION_ENTRY_TYPE)
+ | ((ri < 4 ? (PAGES_PER_REGION - 1) : 0) & REGION_ENTRY_LENGTH);
+}
+
+/*
+ * VM Virtual Page Map
+ *
+ * Input Args:
+ * vm - Virtual Machine
+ * gva - VM Virtual Address
+ * gpa - VM Physical Address
+ * memslot - Memory region slot for new virtual translation tables
+ *
+ * Output Args: None
+ *
+ * Return: None
+ *
+ * Within the VM given by vm, creates a virtual translation for the page
+ * starting at vaddr to the page starting at paddr.
+ */
+void virt_pg_map(struct kvm_vm *vm, uint64_t gva, uint64_t gpa,
+ uint32_t memslot)
+{
+ int ri, idx;
+ uint64_t *entry;
+
+ TEST_ASSERT((gva % vm->page_size) == 0,
+ "Virtual address not on page boundary,\n"
+ " vaddr: 0x%lx vm->page_size: 0x%x",
+ gva, vm->page_size);
+ TEST_ASSERT(sparsebit_is_set(vm->vpages_valid,
+ (gva >> vm->page_shift)),
+ "Invalid virtual address, vaddr: 0x%lx",
+ gva);
+ TEST_ASSERT((gpa % vm->page_size) == 0,
+ "Physical address not on page boundary,\n"
+ " paddr: 0x%lx vm->page_size: 0x%x",
+ gva, vm->page_size);
+ TEST_ASSERT((gpa >> vm->page_shift) <= vm->max_gfn,
+ "Physical address beyond beyond maximum supported,\n"
+ " paddr: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x",
+ gva, vm->max_gfn, vm->page_size);
+
+ /* Walk through region and segment tables */
+ entry = addr_gpa2hva(vm, vm->pgd);
+ for (ri = 1; ri <= 4; ri++) {
+ idx = (gva >> (64 - 11 * ri)) & 0x7ffu;
+ if (entry[idx] & REGION_ENTRY_INVALID)
+ entry[idx] = virt_alloc_region(vm, ri, memslot);
+ entry = addr_gpa2hva(vm, entry[idx] & REGION_ENTRY_ORIGIN);
+ }
+
+ /* Fill in page table entry */
+ idx = (gva >> 12) & 0x0ffu; /* page index */
+ if (!(entry[idx] & PAGE_INVALID))
+ fprintf(stderr,
+ "WARNING: PTE for gpa=0x%"PRIx64" already set!\n", gpa);
+ entry[idx] = gpa;
+}
+
+/*
+ * Address Guest Virtual to Guest Physical
+ *
+ * Input Args:
+ * vm - Virtual Machine
+ * gpa - VM virtual address
+ *
+ * Output Args: None
+ *
+ * Return:
+ * Equivalent VM physical address
+ *
+ * Translates the VM virtual address given by gva to a VM physical
+ * address and then locates the memory region containing the VM
+ * physical address, within the VM given by vm. When found, the host
+ * virtual address providing the memory to the vm physical address is
+ * returned.
+ * A TEST_ASSERT failure occurs if no region containing translated
+ * VM virtual address exists.
+ */
+vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva)
+{
+ int ri, idx;
+ uint64_t *entry;
+
+ TEST_ASSERT(vm->page_size == 4096, "Unsupported page size: 0x%x",
+ vm->page_size);
+
+ entry = addr_gpa2hva(vm, vm->pgd);
+ for (ri = 1; ri <= 4; ri++) {
+ idx = (gva >> (64 - 11 * ri)) & 0x7ffu;
+ TEST_ASSERT(!(entry[idx] & REGION_ENTRY_INVALID),
+ "No region mapping for vm virtual address 0x%lx",
+ gva);
+ entry = addr_gpa2hva(vm, entry[idx] & REGION_ENTRY_ORIGIN);
+ }
+
+ idx = (gva >> 12) & 0x0ffu; /* page index */
+
+ TEST_ASSERT(!(entry[idx] & PAGE_INVALID),
+ "No page mapping for vm virtual address 0x%lx", gva);
+
+ return (entry[idx] & ~0xffful) + (gva & 0xffful);
+}
+
+static void virt_dump_ptes(FILE *stream, struct kvm_vm *vm, uint8_t indent,
+ uint64_t ptea_start)
+{
+ uint64_t *pte, ptea;
+
+ for (ptea = ptea_start; ptea < ptea_start + 0x100 * 8; ptea += 8) {
+ pte = addr_gpa2hva(vm, ptea);
+ if (*pte & PAGE_INVALID)
+ continue;
+ fprintf(stream, "%*spte @ 0x%lx: 0x%016lx\n",
+ indent, "", ptea, *pte);
+ }
+}
+
+static void virt_dump_region(FILE *stream, struct kvm_vm *vm, uint8_t indent,
+ uint64_t reg_tab_addr)
+{
+ uint64_t addr, *entry;
+
+ for (addr = reg_tab_addr; addr < reg_tab_addr + 0x400 * 8; addr += 8) {
+ entry = addr_gpa2hva(vm, addr);
+ if (*entry & REGION_ENTRY_INVALID)
+ continue;
+ fprintf(stream, "%*srt%lde @ 0x%lx: 0x%016lx\n",
+ indent, "", 4 - ((*entry & REGION_ENTRY_TYPE) >> 2),
+ addr, *entry);
+ if (*entry & REGION_ENTRY_TYPE) {
+ virt_dump_region(stream, vm, indent + 2,
+ *entry & REGION_ENTRY_ORIGIN);
+ } else {
+ virt_dump_ptes(stream, vm, indent + 2,
+ *entry & REGION_ENTRY_ORIGIN);
+ }
+ }
+}
+
+void virt_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent)
+{
+ if (!vm->pgd_created)
+ return;
+
+ virt_dump_region(stream, vm, indent, vm->pgd);
+}
+
+/*
+ * Create a VM with reasonable defaults
+ *
+ * Input Args:
+ * vcpuid - The id of the single VCPU to add to the VM.
+ * extra_mem_pages - The size of extra memories to add (this will
+ * decide how much extra space we will need to
+ * setup the page tables using mem slot 0)
+ * guest_code - The vCPU's entry point
+ *
+ * Output Args: None
+ *
+ * Return:
+ * Pointer to opaque structure that describes the created VM.
+ */
+struct kvm_vm *vm_create_default(uint32_t vcpuid, uint64_t extra_mem_pages,
+ void *guest_code)
+{
+ /*
+ * The additional amount of pages required for the page tables is:
+ * 1 * n / 256 + 4 * (n / 256) / 2048 + 4 * (n / 256) / 2048^2 + ...
+ * which is definitely smaller than (n / 256) * 2.
+ */
+ uint64_t extra_pg_pages = extra_mem_pages / 256 * 2;
+ struct kvm_vm *vm;
+
+ vm = vm_create(VM_MODE_DEFAULT,
+ DEFAULT_GUEST_PHY_PAGES + extra_pg_pages, O_RDWR);
+
+ kvm_vm_elf_load(vm, program_invocation_name, 0, 0);
+ vm_vcpu_add_default(vm, vcpuid, guest_code);
+
+ return vm;
+}
+
+/*
+ * Adds a vCPU with reasonable defaults (i.e. a stack and initial PSW)
+ *
+ * Input Args:
+ * vcpuid - The id of the VCPU to add to the VM.
+ * guest_code - The vCPU's entry point
+ */
+void vm_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, void *guest_code)
+{
+ size_t stack_size = DEFAULT_STACK_PGS * getpagesize();
+ uint64_t stack_vaddr;
+ struct kvm_regs regs;
+ struct kvm_sregs sregs;
+ struct kvm_run *run;
+
+ TEST_ASSERT(vm->page_size == 4096, "Unsupported page size: 0x%x",
+ vm->page_size);
+
+ stack_vaddr = vm_vaddr_alloc(vm, stack_size,
+ DEFAULT_GUEST_STACK_VADDR_MIN, 0, 0);
+
+ vm_vcpu_add(vm, vcpuid);
+
+ /* Setup guest registers */
+ vcpu_regs_get(vm, vcpuid, &regs);
+ regs.gprs[15] = stack_vaddr + (DEFAULT_STACK_PGS * getpagesize()) - 160;
+ vcpu_regs_set(vm, vcpuid, &regs);
+
+ vcpu_sregs_get(vm, vcpuid, &sregs);
+ sregs.crs[0] |= 0x00040000; /* Enable floating point regs */
+ sregs.crs[1] = vm->pgd | 0xf; /* Primary region table */
+ vcpu_sregs_set(vm, vcpuid, &sregs);
+
+ run = vcpu_state(vm, vcpuid);
+ run->psw_mask = 0x0400000180000000ULL; /* DAT enabled + 64 bit mode */
+ run->psw_addr = (uintptr_t)guest_code;
+}
+
+void vcpu_dump(FILE *stream, struct kvm_vm *vm, uint32_t vcpuid, uint8_t indent)
+{
+ struct vcpu *vcpu = vm->vcpu_head;
+
+ fprintf(stream, "%*spstate: psw: 0x%.16llx:0x%.16llx\n",
+ indent, "", vcpu->state->psw_mask, vcpu->state->psw_addr);
+}
diff --git a/tools/testing/selftests/kvm/lib/s390x/ucall.c b/tools/testing/selftests/kvm/lib/s390x/ucall.c
new file mode 100644
index 000000000000..fd589dc9bfab
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/s390x/ucall.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ucall support. A ucall is a "hypercall to userspace".
+ *
+ * Copyright (C) 2019 Red Hat, Inc.
+ */
+#include "kvm_util.h"
+
+void ucall_init(struct kvm_vm *vm, void *arg)
+{
+}
+
+void ucall_uninit(struct kvm_vm *vm)
+{
+}
+
+void ucall(uint64_t cmd, int nargs, ...)
+{
+ struct ucall uc = {
+ .cmd = cmd,
+ };
+ va_list va;
+ int i;
+
+ nargs = nargs <= UCALL_MAX_ARGS ? nargs : UCALL_MAX_ARGS;
+
+ va_start(va, nargs);
+ for (i = 0; i < nargs; ++i)
+ uc.args[i] = va_arg(va, uint64_t);
+ va_end(va);
+
+ /* Exit via DIAGNOSE 0x501 (normally used for breakpoints) */
+ asm volatile ("diag 0,%0,0x501" : : "a"(&uc) : "memory");
+}
+
+uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc)
+{
+ struct kvm_run *run = vcpu_state(vm, vcpu_id);
+ struct ucall ucall = {};
+
+ if (run->exit_reason == KVM_EXIT_S390_SIEIC &&
+ run->s390_sieic.icptcode == 4 &&
+ (run->s390_sieic.ipa >> 8) == 0x83 && /* 0x83 means DIAGNOSE */
+ (run->s390_sieic.ipb >> 16) == 0x501) {
+ int reg = run->s390_sieic.ipa & 0xf;
+
+ memcpy(&ucall, addr_gva2hva(vm, run->s.regs.gprs[reg]),
+ sizeof(ucall));
+
+ vcpu_run_complete_io(vm, vcpu_id);
+ if (uc)
+ memcpy(uc, &ucall, sizeof(ucall));
+ }
+
+ return ucall.cmd;
+}
diff --git a/tools/testing/selftests/kvm/lib/sparsebit.c b/tools/testing/selftests/kvm/lib/sparsebit.c
index b132bc95d183..031ba3c932ed 100644
--- a/tools/testing/selftests/kvm/lib/sparsebit.c
+++ b/tools/testing/selftests/kvm/lib/sparsebit.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Sparse bit array
*
* Copyright (C) 2018, Google LLC.
* Copyright (C) 2018, Red Hat, Inc. (code style cleanup and fuzzing driver)
*
- * This work is licensed under the terms of the GNU GPL, version 2.
- *
* This library provides functions to support a memory efficient bit array,
* with an index size of 2^64. A sparsebit array is allocated through
* the use sparsebit_alloc() and free'd via sparsebit_free(),
diff --git a/tools/testing/selftests/kvm/lib/ucall.c b/tools/testing/selftests/kvm/lib/ucall.c
deleted file mode 100644
index a2ab38be2f47..000000000000
--- a/tools/testing/selftests/kvm/lib/ucall.c
+++ /dev/null
@@ -1,150 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * ucall support. A ucall is a "hypercall to userspace".
- *
- * Copyright (C) 2018, Red Hat, Inc.
- */
-#include "kvm_util.h"
-#include "kvm_util_internal.h"
-
-#define UCALL_PIO_PORT ((uint16_t)0x1000)
-
-static ucall_type_t ucall_type;
-static vm_vaddr_t *ucall_exit_mmio_addr;
-
-static bool ucall_mmio_init(struct kvm_vm *vm, vm_paddr_t gpa)
-{
- if (kvm_userspace_memory_region_find(vm, gpa, gpa + 1))
- return false;
-
- virt_pg_map(vm, gpa, gpa, 0);
-
- ucall_exit_mmio_addr = (vm_vaddr_t *)gpa;
- sync_global_to_guest(vm, ucall_exit_mmio_addr);
-
- return true;
-}
-
-void ucall_init(struct kvm_vm *vm, ucall_type_t type, void *arg)
-{
- ucall_type = type;
- sync_global_to_guest(vm, ucall_type);
-
- if (type == UCALL_PIO)
- return;
-
- if (type == UCALL_MMIO) {
- vm_paddr_t gpa, start, end, step, offset;
- unsigned bits;
- bool ret;
-
- if (arg) {
- gpa = (vm_paddr_t)arg;
- ret = ucall_mmio_init(vm, gpa);
- TEST_ASSERT(ret, "Can't set ucall mmio address to %lx", gpa);
- return;
- }
-
- /*
- * Find an address within the allowed physical and virtual address
- * spaces, that does _not_ have a KVM memory region associated with
- * it. Identity mapping an address like this allows the guest to
- * access it, but as KVM doesn't know what to do with it, it
- * will assume it's something userspace handles and exit with
- * KVM_EXIT_MMIO. Well, at least that's how it works for AArch64.
- * Here we start with a guess that the addresses around 5/8th
- * of the allowed space are unmapped and then work both down and
- * up from there in 1/16th allowed space sized steps.
- *
- * Note, we need to use VA-bits - 1 when calculating the allowed
- * virtual address space for an identity mapping because the upper
- * half of the virtual address space is the two's complement of the
- * lower and won't match physical addresses.
- */
- bits = vm->va_bits - 1;
- bits = vm->pa_bits < bits ? vm->pa_bits : bits;
- end = 1ul << bits;
- start = end * 5 / 8;
- step = end / 16;
- for (offset = 0; offset < end - start; offset += step) {
- if (ucall_mmio_init(vm, start - offset))
- return;
- if (ucall_mmio_init(vm, start + offset))
- return;
- }
- TEST_ASSERT(false, "Can't find a ucall mmio address");
- }
-}
-
-void ucall_uninit(struct kvm_vm *vm)
-{
- ucall_type = 0;
- sync_global_to_guest(vm, ucall_type);
- ucall_exit_mmio_addr = 0;
- sync_global_to_guest(vm, ucall_exit_mmio_addr);
-}
-
-static void ucall_pio_exit(struct ucall *uc)
-{
-#ifdef __x86_64__
- asm volatile("in %[port], %%al"
- : : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax");
-#endif
-}
-
-static void ucall_mmio_exit(struct ucall *uc)
-{
- *ucall_exit_mmio_addr = (vm_vaddr_t)uc;
-}
-
-void ucall(uint64_t cmd, int nargs, ...)
-{
- struct ucall uc = {
- .cmd = cmd,
- };
- va_list va;
- int i;
-
- nargs = nargs <= UCALL_MAX_ARGS ? nargs : UCALL_MAX_ARGS;
-
- va_start(va, nargs);
- for (i = 0; i < nargs; ++i)
- uc.args[i] = va_arg(va, uint64_t);
- va_end(va);
-
- switch (ucall_type) {
- case UCALL_PIO:
- ucall_pio_exit(&uc);
- break;
- case UCALL_MMIO:
- ucall_mmio_exit(&uc);
- break;
- };
-}
-
-uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc)
-{
- struct kvm_run *run = vcpu_state(vm, vcpu_id);
-
- memset(uc, 0, sizeof(*uc));
-
-#ifdef __x86_64__
- if (ucall_type == UCALL_PIO && run->exit_reason == KVM_EXIT_IO &&
- run->io.port == UCALL_PIO_PORT) {
- struct kvm_regs regs;
- vcpu_regs_get(vm, vcpu_id, &regs);
- memcpy(uc, addr_gva2hva(vm, (vm_vaddr_t)regs.rdi), sizeof(*uc));
- return uc->cmd;
- }
-#endif
- if (ucall_type == UCALL_MMIO && run->exit_reason == KVM_EXIT_MMIO &&
- run->mmio.phys_addr == (uint64_t)ucall_exit_mmio_addr) {
- vm_vaddr_t gva;
- TEST_ASSERT(run->mmio.is_write && run->mmio.len == 8,
- "Unexpected ucall exit mmio address access");
- gva = *(vm_vaddr_t *)run->mmio.data;
- memcpy(uc, addr_gva2hva(vm, gva), sizeof(*uc));
- }
-
- return uc->cmd;
-}
diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c
index dc7fae9fa424..0a5e487dbc50 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/processor.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tools/testing/selftests/kvm/lib/x86_64/processor.c
*
* Copyright (C) 2018, Google LLC.
- *
- * This work is licensed under the terms of the GNU GPL, version 2.
*/
#define _GNU_SOURCE /* for program_invocation_name */
@@ -229,8 +228,6 @@ void sregs_dump(FILE *stream, struct kvm_sregs *sregs,
void virt_pgd_alloc(struct kvm_vm *vm, uint32_t pgd_memslot)
{
- int rc;
-
TEST_ASSERT(vm->mode == VM_MODE_P52V48_4K, "Attempt to use "
"unknown or unsupported guest mode, mode: 0x%x", vm->mode);
@@ -549,7 +546,6 @@ vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva)
struct pageDirectoryPointerEntry *pdpe;
struct pageDirectoryEntry *pde;
struct pageTableEntry *pte;
- void *hva;
TEST_ASSERT(vm->mode == VM_MODE_P52V48_4K, "Attempt to use "
"unknown or unsupported guest mode, mode: 0x%x", vm->mode);
@@ -582,6 +578,7 @@ vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva)
unmapped_gva:
TEST_ASSERT(false, "No mapping for vm virtual address, "
"gva: 0x%lx", gva);
+ exit(EXIT_FAILURE);
}
static void kvm_setup_gdt(struct kvm_vm *vm, struct kvm_dtable *dt, int gdt_memslot,
@@ -612,7 +609,7 @@ static void kvm_setup_tss_64bit(struct kvm_vm *vm, struct kvm_segment *segp,
kvm_seg_fill_gdt_64bit(vm, segp);
}
-void vcpu_setup(struct kvm_vm *vm, int vcpuid, int pgd_memslot, int gdt_memslot)
+static void vcpu_setup(struct kvm_vm *vm, int vcpuid, int pgd_memslot, int gdt_memslot)
{
struct kvm_sregs sregs;
@@ -658,7 +655,8 @@ void vm_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, void *guest_code)
DEFAULT_GUEST_STACK_VADDR_MIN, 0, 0);
/* Create VCPU */
- vm_vcpu_add(vm, vcpuid, 0, 0);
+ vm_vcpu_add(vm, vcpuid);
+ vcpu_setup(vm, vcpuid, 0, 0);
/* Setup guest general purpose registers */
vcpu_regs_get(vm, vcpuid, &regs);
@@ -823,7 +821,7 @@ struct kvm_vm *vm_create_default(uint32_t vcpuid, uint64_t extra_mem_pages,
uint64_t extra_pg_pages = extra_mem_pages / 512 * 2;
/* Create VM */
- vm = vm_create(VM_MODE_P52V48_4K,
+ vm = vm_create(VM_MODE_DEFAULT,
DEFAULT_GUEST_PHY_PAGES + extra_pg_pages,
O_RDWR);
@@ -1062,9 +1060,11 @@ struct kvm_x86_state *vcpu_save_state(struct kvm_vm *vm, uint32_t vcpuid)
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_XSAVE, r: %i",
r);
- r = ioctl(vcpu->fd, KVM_GET_XCRS, &state->xcrs);
- TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_XCRS, r: %i",
- r);
+ if (kvm_check_cap(KVM_CAP_XCRS)) {
+ r = ioctl(vcpu->fd, KVM_GET_XCRS, &state->xcrs);
+ TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_XCRS, r: %i",
+ r);
+ }
r = ioctl(vcpu->fd, KVM_GET_SREGS, &state->sregs);
TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_SREGS, r: %i",
@@ -1105,9 +1105,11 @@ void vcpu_load_state(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_x86_state *s
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_XSAVE, r: %i",
r);
- r = ioctl(vcpu->fd, KVM_SET_XCRS, &state->xcrs);
- TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_XCRS, r: %i",
- r);
+ if (kvm_check_cap(KVM_CAP_XCRS)) {
+ r = ioctl(vcpu->fd, KVM_SET_XCRS, &state->xcrs);
+ TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_XCRS, r: %i",
+ r);
+ }
r = ioctl(vcpu->fd, KVM_SET_SREGS, &state->sregs);
TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_SREGS, r: %i",
@@ -1139,3 +1141,19 @@ void vcpu_load_state(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_x86_state *s
r);
}
}
+
+bool is_intel_cpu(void)
+{
+ int eax, ebx, ecx, edx;
+ const uint32_t *chunk;
+ const int leaf = 0;
+
+ __asm__ __volatile__(
+ "cpuid"
+ : /* output */ "=a"(eax), "=b"(ebx),
+ "=c"(ecx), "=d"(edx)
+ : /* input */ "0"(leaf), "2"(0));
+
+ chunk = (const uint32_t *)("GenuineIntel");
+ return (ebx == chunk[0] && edx == chunk[1] && ecx == chunk[2]);
+}
diff --git a/tools/testing/selftests/kvm/lib/x86_64/ucall.c b/tools/testing/selftests/kvm/lib/x86_64/ucall.c
new file mode 100644
index 000000000000..4bfc9a90b1de
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/x86_64/ucall.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ucall support. A ucall is a "hypercall to userspace".
+ *
+ * Copyright (C) 2018, Red Hat, Inc.
+ */
+#include "kvm_util.h"
+
+#define UCALL_PIO_PORT ((uint16_t)0x1000)
+
+void ucall_init(struct kvm_vm *vm, void *arg)
+{
+}
+
+void ucall_uninit(struct kvm_vm *vm)
+{
+}
+
+void ucall(uint64_t cmd, int nargs, ...)
+{
+ struct ucall uc = {
+ .cmd = cmd,
+ };
+ va_list va;
+ int i;
+
+ nargs = nargs <= UCALL_MAX_ARGS ? nargs : UCALL_MAX_ARGS;
+
+ va_start(va, nargs);
+ for (i = 0; i < nargs; ++i)
+ uc.args[i] = va_arg(va, uint64_t);
+ va_end(va);
+
+ asm volatile("in %[port], %%al"
+ : : [port] "d" (UCALL_PIO_PORT), "D" (&uc) : "rax");
+}
+
+uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc)
+{
+ struct kvm_run *run = vcpu_state(vm, vcpu_id);
+ struct ucall ucall = {};
+
+ if (run->exit_reason == KVM_EXIT_IO && run->io.port == UCALL_PIO_PORT) {
+ struct kvm_regs regs;
+
+ vcpu_regs_get(vm, vcpu_id, &regs);
+ memcpy(&ucall, addr_gva2hva(vm, (vm_vaddr_t)regs.rdi),
+ sizeof(ucall));
+
+ vcpu_run_complete_io(vm, vcpu_id);
+ if (uc)
+ memcpy(uc, &ucall, sizeof(ucall));
+ }
+
+ return ucall.cmd;
+}
diff --git a/tools/testing/selftests/kvm/lib/x86_64/vmx.c b/tools/testing/selftests/kvm/lib/x86_64/vmx.c
index 771ba6bf751c..9cef0455b819 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/vmx.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/vmx.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tools/testing/selftests/kvm/lib/x86_64/vmx.c
*
* Copyright (C) 2018, Google LLC.
- *
- * This work is licensed under the terms of the GNU GPL, version 2.
*/
-#define _GNU_SOURCE /* for program_invocation_name */
-
#include "test_util.h"
#include "kvm_util.h"
#include "processor.h"
@@ -15,6 +12,26 @@
bool enable_evmcs;
+int vcpu_enable_evmcs(struct kvm_vm *vm, int vcpu_id)
+{
+ uint16_t evmcs_ver;
+
+ struct kvm_enable_cap enable_evmcs_cap = {
+ .cap = KVM_CAP_HYPERV_ENLIGHTENED_VMCS,
+ .args[0] = (unsigned long)&evmcs_ver
+ };
+
+ vcpu_ioctl(vm, vcpu_id, KVM_ENABLE_CAP, &enable_evmcs_cap);
+
+ /* KVM should return supported EVMCS version range */
+ TEST_ASSERT(((evmcs_ver >> 8) >= (evmcs_ver & 0xff)) &&
+ (evmcs_ver & 0xff) > 0,
+ "Incorrect EVMCS version range: %x:%x\n",
+ evmcs_ver & 0xff, evmcs_ver >> 8);
+
+ return evmcs_ver;
+}
+
/* Allocate memory regions for nested VMX tests.
*
* Input Args:
diff --git a/tools/testing/selftests/kvm/s390x/memop.c b/tools/testing/selftests/kvm/s390x/memop.c
new file mode 100644
index 000000000000..9edaa9a134ce
--- /dev/null
+++ b/tools/testing/selftests/kvm/s390x/memop.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Test for s390x KVM_S390_MEM_OP
+ *
+ * Copyright (C) 2019, Red Hat, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include "test_util.h"
+#include "kvm_util.h"
+
+#define VCPU_ID 1
+
+static uint8_t mem1[65536];
+static uint8_t mem2[65536];
+
+static void guest_code(void)
+{
+ int i;
+
+ for (;;) {
+ for (i = 0; i < sizeof(mem2); i++)
+ mem2[i] = mem1[i];
+ GUEST_SYNC(0);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ struct kvm_vm *vm;
+ struct kvm_run *run;
+ struct kvm_s390_mem_op ksmo;
+ int rv, i, maxsize;
+
+ setbuf(stdout, NULL); /* Tell stdout not to buffer its content */
+
+ maxsize = kvm_check_cap(KVM_CAP_S390_MEM_OP);
+ if (!maxsize) {
+ fprintf(stderr, "CAP_S390_MEM_OP not supported -> skip test\n");
+ exit(KSFT_SKIP);
+ }
+ if (maxsize > sizeof(mem1))
+ maxsize = sizeof(mem1);
+
+ /* Create VM */
+ vm = vm_create_default(VCPU_ID, 0, guest_code);
+ run = vcpu_state(vm, VCPU_ID);
+
+ for (i = 0; i < sizeof(mem1); i++)
+ mem1[i] = i * i + i;
+
+ /* Set the first array */
+ ksmo.gaddr = addr_gva2gpa(vm, (uintptr_t)mem1);
+ ksmo.flags = 0;
+ ksmo.size = maxsize;
+ ksmo.op = KVM_S390_MEMOP_LOGICAL_WRITE;
+ ksmo.buf = (uintptr_t)mem1;
+ ksmo.ar = 0;
+ vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo);
+
+ /* Let the guest code copy the first array to the second */
+ vcpu_run(vm, VCPU_ID);
+ TEST_ASSERT(run->exit_reason == KVM_EXIT_S390_SIEIC,
+ "Unexpected exit reason: %u (%s)\n",
+ run->exit_reason,
+ exit_reason_str(run->exit_reason));
+
+ memset(mem2, 0xaa, sizeof(mem2));
+
+ /* Get the second array */
+ ksmo.gaddr = (uintptr_t)mem2;
+ ksmo.flags = 0;
+ ksmo.size = maxsize;
+ ksmo.op = KVM_S390_MEMOP_LOGICAL_READ;
+ ksmo.buf = (uintptr_t)mem2;
+ ksmo.ar = 0;
+ vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo);
+
+ TEST_ASSERT(!memcmp(mem1, mem2, maxsize),
+ "Memory contents do not match!");
+
+ /* Check error conditions - first bad size: */
+ ksmo.gaddr = (uintptr_t)mem1;
+ ksmo.flags = 0;
+ ksmo.size = -1;
+ ksmo.op = KVM_S390_MEMOP_LOGICAL_WRITE;
+ ksmo.buf = (uintptr_t)mem1;
+ ksmo.ar = 0;
+ rv = _vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo);
+ TEST_ASSERT(rv == -1 && errno == E2BIG, "ioctl allows insane sizes");
+
+ /* Zero size: */
+ ksmo.gaddr = (uintptr_t)mem1;
+ ksmo.flags = 0;
+ ksmo.size = 0;
+ ksmo.op = KVM_S390_MEMOP_LOGICAL_WRITE;
+ ksmo.buf = (uintptr_t)mem1;
+ ksmo.ar = 0;
+ rv = _vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo);
+ TEST_ASSERT(rv == -1 && (errno == EINVAL || errno == ENOMEM),
+ "ioctl allows 0 as size");
+
+ /* Bad flags: */
+ ksmo.gaddr = (uintptr_t)mem1;
+ ksmo.flags = -1;
+ ksmo.size = maxsize;
+ ksmo.op = KVM_S390_MEMOP_LOGICAL_WRITE;
+ ksmo.buf = (uintptr_t)mem1;
+ ksmo.ar = 0;
+ rv = _vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo);
+ TEST_ASSERT(rv == -1 && errno == EINVAL, "ioctl allows all flags");
+
+ /* Bad operation: */
+ ksmo.gaddr = (uintptr_t)mem1;
+ ksmo.flags = 0;
+ ksmo.size = maxsize;
+ ksmo.op = -1;
+ ksmo.buf = (uintptr_t)mem1;
+ ksmo.ar = 0;
+ rv = _vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo);
+ TEST_ASSERT(rv == -1 && errno == EINVAL, "ioctl allows bad operations");
+
+ /* Bad guest address: */
+ ksmo.gaddr = ~0xfffUL;
+ ksmo.flags = KVM_S390_MEMOP_F_CHECK_ONLY;
+ ksmo.size = maxsize;
+ ksmo.op = KVM_S390_MEMOP_LOGICAL_WRITE;
+ ksmo.buf = (uintptr_t)mem1;
+ ksmo.ar = 0;
+ rv = _vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo);
+ TEST_ASSERT(rv > 0, "ioctl does not report bad guest memory access");
+
+ /* Bad host address: */
+ ksmo.gaddr = (uintptr_t)mem1;
+ ksmo.flags = 0;
+ ksmo.size = maxsize;
+ ksmo.op = KVM_S390_MEMOP_LOGICAL_WRITE;
+ ksmo.buf = 0;
+ ksmo.ar = 0;
+ rv = _vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo);
+ TEST_ASSERT(rv == -1 && errno == EFAULT,
+ "ioctl does not report bad host memory address");
+
+ /* Bad access register: */
+ run->psw_mask &= ~(3UL << (63 - 17));
+ run->psw_mask |= 1UL << (63 - 17); /* Enable AR mode */
+ vcpu_run(vm, VCPU_ID); /* To sync new state to SIE block */
+ ksmo.gaddr = (uintptr_t)mem1;
+ ksmo.flags = 0;
+ ksmo.size = maxsize;
+ ksmo.op = KVM_S390_MEMOP_LOGICAL_WRITE;
+ ksmo.buf = (uintptr_t)mem1;
+ ksmo.ar = 17;
+ rv = _vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo);
+ TEST_ASSERT(rv == -1 && errno == EINVAL, "ioctl allows ARs > 15");
+ run->psw_mask &= ~(3UL << (63 - 17)); /* Disable AR mode */
+ vcpu_run(vm, VCPU_ID); /* Run to sync new state */
+
+ kvm_vm_free(vm);
+
+ return 0;
+}
diff --git a/tools/testing/selftests/kvm/s390x/sync_regs_test.c b/tools/testing/selftests/kvm/s390x/sync_regs_test.c
new file mode 100644
index 000000000000..d5290b4ad636
--- /dev/null
+++ b/tools/testing/selftests/kvm/s390x/sync_regs_test.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Test for s390x KVM_CAP_SYNC_REGS
+ *
+ * Based on the same test for x86:
+ * Copyright (C) 2018, Google LLC.
+ *
+ * Adaptions for s390x:
+ * Copyright (C) 2019, Red Hat, Inc.
+ *
+ * Test expected behavior of the KVM_CAP_SYNC_REGS functionality.
+ */
+
+#define _GNU_SOURCE /* for program_invocation_short_name */
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include "test_util.h"
+#include "kvm_util.h"
+
+#define VCPU_ID 5
+
+static void guest_code(void)
+{
+ register u64 stage asm("11") = 0;
+
+ for (;;) {
+ GUEST_SYNC(0);
+ asm volatile ("ahi %0,1" : : "r"(stage));
+ }
+}
+
+#define REG_COMPARE(reg) \
+ TEST_ASSERT(left->reg == right->reg, \
+ "Register " #reg \
+ " values did not match: 0x%llx, 0x%llx\n", \
+ left->reg, right->reg)
+
+static void compare_regs(struct kvm_regs *left, struct kvm_sync_regs *right)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ REG_COMPARE(gprs[i]);
+}
+
+static void compare_sregs(struct kvm_sregs *left, struct kvm_sync_regs *right)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ REG_COMPARE(acrs[i]);
+
+ for (i = 0; i < 16; i++)
+ REG_COMPARE(crs[i]);
+}
+
+#undef REG_COMPARE
+
+#define TEST_SYNC_FIELDS (KVM_SYNC_GPRS|KVM_SYNC_ACRS|KVM_SYNC_CRS)
+#define INVALID_SYNC_FIELD 0x80000000
+
+int main(int argc, char *argv[])
+{
+ struct kvm_vm *vm;
+ struct kvm_run *run;
+ struct kvm_regs regs;
+ struct kvm_sregs sregs;
+ int rv, cap;
+
+ /* Tell stdout not to buffer its content */
+ setbuf(stdout, NULL);
+
+ cap = kvm_check_cap(KVM_CAP_SYNC_REGS);
+ if (!cap) {
+ fprintf(stderr, "CAP_SYNC_REGS not supported, skipping test\n");
+ exit(KSFT_SKIP);
+ }
+
+ /* Create VM */
+ vm = vm_create_default(VCPU_ID, 0, guest_code);
+
+ run = vcpu_state(vm, VCPU_ID);
+
+ /* Request reading invalid register set from VCPU. */
+ run->kvm_valid_regs = INVALID_SYNC_FIELD;
+ rv = _vcpu_run(vm, VCPU_ID);
+ TEST_ASSERT(rv < 0 && errno == EINVAL,
+ "Invalid kvm_valid_regs did not cause expected KVM_RUN error: %d\n",
+ rv);
+ vcpu_state(vm, VCPU_ID)->kvm_valid_regs = 0;
+
+ run->kvm_valid_regs = INVALID_SYNC_FIELD | TEST_SYNC_FIELDS;
+ rv = _vcpu_run(vm, VCPU_ID);
+ TEST_ASSERT(rv < 0 && errno == EINVAL,
+ "Invalid kvm_valid_regs did not cause expected KVM_RUN error: %d\n",
+ rv);
+ vcpu_state(vm, VCPU_ID)->kvm_valid_regs = 0;
+
+ /* Request setting invalid register set into VCPU. */
+ run->kvm_dirty_regs = INVALID_SYNC_FIELD;
+ rv = _vcpu_run(vm, VCPU_ID);
+ TEST_ASSERT(rv < 0 && errno == EINVAL,
+ "Invalid kvm_dirty_regs did not cause expected KVM_RUN error: %d\n",
+ rv);
+ vcpu_state(vm, VCPU_ID)->kvm_dirty_regs = 0;
+
+ run->kvm_dirty_regs = INVALID_SYNC_FIELD | TEST_SYNC_FIELDS;
+ rv = _vcpu_run(vm, VCPU_ID);
+ TEST_ASSERT(rv < 0 && errno == EINVAL,
+ "Invalid kvm_dirty_regs did not cause expected KVM_RUN error: %d\n",
+ rv);
+ vcpu_state(vm, VCPU_ID)->kvm_dirty_regs = 0;
+
+ /* Request and verify all valid register sets. */
+ run->kvm_valid_regs = TEST_SYNC_FIELDS;
+ rv = _vcpu_run(vm, VCPU_ID);
+ TEST_ASSERT(rv == 0, "vcpu_run failed: %d\n", rv);
+ TEST_ASSERT(run->exit_reason == KVM_EXIT_S390_SIEIC,
+ "Unexpected exit reason: %u (%s)\n",
+ run->exit_reason,
+ exit_reason_str(run->exit_reason));
+ TEST_ASSERT(run->s390_sieic.icptcode == 4 &&
+ (run->s390_sieic.ipa >> 8) == 0x83 &&
+ (run->s390_sieic.ipb >> 16) == 0x501,
+ "Unexpected interception code: ic=%u, ipa=0x%x, ipb=0x%x\n",
+ run->s390_sieic.icptcode, run->s390_sieic.ipa,
+ run->s390_sieic.ipb);
+
+ vcpu_regs_get(vm, VCPU_ID, &regs);
+ compare_regs(&regs, &run->s.regs);
+
+ vcpu_sregs_get(vm, VCPU_ID, &sregs);
+ compare_sregs(&sregs, &run->s.regs);
+
+ /* Set and verify various register values */
+ run->s.regs.gprs[11] = 0xBAD1DEA;
+ run->s.regs.acrs[0] = 1 << 11;
+
+ run->kvm_valid_regs = TEST_SYNC_FIELDS;
+ run->kvm_dirty_regs = KVM_SYNC_GPRS | KVM_SYNC_ACRS;
+ rv = _vcpu_run(vm, VCPU_ID);
+ TEST_ASSERT(rv == 0, "vcpu_run failed: %d\n", rv);
+ TEST_ASSERT(run->exit_reason == KVM_EXIT_S390_SIEIC,
+ "Unexpected exit reason: %u (%s)\n",
+ run->exit_reason,
+ exit_reason_str(run->exit_reason));
+ TEST_ASSERT(run->s.regs.gprs[11] == 0xBAD1DEA + 1,
+ "r11 sync regs value incorrect 0x%llx.",
+ run->s.regs.gprs[11]);
+ TEST_ASSERT(run->s.regs.acrs[0] == 1 << 11,
+ "acr0 sync regs value incorrect 0x%llx.",
+ run->s.regs.acrs[0]);
+
+ vcpu_regs_get(vm, VCPU_ID, &regs);
+ compare_regs(&regs, &run->s.regs);
+
+ vcpu_sregs_get(vm, VCPU_ID, &sregs);
+ compare_sregs(&sregs, &run->s.regs);
+
+ /* Clear kvm_dirty_regs bits, verify new s.regs values are
+ * overwritten with existing guest values.
+ */
+ run->kvm_valid_regs = TEST_SYNC_FIELDS;
+ run->kvm_dirty_regs = 0;
+ run->s.regs.gprs[11] = 0xDEADBEEF;
+ rv = _vcpu_run(vm, VCPU_ID);
+ TEST_ASSERT(rv == 0, "vcpu_run failed: %d\n", rv);
+ TEST_ASSERT(run->exit_reason == KVM_EXIT_S390_SIEIC,
+ "Unexpected exit reason: %u (%s)\n",
+ run->exit_reason,
+ exit_reason_str(run->exit_reason));
+ TEST_ASSERT(run->s.regs.gprs[11] != 0xDEADBEEF,
+ "r11 sync regs value incorrect 0x%llx.",
+ run->s.regs.gprs[11]);
+
+ kvm_vm_free(vm);
+
+ return 0;
+}
diff --git a/tools/testing/selftests/kvm/x86_64/cr4_cpuid_sync_test.c b/tools/testing/selftests/kvm/x86_64/cr4_cpuid_sync_test.c
index 7c2c4d4055a8..63cc9c3f5ab6 100644
--- a/tools/testing/selftests/kvm/x86_64/cr4_cpuid_sync_test.c
+++ b/tools/testing/selftests/kvm/x86_64/cr4_cpuid_sync_test.c
@@ -87,6 +87,7 @@ int main(int argc, char *argv[])
while (1) {
rc = _vcpu_run(vm, VCPU_ID);
+ TEST_ASSERT(rc == 0, "vcpu_run failed: %d\n", rc);
TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
"Unexpected exit reason: %u (%s),\n",
run->exit_reason,
diff --git a/tools/testing/selftests/kvm/x86_64/evmcs_test.c b/tools/testing/selftests/kvm/x86_64/evmcs_test.c
index 36669684eca5..92915e6408e7 100644
--- a/tools/testing/selftests/kvm/x86_64/evmcs_test.c
+++ b/tools/testing/selftests/kvm/x86_64/evmcs_test.c
@@ -19,8 +19,6 @@
#define VCPU_ID 5
-static bool have_nested_state;
-
void l2_guest_code(void)
{
GUEST_SYNC(6);
@@ -73,7 +71,6 @@ void guest_code(struct vmx_pages *vmx_pages)
int main(int argc, char *argv[])
{
- struct vmx_pages *vmx_pages = NULL;
vm_vaddr_t vmx_pages_gva = 0;
struct kvm_regs regs1, regs2;
@@ -82,13 +79,6 @@ int main(int argc, char *argv[])
struct kvm_x86_state *state;
struct ucall uc;
int stage;
- uint16_t evmcs_ver;
- struct kvm_enable_cap enable_evmcs_cap = {
- .cap = KVM_CAP_HYPERV_ENLIGHTENED_VMCS,
- .args[0] = (unsigned long)&evmcs_ver
- };
-
- struct kvm_cpuid_entry2 *entry = kvm_get_supported_cpuid_entry(1);
/* Create VM */
vm = vm_create_default(VCPU_ID, 0, guest_code);
@@ -101,19 +91,13 @@ int main(int argc, char *argv[])
exit(KSFT_SKIP);
}
- vcpu_ioctl(vm, VCPU_ID, KVM_ENABLE_CAP, &enable_evmcs_cap);
-
- /* KVM should return supported EVMCS version range */
- TEST_ASSERT(((evmcs_ver >> 8) >= (evmcs_ver & 0xff)) &&
- (evmcs_ver & 0xff) > 0,
- "Incorrect EVMCS version range: %x:%x\n",
- evmcs_ver & 0xff, evmcs_ver >> 8);
+ vcpu_enable_evmcs(vm, VCPU_ID);
run = vcpu_state(vm, VCPU_ID);
vcpu_regs_get(vm, VCPU_ID, &regs1);
- vmx_pages = vcpu_alloc_vmx(vm, &vmx_pages_gva);
+ vcpu_alloc_vmx(vm, &vmx_pages_gva);
vcpu_args_set(vm, VCPU_ID, 1, vmx_pages_gva);
for (stage = 1;; stage++) {
@@ -149,8 +133,9 @@ int main(int argc, char *argv[])
/* Restore state in a new VM. */
kvm_vm_restart(vm, O_RDWR);
- vm_vcpu_add(vm, VCPU_ID, 0, 0);
+ vm_vcpu_add(vm, VCPU_ID);
vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid());
+ vcpu_enable_evmcs(vm, VCPU_ID);
vcpu_load_state(vm, VCPU_ID, state);
run = vcpu_state(vm, VCPU_ID);
free(state);
diff --git a/tools/testing/selftests/kvm/x86_64/hyperv_cpuid.c b/tools/testing/selftests/kvm/x86_64/hyperv_cpuid.c
index 9a21e912097c..ee59831fbc98 100644
--- a/tools/testing/selftests/kvm/x86_64/hyperv_cpuid.c
+++ b/tools/testing/selftests/kvm/x86_64/hyperv_cpuid.c
@@ -18,6 +18,7 @@
#include "test_util.h"
#include "kvm_util.h"
#include "processor.h"
+#include "vmx.h"
#define VCPU_ID 0
@@ -52,15 +53,11 @@ static void test_hv_cpuid(struct kvm_cpuid2 *hv_cpuid_entries,
TEST_ASSERT(entry->index == 0,
".index field should be zero");
- TEST_ASSERT(entry->index == 0,
- ".index field should be zero");
-
TEST_ASSERT(entry->flags == 0,
".flags field should be zero");
- TEST_ASSERT(entry->padding[0] == entry->padding[1]
- == entry->padding[2] == 0,
- ".index field should be zero");
+ TEST_ASSERT(!entry->padding[0] && !entry->padding[1] &&
+ !entry->padding[2], "padding should be zero");
/*
* If needed for debug:
@@ -90,7 +87,6 @@ struct kvm_cpuid2 *kvm_get_supported_hv_cpuid(struct kvm_vm *vm)
{
int nent = 20; /* should be enough */
static struct kvm_cpuid2 *cpuid;
- int ret;
cpuid = malloc(sizeof(*cpuid) + nent * sizeof(struct kvm_cpuid_entry2));
@@ -111,12 +107,7 @@ int main(int argc, char *argv[])
{
struct kvm_vm *vm;
int rv;
- uint16_t evmcs_ver;
struct kvm_cpuid2 *hv_cpuid_entries;
- struct kvm_enable_cap enable_evmcs_cap = {
- .cap = KVM_CAP_HYPERV_ENLIGHTENED_VMCS,
- .args[0] = (unsigned long)&evmcs_ver
- };
/* Tell stdout not to buffer its content */
setbuf(stdout, NULL);
@@ -141,14 +132,14 @@ int main(int argc, char *argv[])
free(hv_cpuid_entries);
- rv = _vcpu_ioctl(vm, VCPU_ID, KVM_ENABLE_CAP, &enable_evmcs_cap);
-
- if (rv) {
+ if (!kvm_check_cap(KVM_CAP_HYPERV_ENLIGHTENED_VMCS)) {
fprintf(stderr,
"Enlightened VMCS is unsupported, skip related test\n");
goto vm_free;
}
+ vcpu_enable_evmcs(vm, VCPU_ID);
+
hv_cpuid_entries = kvm_get_supported_hv_cpuid(vm);
if (!hv_cpuid_entries)
return 1;
diff --git a/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
new file mode 100644
index 000000000000..00bb97d76000
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
@@ -0,0 +1,126 @@
+/*
+ * mmio_warning_test
+ *
+ * Copyright (C) 2019, Google LLC.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ *
+ * Test that we don't get a kernel warning when we call KVM_RUN after a
+ * triple fault occurs. To get the triple fault to occur we call KVM_RUN
+ * on a VCPU that hasn't been properly setup.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <kvm_util.h>
+#include <linux/kvm.h>
+#include <processor.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <test_util.h>
+#include <unistd.h>
+
+#define NTHREAD 4
+#define NPROCESS 5
+
+struct thread_context {
+ int kvmcpu;
+ struct kvm_run *run;
+};
+
+void *thr(void *arg)
+{
+ struct thread_context *tc = (struct thread_context *)arg;
+ int res;
+ int kvmcpu = tc->kvmcpu;
+ struct kvm_run *run = tc->run;
+
+ res = ioctl(kvmcpu, KVM_RUN, 0);
+ printf("ret1=%d exit_reason=%d suberror=%d\n",
+ res, run->exit_reason, run->internal.suberror);
+
+ return 0;
+}
+
+void test(void)
+{
+ int i, kvm, kvmvm, kvmcpu;
+ pthread_t th[NTHREAD];
+ struct kvm_run *run;
+ struct thread_context tc;
+
+ kvm = open("/dev/kvm", O_RDWR);
+ TEST_ASSERT(kvm != -1, "failed to open /dev/kvm");
+ kvmvm = ioctl(kvm, KVM_CREATE_VM, 0);
+ TEST_ASSERT(kvmvm != -1, "KVM_CREATE_VM failed");
+ kvmcpu = ioctl(kvmvm, KVM_CREATE_VCPU, 0);
+ TEST_ASSERT(kvmcpu != -1, "KVM_CREATE_VCPU failed");
+ run = (struct kvm_run *)mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED,
+ kvmcpu, 0);
+ tc.kvmcpu = kvmcpu;
+ tc.run = run;
+ srand(getpid());
+ for (i = 0; i < NTHREAD; i++) {
+ pthread_create(&th[i], NULL, thr, (void *)(uintptr_t)&tc);
+ usleep(rand() % 10000);
+ }
+ for (i = 0; i < NTHREAD; i++)
+ pthread_join(th[i], NULL);
+}
+
+int get_warnings_count(void)
+{
+ int warnings;
+ FILE *f;
+
+ f = popen("dmesg | grep \"WARNING:\" | wc -l", "r");
+ fscanf(f, "%d", &warnings);
+ fclose(f);
+
+ return warnings;
+}
+
+int main(void)
+{
+ int warnings_before, warnings_after;
+
+ if (!is_intel_cpu()) {
+ printf("Must be run on an Intel CPU, skipping test\n");
+ exit(KSFT_SKIP);
+ }
+
+ if (vm_is_unrestricted_guest(NULL)) {
+ printf("Unrestricted guest must be disabled, skipping test\n");
+ exit(KSFT_SKIP);
+ }
+
+ warnings_before = get_warnings_count();
+
+ for (int i = 0; i < NPROCESS; ++i) {
+ int status;
+ int pid = fork();
+
+ if (pid < 0)
+ exit(1);
+ if (pid == 0) {
+ test();
+ exit(0);
+ }
+ while (waitpid(pid, &status, __WALL) != pid)
+ ;
+ }
+
+ warnings_after = get_warnings_count();
+ TEST_ASSERT(warnings_before == warnings_after,
+ "Warnings found in kernel. Run 'dmesg' to inspect them.");
+
+ return 0;
+}
diff --git a/tools/testing/selftests/kvm/x86_64/platform_info_test.c b/tools/testing/selftests/kvm/x86_64/platform_info_test.c
index eb3e7a838cb4..f9334bd3cce9 100644
--- a/tools/testing/selftests/kvm/x86_64/platform_info_test.c
+++ b/tools/testing/selftests/kvm/x86_64/platform_info_test.c
@@ -81,7 +81,6 @@ static void test_msr_platform_info_disabled(struct kvm_vm *vm)
int main(int argc, char *argv[])
{
struct kvm_vm *vm;
- struct kvm_run *state;
int rv;
uint64_t msr_platform_info;
@@ -100,8 +99,8 @@ int main(int argc, char *argv[])
msr_platform_info = vcpu_get_msr(vm, VCPU_ID, MSR_PLATFORM_INFO);
vcpu_set_msr(vm, VCPU_ID, MSR_PLATFORM_INFO,
msr_platform_info | MSR_PLATFORM_INFO_MAX_TURBO_RATIO);
- test_msr_platform_info_disabled(vm);
test_msr_platform_info_enabled(vm);
+ test_msr_platform_info_disabled(vm);
vcpu_set_msr(vm, VCPU_ID, MSR_PLATFORM_INFO, msr_platform_info);
kvm_vm_free(vm);
diff --git a/tools/testing/selftests/kvm/x86_64/set_sregs_test.c b/tools/testing/selftests/kvm/x86_64/set_sregs_test.c
index 35640e8e95bc..9f7656184f31 100644
--- a/tools/testing/selftests/kvm/x86_64/set_sregs_test.c
+++ b/tools/testing/selftests/kvm/x86_64/set_sregs_test.c
@@ -1,16 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* KVM_SET_SREGS tests
*
* Copyright (C) 2018, Google LLC.
*
- * This work is licensed under the terms of the GNU GPL, version 2.
- *
* This is a regression test for the bug fixed by the following commit:
* d3802286fa0f ("kvm: x86: Disallow illegal IA32_APIC_BASE MSR values")
*
* That bug allowed a user-mode program that called the KVM_SET_SREGS
* ioctl to put a VCPU's local APIC into an invalid state.
- *
*/
#define _GNU_SOURCE /* for program_invocation_short_name */
#include <fcntl.h>
diff --git a/tools/testing/selftests/kvm/x86_64/smm_test.c b/tools/testing/selftests/kvm/x86_64/smm_test.c
index fb8086964d83..8c063646f2a0 100644
--- a/tools/testing/selftests/kvm/x86_64/smm_test.c
+++ b/tools/testing/selftests/kvm/x86_64/smm_test.c
@@ -87,7 +87,6 @@ void guest_code(struct vmx_pages *vmx_pages)
int main(int argc, char *argv[])
{
- struct vmx_pages *vmx_pages = NULL;
vm_vaddr_t vmx_pages_gva = 0;
struct kvm_regs regs;
@@ -115,7 +114,7 @@ int main(int argc, char *argv[])
vcpu_set_msr(vm, VCPU_ID, MSR_IA32_SMBASE, SMRAM_GPA);
if (kvm_check_cap(KVM_CAP_NESTED_STATE)) {
- vmx_pages = vcpu_alloc_vmx(vm, &vmx_pages_gva);
+ vcpu_alloc_vmx(vm, &vmx_pages_gva);
vcpu_args_set(vm, VCPU_ID, 1, vmx_pages_gva);
} else {
printf("will skip SMM test with VMX enabled\n");
@@ -145,7 +144,7 @@ int main(int argc, char *argv[])
state = vcpu_save_state(vm, VCPU_ID);
kvm_vm_release(vm);
kvm_vm_restart(vm, O_RDWR);
- vm_vcpu_add(vm, VCPU_ID, 0, 0);
+ vm_vcpu_add(vm, VCPU_ID);
vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid());
vcpu_load_state(vm, VCPU_ID, state);
run = vcpu_state(vm, VCPU_ID);
diff --git a/tools/testing/selftests/kvm/x86_64/state_test.c b/tools/testing/selftests/kvm/x86_64/state_test.c
index e0a3c0204b7c..3ab5ec3da9f4 100644
--- a/tools/testing/selftests/kvm/x86_64/state_test.c
+++ b/tools/testing/selftests/kvm/x86_64/state_test.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* KVM_GET/SET_* tests
*
* Copyright (C) 2018, Red Hat, Inc.
*
- * This work is licensed under the terms of the GNU GPL, version 2.
- *
* Tests for vCPU state save/restore, including nested guest state.
*/
#define _GNU_SOURCE /* for program_invocation_short_name */
@@ -22,8 +21,6 @@
#define VCPU_ID 5
-static bool have_nested_state;
-
void l2_guest_code(void)
{
GUEST_SYNC(6);
@@ -122,7 +119,6 @@ void guest_code(struct vmx_pages *vmx_pages)
int main(int argc, char *argv[])
{
- struct vmx_pages *vmx_pages = NULL;
vm_vaddr_t vmx_pages_gva = 0;
struct kvm_regs regs1, regs2;
@@ -132,8 +128,6 @@ int main(int argc, char *argv[])
struct ucall uc;
int stage;
- struct kvm_cpuid_entry2 *entry = kvm_get_supported_cpuid_entry(1);
-
/* Create VM */
vm = vm_create_default(VCPU_ID, 0, guest_code);
vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid());
@@ -142,7 +136,7 @@ int main(int argc, char *argv[])
vcpu_regs_get(vm, VCPU_ID, &regs1);
if (kvm_check_cap(KVM_CAP_NESTED_STATE)) {
- vmx_pages = vcpu_alloc_vmx(vm, &vmx_pages_gva);
+ vcpu_alloc_vmx(vm, &vmx_pages_gva);
vcpu_args_set(vm, VCPU_ID, 1, vmx_pages_gva);
} else {
printf("will skip nested state checks\n");
@@ -182,7 +176,7 @@ int main(int argc, char *argv[])
/* Restore state in a new VM. */
kvm_vm_restart(vm, O_RDWR);
- vm_vcpu_add(vm, VCPU_ID, 0, 0);
+ vm_vcpu_add(vm, VCPU_ID);
vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid());
vcpu_load_state(vm, VCPU_ID, state);
run = vcpu_state(vm, VCPU_ID);
diff --git a/tools/testing/selftests/kvm/x86_64/sync_regs_test.c b/tools/testing/selftests/kvm/x86_64/sync_regs_test.c
index c8478ce9ea77..11c2a70a7b87 100644
--- a/tools/testing/selftests/kvm/x86_64/sync_regs_test.c
+++ b/tools/testing/selftests/kvm/x86_64/sync_regs_test.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Test for x86 KVM_CAP_SYNC_REGS
*
* Copyright (C) 2018, Google LLC.
*
- * This work is licensed under the terms of the GNU GPL, version 2.
- *
* Verifies expected behavior of x86 KVM_CAP_SYNC_REGS functionality,
* including requesting an invalid register set, updates to/from values
* in kvm_run.s.regs when kvm_valid_regs and kvm_dirty_regs are toggled.
@@ -25,9 +24,15 @@
void guest_code(void)
{
+ /*
+ * use a callee-save register, otherwise the compiler
+ * saves it around the call to GUEST_SYNC.
+ */
+ register u32 stage asm("rbx");
for (;;) {
GUEST_SYNC(0);
- asm volatile ("inc %r11");
+ stage++;
+ asm volatile ("" : : "r" (stage));
}
}
@@ -147,7 +152,7 @@ int main(int argc, char *argv[])
compare_vcpu_events(&events, &run->s.regs.events);
/* Set and verify various register values. */
- run->s.regs.regs.r11 = 0xBAD1DEA;
+ run->s.regs.regs.rbx = 0xBAD1DEA;
run->s.regs.sregs.apic_base = 1 << 11;
/* TODO run->s.regs.events.XYZ = ABC; */
@@ -158,9 +163,9 @@ int main(int argc, char *argv[])
"Unexpected exit reason: %u (%s),\n",
run->exit_reason,
exit_reason_str(run->exit_reason));
- TEST_ASSERT(run->s.regs.regs.r11 == 0xBAD1DEA + 1,
- "r11 sync regs value incorrect 0x%llx.",
- run->s.regs.regs.r11);
+ TEST_ASSERT(run->s.regs.regs.rbx == 0xBAD1DEA + 1,
+ "rbx sync regs value incorrect 0x%llx.",
+ run->s.regs.regs.rbx);
TEST_ASSERT(run->s.regs.sregs.apic_base == 1 << 11,
"apic_base sync regs value incorrect 0x%llx.",
run->s.regs.sregs.apic_base);
@@ -179,15 +184,15 @@ int main(int argc, char *argv[])
*/
run->kvm_valid_regs = TEST_SYNC_FIELDS;
run->kvm_dirty_regs = 0;
- run->s.regs.regs.r11 = 0xDEADBEEF;
+ run->s.regs.regs.rbx = 0xDEADBEEF;
rv = _vcpu_run(vm, VCPU_ID);
TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
"Unexpected exit reason: %u (%s),\n",
run->exit_reason,
exit_reason_str(run->exit_reason));
- TEST_ASSERT(run->s.regs.regs.r11 != 0xDEADBEEF,
- "r11 sync regs value incorrect 0x%llx.",
- run->s.regs.regs.r11);
+ TEST_ASSERT(run->s.regs.regs.rbx != 0xDEADBEEF,
+ "rbx sync regs value incorrect 0x%llx.",
+ run->s.regs.regs.rbx);
/* Clear kvm_valid_regs bits and kvm_dirty_bits.
* Verify s.regs values are not overwritten with existing guest values
@@ -195,21 +200,21 @@ int main(int argc, char *argv[])
*/
run->kvm_valid_regs = 0;
run->kvm_dirty_regs = 0;
- run->s.regs.regs.r11 = 0xAAAA;
- regs.r11 = 0xBAC0;
+ run->s.regs.regs.rbx = 0xAAAA;
+ regs.rbx = 0xBAC0;
vcpu_regs_set(vm, VCPU_ID, &regs);
rv = _vcpu_run(vm, VCPU_ID);
TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
"Unexpected exit reason: %u (%s),\n",
run->exit_reason,
exit_reason_str(run->exit_reason));
- TEST_ASSERT(run->s.regs.regs.r11 == 0xAAAA,
- "r11 sync regs value incorrect 0x%llx.",
- run->s.regs.regs.r11);
+ TEST_ASSERT(run->s.regs.regs.rbx == 0xAAAA,
+ "rbx sync regs value incorrect 0x%llx.",
+ run->s.regs.regs.rbx);
vcpu_regs_get(vm, VCPU_ID, &regs);
- TEST_ASSERT(regs.r11 == 0xBAC0 + 1,
- "r11 guest value incorrect 0x%llx.",
- regs.r11);
+ TEST_ASSERT(regs.rbx == 0xBAC0 + 1,
+ "rbx guest value incorrect 0x%llx.",
+ regs.rbx);
/* Clear kvm_valid_regs bits. Verify s.regs values are not overwritten
* with existing guest values but that guest values are overwritten
@@ -217,19 +222,19 @@ int main(int argc, char *argv[])
*/
run->kvm_valid_regs = 0;
run->kvm_dirty_regs = TEST_SYNC_FIELDS;
- run->s.regs.regs.r11 = 0xBBBB;
+ run->s.regs.regs.rbx = 0xBBBB;
rv = _vcpu_run(vm, VCPU_ID);
TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
"Unexpected exit reason: %u (%s),\n",
run->exit_reason,
exit_reason_str(run->exit_reason));
- TEST_ASSERT(run->s.regs.regs.r11 == 0xBBBB,
- "r11 sync regs value incorrect 0x%llx.",
- run->s.regs.regs.r11);
+ TEST_ASSERT(run->s.regs.regs.rbx == 0xBBBB,
+ "rbx sync regs value incorrect 0x%llx.",
+ run->s.regs.regs.rbx);
vcpu_regs_get(vm, VCPU_ID, &regs);
- TEST_ASSERT(regs.r11 == 0xBBBB + 1,
- "r11 guest value incorrect 0x%llx.",
- regs.r11);
+ TEST_ASSERT(regs.rbx == 0xBBBB + 1,
+ "rbx guest value incorrect 0x%llx.",
+ regs.rbx);
kvm_vm_free(vm);
diff --git a/tools/testing/selftests/kvm/x86_64/vmx_close_while_nested_test.c b/tools/testing/selftests/kvm/x86_64/vmx_close_while_nested_test.c
index 6edec6fd790b..3b0ffe01dacd 100644
--- a/tools/testing/selftests/kvm/x86_64/vmx_close_while_nested_test.c
+++ b/tools/testing/selftests/kvm/x86_64/vmx_close_while_nested_test.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vmx_close_while_nested
*
* Copyright (C) 2019, Red Hat, Inc.
*
- * This work is licensed under the terms of the GNU GPL, version 2.
- *
* Verify that nothing bad happens if a KVM user exits with open
* file descriptors while executing a nested guest.
*/
@@ -39,8 +38,6 @@ static void l1_guest_code(struct vmx_pages *vmx_pages)
{
#define L2_GUEST_STACK_SIZE 64
unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];
- uint32_t control;
- uintptr_t save_cr3;
GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages));
GUEST_ASSERT(load_vmcs(vmx_pages));
@@ -55,7 +52,6 @@ static void l1_guest_code(struct vmx_pages *vmx_pages)
int main(int argc, char *argv[])
{
- struct vmx_pages *vmx_pages;
vm_vaddr_t vmx_pages_gva;
struct kvm_cpuid_entry2 *entry = kvm_get_supported_cpuid_entry(1);
@@ -68,7 +64,7 @@ int main(int argc, char *argv[])
vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid());
/* Allocate VMX pages and shared descriptors (vmx_pages). */
- vmx_pages = vcpu_alloc_vmx(vm, &vmx_pages_gva);
+ vcpu_alloc_vmx(vm, &vmx_pages_gva);
vcpu_args_set(vm, VCPU_ID, 1, vmx_pages_gva);
for (;;) {
diff --git a/tools/testing/selftests/kvm/x86_64/vmx_set_nested_state_test.c b/tools/testing/selftests/kvm/x86_64/vmx_set_nested_state_test.c
index 61a2163cf9f1..853e370e8a39 100644
--- a/tools/testing/selftests/kvm/x86_64/vmx_set_nested_state_test.c
+++ b/tools/testing/selftests/kvm/x86_64/vmx_set_nested_state_test.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vmx_set_nested_state_test
*
* Copyright (C) 2019, Google LLC.
*
- * This work is licensed under the terms of the GNU GPL, version 2.
- *
* This test verifies the integrity of calling the ioctl KVM_SET_NESTED_STATE.
*/
@@ -26,24 +25,17 @@
#define VMCS12_REVISION 0x11e57ed0
#define VCPU_ID 5
+bool have_evmcs;
+
void test_nested_state(struct kvm_vm *vm, struct kvm_nested_state *state)
{
- volatile struct kvm_run *run;
-
vcpu_nested_state_set(vm, VCPU_ID, state, false);
- run = vcpu_state(vm, VCPU_ID);
- vcpu_run(vm, VCPU_ID);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_SHUTDOWN,
- "Got exit_reason other than KVM_EXIT_SHUTDOWN: %u (%s),\n",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
}
void test_nested_state_expect_errno(struct kvm_vm *vm,
struct kvm_nested_state *state,
int expected_errno)
{
- volatile struct kvm_run *run;
int rv;
rv = vcpu_nested_state_set(vm, VCPU_ID, state, true);
@@ -51,12 +43,6 @@ void test_nested_state_expect_errno(struct kvm_vm *vm,
"Expected %s (%d) from vcpu_nested_state_set but got rv: %i errno: %s (%d)",
strerror(expected_errno), expected_errno, rv, strerror(errno),
errno);
- run = vcpu_state(vm, VCPU_ID);
- vcpu_run(vm, VCPU_ID);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_SHUTDOWN,
- "Got exit_reason other than KVM_EXIT_SHUTDOWN: %u (%s),\n",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
}
void test_nested_state_expect_einval(struct kvm_vm *vm,
@@ -75,7 +61,7 @@ void set_revision_id_for_vmcs12(struct kvm_nested_state *state,
u32 vmcs12_revision)
{
/* Set revision_id in vmcs12 to vmcs12_revision. */
- *(u32 *)(state->data) = vmcs12_revision;
+ memcpy(&state->data, &vmcs12_revision, sizeof(u32));
}
void set_default_state(struct kvm_nested_state *state)
@@ -91,13 +77,14 @@ void set_default_vmx_state(struct kvm_nested_state *state, int size)
{
memset(state, 0, size);
state->flags = KVM_STATE_NESTED_GUEST_MODE |
- KVM_STATE_NESTED_RUN_PENDING |
- KVM_STATE_NESTED_EVMCS;
+ KVM_STATE_NESTED_RUN_PENDING;
+ if (have_evmcs)
+ state->flags |= KVM_STATE_NESTED_EVMCS;
state->format = 0;
state->size = size;
- state->vmx.vmxon_pa = 0x1000;
- state->vmx.vmcs_pa = 0x2000;
- state->vmx.smm.flags = 0;
+ state->hdr.vmx.vmxon_pa = 0x1000;
+ state->hdr.vmx.vmcs12_pa = 0x2000;
+ state->hdr.vmx.smm.flags = 0;
set_revision_id_for_vmcs12(state, VMCS12_REVISION);
}
@@ -123,39 +110,53 @@ void test_vmx_nested_state(struct kvm_vm *vm)
/*
* We cannot virtualize anything if the guest does not have VMX
* enabled. We expect KVM_SET_NESTED_STATE to return 0 if vmxon_pa
- * is set to -1ull.
+ * is set to -1ull, but the flags must be zero.
*/
set_default_vmx_state(state, state_sz);
- state->vmx.vmxon_pa = -1ull;
+ state->hdr.vmx.vmxon_pa = -1ull;
+ test_nested_state_expect_einval(vm, state);
+
+ state->hdr.vmx.vmcs12_pa = -1ull;
+ state->flags = KVM_STATE_NESTED_EVMCS;
+ test_nested_state_expect_einval(vm, state);
+
+ state->flags = 0;
test_nested_state(vm, state);
/* Enable VMX in the guest CPUID. */
vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid());
- /* It is invalid to have vmxon_pa == -1ull and SMM flags non-zero. */
+ /*
+ * Setting vmxon_pa == -1ull and vmcs_pa == -1ull exits early without
+ * setting the nested state but flags other than eVMCS must be clear.
+ * The eVMCS flag can be set if the enlightened VMCS capability has
+ * been enabled.
+ */
set_default_vmx_state(state, state_sz);
- state->vmx.vmxon_pa = -1ull;
- state->vmx.smm.flags = 1;
+ state->hdr.vmx.vmxon_pa = -1ull;
+ state->hdr.vmx.vmcs12_pa = -1ull;
test_nested_state_expect_einval(vm, state);
- /* It is invalid to have vmxon_pa == -1ull and vmcs_pa != -1ull. */
- set_default_vmx_state(state, state_sz);
- state->vmx.vmxon_pa = -1ull;
- state->vmx.vmcs_pa = 0;
+ state->flags &= KVM_STATE_NESTED_EVMCS;
+ if (have_evmcs) {
+ test_nested_state_expect_einval(vm, state);
+ vcpu_enable_evmcs(vm, VCPU_ID);
+ }
+ test_nested_state(vm, state);
+
+ /* It is invalid to have vmxon_pa == -1ull and SMM flags non-zero. */
+ state->hdr.vmx.smm.flags = 1;
test_nested_state_expect_einval(vm, state);
- /*
- * Setting vmxon_pa == -1ull and vmcs_pa == -1ull exits early without
- * setting the nested state.
- */
+ /* It is invalid to have vmxon_pa == -1ull and vmcs_pa != -1ull. */
set_default_vmx_state(state, state_sz);
- state->vmx.vmxon_pa = -1ull;
- state->vmx.vmcs_pa = -1ull;
- test_nested_state(vm, state);
+ state->hdr.vmx.vmxon_pa = -1ull;
+ state->flags = 0;
+ test_nested_state_expect_einval(vm, state);
/* It is invalid to have vmxon_pa set to a non-page aligned address. */
set_default_vmx_state(state, state_sz);
- state->vmx.vmxon_pa = 1;
+ state->hdr.vmx.vmxon_pa = 1;
test_nested_state_expect_einval(vm, state);
/*
@@ -165,7 +166,7 @@ void test_vmx_nested_state(struct kvm_vm *vm)
set_default_vmx_state(state, state_sz);
state->flags = KVM_STATE_NESTED_GUEST_MODE |
KVM_STATE_NESTED_RUN_PENDING;
- state->vmx.smm.flags = KVM_STATE_NESTED_SMM_GUEST_MODE;
+ state->hdr.vmx.smm.flags = KVM_STATE_NESTED_SMM_GUEST_MODE;
test_nested_state_expect_einval(vm, state);
/*
@@ -174,14 +175,14 @@ void test_vmx_nested_state(struct kvm_vm *vm)
* KVM_STATE_NESTED_SMM_VMXON
*/
set_default_vmx_state(state, state_sz);
- state->vmx.smm.flags = ~(KVM_STATE_NESTED_SMM_GUEST_MODE |
+ state->hdr.vmx.smm.flags = ~(KVM_STATE_NESTED_SMM_GUEST_MODE |
KVM_STATE_NESTED_SMM_VMXON);
test_nested_state_expect_einval(vm, state);
/* Outside SMM, SMM flags must be zero. */
set_default_vmx_state(state, state_sz);
state->flags = 0;
- state->vmx.smm.flags = KVM_STATE_NESTED_SMM_GUEST_MODE;
+ state->hdr.vmx.smm.flags = KVM_STATE_NESTED_SMM_GUEST_MODE;
test_nested_state_expect_einval(vm, state);
/* Size must be large enough to fit kvm_nested_state and vmcs12. */
@@ -191,8 +192,8 @@ void test_vmx_nested_state(struct kvm_vm *vm)
/* vmxon_pa cannot be the same address as vmcs_pa. */
set_default_vmx_state(state, state_sz);
- state->vmx.vmxon_pa = 0;
- state->vmx.vmcs_pa = 0;
+ state->hdr.vmx.vmxon_pa = 0;
+ state->hdr.vmx.vmcs12_pa = 0;
test_nested_state_expect_einval(vm, state);
/* The revision id for vmcs12 must be VMCS12_REVISION. */
@@ -205,16 +206,16 @@ void test_vmx_nested_state(struct kvm_vm *vm)
* it again.
*/
set_default_vmx_state(state, state_sz);
- state->vmx.vmxon_pa = -1ull;
- state->vmx.vmcs_pa = -1ull;
+ state->hdr.vmx.vmxon_pa = -1ull;
+ state->hdr.vmx.vmcs12_pa = -1ull;
state->flags = 0;
test_nested_state(vm, state);
vcpu_nested_state_get(vm, VCPU_ID, state);
TEST_ASSERT(state->size >= sizeof(*state) && state->size <= state_sz,
"Size must be between %d and %d. The size returned was %d.",
sizeof(*state), state_sz, state->size);
- TEST_ASSERT(state->vmx.vmxon_pa == -1ull, "vmxon_pa must be -1ull.");
- TEST_ASSERT(state->vmx.vmcs_pa == -1ull, "vmcs_pa must be -1ull.");
+ TEST_ASSERT(state->hdr.vmx.vmxon_pa == -1ull, "vmxon_pa must be -1ull.");
+ TEST_ASSERT(state->hdr.vmx.vmcs12_pa == -1ull, "vmcs_pa must be -1ull.");
free(state);
}
@@ -225,6 +226,8 @@ int main(int argc, char *argv[])
struct kvm_nested_state state;
struct kvm_cpuid_entry2 *entry = kvm_get_supported_cpuid_entry(1);
+ have_evmcs = kvm_check_cap(KVM_CAP_HYPERV_ENLIGHTENED_VMCS);
+
if (!kvm_check_cap(KVM_CAP_NESTED_STATE)) {
printf("KVM_CAP_NESTED_STATE not available, skipping test\n");
exit(KSFT_SKIP);
diff --git a/tools/testing/selftests/kvm/x86_64/vmx_tsc_adjust_test.c b/tools/testing/selftests/kvm/x86_64/vmx_tsc_adjust_test.c
index 18fa64db0d7a..f36c10eba71e 100644
--- a/tools/testing/selftests/kvm/x86_64/vmx_tsc_adjust_test.c
+++ b/tools/testing/selftests/kvm/x86_64/vmx_tsc_adjust_test.c
@@ -1,11 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vmx_tsc_adjust_test
*
* Copyright (C) 2018, Google LLC.
*
- * This work is licensed under the terms of the GNU GPL, version 2.
- *
- *
* IA32_TSC_ADJUST test
*
* According to the SDM, "if an execution of WRMSR to the
@@ -121,7 +119,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages)
GUEST_DONE();
}
-void report(int64_t val)
+static void report(int64_t val)
{
printf("IA32_TSC_ADJUST is %ld (%lld * TSC_ADJUST_VALUE + %lld).\n",
val, val / TSC_ADJUST_VALUE, val % TSC_ADJUST_VALUE);
@@ -129,7 +127,6 @@ void report(int64_t val)
int main(int argc, char *argv[])
{
- struct vmx_pages *vmx_pages;
vm_vaddr_t vmx_pages_gva;
struct kvm_cpuid_entry2 *entry = kvm_get_supported_cpuid_entry(1);
@@ -142,7 +139,7 @@ int main(int argc, char *argv[])
vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid());
/* Allocate VMX pages and shared descriptors (vmx_pages). */
- vmx_pages = vcpu_alloc_vmx(vm, &vmx_pages_gva);
+ vcpu_alloc_vmx(vm, &vmx_pages_gva);
vcpu_args_set(vm, VCPU_ID, 1, vmx_pages_gva);
for (;;) {
diff --git a/tools/testing/selftests/lib.mk b/tools/testing/selftests/lib.mk
index 077337195783..1c8a1963d03f 100644
--- a/tools/testing/selftests/lib.mk
+++ b/tools/testing/selftests/lib.mk
@@ -70,7 +70,7 @@ define RUN_TESTS
endef
run_tests: all
-ifneq ($(KBUILD_SRC),)
+ifdef building_out_of_srctree
@if [ "X$(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES)" != "X" ]; then
@rsync -aq $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES) $(OUTPUT)
fi
@@ -125,7 +125,7 @@ clean:
# When make O= with kselftest target from main level
# the following aren't defined.
#
-ifneq ($(KBUILD_SRC),)
+ifdef building_out_of_srctree
LINK.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
COMPILE.S = $(CC) $(ASFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
LINK.S = $(CC) $(ASFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
diff --git a/tools/testing/selftests/lib/Makefile b/tools/testing/selftests/lib/Makefile
index 9f26635f3e57..a105f094676e 100644
--- a/tools/testing/selftests/lib/Makefile
+++ b/tools/testing/selftests/lib/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# Makefile for lib/ function selftests
# No binaries, but make sure arg-less "make" doesn't trigger "run_tests"
diff --git a/tools/testing/selftests/livepatch/functions.sh b/tools/testing/selftests/livepatch/functions.sh
index 30195449c63c..79b0affd21fb 100644
--- a/tools/testing/selftests/livepatch/functions.sh
+++ b/tools/testing/selftests/livepatch/functions.sh
@@ -13,6 +13,14 @@ function log() {
echo "$1" > /dev/kmsg
}
+# skip(msg) - testing can't proceed
+# msg - explanation
+function skip() {
+ log "SKIP: $1"
+ echo "SKIP: $1" >&2
+ exit 4
+}
+
# die(msg) - game over, man
# msg - dying words
function die() {
@@ -21,13 +29,27 @@ function die() {
exit 1
}
-# set_dynamic_debug() - setup kernel dynamic debug
-# TODO - push and pop this config?
+function push_dynamic_debug() {
+ DYNAMIC_DEBUG=$(grep '^kernel/livepatch' /sys/kernel/debug/dynamic_debug/control | \
+ awk -F'[: ]' '{print "file " $1 " line " $2 " " $4}')
+}
+
+function pop_dynamic_debug() {
+ if [[ -n "$DYNAMIC_DEBUG" ]]; then
+ echo -n "$DYNAMIC_DEBUG" > /sys/kernel/debug/dynamic_debug/control
+ fi
+}
+
+# set_dynamic_debug() - save the current dynamic debug config and tweak
+# it for the self-tests. Set a script exit trap
+# that restores the original config.
function set_dynamic_debug() {
- cat << EOF > /sys/kernel/debug/dynamic_debug/control
-file kernel/livepatch/* +p
-func klp_try_switch_task -p
-EOF
+ push_dynamic_debug
+ trap pop_dynamic_debug EXIT INT TERM HUP
+ cat <<-EOF > /sys/kernel/debug/dynamic_debug/control
+ file kernel/livepatch/* +p
+ func klp_try_switch_task -p
+ EOF
}
# loop_until(cmd) - loop a command until it is successful or $MAX_RETRIES,
@@ -43,6 +65,12 @@ function loop_until() {
done
}
+function assert_mod() {
+ local mod="$1"
+
+ modprobe --dry-run "$mod" &>/dev/null
+}
+
function is_livepatch_mod() {
local mod="$1"
@@ -75,6 +103,9 @@ function __load_mod() {
function load_mod() {
local mod="$1"; shift
+ assert_mod "$mod" ||
+ skip "unable to load module ${mod}, verify CONFIG_TEST_LIVEPATCH=m and run self-tests as root"
+
is_livepatch_mod "$mod" &&
die "use load_lp() to load the livepatch module $mod"
@@ -88,6 +119,9 @@ function load_mod() {
function load_lp_nowait() {
local mod="$1"; shift
+ assert_mod "$mod" ||
+ skip "unable to load module ${mod}, verify CONFIG_TEST_LIVEPATCH=m and run self-tests as root"
+
is_livepatch_mod "$mod" ||
die "module $mod is not a livepatch"
diff --git a/tools/testing/selftests/membarrier/Makefile b/tools/testing/selftests/membarrier/Makefile
index 02845532b059..97e3bdf3d1e9 100644
--- a/tools/testing/selftests/membarrier/Makefile
+++ b/tools/testing/selftests/membarrier/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
CFLAGS += -g -I../../../../usr/include/
TEST_GEN_PROGS := membarrier_test
diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore
index 6f81130605d7..c7cced739c34 100644
--- a/tools/testing/selftests/net/.gitignore
+++ b/tools/testing/selftests/net/.gitignore
@@ -17,3 +17,7 @@ tcp_inq
tls
txring_overwrite
ip_defrag
+ipv6_flowlabel
+ipv6_flowlabel_mgr
+so_txtime
+tcp_fastopen_backup_key
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 1e6d14d2825c..0bd6b23c97ef 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -5,16 +5,19 @@ CFLAGS = -Wall -Wl,--no-as-needed -O2 -g
CFLAGS += -I../../../../usr/include/
TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh \
- rtnetlink.sh xfrm_policy.sh
+ rtnetlink.sh xfrm_policy.sh test_blackhole_dev.sh
TEST_PROGS += fib_tests.sh fib-onlink-tests.sh pmtu.sh udpgso.sh ip_defrag.sh
TEST_PROGS += udpgso_bench.sh fib_rule_tests.sh msg_zerocopy.sh psock_snd.sh
TEST_PROGS += udpgro_bench.sh udpgro.sh test_vxlan_under_vrf.sh reuseport_addr_any.sh
-TEST_PROGS += test_vxlan_fdb_changelink.sh
+TEST_PROGS += test_vxlan_fdb_changelink.sh so_txtime.sh ipv6_flowlabel.sh
+TEST_PROGS += tcp_fastopen_backup_key.sh fcnal-test.sh l2tp.sh
TEST_PROGS_EXTENDED := in_netns.sh
-TEST_GEN_FILES = socket
+TEST_GEN_FILES = socket nettest
TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy reuseport_addr_any
TEST_GEN_FILES += tcp_mmap tcp_inq psock_snd txring_overwrite
TEST_GEN_FILES += udpgso udpgso_bench_tx udpgso_bench_rx ip_defrag
+TEST_GEN_FILES += so_txtime ipv6_flowlabel ipv6_flowlabel_mgr
+TEST_GEN_FILES += tcp_fastopen_backup_key
TEST_GEN_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa
TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls
diff --git a/tools/testing/selftests/net/config b/tools/testing/selftests/net/config
index 474040448601..b8503a8119b0 100644
--- a/tools/testing/selftests/net/config
+++ b/tools/testing/selftests/net/config
@@ -25,3 +25,7 @@ CONFIG_NF_TABLES_IPV6=y
CONFIG_NF_TABLES_IPV4=y
CONFIG_NFT_CHAIN_NAT_IPV6=m
CONFIG_NFT_CHAIN_NAT_IPV4=m
+CONFIG_NET_SCH_FQ=m
+CONFIG_NET_SCH_ETF=m
+CONFIG_TEST_BLACKHOLE_DEV=m
+CONFIG_KALLSYMS=y
diff --git a/tools/testing/selftests/net/fcnal-test.sh b/tools/testing/selftests/net/fcnal-test.sh
new file mode 100755
index 000000000000..9fd3a0b97f0d
--- /dev/null
+++ b/tools/testing/selftests/net/fcnal-test.sh
@@ -0,0 +1,3432 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2019 David Ahern <dsahern@gmail.com>. All rights reserved.
+#
+# IPv4 and IPv6 functional tests focusing on VRF and routing lookups
+# for various permutations:
+# 1. icmp, tcp, udp and netfilter
+# 2. client, server, no-server
+# 3. global address on interface
+# 4. global address on 'lo'
+# 5. remote and local traffic
+# 6. VRF and non-VRF permutations
+#
+# Setup:
+# ns-A | ns-B
+# No VRF case:
+# [ lo ] [ eth1 ]---|---[ eth1 ] [ lo ]
+# remote address
+# VRF case:
+# [ red ]---[ eth1 ]---|---[ eth1 ] [ lo ]
+#
+# ns-A:
+# eth1: 172.16.1.1/24, 2001:db8:1::1/64
+# lo: 127.0.0.1/8, ::1/128
+# 172.16.2.1/32, 2001:db8:2::1/128
+# red: 127.0.0.1/8, ::1/128
+# 172.16.3.1/32, 2001:db8:3::1/128
+#
+# ns-B:
+# eth1: 172.16.1.2/24, 2001:db8:1::2/64
+# lo2: 127.0.0.1/8, ::1/128
+# 172.16.2.2/32, 2001:db8:2::2/128
+#
+# server / client nomenclature relative to ns-A
+
+VERBOSE=0
+
+NSA_DEV=eth1
+NSB_DEV=eth1
+VRF=red
+VRF_TABLE=1101
+
+# IPv4 config
+NSA_IP=172.16.1.1
+NSB_IP=172.16.1.2
+VRF_IP=172.16.3.1
+
+# IPv6 config
+NSA_IP6=2001:db8:1::1
+NSB_IP6=2001:db8:1::2
+VRF_IP6=2001:db8:3::1
+
+NSA_LO_IP=172.16.2.1
+NSB_LO_IP=172.16.2.2
+NSA_LO_IP6=2001:db8:2::1
+NSB_LO_IP6=2001:db8:2::2
+
+MCAST=ff02::1
+# set after namespace create
+NSA_LINKIP6=
+NSB_LINKIP6=
+
+NSA=ns-A
+NSB=ns-B
+
+NSA_CMD="ip netns exec ${NSA}"
+NSB_CMD="ip netns exec ${NSB}"
+
+which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping)
+
+################################################################################
+# utilities
+
+log_test()
+{
+ local rc=$1
+ local expected=$2
+ local msg="$3"
+
+ [ "${VERBOSE}" = "1" ] && echo
+
+ if [ ${rc} -eq ${expected} ]; then
+ nsuccess=$((nsuccess+1))
+ printf "TEST: %-70s [ OK ]\n" "${msg}"
+ else
+ nfail=$((nfail+1))
+ printf "TEST: %-70s [FAIL]\n" "${msg}"
+ if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
+ echo
+ echo "hit enter to continue, 'q' to quit"
+ read a
+ [ "$a" = "q" ] && exit 1
+ fi
+ fi
+
+ if [ "${PAUSE}" = "yes" ]; then
+ echo
+ echo "hit enter to continue, 'q' to quit"
+ read a
+ [ "$a" = "q" ] && exit 1
+ fi
+
+ kill_procs
+}
+
+log_test_addr()
+{
+ local addr=$1
+ local rc=$2
+ local expected=$3
+ local msg="$4"
+ local astr
+
+ astr=$(addr2str ${addr})
+ log_test $rc $expected "$msg - ${astr}"
+}
+
+log_section()
+{
+ echo
+ echo "###########################################################################"
+ echo "$*"
+ echo "###########################################################################"
+ echo
+}
+
+log_subsection()
+{
+ echo
+ echo "#################################################################"
+ echo "$*"
+ echo
+}
+
+log_start()
+{
+ # make sure we have no test instances running
+ kill_procs
+
+ if [ "${VERBOSE}" = "1" ]; then
+ echo
+ echo "#######################################################"
+ fi
+}
+
+log_debug()
+{
+ if [ "${VERBOSE}" = "1" ]; then
+ echo
+ echo "$*"
+ echo
+ fi
+}
+
+show_hint()
+{
+ if [ "${VERBOSE}" = "1" ]; then
+ echo "HINT: $*"
+ echo
+ fi
+}
+
+kill_procs()
+{
+ killall nettest ping ping6 >/dev/null 2>&1
+ sleep 1
+}
+
+do_run_cmd()
+{
+ local cmd="$*"
+ local out
+
+ if [ "$VERBOSE" = "1" ]; then
+ echo "COMMAND: ${cmd}"
+ fi
+
+ out=$($cmd 2>&1)
+ rc=$?
+ if [ "$VERBOSE" = "1" -a -n "$out" ]; then
+ echo "$out"
+ fi
+
+ return $rc
+}
+
+run_cmd()
+{
+ do_run_cmd ${NSA_CMD} $*
+}
+
+run_cmd_nsb()
+{
+ do_run_cmd ${NSB_CMD} $*
+}
+
+setup_cmd()
+{
+ local cmd="$*"
+ local rc
+
+ run_cmd ${cmd}
+ rc=$?
+ if [ $rc -ne 0 ]; then
+ # show user the command if not done so already
+ if [ "$VERBOSE" = "0" ]; then
+ echo "setup command: $cmd"
+ fi
+ echo "failed. stopping tests"
+ if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
+ echo
+ echo "hit enter to continue"
+ read a
+ fi
+ exit $rc
+ fi
+}
+
+setup_cmd_nsb()
+{
+ local cmd="$*"
+ local rc
+
+ run_cmd_nsb ${cmd}
+ rc=$?
+ if [ $rc -ne 0 ]; then
+ # show user the command if not done so already
+ if [ "$VERBOSE" = "0" ]; then
+ echo "setup command: $cmd"
+ fi
+ echo "failed. stopping tests"
+ if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
+ echo
+ echo "hit enter to continue"
+ read a
+ fi
+ exit $rc
+ fi
+}
+
+# set sysctl values in NS-A
+set_sysctl()
+{
+ echo "SYSCTL: $*"
+ echo
+ run_cmd sysctl -q -w $*
+}
+
+################################################################################
+# Setup for tests
+
+addr2str()
+{
+ case "$1" in
+ 127.0.0.1) echo "loopback";;
+ ::1) echo "IPv6 loopback";;
+
+ ${NSA_IP}) echo "ns-A IP";;
+ ${NSA_IP6}) echo "ns-A IPv6";;
+ ${NSA_LO_IP}) echo "ns-A loopback IP";;
+ ${NSA_LO_IP6}) echo "ns-A loopback IPv6";;
+ ${NSA_LINKIP6}|${NSA_LINKIP6}%*) echo "ns-A IPv6 LLA";;
+
+ ${NSB_IP}) echo "ns-B IP";;
+ ${NSB_IP6}) echo "ns-B IPv6";;
+ ${NSB_LO_IP}) echo "ns-B loopback IP";;
+ ${NSB_LO_IP6}) echo "ns-B loopback IPv6";;
+ ${NSB_LINKIP6}|${NSB_LINKIP6}%*) echo "ns-B IPv6 LLA";;
+
+ ${VRF_IP}) echo "VRF IP";;
+ ${VRF_IP6}) echo "VRF IPv6";;
+
+ ${MCAST}%*) echo "multicast IP";;
+
+ *) echo "unknown";;
+ esac
+}
+
+get_linklocal()
+{
+ local ns=$1
+ local dev=$2
+ local addr
+
+ addr=$(ip -netns ${ns} -6 -br addr show dev ${dev} | \
+ awk '{
+ for (i = 3; i <= NF; ++i) {
+ if ($i ~ /^fe80/)
+ print $i
+ }
+ }'
+ )
+ addr=${addr/\/*}
+
+ [ -z "$addr" ] && return 1
+
+ echo $addr
+
+ return 0
+}
+
+################################################################################
+# create namespaces and vrf
+
+create_vrf()
+{
+ local ns=$1
+ local vrf=$2
+ local table=$3
+ local addr=$4
+ local addr6=$5
+
+ ip -netns ${ns} link add ${vrf} type vrf table ${table}
+ ip -netns ${ns} link set ${vrf} up
+ ip -netns ${ns} route add vrf ${vrf} unreachable default metric 8192
+ ip -netns ${ns} -6 route add vrf ${vrf} unreachable default metric 8192
+
+ ip -netns ${ns} addr add 127.0.0.1/8 dev ${vrf}
+ ip -netns ${ns} -6 addr add ::1 dev ${vrf} nodad
+ if [ "${addr}" != "-" ]; then
+ ip -netns ${ns} addr add dev ${vrf} ${addr}
+ fi
+ if [ "${addr6}" != "-" ]; then
+ ip -netns ${ns} -6 addr add dev ${vrf} ${addr6}
+ fi
+
+ ip -netns ${ns} ru del pref 0
+ ip -netns ${ns} ru add pref 32765 from all lookup local
+ ip -netns ${ns} -6 ru del pref 0
+ ip -netns ${ns} -6 ru add pref 32765 from all lookup local
+}
+
+create_ns()
+{
+ local ns=$1
+ local addr=$2
+ local addr6=$3
+
+ ip netns add ${ns}
+
+ ip -netns ${ns} link set lo up
+ if [ "${addr}" != "-" ]; then
+ ip -netns ${ns} addr add dev lo ${addr}
+ fi
+ if [ "${addr6}" != "-" ]; then
+ ip -netns ${ns} -6 addr add dev lo ${addr6}
+ fi
+
+ ip -netns ${ns} ro add unreachable default metric 8192
+ ip -netns ${ns} -6 ro add unreachable default metric 8192
+
+ ip netns exec ${ns} sysctl -qw net.ipv4.ip_forward=1
+ ip netns exec ${ns} sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1
+ ip netns exec ${ns} sysctl -qw net.ipv6.conf.all.forwarding=1
+ ip netns exec ${ns} sysctl -qw net.ipv6.conf.default.forwarding=1
+}
+
+# create veth pair to connect namespaces and apply addresses.
+connect_ns()
+{
+ local ns1=$1
+ local ns1_dev=$2
+ local ns1_addr=$3
+ local ns1_addr6=$4
+ local ns2=$5
+ local ns2_dev=$6
+ local ns2_addr=$7
+ local ns2_addr6=$8
+
+ ip -netns ${ns1} li add ${ns1_dev} type veth peer name tmp
+ ip -netns ${ns1} li set ${ns1_dev} up
+ ip -netns ${ns1} li set tmp netns ${ns2} name ${ns2_dev}
+ ip -netns ${ns2} li set ${ns2_dev} up
+
+ if [ "${ns1_addr}" != "-" ]; then
+ ip -netns ${ns1} addr add dev ${ns1_dev} ${ns1_addr}
+ ip -netns ${ns2} addr add dev ${ns2_dev} ${ns2_addr}
+ fi
+
+ if [ "${ns1_addr6}" != "-" ]; then
+ ip -netns ${ns1} addr add dev ${ns1_dev} ${ns1_addr6}
+ ip -netns ${ns2} addr add dev ${ns2_dev} ${ns2_addr6}
+ fi
+}
+
+cleanup()
+{
+ # explicit cleanups to check those code paths
+ ip netns | grep -q ${NSA}
+ if [ $? -eq 0 ]; then
+ ip -netns ${NSA} link delete ${VRF}
+ ip -netns ${NSA} ro flush table ${VRF_TABLE}
+
+ ip -netns ${NSA} addr flush dev ${NSA_DEV}
+ ip -netns ${NSA} -6 addr flush dev ${NSA_DEV}
+ ip -netns ${NSA} link set dev ${NSA_DEV} down
+ ip -netns ${NSA} link del dev ${NSA_DEV}
+
+ ip netns del ${NSA}
+ fi
+
+ ip netns del ${NSB}
+}
+
+setup()
+{
+ local with_vrf=${1}
+
+ # make sure we are starting with a clean slate
+ kill_procs
+ cleanup 2>/dev/null
+
+ log_debug "Configuring network namespaces"
+ set -e
+
+ create_ns ${NSA} ${NSA_LO_IP}/32 ${NSA_LO_IP6}/128
+ create_ns ${NSB} ${NSB_LO_IP}/32 ${NSB_LO_IP6}/128
+ connect_ns ${NSA} ${NSA_DEV} ${NSA_IP}/24 ${NSA_IP6}/64 \
+ ${NSB} ${NSB_DEV} ${NSB_IP}/24 ${NSB_IP6}/64
+
+ NSA_LINKIP6=$(get_linklocal ${NSA} ${NSA_DEV})
+ NSB_LINKIP6=$(get_linklocal ${NSB} ${NSB_DEV})
+
+ # tell ns-A how to get to remote addresses of ns-B
+ if [ "${with_vrf}" = "yes" ]; then
+ create_vrf ${NSA} ${VRF} ${VRF_TABLE} ${VRF_IP} ${VRF_IP6}
+
+ ip -netns ${NSA} link set dev ${NSA_DEV} vrf ${VRF}
+ ip -netns ${NSA} ro add vrf ${VRF} ${NSB_LO_IP}/32 via ${NSB_IP} dev ${NSA_DEV}
+ ip -netns ${NSA} -6 ro add vrf ${VRF} ${NSB_LO_IP6}/128 via ${NSB_IP6} dev ${NSA_DEV}
+
+ ip -netns ${NSB} ro add ${VRF_IP}/32 via ${NSA_IP} dev ${NSB_DEV}
+ ip -netns ${NSB} -6 ro add ${VRF_IP6}/128 via ${NSA_IP6} dev ${NSB_DEV}
+ else
+ ip -netns ${NSA} ro add ${NSB_LO_IP}/32 via ${NSB_IP} dev ${NSA_DEV}
+ ip -netns ${NSA} ro add ${NSB_LO_IP6}/128 via ${NSB_IP6} dev ${NSA_DEV}
+ fi
+
+
+ # tell ns-B how to get to remote addresses of ns-A
+ ip -netns ${NSB} ro add ${NSA_LO_IP}/32 via ${NSA_IP} dev ${NSB_DEV}
+ ip -netns ${NSB} ro add ${NSA_LO_IP6}/128 via ${NSA_IP6} dev ${NSB_DEV}
+
+ set +e
+
+ sleep 1
+}
+
+################################################################################
+# IPv4
+
+ipv4_ping_novrf()
+{
+ local a
+
+ #
+ # out
+ #
+ for a in ${NSB_IP} ${NSB_LO_IP}
+ do
+ log_start
+ run_cmd ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 0 "ping out"
+
+ log_start
+ run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 0 "ping out, device bind"
+
+ log_start
+ run_cmd ping -c1 -w1 -I ${NSA_LO_IP} ${a}
+ log_test_addr ${a} $? 0 "ping out, address bind"
+ done
+
+ #
+ # in
+ #
+ for a in ${NSA_IP} ${NSA_LO_IP}
+ do
+ log_start
+ run_cmd_nsb ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 0 "ping in"
+ done
+
+ #
+ # local traffic
+ #
+ for a in ${NSA_IP} ${NSA_LO_IP} 127.0.0.1
+ do
+ log_start
+ run_cmd ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 0 "ping local"
+ done
+
+ #
+ # local traffic, socket bound to device
+ #
+ # address on device
+ a=${NSA_IP}
+ log_start
+ run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 0 "ping local, device bind"
+
+ # loopback addresses not reachable from device bind
+ # fails in a really weird way though because ipv4 special cases
+ # route lookups with oif set.
+ for a in ${NSA_LO_IP} 127.0.0.1
+ do
+ log_start
+ show_hint "Fails since address on loopback device is out of device scope"
+ run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 1 "ping local, device bind"
+ done
+
+ #
+ # ip rule blocks reachability to remote address
+ #
+ log_start
+ setup_cmd ip rule add pref 32765 from all lookup local
+ setup_cmd ip rule del pref 0 from all lookup local
+ setup_cmd ip rule add pref 50 to ${NSB_LO_IP} prohibit
+ setup_cmd ip rule add pref 51 from ${NSB_IP} prohibit
+
+ a=${NSB_LO_IP}
+ run_cmd ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping out, blocked by rule"
+
+ # NOTE: ipv4 actually allows the lookup to fail and yet still create
+ # a viable rtable if the oif (e.g., bind to device) is set, so this
+ # case succeeds despite the rule
+ # run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+
+ a=${NSA_LO_IP}
+ log_start
+ show_hint "Response generates ICMP (or arp request is ignored) due to ip rule"
+ run_cmd_nsb ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 1 "ping in, blocked by rule"
+
+ [ "$VERBOSE" = "1" ] && echo
+ setup_cmd ip rule del pref 32765 from all lookup local
+ setup_cmd ip rule add pref 0 from all lookup local
+ setup_cmd ip rule del pref 50 to ${NSB_LO_IP} prohibit
+ setup_cmd ip rule del pref 51 from ${NSB_IP} prohibit
+
+ #
+ # route blocks reachability to remote address
+ #
+ log_start
+ setup_cmd ip route replace unreachable ${NSB_LO_IP}
+ setup_cmd ip route replace unreachable ${NSB_IP}
+
+ a=${NSB_LO_IP}
+ run_cmd ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping out, blocked by route"
+
+ # NOTE: ipv4 actually allows the lookup to fail and yet still create
+ # a viable rtable if the oif (e.g., bind to device) is set, so this
+ # case succeeds despite not having a route for the address
+ # run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+
+ a=${NSA_LO_IP}
+ log_start
+ show_hint "Response is dropped (or arp request is ignored) due to ip route"
+ run_cmd_nsb ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 1 "ping in, blocked by route"
+
+ #
+ # remove 'remote' routes; fallback to default
+ #
+ log_start
+ setup_cmd ip ro del ${NSB_LO_IP}
+
+ a=${NSB_LO_IP}
+ run_cmd ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping out, unreachable default route"
+
+ # NOTE: ipv4 actually allows the lookup to fail and yet still create
+ # a viable rtable if the oif (e.g., bind to device) is set, so this
+ # case succeeds despite not having a route for the address
+ # run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+}
+
+ipv4_ping_vrf()
+{
+ local a
+
+ # should default on; does not exist on older kernels
+ set_sysctl net.ipv4.raw_l3mdev_accept=1 2>/dev/null
+
+ #
+ # out
+ #
+ for a in ${NSB_IP} ${NSB_LO_IP}
+ do
+ log_start
+ run_cmd ping -c1 -w1 -I ${VRF} ${a}
+ log_test_addr ${a} $? 0 "ping out, VRF bind"
+
+ log_start
+ run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 0 "ping out, device bind"
+
+ log_start
+ run_cmd ip vrf exec ${VRF} ping -c1 -w1 -I ${NSA_IP} ${a}
+ log_test_addr ${a} $? 0 "ping out, vrf device + dev address bind"
+
+ log_start
+ run_cmd ip vrf exec ${VRF} ping -c1 -w1 -I ${VRF_IP} ${a}
+ log_test_addr ${a} $? 0 "ping out, vrf device + vrf address bind"
+ done
+
+ #
+ # in
+ #
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd_nsb ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 0 "ping in"
+ done
+
+ #
+ # local traffic, local address
+ #
+ for a in ${NSA_IP} ${VRF_IP} 127.0.0.1
+ do
+ log_start
+ show_hint "Source address should be ${a}"
+ run_cmd ping -c1 -w1 -I ${VRF} ${a}
+ log_test_addr ${a} $? 0 "ping local, VRF bind"
+ done
+
+ #
+ # local traffic, socket bound to device
+ #
+ # address on device
+ a=${NSA_IP}
+ log_start
+ run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 0 "ping local, device bind"
+
+ # vrf device is out of scope
+ for a in ${VRF_IP} 127.0.0.1
+ do
+ log_start
+ show_hint "Fails since address on vrf device is out of device scope"
+ run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 1 "ping local, device bind"
+ done
+
+ #
+ # ip rule blocks address
+ #
+ log_start
+ setup_cmd ip rule add pref 50 to ${NSB_LO_IP} prohibit
+ setup_cmd ip rule add pref 51 from ${NSB_IP} prohibit
+
+ a=${NSB_LO_IP}
+ run_cmd ping -c1 -w1 -I ${VRF} ${a}
+ log_test_addr ${a} $? 2 "ping out, vrf bind, blocked by rule"
+
+ log_start
+ run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 2 "ping out, device bind, blocked by rule"
+
+ a=${NSA_LO_IP}
+ log_start
+ show_hint "Response lost due to ip rule"
+ run_cmd_nsb ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 1 "ping in, blocked by rule"
+
+ [ "$VERBOSE" = "1" ] && echo
+ setup_cmd ip rule del pref 50 to ${NSB_LO_IP} prohibit
+ setup_cmd ip rule del pref 51 from ${NSB_IP} prohibit
+
+ #
+ # remove 'remote' routes; fallback to default
+ #
+ log_start
+ setup_cmd ip ro del vrf ${VRF} ${NSB_LO_IP}
+
+ a=${NSB_LO_IP}
+ run_cmd ping -c1 -w1 -I ${VRF} ${a}
+ log_test_addr ${a} $? 2 "ping out, vrf bind, unreachable route"
+
+ log_start
+ run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 2 "ping out, device bind, unreachable route"
+
+ a=${NSA_LO_IP}
+ log_start
+ show_hint "Response lost by unreachable route"
+ run_cmd_nsb ping -c1 -w1 ${a}
+ log_test_addr ${a} $? 1 "ping in, unreachable route"
+}
+
+ipv4_ping()
+{
+ log_section "IPv4 ping"
+
+ log_subsection "No VRF"
+ setup
+ set_sysctl net.ipv4.raw_l3mdev_accept=0 2>/dev/null
+ ipv4_ping_novrf
+ setup
+ set_sysctl net.ipv4.raw_l3mdev_accept=1 2>/dev/null
+ ipv4_ping_novrf
+
+ log_subsection "With VRF"
+ setup "yes"
+ ipv4_ping_vrf
+}
+
+################################################################################
+# IPv4 TCP
+
+ipv4_tcp_novrf()
+{
+ local a
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP} ${NSA_LO_IP}
+ do
+ log_start
+ run_cmd nettest -s &
+ sleep 1
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -s -d ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 0 "Device server"
+
+ # verify TCP reset sent and received
+ for a in ${NSA_IP} ${NSA_LO_IP}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since there is no server"
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ #
+ # client
+ #
+ for a in ${NSB_IP} ${NSB_LO_IP}
+ do
+ log_start
+ run_cmd_nsb nettest -s &
+ sleep 1
+ run_cmd nettest -r ${a} -0 ${NSA_IP}
+ log_test_addr ${a} $? 0 "Client"
+
+ log_start
+ run_cmd_nsb nettest -s &
+ sleep 1
+ run_cmd nettest -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 0 "Client, device bind"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -r ${a}
+ log_test_addr ${a} $? 1 "No server, unbound client"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "No server, device client"
+ done
+
+ #
+ # local address tests
+ #
+ for a in ${NSA_IP} ${NSA_LO_IP} 127.0.0.1
+ do
+ log_start
+ run_cmd nettest -s &
+ sleep 1
+ run_cmd nettest -r ${a} -0 ${a} -1 ${a}
+ log_test_addr ${a} $? 0 "Global server, local connection"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -s -d ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -r ${a} -0 ${a}
+ log_test_addr ${a} $? 0 "Device server, unbound client, local connection"
+
+ for a in ${NSA_LO_IP} 127.0.0.1
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since addresses on loopback are out of device scope"
+ run_cmd nettest -s -d ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -r ${a}
+ log_test_addr ${a} $? 1 "Device server, unbound client, local connection"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -s &
+ sleep 1
+ run_cmd nettest -r ${a} -0 ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 0 "Global server, device client, local connection"
+
+ for a in ${NSA_LO_IP} 127.0.0.1
+ do
+ log_start
+ show_hint "Should fail 'No route to host' since addresses on loopback are out of device scope"
+ run_cmd nettest -s &
+ sleep 1
+ run_cmd nettest -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "Global server, device client, local connection"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -d ${NSA_DEV} -r ${a} -0 ${a}
+ log_test_addr ${a} $? 0 "Device server, device client, local connection"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 1 "No server, device client, local conn"
+}
+
+ipv4_tcp_vrf()
+{
+ local a
+
+ # disable global server
+ log_subsection "Global server disabled"
+
+ set_sysctl net.ipv4.tcp_l3mdev_accept=0
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since global server with VRF is disabled"
+ run_cmd nettest -s &
+ sleep 1
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 1 "Global server"
+
+ log_start
+ run_cmd nettest -s -d ${VRF} -2 ${VRF} &
+ sleep 1
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+
+ log_start
+ run_cmd nettest -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 0 "Device server"
+
+ # verify TCP reset received
+ log_start
+ show_hint "Should fail 'Connection refused' since there is no server"
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ # local address tests
+ # (${VRF_IP} and 127.0.0.1 both timeout)
+ a=${NSA_IP}
+ log_start
+ show_hint "Should fail 'Connection refused' since global server with VRF is disabled"
+ run_cmd nettest -s &
+ sleep 1
+ run_cmd nettest -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "Global server, local connection"
+
+ #
+ # enable VRF global server
+ #
+ log_subsection "VRF Global server enabled"
+ set_sysctl net.ipv4.tcp_l3mdev_accept=1
+
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ show_hint "client socket should be bound to VRF"
+ run_cmd nettest -s -2 ${VRF} &
+ sleep 1
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+
+ log_start
+ show_hint "client socket should be bound to VRF"
+ run_cmd nettest -s -d ${VRF} -2 ${VRF} &
+ sleep 1
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+
+ # verify TCP reset received
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ a=${NSA_IP}
+ log_start
+ show_hint "client socket should be bound to device"
+ run_cmd nettest -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 0 "Device server"
+
+ # local address tests
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ show_hint "Should fail 'No route to host' since client is not bound to VRF"
+ run_cmd nettest -s -2 ${VRF} &
+ sleep 1
+ run_cmd nettest -r ${a}
+ log_test_addr ${a} $? 1 "Global server, local connection"
+ done
+
+ #
+ # client
+ #
+ for a in ${NSB_IP} ${NSB_LO_IP}
+ do
+ log_start
+ run_cmd_nsb nettest -s &
+ sleep 1
+ run_cmd nettest -r ${a} -d ${VRF}
+ log_test_addr ${a} $? 0 "Client, VRF bind"
+
+ log_start
+ run_cmd_nsb nettest -s &
+ sleep 1
+ run_cmd nettest -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 0 "Client, device bind"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -r ${a} -d ${VRF}
+ log_test_addr ${a} $? 1 "No server, VRF client"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "No server, device client"
+ done
+
+ for a in ${NSA_IP} ${VRF_IP} 127.0.0.1
+ do
+ log_start
+ run_cmd nettest -s -d ${VRF} -2 ${VRF} &
+ sleep 1
+ run_cmd nettest -r ${a} -d ${VRF} -0 ${a}
+ log_test_addr ${a} $? 0 "VRF server, VRF client, local connection"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -s -d ${VRF} -2 ${VRF} &
+ sleep 1
+ run_cmd nettest -r ${a} -d ${NSA_DEV} -0 ${a}
+ log_test_addr ${a} $? 0 "VRF server, device client, local connection"
+
+ log_start
+ show_hint "Should fail 'No route to host' since client is out of VRF scope"
+ run_cmd nettest -s -d ${VRF} &
+ sleep 1
+ run_cmd nettest -r ${a}
+ log_test_addr ${a} $? 1 "VRF server, unbound client, local connection"
+
+ log_start
+ run_cmd nettest -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -r ${a} -d ${VRF} -0 ${a}
+ log_test_addr ${a} $? 0 "Device server, VRF client, local connection"
+
+ log_start
+ run_cmd nettest -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -r ${a} -d ${NSA_DEV} -0 ${a}
+ log_test_addr ${a} $? 0 "Device server, device client, local connection"
+}
+
+ipv4_tcp()
+{
+ log_section "IPv4/TCP"
+ log_subsection "No VRF"
+ setup
+
+ # tcp_l3mdev_accept should have no affect without VRF;
+ # run tests with it enabled and disabled to verify
+ log_subsection "tcp_l3mdev_accept disabled"
+ set_sysctl net.ipv4.tcp_l3mdev_accept=0
+ ipv4_tcp_novrf
+ log_subsection "tcp_l3mdev_accept enabled"
+ set_sysctl net.ipv4.tcp_l3mdev_accept=1
+ ipv4_tcp_novrf
+
+ log_subsection "With VRF"
+ setup "yes"
+ ipv4_tcp_vrf
+}
+
+################################################################################
+# IPv4 UDP
+
+ipv4_udp_novrf()
+{
+ local a
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP} ${NSA_LO_IP}
+ do
+ log_start
+ run_cmd nettest -D -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+
+ log_start
+ show_hint "Should fail 'Connection refused' since there is no server"
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 0 "Device server"
+
+ #
+ # client
+ #
+ for a in ${NSB_IP} ${NSB_LO_IP}
+ do
+ log_start
+ run_cmd_nsb nettest -D -s &
+ sleep 1
+ run_cmd nettest -D -r ${a} -0 ${NSA_IP}
+ log_test_addr ${a} $? 0 "Client"
+
+ log_start
+ run_cmd_nsb nettest -D -s &
+ sleep 1
+ run_cmd nettest -D -r ${a} -d ${NSA_DEV} -0 ${NSA_IP}
+ log_test_addr ${a} $? 0 "Client, device bind"
+
+ log_start
+ run_cmd_nsb nettest -D -s &
+ sleep 1
+ run_cmd nettest -D -r ${a} -d ${NSA_DEV} -C -0 ${NSA_IP}
+ log_test_addr ${a} $? 0 "Client, device send via cmsg"
+
+ log_start
+ run_cmd_nsb nettest -D -s &
+ sleep 1
+ run_cmd nettest -D -r ${a} -d ${NSA_DEV} -S -0 ${NSA_IP}
+ log_test_addr ${a} $? 0 "Client, device bind via IP_UNICAST_IF"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -D -r ${a}
+ log_test_addr ${a} $? 1 "No server, unbound client"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -D -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "No server, device client"
+ done
+
+ #
+ # local address tests
+ #
+ for a in ${NSA_IP} ${NSA_LO_IP} 127.0.0.1
+ do
+ log_start
+ run_cmd nettest -D -s &
+ sleep 1
+ run_cmd nettest -D -r ${a} -0 ${a} -1 ${a}
+ log_test_addr ${a} $? 0 "Global server, local connection"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -s -D -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -r ${a}
+ log_test_addr ${a} $? 0 "Device server, unbound client, local connection"
+
+ for a in ${NSA_LO_IP} 127.0.0.1
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since address is out of device scope"
+ run_cmd nettest -s -D -d ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -r ${a}
+ log_test_addr ${a} $? 1 "Device server, unbound client, local connection"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -s -D &
+ sleep 1
+ run_cmd nettest -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "Global server, device client, local connection"
+
+ log_start
+ run_cmd nettest -s -D &
+ sleep 1
+ run_cmd nettest -D -d ${NSA_DEV} -C -r ${a}
+ log_test_addr ${a} $? 0 "Global server, device send via cmsg, local connection"
+
+ log_start
+ run_cmd nettest -s -D &
+ sleep 1
+ run_cmd nettest -D -d ${NSA_DEV} -S -r ${a}
+ log_test_addr ${a} $? 0 "Global server, device client via IP_UNICAST_IF, local connection"
+
+ # IPv4 with device bind has really weird behavior - it overrides the
+ # fib lookup, generates an rtable and tries to send the packet. This
+ # causes failures for local traffic at different places
+ for a in ${NSA_LO_IP} 127.0.0.1
+ do
+ log_start
+ show_hint "Should fail since addresses on loopback are out of device scope"
+ run_cmd nettest -D -s &
+ sleep 1
+ run_cmd nettest -D -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 2 "Global server, device client, local connection"
+
+ log_start
+ show_hint "Should fail since addresses on loopback are out of device scope"
+ run_cmd nettest -D -s &
+ sleep 1
+ run_cmd nettest -D -r ${a} -d ${NSA_DEV} -C
+ log_test_addr ${a} $? 1 "Global server, device send via cmsg, local connection"
+
+ log_start
+ show_hint "Should fail since addresses on loopback are out of device scope"
+ run_cmd nettest -D -s &
+ sleep 1
+ run_cmd nettest -D -r ${a} -d ${NSA_DEV} -S
+ log_test_addr ${a} $? 1 "Global server, device client via IP_UNICAST_IF, local connection"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -D -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${NSA_DEV} -r ${a} -0 ${a}
+ log_test_addr ${a} $? 0 "Device server, device client, local conn"
+
+ log_start
+ run_cmd nettest -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 2 "No server, device client, local conn"
+}
+
+ipv4_udp_vrf()
+{
+ local a
+
+ # disable global server
+ log_subsection "Global server disabled"
+ set_sysctl net.ipv4.udp_l3mdev_accept=0
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ show_hint "Fails because ingress is in a VRF and global server is disabled"
+ run_cmd nettest -D -s &
+ sleep 1
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 1 "Global server"
+
+ log_start
+ run_cmd nettest -D -d ${VRF} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+
+ log_start
+ run_cmd nettest -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server"
+
+ log_start
+ show_hint "Should fail 'Connection refused' since there is no server"
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+
+ log_start
+ show_hint "Should fail 'Connection refused' since global server is out of scope"
+ run_cmd nettest -D -s &
+ sleep 1
+ run_cmd nettest -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 1 "Global server, VRF client, local connection"
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -s -D -d ${VRF} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, VRF client, local conn"
+
+ log_start
+ run_cmd nettest -s -D -d ${VRF} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, enslaved device client, local connection"
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -s -D -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server, VRF client, local conn"
+
+ log_start
+ run_cmd nettest -s -D -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server, device client, local conn"
+
+ # enable global server
+ log_subsection "Global server enabled"
+ set_sysctl net.ipv4.udp_l3mdev_accept=1
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd nettest -D -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+
+ log_start
+ run_cmd nettest -D -d ${VRF} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+
+ log_start
+ run_cmd nettest -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd_nsb nettest -D -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ #
+ # client tests
+ #
+ log_start
+ run_cmd_nsb nettest -D -s &
+ sleep 1
+ run_cmd nettest -d ${VRF} -D -r ${NSB_IP} -1 ${NSA_IP}
+ log_test $? 0 "VRF client"
+
+ log_start
+ run_cmd_nsb nettest -D -s &
+ sleep 1
+ run_cmd nettest -d ${NSA_DEV} -D -r ${NSB_IP} -1 ${NSA_IP}
+ log_test $? 0 "Enslaved device client"
+
+ # negative test - should fail
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -D -d ${VRF} -r ${NSB_IP}
+ log_test $? 1 "No server, VRF client"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -D -d ${NSA_DEV} -r ${NSB_IP}
+ log_test $? 1 "No server, enslaved device client"
+
+ #
+ # local address tests
+ #
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -D -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "Global server, VRF client, local conn"
+
+ log_start
+ run_cmd nettest -s -D -d ${VRF} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, VRF client, local conn"
+
+ log_start
+ run_cmd nettest -s -D -d ${VRF} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, device client, local conn"
+
+ log_start
+ run_cmd nettest -s -D -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server, VRF client, local conn"
+
+ log_start
+ run_cmd nettest -s -D -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server, device client, local conn"
+
+ for a in ${VRF_IP} 127.0.0.1
+ do
+ log_start
+ run_cmd nettest -D -s -2 ${VRF} &
+ sleep 1
+ run_cmd nettest -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "Global server, VRF client, local conn"
+ done
+
+ for a in ${VRF_IP} 127.0.0.1
+ do
+ log_start
+ run_cmd nettest -s -D -d ${VRF} -2 ${VRF} &
+ sleep 1
+ run_cmd nettest -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, VRF client, local conn"
+ done
+
+ # negative test - should fail
+ # verifies ECONNREFUSED
+ for a in ${NSA_IP} ${VRF_IP} 127.0.0.1
+ do
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 1 "No server, VRF client, local conn"
+ done
+}
+
+ipv4_udp()
+{
+ log_section "IPv4/UDP"
+ log_subsection "No VRF"
+
+ setup
+
+ # udp_l3mdev_accept should have no affect without VRF;
+ # run tests with it enabled and disabled to verify
+ log_subsection "udp_l3mdev_accept disabled"
+ set_sysctl net.ipv4.udp_l3mdev_accept=0
+ ipv4_udp_novrf
+ log_subsection "udp_l3mdev_accept enabled"
+ set_sysctl net.ipv4.udp_l3mdev_accept=1
+ ipv4_udp_novrf
+
+ log_subsection "With VRF"
+ setup "yes"
+ ipv4_udp_vrf
+}
+
+################################################################################
+# IPv4 address bind
+#
+# verifies ability or inability to bind to an address / device
+
+ipv4_addr_bind_novrf()
+{
+ #
+ # raw socket
+ #
+ for a in ${NSA_IP} ${NSA_LO_IP}
+ do
+ log_start
+ run_cmd nettest -s -R -P icmp -l ${a} -b
+ log_test_addr ${a} $? 0 "Raw socket bind to local address"
+
+ log_start
+ run_cmd nettest -s -R -P icmp -l ${a} -d ${NSA_DEV} -b
+ log_test_addr ${a} $? 0 "Raw socket bind to local address after device bind"
+ done
+
+ #
+ # tcp sockets
+ #
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest -l ${a} -r ${NSB_IP} -t1 -b
+ log_test_addr ${a} $? 0 "TCP socket bind to local address"
+
+ log_start
+ run_cmd nettest -l ${a} -r ${NSB_IP} -d ${NSA_DEV} -t1 -b
+ log_test_addr ${a} $? 0 "TCP socket bind to local address after device bind"
+
+ # Sadly, the kernel allows binding a socket to a device and then
+ # binding to an address not on the device. The only restriction
+ # is that the address is valid in the L3 domain. So this test
+ # passes when it really should not
+ #a=${NSA_LO_IP}
+ #log_start
+ #show_hint "Should fail with 'Cannot assign requested address'"
+ #run_cmd nettest -s -l ${a} -d ${NSA_DEV} -t1 -b
+ #log_test_addr ${a} $? 1 "TCP socket bind to out of scope local address"
+}
+
+ipv4_addr_bind_vrf()
+{
+ #
+ # raw socket
+ #
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd nettest -s -R -P icmp -l ${a} -b
+ log_test_addr ${a} $? 0 "Raw socket bind to local address"
+
+ log_start
+ run_cmd nettest -s -R -P icmp -l ${a} -d ${NSA_DEV} -b
+ log_test_addr ${a} $? 0 "Raw socket bind to local address after device bind"
+ log_start
+ run_cmd nettest -s -R -P icmp -l ${a} -d ${VRF} -b
+ log_test_addr ${a} $? 0 "Raw socket bind to local address after VRF bind"
+ done
+
+ a=${NSA_LO_IP}
+ log_start
+ show_hint "Address on loopback is out of VRF scope"
+ run_cmd nettest -s -R -P icmp -l ${a} -d ${VRF} -b
+ log_test_addr ${a} $? 1 "Raw socket bind to out of scope address after VRF bind"
+
+ #
+ # tcp sockets
+ #
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd nettest -s -l ${a} -d ${VRF} -t1 -b
+ log_test_addr ${a} $? 0 "TCP socket bind to local address"
+
+ log_start
+ run_cmd nettest -s -l ${a} -d ${NSA_DEV} -t1 -b
+ log_test_addr ${a} $? 0 "TCP socket bind to local address after device bind"
+ done
+
+ a=${NSA_LO_IP}
+ log_start
+ show_hint "Address on loopback out of scope for VRF"
+ run_cmd nettest -s -l ${a} -d ${VRF} -t1 -b
+ log_test_addr ${a} $? 1 "TCP socket bind to invalid local address for VRF"
+
+ log_start
+ show_hint "Address on loopback out of scope for device in VRF"
+ run_cmd nettest -s -l ${a} -d ${NSA_DEV} -t1 -b
+ log_test_addr ${a} $? 1 "TCP socket bind to invalid local address for device bind"
+}
+
+ipv4_addr_bind()
+{
+ log_section "IPv4 address binds"
+
+ log_subsection "No VRF"
+ setup
+ ipv4_addr_bind_novrf
+
+ log_subsection "With VRF"
+ setup "yes"
+ ipv4_addr_bind_vrf
+}
+
+################################################################################
+# IPv4 runtime tests
+
+ipv4_rt()
+{
+ local desc="$1"
+ local varg="$2"
+ local with_vrf="yes"
+ local a
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd nettest ${varg} -s &
+ sleep 1
+ run_cmd_nsb nettest ${varg} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, global server"
+
+ setup ${with_vrf}
+ done
+
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd nettest ${varg} -s -d ${VRF} &
+ sleep 1
+ run_cmd_nsb nettest ${varg} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, VRF server"
+
+ setup ${with_vrf}
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest ${varg} -s -d ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest ${varg} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, enslaved device server"
+
+ setup ${with_vrf}
+
+ #
+ # client test
+ #
+ log_start
+ run_cmd_nsb nettest ${varg} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${VRF} -r ${NSB_IP} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, VRF client"
+
+ setup ${with_vrf}
+
+ log_start
+ run_cmd_nsb nettest ${varg} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${NSA_DEV} -r ${NSB_IP} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, enslaved device client"
+
+ setup ${with_vrf}
+
+ #
+ # local address tests
+ #
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd nettest ${varg} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${VRF} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, global server, VRF client, local"
+
+ setup ${with_vrf}
+ done
+
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd nettest ${varg} -d ${VRF} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${VRF} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, VRF server and client, local"
+
+ setup ${with_vrf}
+ done
+
+ a=${NSA_IP}
+ log_start
+ run_cmd nettest ${varg} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${NSA_DEV} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, global server, enslaved device client, local"
+
+ setup ${with_vrf}
+
+ log_start
+ run_cmd nettest ${varg} -d ${VRF} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${NSA_DEV} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, VRF server, enslaved device client, local"
+
+ setup ${with_vrf}
+
+ log_start
+ run_cmd nettest ${varg} -d ${NSA_DEV} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${NSA_DEV} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, enslaved device server and client, local"
+}
+
+ipv4_ping_rt()
+{
+ local with_vrf="yes"
+ local a
+
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd_nsb ping -f ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "Device delete with active traffic - ping in"
+
+ setup ${with_vrf}
+ done
+
+ a=${NSB_IP}
+ log_start
+ run_cmd ping -f -I ${VRF} ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "Device delete with active traffic - ping out"
+}
+
+ipv4_runtime()
+{
+ log_section "Run time tests - ipv4"
+
+ setup "yes"
+ ipv4_ping_rt
+
+ setup "yes"
+ ipv4_rt "TCP active socket" "-n -1"
+
+ setup "yes"
+ ipv4_rt "TCP passive socket" "-i"
+}
+
+################################################################################
+# IPv6
+
+ipv6_ping_novrf()
+{
+ local a
+
+ # should not have an impact, but make a known state
+ set_sysctl net.ipv4.raw_l3mdev_accept=0 2>/dev/null
+
+ #
+ # out
+ #
+ for a in ${NSB_IP6} ${NSB_LO_IP6} ${NSB_LINKIP6}%${NSA_DEV} ${MCAST}%${NSA_DEV}
+ do
+ log_start
+ run_cmd ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 0 "ping out"
+ done
+
+ for a in ${NSB_IP6} ${NSB_LO_IP6}
+ do
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 0 "ping out, device bind"
+
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_LO_IP6} ${a}
+ log_test_addr ${a} $? 0 "ping out, loopback address bind"
+ done
+
+ #
+ # in
+ #
+ for a in ${NSA_IP6} ${NSA_LO_IP6} ${NSA_LINKIP6}%${NSB_DEV} ${MCAST}%${NSB_DEV}
+ do
+ log_start
+ run_cmd_nsb ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 0 "ping in"
+ done
+
+ #
+ # local traffic, local address
+ #
+ for a in ${NSA_IP6} ${NSA_LO_IP6} ::1 ${NSA_LINKIP6}%${NSA_DEV} ${MCAST}%${NSA_DEV}
+ do
+ log_start
+ run_cmd ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 0 "ping local, no bind"
+ done
+
+ for a in ${NSA_IP6} ${NSA_LINKIP6}%${NSA_DEV} ${MCAST}%${NSA_DEV}
+ do
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 0 "ping local, device bind"
+ done
+
+ for a in ${NSA_LO_IP6} ::1
+ do
+ log_start
+ show_hint "Fails since address on loopback is out of device scope"
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 2 "ping local, device bind"
+ done
+
+ #
+ # ip rule blocks address
+ #
+ log_start
+ setup_cmd ip -6 rule add pref 32765 from all lookup local
+ setup_cmd ip -6 rule del pref 0 from all lookup local
+ setup_cmd ip -6 rule add pref 50 to ${NSB_LO_IP6} prohibit
+ setup_cmd ip -6 rule add pref 51 from ${NSB_IP6} prohibit
+
+ a=${NSB_LO_IP6}
+ run_cmd ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping out, blocked by rule"
+
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 2 "ping out, device bind, blocked by rule"
+
+ a=${NSA_LO_IP6}
+ log_start
+ show_hint "Response lost due to ip rule"
+ run_cmd_nsb ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 1 "ping in, blocked by rule"
+
+ setup_cmd ip -6 rule add pref 0 from all lookup local
+ setup_cmd ip -6 rule del pref 32765 from all lookup local
+ setup_cmd ip -6 rule del pref 50 to ${NSB_LO_IP6} prohibit
+ setup_cmd ip -6 rule del pref 51 from ${NSB_IP6} prohibit
+
+ #
+ # route blocks reachability to remote address
+ #
+ log_start
+ setup_cmd ip -6 route del ${NSB_LO_IP6}
+ setup_cmd ip -6 route add unreachable ${NSB_LO_IP6} metric 10
+ setup_cmd ip -6 route add unreachable ${NSB_IP6} metric 10
+
+ a=${NSB_LO_IP6}
+ run_cmd ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping out, blocked by route"
+
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 2 "ping out, device bind, blocked by route"
+
+ a=${NSA_LO_IP6}
+ log_start
+ show_hint "Response lost due to ip route"
+ run_cmd_nsb ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 1 "ping in, blocked by route"
+
+
+ #
+ # remove 'remote' routes; fallback to default
+ #
+ log_start
+ setup_cmd ip -6 ro del unreachable ${NSB_LO_IP6}
+ setup_cmd ip -6 ro del unreachable ${NSB_IP6}
+
+ a=${NSB_LO_IP6}
+ run_cmd ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping out, unreachable route"
+
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 2 "ping out, device bind, unreachable route"
+}
+
+ipv6_ping_vrf()
+{
+ local a
+
+ # should default on; does not exist on older kernels
+ set_sysctl net.ipv4.raw_l3mdev_accept=1 2>/dev/null
+
+ #
+ # out
+ #
+ for a in ${NSB_IP6} ${NSB_LO_IP6}
+ do
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${VRF} ${a}
+ log_test_addr ${a} $? 0 "ping out, VRF bind"
+ done
+
+ for a in ${NSB_LINKIP6}%${VRF} ${MCAST}%${VRF}
+ do
+ log_start
+ show_hint "Fails since VRF device does not support linklocal or multicast"
+ run_cmd ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping out, VRF bind"
+ done
+
+ for a in ${NSB_IP6} ${NSB_LO_IP6} ${NSB_LINKIP6}%${NSA_DEV} ${MCAST}%${NSA_DEV}
+ do
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 0 "ping out, device bind"
+ done
+
+ for a in ${NSB_IP6} ${NSB_LO_IP6} ${NSB_LINKIP6}%${NSA_DEV}
+ do
+ log_start
+ run_cmd ip vrf exec ${VRF} ${ping6} -c1 -w1 -I ${VRF_IP6} ${a}
+ log_test_addr ${a} $? 0 "ping out, vrf device+address bind"
+ done
+
+ #
+ # in
+ #
+ for a in ${NSA_IP6} ${VRF_IP6} ${NSA_LINKIP6}%${NSB_DEV} ${MCAST}%${NSB_DEV}
+ do
+ log_start
+ run_cmd_nsb ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 0 "ping in"
+ done
+
+ a=${NSA_LO_IP6}
+ log_start
+ show_hint "Fails since loopback address is out of VRF scope"
+ run_cmd_nsb ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 1 "ping in"
+
+ #
+ # local traffic, local address
+ #
+ for a in ${NSA_IP6} ${VRF_IP6} ::1
+ do
+ log_start
+ show_hint "Source address should be ${a}"
+ run_cmd ${ping6} -c1 -w1 -I ${VRF} ${a}
+ log_test_addr ${a} $? 0 "ping local, VRF bind"
+ done
+
+ for a in ${NSA_IP6} ${NSA_LINKIP6}%${NSA_DEV} ${MCAST}%${NSA_DEV}
+ do
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 0 "ping local, device bind"
+ done
+
+ # LLA to GUA - remove ipv6 global addresses from ns-B
+ setup_cmd_nsb ip -6 addr del ${NSB_IP6}/64 dev ${NSB_DEV}
+ setup_cmd_nsb ip -6 addr del ${NSB_LO_IP6}/128 dev lo
+ setup_cmd_nsb ip -6 ro add ${NSA_IP6}/128 via ${NSA_LINKIP6} dev ${NSB_DEV}
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd_nsb ${ping6} -c1 -w1 ${NSA_IP6}
+ log_test_addr ${a} $? 0 "ping in, LLA to GUA"
+ done
+
+ setup_cmd_nsb ip -6 ro del ${NSA_IP6}/128 via ${NSA_LINKIP6} dev ${NSB_DEV}
+ setup_cmd_nsb ip -6 addr add ${NSB_IP6}/64 dev ${NSB_DEV}
+ setup_cmd_nsb ip -6 addr add ${NSB_LO_IP6}/128 dev lo
+
+ #
+ # ip rule blocks address
+ #
+ log_start
+ setup_cmd ip -6 rule add pref 50 to ${NSB_LO_IP6} prohibit
+ setup_cmd ip -6 rule add pref 51 from ${NSB_IP6} prohibit
+
+ a=${NSB_LO_IP6}
+ run_cmd ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping out, blocked by rule"
+
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 2 "ping out, device bind, blocked by rule"
+
+ a=${NSA_LO_IP6}
+ log_start
+ show_hint "Response lost due to ip rule"
+ run_cmd_nsb ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 1 "ping in, blocked by rule"
+
+ log_start
+ setup_cmd ip -6 rule del pref 50 to ${NSB_LO_IP6} prohibit
+ setup_cmd ip -6 rule del pref 51 from ${NSB_IP6} prohibit
+
+ #
+ # remove 'remote' routes; fallback to default
+ #
+ log_start
+ setup_cmd ip -6 ro del ${NSB_LO_IP6} vrf ${VRF}
+
+ a=${NSB_LO_IP6}
+ run_cmd ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping out, unreachable route"
+
+ log_start
+ run_cmd ${ping6} -c1 -w1 -I ${NSA_DEV} ${a}
+ log_test_addr ${a} $? 2 "ping out, device bind, unreachable route"
+
+ ip -netns ${NSB} -6 ro del ${NSA_LO_IP6}
+ a=${NSA_LO_IP6}
+ log_start
+ run_cmd_nsb ${ping6} -c1 -w1 ${a}
+ log_test_addr ${a} $? 2 "ping in, unreachable route"
+}
+
+ipv6_ping()
+{
+ log_section "IPv6 ping"
+
+ log_subsection "No VRF"
+ setup
+ ipv6_ping_novrf
+
+ log_subsection "With VRF"
+ setup "yes"
+ ipv6_ping_vrf
+}
+
+################################################################################
+# IPv6 TCP
+
+ipv6_tcp_novrf()
+{
+ local a
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP6} ${NSA_LO_IP6} ${NSA_LINKIP6}%${NSB_DEV}
+ do
+ log_start
+ run_cmd nettest -6 -s &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+ done
+
+ # verify TCP reset received
+ for a in ${NSA_IP6} ${NSA_LO_IP6} ${NSA_LINKIP6}%${NSB_DEV}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ #
+ # client
+ #
+ for a in ${NSB_IP6} ${NSB_LO_IP6} ${NSB_LINKIP6}%${NSA_DEV}
+ do
+ log_start
+ run_cmd_nsb nettest -6 -s &
+ sleep 1
+ run_cmd nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "Client"
+ done
+
+ for a in ${NSB_IP6} ${NSB_LO_IP6} ${NSB_LINKIP6}%${NSA_DEV}
+ do
+ log_start
+ run_cmd_nsb nettest -6 -s &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 0 "Client, device bind"
+ done
+
+ for a in ${NSB_IP6} ${NSB_LO_IP6} ${NSB_LINKIP6}%${NSA_DEV}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -6 -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "No server, device client"
+ done
+
+ #
+ # local address tests
+ #
+ for a in ${NSA_IP6} ${NSA_LO_IP6} ::1
+ do
+ log_start
+ run_cmd nettest -6 -s &
+ sleep 1
+ run_cmd nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "Global server, local connection"
+ done
+
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -0 ${a}
+ log_test_addr ${a} $? 0 "Device server, unbound client, local connection"
+
+ for a in ${NSA_LO_IP6} ::1
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since addresses on loopback are out of device scope"
+ run_cmd nettest -6 -s -d ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -r ${a}
+ log_test_addr ${a} $? 1 "Device server, unbound client, local connection"
+ done
+
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -s &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${NSA_DEV} -0 ${a}
+ log_test_addr ${a} $? 0 "Global server, device client, local connection"
+
+ for a in ${NSA_LO_IP6} ::1
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since addresses on loopback are out of device scope"
+ run_cmd nettest -6 -s &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "Global server, device client, local connection"
+ done
+
+ for a in ${NSA_IP6} ${NSA_LINKIP6}
+ do
+ log_start
+ run_cmd nettest -6 -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "Device server, device client, local conn"
+ done
+
+ for a in ${NSA_IP6} ${NSA_LINKIP6}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -6 -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 1 "No server, device client, local conn"
+ done
+}
+
+ipv6_tcp_vrf()
+{
+ local a
+
+ # disable global server
+ log_subsection "Global server disabled"
+
+ set_sysctl net.ipv4.tcp_l3mdev_accept=0
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP6} ${VRF_IP6} ${NSA_LINKIP6}%${NSB_DEV}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since global server with VRF is disabled"
+ run_cmd nettest -6 -s &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 1 "Global server"
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -s -d ${VRF} -2 ${VRF} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+ done
+
+ # link local is always bound to ingress device
+ a=${NSA_LINKIP6}%${NSB_DEV}
+ log_start
+ run_cmd nettest -6 -s -d ${VRF} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+
+ for a in ${NSA_IP6} ${VRF_IP6} ${NSA_LINKIP6}%${NSB_DEV}
+ do
+ log_start
+ run_cmd nettest -6 -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "Device server"
+ done
+
+ # verify TCP reset received
+ for a in ${NSA_IP6} ${VRF_IP6} ${NSA_LINKIP6}%${NSB_DEV}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ # local address tests
+ a=${NSA_IP6}
+ log_start
+ show_hint "Should fail 'Connection refused' since global server with VRF is disabled"
+ run_cmd nettest -6 -s &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "Global server, local connection"
+
+ #
+ # enable VRF global server
+ #
+ log_subsection "VRF Global server enabled"
+ set_sysctl net.ipv4.tcp_l3mdev_accept=1
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -s -2 ${VRF} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -s -d ${VRF} -2 ${VRF} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+ done
+
+ # For LLA, child socket is bound to device
+ a=${NSA_LINKIP6}%${NSB_DEV}
+ log_start
+ run_cmd nettest -6 -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+
+ log_start
+ run_cmd nettest -6 -s -d ${VRF} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+
+ for a in ${NSA_IP6} ${NSA_LINKIP6}%${NSB_DEV}
+ do
+ log_start
+ run_cmd nettest -6 -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 0 "Device server"
+ done
+
+ # verify TCP reset received
+ for a in ${NSA_IP6} ${VRF_IP6} ${NSA_LINKIP6}%${NSB_DEV}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ # local address tests
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ show_hint "Fails 'No route to host' since client is not in VRF"
+ run_cmd nettest -6 -s -2 ${VRF} &
+ sleep 1
+ run_cmd nettest -6 -r ${a}
+ log_test_addr ${a} $? 1 "Global server, local connection"
+ done
+
+
+ #
+ # client
+ #
+ for a in ${NSB_IP6} ${NSB_LO_IP6}
+ do
+ log_start
+ run_cmd_nsb nettest -6 -s &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${VRF}
+ log_test_addr ${a} $? 0 "Client, VRF bind"
+ done
+
+ a=${NSB_LINKIP6}
+ log_start
+ show_hint "Fails since VRF device does not allow linklocal addresses"
+ run_cmd_nsb nettest -6 -s &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${VRF}
+ log_test_addr ${a} $? 1 "Client, VRF bind"
+
+ for a in ${NSB_IP6} ${NSB_LO_IP6} ${NSB_LINKIP6}
+ do
+ log_start
+ run_cmd_nsb nettest -6 -s &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 0 "Client, device bind"
+ done
+
+ for a in ${NSB_IP6} ${NSB_LO_IP6}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -6 -r ${a} -d ${VRF}
+ log_test_addr ${a} $? 1 "No server, VRF client"
+ done
+
+ for a in ${NSB_IP6} ${NSB_LO_IP6} ${NSB_LINKIP6}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -6 -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "No server, device client"
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6} ::1
+ do
+ log_start
+ run_cmd nettest -6 -s -d ${VRF} -2 ${VRF} &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${VRF} -0 ${a}
+ log_test_addr ${a} $? 0 "VRF server, VRF client, local connection"
+ done
+
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -s -d ${VRF} -2 ${VRF} &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${NSA_DEV} -0 ${a}
+ log_test_addr ${a} $? 0 "VRF server, device client, local connection"
+
+ a=${NSA_IP6}
+ log_start
+ show_hint "Should fail since unbound client is out of VRF scope"
+ run_cmd nettest -6 -s -d ${VRF} &
+ sleep 1
+ run_cmd nettest -6 -r ${a}
+ log_test_addr ${a} $? 1 "VRF server, unbound client, local connection"
+
+ log_start
+ run_cmd nettest -6 -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${VRF} -0 ${a}
+ log_test_addr ${a} $? 0 "Device server, VRF client, local connection"
+
+ for a in ${NSA_IP6} ${NSA_LINKIP6}
+ do
+ log_start
+ run_cmd nettest -6 -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -r ${a} -d ${NSA_DEV} -0 ${a}
+ log_test_addr ${a} $? 0 "Device server, device client, local connection"
+ done
+}
+
+ipv6_tcp()
+{
+ log_section "IPv6/TCP"
+ log_subsection "No VRF"
+ setup
+
+ # tcp_l3mdev_accept should have no affect without VRF;
+ # run tests with it enabled and disabled to verify
+ log_subsection "tcp_l3mdev_accept disabled"
+ set_sysctl net.ipv4.tcp_l3mdev_accept=0
+ ipv6_tcp_novrf
+ log_subsection "tcp_l3mdev_accept enabled"
+ set_sysctl net.ipv4.tcp_l3mdev_accept=1
+ ipv6_tcp_novrf
+
+ log_subsection "With VRF"
+ setup "yes"
+ ipv6_tcp_vrf
+}
+
+################################################################################
+# IPv6 UDP
+
+ipv6_udp_novrf()
+{
+ local a
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP6} ${NSA_LINKIP6}%${NSB_DEV}
+ do
+ log_start
+ run_cmd nettest -6 -D -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 0 "Device server"
+ done
+
+ a=${NSA_LO_IP6}
+ log_start
+ run_cmd nettest -6 -D -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+
+ # should fail since loopback address is out of scope for a device
+ # bound server, but it does not - hence this is more documenting
+ # behavior.
+ #log_start
+ #show_hint "Should fail since loopback address is out of scope"
+ #run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ #sleep 1
+ #run_cmd_nsb nettest -6 -D -r ${a}
+ #log_test_addr ${a} $? 1 "Device server"
+
+ # negative test - should fail
+ for a in ${NSA_IP6} ${NSA_LO_IP6} ${NSA_LINKIP6}%${NSB_DEV}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since there is no server"
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ #
+ # client
+ #
+ for a in ${NSB_IP6} ${NSB_LO_IP6} ${NSB_LINKIP6}%${NSA_DEV}
+ do
+ log_start
+ run_cmd_nsb nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a} -0 ${NSA_IP6}
+ log_test_addr ${a} $? 0 "Client"
+
+ log_start
+ run_cmd_nsb nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a} -d ${NSA_DEV} -0 ${NSA_IP6}
+ log_test_addr ${a} $? 0 "Client, device bind"
+
+ log_start
+ run_cmd_nsb nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a} -d ${NSA_DEV} -C -0 ${NSA_IP6}
+ log_test_addr ${a} $? 0 "Client, device send via cmsg"
+
+ log_start
+ run_cmd_nsb nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a} -d ${NSA_DEV} -S -0 ${NSA_IP6}
+ log_test_addr ${a} $? 0 "Client, device bind via IPV6_UNICAST_IF"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 1 "No server, unbound client"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -6 -D -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "No server, device client"
+ done
+
+ #
+ # local address tests
+ #
+ for a in ${NSA_IP6} ${NSA_LO_IP6} ::1
+ do
+ log_start
+ run_cmd nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a} -0 ${a} -1 ${a}
+ log_test_addr ${a} $? 0 "Global server, local connection"
+ done
+
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -s -D -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 0 "Device server, unbound client, local connection"
+
+ for a in ${NSA_LO_IP6} ::1
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since address is out of device scope"
+ run_cmd nettest -6 -s -D -d ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 1 "Device server, local connection"
+ done
+
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -s -D &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "Global server, device client, local connection"
+
+ log_start
+ run_cmd nettest -6 -s -D &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -C -r ${a}
+ log_test_addr ${a} $? 0 "Global server, device send via cmsg, local connection"
+
+ log_start
+ run_cmd nettest -6 -s -D &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -S -r ${a}
+ log_test_addr ${a} $? 0 "Global server, device client via IPV6_UNICAST_IF, local connection"
+
+ for a in ${NSA_LO_IP6} ::1
+ do
+ log_start
+ show_hint "Should fail 'No route to host' since addresses on loopback are out of device scope"
+ run_cmd nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a} -d ${NSA_DEV}
+ log_test_addr ${a} $? 1 "Global server, device client, local connection"
+
+ log_start
+ show_hint "Should fail 'No route to host' since addresses on loopback are out of device scope"
+ run_cmd nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a} -d ${NSA_DEV} -C
+ log_test_addr ${a} $? 1 "Global server, device send via cmsg, local connection"
+
+ log_start
+ show_hint "Should fail 'No route to host' since addresses on loopback are out of device scope"
+ run_cmd nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -r ${a} -d ${NSA_DEV} -S
+ log_test_addr ${a} $? 1 "Global server, device client via IP_UNICAST_IF, local connection"
+ done
+
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -D -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a} -0 ${a}
+ log_test_addr ${a} $? 0 "Device server, device client, local conn"
+
+ log_start
+ show_hint "Should fail 'Connection refused'"
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 1 "No server, device client, local conn"
+
+ # LLA to GUA
+ run_cmd_nsb ip -6 addr del ${NSB_IP6}/64 dev ${NSB_DEV}
+ run_cmd_nsb ip -6 ro add ${NSA_IP6}/128 dev ${NSB_DEV}
+ log_start
+ run_cmd nettest -6 -s -D &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${NSA_IP6}
+ log_test $? 0 "UDP in - LLA to GUA"
+
+ run_cmd_nsb ip -6 ro del ${NSA_IP6}/128 dev ${NSB_DEV}
+ run_cmd_nsb ip -6 addr add ${NSB_IP6}/64 dev ${NSB_DEV} nodad
+}
+
+ipv6_udp_vrf()
+{
+ local a
+
+ # disable global server
+ log_subsection "Global server disabled"
+ set_sysctl net.ipv4.udp_l3mdev_accept=0
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since global server is disabled"
+ run_cmd nettest -6 -D -s &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 1 "Global server"
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -D -d ${VRF} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server"
+ done
+
+ # negative test - should fail
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since there is no server"
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ #
+ # local address tests
+ #
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ show_hint "Should fail 'Connection refused' since global server is disabled"
+ run_cmd nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 1 "Global server, VRF client, local conn"
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -D -d ${VRF} -s &
+ sleep 1
+ run_cmd nettest -6 -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, VRF client, local conn"
+ done
+
+ a=${NSA_IP6}
+ log_start
+ show_hint "Should fail 'Connection refused' since global server is disabled"
+ run_cmd nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 1 "Global server, device client, local conn"
+
+ log_start
+ run_cmd nettest -6 -D -d ${VRF} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, device client, local conn"
+
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server, VRF client, local conn"
+
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server, device client, local conn"
+
+ # disable global server
+ log_subsection "Global server enabled"
+ set_sysctl net.ipv4.udp_l3mdev_accept=1
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -D -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 0 "Global server"
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -D -d ${VRF} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 0 "VRF server"
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 0 "Enslaved device server"
+ done
+
+ # negative test - should fail
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd_nsb nettest -6 -D -r ${a}
+ log_test_addr ${a} $? 1 "No server"
+ done
+
+ #
+ # client tests
+ #
+ log_start
+ run_cmd_nsb nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -d ${VRF} -r ${NSB_IP6}
+ log_test $? 0 "VRF client"
+
+ # negative test - should fail
+ log_start
+ run_cmd nettest -6 -D -d ${VRF} -r ${NSB_IP6}
+ log_test $? 1 "No server, VRF client"
+
+ log_start
+ run_cmd_nsb nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${NSB_IP6}
+ log_test $? 0 "Enslaved device client"
+
+ # negative test - should fail
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${NSB_IP6}
+ log_test $? 1 "No server, enslaved device client"
+
+ #
+ # local address tests
+ #
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -D -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "Global server, VRF client, local conn"
+
+ #log_start
+ run_cmd nettest -6 -D -d ${VRF} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, VRF client, local conn"
+
+
+ a=${VRF_IP6}
+ log_start
+ run_cmd nettest -6 -D -s -2 ${VRF} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "Global server, VRF client, local conn"
+
+ log_start
+ run_cmd nettest -6 -D -d ${VRF} -s -2 ${VRF} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, VRF client, local conn"
+
+ # negative test - should fail
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 1 "No server, VRF client, local conn"
+ done
+
+ # device to global IP
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -D -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "Global server, device client, local conn"
+
+ log_start
+ run_cmd nettest -6 -D -d ${VRF} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "VRF server, device client, local conn"
+
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${VRF} -r ${a}
+ log_test_addr ${a} $? 0 "Device server, VRF client, local conn"
+
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 0 "Device server, device client, local conn"
+
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
+ log_test_addr ${a} $? 1 "No server, device client, local conn"
+
+
+ # link local addresses
+ log_start
+ run_cmd nettest -6 -D -s &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -d ${NSB_DEV} -r ${NSA_LINKIP6}
+ log_test $? 0 "Global server, linklocal IP"
+
+ log_start
+ run_cmd_nsb nettest -6 -D -d ${NSB_DEV} -r ${NSA_LINKIP6}
+ log_test $? 1 "No server, linklocal IP"
+
+
+ log_start
+ run_cmd_nsb nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${NSB_LINKIP6}
+ log_test $? 0 "Enslaved device client, linklocal IP"
+
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${NSB_LINKIP6}
+ log_test $? 1 "No server, device client, peer linklocal IP"
+
+
+ log_start
+ run_cmd nettest -6 -D -s &
+ sleep 1
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${NSA_LINKIP6}
+ log_test $? 0 "Enslaved device client, local conn - linklocal IP"
+
+ log_start
+ run_cmd nettest -6 -D -d ${NSA_DEV} -r ${NSA_LINKIP6}
+ log_test $? 1 "No server, device client, local conn - linklocal IP"
+
+ # LLA to GUA
+ run_cmd_nsb ip -6 addr del ${NSB_IP6}/64 dev ${NSB_DEV}
+ run_cmd_nsb ip -6 ro add ${NSA_IP6}/128 dev ${NSB_DEV}
+ log_start
+ run_cmd nettest -6 -s -D &
+ sleep 1
+ run_cmd_nsb nettest -6 -D -r ${NSA_IP6}
+ log_test $? 0 "UDP in - LLA to GUA"
+
+ run_cmd_nsb ip -6 ro del ${NSA_IP6}/128 dev ${NSB_DEV}
+ run_cmd_nsb ip -6 addr add ${NSB_IP6}/64 dev ${NSB_DEV} nodad
+}
+
+ipv6_udp()
+{
+ # should not matter, but set to known state
+ set_sysctl net.ipv4.udp_early_demux=1
+
+ log_section "IPv6/UDP"
+ log_subsection "No VRF"
+ setup
+
+ # udp_l3mdev_accept should have no affect without VRF;
+ # run tests with it enabled and disabled to verify
+ log_subsection "udp_l3mdev_accept disabled"
+ set_sysctl net.ipv4.udp_l3mdev_accept=0
+ ipv6_udp_novrf
+ log_subsection "udp_l3mdev_accept enabled"
+ set_sysctl net.ipv4.udp_l3mdev_accept=1
+ ipv6_udp_novrf
+
+ log_subsection "With VRF"
+ setup "yes"
+ ipv6_udp_vrf
+}
+
+################################################################################
+# IPv6 address bind
+
+ipv6_addr_bind_novrf()
+{
+ #
+ # raw socket
+ #
+ for a in ${NSA_IP6} ${NSA_LO_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -s -R -P ipv6-icmp -l ${a} -b
+ log_test_addr ${a} $? 0 "Raw socket bind to local address"
+
+ log_start
+ run_cmd nettest -6 -s -R -P ipv6-icmp -l ${a} -d ${NSA_DEV} -b
+ log_test_addr ${a} $? 0 "Raw socket bind to local address after device bind"
+ done
+
+ #
+ # tcp sockets
+ #
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -s -l ${a} -t1 -b
+ log_test_addr ${a} $? 0 "TCP socket bind to local address"
+
+ log_start
+ run_cmd nettest -6 -s -l ${a} -d ${NSA_DEV} -t1 -b
+ log_test_addr ${a} $? 0 "TCP socket bind to local address after device bind"
+
+ a=${NSA_LO_IP6}
+ log_start
+ show_hint "Should fail with 'Cannot assign requested address'"
+ run_cmd nettest -6 -s -l ${a} -d ${NSA_DEV} -t1 -b
+ log_test_addr ${a} $? 1 "TCP socket bind to out of scope local address"
+}
+
+ipv6_addr_bind_vrf()
+{
+ #
+ # raw socket
+ #
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -s -R -P ipv6-icmp -l ${a} -d ${VRF} -b
+ log_test_addr ${a} $? 0 "Raw socket bind to local address after vrf bind"
+
+ log_start
+ run_cmd nettest -6 -s -R -P ipv6-icmp -l ${a} -d ${NSA_DEV} -b
+ log_test_addr ${a} $? 0 "Raw socket bind to local address after device bind"
+ done
+
+ a=${NSA_LO_IP6}
+ log_start
+ show_hint "Address on loopback is out of VRF scope"
+ run_cmd nettest -6 -s -R -P ipv6-icmp -l ${a} -d ${VRF} -b
+ log_test_addr ${a} $? 1 "Raw socket bind to invalid local address after vrf bind"
+
+ #
+ # tcp sockets
+ #
+ # address on enslaved device is valid for the VRF or device in a VRF
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -s -l ${a} -d ${VRF} -t1 -b
+ log_test_addr ${a} $? 0 "TCP socket bind to local address with VRF bind"
+ done
+
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest -6 -s -l ${a} -d ${NSA_DEV} -t1 -b
+ log_test_addr ${a} $? 0 "TCP socket bind to local address with device bind"
+
+ a=${VRF_IP6}
+ log_start
+ run_cmd nettest -6 -s -l ${a} -d ${NSA_DEV} -t1 -b
+ log_test_addr ${a} $? 1 "TCP socket bind to VRF address with device bind"
+
+ a=${NSA_LO_IP6}
+ log_start
+ show_hint "Address on loopback out of scope for VRF"
+ run_cmd nettest -6 -s -l ${a} -d ${VRF} -t1 -b
+ log_test_addr ${a} $? 1 "TCP socket bind to invalid local address for VRF"
+
+ log_start
+ show_hint "Address on loopback out of scope for device in VRF"
+ run_cmd nettest -6 -s -l ${a} -d ${NSA_DEV} -t1 -b
+ log_test_addr ${a} $? 1 "TCP socket bind to invalid local address for device bind"
+
+}
+
+ipv6_addr_bind()
+{
+ log_section "IPv6 address binds"
+
+ log_subsection "No VRF"
+ setup
+ ipv6_addr_bind_novrf
+
+ log_subsection "With VRF"
+ setup "yes"
+ ipv6_addr_bind_vrf
+}
+
+################################################################################
+# IPv6 runtime tests
+
+ipv6_rt()
+{
+ local desc="$1"
+ local varg="-6 $2"
+ local with_vrf="yes"
+ local a
+
+ #
+ # server tests
+ #
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest ${varg} -s &
+ sleep 1
+ run_cmd_nsb nettest ${varg} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, global server"
+
+ setup ${with_vrf}
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest ${varg} -d ${VRF} -s &
+ sleep 1
+ run_cmd_nsb nettest ${varg} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, VRF server"
+
+ setup ${with_vrf}
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest ${varg} -d ${NSA_DEV} -s &
+ sleep 1
+ run_cmd_nsb nettest ${varg} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, enslaved device server"
+
+ setup ${with_vrf}
+ done
+
+ #
+ # client test
+ #
+ log_start
+ run_cmd_nsb nettest ${varg} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${VRF} -r ${NSB_IP6} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test 0 0 "${desc}, VRF client"
+
+ setup ${with_vrf}
+
+ log_start
+ run_cmd_nsb nettest ${varg} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${NSA_DEV} -r ${NSB_IP6} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test 0 0 "${desc}, enslaved device client"
+
+ setup ${with_vrf}
+
+
+ #
+ # local address tests
+ #
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest ${varg} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${VRF} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, global server, VRF client"
+
+ setup ${with_vrf}
+ done
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest ${varg} -d ${VRF} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${VRF} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, VRF server and client"
+
+ setup ${with_vrf}
+ done
+
+ a=${NSA_IP6}
+ log_start
+ run_cmd nettest ${varg} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${NSA_DEV} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, global server, device client"
+
+ setup ${with_vrf}
+
+ log_start
+ run_cmd nettest ${varg} -d ${VRF} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${NSA_DEV} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, VRF server, device client"
+
+ setup ${with_vrf}
+
+ log_start
+ run_cmd nettest ${varg} -d ${NSA_DEV} -s &
+ sleep 1
+ run_cmd nettest ${varg} -d ${NSA_DEV} -r ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "${desc}, device server, device client"
+}
+
+ipv6_ping_rt()
+{
+ local with_vrf="yes"
+ local a
+
+ a=${NSA_IP6}
+ log_start
+ run_cmd_nsb ${ping6} -f ${a} &
+ sleep 3
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "Device delete with active traffic - ping in"
+
+ setup ${with_vrf}
+
+ log_start
+ run_cmd ${ping6} -f ${NSB_IP6} -I ${VRF} &
+ sleep 1
+ run_cmd ip link del ${VRF}
+ sleep 1
+ log_test_addr ${a} 0 0 "Device delete with active traffic - ping out"
+}
+
+ipv6_runtime()
+{
+ log_section "Run time tests - ipv6"
+
+ setup "yes"
+ ipv6_ping_rt
+
+ setup "yes"
+ ipv6_rt "TCP active socket" "-n -1"
+
+ setup "yes"
+ ipv6_rt "TCP passive socket" "-i"
+
+ setup "yes"
+ ipv6_rt "UDP active socket" "-D -n -1"
+}
+
+################################################################################
+# netfilter blocking connections
+
+netfilter_tcp_reset()
+{
+ local a
+
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd nettest -s &
+ sleep 1
+ run_cmd_nsb nettest -r ${a}
+ log_test_addr ${a} $? 1 "Global server, reject with TCP-reset on Rx"
+ done
+}
+
+netfilter_icmp()
+{
+ local stype="$1"
+ local arg
+ local a
+
+ [ "${stype}" = "UDP" ] && arg="-D"
+
+ for a in ${NSA_IP} ${VRF_IP}
+ do
+ log_start
+ run_cmd nettest ${arg} -s &
+ sleep 1
+ run_cmd_nsb nettest ${arg} -r ${a}
+ log_test_addr ${a} $? 1 "Global ${stype} server, Rx reject icmp-port-unreach"
+ done
+}
+
+ipv4_netfilter()
+{
+ log_section "IPv4 Netfilter"
+ log_subsection "TCP reset"
+
+ setup "yes"
+ run_cmd iptables -A INPUT -p tcp --dport 12345 -j REJECT --reject-with tcp-reset
+
+ netfilter_tcp_reset
+
+ log_start
+ log_subsection "ICMP unreachable"
+
+ log_start
+ run_cmd iptables -F
+ run_cmd iptables -A INPUT -p tcp --dport 12345 -j REJECT --reject-with icmp-port-unreachable
+ run_cmd iptables -A INPUT -p udp --dport 12345 -j REJECT --reject-with icmp-port-unreachable
+
+ netfilter_icmp "TCP"
+ netfilter_icmp "UDP"
+
+ log_start
+ iptables -F
+}
+
+netfilter_tcp6_reset()
+{
+ local a
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -s &
+ sleep 1
+ run_cmd_nsb nettest -6 -r ${a}
+ log_test_addr ${a} $? 1 "Global server, reject with TCP-reset on Rx"
+ done
+}
+
+netfilter_icmp6()
+{
+ local stype="$1"
+ local arg
+ local a
+
+ [ "${stype}" = "UDP" ] && arg="$arg -D"
+
+ for a in ${NSA_IP6} ${VRF_IP6}
+ do
+ log_start
+ run_cmd nettest -6 -s ${arg} &
+ sleep 1
+ run_cmd_nsb nettest -6 ${arg} -r ${a}
+ log_test_addr ${a} $? 1 "Global ${stype} server, Rx reject icmp-port-unreach"
+ done
+}
+
+ipv6_netfilter()
+{
+ log_section "IPv6 Netfilter"
+ log_subsection "TCP reset"
+
+ setup "yes"
+ run_cmd ip6tables -A INPUT -p tcp --dport 12345 -j REJECT --reject-with tcp-reset
+
+ netfilter_tcp6_reset
+
+ log_subsection "ICMP unreachable"
+
+ log_start
+ run_cmd ip6tables -F
+ run_cmd ip6tables -A INPUT -p tcp --dport 12345 -j REJECT --reject-with icmp6-port-unreachable
+ run_cmd ip6tables -A INPUT -p udp --dport 12345 -j REJECT --reject-with icmp6-port-unreachable
+
+ netfilter_icmp6 "TCP"
+ netfilter_icmp6 "UDP"
+
+ log_start
+ ip6tables -F
+}
+
+################################################################################
+# specific use cases
+
+# VRF only.
+# ns-A device enslaved to bridge. Verify traffic with and without
+# br_netfilter module loaded. Repeat with SVI on bridge.
+use_case_br()
+{
+ setup "yes"
+
+ setup_cmd ip link set ${NSA_DEV} down
+ setup_cmd ip addr del dev ${NSA_DEV} ${NSA_IP}/24
+ setup_cmd ip -6 addr del dev ${NSA_DEV} ${NSA_IP6}/64
+
+ setup_cmd ip link add br0 type bridge
+ setup_cmd ip addr add dev br0 ${NSA_IP}/24
+ setup_cmd ip -6 addr add dev br0 ${NSA_IP6}/64 nodad
+
+ setup_cmd ip li set ${NSA_DEV} master br0
+ setup_cmd ip li set ${NSA_DEV} up
+ setup_cmd ip li set br0 up
+ setup_cmd ip li set br0 vrf ${VRF}
+
+ rmmod br_netfilter 2>/dev/null
+ sleep 5 # DAD
+
+ run_cmd ip neigh flush all
+ run_cmd ping -c1 -w1 -I br0 ${NSB_IP}
+ log_test $? 0 "Bridge into VRF - IPv4 ping out"
+
+ run_cmd ip neigh flush all
+ run_cmd ${ping6} -c1 -w1 -I br0 ${NSB_IP6}
+ log_test $? 0 "Bridge into VRF - IPv6 ping out"
+
+ run_cmd ip neigh flush all
+ run_cmd_nsb ping -c1 -w1 ${NSA_IP}
+ log_test $? 0 "Bridge into VRF - IPv4 ping in"
+
+ run_cmd ip neigh flush all
+ run_cmd_nsb ${ping6} -c1 -w1 ${NSA_IP6}
+ log_test $? 0 "Bridge into VRF - IPv6 ping in"
+
+ modprobe br_netfilter
+ if [ $? -eq 0 ]; then
+ run_cmd ip neigh flush all
+ run_cmd ping -c1 -w1 -I br0 ${NSB_IP}
+ log_test $? 0 "Bridge into VRF with br_netfilter - IPv4 ping out"
+
+ run_cmd ip neigh flush all
+ run_cmd ${ping6} -c1 -w1 -I br0 ${NSB_IP6}
+ log_test $? 0 "Bridge into VRF with br_netfilter - IPv6 ping out"
+
+ run_cmd ip neigh flush all
+ run_cmd_nsb ping -c1 -w1 ${NSA_IP}
+ log_test $? 0 "Bridge into VRF with br_netfilter - IPv4 ping in"
+
+ run_cmd ip neigh flush all
+ run_cmd_nsb ${ping6} -c1 -w1 ${NSA_IP6}
+ log_test $? 0 "Bridge into VRF with br_netfilter - IPv6 ping in"
+ fi
+
+ setup_cmd ip li set br0 nomaster
+ setup_cmd ip li add br0.100 link br0 type vlan id 100
+ setup_cmd ip li set br0.100 vrf ${VRF} up
+ setup_cmd ip addr add dev br0.100 172.16.101.1/24
+ setup_cmd ip -6 addr add dev br0.100 2001:db8:101::1/64 nodad
+
+ setup_cmd_nsb ip li add vlan100 link ${NSB_DEV} type vlan id 100
+ setup_cmd_nsb ip addr add dev vlan100 172.16.101.2/24
+ setup_cmd_nsb ip -6 addr add dev vlan100 2001:db8:101::2/64 nodad
+ setup_cmd_nsb ip li set vlan100 up
+ sleep 1
+
+ rmmod br_netfilter 2>/dev/null
+
+ run_cmd ip neigh flush all
+ run_cmd ping -c1 -w1 -I br0.100 172.16.101.2
+ log_test $? 0 "Bridge vlan into VRF - IPv4 ping out"
+
+ run_cmd ip neigh flush all
+ run_cmd ${ping6} -c1 -w1 -I br0.100 2001:db8:101::2
+ log_test $? 0 "Bridge vlan into VRF - IPv6 ping out"
+
+ run_cmd ip neigh flush all
+ run_cmd_nsb ping -c1 -w1 172.16.101.1
+ log_test $? 0 "Bridge vlan into VRF - IPv4 ping in"
+
+ run_cmd ip neigh flush all
+ run_cmd_nsb ${ping6} -c1 -w1 2001:db8:101::1
+ log_test $? 0 "Bridge vlan into VRF - IPv6 ping in"
+
+ modprobe br_netfilter
+ if [ $? -eq 0 ]; then
+ run_cmd ip neigh flush all
+ run_cmd ping -c1 -w1 -I br0.100 172.16.101.2
+ log_test $? 0 "Bridge vlan into VRF with br_netfilter - IPv4 ping out"
+
+ run_cmd ip neigh flush all
+ run_cmd ${ping6} -c1 -w1 -I br0.100 2001:db8:101::2
+ log_test $? 0 "Bridge vlan into VRF with br_netfilter - IPv6 ping out"
+
+ run_cmd ip neigh flush all
+ run_cmd_nsb ping -c1 -w1 172.16.101.1
+ log_test $? 0 "Bridge vlan into VRF - IPv4 ping in"
+
+ run_cmd ip neigh flush all
+ run_cmd_nsb ${ping6} -c1 -w1 2001:db8:101::1
+ log_test $? 0 "Bridge vlan into VRF - IPv6 ping in"
+ fi
+
+ setup_cmd ip li del br0 2>/dev/null
+ setup_cmd_nsb ip li del vlan100 2>/dev/null
+}
+
+use_cases()
+{
+ log_section "Use cases"
+ use_case_br
+}
+
+################################################################################
+# usage
+
+usage()
+{
+ cat <<EOF
+usage: ${0##*/} OPTS
+
+ -4 IPv4 tests only
+ -6 IPv6 tests only
+ -t <test> Test name/set to run
+ -p Pause on fail
+ -P Pause after each test
+ -v Be verbose
+EOF
+}
+
+################################################################################
+# main
+
+TESTS_IPV4="ipv4_ping ipv4_tcp ipv4_udp ipv4_addr_bind ipv4_runtime ipv4_netfilter"
+TESTS_IPV6="ipv6_ping ipv6_tcp ipv6_udp ipv6_addr_bind ipv6_runtime ipv6_netfilter"
+TESTS_OTHER="use_cases"
+
+PAUSE_ON_FAIL=no
+PAUSE=no
+
+while getopts :46t:pPvh o
+do
+ case $o in
+ 4) TESTS=ipv4;;
+ 6) TESTS=ipv6;;
+ t) TESTS=$OPTARG;;
+ p) PAUSE_ON_FAIL=yes;;
+ P) PAUSE=yes;;
+ v) VERBOSE=1;;
+ h) usage; exit 0;;
+ *) usage; exit 1;;
+ esac
+done
+
+# make sure we don't pause twice
+[ "${PAUSE}" = "yes" ] && PAUSE_ON_FAIL=no
+
+#
+# show user test config
+#
+if [ -z "$TESTS" ]; then
+ TESTS="$TESTS_IPV4 $TESTS_IPV6 $TESTS_OTHER"
+elif [ "$TESTS" = "ipv4" ]; then
+ TESTS="$TESTS_IPV4"
+elif [ "$TESTS" = "ipv6" ]; then
+ TESTS="$TESTS_IPV6"
+fi
+
+which nettest >/dev/null
+if [ $? -ne 0 ]; then
+ echo "'nettest' command not found; skipping tests"
+ exit 0
+fi
+
+declare -i nfail=0
+declare -i nsuccess=0
+
+for t in $TESTS
+do
+ case $t in
+ ipv4_ping|ping) ipv4_ping;;
+ ipv4_tcp|tcp) ipv4_tcp;;
+ ipv4_udp|udp) ipv4_udp;;
+ ipv4_bind|bind) ipv4_addr_bind;;
+ ipv4_runtime) ipv4_runtime;;
+ ipv4_netfilter) ipv4_netfilter;;
+
+ ipv6_ping|ping6) ipv6_ping;;
+ ipv6_tcp|tcp6) ipv6_tcp;;
+ ipv6_udp|udp6) ipv6_udp;;
+ ipv6_bind|bind6) ipv6_addr_bind;;
+ ipv6_runtime) ipv6_runtime;;
+ ipv6_netfilter) ipv6_netfilter;;
+
+ use_cases) use_cases;;
+
+ # setup namespaces and config, but do not run any tests
+ setup) setup; exit 0;;
+ vrf_setup) setup "yes"; exit 0;;
+
+ help) echo "Test names: $TESTS"; exit 0;;
+ esac
+done
+
+cleanup 2>/dev/null
+
+printf "\nTests passed: %3d\n" ${nsuccess}
+printf "Tests failed: %3d\n" ${nfail}
diff --git a/tools/testing/selftests/net/fib-onlink-tests.sh b/tools/testing/selftests/net/fib-onlink-tests.sh
index 864f865eee55..c287b90b8af8 100755
--- a/tools/testing/selftests/net/fib-onlink-tests.sh
+++ b/tools/testing/selftests/net/fib-onlink-tests.sh
@@ -4,6 +4,7 @@
# IPv4 and IPv6 onlink tests
PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
+VERBOSE=0
# Network interfaces
# - odd in current namespace; even in peer ns
@@ -91,10 +92,10 @@ log_test()
if [ ${rc} -eq ${expected} ]; then
nsuccess=$((nsuccess+1))
- printf "\n TEST: %-50s [ OK ]\n" "${msg}"
+ printf " TEST: %-50s [ OK ]\n" "${msg}"
else
nfail=$((nfail+1))
- printf "\n TEST: %-50s [FAIL]\n" "${msg}"
+ printf " TEST: %-50s [FAIL]\n" "${msg}"
if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
echo
echo "hit enter to continue, 'q' to quit"
@@ -121,9 +122,23 @@ log_subsection()
run_cmd()
{
- echo
- echo "COMMAND: $*"
- eval $*
+ local cmd="$*"
+ local out
+ local rc
+
+ if [ "$VERBOSE" = "1" ]; then
+ printf " COMMAND: $cmd\n"
+ fi
+
+ out=$(eval $cmd 2>&1)
+ rc=$?
+ if [ "$VERBOSE" = "1" -a -n "$out" ]; then
+ echo " $out"
+ fi
+
+ [ "$VERBOSE" = "1" ] && echo
+
+ return $rc
}
get_linklocal()
@@ -451,11 +466,34 @@ run_onlink_tests()
}
################################################################################
+# usage
+
+usage()
+{
+ cat <<EOF
+usage: ${0##*/} OPTS
+
+ -p Pause on fail
+ -v verbose mode (show commands and output)
+EOF
+}
+
+################################################################################
# main
nsuccess=0
nfail=0
+while getopts :t:pPhv o
+do
+ case $o in
+ p) PAUSE_ON_FAIL=yes;;
+ v) VERBOSE=$(($VERBOSE + 1));;
+ h) usage; exit 0;;
+ *) usage; exit 1;;
+ esac
+done
+
cleanup
setup
run_onlink_tests
diff --git a/tools/testing/selftests/net/fib_nexthop_multiprefix.sh b/tools/testing/selftests/net/fib_nexthop_multiprefix.sh
new file mode 100755
index 000000000000..e6828732843e
--- /dev/null
+++ b/tools/testing/selftests/net/fib_nexthop_multiprefix.sh
@@ -0,0 +1,290 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Validate cached routes in fib{6}_nh that is used by multiple prefixes.
+# Validate a different # exception is generated in h0 for each remote host.
+#
+# h1
+# /
+# h0 - r1 - h2
+# \
+# h3
+#
+# routing in h0 to hN is done with nexthop objects.
+
+PAUSE_ON_FAIL=no
+VERBOSE=0
+
+################################################################################
+# helpers
+
+log_test()
+{
+ local rc=$1
+ local expected=$2
+ local msg="$3"
+
+ if [ ${rc} -eq ${expected} ]; then
+ printf "TEST: %-60s [ OK ]\n" "${msg}"
+ nsuccess=$((nsuccess+1))
+ else
+ ret=1
+ nfail=$((nfail+1))
+ printf "TEST: %-60s [FAIL]\n" "${msg}"
+ if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
+ echo
+ echo "hit enter to continue, 'q' to quit"
+ read a
+ [ "$a" = "q" ] && exit 1
+ fi
+ fi
+
+ [ "$VERBOSE" = "1" ] && echo
+}
+
+run_cmd()
+{
+ local cmd="$*"
+ local out
+ local rc
+
+ if [ "$VERBOSE" = "1" ]; then
+ echo "COMMAND: $cmd"
+ fi
+
+ out=$(eval $cmd 2>&1)
+ rc=$?
+ if [ "$VERBOSE" = "1" -a -n "$out" ]; then
+ echo "$out"
+ fi
+
+ [ "$VERBOSE" = "1" ] && echo
+
+ return $rc
+}
+
+################################################################################
+# config
+
+create_ns()
+{
+ local ns=${1}
+
+ ip netns del ${ns} 2>/dev/null
+
+ ip netns add ${ns}
+ ip -netns ${ns} addr add 127.0.0.1/8 dev lo
+ ip -netns ${ns} link set lo up
+
+ ip netns exec ${ns} sysctl -q -w net.ipv6.conf.all.keep_addr_on_down=1
+ case ${ns} in
+ h*)
+ ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=0
+ ;;
+ r*)
+ ip netns exec $ns sysctl -q -w net.ipv4.ip_forward=1
+ ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=1
+ ;;
+ esac
+}
+
+setup()
+{
+ local ns
+ local i
+
+ #set -e
+
+ for ns in h0 r1 h1 h2 h3
+ do
+ create_ns ${ns}
+ done
+
+ #
+ # create interconnects
+ #
+
+ for i in 0 1 2 3
+ do
+ ip -netns h${i} li add eth0 type veth peer name r1h${i}
+ ip -netns h${i} li set eth0 up
+ ip -netns h${i} li set r1h${i} netns r1 name eth${i} up
+
+ ip -netns h${i} addr add dev eth0 172.16.10${i}.1/24
+ ip -netns h${i} -6 addr add dev eth0 2001:db8:10${i}::1/64
+ ip -netns r1 addr add dev eth${i} 172.16.10${i}.254/24
+ ip -netns r1 -6 addr add dev eth${i} 2001:db8:10${i}::64/64
+ done
+
+ ip -netns h0 nexthop add id 4 via 172.16.100.254 dev eth0
+ ip -netns h0 nexthop add id 6 via 2001:db8:100::64 dev eth0
+
+ # routing from h0 to h1-h3 and back
+ for i in 1 2 3
+ do
+ ip -netns h0 ro add 172.16.10${i}.0/24 nhid 4
+ ip -netns h${i} ro add 172.16.100.0/24 via 172.16.10${i}.254
+
+ ip -netns h0 -6 ro add 2001:db8:10${i}::/64 nhid 6
+ ip -netns h${i} -6 ro add 2001:db8:100::/64 via 2001:db8:10${i}::64
+ done
+
+ if [ "$VERBOSE" = "1" ]; then
+ echo
+ echo "host 1 config"
+ ip -netns h0 li sh
+ ip -netns h0 ro sh
+ ip -netns h0 -6 ro sh
+ fi
+
+ #set +e
+}
+
+cleanup()
+{
+ for n in h1 r1 h2 h3 h4
+ do
+ ip netns del ${n} 2>/dev/null
+ done
+}
+
+change_mtu()
+{
+ local hostid=$1
+ local mtu=$2
+
+ run_cmd ip -netns h${hostid} li set eth0 mtu ${mtu}
+ run_cmd ip -netns r1 li set eth${hostid} mtu ${mtu}
+}
+
+################################################################################
+# validate exceptions
+
+validate_v4_exception()
+{
+ local i=$1
+ local mtu=$2
+ local ping_sz=$3
+ local dst="172.16.10${i}.1"
+ local h0=172.16.100.1
+ local r1=172.16.100.254
+ local rc
+
+ if [ ${ping_sz} != "0" ]; then
+ run_cmd ip netns exec h0 ping -s ${ping_sz} -c5 -w5 ${dst}
+ fi
+
+ if [ "$VERBOSE" = "1" ]; then
+ echo "Route get"
+ ip -netns h0 ro get ${dst}
+ echo "Searching for:"
+ echo " cache .* mtu ${mtu}"
+ echo
+ fi
+
+ ip -netns h0 ro get ${dst} | \
+ grep -q "cache .* mtu ${mtu}"
+ rc=$?
+
+ log_test $rc 0 "IPv4: host 0 to host ${i}, mtu ${mtu}"
+}
+
+validate_v6_exception()
+{
+ local i=$1
+ local mtu=$2
+ local ping_sz=$3
+ local dst="2001:db8:10${i}::1"
+ local h0=2001:db8:100::1
+ local r1=2001:db8:100::64
+ local rc
+
+ if [ ${ping_sz} != "0" ]; then
+ run_cmd ip netns exec h0 ping6 -s ${ping_sz} -c5 -w5 ${dst}
+ fi
+
+ if [ "$VERBOSE" = "1" ]; then
+ echo "Route get"
+ ip -netns h0 -6 ro get ${dst}
+ echo "Searching for:"
+ echo " ${dst} from :: via ${r1} dev eth0 src ${h0} .* mtu ${mtu}"
+ echo
+ fi
+
+ ip -netns h0 -6 ro get ${dst} | \
+ grep -q "${dst} from :: via ${r1} dev eth0 src ${h0} .* mtu ${mtu}"
+ rc=$?
+
+ log_test $rc 0 "IPv6: host 0 to host ${i}, mtu ${mtu}"
+}
+
+################################################################################
+# main
+
+while getopts :pv o
+do
+ case $o in
+ p) PAUSE_ON_FAIL=yes;;
+ v) VERBOSE=1;;
+ esac
+done
+
+cleanup
+setup
+sleep 2
+
+cpus=$(cat /sys/devices/system/cpu/online)
+cpus="$(seq ${cpus/-/ })"
+ret=0
+for i in 1 2 3
+do
+ # generate a cached route per-cpu
+ for c in ${cpus}; do
+ run_cmd taskset -c ${c} ip netns exec h0 ping -c1 -w1 172.16.10${i}.1
+ [ $? -ne 0 ] && printf "\nERROR: ping to h${i} failed\n" && ret=1
+
+ run_cmd taskset -c ${c} ip netns exec h0 ping6 -c1 -w1 2001:db8:10${i}::1
+ [ $? -ne 0 ] && printf "\nERROR: ping6 to h${i} failed\n" && ret=1
+
+ [ $ret -ne 0 ] && break
+ done
+ [ $ret -ne 0 ] && break
+done
+
+if [ $ret -eq 0 ]; then
+ # generate different exceptions in h0 for h1, h2 and h3
+ change_mtu 1 1300
+ validate_v4_exception 1 1300 1350
+ validate_v6_exception 1 1300 1350
+ echo
+
+ change_mtu 2 1350
+ validate_v4_exception 2 1350 1400
+ validate_v6_exception 2 1350 1400
+ echo
+
+ change_mtu 3 1400
+ validate_v4_exception 3 1400 1450
+ validate_v6_exception 3 1400 1450
+ echo
+
+ validate_v4_exception 1 1300 0
+ validate_v6_exception 1 1300 0
+ echo
+
+ validate_v4_exception 2 1350 0
+ validate_v6_exception 2 1350 0
+ echo
+
+ validate_v4_exception 3 1400 0
+ validate_v6_exception 3 1400 0
+
+ # targeted deletes to trigger cleanup paths in kernel
+ ip -netns h0 ro del 172.16.102.0/24 nhid 4
+ ip -netns h0 -6 ro del 2001:db8:102::/64 nhid 6
+
+ ip -netns h0 nexthop del id 4
+ ip -netns h0 nexthop del id 6
+fi
+
+cleanup
diff --git a/tools/testing/selftests/net/fib_nexthops.sh b/tools/testing/selftests/net/fib_nexthops.sh
new file mode 100755
index 000000000000..f9ebeac1e6f2
--- /dev/null
+++ b/tools/testing/selftests/net/fib_nexthops.sh
@@ -0,0 +1,1028 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# ns: me | ns: peer | ns: remote
+# 2001:db8:91::1 | 2001:db8:91::2 |
+# 172.16.1.1 | 172.16.1.2 |
+# veth1 <---|---> veth2 |
+# | veth5 <--|--> veth6 172.16.101.1
+# veth3 <---|---> veth4 | 2001:db8:101::1
+# 172.16.2.1 | 172.16.2.2 |
+# 2001:db8:92::1 | 2001:db8:92::2 |
+#
+# This test is for checking IPv4 and IPv6 FIB behavior with nexthop
+# objects. Device reference counts and network namespace cleanup tested
+# by use of network namespace for peer.
+
+ret=0
+# Kselftest framework requirement - SKIP code is 4.
+ksft_skip=4
+
+# all tests in this script. Can be overridden with -t option
+IPV4_TESTS="ipv4_fcnal ipv4_grp_fcnal ipv4_withv6_fcnal ipv4_fcnal_runtime"
+IPV6_TESTS="ipv6_fcnal ipv6_grp_fcnal ipv6_fcnal_runtime"
+
+ALL_TESTS="basic ${IPV4_TESTS} ${IPV6_TESTS}"
+TESTS="${ALL_TESTS}"
+VERBOSE=0
+PAUSE_ON_FAIL=no
+PAUSE=no
+
+nsid=100
+
+################################################################################
+# utilities
+
+log_test()
+{
+ local rc=$1
+ local expected=$2
+ local msg="$3"
+
+ if [ ${rc} -eq ${expected} ]; then
+ printf "TEST: %-60s [ OK ]\n" "${msg}"
+ nsuccess=$((nsuccess+1))
+ else
+ ret=1
+ nfail=$((nfail+1))
+ printf "TEST: %-60s [FAIL]\n" "${msg}"
+ if [ "$VERBOSE" = "1" ]; then
+ echo " rc=$rc, expected $expected"
+ fi
+
+ if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
+ echo
+ echo "hit enter to continue, 'q' to quit"
+ read a
+ [ "$a" = "q" ] && exit 1
+ fi
+ fi
+
+ if [ "${PAUSE}" = "yes" ]; then
+ echo
+ echo "hit enter to continue, 'q' to quit"
+ read a
+ [ "$a" = "q" ] && exit 1
+ fi
+
+ [ "$VERBOSE" = "1" ] && echo
+}
+
+run_cmd()
+{
+ local cmd="$1"
+ local out
+ local stderr="2>/dev/null"
+
+ if [ "$VERBOSE" = "1" ]; then
+ printf "COMMAND: $cmd\n"
+ stderr=
+ fi
+
+ out=$(eval $cmd $stderr)
+ rc=$?
+ if [ "$VERBOSE" = "1" -a -n "$out" ]; then
+ echo " $out"
+ fi
+
+ return $rc
+}
+
+get_linklocal()
+{
+ local dev=$1
+ local ns
+ local addr
+
+ [ -n "$2" ] && ns="-netns $2"
+ addr=$(ip $ns -6 -br addr show dev ${dev} | \
+ awk '{
+ for (i = 3; i <= NF; ++i) {
+ if ($i ~ /^fe80/)
+ print $i
+ }
+ }'
+ )
+ addr=${addr/\/*}
+
+ [ -z "$addr" ] && return 1
+
+ echo $addr
+
+ return 0
+}
+
+create_ns()
+{
+ local n=${1}
+
+ ip netns del ${n} 2>/dev/null
+
+ set -e
+ ip netns add ${n}
+ ip netns set ${n} $((nsid++))
+ ip -netns ${n} addr add 127.0.0.1/8 dev lo
+ ip -netns ${n} link set lo up
+
+ ip netns exec ${n} sysctl -qw net.ipv4.ip_forward=1
+ ip netns exec ${n} sysctl -qw net.ipv4.fib_multipath_use_neigh=1
+ ip netns exec ${n} sysctl -qw net.ipv4.conf.default.ignore_routes_with_linkdown=1
+ ip netns exec ${n} sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1
+ ip netns exec ${n} sysctl -qw net.ipv6.conf.all.forwarding=1
+ ip netns exec ${n} sysctl -qw net.ipv6.conf.default.forwarding=1
+ ip netns exec ${n} sysctl -qw net.ipv6.conf.default.ignore_routes_with_linkdown=1
+ ip netns exec ${n} sysctl -qw net.ipv6.conf.all.accept_dad=0
+ ip netns exec ${n} sysctl -qw net.ipv6.conf.default.accept_dad=0
+
+ set +e
+}
+
+setup()
+{
+ cleanup
+
+ create_ns me
+ create_ns peer
+ create_ns remote
+
+ IP="ip -netns me"
+ set -e
+ $IP li add veth1 type veth peer name veth2
+ $IP li set veth1 up
+ $IP addr add 172.16.1.1/24 dev veth1
+ $IP -6 addr add 2001:db8:91::1/64 dev veth1
+
+ $IP li add veth3 type veth peer name veth4
+ $IP li set veth3 up
+ $IP addr add 172.16.2.1/24 dev veth3
+ $IP -6 addr add 2001:db8:92::1/64 dev veth3
+
+ $IP li set veth2 netns peer up
+ ip -netns peer addr add 172.16.1.2/24 dev veth2
+ ip -netns peer -6 addr add 2001:db8:91::2/64 dev veth2
+
+ $IP li set veth4 netns peer up
+ ip -netns peer addr add 172.16.2.2/24 dev veth4
+ ip -netns peer -6 addr add 2001:db8:92::2/64 dev veth4
+
+ ip -netns remote li add veth5 type veth peer name veth6
+ ip -netns remote li set veth5 up
+ ip -netns remote addr add dev veth5 172.16.101.1/24
+ ip -netns remote addr add dev veth5 2001:db8:101::1/64
+ ip -netns remote ro add 172.16.0.0/22 via 172.16.101.2
+ ip -netns remote -6 ro add 2001:db8:90::/40 via 2001:db8:101::2
+
+ ip -netns remote li set veth6 netns peer up
+ ip -netns peer addr add dev veth6 172.16.101.2/24
+ ip -netns peer addr add dev veth6 2001:db8:101::2/64
+ set +e
+}
+
+cleanup()
+{
+ local ns
+
+ for ns in me peer remote; do
+ ip netns del ${ns} 2>/dev/null
+ done
+}
+
+check_output()
+{
+ local out="$1"
+ local expected="$2"
+ local rc=0
+
+ [ "${out}" = "${expected}" ] && return 0
+
+ if [ -z "${out}" ]; then
+ if [ "$VERBOSE" = "1" ]; then
+ printf "\nNo entry found\n"
+ printf "Expected:\n"
+ printf " ${expected}\n"
+ fi
+ return 1
+ fi
+
+ out=$(echo ${out})
+ if [ "${out}" != "${expected}" ]; then
+ rc=1
+ if [ "${VERBOSE}" = "1" ]; then
+ printf " Unexpected entry. Have:\n"
+ printf " ${out}\n"
+ printf " Expected:\n"
+ printf " ${expected}\n\n"
+ else
+ echo " WARNING: Unexpected route entry"
+ fi
+ fi
+
+ return $rc
+}
+
+check_nexthop()
+{
+ local nharg="$1"
+ local expected="$2"
+ local out
+
+ out=$($IP nexthop ls ${nharg} 2>/dev/null)
+
+ check_output "${out}" "${expected}"
+}
+
+check_route()
+{
+ local pfx="$1"
+ local expected="$2"
+ local out
+
+ out=$($IP route ls match ${pfx} 2>/dev/null)
+
+ check_output "${out}" "${expected}"
+}
+
+check_route6()
+{
+ local pfx="$1"
+ local expected="$2"
+ local out
+
+ out=$($IP -6 route ls match ${pfx} 2>/dev/null)
+
+ check_output "${out}" "${expected}"
+}
+
+################################################################################
+# basic operations (add, delete, replace) on nexthops and nexthop groups
+#
+# IPv6
+
+ipv6_fcnal()
+{
+ local rc
+
+ echo
+ echo "IPv6"
+ echo "----------------------"
+
+ run_cmd "$IP nexthop add id 52 via 2001:db8:91::2 dev veth1"
+ rc=$?
+ log_test $rc 0 "Create nexthop with id, gw, dev"
+ if [ $rc -ne 0 ]; then
+ echo "Basic IPv6 create fails; can not continue"
+ return 1
+ fi
+
+ run_cmd "$IP nexthop get id 52"
+ log_test $? 0 "Get nexthop by id"
+ check_nexthop "id 52" "id 52 via 2001:db8:91::2 dev veth1 scope link"
+
+ run_cmd "$IP nexthop del id 52"
+ log_test $? 0 "Delete nexthop by id"
+ check_nexthop "id 52" ""
+
+ #
+ # gw, device spec
+ #
+ # gw validation, no device - fails since dev required
+ run_cmd "$IP nexthop add id 52 via 2001:db8:92::3"
+ log_test $? 2 "Create nexthop - gw only"
+
+ # gw is not reachable throught given dev
+ run_cmd "$IP nexthop add id 53 via 2001:db8:3::3 dev veth1"
+ log_test $? 2 "Create nexthop - invalid gw+dev combination"
+
+ # onlink arg overrides gw+dev lookup
+ run_cmd "$IP nexthop add id 53 via 2001:db8:3::3 dev veth1 onlink"
+ log_test $? 0 "Create nexthop - gw+dev and onlink"
+
+ # admin down should delete nexthops
+ set -e
+ run_cmd "$IP -6 nexthop add id 55 via 2001:db8:91::3 dev veth1"
+ run_cmd "$IP nexthop add id 56 via 2001:db8:91::4 dev veth1"
+ run_cmd "$IP nexthop add id 57 via 2001:db8:91::5 dev veth1"
+ run_cmd "$IP li set dev veth1 down"
+ set +e
+ check_nexthop "dev veth1" ""
+ log_test $? 0 "Nexthops removed on admin down"
+}
+
+ipv6_grp_fcnal()
+{
+ local rc
+
+ echo
+ echo "IPv6 groups functional"
+ echo "----------------------"
+
+ # basic functionality: create a nexthop group, default weight
+ run_cmd "$IP nexthop add id 61 via 2001:db8:91::2 dev veth1"
+ run_cmd "$IP nexthop add id 101 group 61"
+ log_test $? 0 "Create nexthop group with single nexthop"
+
+ # get nexthop group
+ run_cmd "$IP nexthop get id 101"
+ log_test $? 0 "Get nexthop group by id"
+ check_nexthop "id 101" "id 101 group 61"
+
+ # delete nexthop group
+ run_cmd "$IP nexthop del id 101"
+ log_test $? 0 "Delete nexthop group by id"
+ check_nexthop "id 101" ""
+
+ $IP nexthop flush >/dev/null 2>&1
+ check_nexthop "id 101" ""
+
+ #
+ # create group with multiple nexthops - mix of gw and dev only
+ #
+ run_cmd "$IP nexthop add id 62 via 2001:db8:91::2 dev veth1"
+ run_cmd "$IP nexthop add id 63 via 2001:db8:91::3 dev veth1"
+ run_cmd "$IP nexthop add id 64 via 2001:db8:91::4 dev veth1"
+ run_cmd "$IP nexthop add id 65 dev veth1"
+ run_cmd "$IP nexthop add id 102 group 62/63/64/65"
+ log_test $? 0 "Nexthop group with multiple nexthops"
+ check_nexthop "id 102" "id 102 group 62/63/64/65"
+
+ # Delete nexthop in a group and group is updated
+ run_cmd "$IP nexthop del id 63"
+ check_nexthop "id 102" "id 102 group 62/64/65"
+ log_test $? 0 "Nexthop group updated when entry is deleted"
+
+ # create group with multiple weighted nexthops
+ run_cmd "$IP nexthop add id 63 via 2001:db8:91::3 dev veth1"
+ run_cmd "$IP nexthop add id 103 group 62/63,2/64,3/65,4"
+ log_test $? 0 "Nexthop group with weighted nexthops"
+ check_nexthop "id 103" "id 103 group 62/63,2/64,3/65,4"
+
+ # Delete nexthop in a weighted group and group is updated
+ run_cmd "$IP nexthop del id 63"
+ check_nexthop "id 103" "id 103 group 62/64,3/65,4"
+ log_test $? 0 "Weighted nexthop group updated when entry is deleted"
+
+ # admin down - nexthop is removed from group
+ run_cmd "$IP li set dev veth1 down"
+ check_nexthop "dev veth1" ""
+ log_test $? 0 "Nexthops in groups removed on admin down"
+
+ # expect groups to have been deleted as well
+ check_nexthop "" ""
+
+ run_cmd "$IP li set dev veth1 up"
+
+ $IP nexthop flush >/dev/null 2>&1
+
+ # group with nexthops using different devices
+ set -e
+ run_cmd "$IP nexthop add id 62 via 2001:db8:91::2 dev veth1"
+ run_cmd "$IP nexthop add id 63 via 2001:db8:91::3 dev veth1"
+ run_cmd "$IP nexthop add id 64 via 2001:db8:91::4 dev veth1"
+ run_cmd "$IP nexthop add id 65 via 2001:db8:91::5 dev veth1"
+
+ run_cmd "$IP nexthop add id 72 via 2001:db8:92::2 dev veth3"
+ run_cmd "$IP nexthop add id 73 via 2001:db8:92::3 dev veth3"
+ run_cmd "$IP nexthop add id 74 via 2001:db8:92::4 dev veth3"
+ run_cmd "$IP nexthop add id 75 via 2001:db8:92::5 dev veth3"
+ set +e
+
+ # multiple groups with same nexthop
+ run_cmd "$IP nexthop add id 104 group 62"
+ run_cmd "$IP nexthop add id 105 group 62"
+ check_nexthop "group" "id 104 group 62 id 105 group 62"
+ log_test $? 0 "Multiple groups with same nexthop"
+
+ run_cmd "$IP nexthop flush groups"
+ [ $? -ne 0 ] && return 1
+
+ # on admin down of veth1, it should be removed from the group
+ run_cmd "$IP nexthop add id 105 group 62/63/72/73/64"
+ run_cmd "$IP li set veth1 down"
+ check_nexthop "id 105" "id 105 group 72/73"
+ log_test $? 0 "Nexthops in group removed on admin down - mixed group"
+
+ run_cmd "$IP nexthop add id 106 group 105/74"
+ log_test $? 2 "Nexthop group can not have a group as an entry"
+
+ # a group can have a blackhole entry only if it is the only
+ # nexthop in the group. Needed for atomic replace with an
+ # actual nexthop group
+ run_cmd "$IP -6 nexthop add id 31 blackhole"
+ run_cmd "$IP nexthop add id 107 group 31"
+ log_test $? 0 "Nexthop group with a blackhole entry"
+
+ run_cmd "$IP nexthop add id 108 group 31/24"
+ log_test $? 2 "Nexthop group can not have a blackhole and another nexthop"
+}
+
+ipv6_fcnal_runtime()
+{
+ local rc
+
+ echo
+ echo "IPv6 functional runtime"
+ echo "-----------------------"
+
+ sleep 5
+
+ #
+ # IPv6 - the basics
+ #
+ run_cmd "$IP nexthop add id 81 via 2001:db8:91::2 dev veth1"
+ run_cmd "$IP ro add 2001:db8:101::1/128 nhid 81"
+ log_test $? 0 "Route add"
+
+ run_cmd "$IP ro delete 2001:db8:101::1/128 nhid 81"
+ log_test $? 0 "Route delete"
+
+ run_cmd "$IP ro add 2001:db8:101::1/128 nhid 81"
+ run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
+ log_test $? 0 "Ping with nexthop"
+
+ run_cmd "$IP nexthop add id 82 via 2001:db8:92::2 dev veth3"
+ run_cmd "$IP nexthop add id 122 group 81/82"
+ run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 122"
+ run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
+ log_test $? 0 "Ping - multipath"
+
+ #
+ # IPv6 with blackhole nexthops
+ #
+ run_cmd "$IP -6 nexthop add id 83 blackhole"
+ run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 83"
+ run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
+ log_test $? 2 "Ping - blackhole"
+
+ run_cmd "$IP nexthop replace id 83 via 2001:db8:91::2 dev veth1"
+ run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
+ log_test $? 0 "Ping - blackhole replaced with gateway"
+
+ run_cmd "$IP -6 nexthop replace id 83 blackhole"
+ run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
+ log_test $? 2 "Ping - gateway replaced by blackhole"
+
+ run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 122"
+ run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
+ if [ $? -eq 0 ]; then
+ run_cmd "$IP nexthop replace id 122 group 83"
+ run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
+ log_test $? 2 "Ping - group with blackhole"
+
+ run_cmd "$IP nexthop replace id 122 group 81/82"
+ run_cmd "ip netns exec me ping -c1 -w1 2001:db8:101::1"
+ log_test $? 0 "Ping - group blackhole replaced with gateways"
+ else
+ log_test 2 0 "Ping - multipath failed"
+ fi
+
+ #
+ # device only and gw + dev only mix
+ #
+ run_cmd "$IP -6 nexthop add id 85 dev veth1"
+ run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 85"
+ log_test $? 0 "IPv6 route with device only nexthop"
+ check_route6 "2001:db8:101::1" "2001:db8:101::1 nhid 85 dev veth1 metric 1024 pref medium"
+
+ run_cmd "$IP nexthop add id 123 group 81/85"
+ run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 123"
+ log_test $? 0 "IPv6 multipath route with nexthop mix - dev only + gw"
+ check_route6 "2001:db8:101::1" "2001:db8:101::1 nhid 123 metric 1024 nexthop via 2001:db8:91::2 dev veth1 weight 1 nexthop dev veth1 weight 1 pref medium"
+
+ #
+ # IPv6 route with v4 nexthop - not allowed
+ #
+ run_cmd "$IP ro delete 2001:db8:101::1/128"
+ run_cmd "$IP nexthop add id 84 via 172.16.1.1 dev veth1"
+ run_cmd "$IP ro add 2001:db8:101::1/128 nhid 84"
+ log_test $? 2 "IPv6 route can not have a v4 gateway"
+
+ run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 81"
+ run_cmd "$IP nexthop replace id 81 via 172.16.1.1 dev veth1"
+ log_test $? 2 "Nexthop replace - v6 route, v4 nexthop"
+
+ run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 122"
+ run_cmd "$IP nexthop replace id 81 via 172.16.1.1 dev veth1"
+ log_test $? 2 "Nexthop replace of group entry - v6 route, v4 nexthop"
+
+ $IP nexthop flush >/dev/null 2>&1
+
+ #
+ # weird IPv6 cases
+ #
+ run_cmd "$IP nexthop add id 86 via 2001:db8:91::2 dev veth1"
+ run_cmd "$IP ro add 2001:db8:101::1/128 nhid 81"
+
+ # TO-DO:
+ # existing route with old nexthop; append route with new nexthop
+ # existing route with old nexthop; replace route with new
+ # existing route with new nexthop; replace route with old
+ # route with src address and using nexthop - not allowed
+}
+
+ipv4_fcnal()
+{
+ local rc
+
+ echo
+ echo "IPv4 functional"
+ echo "----------------------"
+
+ #
+ # basic IPv4 ops - add, get, delete
+ #
+ run_cmd "$IP nexthop add id 12 via 172.16.1.2 dev veth1"
+ rc=$?
+ log_test $rc 0 "Create nexthop with id, gw, dev"
+ if [ $rc -ne 0 ]; then
+ echo "Basic IPv4 create fails; can not continue"
+ return 1
+ fi
+
+ run_cmd "$IP nexthop get id 12"
+ log_test $? 0 "Get nexthop by id"
+ check_nexthop "id 12" "id 12 via 172.16.1.2 dev veth1 scope link"
+
+ run_cmd "$IP nexthop del id 12"
+ log_test $? 0 "Delete nexthop by id"
+ check_nexthop "id 52" ""
+
+ #
+ # gw, device spec
+ #
+ # gw validation, no device - fails since dev is required
+ run_cmd "$IP nexthop add id 12 via 172.16.2.3"
+ log_test $? 2 "Create nexthop - gw only"
+
+ # gw not reachable through given dev
+ run_cmd "$IP nexthop add id 13 via 172.16.3.2 dev veth1"
+ log_test $? 2 "Create nexthop - invalid gw+dev combination"
+
+ # onlink flag overrides gw+dev lookup
+ run_cmd "$IP nexthop add id 13 via 172.16.3.2 dev veth1 onlink"
+ log_test $? 0 "Create nexthop - gw+dev and onlink"
+
+ # admin down should delete nexthops
+ set -e
+ run_cmd "$IP nexthop add id 15 via 172.16.1.3 dev veth1"
+ run_cmd "$IP nexthop add id 16 via 172.16.1.4 dev veth1"
+ run_cmd "$IP nexthop add id 17 via 172.16.1.5 dev veth1"
+ run_cmd "$IP li set dev veth1 down"
+ set +e
+ check_nexthop "dev veth1" ""
+ log_test $? 0 "Nexthops removed on admin down"
+}
+
+ipv4_grp_fcnal()
+{
+ local rc
+
+ echo
+ echo "IPv4 groups functional"
+ echo "----------------------"
+
+ # basic functionality: create a nexthop group, default weight
+ run_cmd "$IP nexthop add id 11 via 172.16.1.2 dev veth1"
+ run_cmd "$IP nexthop add id 101 group 11"
+ log_test $? 0 "Create nexthop group with single nexthop"
+
+ # get nexthop group
+ run_cmd "$IP nexthop get id 101"
+ log_test $? 0 "Get nexthop group by id"
+ check_nexthop "id 101" "id 101 group 11"
+
+ # delete nexthop group
+ run_cmd "$IP nexthop del id 101"
+ log_test $? 0 "Delete nexthop group by id"
+ check_nexthop "id 101" ""
+
+ $IP nexthop flush >/dev/null 2>&1
+
+ #
+ # create group with multiple nexthops
+ run_cmd "$IP nexthop add id 12 via 172.16.1.2 dev veth1"
+ run_cmd "$IP nexthop add id 13 via 172.16.1.3 dev veth1"
+ run_cmd "$IP nexthop add id 14 via 172.16.1.4 dev veth1"
+ run_cmd "$IP nexthop add id 15 via 172.16.1.5 dev veth1"
+ run_cmd "$IP nexthop add id 102 group 12/13/14/15"
+ log_test $? 0 "Nexthop group with multiple nexthops"
+ check_nexthop "id 102" "id 102 group 12/13/14/15"
+
+ # Delete nexthop in a group and group is updated
+ run_cmd "$IP nexthop del id 13"
+ check_nexthop "id 102" "id 102 group 12/14/15"
+ log_test $? 0 "Nexthop group updated when entry is deleted"
+
+ # create group with multiple weighted nexthops
+ run_cmd "$IP nexthop add id 13 via 172.16.1.3 dev veth1"
+ run_cmd "$IP nexthop add id 103 group 12/13,2/14,3/15,4"
+ log_test $? 0 "Nexthop group with weighted nexthops"
+ check_nexthop "id 103" "id 103 group 12/13,2/14,3/15,4"
+
+ # Delete nexthop in a weighted group and group is updated
+ run_cmd "$IP nexthop del id 13"
+ check_nexthop "id 103" "id 103 group 12/14,3/15,4"
+ log_test $? 0 "Weighted nexthop group updated when entry is deleted"
+
+ # admin down - nexthop is removed from group
+ run_cmd "$IP li set dev veth1 down"
+ check_nexthop "dev veth1" ""
+ log_test $? 0 "Nexthops in groups removed on admin down"
+
+ # expect groups to have been deleted as well
+ check_nexthop "" ""
+
+ run_cmd "$IP li set dev veth1 up"
+
+ $IP nexthop flush >/dev/null 2>&1
+
+ # group with nexthops using different devices
+ set -e
+ run_cmd "$IP nexthop add id 12 via 172.16.1.2 dev veth1"
+ run_cmd "$IP nexthop add id 13 via 172.16.1.3 dev veth1"
+ run_cmd "$IP nexthop add id 14 via 172.16.1.4 dev veth1"
+ run_cmd "$IP nexthop add id 15 via 172.16.1.5 dev veth1"
+
+ run_cmd "$IP nexthop add id 22 via 172.16.2.2 dev veth3"
+ run_cmd "$IP nexthop add id 23 via 172.16.2.3 dev veth3"
+ run_cmd "$IP nexthop add id 24 via 172.16.2.4 dev veth3"
+ run_cmd "$IP nexthop add id 25 via 172.16.2.5 dev veth3"
+ set +e
+
+ # multiple groups with same nexthop
+ run_cmd "$IP nexthop add id 104 group 12"
+ run_cmd "$IP nexthop add id 105 group 12"
+ check_nexthop "group" "id 104 group 12 id 105 group 12"
+ log_test $? 0 "Multiple groups with same nexthop"
+
+ run_cmd "$IP nexthop flush groups"
+ [ $? -ne 0 ] && return 1
+
+ # on admin down of veth1, it should be removed from the group
+ run_cmd "$IP nexthop add id 105 group 12/13/22/23/14"
+ run_cmd "$IP li set veth1 down"
+ check_nexthop "id 105" "id 105 group 22/23"
+ log_test $? 0 "Nexthops in group removed on admin down - mixed group"
+
+ run_cmd "$IP nexthop add id 106 group 105/24"
+ log_test $? 2 "Nexthop group can not have a group as an entry"
+
+ # a group can have a blackhole entry only if it is the only
+ # nexthop in the group. Needed for atomic replace with an
+ # actual nexthop group
+ run_cmd "$IP nexthop add id 31 blackhole"
+ run_cmd "$IP nexthop add id 107 group 31"
+ log_test $? 0 "Nexthop group with a blackhole entry"
+
+ run_cmd "$IP nexthop add id 108 group 31/24"
+ log_test $? 2 "Nexthop group can not have a blackhole and another nexthop"
+}
+
+ipv4_withv6_fcnal()
+{
+ local lladdr
+
+ set -e
+ lladdr=$(get_linklocal veth2 peer)
+ run_cmd "$IP nexthop add id 11 via ${lladdr} dev veth1"
+ set +e
+ run_cmd "$IP ro add 172.16.101.1/32 nhid 11"
+ log_test $? 0 "IPv6 nexthop with IPv4 route"
+ check_route "172.16.101.1" "172.16.101.1 nhid 11 via inet6 ${lladdr} dev veth1"
+
+ set -e
+ run_cmd "$IP nexthop add id 12 via 172.16.1.2 dev veth1"
+ run_cmd "$IP nexthop add id 101 group 11/12"
+ set +e
+ run_cmd "$IP ro replace 172.16.101.1/32 nhid 101"
+ log_test $? 0 "IPv6 nexthop with IPv4 route"
+
+ check_route "172.16.101.1" "172.16.101.1 nhid 101 nexthop via inet6 ${lladdr} dev veth1 weight 1 nexthop via 172.16.1.2 dev veth1 weight 1"
+
+ run_cmd "$IP ro replace 172.16.101.1/32 via inet6 ${lladdr} dev veth1"
+ log_test $? 0 "IPv4 route with IPv6 gateway"
+ check_route "172.16.101.1" "172.16.101.1 via inet6 ${lladdr} dev veth1"
+
+ run_cmd "$IP ro replace 172.16.101.1/32 via inet6 2001:db8:50::1 dev veth1"
+ log_test $? 2 "IPv4 route with invalid IPv6 gateway"
+}
+
+ipv4_fcnal_runtime()
+{
+ local lladdr
+ local rc
+
+ echo
+ echo "IPv4 functional runtime"
+ echo "-----------------------"
+
+ run_cmd "$IP nexthop add id 21 via 172.16.1.2 dev veth1"
+ run_cmd "$IP ro add 172.16.101.1/32 nhid 21"
+ log_test $? 0 "Route add"
+ check_route "172.16.101.1" "172.16.101.1 nhid 21 via 172.16.1.2 dev veth1"
+
+ run_cmd "$IP ro delete 172.16.101.1/32 nhid 21"
+ log_test $? 0 "Route delete"
+
+ #
+ # scope mismatch
+ #
+ run_cmd "$IP nexthop add id 22 via 172.16.1.2 dev veth1"
+ run_cmd "$IP ro add 172.16.101.1/32 nhid 22 scope host"
+ log_test $? 2 "Route add - scope conflict with nexthop"
+
+ run_cmd "$IP nexthop replace id 22 dev veth3"
+ run_cmd "$IP ro add 172.16.101.1/32 nhid 22 scope host"
+ run_cmd "$IP nexthop replace id 22 via 172.16.2.2 dev veth3"
+ log_test $? 2 "Nexthop replace with invalid scope for existing route"
+
+ #
+ # add route with nexthop and check traffic
+ #
+ run_cmd "$IP nexthop replace id 21 via 172.16.1.2 dev veth1"
+ run_cmd "$IP ro replace 172.16.101.1/32 nhid 21"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 0 "Basic ping"
+
+ run_cmd "$IP nexthop replace id 22 via 172.16.2.2 dev veth3"
+ run_cmd "$IP nexthop add id 122 group 21/22"
+ run_cmd "$IP ro replace 172.16.101.1/32 nhid 122"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 0 "Ping - multipath"
+
+ #
+ # IPv4 with blackhole nexthops
+ #
+ run_cmd "$IP nexthop add id 23 blackhole"
+ run_cmd "$IP ro replace 172.16.101.1/32 nhid 23"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 2 "Ping - blackhole"
+
+ run_cmd "$IP nexthop replace id 23 via 172.16.1.2 dev veth1"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 0 "Ping - blackhole replaced with gateway"
+
+ run_cmd "$IP nexthop replace id 23 blackhole"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 2 "Ping - gateway replaced by blackhole"
+
+ run_cmd "$IP ro replace 172.16.101.1/32 nhid 122"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ if [ $? -eq 0 ]; then
+ run_cmd "$IP nexthop replace id 122 group 23"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 2 "Ping - group with blackhole"
+
+ run_cmd "$IP nexthop replace id 122 group 21/22"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 0 "Ping - group blackhole replaced with gateways"
+ else
+ log_test 2 0 "Ping - multipath failed"
+ fi
+
+ #
+ # device only and gw + dev only mix
+ #
+ run_cmd "$IP nexthop add id 85 dev veth1"
+ run_cmd "$IP ro replace 172.16.101.1/32 nhid 85"
+ log_test $? 0 "IPv4 route with device only nexthop"
+ check_route "172.16.101.1" "172.16.101.1 nhid 85 dev veth1"
+
+ run_cmd "$IP nexthop add id 123 group 21/85"
+ run_cmd "$IP ro replace 172.16.101.1/32 nhid 123"
+ log_test $? 0 "IPv4 multipath route with nexthop mix - dev only + gw"
+ check_route "172.16.101.1" "172.16.101.1 nhid 123 nexthop via 172.16.1.2 dev veth1 weight 1 nexthop dev veth1 weight 1"
+
+ #
+ # IPv4 with IPv6
+ #
+ set -e
+ lladdr=$(get_linklocal veth2 peer)
+ run_cmd "$IP nexthop add id 24 via ${lladdr} dev veth1"
+ set +e
+ run_cmd "$IP ro replace 172.16.101.1/32 nhid 24"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 0 "IPv6 nexthop with IPv4 route"
+
+ $IP neigh sh | grep -q "${lladdr} dev veth1"
+ if [ $? -eq 1 ]; then
+ echo " WARNING: Neigh entry missing for ${lladdr}"
+ $IP neigh sh | grep 'dev veth1'
+ fi
+
+ $IP neigh sh | grep -q "172.16.101.1 dev eth1"
+ if [ $? -eq 0 ]; then
+ echo " WARNING: Neigh entry exists for 172.16.101.1"
+ $IP neigh sh | grep 'dev veth1'
+ fi
+
+ set -e
+ run_cmd "$IP nexthop add id 25 via 172.16.1.2 dev veth1"
+ run_cmd "$IP nexthop add id 101 group 24/25"
+ set +e
+ run_cmd "$IP ro replace 172.16.101.1/32 nhid 101"
+ log_test $? 0 "IPv4 route with mixed v4-v6 multipath route"
+
+ check_route "172.16.101.1" "172.16.101.1 nhid 101 nexthop via inet6 ${lladdr} dev veth1 weight 1 nexthop via 172.16.1.2 dev veth1 weight 1"
+
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 0 "IPv6 nexthop with IPv4 route"
+
+ run_cmd "$IP ro replace 172.16.101.1/32 via inet6 ${lladdr} dev veth1"
+ run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+ log_test $? 0 "IPv4 route with IPv6 gateway"
+
+ $IP neigh sh | grep -q "${lladdr} dev veth1"
+ if [ $? -eq 1 ]; then
+ echo " WARNING: Neigh entry missing for ${lladdr}"
+ $IP neigh sh | grep 'dev veth1'
+ fi
+
+ $IP neigh sh | grep -q "172.16.101.1 dev eth1"
+ if [ $? -eq 0 ]; then
+ echo " WARNING: Neigh entry exists for 172.16.101.1"
+ $IP neigh sh | grep 'dev veth1'
+ fi
+
+ #
+ # MPLS as an example of LWT encap
+ #
+ run_cmd "$IP nexthop add id 51 encap mpls 101 via 172.16.1.2 dev veth1"
+ log_test $? 0 "IPv4 route with MPLS encap"
+ check_nexthop "id 51" "id 51 encap mpls 101 via 172.16.1.2 dev veth1 scope link"
+ log_test $? 0 "IPv4 route with MPLS encap - check"
+
+ run_cmd "$IP nexthop add id 52 encap mpls 102 via inet6 2001:db8:91::2 dev veth1"
+ log_test $? 0 "IPv4 route with MPLS encap and v6 gateway"
+ check_nexthop "id 52" "id 52 encap mpls 102 via 2001:db8:91::2 dev veth1 scope link"
+ log_test $? 0 "IPv4 route with MPLS encap, v6 gw - check"
+}
+
+basic()
+{
+ echo
+ echo "Basic functional tests"
+ echo "----------------------"
+ run_cmd "$IP nexthop ls"
+ log_test $? 0 "List with nothing defined"
+
+ run_cmd "$IP nexthop get id 1"
+ log_test $? 2 "Nexthop get on non-existent id"
+
+ # attempt to create nh without a device or gw - fails
+ run_cmd "$IP nexthop add id 1"
+ log_test $? 2 "Nexthop with no device or gateway"
+
+ # attempt to create nh with down device - fails
+ $IP li set veth1 down
+ run_cmd "$IP nexthop add id 1 dev veth1"
+ log_test $? 2 "Nexthop with down device"
+
+ # create nh with linkdown device - fails
+ $IP li set veth1 up
+ ip -netns peer li set veth2 down
+ run_cmd "$IP nexthop add id 1 dev veth1"
+ log_test $? 2 "Nexthop with device that is linkdown"
+ ip -netns peer li set veth2 up
+
+ # device only
+ run_cmd "$IP nexthop add id 1 dev veth1"
+ log_test $? 0 "Nexthop with device only"
+
+ # create nh with duplicate id
+ run_cmd "$IP nexthop add id 1 dev veth3"
+ log_test $? 2 "Nexthop with duplicate id"
+
+ # blackhole nexthop
+ run_cmd "$IP nexthop add id 2 blackhole"
+ log_test $? 0 "Blackhole nexthop"
+
+ # blackhole nexthop can not have other specs
+ run_cmd "$IP nexthop replace id 2 blackhole dev veth1"
+ log_test $? 2 "Blackhole nexthop with other attributes"
+
+ #
+ # groups
+ #
+
+ run_cmd "$IP nexthop add id 101 group 1"
+ log_test $? 0 "Create group"
+
+ run_cmd "$IP nexthop add id 102 group 2"
+ log_test $? 0 "Create group with blackhole nexthop"
+
+ # multipath group can not have a blackhole as 1 path
+ run_cmd "$IP nexthop add id 103 group 1/2"
+ log_test $? 2 "Create multipath group where 1 path is a blackhole"
+
+ # multipath group can not have a member replaced by a blackhole
+ run_cmd "$IP nexthop replace id 2 dev veth3"
+ run_cmd "$IP nexthop replace id 102 group 1/2"
+ run_cmd "$IP nexthop replace id 2 blackhole"
+ log_test $? 2 "Multipath group can not have a member replaced by blackhole"
+
+ # attempt to create group with non-existent nexthop
+ run_cmd "$IP nexthop add id 103 group 12"
+ log_test $? 2 "Create group with non-existent nexthop"
+
+ # attempt to create group with same nexthop
+ run_cmd "$IP nexthop add id 103 group 1/1"
+ log_test $? 2 "Create group with same nexthop multiple times"
+
+ # replace nexthop with a group - fails
+ run_cmd "$IP nexthop replace id 2 group 1"
+ log_test $? 2 "Replace nexthop with nexthop group"
+
+ # replace nexthop group with a nexthop - fails
+ run_cmd "$IP nexthop replace id 101 dev veth1"
+ log_test $? 2 "Replace nexthop group with nexthop"
+
+ # nexthop group with other attributes fail
+ run_cmd "$IP nexthop add id 104 group 1 dev veth1"
+ log_test $? 2 "Nexthop group and device"
+
+ run_cmd "$IP nexthop add id 104 group 1 blackhole"
+ log_test $? 2 "Nexthop group and blackhole"
+
+ $IP nexthop flush >/dev/null 2>&1
+}
+
+################################################################################
+# usage
+
+usage()
+{
+ cat <<EOF
+usage: ${0##*/} OPTS
+
+ -t <test> Test(s) to run (default: all)
+ (options: $ALL_TESTS)
+ -4 IPv4 tests only
+ -6 IPv6 tests only
+ -p Pause on fail
+ -P Pause after each test before cleanup
+ -v verbose mode (show commands and output)
+
+ Runtime test
+ -n num Number of nexthops to target
+ -N Use new style to install routes in DUT
+
+done
+EOF
+}
+
+################################################################################
+# main
+
+while getopts :t:pP46hv o
+do
+ case $o in
+ t) TESTS=$OPTARG;;
+ 4) TESTS=${IPV4_TESTS};;
+ 6) TESTS=${IPV6_TESTS};;
+ p) PAUSE_ON_FAIL=yes;;
+ P) PAUSE=yes;;
+ v) VERBOSE=$(($VERBOSE + 1));;
+ h) usage; exit 0;;
+ *) usage; exit 1;;
+ esac
+done
+
+# make sure we don't pause twice
+[ "${PAUSE}" = "yes" ] && PAUSE_ON_FAIL=no
+
+if [ "$(id -u)" -ne 0 ];then
+ echo "SKIP: Need root privileges"
+ exit $ksft_skip;
+fi
+
+if [ ! -x "$(command -v ip)" ]; then
+ echo "SKIP: Could not run test without ip tool"
+ exit $ksft_skip
+fi
+
+ip help 2>&1 | grep -q nexthop
+if [ $? -ne 0 ]; then
+ echo "SKIP: iproute2 too old, missing nexthop command"
+ exit $ksft_skip
+fi
+
+out=$(ip nexthop ls 2>&1 | grep -q "Operation not supported")
+if [ $? -eq 0 ]; then
+ echo "SKIP: kernel lacks nexthop support"
+ exit $ksft_skip
+fi
+
+for t in $TESTS
+do
+ case $t in
+ none) IP="ip -netns peer"; setup; exit 0;;
+ *) setup; $t; cleanup;;
+ esac
+done
+
+if [ "$TESTS" != "none" ]; then
+ printf "\nTests passed: %3d\n" ${nsuccess}
+ printf "Tests failed: %3d\n" ${nfail}
+fi
+
+exit $ret
diff --git a/tools/testing/selftests/net/fib_rule_tests.sh b/tools/testing/selftests/net/fib_rule_tests.sh
index 4b7e107865bf..a93e6b690e06 100755
--- a/tools/testing/selftests/net/fib_rule_tests.sh
+++ b/tools/testing/selftests/net/fib_rule_tests.sh
@@ -15,6 +15,7 @@ GW_IP6=2001:db8:1::2
SRC_IP6=2001:db8:1::3
DEV_ADDR=192.51.100.1
+DEV_ADDR6=2001:db8:1::1
DEV=dummy0
log_test()
@@ -55,8 +56,8 @@ setup()
$IP link add dummy0 type dummy
$IP link set dev dummy0 up
- $IP address add 198.51.100.1/24 dev dummy0
- $IP -6 address add 2001:db8:1::1/64 dev dummy0
+ $IP address add $DEV_ADDR/24 dev dummy0
+ $IP -6 address add $DEV_ADDR6/64 dev dummy0
set +e
}
@@ -186,8 +187,13 @@ fib_rule4_test()
match="oif $DEV"
fib_rule4_test_match_n_redirect "$match" "$match" "oif redirect to table"
+ # need enable forwarding and disable rp_filter temporarily as all the
+ # addresses are in the same subnet and egress device == ingress device.
+ ip netns exec testns sysctl -w net.ipv4.ip_forward=1
+ ip netns exec testns sysctl -w net.ipv4.conf.$DEV.rp_filter=0
match="from $SRC_IP iif $DEV"
fib_rule4_test_match_n_redirect "$match" "$match" "iif redirect to table"
+ ip netns exec testns sysctl -w net.ipv4.ip_forward=0
match="tos 0x10"
fib_rule4_test_match_n_redirect "$match" "$match" "tos redirect to table"
diff --git a/tools/testing/selftests/net/fib_tests.sh b/tools/testing/selftests/net/fib_tests.sh
index 9457aaeae092..4465fc2dae14 100755
--- a/tools/testing/selftests/net/fib_tests.sh
+++ b/tools/testing/selftests/net/fib_tests.sh
@@ -9,12 +9,13 @@ ret=0
ksft_skip=4
# all tests in this script. Can be overridden with -t option
-TESTS="unregister down carrier nexthop ipv6_rt ipv4_rt ipv6_addr_metric ipv4_addr_metric ipv6_route_metrics ipv4_route_metrics ipv4_route_v6_gw"
+TESTS="unregister down carrier nexthop ipv6_rt ipv4_rt ipv6_addr_metric ipv4_addr_metric ipv6_route_metrics ipv4_route_metrics ipv4_route_v6_gw rp_filter"
VERBOSE=0
PAUSE_ON_FAIL=no
PAUSE=no
IP="ip -netns ns1"
+NS_EXEC="ip netns exec ns1"
log_test()
{
@@ -433,6 +434,37 @@ fib_carrier_test()
fib_carrier_unicast_test
}
+fib_rp_filter_test()
+{
+ echo
+ echo "IPv4 rp_filter tests"
+
+ setup
+
+ set -e
+ $IP link set dev lo address 52:54:00:6a:c7:5e
+ $IP link set dummy0 address 52:54:00:6a:c7:5e
+ $IP link add dummy1 type dummy
+ $IP link set dummy1 address 52:54:00:6a:c7:5e
+ $IP link set dev dummy1 up
+ $NS_EXEC sysctl -qw net.ipv4.conf.all.rp_filter=1
+ $NS_EXEC sysctl -qw net.ipv4.conf.all.accept_local=1
+ $NS_EXEC sysctl -qw net.ipv4.conf.all.route_localnet=1
+
+ $NS_EXEC tc qd add dev dummy1 parent root handle 1: fq_codel
+ $NS_EXEC tc filter add dev dummy1 parent 1: protocol arp basic action mirred egress redirect dev lo
+ $NS_EXEC tc filter add dev dummy1 parent 1: protocol ip basic action mirred egress redirect dev lo
+ set +e
+
+ run_cmd "ip netns exec ns1 ping -I dummy1 -w1 -c1 198.51.100.1"
+ log_test $? 0 "rp_filter passes local packets"
+
+ run_cmd "ip netns exec ns1 ping -I dummy1 -w1 -c1 127.0.0.1"
+ log_test $? 0 "rp_filter passes loopback packets"
+
+ cleanup
+}
+
################################################################################
# Tests on nexthop spec
@@ -1557,6 +1589,7 @@ do
fib_unreg_test|unregister) fib_unreg_test;;
fib_down_test|down) fib_down_test;;
fib_carrier_test|carrier) fib_carrier_test;;
+ fib_rp_filter_test|rp_filter) fib_rp_filter_test;;
fib_nexthop_test|nexthop) fib_nexthop_test;;
ipv6_route_test|ipv6_rt) ipv6_route_test;;
ipv4_route_test|ipv4_rt) ipv4_route_test;;
diff --git a/tools/testing/selftests/net/forwarding/devlink_lib.sh b/tools/testing/selftests/net/forwarding/devlink_lib.sh
index 8553a67a2322..13d03a6d85ba 100644
--- a/tools/testing/selftests/net/forwarding/devlink_lib.sh
+++ b/tools/testing/selftests/net/forwarding/devlink_lib.sh
@@ -4,19 +4,21 @@
##############################################################################
# Defines
-DEVLINK_DEV=$(devlink port show "${NETIFS[p1]}" -j \
- | jq -r '.port | keys[]' | cut -d/ -f-2)
-if [ -z "$DEVLINK_DEV" ]; then
- echo "SKIP: ${NETIFS[p1]} has no devlink device registered for it"
- exit 1
-fi
-if [[ "$(echo $DEVLINK_DEV | grep -c pci)" -eq 0 ]]; then
- echo "SKIP: devlink device's bus is not PCI"
- exit 1
-fi
+if [[ ! -v DEVLINK_DEV ]]; then
+ DEVLINK_DEV=$(devlink port show "${NETIFS[p1]}" -j \
+ | jq -r '.port | keys[]' | cut -d/ -f-2)
+ if [ -z "$DEVLINK_DEV" ]; then
+ echo "SKIP: ${NETIFS[p1]} has no devlink device registered for it"
+ exit 1
+ fi
+ if [[ "$(echo $DEVLINK_DEV | grep -c pci)" -eq 0 ]]; then
+ echo "SKIP: devlink device's bus is not PCI"
+ exit 1
+ fi
-DEVLINK_VIDDID=$(lspci -s $(echo $DEVLINK_DEV | cut -d"/" -f2) \
- -n | cut -d" " -f3)
+ DEVLINK_VIDDID=$(lspci -s $(echo $DEVLINK_DEV | cut -d"/" -f2) \
+ -n | cut -d" " -f3)
+fi
##############################################################################
# Sanity checks
@@ -27,6 +29,12 @@ if [ $? -ne 0 ]; then
exit 1
fi
+devlink help 2>&1 | grep trap &> /dev/null
+if [ $? -ne 0 ]; then
+ echo "SKIP: iproute2 too old, missing devlink trap support"
+ exit 1
+fi
+
##############################################################################
# Devlink helpers
@@ -190,3 +198,160 @@ devlink_tc_bind_pool_th_restore()
devlink sb tc bind set $port tc $tc type $dir \
pool ${orig[0]} th ${orig[1]}
}
+
+devlink_traps_num_get()
+{
+ devlink -j trap | jq '.[]["'$DEVLINK_DEV'"] | length'
+}
+
+devlink_traps_get()
+{
+ devlink -j trap | jq -r '.[]["'$DEVLINK_DEV'"][].name'
+}
+
+devlink_trap_type_get()
+{
+ local trap_name=$1; shift
+
+ devlink -j trap show $DEVLINK_DEV trap $trap_name \
+ | jq -r '.[][][].type'
+}
+
+devlink_trap_action_set()
+{
+ local trap_name=$1; shift
+ local action=$1; shift
+
+ # Pipe output to /dev/null to avoid expected warnings.
+ devlink trap set $DEVLINK_DEV trap $trap_name \
+ action $action &> /dev/null
+}
+
+devlink_trap_action_get()
+{
+ local trap_name=$1; shift
+
+ devlink -j trap show $DEVLINK_DEV trap $trap_name \
+ | jq -r '.[][][].action'
+}
+
+devlink_trap_group_get()
+{
+ devlink -j trap show $DEVLINK_DEV trap $trap_name \
+ | jq -r '.[][][].group'
+}
+
+devlink_trap_metadata_test()
+{
+ local trap_name=$1; shift
+ local metadata=$1; shift
+
+ devlink -jv trap show $DEVLINK_DEV trap $trap_name \
+ | jq -e '.[][][].metadata | contains(["'$metadata'"])' \
+ &> /dev/null
+}
+
+devlink_trap_rx_packets_get()
+{
+ local trap_name=$1; shift
+
+ devlink -js trap show $DEVLINK_DEV trap $trap_name \
+ | jq '.[][][]["stats"]["rx"]["packets"]'
+}
+
+devlink_trap_rx_bytes_get()
+{
+ local trap_name=$1; shift
+
+ devlink -js trap show $DEVLINK_DEV trap $trap_name \
+ | jq '.[][][]["stats"]["rx"]["bytes"]'
+}
+
+devlink_trap_stats_idle_test()
+{
+ local trap_name=$1; shift
+ local t0_packets t0_bytes
+ local t1_packets t1_bytes
+
+ t0_packets=$(devlink_trap_rx_packets_get $trap_name)
+ t0_bytes=$(devlink_trap_rx_bytes_get $trap_name)
+
+ sleep 1
+
+ t1_packets=$(devlink_trap_rx_packets_get $trap_name)
+ t1_bytes=$(devlink_trap_rx_bytes_get $trap_name)
+
+ if [[ $t0_packets -eq $t1_packets && $t0_bytes -eq $t1_bytes ]]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+devlink_traps_enable_all()
+{
+ local trap_name
+
+ for trap_name in $(devlink_traps_get); do
+ devlink_trap_action_set $trap_name "trap"
+ done
+}
+
+devlink_traps_disable_all()
+{
+ for trap_name in $(devlink_traps_get); do
+ devlink_trap_action_set $trap_name "drop"
+ done
+}
+
+devlink_trap_groups_get()
+{
+ devlink -j trap group | jq -r '.[]["'$DEVLINK_DEV'"][].name'
+}
+
+devlink_trap_group_action_set()
+{
+ local group_name=$1; shift
+ local action=$1; shift
+
+ # Pipe output to /dev/null to avoid expected warnings.
+ devlink trap group set $DEVLINK_DEV group $group_name action $action \
+ &> /dev/null
+}
+
+devlink_trap_group_rx_packets_get()
+{
+ local group_name=$1; shift
+
+ devlink -js trap group show $DEVLINK_DEV group $group_name \
+ | jq '.[][][]["stats"]["rx"]["packets"]'
+}
+
+devlink_trap_group_rx_bytes_get()
+{
+ local group_name=$1; shift
+
+ devlink -js trap group show $DEVLINK_DEV group $group_name \
+ | jq '.[][][]["stats"]["rx"]["bytes"]'
+}
+
+devlink_trap_group_stats_idle_test()
+{
+ local group_name=$1; shift
+ local t0_packets t0_bytes
+ local t1_packets t1_bytes
+
+ t0_packets=$(devlink_trap_group_rx_packets_get $group_name)
+ t0_bytes=$(devlink_trap_group_rx_bytes_get $group_name)
+
+ sleep 1
+
+ t1_packets=$(devlink_trap_group_rx_packets_get $group_name)
+ t1_bytes=$(devlink_trap_group_rx_bytes_get $group_name)
+
+ if [[ $t0_packets -eq $t1_packets && $t0_bytes -eq $t1_bytes ]]; then
+ return 0
+ else
+ return 1
+ fi
+}
diff --git a/tools/testing/selftests/net/forwarding/gre_inner_v4_multipath.sh b/tools/testing/selftests/net/forwarding/gre_inner_v4_multipath.sh
new file mode 100755
index 000000000000..e4009f658003
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/gre_inner_v4_multipath.sh
@@ -0,0 +1,305 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# Test traffic distribution when there are multiple routes between an IPv4
+# GRE tunnel. The tunnel carries IPv4 traffic between multiple hosts.
+# Multiple routes are in the underlay network. With the default multipath
+# policy, SW2 will only look at the outer IP addresses, hence only a single
+# route would be used.
+#
+# +-------------------------+
+# | H1 |
+# | $h1 + |
+# | 192.0.3.{2-62}/24 | |
+# +-------------------|-----+
+# |
+# +-------------------|------------------------+
+# | SW1 | |
+# | $ol1 + |
+# | 192.0.3.1/24 |
+# | |
+# | + g1 (gre) |
+# | loc=192.0.2.65 |
+# | rem=192.0.2.66 --. |
+# | tos=inherit | |
+# | v |
+# | + $ul1 |
+# | | 192.0.2.129/28 |
+# +---------------------|----------------------+
+# |
+# +---------------------|----------------------+
+# | SW2 | |
+# | $ul21 + |
+# | 192.0.2.130/28 |
+# | | |
+# ! ________________|_____ |
+# | / \ |
+# | | | |
+# | + $ul22.111 (vlan) + $ul22.222 (vlan) |
+# | | 192.0.2.145/28 | 192.0.2.161/28 |
+# | | | |
+# +--|----------------------|------------------+
+# | |
+# +--|----------------------|------------------+
+# | | | |
+# | + $ul32.111 (vlan) + $ul32.222 (vlan) |
+# | | 192.0.2.146/28 | 192.0.2.162/28 |
+# | | | |
+# | \______________________/ |
+# | | |
+# | | |
+# | $ul31 + |
+# | 192.0.2.177/28 | SW3 |
+# +---------------------|----------------------+
+# |
+# +---------------------|----------------------+
+# | + $ul4 |
+# | ^ 192.0.2.178/28 |
+# | | |
+# | + g2 (gre) | |
+# | loc=192.0.2.66 | |
+# | rem=192.0.2.65 --' |
+# | tos=inherit |
+# | |
+# | $ol4 + |
+# | 192.0.4.1/24 | SW4 |
+# +--------------------|-----------------------+
+# |
+# +--------------------|---------+
+# | | |
+# | $h2 + |
+# | 192.0.4.{2-62}/24 H2 |
+# +------------------------------+
+
+ALL_TESTS="
+ ping_ipv4
+ multipath_ipv4
+"
+
+NUM_NETIFS=10
+source lib.sh
+
+h1_create()
+{
+ simple_if_init $h1 192.0.3.2/24
+ ip route add vrf v$h1 192.0.4.0/24 via 192.0.3.1
+}
+
+h1_destroy()
+{
+ ip route del vrf v$h1 192.0.4.0/24 via 192.0.3.1
+ simple_if_fini $h1 192.0.3.2/24
+}
+
+sw1_create()
+{
+ simple_if_init $ol1 192.0.3.1/24
+ __simple_if_init $ul1 v$ol1 192.0.2.129/28
+
+ tunnel_create g1 gre 192.0.2.65 192.0.2.66 tos inherit dev v$ol1
+ __simple_if_init g1 v$ol1 192.0.2.65/32
+ ip route add vrf v$ol1 192.0.2.66/32 via 192.0.2.130
+
+ ip route add vrf v$ol1 192.0.4.0/24 nexthop dev g1
+}
+
+sw1_destroy()
+{
+ ip route del vrf v$ol1 192.0.4.0/24
+
+ ip route del vrf v$ol1 192.0.2.66/32
+ __simple_if_fini g1 192.0.2.65/32
+ tunnel_destroy g1
+
+ __simple_if_fini $ul1 192.0.2.129/28
+ simple_if_fini $ol1 192.0.3.1/24
+}
+
+sw2_create()
+{
+ simple_if_init $ul21 192.0.2.130/28
+ __simple_if_init $ul22 v$ul21
+ vlan_create $ul22 111 v$ul21 192.0.2.145/28
+ vlan_create $ul22 222 v$ul21 192.0.2.161/28
+
+ ip route add vrf v$ul21 192.0.2.65/32 via 192.0.2.129
+ ip route add vrf v$ul21 192.0.2.66/32 \
+ nexthop via 192.0.2.146 \
+ nexthop via 192.0.2.162
+}
+
+sw2_destroy()
+{
+ ip route del vrf v$ul21 192.0.2.66/32
+ ip route del vrf v$ul21 192.0.2.65/32
+
+ vlan_destroy $ul22 222
+ vlan_destroy $ul22 111
+ __simple_if_fini $ul22
+ simple_if_fini $ul21 192.0.2.130/28
+}
+
+sw3_create()
+{
+ simple_if_init $ul31 192.0.2.177/28
+ __simple_if_init $ul32 v$ul31
+ vlan_create $ul32 111 v$ul31 192.0.2.146/28
+ vlan_create $ul32 222 v$ul31 192.0.2.162/28
+
+ ip route add vrf v$ul31 192.0.2.66/32 via 192.0.2.178
+ ip route add vrf v$ul31 192.0.2.65/32 \
+ nexthop via 192.0.2.145 \
+ nexthop via 192.0.2.161
+
+ tc qdisc add dev $ul32 clsact
+ tc filter add dev $ul32 ingress pref 111 prot 802.1Q \
+ flower vlan_id 111 action pass
+ tc filter add dev $ul32 ingress pref 222 prot 802.1Q \
+ flower vlan_id 222 action pass
+}
+
+sw3_destroy()
+{
+ tc qdisc del dev $ul32 clsact
+
+ ip route del vrf v$ul31 192.0.2.65/32
+ ip route del vrf v$ul31 192.0.2.66/32
+
+ vlan_destroy $ul32 222
+ vlan_destroy $ul32 111
+ __simple_if_fini $ul32
+ simple_if_fini $ul31 192.0.2.177/28
+}
+
+sw4_create()
+{
+ simple_if_init $ol4 192.0.4.1/24
+ __simple_if_init $ul4 v$ol4 192.0.2.178/28
+
+ tunnel_create g2 gre 192.0.2.66 192.0.2.65 tos inherit dev v$ol4
+ __simple_if_init g2 v$ol4 192.0.2.66/32
+ ip route add vrf v$ol4 192.0.2.65/32 via 192.0.2.177
+
+ ip route add vrf v$ol4 192.0.3.0/24 nexthop dev g2
+}
+
+sw4_destroy()
+{
+ ip route del vrf v$ol4 192.0.3.0/24
+
+ ip route del vrf v$ol4 192.0.2.65/32
+ __simple_if_fini g2 192.0.2.66/32
+ tunnel_destroy g2
+
+ __simple_if_fini $ul4 192.0.2.178/28
+ simple_if_fini $ol4 192.0.4.1/24
+}
+
+h2_create()
+{
+ simple_if_init $h2 192.0.4.2/24
+ ip route add vrf v$h2 192.0.3.0/24 via 192.0.4.1
+}
+
+h2_destroy()
+{
+ ip route del vrf v$h2 192.0.3.0/24 via 192.0.4.1
+ simple_if_fini $h2 192.0.4.2/24
+}
+
+setup_prepare()
+{
+ h1=${NETIFS[p1]}
+
+ ol1=${NETIFS[p2]}
+ ul1=${NETIFS[p3]}
+
+ ul21=${NETIFS[p4]}
+ ul22=${NETIFS[p5]}
+
+ ul32=${NETIFS[p6]}
+ ul31=${NETIFS[p7]}
+
+ ul4=${NETIFS[p8]}
+ ol4=${NETIFS[p9]}
+
+ h2=${NETIFS[p10]}
+
+ vrf_prepare
+ h1_create
+ sw1_create
+ sw2_create
+ sw3_create
+ sw4_create
+ h2_create
+
+ forwarding_enable
+}
+
+cleanup()
+{
+ pre_cleanup
+
+ forwarding_restore
+
+ h2_destroy
+ sw4_destroy
+ sw3_destroy
+ sw2_destroy
+ sw1_destroy
+ h1_destroy
+ vrf_cleanup
+}
+
+multipath4_test()
+{
+ local what=$1; shift
+ local weight1=$1; shift
+ local weight2=$1; shift
+
+ sysctl_set net.ipv4.fib_multipath_hash_policy 2
+ ip route replace vrf v$ul21 192.0.2.66/32 \
+ nexthop via 192.0.2.146 weight $weight1 \
+ nexthop via 192.0.2.162 weight $weight2
+
+ local t0_111=$(tc_rule_stats_get $ul32 111 ingress)
+ local t0_222=$(tc_rule_stats_get $ul32 222 ingress)
+
+ ip vrf exec v$h1 \
+ $MZ $h1 -q -p 64 -A "192.0.3.2-192.0.3.62" -B "192.0.4.2-192.0.4.62" \
+ -d 1msec -c 50 -t udp "sp=1024,dp=1024"
+ sleep 1
+
+ local t1_111=$(tc_rule_stats_get $ul32 111 ingress)
+ local t1_222=$(tc_rule_stats_get $ul32 222 ingress)
+
+ local d111=$((t1_111 - t0_111))
+ local d222=$((t1_222 - t0_222))
+ multipath_eval "$what" $weight1 $weight2 $d111 $d222
+
+ ip route replace vrf v$ul21 192.0.2.66/32 \
+ nexthop via 192.0.2.146 \
+ nexthop via 192.0.2.162
+ sysctl_restore net.ipv4.fib_multipath_hash_policy
+}
+
+ping_ipv4()
+{
+ ping_test $h1 192.0.4.2
+}
+
+multipath_ipv4()
+{
+ log_info "Running IPv4 over GRE over IPv4 multipath tests"
+ multipath4_test "ECMP" 1 1
+ multipath4_test "Weighted MP 2:1" 2 1
+ multipath4_test "Weighted MP 11:45" 11 45
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/gre_inner_v6_multipath.sh b/tools/testing/selftests/net/forwarding/gre_inner_v6_multipath.sh
new file mode 100755
index 000000000000..e449475c4d3e
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/gre_inner_v6_multipath.sh
@@ -0,0 +1,306 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# Test traffic distribution when there are multiple routes between an IPv4
+# GRE tunnel. The tunnel carries IPv6 traffic between multiple hosts.
+# Multiple routes are in the underlay network. With the default multipath
+# policy, SW2 will only look at the outer IP addresses, hence only a single
+# route would be used.
+#
+# +-------------------------+
+# | H1 |
+# | $h1 + |
+# | 2001:db8:1::2/64 | |
+# +-------------------|-----+
+# |
+# +-------------------|------------------------+
+# | SW1 | |
+# | $ol1 + |
+# | 2001:db8:1::1/64 |
+# | |
+# | + g1 (gre) |
+# | loc=192.0.2.65 |
+# | rem=192.0.2.66 --. |
+# | tos=inherit | |
+# | v |
+# | + $ul1 |
+# | | 192.0.2.129/28 |
+# +---------------------|----------------------+
+# |
+# +---------------------|----------------------+
+# | SW2 | |
+# | $ul21 + |
+# | 192.0.2.130/28 |
+# | | |
+# ! ________________|_____ |
+# | / \ |
+# | | | |
+# | + $ul22.111 (vlan) + $ul22.222 (vlan) |
+# | | 192.0.2.145/28 | 192.0.2.161/28 |
+# | | | |
+# +--|----------------------|------------------+
+# | |
+# +--|----------------------|------------------+
+# | | | |
+# | + $ul32.111 (vlan) + $ul32.222 (vlan) |
+# | | 192.0.2.146/28 | 192.0.2.162/28 |
+# | | | |
+# | \______________________/ |
+# | | |
+# | | |
+# | $ul31 + |
+# | 192.0.2.177/28 | SW3 |
+# +---------------------|----------------------+
+# |
+# +---------------------|----------------------+
+# | + $ul4 |
+# | ^ 192.0.2.178/28 |
+# | | |
+# | + g2 (gre) | |
+# | loc=192.0.2.66 | |
+# | rem=192.0.2.65 --' |
+# | tos=inherit |
+# | |
+# | $ol4 + |
+# | 2001:db8:2::1/64 | SW4 |
+# +--------------------|-----------------------+
+# |
+# +--------------------|---------+
+# | | |
+# | $h2 + |
+# | 2001:db8:2::2/64 H2 |
+# +------------------------------+
+
+ALL_TESTS="
+ ping_ipv6
+ multipath_ipv6
+"
+
+NUM_NETIFS=10
+source lib.sh
+
+h1_create()
+{
+ simple_if_init $h1 2001:db8:1::2/64
+ ip -6 route add vrf v$h1 2001:db8:2::/64 via 2001:db8:1::1
+}
+
+h1_destroy()
+{
+ ip -6 route del vrf v$h1 2001:db8:2::/64 via 2001:db8:1::1
+ simple_if_fini $h1 2001:db8:1::2/64
+}
+
+sw1_create()
+{
+ simple_if_init $ol1 2001:db8:1::1/64
+ __simple_if_init $ul1 v$ol1 192.0.2.129/28
+
+ tunnel_create g1 gre 192.0.2.65 192.0.2.66 tos inherit dev v$ol1
+ __simple_if_init g1 v$ol1 192.0.2.65/32
+ ip route add vrf v$ol1 192.0.2.66/32 via 192.0.2.130
+
+ ip -6 route add vrf v$ol1 2001:db8:2::/64 dev g1
+}
+
+sw1_destroy()
+{
+ ip -6 route del vrf v$ol1 2001:db8:2::/64
+
+ ip route del vrf v$ol1 192.0.2.66/32
+ __simple_if_fini g1 192.0.2.65/32
+ tunnel_destroy g1
+
+ __simple_if_fini $ul1 192.0.2.129/28
+ simple_if_fini $ol1 2001:db8:1::1/64
+}
+
+sw2_create()
+{
+ simple_if_init $ul21 192.0.2.130/28
+ __simple_if_init $ul22 v$ul21
+ vlan_create $ul22 111 v$ul21 192.0.2.145/28
+ vlan_create $ul22 222 v$ul21 192.0.2.161/28
+
+ ip route add vrf v$ul21 192.0.2.65/32 via 192.0.2.129
+ ip route add vrf v$ul21 192.0.2.66/32 \
+ nexthop via 192.0.2.146 \
+ nexthop via 192.0.2.162
+}
+
+sw2_destroy()
+{
+ ip route del vrf v$ul21 192.0.2.66/32
+ ip route del vrf v$ul21 192.0.2.65/32
+
+ vlan_destroy $ul22 222
+ vlan_destroy $ul22 111
+ __simple_if_fini $ul22
+ simple_if_fini $ul21 192.0.2.130/28
+}
+
+sw3_create()
+{
+ simple_if_init $ul31 192.0.2.177/28
+ __simple_if_init $ul32 v$ul31
+ vlan_create $ul32 111 v$ul31 192.0.2.146/28
+ vlan_create $ul32 222 v$ul31 192.0.2.162/28
+
+ ip route add vrf v$ul31 192.0.2.66/32 via 192.0.2.178
+ ip route add vrf v$ul31 192.0.2.65/32 \
+ nexthop via 192.0.2.145 \
+ nexthop via 192.0.2.161
+
+ tc qdisc add dev $ul32 clsact
+ tc filter add dev $ul32 ingress pref 111 prot 802.1Q \
+ flower vlan_id 111 action pass
+ tc filter add dev $ul32 ingress pref 222 prot 802.1Q \
+ flower vlan_id 222 action pass
+}
+
+sw3_destroy()
+{
+ tc qdisc del dev $ul32 clsact
+
+ ip route del vrf v$ul31 192.0.2.65/32
+ ip route del vrf v$ul31 192.0.2.66/32
+
+ vlan_destroy $ul32 222
+ vlan_destroy $ul32 111
+ __simple_if_fini $ul32
+ simple_if_fini $ul31 192.0.2.177/28
+}
+
+sw4_create()
+{
+ simple_if_init $ol4 2001:db8:2::1/64
+ __simple_if_init $ul4 v$ol4 192.0.2.178/28
+
+ tunnel_create g2 gre 192.0.2.66 192.0.2.65 tos inherit dev v$ol4
+ __simple_if_init g2 v$ol4 192.0.2.66/32
+ ip route add vrf v$ol4 192.0.2.65/32 via 192.0.2.177
+
+ ip -6 route add vrf v$ol4 2001:db8:1::/64 dev g2
+}
+
+sw4_destroy()
+{
+ ip -6 route del vrf v$ol4 2001:db8:1::/64
+
+ ip route del vrf v$ol4 192.0.2.65/32
+ __simple_if_fini g2 192.0.2.66/32
+ tunnel_destroy g2
+
+ __simple_if_fini $ul4 192.0.2.178/28
+ simple_if_fini $ol4 2001:db8:2::1/64
+}
+
+h2_create()
+{
+ simple_if_init $h2 2001:db8:2::2/64
+ ip -6 route add vrf v$h2 2001:db8:1::/64 via 2001:db8:2::1
+}
+
+h2_destroy()
+{
+ ip -6 route del vrf v$h2 2001:db8:1::/64 via 2001:db8:2::1
+ simple_if_fini $h2 2001:db8:2::2/64
+}
+
+setup_prepare()
+{
+ h1=${NETIFS[p1]}
+
+ ol1=${NETIFS[p2]}
+ ul1=${NETIFS[p3]}
+
+ ul21=${NETIFS[p4]}
+ ul22=${NETIFS[p5]}
+
+ ul32=${NETIFS[p6]}
+ ul31=${NETIFS[p7]}
+
+ ul4=${NETIFS[p8]}
+ ol4=${NETIFS[p9]}
+
+ h2=${NETIFS[p10]}
+
+ vrf_prepare
+ h1_create
+ sw1_create
+ sw2_create
+ sw3_create
+ sw4_create
+ h2_create
+
+ forwarding_enable
+}
+
+cleanup()
+{
+ pre_cleanup
+
+ forwarding_restore
+
+ h2_destroy
+ sw4_destroy
+ sw3_destroy
+ sw2_destroy
+ sw1_destroy
+ h1_destroy
+ vrf_cleanup
+}
+
+multipath6_test()
+{
+ local what=$1; shift
+ local weight1=$1; shift
+ local weight2=$1; shift
+
+ sysctl_set net.ipv4.fib_multipath_hash_policy 2
+ ip route replace vrf v$ul21 192.0.2.66/32 \
+ nexthop via 192.0.2.146 weight $weight1 \
+ nexthop via 192.0.2.162 weight $weight2
+
+ local t0_111=$(tc_rule_stats_get $ul32 111 ingress)
+ local t0_222=$(tc_rule_stats_get $ul32 222 ingress)
+
+ ip vrf exec v$h1 \
+ $MZ $h1 -6 -q -p 64 -A "2001:db8:1::2-2001:db8:1::1e" \
+ -B "2001:db8:2::2-2001:db8:2::1e" \
+ -d 1msec -c 50 -t udp "sp=1024,dp=1024"
+ sleep 1
+
+ local t1_111=$(tc_rule_stats_get $ul32 111 ingress)
+ local t1_222=$(tc_rule_stats_get $ul32 222 ingress)
+
+ local d111=$((t1_111 - t0_111))
+ local d222=$((t1_222 - t0_222))
+ multipath_eval "$what" $weight1 $weight2 $d111 $d222
+
+ ip route replace vrf v$ul21 192.0.2.66/32 \
+ nexthop via 192.0.2.146 \
+ nexthop via 192.0.2.162
+ sysctl_restore net.ipv4.fib_multipath_hash_policy
+}
+
+ping_ipv6()
+{
+ ping_test $h1 2001:db8:2::2
+}
+
+multipath_ipv6()
+{
+ log_info "Running IPv6 over GRE over IPv4 multipath tests"
+ multipath6_test "ECMP" 1 1
+ multipath6_test "Weighted MP 2:1" 2 1
+ multipath6_test "Weighted MP 11:45" 11 45
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/gre_multipath.sh b/tools/testing/selftests/net/forwarding/gre_multipath.sh
index cca2baa03fb8..a8d8e8b3dc81 100755
--- a/tools/testing/selftests/net/forwarding/gre_multipath.sh
+++ b/tools/testing/selftests/net/forwarding/gre_multipath.sh
@@ -93,18 +93,10 @@ sw1_create()
ip route add vrf v$ol1 192.0.2.16/28 \
nexthop dev g1a \
nexthop dev g1b
-
- tc qdisc add dev $ul1 clsact
- tc filter add dev $ul1 egress pref 111 prot ipv4 \
- flower dst_ip 192.0.2.66 action pass
- tc filter add dev $ul1 egress pref 222 prot ipv4 \
- flower dst_ip 192.0.2.82 action pass
}
sw1_destroy()
{
- tc qdisc del dev $ul1 clsact
-
ip route del vrf v$ol1 192.0.2.16/28
ip route del vrf v$ol1 192.0.2.82/32 via 192.0.2.146
@@ -139,10 +131,18 @@ sw2_create()
ip route add vrf v$ol2 192.0.2.0/28 \
nexthop dev g2a \
nexthop dev g2b
+
+ tc qdisc add dev $ul2 clsact
+ tc filter add dev $ul2 ingress pref 111 prot 802.1Q \
+ flower vlan_id 111 action pass
+ tc filter add dev $ul2 ingress pref 222 prot 802.1Q \
+ flower vlan_id 222 action pass
}
sw2_destroy()
{
+ tc qdisc del dev $ul2 clsact
+
ip route del vrf v$ol2 192.0.2.0/28
ip route del vrf v$ol2 192.0.2.81/32 via 192.0.2.145
@@ -187,12 +187,16 @@ setup_prepare()
sw1_create
sw2_create
h2_create
+
+ forwarding_enable
}
cleanup()
{
pre_cleanup
+ forwarding_restore
+
h2_destroy
sw2_destroy
sw1_destroy
@@ -211,15 +215,15 @@ multipath4_test()
nexthop dev g1a weight $weight1 \
nexthop dev g1b weight $weight2
- local t0_111=$(tc_rule_stats_get $ul1 111 egress)
- local t0_222=$(tc_rule_stats_get $ul1 222 egress)
+ local t0_111=$(tc_rule_stats_get $ul2 111 ingress)
+ local t0_222=$(tc_rule_stats_get $ul2 222 ingress)
ip vrf exec v$h1 \
$MZ $h1 -q -p 64 -A 192.0.2.1 -B 192.0.2.18 \
-d 1msec -t udp "sp=1024,dp=0-32768"
- local t1_111=$(tc_rule_stats_get $ul1 111 egress)
- local t1_222=$(tc_rule_stats_get $ul1 222 egress)
+ local t1_111=$(tc_rule_stats_get $ul2 111 ingress)
+ local t1_222=$(tc_rule_stats_get $ul2 222 ingress)
local d111=$((t1_111 - t0_111))
local d222=$((t1_222 - t0_222))
diff --git a/tools/testing/selftests/net/forwarding/ip6gre_inner_v4_multipath.sh b/tools/testing/selftests/net/forwarding/ip6gre_inner_v4_multipath.sh
new file mode 100755
index 000000000000..a257979d3fc5
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/ip6gre_inner_v4_multipath.sh
@@ -0,0 +1,304 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# Test traffic distribution when there are multiple routes between an IPv6
+# GRE tunnel. The tunnel carries IPv4 traffic between multiple hosts.
+# Multiple routes are in the underlay network. With the default multipath
+# policy, SW2 will only look at the outer IP addresses, hence only a single
+# route would be used.
+#
+# +-------------------------+
+# | H1 |
+# | $h1 + |
+# | 192.0.3.{2-62}/24 | |
+# +-------------------|-----+
+# |
+# +-------------------|-------------------------+
+# | SW1 | |
+# | $ol1 + |
+# | 192.0.3.1/24 |
+# | |
+# | + g1 (gre) |
+# | loc=2001:db8:40::1 |
+# | rem=2001:db8:40::2 --. |
+# | tos=inherit | |
+# | v |
+# | + $ul1 |
+# | | 2001:db8:80::1/64 |
+# +-------------------------|-------------------+
+# |
+# +-------------------------|-------------------+
+# | SW2 | |
+# | $ul21 + |
+# | 2001:db8:80::2/64 |
+# | | |
+# ! ________________|_____ |
+# | / \ |
+# | | | |
+# | + $ul22.111 (vlan) + $ul22.222 (vlan) |
+# | | 2001:db8:81::1/64 | 2001:db8:82::1/64 |
+# | | | |
+# +--|----------------------|-------------------+
+# | |
+# +--|----------------------|-------------------+
+# | | | |
+# | + $ul32.111 (vlan) + $ul32.222 (vlan) |
+# | | 2001:db8:81::2/64 | 2001:db8:82::2/64 |
+# | | | |
+# | \______________________/ |
+# | | |
+# | | |
+# | $ul31 + |
+# | 2001:db8:83::2/64 | SW3 |
+# +-------------------------|-------------------+
+# |
+# +-------------------------|-------------------+
+# | + $ul4 |
+# | ^ 2001:db8:83::1/64 |
+# | + g2 (gre) | |
+# | loc=2001:db8:40::2 | |
+# | rem=2001:db8:40::1 --' |
+# | tos=inherit |
+# | |
+# | $ol4 + |
+# | 192.0.4.1/24 | SW4 |
+# +--------------------|------------------------+
+# |
+# +--------------------|---------+
+# | | |
+# | $h2 + |
+# | 192.0.4.{2-62}/24 H2 |
+# +------------------------------+
+
+ALL_TESTS="
+ ping_ipv4
+ multipath_ipv4
+"
+
+NUM_NETIFS=10
+source lib.sh
+
+h1_create()
+{
+ simple_if_init $h1 192.0.3.2/24
+ ip route add vrf v$h1 192.0.4.0/24 via 192.0.3.1
+}
+
+h1_destroy()
+{
+ ip route del vrf v$h1 192.0.4.0/24 via 192.0.3.1
+ simple_if_fini $h1 192.0.3.2/24
+}
+
+sw1_create()
+{
+ simple_if_init $ol1 192.0.3.1/24
+ __simple_if_init $ul1 v$ol1 2001:db8:80::1/64
+
+ tunnel_create g1 ip6gre 2001:db8:40::1 2001:db8:40::2 tos inherit dev v$ol1
+ __simple_if_init g1 v$ol1 2001:db8:40::1/128
+ ip -6 route add vrf v$ol1 2001:db8:40::2/128 via 2001:db8:80::2
+
+ ip route add vrf v$ol1 192.0.4.0/24 nexthop dev g1
+}
+
+sw1_destroy()
+{
+ ip route del vrf v$ol1 192.0.4.0/24
+
+ ip -6 route del vrf v$ol1 2001:db8:40::2/128
+ __simple_if_fini g1 2001:db8:40::1/128
+ tunnel_destroy g1
+
+ __simple_if_fini $ul1 2001:db8:80::1/64
+ simple_if_fini $ol1 192.0.3.1/24
+}
+
+sw2_create()
+{
+ simple_if_init $ul21 2001:db8:80::2/64
+ __simple_if_init $ul22 v$ul21
+ vlan_create $ul22 111 v$ul21 2001:db8:81::1/64
+ vlan_create $ul22 222 v$ul21 2001:db8:82::1/64
+
+ ip -6 route add vrf v$ul21 2001:db8:40::1/128 via 2001:db8:80::1
+ ip -6 route add vrf v$ul21 2001:db8:40::2/128 \
+ nexthop via 2001:db8:81::2 \
+ nexthop via 2001:db8:82::2
+}
+
+sw2_destroy()
+{
+ ip -6 route del vrf v$ul21 2001:db8:40::2/128
+ ip -6 route del vrf v$ul21 2001:db8:40::1/128
+
+ vlan_destroy $ul22 222
+ vlan_destroy $ul22 111
+ __simple_if_fini $ul22
+ simple_if_fini $ul21 2001:db8:80::2/64
+}
+
+sw3_create()
+{
+ simple_if_init $ul31 2001:db8:83::2/64
+ __simple_if_init $ul32 v$ul31
+ vlan_create $ul32 111 v$ul31 2001:db8:81::2/64
+ vlan_create $ul32 222 v$ul31 2001:db8:82::2/64
+
+ ip -6 route add vrf v$ul31 2001:db8:40::2/128 via 2001:db8:83::1
+ ip -6 route add vrf v$ul31 2001:db8:40::1/128 \
+ nexthop via 2001:db8:81::1 \
+ nexthop via 2001:db8:82::1
+
+ tc qdisc add dev $ul32 clsact
+ tc filter add dev $ul32 ingress pref 111 prot 802.1Q \
+ flower vlan_id 111 action pass
+ tc filter add dev $ul32 ingress pref 222 prot 802.1Q \
+ flower vlan_id 222 action pass
+}
+
+sw3_destroy()
+{
+ tc qdisc del dev $ul32 clsact
+
+ ip -6 route del vrf v$ul31 2001:db8:40::1/128
+ ip -6 route del vrf v$ul31 2001:db8:40::2/128
+
+ vlan_destroy $ul32 222
+ vlan_destroy $ul32 111
+ __simple_if_fini $ul32
+ simple_if_fini $ul31 2001:Db8:83::2/64
+}
+
+sw4_create()
+{
+ simple_if_init $ol4 192.0.4.1/24
+ __simple_if_init $ul4 v$ol4 2001:db8:83::1/64
+
+ tunnel_create g2 ip6gre 2001:db8:40::2 2001:db8:40::1 tos inherit dev v$ol4
+ __simple_if_init g2 v$ol4 2001:db8:40::2/128
+ ip -6 route add vrf v$ol4 2001:db8:40::1/128 via 2001:db8:83::2
+
+ ip route add vrf v$ol4 192.0.3.0/24 nexthop dev g2
+}
+
+sw4_destroy()
+{
+ ip route del vrf v$ol4 192.0.3.0/24
+
+ ip -6 route del vrf v$ol4 2001:db8:40::1/128
+ __simple_if_fini g2 2001:db8:40::2/128
+ tunnel_destroy g2
+
+ __simple_if_fini $ul4 2001:db8:83::1/64
+ simple_if_fini $ol4 192.0.4.1/24
+}
+
+h2_create()
+{
+ simple_if_init $h2 192.0.4.2/24
+ ip route add vrf v$h2 192.0.3.0/24 via 192.0.4.1
+}
+
+h2_destroy()
+{
+ ip route del vrf v$h2 192.0.3.0/24 via 192.0.4.1
+ simple_if_fini $h2 192.0.4.2/24
+}
+
+setup_prepare()
+{
+ h1=${NETIFS[p1]}
+
+ ol1=${NETIFS[p2]}
+ ul1=${NETIFS[p3]}
+
+ ul21=${NETIFS[p4]}
+ ul22=${NETIFS[p5]}
+
+ ul32=${NETIFS[p6]}
+ ul31=${NETIFS[p7]}
+
+ ul4=${NETIFS[p8]}
+ ol4=${NETIFS[p9]}
+
+ h2=${NETIFS[p10]}
+
+ vrf_prepare
+ h1_create
+ sw1_create
+ sw2_create
+ sw3_create
+ sw4_create
+ h2_create
+
+ forwarding_enable
+}
+
+cleanup()
+{
+ pre_cleanup
+
+ forwarding_restore
+
+ h2_destroy
+ sw4_destroy
+ sw3_destroy
+ sw2_destroy
+ sw1_destroy
+ h1_destroy
+ vrf_cleanup
+}
+
+multipath4_test()
+{
+ local what=$1; shift
+ local weight1=$1; shift
+ local weight2=$1; shift
+
+ sysctl_set net.ipv6.fib_multipath_hash_policy 2
+ ip route replace vrf v$ul21 2001:db8:40::2/128 \
+ nexthop via 2001:db8:81::2 weight $weight1 \
+ nexthop via 2001:db8:82::2 weight $weight2
+
+ local t0_111=$(tc_rule_stats_get $ul32 111 ingress)
+ local t0_222=$(tc_rule_stats_get $ul32 222 ingress)
+
+ ip vrf exec v$h1 \
+ $MZ $h1 -q -p 64 -A "192.0.3.2-192.0.3.62" -B "192.0.4.2-192.0.4.62" \
+ -d 1msec -c 50 -t udp "sp=1024,dp=1024"
+ sleep 1
+
+ local t1_111=$(tc_rule_stats_get $ul32 111 ingress)
+ local t1_222=$(tc_rule_stats_get $ul32 222 ingress)
+
+ local d111=$((t1_111 - t0_111))
+ local d222=$((t1_222 - t0_222))
+ multipath_eval "$what" $weight1 $weight2 $d111 $d222
+
+ ip route replace vrf v$ul21 2001:db8:40::2/128 \
+ nexthop via 2001:db8:81::2 \
+ nexthop via 2001:db8:82::2
+ sysctl_restore net.ipv6.fib_multipath_hash_policy
+}
+
+ping_ipv4()
+{
+ ping_test $h1 192.0.4.2
+}
+
+multipath_ipv4()
+{
+ log_info "Running IPv4 over GRE over IPv6 multipath tests"
+ multipath4_test "ECMP" 1 1
+ multipath4_test "Weighted MP 2:1" 2 1
+ multipath4_test "Weighted MP 11:45" 11 45
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/ip6gre_inner_v6_multipath.sh b/tools/testing/selftests/net/forwarding/ip6gre_inner_v6_multipath.sh
new file mode 100755
index 000000000000..d208f5243ade
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/ip6gre_inner_v6_multipath.sh
@@ -0,0 +1,305 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# Test traffic distribution when there are multiple routes between an IPv6
+# GRE tunnel. The tunnel carries IPv6 traffic between multiple hosts.
+# Multiple routes are in the underlay network. With the default multipath
+# policy, SW2 will only look at the outer IP addresses, hence only a single
+# route would be used.
+#
+# +-------------------------+
+# | H1 |
+# | $h1 + |
+# | 2001:db8:1::2/64 | |
+# +-------------------|-----+
+# |
+# +-------------------|-------------------------+
+# | SW1 | |
+# | $ol1 + |
+# | 2001:db8:1::1/64 |
+# | |
+# | + g1 (gre) |
+# | loc=2001:db8:40::1 |
+# | rem=2001:db8:40::2 --. |
+# | tos=inherit | |
+# | v |
+# | + $ul1 |
+# | | 2001:db8:80::1/64 |
+# +-------------------------|-------------------+
+# |
+# +-------------------------|-------------------+
+# | SW2 | |
+# | $ul21 + |
+# | 2001:db8:80::2/64 |
+# | | |
+# ! ________________|_____ |
+# | / \ |
+# | | | |
+# | + $ul22.111 (vlan) + $ul22.222 (vlan) |
+# | | 2001:db8:81::1/64 | 2001:db8:82::1/64 |
+# | | | |
+# +--|----------------------|-------------------+
+# | |
+# +--|----------------------|-------------------+
+# | | | |
+# | + $ul32.111 (vlan) + $ul32.222 (vlan) |
+# | | 2001:db8:81::2/64 | 2001:db8:82::2/64 |
+# | | | |
+# | \______________________/ |
+# | | |
+# | | |
+# | $ul31 + |
+# | 2001:db8:83::2/64 | SW3 |
+# +-------------------------|-------------------+
+# |
+# +-------------------------|-------------------+
+# | + $ul4 |
+# | ^ 2001:db8:83::1/64 |
+# | + g2 (gre) | |
+# | loc=2001:db8:40::2 | |
+# | rem=2001:db8:40::1 --' |
+# | tos=inherit |
+# | |
+# | $ol4 + |
+# | 2001:db8:2::1/64 | SW4 |
+# +--------------------|------------------------+
+# |
+# +--------------------|---------+
+# | | |
+# | $h2 + |
+# | 2001:db8:2::2/64 H2 |
+# +------------------------------+
+
+ALL_TESTS="
+ ping_ipv6
+ multipath_ipv6
+"
+
+NUM_NETIFS=10
+source lib.sh
+
+h1_create()
+{
+ simple_if_init $h1 2001:db8:1::2/64
+ ip -6 route add vrf v$h1 2001:db8:2::/64 via 2001:db8:1::1
+}
+
+h1_destroy()
+{
+ ip -6 route del vrf v$h1 2001:db8:2::/64 via 2001:db8:1::1
+ simple_if_fini $h1 2001:db8:1::2/64
+}
+
+sw1_create()
+{
+ simple_if_init $ol1 2001:db8:1::1/64
+ __simple_if_init $ul1 v$ol1 2001:db8:80::1/64
+
+ tunnel_create g1 ip6gre 2001:db8:40::1 2001:db8:40::2 tos inherit dev v$ol1
+ __simple_if_init g1 v$ol1 2001:db8:40::1/128
+ ip -6 route add vrf v$ol1 2001:db8:40::2/128 via 2001:db8:80::2
+
+ ip -6 route add vrf v$ol1 2001:db8:2::/64 dev g1
+}
+
+sw1_destroy()
+{
+ ip -6 route del vrf v$ol1 2001:db8:2::/64
+
+ ip -6 route del vrf v$ol1 2001:db8:40::2/128
+ __simple_if_fini g1 2001:db8:40::1/128
+ tunnel_destroy g1
+
+ __simple_if_fini $ul1 2001:db8:80::1/64
+ simple_if_fini $ol1 2001:db8:1::1/64
+}
+
+sw2_create()
+{
+ simple_if_init $ul21 2001:db8:80::2/64
+ __simple_if_init $ul22 v$ul21
+ vlan_create $ul22 111 v$ul21 2001:db8:81::1/64
+ vlan_create $ul22 222 v$ul21 2001:db8:82::1/64
+
+ ip -6 route add vrf v$ul21 2001:db8:40::1/128 via 2001:db8:80::1
+ ip -6 route add vrf v$ul21 2001:db8:40::2/128 \
+ nexthop via 2001:db8:81::2 \
+ nexthop via 2001:db8:82::2
+}
+
+sw2_destroy()
+{
+ ip -6 route del vrf v$ul21 2001:db8:40::2/128
+ ip -6 route del vrf v$ul21 2001:db8:40::1/128
+
+ vlan_destroy $ul22 222
+ vlan_destroy $ul22 111
+ __simple_if_fini $ul22
+ simple_if_fini $ul21 2001:db8:80::2/64
+}
+
+sw3_create()
+{
+ simple_if_init $ul31 2001:db8:83::2/64
+ __simple_if_init $ul32 v$ul31
+ vlan_create $ul32 111 v$ul31 2001:db8:81::2/64
+ vlan_create $ul32 222 v$ul31 2001:db8:82::2/64
+
+ ip -6 route add vrf v$ul31 2001:db8:40::2/128 via 2001:db8:83::1
+ ip -6 route add vrf v$ul31 2001:db8:40::1/128 \
+ nexthop via 2001:db8:81::1 \
+ nexthop via 2001:db8:82::1
+
+ tc qdisc add dev $ul32 clsact
+ tc filter add dev $ul32 ingress pref 111 prot 802.1Q \
+ flower vlan_id 111 action pass
+ tc filter add dev $ul32 ingress pref 222 prot 802.1Q \
+ flower vlan_id 222 action pass
+}
+
+sw3_destroy()
+{
+ tc qdisc del dev $ul32 clsact
+
+ ip -6 route del vrf v$ul31 2001:db8:40::1/128
+ ip -6 route del vrf v$ul31 2001:db8:40::2/128
+
+ vlan_destroy $ul32 222
+ vlan_destroy $ul32 111
+ __simple_if_fini $ul32
+ simple_if_fini $ul31 2001:Db8:83::2/64
+}
+
+sw4_create()
+{
+ simple_if_init $ol4 2001:db8:2::1/64
+ __simple_if_init $ul4 v$ol4 2001:db8:83::1/64
+
+ tunnel_create g2 ip6gre 2001:db8:40::2 2001:db8:40::1 tos inherit dev v$ol4
+ __simple_if_init g2 v$ol4 2001:db8:40::2/128
+ ip -6 route add vrf v$ol4 2001:db8:40::1/128 via 2001:db8:83::2
+
+ ip -6 route add vrf v$ol4 2001:db8:1::/64 dev g2
+}
+
+sw4_destroy()
+{
+ ip -6 route del vrf v$ol4 2001:db8:1::/64
+
+ ip -6 route del vrf v$ol4 2001:db8:40::1/128
+ __simple_if_fini g2 2001:db8:40::2/128
+ tunnel_destroy g2
+
+ __simple_if_fini $ul4 2001:db8:83::1/64
+ simple_if_fini $ol4 2001:db8:2::1/64
+}
+
+h2_create()
+{
+ simple_if_init $h2 2001:db8:2::2/64
+ ip -6 route add vrf v$h2 2001:db8:1::/64 via 2001:db8:2::1
+}
+
+h2_destroy()
+{
+ ip -6 route del vrf v$h2 2001:db8:1::/64 via 2001:db8:2::1
+ simple_if_fini $h2 2001:db8:2::2/64
+}
+
+setup_prepare()
+{
+ h1=${NETIFS[p1]}
+
+ ol1=${NETIFS[p2]}
+ ul1=${NETIFS[p3]}
+
+ ul21=${NETIFS[p4]}
+ ul22=${NETIFS[p5]}
+
+ ul32=${NETIFS[p6]}
+ ul31=${NETIFS[p7]}
+
+ ul4=${NETIFS[p8]}
+ ol4=${NETIFS[p9]}
+
+ h2=${NETIFS[p10]}
+
+ vrf_prepare
+ h1_create
+ sw1_create
+ sw2_create
+ sw3_create
+ sw4_create
+ h2_create
+
+ forwarding_enable
+}
+
+cleanup()
+{
+ pre_cleanup
+
+ forwarding_restore
+
+ h2_destroy
+ sw4_destroy
+ sw3_destroy
+ sw2_destroy
+ sw1_destroy
+ h1_destroy
+ vrf_cleanup
+}
+
+multipath6_test()
+{
+ local what=$1; shift
+ local weight1=$1; shift
+ local weight2=$1; shift
+
+ sysctl_set net.ipv6.fib_multipath_hash_policy 2
+ ip route replace vrf v$ul21 2001:db8:40::2/128 \
+ nexthop via 2001:db8:81::2 weight $weight1 \
+ nexthop via 2001:db8:82::2 weight $weight2
+
+ local t0_111=$(tc_rule_stats_get $ul32 111 ingress)
+ local t0_222=$(tc_rule_stats_get $ul32 222 ingress)
+
+ ip vrf exec v$h1 \
+ $MZ $h1 -6 -q -p 64 -A "2001:db8:1::2-2001:db8:1::1e" \
+ -B "2001:db8:2::2-2001:db8:2::1e" \
+ -d 1msec -c 50 -t udp "sp=1024,dp=1024"
+ sleep 1
+
+ local t1_111=$(tc_rule_stats_get $ul32 111 ingress)
+ local t1_222=$(tc_rule_stats_get $ul32 222 ingress)
+
+ local d111=$((t1_111 - t0_111))
+ local d222=$((t1_222 - t0_222))
+ multipath_eval "$what" $weight1 $weight2 $d111 $d222
+
+ ip route replace vrf v$ul21 2001:db8:40::2/128 \
+ nexthop via 2001:db8:81::2 \
+ nexthop via 2001:db8:82::2
+ sysctl_restore net.ipv6.fib_multipath_hash_policy
+}
+
+ping_ipv6()
+{
+ ping_test $h1 2001:db8:2::2
+}
+
+multipath_ipv6()
+{
+ log_info "Running IPv6 over GRE over IPv6 multipath tests"
+ multipath6_test "ECMP" 1 1
+ multipath6_test "Weighted MP 2:1" 2 1
+ multipath6_test "Weighted MP 11:45" 11 45
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh
index 9385dc971269..85c587a03c8a 100644
--- a/tools/testing/selftests/net/forwarding/lib.sh
+++ b/tools/testing/selftests/net/forwarding/lib.sh
@@ -250,6 +250,25 @@ setup_wait()
sleep $WAIT_TIME
}
+cmd_jq()
+{
+ local cmd=$1
+ local jq_exp=$2
+ local ret
+ local output
+
+ output="$($cmd)"
+ # it the command fails, return error right away
+ ret=$?
+ if [[ $ret -ne 0 ]]; then
+ return $ret
+ fi
+ output=$(echo $output | jq -r "$jq_exp")
+ echo $output
+ # return success only in case of non-empty output
+ [ ! -z "$output" ]
+}
+
lldpad_app_wait_set()
{
local dev=$1; shift
diff --git a/tools/testing/selftests/net/forwarding/router_broadcast.sh b/tools/testing/selftests/net/forwarding/router_broadcast.sh
index 9a678ece32b4..4eac0a06f451 100755
--- a/tools/testing/selftests/net/forwarding/router_broadcast.sh
+++ b/tools/testing/selftests/net/forwarding/router_broadcast.sh
@@ -145,16 +145,19 @@ bc_forwarding_disable()
{
sysctl_set net.ipv4.conf.all.bc_forwarding 0
sysctl_set net.ipv4.conf.$rp1.bc_forwarding 0
+ sysctl_set net.ipv4.conf.$rp2.bc_forwarding 0
}
bc_forwarding_enable()
{
sysctl_set net.ipv4.conf.all.bc_forwarding 1
sysctl_set net.ipv4.conf.$rp1.bc_forwarding 1
+ sysctl_set net.ipv4.conf.$rp2.bc_forwarding 1
}
bc_forwarding_restore()
{
+ sysctl_restore net.ipv4.conf.$rp2.bc_forwarding
sysctl_restore net.ipv4.conf.$rp1.bc_forwarding
sysctl_restore net.ipv4.conf.all.bc_forwarding
}
@@ -171,7 +174,7 @@ ping_test_from()
log_info "ping $dip, expected reply from $from"
ip vrf exec $(master_name_get $oif) \
$PING -I $oif $dip -c 10 -i 0.1 -w $PING_TIMEOUT -b 2>&1 \
- | grep $from &> /dev/null
+ | grep "bytes from $from" > /dev/null
check_err_fail $fail $?
}
diff --git a/tools/testing/selftests/net/forwarding/router_mpath_nh.sh b/tools/testing/selftests/net/forwarding/router_mpath_nh.sh
new file mode 100755
index 000000000000..cf3d26c233e8
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/router_mpath_nh.sh
@@ -0,0 +1,359 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+ALL_TESTS="ping_ipv4 ping_ipv6 multipath_test"
+NUM_NETIFS=8
+source lib.sh
+
+h1_create()
+{
+ vrf_create "vrf-h1"
+ ip link set dev $h1 master vrf-h1
+
+ ip link set dev vrf-h1 up
+ ip link set dev $h1 up
+
+ ip address add 192.0.2.2/24 dev $h1
+ ip address add 2001:db8:1::2/64 dev $h1
+
+ ip route add 198.51.100.0/24 vrf vrf-h1 nexthop via 192.0.2.1
+ ip route add 2001:db8:2::/64 vrf vrf-h1 nexthop via 2001:db8:1::1
+}
+
+h1_destroy()
+{
+ ip route del 2001:db8:2::/64 vrf vrf-h1
+ ip route del 198.51.100.0/24 vrf vrf-h1
+
+ ip address del 2001:db8:1::2/64 dev $h1
+ ip address del 192.0.2.2/24 dev $h1
+
+ ip link set dev $h1 down
+ vrf_destroy "vrf-h1"
+}
+
+h2_create()
+{
+ vrf_create "vrf-h2"
+ ip link set dev $h2 master vrf-h2
+
+ ip link set dev vrf-h2 up
+ ip link set dev $h2 up
+
+ ip address add 198.51.100.2/24 dev $h2
+ ip address add 2001:db8:2::2/64 dev $h2
+
+ ip route add 192.0.2.0/24 vrf vrf-h2 nexthop via 198.51.100.1
+ ip route add 2001:db8:1::/64 vrf vrf-h2 nexthop via 2001:db8:2::1
+}
+
+h2_destroy()
+{
+ ip route del 2001:db8:1::/64 vrf vrf-h2
+ ip route del 192.0.2.0/24 vrf vrf-h2
+
+ ip address del 2001:db8:2::2/64 dev $h2
+ ip address del 198.51.100.2/24 dev $h2
+
+ ip link set dev $h2 down
+ vrf_destroy "vrf-h2"
+}
+
+router1_create()
+{
+ vrf_create "vrf-r1"
+ ip link set dev $rp11 master vrf-r1
+ ip link set dev $rp12 master vrf-r1
+ ip link set dev $rp13 master vrf-r1
+
+ ip link set dev vrf-r1 up
+ ip link set dev $rp11 up
+ ip link set dev $rp12 up
+ ip link set dev $rp13 up
+
+ ip address add 192.0.2.1/24 dev $rp11
+ ip address add 2001:db8:1::1/64 dev $rp11
+
+ ip address add 169.254.2.12/24 dev $rp12
+ ip address add fe80:2::12/64 dev $rp12
+
+ ip address add 169.254.3.13/24 dev $rp13
+ ip address add fe80:3::13/64 dev $rp13
+}
+
+router1_destroy()
+{
+ ip route del 2001:db8:2::/64 vrf vrf-r1
+ ip route del 198.51.100.0/24 vrf vrf-r1
+
+ ip address del fe80:3::13/64 dev $rp13
+ ip address del 169.254.3.13/24 dev $rp13
+
+ ip address del fe80:2::12/64 dev $rp12
+ ip address del 169.254.2.12/24 dev $rp12
+
+ ip address del 2001:db8:1::1/64 dev $rp11
+ ip address del 192.0.2.1/24 dev $rp11
+
+ ip nexthop del id 103
+ ip nexthop del id 101
+ ip nexthop del id 102
+ ip nexthop del id 106
+ ip nexthop del id 104
+ ip nexthop del id 105
+
+ ip link set dev $rp13 down
+ ip link set dev $rp12 down
+ ip link set dev $rp11 down
+
+ vrf_destroy "vrf-r1"
+}
+
+router2_create()
+{
+ vrf_create "vrf-r2"
+ ip link set dev $rp21 master vrf-r2
+ ip link set dev $rp22 master vrf-r2
+ ip link set dev $rp23 master vrf-r2
+
+ ip link set dev vrf-r2 up
+ ip link set dev $rp21 up
+ ip link set dev $rp22 up
+ ip link set dev $rp23 up
+
+ ip address add 198.51.100.1/24 dev $rp21
+ ip address add 2001:db8:2::1/64 dev $rp21
+
+ ip address add 169.254.2.22/24 dev $rp22
+ ip address add fe80:2::22/64 dev $rp22
+
+ ip address add 169.254.3.23/24 dev $rp23
+ ip address add fe80:3::23/64 dev $rp23
+}
+
+router2_destroy()
+{
+ ip route del 2001:db8:1::/64 vrf vrf-r2
+ ip route del 192.0.2.0/24 vrf vrf-r2
+
+ ip address del fe80:3::23/64 dev $rp23
+ ip address del 169.254.3.23/24 dev $rp23
+
+ ip address del fe80:2::22/64 dev $rp22
+ ip address del 169.254.2.22/24 dev $rp22
+
+ ip address del 2001:db8:2::1/64 dev $rp21
+ ip address del 198.51.100.1/24 dev $rp21
+
+ ip nexthop del id 201
+ ip nexthop del id 202
+ ip nexthop del id 204
+ ip nexthop del id 205
+
+ ip link set dev $rp23 down
+ ip link set dev $rp22 down
+ ip link set dev $rp21 down
+
+ vrf_destroy "vrf-r2"
+}
+
+routing_nh_obj()
+{
+ ip nexthop add id 101 via 169.254.2.22 dev $rp12
+ ip nexthop add id 102 via 169.254.3.23 dev $rp13
+ ip nexthop add id 103 group 101/102
+ ip route add 198.51.100.0/24 vrf vrf-r1 nhid 103
+
+ ip nexthop add id 104 via fe80:2::22 dev $rp12
+ ip nexthop add id 105 via fe80:3::23 dev $rp13
+ ip nexthop add id 106 group 104/105
+ ip route add 2001:db8:2::/64 vrf vrf-r1 nhid 106
+
+ ip nexthop add id 201 via 169.254.2.12 dev $rp22
+ ip nexthop add id 202 via 169.254.3.13 dev $rp23
+ ip nexthop add id 203 group 201/202
+ ip route add 192.0.2.0/24 vrf vrf-r2 nhid 203
+
+ ip nexthop add id 204 via fe80:2::12 dev $rp22
+ ip nexthop add id 205 via fe80:3::13 dev $rp23
+ ip nexthop add id 206 group 204/205
+ ip route add 2001:db8:1::/64 vrf vrf-r2 nhid 206
+}
+
+multipath4_test()
+{
+ local desc="$1"
+ local weight_rp12=$2
+ local weight_rp13=$3
+ local t0_rp12 t0_rp13 t1_rp12 t1_rp13
+ local packets_rp12 packets_rp13
+
+ # Transmit multiple flows from h1 to h2 and make sure they are
+ # distributed between both multipath links (rp12 and rp13)
+ # according to the configured weights.
+ sysctl_set net.ipv4.fib_multipath_hash_policy 1
+ ip nexthop replace id 103 group 101,$weight_rp12/102,$weight_rp13
+
+ t0_rp12=$(link_stats_tx_packets_get $rp12)
+ t0_rp13=$(link_stats_tx_packets_get $rp13)
+
+ ip vrf exec vrf-h1 $MZ -q -p 64 -A 192.0.2.2 -B 198.51.100.2 \
+ -d 1msec -t udp "sp=1024,dp=0-32768"
+
+ t1_rp12=$(link_stats_tx_packets_get $rp12)
+ t1_rp13=$(link_stats_tx_packets_get $rp13)
+
+ let "packets_rp12 = $t1_rp12 - $t0_rp12"
+ let "packets_rp13 = $t1_rp13 - $t0_rp13"
+ multipath_eval "$desc" $weight_rp12 $weight_rp13 $packets_rp12 $packets_rp13
+
+ # Restore settings.
+ ip nexthop replace id 103 group 101/102
+ sysctl_restore net.ipv4.fib_multipath_hash_policy
+}
+
+multipath6_l4_test()
+{
+ local desc="$1"
+ local weight_rp12=$2
+ local weight_rp13=$3
+ local t0_rp12 t0_rp13 t1_rp12 t1_rp13
+ local packets_rp12 packets_rp13
+
+ # Transmit multiple flows from h1 to h2 and make sure they are
+ # distributed between both multipath links (rp12 and rp13)
+ # according to the configured weights.
+ sysctl_set net.ipv6.fib_multipath_hash_policy 1
+
+ ip nexthop replace id 106 group 104,$weight_rp12/105,$weight_rp13
+
+ t0_rp12=$(link_stats_tx_packets_get $rp12)
+ t0_rp13=$(link_stats_tx_packets_get $rp13)
+
+ $MZ $h1 -6 -q -p 64 -A 2001:db8:1::2 -B 2001:db8:2::2 \
+ -d 1msec -t udp "sp=1024,dp=0-32768"
+
+ t1_rp12=$(link_stats_tx_packets_get $rp12)
+ t1_rp13=$(link_stats_tx_packets_get $rp13)
+
+ let "packets_rp12 = $t1_rp12 - $t0_rp12"
+ let "packets_rp13 = $t1_rp13 - $t0_rp13"
+ multipath_eval "$desc" $weight_rp12 $weight_rp13 $packets_rp12 $packets_rp13
+
+ ip nexthop replace id 106 group 104/105
+
+ sysctl_restore net.ipv6.fib_multipath_hash_policy
+}
+
+multipath6_test()
+{
+ local desc="$1"
+ local weight_rp12=$2
+ local weight_rp13=$3
+ local t0_rp12 t0_rp13 t1_rp12 t1_rp13
+ local packets_rp12 packets_rp13
+
+ ip nexthop replace id 106 group 104,$weight_rp12/105,$weight_rp13
+
+ t0_rp12=$(link_stats_tx_packets_get $rp12)
+ t0_rp13=$(link_stats_tx_packets_get $rp13)
+
+ # Generate 16384 echo requests, each with a random flow label.
+ for _ in $(seq 1 16384); do
+ ip vrf exec vrf-h1 $PING6 2001:db8:2::2 -F 0 -c 1 -q >/dev/null 2>&1
+ done
+
+ t1_rp12=$(link_stats_tx_packets_get $rp12)
+ t1_rp13=$(link_stats_tx_packets_get $rp13)
+
+ let "packets_rp12 = $t1_rp12 - $t0_rp12"
+ let "packets_rp13 = $t1_rp13 - $t0_rp13"
+ multipath_eval "$desc" $weight_rp12 $weight_rp13 $packets_rp12 $packets_rp13
+
+ ip nexthop replace id 106 group 104/105
+}
+
+multipath_test()
+{
+ log_info "Running IPv4 multipath tests"
+ multipath4_test "ECMP" 1 1
+ multipath4_test "Weighted MP 2:1" 2 1
+ multipath4_test "Weighted MP 11:45" 11 45
+
+ log_info "Running IPv6 multipath tests"
+ multipath6_test "ECMP" 1 1
+ multipath6_test "Weighted MP 2:1" 2 1
+ multipath6_test "Weighted MP 11:45" 11 45
+
+ log_info "Running IPv6 L4 hash multipath tests"
+ multipath6_l4_test "ECMP" 1 1
+ multipath6_l4_test "Weighted MP 2:1" 2 1
+ multipath6_l4_test "Weighted MP 11:45" 11 45
+}
+
+setup_prepare()
+{
+ h1=${NETIFS[p1]}
+ rp11=${NETIFS[p2]}
+
+ rp12=${NETIFS[p3]}
+ rp22=${NETIFS[p4]}
+
+ rp13=${NETIFS[p5]}
+ rp23=${NETIFS[p6]}
+
+ rp21=${NETIFS[p7]}
+ h2=${NETIFS[p8]}
+
+ vrf_prepare
+
+ h1_create
+ h2_create
+
+ router1_create
+ router2_create
+ routing_nh_obj
+
+ forwarding_enable
+}
+
+cleanup()
+{
+ pre_cleanup
+
+ forwarding_restore
+
+ router2_destroy
+ router1_destroy
+
+ h2_destroy
+ h1_destroy
+
+ vrf_cleanup
+}
+
+ping_ipv4()
+{
+ ping_test $h1 198.51.100.2
+}
+
+ping_ipv6()
+{
+ ping6_test $h1 2001:db8:2::2
+}
+
+ip nexthop ls >/dev/null 2>&1
+if [ $? -ne 0 ]; then
+ echo "Nexthop objects not supported; skipping tests"
+ exit 0
+fi
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+routing_nh_obj
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/tc_common.sh b/tools/testing/selftests/net/forwarding/tc_common.sh
index 9d3b64a2a264..315e934358d4 100644
--- a/tools/testing/selftests/net/forwarding/tc_common.sh
+++ b/tools/testing/selftests/net/forwarding/tc_common.sh
@@ -8,18 +8,9 @@ tc_check_packets()
local id=$1
local handle=$2
local count=$3
- local ret
- output="$(tc -j -s filter show $id)"
- # workaround the jq bug which causes jq to return 0 in case input is ""
- ret=$?
- if [[ $ret -ne 0 ]]; then
- return $ret
- fi
- echo $output | \
- jq -e ".[] \
- | select(.options.handle == $handle) \
- | select(.options.actions[0].stats.packets == $count)" \
- &> /dev/null
- return $?
+ cmd_jq "tc -j -s filter show $id" \
+ ".[] | select(.options.handle == $handle) | \
+ select(.options.actions[0].stats.packets == $count)" \
+ &> /dev/null
}
diff --git a/tools/testing/selftests/net/forwarding/tc_flower.sh b/tools/testing/selftests/net/forwarding/tc_flower.sh
index 29bcfa84aec7..058c746ee300 100755
--- a/tools/testing/selftests/net/forwarding/tc_flower.sh
+++ b/tools/testing/selftests/net/forwarding/tc_flower.sh
@@ -2,7 +2,8 @@
# SPDX-License-Identifier: GPL-2.0
ALL_TESTS="match_dst_mac_test match_src_mac_test match_dst_ip_test \
- match_src_ip_test match_ip_flags_test match_pcp_test match_vlan_test"
+ match_src_ip_test match_ip_flags_test match_pcp_test match_vlan_test \
+ match_ip_tos_test match_indev_test"
NUM_NETIFS=2
source tc_common.sh
source lib.sh
@@ -276,6 +277,63 @@ match_vlan_test()
log_test "VLAN match ($tcflags)"
}
+match_ip_tos_test()
+{
+ RET=0
+
+ tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
+ $tcflags dst_ip 192.0.2.2 ip_tos 0x20 action drop
+ tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
+ $tcflags dst_ip 192.0.2.2 ip_tos 0x18 action drop
+
+ $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
+ -t ip tos=18 -q
+
+ tc_check_packets "dev $h2 ingress" 101 1
+ check_fail $? "Matched on a wrong filter (0x18)"
+
+ tc_check_packets "dev $h2 ingress" 102 1
+ check_err $? "Did not match on correct filter (0x18)"
+
+ $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
+ -t ip tos=20 -q
+
+ tc_check_packets "dev $h2 ingress" 102 2
+ check_fail $? "Matched on a wrong filter (0x20)"
+
+ tc_check_packets "dev $h2 ingress" 101 1
+ check_err $? "Did not match on correct filter (0x20)"
+
+ tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
+ tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
+
+ log_test "ip_tos match ($tcflags)"
+}
+
+match_indev_test()
+{
+ RET=0
+
+ tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
+ $tcflags indev $h1 dst_mac $h2mac action drop
+ tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
+ $tcflags indev $h2 dst_mac $h2mac action drop
+
+ $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
+ -t ip -q
+
+ tc_check_packets "dev $h2 ingress" 101 1
+ check_fail $? "Matched on a wrong filter"
+
+ tc_check_packets "dev $h2 ingress" 102 1
+ check_err $? "Did not match on correct filter"
+
+ tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
+ tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
+
+ log_test "indev match ($tcflags)"
+}
+
setup_prepare()
{
h1=${NETIFS[p1]}
diff --git a/tools/testing/selftests/net/forwarding/tc_flower_router.sh b/tools/testing/selftests/net/forwarding/tc_flower_router.sh
new file mode 100755
index 000000000000..4aee9c9e69f6
--- /dev/null
+++ b/tools/testing/selftests/net/forwarding/tc_flower_router.sh
@@ -0,0 +1,172 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+ALL_TESTS="match_indev_egress_test"
+NUM_NETIFS=6
+source tc_common.sh
+source lib.sh
+
+h1_create()
+{
+ simple_if_init $h1 192.0.1.1/24
+
+ ip route add 192.0.2.0/24 vrf v$h1 nexthop via 192.0.1.2
+ ip route add 192.0.3.0/24 vrf v$h1 nexthop via 192.0.1.2
+}
+
+h1_destroy()
+{
+ ip route del 192.0.3.0/24 vrf v$h1
+ ip route del 192.0.2.0/24 vrf v$h1
+
+ simple_if_fini $h1 192.0.1.1/24
+}
+
+h2_create()
+{
+ simple_if_init $h2 192.0.2.1/24
+
+ ip route add 192.0.1.0/24 vrf v$h2 nexthop via 192.0.2.2
+ ip route add 192.0.3.0/24 vrf v$h2 nexthop via 192.0.2.2
+}
+
+h2_destroy()
+{
+ ip route del 192.0.3.0/24 vrf v$h2
+ ip route del 192.0.1.0/24 vrf v$h2
+
+ simple_if_fini $h2 192.0.2.1/24
+}
+
+h3_create()
+{
+ simple_if_init $h3 192.0.3.1/24
+
+ ip route add 192.0.1.0/24 vrf v$h3 nexthop via 192.0.3.2
+ ip route add 192.0.2.0/24 vrf v$h3 nexthop via 192.0.3.2
+}
+
+h3_destroy()
+{
+ ip route del 192.0.2.0/24 vrf v$h3
+ ip route del 192.0.1.0/24 vrf v$h3
+
+ simple_if_fini $h3 192.0.3.1/24
+}
+
+
+router_create()
+{
+ ip link set dev $rp1 up
+ ip link set dev $rp2 up
+ ip link set dev $rp3 up
+
+ tc qdisc add dev $rp3 clsact
+
+ ip address add 192.0.1.2/24 dev $rp1
+ ip address add 192.0.2.2/24 dev $rp2
+ ip address add 192.0.3.2/24 dev $rp3
+}
+
+router_destroy()
+{
+ ip address del 192.0.3.2/24 dev $rp3
+ ip address del 192.0.2.2/24 dev $rp2
+ ip address del 192.0.1.2/24 dev $rp1
+
+ tc qdisc del dev $rp3 clsact
+
+ ip link set dev $rp3 down
+ ip link set dev $rp2 down
+ ip link set dev $rp1 down
+}
+
+match_indev_egress_test()
+{
+ RET=0
+
+ tc filter add dev $rp3 egress protocol ip pref 1 handle 101 flower \
+ $tcflags indev $rp1 dst_ip 192.0.3.1 action drop
+ tc filter add dev $rp3 egress protocol ip pref 2 handle 102 flower \
+ $tcflags indev $rp2 dst_ip 192.0.3.1 action drop
+
+ $MZ $h1 -c 1 -p 64 -a $h1mac -b $rp1mac -A 192.0.1.1 -B 192.0.3.1 \
+ -t ip -q
+
+ tc_check_packets "dev $rp3 egress" 102 1
+ check_fail $? "Matched on a wrong filter"
+
+ tc_check_packets "dev $rp3 egress" 101 1
+ check_err $? "Did not match on correct filter"
+
+ $MZ $h2 -c 1 -p 64 -a $h2mac -b $rp2mac -A 192.0.2.1 -B 192.0.3.1 \
+ -t ip -q
+
+ tc_check_packets "dev $rp3 egress" 101 2
+ check_fail $? "Matched on a wrong filter"
+
+ tc_check_packets "dev $rp3 egress" 102 1
+ check_err $? "Did not match on correct filter"
+
+ tc filter del dev $rp3 egress protocol ip pref 2 handle 102 flower
+ tc filter del dev $rp3 egress protocol ip pref 1 handle 101 flower
+
+ log_test "indev egress match ($tcflags)"
+}
+
+setup_prepare()
+{
+ h1=${NETIFS[p1]}
+ rp1=${NETIFS[p2]}
+
+ h2=${NETIFS[p3]}
+ rp2=${NETIFS[p4]}
+
+ h3=${NETIFS[p5]}
+ rp3=${NETIFS[p6]}
+
+ h1mac=$(mac_get $h1)
+ rp1mac=$(mac_get $rp1)
+ h2mac=$(mac_get $h2)
+ rp2mac=$(mac_get $rp2)
+
+ vrf_prepare
+
+ h1_create
+ h2_create
+ h3_create
+
+ router_create
+
+ forwarding_enable
+}
+
+cleanup()
+{
+ pre_cleanup
+
+ forwarding_restore
+
+ router_destroy
+
+ h3_destroy
+ h2_destroy
+ h1_destroy
+
+ vrf_cleanup
+}
+
+trap cleanup EXIT
+
+setup_prepare
+setup_wait
+
+tc_offload_check
+if [[ $? -ne 0 ]]; then
+ log_info "Could not test offloaded functionality"
+else
+ tcflags="skip_sw"
+ tests_run
+fi
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/net/forwarding/tc_shblocks.sh b/tools/testing/selftests/net/forwarding/tc_shblocks.sh
index 9826a446e2c0..772e00ac3230 100755
--- a/tools/testing/selftests/net/forwarding/tc_shblocks.sh
+++ b/tools/testing/selftests/net/forwarding/tc_shblocks.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
-ALL_TESTS="shared_block_test"
+ALL_TESTS="shared_block_test match_indev_test"
NUM_NETIFS=4
source tc_common.sh
source lib.sh
@@ -70,6 +70,33 @@ shared_block_test()
log_test "shared block ($tcflags)"
}
+match_indev_test()
+{
+ RET=0
+
+ tc filter add block 22 protocol ip pref 1 handle 101 flower \
+ $tcflags indev $swp1 dst_mac $swmac action drop
+ tc filter add block 22 protocol ip pref 2 handle 102 flower \
+ $tcflags indev $swp2 dst_mac $swmac action drop
+
+ $MZ $h1 -c 1 -p 64 -a $h1mac -b $swmac -A 192.0.2.1 -B 192.0.2.2 \
+ -t ip -q
+
+ tc_check_packets "block 22" 101 1
+ check_err $? "Did not match first incoming packet on a block"
+
+ $MZ $h2 -c 1 -p 64 -a $h2mac -b $swmac -A 192.0.2.1 -B 192.0.2.2 \
+ -t ip -q
+
+ tc_check_packets "block 22" 102 1
+ check_err $? "Did not match second incoming packet on a block"
+
+ tc filter del block 22 protocol ip pref 1 handle 101 flower
+ tc filter del block 22 protocol ip pref 2 handle 102 flower
+
+ log_test "indev match ($tcflags)"
+}
+
setup_prepare()
{
h1=${NETIFS[p1]}
diff --git a/tools/testing/selftests/net/icmp_redirect.sh b/tools/testing/selftests/net/icmp_redirect.sh
new file mode 100755
index 000000000000..18c5de53558a
--- /dev/null
+++ b/tools/testing/selftests/net/icmp_redirect.sh
@@ -0,0 +1,534 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# redirect test
+#
+# .253 +----+
+# +----| r1 |
+# | +----+
+# +----+ | |.1
+# | h1 |--------------+ | 10.1.1.0/30 2001:db8:1::0/126
+# +----+ .1 | |.2
+# 172.16.1/24 | +----+ +----+
+# 2001:db8:16:1/64 +----| r2 |-------------------| h2 |
+# .254 +----+ .254 .2 +----+
+# 172.16.2/24
+# 2001:db8:16:2/64
+#
+# Route from h1 to h2 goes through r1, eth1 - connection between r1 and r2.
+# Route on r1 changed to go to r2 via eth0. This causes a redirect to be sent
+# from r1 to h1 telling h1 to use r2 when talking to h2.
+
+VERBOSE=0
+PAUSE_ON_FAIL=no
+
+H1_N1_IP=172.16.1.1
+R1_N1_IP=172.16.1.253
+R2_N1_IP=172.16.1.254
+
+H1_N1_IP6=2001:db8:16:1::1
+R1_N1_IP6=2001:db8:16:1::253
+R2_N1_IP6=2001:db8:16:1::254
+
+R1_R2_N1_IP=10.1.1.1
+R2_R1_N1_IP=10.1.1.2
+
+R1_R2_N1_IP6=2001:db8:1::1
+R2_R1_N1_IP6=2001:db8:1::2
+
+H2_N2=172.16.2.0/24
+H2_N2_6=2001:db8:16:2::/64
+H2_N2_IP=172.16.2.2
+R2_N2_IP=172.16.2.254
+H2_N2_IP6=2001:db8:16:2::2
+R2_N2_IP6=2001:db8:16:2::254
+
+VRF=red
+VRF_TABLE=1111
+
+################################################################################
+# helpers
+
+log_section()
+{
+ echo
+ echo "###########################################################################"
+ echo "$*"
+ echo "###########################################################################"
+ echo
+}
+
+log_test()
+{
+ local rc=$1
+ local expected=$2
+ local msg="$3"
+
+ if [ ${rc} -eq ${expected} ]; then
+ printf "TEST: %-60s [ OK ]\n" "${msg}"
+ nsuccess=$((nsuccess+1))
+ else
+ ret=1
+ nfail=$((nfail+1))
+ printf "TEST: %-60s [FAIL]\n" "${msg}"
+ if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
+ echo
+ echo "hit enter to continue, 'q' to quit"
+ read a
+ [ "$a" = "q" ] && exit 1
+ fi
+ fi
+}
+
+log_debug()
+{
+ if [ "$VERBOSE" = "1" ]; then
+ echo "$*"
+ fi
+}
+
+run_cmd()
+{
+ local cmd="$*"
+ local out
+ local rc
+
+ if [ "$VERBOSE" = "1" ]; then
+ echo "COMMAND: $cmd"
+ fi
+
+ out=$(eval $cmd 2>&1)
+ rc=$?
+ if [ "$VERBOSE" = "1" -a -n "$out" ]; then
+ echo "$out"
+ fi
+
+ [ "$VERBOSE" = "1" ] && echo
+
+ return $rc
+}
+
+get_linklocal()
+{
+ local ns=$1
+ local dev=$2
+ local addr
+
+ addr=$(ip -netns $ns -6 -br addr show dev ${dev} | \
+ awk '{
+ for (i = 3; i <= NF; ++i) {
+ if ($i ~ /^fe80/)
+ print $i
+ }
+ }'
+ )
+ addr=${addr/\/*}
+
+ [ -z "$addr" ] && return 1
+
+ echo $addr
+
+ return 0
+}
+
+################################################################################
+# setup and teardown
+
+cleanup()
+{
+ local ns
+
+ for ns in h1 h2 r1 r2; do
+ ip netns del $ns 2>/dev/null
+ done
+}
+
+create_vrf()
+{
+ local ns=$1
+
+ ip -netns ${ns} link add ${VRF} type vrf table ${VRF_TABLE}
+ ip -netns ${ns} link set ${VRF} up
+ ip -netns ${ns} route add vrf ${VRF} unreachable default metric 8192
+ ip -netns ${ns} -6 route add vrf ${VRF} unreachable default metric 8192
+
+ ip -netns ${ns} addr add 127.0.0.1/8 dev ${VRF}
+ ip -netns ${ns} -6 addr add ::1 dev ${VRF} nodad
+
+ ip -netns ${ns} ru del pref 0
+ ip -netns ${ns} ru add pref 32765 from all lookup local
+ ip -netns ${ns} -6 ru del pref 0
+ ip -netns ${ns} -6 ru add pref 32765 from all lookup local
+}
+
+setup()
+{
+ local ns
+
+ #
+ # create nodes as namespaces
+ #
+ for ns in h1 h2 r1 r2; do
+ ip netns add $ns
+ ip -netns $ns li set lo up
+
+ case "${ns}" in
+ h[12]) ip netns exec $ns sysctl -q -w net.ipv4.conf.all.accept_redirects=1
+ ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=0
+ ip netns exec $ns sysctl -q -w net.ipv6.conf.all.accept_redirects=1
+ ip netns exec $ns sysctl -q -w net.ipv6.conf.all.keep_addr_on_down=1
+ ;;
+ r[12]) ip netns exec $ns sysctl -q -w net.ipv4.ip_forward=1
+ ip netns exec $ns sysctl -q -w net.ipv4.conf.all.send_redirects=1
+
+ ip netns exec $ns sysctl -q -w net.ipv6.conf.all.forwarding=1
+ ip netns exec $ns sysctl -q -w net.ipv6.route.mtu_expires=10
+ esac
+ done
+
+ #
+ # create interconnects
+ #
+ ip -netns h1 li add eth0 type veth peer name r1h1
+ ip -netns h1 li set r1h1 netns r1 name eth0 up
+
+ ip -netns h1 li add eth1 type veth peer name r2h1
+ ip -netns h1 li set r2h1 netns r2 name eth0 up
+
+ ip -netns h2 li add eth0 type veth peer name r2h2
+ ip -netns h2 li set eth0 up
+ ip -netns h2 li set r2h2 netns r2 name eth2 up
+
+ ip -netns r1 li add eth1 type veth peer name r2r1
+ ip -netns r1 li set eth1 up
+ ip -netns r1 li set r2r1 netns r2 name eth1 up
+
+ #
+ # h1
+ #
+ if [ "${WITH_VRF}" = "yes" ]; then
+ create_vrf "h1"
+ H1_VRF_ARG="vrf ${VRF}"
+ H1_PING_ARG="-I ${VRF}"
+ else
+ H1_VRF_ARG=
+ H1_PING_ARG=
+ fi
+ ip -netns h1 li add br0 type bridge
+ if [ "${WITH_VRF}" = "yes" ]; then
+ ip -netns h1 li set br0 vrf ${VRF} up
+ else
+ ip -netns h1 li set br0 up
+ fi
+ ip -netns h1 addr add dev br0 ${H1_N1_IP}/24
+ ip -netns h1 -6 addr add dev br0 ${H1_N1_IP6}/64 nodad
+ ip -netns h1 li set eth0 master br0 up
+ ip -netns h1 li set eth1 master br0 up
+
+ #
+ # h2
+ #
+ ip -netns h2 addr add dev eth0 ${H2_N2_IP}/24
+ ip -netns h2 ro add default via ${R2_N2_IP} dev eth0
+ ip -netns h2 -6 addr add dev eth0 ${H2_N2_IP6}/64 nodad
+ ip -netns h2 -6 ro add default via ${R2_N2_IP6} dev eth0
+
+ #
+ # r1
+ #
+ ip -netns r1 addr add dev eth0 ${R1_N1_IP}/24
+ ip -netns r1 -6 addr add dev eth0 ${R1_N1_IP6}/64 nodad
+ ip -netns r1 addr add dev eth1 ${R1_R2_N1_IP}/30
+ ip -netns r1 -6 addr add dev eth1 ${R1_R2_N1_IP6}/126 nodad
+
+ #
+ # r2
+ #
+ ip -netns r2 addr add dev eth0 ${R2_N1_IP}/24
+ ip -netns r2 -6 addr add dev eth0 ${R2_N1_IP6}/64 nodad
+ ip -netns r2 addr add dev eth1 ${R2_R1_N1_IP}/30
+ ip -netns r2 -6 addr add dev eth1 ${R2_R1_N1_IP6}/126 nodad
+ ip -netns r2 addr add dev eth2 ${R2_N2_IP}/24
+ ip -netns r2 -6 addr add dev eth2 ${R2_N2_IP6}/64 nodad
+
+ sleep 2
+
+ R1_LLADDR=$(get_linklocal r1 eth0)
+ if [ $? -ne 0 ]; then
+ echo "Error: Failed to get link-local address of r1's eth0"
+ exit 1
+ fi
+ log_debug "initial gateway is R1's lladdr = ${R1_LLADDR}"
+
+ R2_LLADDR=$(get_linklocal r2 eth0)
+ if [ $? -ne 0 ]; then
+ echo "Error: Failed to get link-local address of r2's eth0"
+ exit 1
+ fi
+ log_debug "initial gateway is R2's lladdr = ${R2_LLADDR}"
+}
+
+change_h2_mtu()
+{
+ local mtu=$1
+
+ run_cmd ip -netns h2 li set eth0 mtu ${mtu}
+ run_cmd ip -netns r2 li set eth2 mtu ${mtu}
+}
+
+check_exception()
+{
+ local mtu="$1"
+ local with_redirect="$2"
+ local desc="$3"
+
+ # From 172.16.1.101: icmp_seq=1 Redirect Host(New nexthop: 172.16.1.102)
+ if [ "$VERBOSE" = "1" ]; then
+ echo "Commands to check for exception:"
+ run_cmd ip -netns h1 ro get ${H1_VRF_ARG} ${H2_N2_IP}
+ run_cmd ip -netns h1 -6 ro get ${H1_VRF_ARG} ${H2_N2_IP6}
+ fi
+
+ if [ -n "${mtu}" ]; then
+ mtu=" mtu ${mtu}"
+ fi
+ if [ "$with_redirect" = "yes" ]; then
+ ip -netns h1 ro get ${H1_VRF_ARG} ${H2_N2_IP} | \
+ grep -q "cache <redirected> expires [0-9]*sec${mtu}"
+ elif [ -n "${mtu}" ]; then
+ ip -netns h1 ro get ${H1_VRF_ARG} ${H2_N2_IP} | \
+ grep -q "cache expires [0-9]*sec${mtu}"
+ else
+ # want to verify that neither mtu nor redirected appears in
+ # the route get output. The -v will wipe out the cache line
+ # if either are set so the last grep -q will not find a match
+ ip -netns h1 ro get ${H1_VRF_ARG} ${H2_N2_IP} | \
+ grep -E -v 'mtu|redirected' | grep -q "cache"
+ fi
+ log_test $? 0 "IPv4: ${desc}"
+
+ if [ "$with_redirect" = "yes" ]; then
+ ip -netns h1 -6 ro get ${H1_VRF_ARG} ${H2_N2_IP6} | \
+ grep -q "${H2_N2_IP6} from :: via ${R2_LLADDR} dev br0.*${mtu}"
+ elif [ -n "${mtu}" ]; then
+ ip -netns h1 -6 ro get ${H1_VRF_ARG} ${H2_N2_IP6} | \
+ grep -q "${mtu}"
+ else
+ # IPv6 is a bit harder. First strip out the match if it
+ # contains an mtu exception and then look for the first
+ # gateway - R1's lladdr
+ ip -netns h1 -6 ro get ${H1_VRF_ARG} ${H2_N2_IP6} | \
+ grep -v "mtu" | grep -q "${R1_LLADDR}"
+ fi
+ log_test $? 0 "IPv6: ${desc}"
+}
+
+run_ping()
+{
+ local sz=$1
+
+ run_cmd ip netns exec h1 ping -q -M want -i 0.5 -c 10 -w 2 -s ${sz} ${H1_PING_ARG} ${H2_N2_IP}
+ run_cmd ip netns exec h1 ${ping6} -q -M want -i 0.5 -c 10 -w 2 -s ${sz} ${H1_PING_ARG} ${H2_N2_IP6}
+}
+
+replace_route_new()
+{
+ # r1 to h2 via r2 and eth0
+ run_cmd ip -netns r1 nexthop replace id 1 via ${R2_N1_IP} dev eth0
+ run_cmd ip -netns r1 nexthop replace id 2 via ${R2_LLADDR} dev eth0
+}
+
+reset_route_new()
+{
+ run_cmd ip -netns r1 nexthop flush
+ run_cmd ip -netns h1 nexthop flush
+
+ initial_route_new
+}
+
+initial_route_new()
+{
+ # r1 to h2 via r2 and eth1
+ run_cmd ip -netns r1 nexthop add id 1 via ${R2_R1_N1_IP} dev eth1
+ run_cmd ip -netns r1 ro add ${H2_N2} nhid 1
+
+ run_cmd ip -netns r1 nexthop add id 2 via ${R2_R1_N1_IP6} dev eth1
+ run_cmd ip -netns r1 -6 ro add ${H2_N2_6} nhid 2
+
+ # h1 to h2 via r1
+ run_cmd ip -netns h1 nexthop add id 1 via ${R1_N1_IP} dev br0
+ run_cmd ip -netns h1 ro add ${H1_VRF_ARG} ${H2_N2} nhid 1
+
+ run_cmd ip -netns h1 nexthop add id 2 via ${R1_LLADDR} dev br0
+ run_cmd ip -netns h1 -6 ro add ${H1_VRF_ARG} ${H2_N2_6} nhid 2
+}
+
+replace_route_legacy()
+{
+ # r1 to h2 via r2 and eth0
+ run_cmd ip -netns r1 ro replace ${H2_N2} via ${R2_N1_IP} dev eth0
+ run_cmd ip -netns r1 -6 ro replace ${H2_N2_6} via ${R2_LLADDR} dev eth0
+}
+
+reset_route_legacy()
+{
+ run_cmd ip -netns r1 ro del ${H2_N2}
+ run_cmd ip -netns r1 -6 ro del ${H2_N2_6}
+
+ run_cmd ip -netns h1 ro del ${H1_VRF_ARG} ${H2_N2}
+ run_cmd ip -netns h1 -6 ro del ${H1_VRF_ARG} ${H2_N2_6}
+
+ initial_route_legacy
+}
+
+initial_route_legacy()
+{
+ # r1 to h2 via r2 and eth1
+ run_cmd ip -netns r1 ro add ${H2_N2} via ${R2_R1_N1_IP} dev eth1
+ run_cmd ip -netns r1 -6 ro add ${H2_N2_6} via ${R2_R1_N1_IP6} dev eth1
+
+ # h1 to h2 via r1
+ # - IPv6 redirect only works if gateway is the LLA
+ run_cmd ip -netns h1 ro add ${H1_VRF_ARG} ${H2_N2} via ${R1_N1_IP} dev br0
+ run_cmd ip -netns h1 -6 ro add ${H1_VRF_ARG} ${H2_N2_6} via ${R1_LLADDR} dev br0
+}
+
+check_connectivity()
+{
+ local rc
+
+ run_cmd ip netns exec h1 ping -c1 -w1 ${H1_PING_ARG} ${H2_N2_IP}
+ rc=$?
+ run_cmd ip netns exec h1 ${ping6} -c1 -w1 ${H1_PING_ARG} ${H2_N2_IP6}
+ [ $? -ne 0 ] && rc=$?
+
+ return $rc
+}
+
+do_test()
+{
+ local ttype="$1"
+
+ eval initial_route_${ttype}
+
+ # verify connectivity
+ check_connectivity
+ if [ $? -ne 0 ]; then
+ echo "Error: Basic connectivity is broken"
+ ret=1
+ return
+ fi
+
+ # redirect exception followed by mtu
+ eval replace_route_${ttype}
+ run_ping 64
+ check_exception "" "yes" "redirect exception"
+
+ check_connectivity
+ if [ $? -ne 0 ]; then
+ echo "Error: Basic connectivity is broken after redirect"
+ ret=1
+ return
+ fi
+
+ change_h2_mtu 1300
+ run_ping 1350
+ check_exception "1300" "yes" "redirect exception plus mtu"
+
+ # remove exceptions and restore routing
+ change_h2_mtu 1500
+ eval reset_route_${ttype}
+
+ check_connectivity
+ if [ $? -ne 0 ]; then
+ echo "Error: Basic connectivity is broken after reset"
+ ret=1
+ return
+ fi
+ check_exception "" "no" "routing reset"
+
+ # MTU exception followed by redirect
+ change_h2_mtu 1300
+ run_ping 1350
+ check_exception "1300" "no" "mtu exception"
+
+ eval replace_route_${ttype}
+ run_ping 64
+ check_exception "1300" "yes" "mtu exception plus redirect"
+
+ check_connectivity
+ if [ $? -ne 0 ]; then
+ echo "Error: Basic connectivity is broken after redirect"
+ ret=1
+ return
+ fi
+}
+
+################################################################################
+# usage
+
+usage()
+{
+ cat <<EOF
+usage: ${0##*/} OPTS
+
+ -p Pause on fail
+ -v verbose mode (show commands and output)
+EOF
+}
+
+################################################################################
+# main
+
+# Some systems don't have a ping6 binary anymore
+which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping)
+
+ret=0
+nsuccess=0
+nfail=0
+
+while getopts :pv o
+do
+ case $o in
+ p) PAUSE_ON_FAIL=yes;;
+ v) VERBOSE=$(($VERBOSE + 1));;
+ *) usage; exit 1;;
+ esac
+done
+
+trap cleanup EXIT
+
+cleanup
+WITH_VRF=no
+setup
+
+log_section "Legacy routing"
+do_test "legacy"
+
+cleanup
+log_section "Legacy routing with VRF"
+WITH_VRF=yes
+setup
+do_test "legacy"
+
+cleanup
+log_section "Routing with nexthop objects"
+ip nexthop ls >/dev/null 2>&1
+if [ $? -eq 0 ]; then
+ WITH_VRF=no
+ setup
+ do_test "new"
+
+ cleanup
+ log_section "Routing with nexthop objects and VRF"
+ WITH_VRF=yes
+ setup
+ do_test "new"
+else
+ echo "Nexthop objects not supported; skipping tests"
+fi
+
+printf "\nTests passed: %3d\n" ${nsuccess}
+printf "Tests failed: %3d\n" ${nfail}
+
+exit $ret
diff --git a/tools/testing/selftests/net/ipv6_flowlabel.c b/tools/testing/selftests/net/ipv6_flowlabel.c
new file mode 100644
index 000000000000..a7c41375374f
--- /dev/null
+++ b/tools/testing/selftests/net/ipv6_flowlabel.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Test IPV6_FLOWINFO cmsg on send and recv */
+
+#define _GNU_SOURCE
+
+#include <arpa/inet.h>
+#include <asm/byteorder.h>
+#include <error.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <linux/in6.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+/* uapi/glibc weirdness may leave this undefined */
+#ifndef IPV6_FLOWINFO
+#define IPV6_FLOWINFO 11
+#endif
+
+#ifndef IPV6_FLOWLABEL_MGR
+#define IPV6_FLOWLABEL_MGR 32
+#endif
+
+#define FLOWLABEL_WILDCARD ((uint32_t) -1)
+
+static const char cfg_data[] = "a";
+static uint32_t cfg_label = 1;
+
+static void do_send(int fd, bool with_flowlabel, uint32_t flowlabel)
+{
+ char control[CMSG_SPACE(sizeof(flowlabel))] = {0};
+ struct msghdr msg = {0};
+ struct iovec iov = {0};
+ int ret;
+
+ iov.iov_base = (char *)cfg_data;
+ iov.iov_len = sizeof(cfg_data);
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ if (with_flowlabel) {
+ struct cmsghdr *cm;
+
+ cm = (void *)control;
+ cm->cmsg_len = CMSG_LEN(sizeof(flowlabel));
+ cm->cmsg_level = SOL_IPV6;
+ cm->cmsg_type = IPV6_FLOWINFO;
+ *(uint32_t *)CMSG_DATA(cm) = htonl(flowlabel);
+
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+ }
+
+ ret = sendmsg(fd, &msg, 0);
+ if (ret == -1)
+ error(1, errno, "send");
+
+ if (with_flowlabel)
+ fprintf(stderr, "sent with label %u\n", flowlabel);
+ else
+ fprintf(stderr, "sent without label\n");
+}
+
+static void do_recv(int fd, bool with_flowlabel, uint32_t expect)
+{
+ char control[CMSG_SPACE(sizeof(expect))];
+ char data[sizeof(cfg_data)];
+ struct msghdr msg = {0};
+ struct iovec iov = {0};
+ struct cmsghdr *cm;
+ uint32_t flowlabel;
+ int ret;
+
+ iov.iov_base = data;
+ iov.iov_len = sizeof(data);
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ memset(control, 0, sizeof(control));
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ ret = recvmsg(fd, &msg, 0);
+ if (ret == -1)
+ error(1, errno, "recv");
+ if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC))
+ error(1, 0, "recv: truncated");
+ if (ret != sizeof(cfg_data))
+ error(1, 0, "recv: length mismatch");
+ if (memcmp(data, cfg_data, sizeof(data)))
+ error(1, 0, "recv: data mismatch");
+
+ cm = CMSG_FIRSTHDR(&msg);
+ if (with_flowlabel) {
+ if (!cm)
+ error(1, 0, "recv: missing cmsg");
+ if (CMSG_NXTHDR(&msg, cm))
+ error(1, 0, "recv: too many cmsg");
+ if (cm->cmsg_level != SOL_IPV6 ||
+ cm->cmsg_type != IPV6_FLOWINFO)
+ error(1, 0, "recv: unexpected cmsg level or type");
+
+ flowlabel = ntohl(*(uint32_t *)CMSG_DATA(cm));
+ fprintf(stderr, "recv with label %u\n", flowlabel);
+
+ if (expect != FLOWLABEL_WILDCARD && expect != flowlabel)
+ fprintf(stderr, "recv: incorrect flowlabel %u != %u\n",
+ flowlabel, expect);
+
+ } else {
+ fprintf(stderr, "recv without label\n");
+ }
+}
+
+static bool get_autoflowlabel_enabled(void)
+{
+ int fd, ret;
+ char val;
+
+ fd = open("/proc/sys/net/ipv6/auto_flowlabels", O_RDONLY);
+ if (fd == -1)
+ error(1, errno, "open sysctl");
+
+ ret = read(fd, &val, 1);
+ if (ret == -1)
+ error(1, errno, "read sysctl");
+ if (ret == 0)
+ error(1, 0, "read sysctl: 0");
+
+ if (close(fd))
+ error(1, errno, "close sysctl");
+
+ return val == '1';
+}
+
+static void flowlabel_get(int fd, uint32_t label, uint8_t share, uint16_t flags)
+{
+ struct in6_flowlabel_req req = {
+ .flr_action = IPV6_FL_A_GET,
+ .flr_label = htonl(label),
+ .flr_flags = flags,
+ .flr_share = share,
+ };
+
+ /* do not pass IPV6_ADDR_ANY or IPV6_ADDR_MAPPED */
+ req.flr_dst.s6_addr[0] = 0xfd;
+ req.flr_dst.s6_addr[15] = 0x1;
+
+ if (setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req)))
+ error(1, errno, "setsockopt flowlabel get");
+}
+
+static void parse_opts(int argc, char **argv)
+{
+ int c;
+
+ while ((c = getopt(argc, argv, "l:")) != -1) {
+ switch (c) {
+ case 'l':
+ cfg_label = strtoul(optarg, NULL, 0);
+ break;
+ default:
+ error(1, 0, "%s: parse error", argv[0]);
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct sockaddr_in6 addr = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(8000),
+ .sin6_addr = IN6ADDR_LOOPBACK_INIT,
+ };
+ const int one = 1;
+ int fdt, fdr;
+
+ parse_opts(argc, argv);
+
+ fdt = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (fdt == -1)
+ error(1, errno, "socket t");
+
+ fdr = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (fdr == -1)
+ error(1, errno, "socket r");
+
+ if (connect(fdt, (void *)&addr, sizeof(addr)))
+ error(1, errno, "connect");
+ if (bind(fdr, (void *)&addr, sizeof(addr)))
+ error(1, errno, "bind");
+
+ flowlabel_get(fdt, cfg_label, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE);
+
+ if (setsockopt(fdr, SOL_IPV6, IPV6_FLOWINFO, &one, sizeof(one)))
+ error(1, errno, "setsockopt flowinfo");
+
+ if (get_autoflowlabel_enabled()) {
+ fprintf(stderr, "send no label: recv auto flowlabel\n");
+ do_send(fdt, false, 0);
+ do_recv(fdr, true, FLOWLABEL_WILDCARD);
+ } else {
+ fprintf(stderr, "send no label: recv no label (auto off)\n");
+ do_send(fdt, false, 0);
+ do_recv(fdr, false, 0);
+ }
+
+ fprintf(stderr, "send label\n");
+ do_send(fdt, true, cfg_label);
+ do_recv(fdr, true, cfg_label);
+
+ if (close(fdr))
+ error(1, errno, "close r");
+ if (close(fdt))
+ error(1, errno, "close t");
+
+ return 0;
+}
diff --git a/tools/testing/selftests/net/ipv6_flowlabel.sh b/tools/testing/selftests/net/ipv6_flowlabel.sh
new file mode 100755
index 000000000000..d3bc6442704e
--- /dev/null
+++ b/tools/testing/selftests/net/ipv6_flowlabel.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+#
+# Regression tests for IPv6 flowlabels
+#
+# run in separate namespaces to avoid mgmt db conflicts betweent tests
+
+set -e
+
+echo "TEST management"
+./in_netns.sh ./ipv6_flowlabel_mgr
+
+echo "TEST datapath"
+./in_netns.sh \
+ sh -c 'sysctl -q -w net.ipv6.auto_flowlabels=0 && ./ipv6_flowlabel -l 1'
+
+echo "TEST datapath (with auto-flowlabels)"
+./in_netns.sh \
+ sh -c 'sysctl -q -w net.ipv6.auto_flowlabels=1 && ./ipv6_flowlabel -l 1'
+
+echo OK. All tests passed
diff --git a/tools/testing/selftests/net/ipv6_flowlabel_mgr.c b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c
new file mode 100644
index 000000000000..af95b48acea9
--- /dev/null
+++ b/tools/testing/selftests/net/ipv6_flowlabel_mgr.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Test IPV6_FLOWINFO_MGR */
+
+#define _GNU_SOURCE
+
+#include <arpa/inet.h>
+#include <error.h>
+#include <errno.h>
+#include <limits.h>
+#include <linux/in6.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+/* uapi/glibc weirdness may leave this undefined */
+#ifndef IPV6_FLOWLABEL_MGR
+#define IPV6_FLOWLABEL_MGR 32
+#endif
+
+/* from net/ipv6/ip6_flowlabel.c */
+#define FL_MIN_LINGER 6
+
+#define explain(x) \
+ do { if (cfg_verbose) fprintf(stderr, " " x "\n"); } while (0)
+
+#define __expect(x) \
+ do { \
+ if (!(x)) \
+ fprintf(stderr, "[OK] " #x "\n"); \
+ else \
+ error(1, 0, "[ERR] " #x " (line %d)", __LINE__); \
+ } while (0)
+
+#define expect_pass(x) __expect(x)
+#define expect_fail(x) __expect(!(x))
+
+static bool cfg_long_running;
+static bool cfg_verbose;
+
+static int flowlabel_get(int fd, uint32_t label, uint8_t share, uint16_t flags)
+{
+ struct in6_flowlabel_req req = {
+ .flr_action = IPV6_FL_A_GET,
+ .flr_label = htonl(label),
+ .flr_flags = flags,
+ .flr_share = share,
+ };
+
+ /* do not pass IPV6_ADDR_ANY or IPV6_ADDR_MAPPED */
+ req.flr_dst.s6_addr[0] = 0xfd;
+ req.flr_dst.s6_addr[15] = 0x1;
+
+ return setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req));
+}
+
+static int flowlabel_put(int fd, uint32_t label)
+{
+ struct in6_flowlabel_req req = {
+ .flr_action = IPV6_FL_A_PUT,
+ .flr_label = htonl(label),
+ };
+
+ return setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req));
+}
+
+static void run_tests(int fd)
+{
+ int wstatus;
+ pid_t pid;
+
+ explain("cannot get non-existent label");
+ expect_fail(flowlabel_get(fd, 1, IPV6_FL_S_ANY, 0));
+
+ explain("cannot put non-existent label");
+ expect_fail(flowlabel_put(fd, 1));
+
+ explain("cannot create label greater than 20 bits");
+ expect_fail(flowlabel_get(fd, 0x1FFFFF, IPV6_FL_S_ANY,
+ IPV6_FL_F_CREATE));
+
+ explain("create a new label (FL_F_CREATE)");
+ expect_pass(flowlabel_get(fd, 1, IPV6_FL_S_ANY, IPV6_FL_F_CREATE));
+ explain("can get the label (without FL_F_CREATE)");
+ expect_pass(flowlabel_get(fd, 1, IPV6_FL_S_ANY, 0));
+ explain("can get it again with create flag set, too");
+ expect_pass(flowlabel_get(fd, 1, IPV6_FL_S_ANY, IPV6_FL_F_CREATE));
+ explain("cannot get it again with the exclusive (FL_FL_EXCL) flag");
+ expect_fail(flowlabel_get(fd, 1, IPV6_FL_S_ANY,
+ IPV6_FL_F_CREATE | IPV6_FL_F_EXCL));
+ explain("can now put exactly three references");
+ expect_pass(flowlabel_put(fd, 1));
+ expect_pass(flowlabel_put(fd, 1));
+ expect_pass(flowlabel_put(fd, 1));
+ expect_fail(flowlabel_put(fd, 1));
+
+ explain("create a new exclusive label (FL_S_EXCL)");
+ expect_pass(flowlabel_get(fd, 2, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE));
+ explain("cannot get it again in non-exclusive mode");
+ expect_fail(flowlabel_get(fd, 2, IPV6_FL_S_ANY, IPV6_FL_F_CREATE));
+ explain("cannot get it again in exclusive mode either");
+ expect_fail(flowlabel_get(fd, 2, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE));
+ expect_pass(flowlabel_put(fd, 2));
+
+ if (cfg_long_running) {
+ explain("cannot reuse the label, due to linger");
+ expect_fail(flowlabel_get(fd, 2, IPV6_FL_S_ANY,
+ IPV6_FL_F_CREATE));
+ explain("after sleep, can reuse");
+ sleep(FL_MIN_LINGER * 2 + 1);
+ expect_pass(flowlabel_get(fd, 2, IPV6_FL_S_ANY,
+ IPV6_FL_F_CREATE));
+ }
+
+ explain("create a new user-private label (FL_S_USER)");
+ expect_pass(flowlabel_get(fd, 3, IPV6_FL_S_USER, IPV6_FL_F_CREATE));
+ explain("cannot get it again in non-exclusive mode");
+ expect_fail(flowlabel_get(fd, 3, IPV6_FL_S_ANY, 0));
+ explain("cannot get it again in exclusive mode");
+ expect_fail(flowlabel_get(fd, 3, IPV6_FL_S_EXCL, 0));
+ explain("can get it again in user mode");
+ expect_pass(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0));
+ explain("child process can get it too, but not after setuid(nobody)");
+ pid = fork();
+ if (pid == -1)
+ error(1, errno, "fork");
+ if (!pid) {
+ expect_pass(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0));
+ if (setuid(USHRT_MAX))
+ fprintf(stderr, "[INFO] skip setuid child test\n");
+ else
+ expect_fail(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0));
+ exit(0);
+ }
+ if (wait(&wstatus) == -1)
+ error(1, errno, "wait");
+ if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0)
+ error(1, errno, "wait: unexpected child result");
+
+ explain("create a new process-private label (FL_S_PROCESS)");
+ expect_pass(flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, IPV6_FL_F_CREATE));
+ explain("can get it again");
+ expect_pass(flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, 0));
+ explain("child process cannot can get it");
+ pid = fork();
+ if (pid == -1)
+ error(1, errno, "fork");
+ if (!pid) {
+ expect_fail(flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, 0));
+ exit(0);
+ }
+ if (wait(&wstatus) == -1)
+ error(1, errno, "wait");
+ if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0)
+ error(1, errno, "wait: unexpected child result");
+}
+
+static void parse_opts(int argc, char **argv)
+{
+ int c;
+
+ while ((c = getopt(argc, argv, "lv")) != -1) {
+ switch (c) {
+ case 'l':
+ cfg_long_running = true;
+ break;
+ case 'v':
+ cfg_verbose = true;
+ break;
+ default:
+ error(1, 0, "%s: parse error", argv[0]);
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int fd;
+
+ parse_opts(argc, argv);
+
+ fd = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (fd == -1)
+ error(1, errno, "socket");
+
+ run_tests(fd);
+
+ if (close(fd))
+ error(1, errno, "close");
+
+ return 0;
+}
diff --git a/tools/testing/selftests/net/l2tp.sh b/tools/testing/selftests/net/l2tp.sh
new file mode 100644
index 000000000000..5782433886fc
--- /dev/null
+++ b/tools/testing/selftests/net/l2tp.sh
@@ -0,0 +1,382 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# L2TPv3 tunnel between 2 hosts
+#
+# host-1 | router | host-2
+# | |
+# lo l2tp | | l2tp lo
+# 172.16.101.1 172.16.1.1 | | 172.16.1.2 172.16.101.2
+# fc00:101::1 fc00:1::1 | | fc00:1::2 fc00:101::2
+# | |
+# eth0 | | eth0
+# 10.1.1.1 | | 10.1.2.1
+# 2001:db8:1::1 | | 2001:db8:2::1
+
+VERBOSE=0
+PAUSE_ON_FAIL=no
+
+which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping)
+
+################################################################################
+#
+log_test()
+{
+ local rc=$1
+ local expected=$2
+ local msg="$3"
+
+ if [ ${rc} -eq ${expected} ]; then
+ printf "TEST: %-60s [ OK ]\n" "${msg}"
+ nsuccess=$((nsuccess+1))
+ else
+ ret=1
+ nfail=$((nfail+1))
+ printf "TEST: %-60s [FAIL]\n" "${msg}"
+ if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
+ echo
+ echo "hit enter to continue, 'q' to quit"
+ read a
+ [ "$a" = "q" ] && exit 1
+ fi
+ fi
+}
+
+run_cmd()
+{
+ local ns
+ local cmd
+ local out
+ local rc
+
+ ns="$1"
+ shift
+ cmd="$*"
+
+ if [ "$VERBOSE" = "1" ]; then
+ printf " COMMAND: $cmd\n"
+ fi
+
+ out=$(eval ip netns exec ${ns} ${cmd} 2>&1)
+ rc=$?
+ if [ "$VERBOSE" = "1" -a -n "$out" ]; then
+ echo " $out"
+ fi
+
+ [ "$VERBOSE" = "1" ] && echo
+
+ return $rc
+}
+
+################################################################################
+# create namespaces and interconnects
+
+create_ns()
+{
+ local ns=$1
+ local addr=$2
+ local addr6=$3
+
+ [ -z "${addr}" ] && addr="-"
+ [ -z "${addr6}" ] && addr6="-"
+
+ ip netns add ${ns}
+
+ ip -netns ${ns} link set lo up
+ if [ "${addr}" != "-" ]; then
+ ip -netns ${ns} addr add dev lo ${addr}
+ fi
+ if [ "${addr6}" != "-" ]; then
+ ip -netns ${ns} -6 addr add dev lo ${addr6}
+ fi
+
+ ip -netns ${ns} ro add unreachable default metric 8192
+ ip -netns ${ns} -6 ro add unreachable default metric 8192
+
+ ip netns exec ${ns} sysctl -qw net.ipv4.ip_forward=1
+ ip netns exec ${ns} sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1
+ ip netns exec ${ns} sysctl -qw net.ipv6.conf.all.forwarding=1
+ ip netns exec ${ns} sysctl -qw net.ipv6.conf.default.forwarding=1
+ ip netns exec ${ns} sysctl -qw net.ipv6.conf.default.accept_dad=0
+}
+
+# create veth pair to connect namespaces and apply addresses.
+connect_ns()
+{
+ local ns1=$1
+ local ns1_dev=$2
+ local ns1_addr=$3
+ local ns1_addr6=$4
+ local ns2=$5
+ local ns2_dev=$6
+ local ns2_addr=$7
+ local ns2_addr6=$8
+
+ ip -netns ${ns1} li add ${ns1_dev} type veth peer name tmp
+ ip -netns ${ns1} li set ${ns1_dev} up
+ ip -netns ${ns1} li set tmp netns ${ns2} name ${ns2_dev}
+ ip -netns ${ns2} li set ${ns2_dev} up
+
+ if [ "${ns1_addr}" != "-" ]; then
+ ip -netns ${ns1} addr add dev ${ns1_dev} ${ns1_addr}
+ ip -netns ${ns2} addr add dev ${ns2_dev} ${ns2_addr}
+ fi
+
+ if [ "${ns1_addr6}" != "-" ]; then
+ ip -netns ${ns1} addr add dev ${ns1_dev} ${ns1_addr6}
+ ip -netns ${ns2} addr add dev ${ns2_dev} ${ns2_addr6}
+ fi
+}
+
+################################################################################
+# test setup
+
+cleanup()
+{
+ local ns
+
+ for ns in host-1 host-2 router
+ do
+ ip netns del ${ns} 2>/dev/null
+ done
+}
+
+setup_l2tp_ipv4()
+{
+ #
+ # configure l2tpv3 tunnel on host-1
+ #
+ ip -netns host-1 l2tp add tunnel tunnel_id 1041 peer_tunnel_id 1042 \
+ encap ip local 10.1.1.1 remote 10.1.2.1
+ ip -netns host-1 l2tp add session name l2tp4 tunnel_id 1041 \
+ session_id 1041 peer_session_id 1042
+ ip -netns host-1 link set dev l2tp4 up
+ ip -netns host-1 addr add dev l2tp4 172.16.1.1 peer 172.16.1.2
+
+ #
+ # configure l2tpv3 tunnel on host-2
+ #
+ ip -netns host-2 l2tp add tunnel tunnel_id 1042 peer_tunnel_id 1041 \
+ encap ip local 10.1.2.1 remote 10.1.1.1
+ ip -netns host-2 l2tp add session name l2tp4 tunnel_id 1042 \
+ session_id 1042 peer_session_id 1041
+ ip -netns host-2 link set dev l2tp4 up
+ ip -netns host-2 addr add dev l2tp4 172.16.1.2 peer 172.16.1.1
+
+ #
+ # add routes to loopback addresses
+ #
+ ip -netns host-1 ro add 172.16.101.2/32 via 172.16.1.2
+ ip -netns host-2 ro add 172.16.101.1/32 via 172.16.1.1
+}
+
+setup_l2tp_ipv6()
+{
+ #
+ # configure l2tpv3 tunnel on host-1
+ #
+ ip -netns host-1 l2tp add tunnel tunnel_id 1061 peer_tunnel_id 1062 \
+ encap ip local 2001:db8:1::1 remote 2001:db8:2::1
+ ip -netns host-1 l2tp add session name l2tp6 tunnel_id 1061 \
+ session_id 1061 peer_session_id 1062
+ ip -netns host-1 link set dev l2tp6 up
+ ip -netns host-1 addr add dev l2tp6 fc00:1::1 peer fc00:1::2
+
+ #
+ # configure l2tpv3 tunnel on host-2
+ #
+ ip -netns host-2 l2tp add tunnel tunnel_id 1062 peer_tunnel_id 1061 \
+ encap ip local 2001:db8:2::1 remote 2001:db8:1::1
+ ip -netns host-2 l2tp add session name l2tp6 tunnel_id 1062 \
+ session_id 1062 peer_session_id 1061
+ ip -netns host-2 link set dev l2tp6 up
+ ip -netns host-2 addr add dev l2tp6 fc00:1::2 peer fc00:1::1
+
+ #
+ # add routes to loopback addresses
+ #
+ ip -netns host-1 -6 ro add fc00:101::2/128 via fc00:1::2
+ ip -netns host-2 -6 ro add fc00:101::1/128 via fc00:1::1
+}
+
+setup()
+{
+ # start clean
+ cleanup
+
+ set -e
+ create_ns host-1 172.16.101.1/32 fc00:101::1/128
+ create_ns host-2 172.16.101.2/32 fc00:101::2/128
+ create_ns router
+
+ connect_ns host-1 eth0 10.1.1.1/24 2001:db8:1::1/64 \
+ router eth1 10.1.1.2/24 2001:db8:1::2/64
+
+ connect_ns host-2 eth0 10.1.2.1/24 2001:db8:2::1/64 \
+ router eth2 10.1.2.2/24 2001:db8:2::2/64
+
+ ip -netns host-1 ro add 10.1.2.0/24 via 10.1.1.2
+ ip -netns host-1 -6 ro add 2001:db8:2::/64 via 2001:db8:1::2
+
+ ip -netns host-2 ro add 10.1.1.0/24 via 10.1.2.2
+ ip -netns host-2 -6 ro add 2001:db8:1::/64 via 2001:db8:2::2
+
+ setup_l2tp_ipv4
+ setup_l2tp_ipv6
+ set +e
+}
+
+setup_ipsec()
+{
+ #
+ # IPv4
+ #
+ run_cmd host-1 ip xfrm policy add \
+ src 10.1.1.1 dst 10.1.2.1 dir out \
+ tmpl proto esp mode transport
+
+ run_cmd host-1 ip xfrm policy add \
+ src 10.1.2.1 dst 10.1.1.1 dir in \
+ tmpl proto esp mode transport
+
+ run_cmd host-2 ip xfrm policy add \
+ src 10.1.1.1 dst 10.1.2.1 dir in \
+ tmpl proto esp mode transport
+
+ run_cmd host-2 ip xfrm policy add \
+ src 10.1.2.1 dst 10.1.1.1 dir out \
+ tmpl proto esp mode transport
+
+ ip -netns host-1 xfrm state add \
+ src 10.1.1.1 dst 10.1.2.1 \
+ spi 0x1000 proto esp aead 'rfc4106(gcm(aes))' \
+ 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode transport
+
+ ip -netns host-1 xfrm state add \
+ src 10.1.2.1 dst 10.1.1.1 \
+ spi 0x1001 proto esp aead 'rfc4106(gcm(aes))' \
+ 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode transport
+
+ ip -netns host-2 xfrm state add \
+ src 10.1.1.1 dst 10.1.2.1 \
+ spi 0x1000 proto esp aead 'rfc4106(gcm(aes))' \
+ 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode transport
+
+ ip -netns host-2 xfrm state add \
+ src 10.1.2.1 dst 10.1.1.1 \
+ spi 0x1001 proto esp aead 'rfc4106(gcm(aes))' \
+ 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode transport
+
+ #
+ # IPV6
+ #
+ run_cmd host-1 ip -6 xfrm policy add \
+ src 2001:db8:1::1 dst 2001:db8:2::1 dir out \
+ tmpl proto esp mode transport
+
+ run_cmd host-1 ip -6 xfrm policy add \
+ src 2001:db8:2::1 dst 2001:db8:1::1 dir in \
+ tmpl proto esp mode transport
+
+ run_cmd host-2 ip -6 xfrm policy add \
+ src 2001:db8:1::1 dst 2001:db8:2::1 dir in \
+ tmpl proto esp mode transport
+
+ run_cmd host-2 ip -6 xfrm policy add \
+ src 2001:db8:2::1 dst 2001:db8:1::1 dir out \
+ tmpl proto esp mode transport
+
+ ip -netns host-1 -6 xfrm state add \
+ src 2001:db8:1::1 dst 2001:db8:2::1 \
+ spi 0x1000 proto esp aead 'rfc4106(gcm(aes))' \
+ 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode transport
+
+ ip -netns host-1 -6 xfrm state add \
+ src 2001:db8:2::1 dst 2001:db8:1::1 \
+ spi 0x1001 proto esp aead 'rfc4106(gcm(aes))' \
+ 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode transport
+
+ ip -netns host-2 -6 xfrm state add \
+ src 2001:db8:1::1 dst 2001:db8:2::1 \
+ spi 0x1000 proto esp aead 'rfc4106(gcm(aes))' \
+ 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode transport
+
+ ip -netns host-2 -6 xfrm state add \
+ src 2001:db8:2::1 dst 2001:db8:1::1 \
+ spi 0x1001 proto esp aead 'rfc4106(gcm(aes))' \
+ 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode transport
+}
+
+teardown_ipsec()
+{
+ run_cmd host-1 ip xfrm state flush
+ run_cmd host-1 ip xfrm policy flush
+ run_cmd host-2 ip xfrm state flush
+ run_cmd host-2 ip xfrm policy flush
+}
+
+################################################################################
+# generate traffic through tunnel for various cases
+
+run_ping()
+{
+ local desc="$1"
+
+ run_cmd host-1 ping -c1 -w1 172.16.1.2
+ log_test $? 0 "IPv4 basic L2TP tunnel ${desc}"
+
+ run_cmd host-1 ping -c1 -w1 -I 172.16.101.1 172.16.101.2
+ log_test $? 0 "IPv4 route through L2TP tunnel ${desc}"
+
+ run_cmd host-1 ${ping6} -c1 -w1 fc00:1::2
+ log_test $? 0 "IPv6 basic L2TP tunnel ${desc}"
+
+ run_cmd host-1 ${ping6} -c1 -w1 -I fc00:101::1 fc00:101::2
+ log_test $? 0 "IPv6 route through L2TP tunnel ${desc}"
+}
+
+run_tests()
+{
+ local desc
+
+ setup
+ run_ping
+
+ setup_ipsec
+ run_ping "- with IPsec"
+ run_cmd host-1 ping -c1 -w1 172.16.1.2
+ log_test $? 0 "IPv4 basic L2TP tunnel ${desc}"
+
+ run_cmd host-1 ping -c1 -w1 -I 172.16.101.1 172.16.101.2
+ log_test $? 0 "IPv4 route through L2TP tunnel ${desc}"
+
+ run_cmd host-1 ${ping6} -c1 -w1 fc00:1::2
+ log_test $? 0 "IPv6 basic L2TP tunnel - with IPsec"
+
+ run_cmd host-1 ${ping6} -c1 -w1 -I fc00:101::1 fc00:101::2
+ log_test $? 0 "IPv6 route through L2TP tunnel - with IPsec"
+
+ teardown_ipsec
+ run_ping "- after IPsec teardown"
+}
+
+################################################################################
+# main
+
+declare -i nfail=0
+declare -i nsuccess=0
+
+while getopts :pv o
+do
+ case $o in
+ p) PAUSE_ON_FAIL=yes;;
+ v) VERBOSE=$(($VERBOSE + 1));;
+ *) exit 1;;
+ esac
+done
+
+run_tests
+cleanup
+
+printf "\nTests passed: %3d\n" ${nsuccess}
+printf "Tests failed: %3d\n" ${nfail}
diff --git a/tools/testing/selftests/net/nettest.c b/tools/testing/selftests/net/nettest.c
new file mode 100644
index 000000000000..c08f4db8330d
--- /dev/null
+++ b/tools/testing/selftests/net/nettest.c
@@ -0,0 +1,1757 @@
+// SPDX-License-Identifier: GPL-2.0
+/* nettest - used for functional tests of networking APIs
+ *
+ * Copyright (c) 2013-2019 David Ahern <dsahern@gmail.com>. All rights reserved.
+ */
+
+#define _GNU_SOURCE
+#include <features.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <linux/tcp.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+#ifndef IPV6_UNICAST_IF
+#define IPV6_UNICAST_IF 76
+#endif
+#ifndef IPV6_MULTICAST_IF
+#define IPV6_MULTICAST_IF 17
+#endif
+
+#define DEFAULT_PORT 12345
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+struct sock_args {
+ /* local address */
+ union {
+ struct in_addr in;
+ struct in6_addr in6;
+ } local_addr;
+
+ /* remote address */
+ union {
+ struct in_addr in;
+ struct in6_addr in6;
+ } remote_addr;
+ int scope_id; /* remote scope; v6 send only */
+
+ struct in_addr grp; /* multicast group */
+
+ unsigned int has_local_ip:1,
+ has_remote_ip:1,
+ has_grp:1,
+ has_expected_laddr:1,
+ has_expected_raddr:1,
+ bind_test_only:1;
+
+ unsigned short port;
+
+ int type; /* DGRAM, STREAM, RAW */
+ int protocol;
+ int version; /* AF_INET/AF_INET6 */
+
+ int use_setsockopt;
+ int use_cmsg;
+ const char *dev;
+ int ifindex;
+ const char *password;
+
+ /* expected addresses and device index for connection */
+ int expected_ifindex;
+
+ /* local address */
+ union {
+ struct in_addr in;
+ struct in6_addr in6;
+ } expected_laddr;
+
+ /* remote address */
+ union {
+ struct in_addr in;
+ struct in6_addr in6;
+ } expected_raddr;
+};
+
+static int server_mode;
+static unsigned int prog_timeout = 5;
+static unsigned int interactive;
+static int iter = 1;
+static char *msg = "Hello world!";
+static int msglen;
+static int quiet;
+static int try_broadcast = 1;
+
+static char *timestamp(char *timebuf, int buflen)
+{
+ time_t now;
+
+ now = time(NULL);
+ if (strftime(timebuf, buflen, "%T", localtime(&now)) == 0) {
+ memset(timebuf, 0, buflen);
+ strncpy(timebuf, "00:00:00", buflen-1);
+ }
+
+ return timebuf;
+}
+
+static void log_msg(const char *format, ...)
+{
+ char timebuf[64];
+ va_list args;
+
+ if (quiet)
+ return;
+
+ fprintf(stdout, "%s %s:",
+ timestamp(timebuf, sizeof(timebuf)),
+ server_mode ? "server" : "client");
+ va_start(args, format);
+ vfprintf(stdout, format, args);
+ va_end(args);
+
+ fflush(stdout);
+}
+
+static void log_error(const char *format, ...)
+{
+ char timebuf[64];
+ va_list args;
+
+ if (quiet)
+ return;
+
+ fprintf(stderr, "%s %s:",
+ timestamp(timebuf, sizeof(timebuf)),
+ server_mode ? "server" : "client");
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+
+ fflush(stderr);
+}
+
+static void log_err_errno(const char *fmt, ...)
+{
+ char timebuf[64];
+ va_list args;
+
+ if (quiet)
+ return;
+
+ fprintf(stderr, "%s %s: ",
+ timestamp(timebuf, sizeof(timebuf)),
+ server_mode ? "server" : "client");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+
+ fprintf(stderr, ": %d: %s\n", errno, strerror(errno));
+ fflush(stderr);
+}
+
+static void log_address(const char *desc, struct sockaddr *sa)
+{
+ char addrstr[64];
+
+ if (quiet)
+ return;
+
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *s = (struct sockaddr_in *) sa;
+
+ log_msg("%s %s:%d",
+ desc,
+ inet_ntop(AF_INET, &s->sin_addr, addrstr,
+ sizeof(addrstr)),
+ ntohs(s->sin_port));
+
+ } else if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa;
+
+ log_msg("%s [%s]:%d",
+ desc,
+ inet_ntop(AF_INET6, &s6->sin6_addr, addrstr,
+ sizeof(addrstr)),
+ ntohs(s6->sin6_port));
+ }
+
+ printf("\n");
+
+ fflush(stdout);
+}
+
+static int tcp_md5sig(int sd, void *addr, socklen_t alen, const char *password)
+{
+ struct tcp_md5sig md5sig;
+ int keylen = password ? strlen(password) : 0;
+ int rc;
+
+ memset(&md5sig, 0, sizeof(md5sig));
+ memcpy(&md5sig.tcpm_addr, addr, alen);
+ md5sig.tcpm_keylen = keylen;
+
+ if (keylen)
+ memcpy(md5sig.tcpm_key, password, keylen);
+
+ rc = setsockopt(sd, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof(md5sig));
+ if (rc < 0) {
+ /* ENOENT is harmless. Returned when a password is cleared */
+ if (errno == ENOENT)
+ rc = 0;
+ else
+ log_err_errno("setsockopt(TCP_MD5SIG)");
+ }
+
+ return rc;
+}
+
+static int tcp_md5_remote(int sd, struct sock_args *args)
+{
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ };
+ struct sockaddr_in6 sin6 = {
+ .sin6_family = AF_INET6,
+ };
+ void *addr;
+ int alen;
+
+ switch (args->version) {
+ case AF_INET:
+ sin.sin_port = htons(args->port);
+ sin.sin_addr = args->remote_addr.in;
+ addr = &sin;
+ alen = sizeof(sin);
+ break;
+ case AF_INET6:
+ sin6.sin6_port = htons(args->port);
+ sin6.sin6_addr = args->remote_addr.in6;
+ addr = &sin6;
+ alen = sizeof(sin6);
+ break;
+ default:
+ log_error("unknown address family\n");
+ exit(1);
+ }
+
+ if (tcp_md5sig(sd, addr, alen, args->password))
+ return -1;
+
+ return 0;
+}
+
+static int get_ifidx(const char *ifname)
+{
+ struct ifreq ifdata;
+ int sd, rc;
+
+ if (!ifname || *ifname == '\0')
+ return -1;
+
+ memset(&ifdata, 0, sizeof(ifdata));
+
+ strcpy(ifdata.ifr_name, ifname);
+
+ sd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sd < 0) {
+ log_err_errno("socket failed");
+ return -1;
+ }
+
+ rc = ioctl(sd, SIOCGIFINDEX, (char *)&ifdata);
+ close(sd);
+ if (rc != 0) {
+ log_err_errno("ioctl(SIOCGIFINDEX) failed");
+ return -1;
+ }
+
+ return ifdata.ifr_ifindex;
+}
+
+static int bind_to_device(int sd, const char *name)
+{
+ int rc;
+
+ rc = setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name)+1);
+ if (rc < 0)
+ log_err_errno("setsockopt(SO_BINDTODEVICE)");
+
+ return rc;
+}
+
+static int get_bind_to_device(int sd, char *name, size_t len)
+{
+ int rc;
+ socklen_t optlen = len;
+
+ name[0] = '\0';
+ rc = getsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, name, &optlen);
+ if (rc < 0)
+ log_err_errno("setsockopt(SO_BINDTODEVICE)");
+
+ return rc;
+}
+
+static int check_device(int sd, struct sock_args *args)
+{
+ int ifindex = 0;
+ char name[32];
+
+ if (get_bind_to_device(sd, name, sizeof(name)))
+ *name = '\0';
+ else
+ ifindex = get_ifidx(name);
+
+ log_msg(" bound to device %s/%d\n",
+ *name ? name : "<none>", ifindex);
+
+ if (!args->expected_ifindex)
+ return 0;
+
+ if (args->expected_ifindex != ifindex) {
+ log_error("Device index mismatch: expected %d have %d\n",
+ args->expected_ifindex, ifindex);
+ return 1;
+ }
+
+ log_msg("Device index matches: expected %d have %d\n",
+ args->expected_ifindex, ifindex);
+
+ return 0;
+}
+
+static int set_pktinfo_v4(int sd)
+{
+ int one = 1;
+ int rc;
+
+ rc = setsockopt(sd, SOL_IP, IP_PKTINFO, &one, sizeof(one));
+ if (rc < 0 && rc != -ENOTSUP)
+ log_err_errno("setsockopt(IP_PKTINFO)");
+
+ return rc;
+}
+
+static int set_recvpktinfo_v6(int sd)
+{
+ int one = 1;
+ int rc;
+
+ rc = setsockopt(sd, SOL_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
+ if (rc < 0 && rc != -ENOTSUP)
+ log_err_errno("setsockopt(IPV6_RECVPKTINFO)");
+
+ return rc;
+}
+
+static int set_recverr_v4(int sd)
+{
+ int one = 1;
+ int rc;
+
+ rc = setsockopt(sd, SOL_IP, IP_RECVERR, &one, sizeof(one));
+ if (rc < 0 && rc != -ENOTSUP)
+ log_err_errno("setsockopt(IP_RECVERR)");
+
+ return rc;
+}
+
+static int set_recverr_v6(int sd)
+{
+ int one = 1;
+ int rc;
+
+ rc = setsockopt(sd, SOL_IPV6, IPV6_RECVERR, &one, sizeof(one));
+ if (rc < 0 && rc != -ENOTSUP)
+ log_err_errno("setsockopt(IPV6_RECVERR)");
+
+ return rc;
+}
+
+static int set_unicast_if(int sd, int ifindex, int version)
+{
+ int opt = IP_UNICAST_IF;
+ int level = SOL_IP;
+ int rc;
+
+ ifindex = htonl(ifindex);
+
+ if (version == AF_INET6) {
+ opt = IPV6_UNICAST_IF;
+ level = SOL_IPV6;
+ }
+ rc = setsockopt(sd, level, opt, &ifindex, sizeof(ifindex));
+ if (rc < 0)
+ log_err_errno("setsockopt(IP_UNICAST_IF)");
+
+ return rc;
+}
+
+static int set_multicast_if(int sd, int ifindex)
+{
+ struct ip_mreqn mreq = { .imr_ifindex = ifindex };
+ int rc;
+
+ rc = setsockopt(sd, SOL_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq));
+ if (rc < 0)
+ log_err_errno("setsockopt(IP_MULTICAST_IF)");
+
+ return rc;
+}
+
+static int set_membership(int sd, uint32_t grp, uint32_t addr, int ifindex)
+{
+ uint32_t if_addr = addr;
+ struct ip_mreqn mreq;
+ int rc;
+
+ if (addr == htonl(INADDR_ANY) && !ifindex) {
+ log_error("Either local address or device needs to be given for multicast membership\n");
+ return -1;
+ }
+
+ mreq.imr_multiaddr.s_addr = grp;
+ mreq.imr_address.s_addr = if_addr;
+ mreq.imr_ifindex = ifindex;
+
+ rc = setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+ if (rc < 0) {
+ log_err_errno("setsockopt(IP_ADD_MEMBERSHIP)");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int set_broadcast(int sd)
+{
+ unsigned int one = 1;
+ int rc = 0;
+
+ if (setsockopt(sd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) != 0) {
+ log_err_errno("setsockopt(SO_BROADCAST)");
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static int set_reuseport(int sd)
+{
+ unsigned int one = 1;
+ int rc = 0;
+
+ if (setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) != 0) {
+ log_err_errno("setsockopt(SO_REUSEPORT)");
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static int set_reuseaddr(int sd)
+{
+ unsigned int one = 1;
+ int rc = 0;
+
+ if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != 0) {
+ log_err_errno("setsockopt(SO_REUSEADDR)");
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static int str_to_uint(const char *str, int min, int max, unsigned int *value)
+{
+ int number;
+ char *end;
+
+ errno = 0;
+ number = (unsigned int) strtoul(str, &end, 0);
+
+ /* entire string should be consumed by conversion
+ * and value should be between min and max
+ */
+ if (((*end == '\0') || (*end == '\n')) && (end != str) &&
+ (errno != ERANGE) && (min <= number) && (number <= max)) {
+ *value = number;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int expected_addr_match(struct sockaddr *sa, void *expected,
+ const char *desc)
+{
+ char addrstr[64];
+ int rc = 0;
+
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *s = (struct sockaddr_in *) sa;
+ struct in_addr *exp_in = (struct in_addr *) expected;
+
+ if (s->sin_addr.s_addr != exp_in->s_addr) {
+ log_error("%s address does not match expected %s",
+ desc,
+ inet_ntop(AF_INET, exp_in,
+ addrstr, sizeof(addrstr)));
+ rc = 1;
+ }
+ } else if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa;
+ struct in6_addr *exp_in = (struct in6_addr *) expected;
+
+ if (memcmp(&s6->sin6_addr, exp_in, sizeof(*exp_in))) {
+ log_error("%s address does not match expected %s",
+ desc,
+ inet_ntop(AF_INET6, exp_in,
+ addrstr, sizeof(addrstr)));
+ rc = 1;
+ }
+ } else {
+ log_error("%s address does not match expected - unknown family",
+ desc);
+ rc = 1;
+ }
+
+ if (!rc)
+ log_msg("%s address matches expected\n", desc);
+
+ return rc;
+}
+
+static int show_sockstat(int sd, struct sock_args *args)
+{
+ struct sockaddr_in6 local_addr, remote_addr;
+ socklen_t alen = sizeof(local_addr);
+ struct sockaddr *sa;
+ const char *desc;
+ int rc = 0;
+
+ desc = server_mode ? "server local:" : "client local:";
+ sa = (struct sockaddr *) &local_addr;
+ if (getsockname(sd, sa, &alen) == 0) {
+ log_address(desc, sa);
+
+ if (args->has_expected_laddr) {
+ rc = expected_addr_match(sa, &args->expected_laddr,
+ "local");
+ }
+ } else {
+ log_err_errno("getsockname failed");
+ }
+
+ sa = (struct sockaddr *) &remote_addr;
+ desc = server_mode ? "server peer:" : "client peer:";
+ if (getpeername(sd, sa, &alen) == 0) {
+ log_address(desc, sa);
+
+ if (args->has_expected_raddr) {
+ rc |= expected_addr_match(sa, &args->expected_raddr,
+ "remote");
+ }
+ } else {
+ log_err_errno("getpeername failed");
+ }
+
+ return rc;
+}
+
+static int get_index_from_cmsg(struct msghdr *m)
+{
+ struct cmsghdr *cm;
+ int ifindex = 0;
+ char buf[64];
+
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(m);
+ m->msg_controllen != 0 && cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(m, cm)) {
+
+ if (cm->cmsg_level == SOL_IP &&
+ cm->cmsg_type == IP_PKTINFO) {
+ struct in_pktinfo *pi;
+
+ pi = (struct in_pktinfo *)(CMSG_DATA(cm));
+ inet_ntop(AF_INET, &pi->ipi_addr, buf, sizeof(buf));
+ ifindex = pi->ipi_ifindex;
+ } else if (cm->cmsg_level == SOL_IPV6 &&
+ cm->cmsg_type == IPV6_PKTINFO) {
+ struct in6_pktinfo *pi6;
+
+ pi6 = (struct in6_pktinfo *)(CMSG_DATA(cm));
+ inet_ntop(AF_INET6, &pi6->ipi6_addr, buf, sizeof(buf));
+ ifindex = pi6->ipi6_ifindex;
+ }
+ }
+
+ if (ifindex) {
+ log_msg(" pktinfo: ifindex %d dest addr %s\n",
+ ifindex, buf);
+ }
+ return ifindex;
+}
+
+static int send_msg_no_cmsg(int sd, void *addr, socklen_t alen)
+{
+ int err;
+
+again:
+ err = sendto(sd, msg, msglen, 0, addr, alen);
+ if (err < 0) {
+ if (errno == EACCES && try_broadcast) {
+ try_broadcast = 0;
+ if (!set_broadcast(sd))
+ goto again;
+ errno = EACCES;
+ }
+
+ log_err_errno("sendto failed");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int send_msg_cmsg(int sd, void *addr, socklen_t alen,
+ int ifindex, int version)
+{
+ unsigned char cmsgbuf[64];
+ struct iovec iov[2];
+ struct cmsghdr *cm;
+ struct msghdr m;
+ int err;
+
+ iov[0].iov_base = msg;
+ iov[0].iov_len = msglen;
+ m.msg_iov = iov;
+ m.msg_iovlen = 1;
+ m.msg_name = (caddr_t)addr;
+ m.msg_namelen = alen;
+
+ memset(cmsgbuf, 0, sizeof(cmsgbuf));
+ cm = (struct cmsghdr *)cmsgbuf;
+ m.msg_control = (caddr_t)cm;
+
+ if (version == AF_INET) {
+ struct in_pktinfo *pi;
+
+ cm->cmsg_level = SOL_IP;
+ cm->cmsg_type = IP_PKTINFO;
+ cm->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+ pi = (struct in_pktinfo *)(CMSG_DATA(cm));
+ pi->ipi_ifindex = ifindex;
+
+ m.msg_controllen = cm->cmsg_len;
+
+ } else if (version == AF_INET6) {
+ struct in6_pktinfo *pi6;
+
+ cm->cmsg_level = SOL_IPV6;
+ cm->cmsg_type = IPV6_PKTINFO;
+ cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+
+ pi6 = (struct in6_pktinfo *)(CMSG_DATA(cm));
+ pi6->ipi6_ifindex = ifindex;
+
+ m.msg_controllen = cm->cmsg_len;
+ }
+
+again:
+ err = sendmsg(sd, &m, 0);
+ if (err < 0) {
+ if (errno == EACCES && try_broadcast) {
+ try_broadcast = 0;
+ if (!set_broadcast(sd))
+ goto again;
+ errno = EACCES;
+ }
+
+ log_err_errno("sendmsg failed");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int send_msg(int sd, void *addr, socklen_t alen, struct sock_args *args)
+{
+ if (args->type == SOCK_STREAM) {
+ if (write(sd, msg, msglen) < 0) {
+ log_err_errno("write failed sending msg to peer");
+ return 1;
+ }
+ } else if (args->ifindex && args->use_cmsg) {
+ if (send_msg_cmsg(sd, addr, alen, args->ifindex, args->version))
+ return 1;
+ } else {
+ if (send_msg_no_cmsg(sd, addr, alen))
+ return 1;
+ }
+
+ log_msg("Sent message:\n");
+ log_msg(" %.24s%s\n", msg, msglen > 24 ? " ..." : "");
+
+ return 0;
+}
+
+static int socket_read_dgram(int sd, struct sock_args *args)
+{
+ unsigned char addr[sizeof(struct sockaddr_in6)];
+ struct sockaddr *sa = (struct sockaddr *) addr;
+ socklen_t alen = sizeof(addr);
+ struct iovec iov[2];
+ struct msghdr m = {
+ .msg_name = (caddr_t)addr,
+ .msg_namelen = alen,
+ .msg_iov = iov,
+ .msg_iovlen = 1,
+ };
+ unsigned char cmsgbuf[256];
+ struct cmsghdr *cm = (struct cmsghdr *)cmsgbuf;
+ char buf[16*1024];
+ int ifindex;
+ int len;
+
+ iov[0].iov_base = (caddr_t)buf;
+ iov[0].iov_len = sizeof(buf);
+
+ memset(cmsgbuf, 0, sizeof(cmsgbuf));
+ m.msg_control = (caddr_t)cm;
+ m.msg_controllen = sizeof(cmsgbuf);
+
+ len = recvmsg(sd, &m, 0);
+ if (len == 0) {
+ log_msg("peer closed connection.\n");
+ return 0;
+ } else if (len < 0) {
+ log_msg("failed to read message: %d: %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ buf[len] = '\0';
+
+ log_address("Message from:", sa);
+ log_msg(" %.24s%s\n", buf, len > 24 ? " ..." : "");
+
+ ifindex = get_index_from_cmsg(&m);
+ if (args->expected_ifindex) {
+ if (args->expected_ifindex != ifindex) {
+ log_error("Device index mismatch: expected %d have %d\n",
+ args->expected_ifindex, ifindex);
+ return -1;
+ }
+ log_msg("Device index matches: expected %d have %d\n",
+ args->expected_ifindex, ifindex);
+ }
+
+ if (!interactive && server_mode) {
+ if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa;
+ struct in6_addr *in6 = &s6->sin6_addr;
+
+ if (IN6_IS_ADDR_V4MAPPED(in6)) {
+ const uint32_t *pa = (uint32_t *) &in6->s6_addr;
+ struct in_addr in4;
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in *) addr;
+ pa += 3;
+ in4.s_addr = *pa;
+ sin->sin_addr = in4;
+ sin->sin_family = AF_INET;
+ if (send_msg_cmsg(sd, addr, alen,
+ ifindex, AF_INET) < 0)
+ goto out_err;
+ }
+ }
+again:
+ iov[0].iov_len = len;
+
+ if (args->version == AF_INET6) {
+ struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa;
+
+ if (args->dev) {
+ /* avoid PKTINFO conflicts with bindtodev */
+ if (sendto(sd, buf, len, 0,
+ (void *) addr, alen) < 0)
+ goto out_err;
+ } else {
+ /* kernel is allowing scope_id to be set to VRF
+ * index for LLA. for sends to global address
+ * reset scope id
+ */
+ s6->sin6_scope_id = ifindex;
+ if (sendmsg(sd, &m, 0) < 0)
+ goto out_err;
+ }
+ } else {
+ int err;
+
+ err = sendmsg(sd, &m, 0);
+ if (err < 0) {
+ if (errno == EACCES && try_broadcast) {
+ try_broadcast = 0;
+ if (!set_broadcast(sd))
+ goto again;
+ errno = EACCES;
+ }
+ goto out_err;
+ }
+ }
+ log_msg("Sent message:\n");
+ log_msg(" %.24s%s\n", buf, len > 24 ? " ..." : "");
+ }
+
+ return 1;
+out_err:
+ log_err_errno("failed to send msg to peer");
+ return -1;
+}
+
+static int socket_read_stream(int sd)
+{
+ char buf[1024];
+ int len;
+
+ len = read(sd, buf, sizeof(buf)-1);
+ if (len == 0) {
+ log_msg("client closed connection.\n");
+ return 0;
+ } else if (len < 0) {
+ log_msg("failed to read message\n");
+ return -1;
+ }
+
+ buf[len] = '\0';
+ log_msg("Incoming message:\n");
+ log_msg(" %.24s%s\n", buf, len > 24 ? " ..." : "");
+
+ if (!interactive && server_mode) {
+ if (write(sd, buf, len) < 0) {
+ log_err_errno("failed to send buf");
+ return -1;
+ }
+ log_msg("Sent message:\n");
+ log_msg(" %.24s%s\n", buf, len > 24 ? " ..." : "");
+ }
+
+ return 1;
+}
+
+static int socket_read(int sd, struct sock_args *args)
+{
+ if (args->type == SOCK_STREAM)
+ return socket_read_stream(sd);
+
+ return socket_read_dgram(sd, args);
+}
+
+static int stdin_to_socket(int sd, int type, void *addr, socklen_t alen)
+{
+ char buf[1024];
+ int len;
+
+ if (fgets(buf, sizeof(buf), stdin) == NULL)
+ return 0;
+
+ len = strlen(buf);
+ if (type == SOCK_STREAM) {
+ if (write(sd, buf, len) < 0) {
+ log_err_errno("failed to send buf");
+ return -1;
+ }
+ } else {
+ int err;
+
+again:
+ err = sendto(sd, buf, len, 0, addr, alen);
+ if (err < 0) {
+ if (errno == EACCES && try_broadcast) {
+ try_broadcast = 0;
+ if (!set_broadcast(sd))
+ goto again;
+ errno = EACCES;
+ }
+ log_err_errno("failed to send msg to peer");
+ return -1;
+ }
+ }
+ log_msg("Sent message:\n");
+ log_msg(" %.24s%s\n", buf, len > 24 ? " ..." : "");
+
+ return 1;
+}
+
+static void set_recv_attr(int sd, int version)
+{
+ if (version == AF_INET6) {
+ set_recvpktinfo_v6(sd);
+ set_recverr_v6(sd);
+ } else {
+ set_pktinfo_v4(sd);
+ set_recverr_v4(sd);
+ }
+}
+
+static int msg_loop(int client, int sd, void *addr, socklen_t alen,
+ struct sock_args *args)
+{
+ struct timeval timeout = { .tv_sec = prog_timeout }, *ptval = NULL;
+ fd_set rfds;
+ int nfds;
+ int rc;
+
+ if (args->type != SOCK_STREAM)
+ set_recv_attr(sd, args->version);
+
+ if (msg) {
+ msglen = strlen(msg);
+
+ /* client sends first message */
+ if (client) {
+ if (send_msg(sd, addr, alen, args))
+ return 1;
+ }
+ if (!interactive) {
+ ptval = &timeout;
+ if (!prog_timeout)
+ timeout.tv_sec = 5;
+ }
+ }
+
+ nfds = interactive ? MAX(fileno(stdin), sd) + 1 : sd + 1;
+ while (1) {
+ FD_ZERO(&rfds);
+ FD_SET(sd, &rfds);
+ if (interactive)
+ FD_SET(fileno(stdin), &rfds);
+
+ rc = select(nfds, &rfds, NULL, NULL, ptval);
+ if (rc < 0) {
+ if (errno == EINTR)
+ continue;
+
+ rc = 1;
+ log_err_errno("select failed");
+ break;
+ } else if (rc == 0) {
+ log_error("Timed out waiting for response\n");
+ rc = 2;
+ break;
+ }
+
+ if (FD_ISSET(sd, &rfds)) {
+ rc = socket_read(sd, args);
+ if (rc < 0) {
+ rc = 1;
+ break;
+ }
+ if (rc == 0)
+ break;
+ }
+
+ rc = 0;
+
+ if (FD_ISSET(fileno(stdin), &rfds)) {
+ if (stdin_to_socket(sd, args->type, addr, alen) <= 0)
+ break;
+ }
+
+ if (interactive)
+ continue;
+
+ if (iter != -1) {
+ --iter;
+ if (iter == 0)
+ break;
+ }
+
+ log_msg("Going into quiet mode\n");
+ quiet = 1;
+
+ if (client) {
+ if (send_msg(sd, addr, alen, args)) {
+ rc = 1;
+ break;
+ }
+ }
+ }
+
+ return rc;
+}
+
+static int msock_init(struct sock_args *args, int server)
+{
+ uint32_t if_addr = htonl(INADDR_ANY);
+ struct sockaddr_in laddr = {
+ .sin_family = AF_INET,
+ .sin_port = htons(args->port),
+ };
+ int one = 1;
+ int sd;
+
+ if (!server && args->has_local_ip)
+ if_addr = args->local_addr.in.s_addr;
+
+ sd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sd < 0) {
+ log_err_errno("socket");
+ return -1;
+ }
+
+ if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&one, sizeof(one)) < 0) {
+ log_err_errno("Setting SO_REUSEADDR error");
+ goto out_err;
+ }
+
+ if (setsockopt(sd, SOL_SOCKET, SO_BROADCAST,
+ (char *)&one, sizeof(one)) < 0)
+ log_err_errno("Setting SO_BROADCAST error");
+
+ if (args->dev && bind_to_device(sd, args->dev) != 0)
+ goto out_err;
+ else if (args->use_setsockopt &&
+ set_multicast_if(sd, args->ifindex))
+ goto out_err;
+
+ laddr.sin_addr.s_addr = if_addr;
+
+ if (bind(sd, (struct sockaddr *) &laddr, sizeof(laddr)) < 0) {
+ log_err_errno("bind failed");
+ goto out_err;
+ }
+
+ if (server &&
+ set_membership(sd, args->grp.s_addr,
+ args->local_addr.in.s_addr, args->ifindex))
+ goto out_err;
+
+ return sd;
+out_err:
+ close(sd);
+ return -1;
+}
+
+static int msock_server(struct sock_args *args)
+{
+ return msock_init(args, 1);
+}
+
+static int msock_client(struct sock_args *args)
+{
+ return msock_init(args, 0);
+}
+
+static int bind_socket(int sd, struct sock_args *args)
+{
+ struct sockaddr_in serv_addr = {
+ .sin_family = AF_INET,
+ };
+ struct sockaddr_in6 serv6_addr = {
+ .sin6_family = AF_INET6,
+ };
+ void *addr;
+ socklen_t alen;
+
+ if (!args->has_local_ip && args->type == SOCK_RAW)
+ return 0;
+
+ switch (args->version) {
+ case AF_INET:
+ serv_addr.sin_port = htons(args->port);
+ serv_addr.sin_addr = args->local_addr.in;
+ addr = &serv_addr;
+ alen = sizeof(serv_addr);
+ break;
+
+ case AF_INET6:
+ serv6_addr.sin6_port = htons(args->port);
+ serv6_addr.sin6_addr = args->local_addr.in6;
+ addr = &serv6_addr;
+ alen = sizeof(serv6_addr);
+ break;
+
+ default:
+ log_error("Invalid address family\n");
+ return -1;
+ }
+
+ if (bind(sd, addr, alen) < 0) {
+ log_err_errno("error binding socket");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int lsock_init(struct sock_args *args)
+{
+ long flags;
+ int sd;
+
+ sd = socket(args->version, args->type, args->protocol);
+ if (sd < 0) {
+ log_err_errno("Error opening socket");
+ return -1;
+ }
+
+ if (set_reuseaddr(sd) != 0)
+ goto err;
+
+ if (set_reuseport(sd) != 0)
+ goto err;
+
+ if (args->dev && bind_to_device(sd, args->dev) != 0)
+ goto err;
+ else if (args->use_setsockopt &&
+ set_unicast_if(sd, args->ifindex, args->version))
+ goto err;
+
+ if (bind_socket(sd, args))
+ goto err;
+
+ if (args->bind_test_only)
+ goto out;
+
+ if (args->type == SOCK_STREAM && listen(sd, 1) < 0) {
+ log_err_errno("listen failed");
+ goto err;
+ }
+
+ flags = fcntl(sd, F_GETFL);
+ if ((flags < 0) || (fcntl(sd, F_SETFL, flags|O_NONBLOCK) < 0)) {
+ log_err_errno("Failed to set non-blocking option");
+ goto err;
+ }
+
+ if (fcntl(sd, F_SETFD, FD_CLOEXEC) < 0)
+ log_err_errno("Failed to set close-on-exec flag");
+
+out:
+ return sd;
+
+err:
+ close(sd);
+ return -1;
+}
+
+static int do_server(struct sock_args *args)
+{
+ struct timeval timeout = { .tv_sec = prog_timeout }, *ptval = NULL;
+ unsigned char addr[sizeof(struct sockaddr_in6)] = {};
+ socklen_t alen = sizeof(addr);
+ int lsd, csd = -1;
+
+ fd_set rfds;
+ int rc;
+
+ if (prog_timeout)
+ ptval = &timeout;
+
+ if (args->has_grp)
+ lsd = msock_server(args);
+ else
+ lsd = lsock_init(args);
+
+ if (lsd < 0)
+ return 1;
+
+ if (args->bind_test_only) {
+ close(lsd);
+ return 0;
+ }
+
+ if (args->type != SOCK_STREAM) {
+ rc = msg_loop(0, lsd, (void *) addr, alen, args);
+ close(lsd);
+ return rc;
+ }
+
+ if (args->password && tcp_md5_remote(lsd, args)) {
+ close(lsd);
+ return -1;
+ }
+
+ while (1) {
+ log_msg("\n");
+ log_msg("waiting for client connection.\n");
+ FD_ZERO(&rfds);
+ FD_SET(lsd, &rfds);
+
+ rc = select(lsd+1, &rfds, NULL, NULL, ptval);
+ if (rc == 0) {
+ rc = 2;
+ break;
+ }
+
+ if (rc < 0) {
+ if (errno == EINTR)
+ continue;
+
+ log_err_errno("select failed");
+ break;
+ }
+
+ if (FD_ISSET(lsd, &rfds)) {
+
+ csd = accept(lsd, (void *) addr, &alen);
+ if (csd < 0) {
+ log_err_errno("accept failed");
+ break;
+ }
+
+ rc = show_sockstat(csd, args);
+ if (rc)
+ break;
+
+ rc = check_device(csd, args);
+ if (rc)
+ break;
+ }
+
+ rc = msg_loop(0, csd, (void *) addr, alen, args);
+ close(csd);
+
+ if (!interactive)
+ break;
+ }
+
+ close(lsd);
+
+ return rc;
+}
+
+static int wait_for_connect(int sd)
+{
+ struct timeval _tv = { .tv_sec = prog_timeout }, *tv = NULL;
+ fd_set wfd;
+ int val = 0, sz = sizeof(val);
+ int rc;
+
+ FD_ZERO(&wfd);
+ FD_SET(sd, &wfd);
+
+ if (prog_timeout)
+ tv = &_tv;
+
+ rc = select(FD_SETSIZE, NULL, &wfd, NULL, tv);
+ if (rc == 0) {
+ log_error("connect timed out\n");
+ return -2;
+ } else if (rc < 0) {
+ log_err_errno("select failed");
+ return -3;
+ }
+
+ if (getsockopt(sd, SOL_SOCKET, SO_ERROR, &val, (socklen_t *)&sz) < 0) {
+ log_err_errno("getsockopt(SO_ERROR) failed");
+ return -4;
+ }
+
+ if (val != 0) {
+ log_error("connect failed: %d: %s\n", val, strerror(val));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int connectsock(void *addr, socklen_t alen, struct sock_args *args)
+{
+ int sd, rc = -1;
+ long flags;
+
+ sd = socket(args->version, args->type, args->protocol);
+ if (sd < 0) {
+ log_err_errno("Failed to create socket");
+ return -1;
+ }
+
+ flags = fcntl(sd, F_GETFL);
+ if ((flags < 0) || (fcntl(sd, F_SETFL, flags|O_NONBLOCK) < 0)) {
+ log_err_errno("Failed to set non-blocking option");
+ goto err;
+ }
+
+ if (set_reuseport(sd) != 0)
+ goto err;
+
+ if (args->dev && bind_to_device(sd, args->dev) != 0)
+ goto err;
+ else if (args->use_setsockopt &&
+ set_unicast_if(sd, args->ifindex, args->version))
+ goto err;
+
+ if (args->has_local_ip && bind_socket(sd, args))
+ goto err;
+
+ if (args->type != SOCK_STREAM)
+ goto out;
+
+ if (args->password && tcp_md5sig(sd, addr, alen, args->password))
+ goto err;
+
+ if (args->bind_test_only)
+ goto out;
+
+ if (connect(sd, addr, alen) < 0) {
+ if (errno != EINPROGRESS) {
+ log_err_errno("Failed to connect to remote host");
+ rc = -1;
+ goto err;
+ }
+ rc = wait_for_connect(sd);
+ if (rc < 0)
+ goto err;
+ }
+out:
+ return sd;
+
+err:
+ close(sd);
+ return rc;
+}
+
+static int do_client(struct sock_args *args)
+{
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ };
+ struct sockaddr_in6 sin6 = {
+ .sin6_family = AF_INET6,
+ };
+ void *addr;
+ int alen;
+ int rc = 0;
+ int sd;
+
+ if (!args->has_remote_ip && !args->has_grp) {
+ fprintf(stderr, "remote IP or multicast group not given\n");
+ return 1;
+ }
+
+ switch (args->version) {
+ case AF_INET:
+ sin.sin_port = htons(args->port);
+ if (args->has_grp)
+ sin.sin_addr = args->grp;
+ else
+ sin.sin_addr = args->remote_addr.in;
+ addr = &sin;
+ alen = sizeof(sin);
+ break;
+ case AF_INET6:
+ sin6.sin6_port = htons(args->port);
+ sin6.sin6_addr = args->remote_addr.in6;
+ sin6.sin6_scope_id = args->scope_id;
+ addr = &sin6;
+ alen = sizeof(sin6);
+ break;
+ }
+
+ if (args->has_grp)
+ sd = msock_client(args);
+ else
+ sd = connectsock(addr, alen, args);
+
+ if (sd < 0)
+ return -sd;
+
+ if (args->bind_test_only)
+ goto out;
+
+ if (args->type == SOCK_STREAM) {
+ rc = show_sockstat(sd, args);
+ if (rc != 0)
+ goto out;
+ }
+
+ rc = msg_loop(1, sd, addr, alen, args);
+
+out:
+ close(sd);
+
+ return rc;
+}
+
+enum addr_type {
+ ADDR_TYPE_LOCAL,
+ ADDR_TYPE_REMOTE,
+ ADDR_TYPE_MCAST,
+ ADDR_TYPE_EXPECTED_LOCAL,
+ ADDR_TYPE_EXPECTED_REMOTE,
+};
+
+static int convert_addr(struct sock_args *args, const char *_str,
+ enum addr_type atype)
+{
+ int family = args->version;
+ struct in6_addr *in6;
+ struct in_addr *in;
+ const char *desc;
+ char *str, *dev;
+ void *addr;
+ int rc = 0;
+
+ str = strdup(_str);
+ if (!str)
+ return -ENOMEM;
+
+ switch (atype) {
+ case ADDR_TYPE_LOCAL:
+ desc = "local";
+ addr = &args->local_addr;
+ break;
+ case ADDR_TYPE_REMOTE:
+ desc = "remote";
+ addr = &args->remote_addr;
+ break;
+ case ADDR_TYPE_MCAST:
+ desc = "mcast grp";
+ addr = &args->grp;
+ break;
+ case ADDR_TYPE_EXPECTED_LOCAL:
+ desc = "expected local";
+ addr = &args->expected_laddr;
+ break;
+ case ADDR_TYPE_EXPECTED_REMOTE:
+ desc = "expected remote";
+ addr = &args->expected_raddr;
+ break;
+ default:
+ log_error("unknown address type");
+ exit(1);
+ }
+
+ switch (family) {
+ case AF_INET:
+ in = (struct in_addr *) addr;
+ if (str) {
+ if (inet_pton(AF_INET, str, in) == 0) {
+ log_error("Invalid %s IP address\n", desc);
+ rc = -1;
+ goto out;
+ }
+ } else {
+ in->s_addr = htonl(INADDR_ANY);
+ }
+ break;
+
+ case AF_INET6:
+ dev = strchr(str, '%');
+ if (dev) {
+ *dev = '\0';
+ dev++;
+ }
+
+ in6 = (struct in6_addr *) addr;
+ if (str) {
+ if (inet_pton(AF_INET6, str, in6) == 0) {
+ log_error("Invalid %s IPv6 address\n", desc);
+ rc = -1;
+ goto out;
+ }
+ } else {
+ *in6 = in6addr_any;
+ }
+ if (dev) {
+ args->scope_id = get_ifidx(dev);
+ if (args->scope_id < 0) {
+ log_error("Invalid scope on %s IPv6 address\n",
+ desc);
+ rc = -1;
+ goto out;
+ }
+ }
+ break;
+
+ default:
+ log_error("Invalid address family\n");
+ }
+
+out:
+ free(str);
+ return rc;
+}
+
+static char *random_msg(int len)
+{
+ int i, n = 0, olen = len + 1;
+ char *m;
+
+ if (len <= 0)
+ return NULL;
+
+ m = malloc(olen);
+ if (!m)
+ return NULL;
+
+ while (len > 26) {
+ i = snprintf(m + n, olen - n, "%.26s",
+ "abcdefghijklmnopqrstuvwxyz");
+ n += i;
+ len -= i;
+ }
+ i = snprintf(m + n, olen - n, "%.*s", len,
+ "abcdefghijklmnopqrstuvwxyz");
+ return m;
+}
+
+#define GETOPT_STR "sr:l:p:t:g:P:DRn:M:d:SCi6L:0:1:2:Fbq"
+
+static void print_usage(char *prog)
+{
+ printf(
+ "usage: %s OPTS\n"
+ "Required:\n"
+ " -r addr remote address to connect to (client mode only)\n"
+ " -p port port to connect to (client mode)/listen on (server mode)\n"
+ " (default: %d)\n"
+ " -s server mode (default: client mode)\n"
+ " -t timeout seconds (default: none)\n"
+ "\n"
+ "Optional:\n"
+ " -F Restart server loop\n"
+ " -6 IPv6 (default is IPv4)\n"
+ " -P proto protocol for socket: icmp, ospf (default: none)\n"
+ " -D|R datagram (D) / raw (R) socket (default stream)\n"
+ " -l addr local address to bind to\n"
+ "\n"
+ " -d dev bind socket to given device name\n"
+ " -S use setsockopt (IP_UNICAST_IF or IP_MULTICAST_IF)\n"
+ " to set device binding\n"
+ " -C use cmsg and IP_PKTINFO to specify device binding\n"
+ "\n"
+ " -L len send random message of given length\n"
+ " -n num number of times to send message\n"
+ "\n"
+ " -M password use MD5 sum protection\n"
+ " -g grp multicast group (e.g., 239.1.1.1)\n"
+ " -i interactive mode (default is echo and terminate)\n"
+ "\n"
+ " -0 addr Expected local address\n"
+ " -1 addr Expected remote address\n"
+ " -2 dev Expected device name (or index) to receive packet\n"
+ "\n"
+ " -b Bind test only.\n"
+ " -q Be quiet. Run test without printing anything.\n"
+ , prog, DEFAULT_PORT);
+}
+
+int main(int argc, char *argv[])
+{
+ struct sock_args args = {
+ .version = AF_INET,
+ .type = SOCK_STREAM,
+ .port = DEFAULT_PORT,
+ };
+ struct protoent *pe;
+ unsigned int tmp;
+ int forever = 0;
+
+ /* process inputs */
+ extern char *optarg;
+ int rc = 0;
+
+ /*
+ * process input args
+ */
+
+ while ((rc = getopt(argc, argv, GETOPT_STR)) != -1) {
+ switch (rc) {
+ case 's':
+ server_mode = 1;
+ break;
+ case 'F':
+ forever = 1;
+ break;
+ case 'l':
+ args.has_local_ip = 1;
+ if (convert_addr(&args, optarg, ADDR_TYPE_LOCAL) < 0)
+ return 1;
+ break;
+ case 'r':
+ args.has_remote_ip = 1;
+ if (convert_addr(&args, optarg, ADDR_TYPE_REMOTE) < 0)
+ return 1;
+ break;
+ case 'p':
+ if (str_to_uint(optarg, 1, 65535, &tmp) != 0) {
+ fprintf(stderr, "Invalid port\n");
+ return 1;
+ }
+ args.port = (unsigned short) tmp;
+ break;
+ case 't':
+ if (str_to_uint(optarg, 0, INT_MAX,
+ &prog_timeout) != 0) {
+ fprintf(stderr, "Invalid timeout\n");
+ return 1;
+ }
+ break;
+ case 'D':
+ args.type = SOCK_DGRAM;
+ break;
+ case 'R':
+ args.type = SOCK_RAW;
+ args.port = 0;
+ break;
+ case 'P':
+ pe = getprotobyname(optarg);
+ if (pe) {
+ args.protocol = pe->p_proto;
+ } else {
+ if (str_to_uint(optarg, 0, 0xffff, &tmp) != 0) {
+ fprintf(stderr, "Invalid protocol\n");
+ return 1;
+ }
+ args.protocol = tmp;
+ }
+ break;
+ case 'n':
+ iter = atoi(optarg);
+ break;
+ case 'L':
+ msg = random_msg(atoi(optarg));
+ break;
+ case 'M':
+ args.password = optarg;
+ break;
+ case 'S':
+ args.use_setsockopt = 1;
+ break;
+ case 'C':
+ args.use_cmsg = 1;
+ break;
+ case 'd':
+ args.dev = optarg;
+ args.ifindex = get_ifidx(optarg);
+ if (args.ifindex < 0) {
+ fprintf(stderr, "Invalid device name\n");
+ return 1;
+ }
+ break;
+ case 'i':
+ interactive = 1;
+ break;
+ case 'g':
+ args.has_grp = 1;
+ if (convert_addr(&args, optarg, ADDR_TYPE_MCAST) < 0)
+ return 1;
+ args.type = SOCK_DGRAM;
+ break;
+ case '6':
+ args.version = AF_INET6;
+ break;
+ case 'b':
+ args.bind_test_only = 1;
+ break;
+ case '0':
+ args.has_expected_laddr = 1;
+ if (convert_addr(&args, optarg,
+ ADDR_TYPE_EXPECTED_LOCAL))
+ return 1;
+ break;
+ case '1':
+ args.has_expected_raddr = 1;
+ if (convert_addr(&args, optarg,
+ ADDR_TYPE_EXPECTED_REMOTE))
+ return 1;
+
+ break;
+ case '2':
+ if (str_to_uint(optarg, 0, INT_MAX, &tmp) == 0) {
+ args.expected_ifindex = (int)tmp;
+ } else {
+ args.expected_ifindex = get_ifidx(optarg);
+ if (args.expected_ifindex < 0) {
+ fprintf(stderr,
+ "Invalid expected device\n");
+ return 1;
+ }
+ }
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ default:
+ print_usage(argv[0]);
+ return 1;
+ }
+ }
+
+ if (args.password &&
+ (!args.has_remote_ip || args.type != SOCK_STREAM)) {
+ log_error("MD5 passwords apply to TCP only and require a remote ip for the password\n");
+ return 1;
+ }
+
+ if ((args.use_setsockopt || args.use_cmsg) && !args.ifindex) {
+ fprintf(stderr, "Device binding not specified\n");
+ return 1;
+ }
+ if (args.use_setsockopt || args.use_cmsg)
+ args.dev = NULL;
+
+ if (iter == 0) {
+ fprintf(stderr, "Invalid number of messages to send\n");
+ return 1;
+ }
+
+ if (args.type == SOCK_STREAM && !args.protocol)
+ args.protocol = IPPROTO_TCP;
+ if (args.type == SOCK_DGRAM && !args.protocol)
+ args.protocol = IPPROTO_UDP;
+
+ if ((args.type == SOCK_STREAM || args.type == SOCK_DGRAM) &&
+ args.port == 0) {
+ fprintf(stderr, "Invalid port number\n");
+ return 1;
+ }
+
+ if (!server_mode && !args.has_grp &&
+ !args.has_remote_ip && !args.has_local_ip) {
+ fprintf(stderr,
+ "Local (server mode) or remote IP (client IP) required\n");
+ return 1;
+ }
+
+ if (interactive) {
+ prog_timeout = 0;
+ msg = NULL;
+ }
+
+ if (server_mode) {
+ do {
+ rc = do_server(&args);
+ } while (forever);
+
+ return rc;
+ }
+ return do_client(&args);
+}
diff --git a/tools/testing/selftests/net/pmtu.sh b/tools/testing/selftests/net/pmtu.sh
index 524b15dabb3c..ab367e75f095 100755
--- a/tools/testing/selftests/net/pmtu.sh
+++ b/tools/testing/selftests/net/pmtu.sh
@@ -111,6 +111,14 @@
#
# - cleanup_ipv6_exception
# Same as above, but use IPv6 transport from A to B
+#
+# - list_flush_ipv4_exception
+# Using the same topology as in pmtu_ipv4, create exceptions, and check
+# they are shown when listing exception caches, gone after flushing them
+#
+# - list_flush_ipv6_exception
+# Using the same topology as in pmtu_ipv6, create exceptions, and check
+# they are shown when listing exception caches, gone after flushing them
# Kselftest framework requirement - SKIP code is 4.
@@ -123,39 +131,42 @@ TRACING=0
# Some systems don't have a ping6 binary anymore
which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping)
+# Name Description re-run with nh
tests="
- pmtu_ipv4_exception ipv4: PMTU exceptions
- pmtu_ipv6_exception ipv6: PMTU exceptions
- pmtu_ipv4_vxlan4_exception IPv4 over vxlan4: PMTU exceptions
- pmtu_ipv6_vxlan4_exception IPv6 over vxlan4: PMTU exceptions
- pmtu_ipv4_vxlan6_exception IPv4 over vxlan6: PMTU exceptions
- pmtu_ipv6_vxlan6_exception IPv6 over vxlan6: PMTU exceptions
- pmtu_ipv4_geneve4_exception IPv4 over geneve4: PMTU exceptions
- pmtu_ipv6_geneve4_exception IPv6 over geneve4: PMTU exceptions
- pmtu_ipv4_geneve6_exception IPv4 over geneve6: PMTU exceptions
- pmtu_ipv6_geneve6_exception IPv6 over geneve6: PMTU exceptions
- pmtu_ipv4_fou4_exception IPv4 over fou4: PMTU exceptions
- pmtu_ipv6_fou4_exception IPv6 over fou4: PMTU exceptions
- pmtu_ipv4_fou6_exception IPv4 over fou6: PMTU exceptions
- pmtu_ipv6_fou6_exception IPv6 over fou6: PMTU exceptions
- pmtu_ipv4_gue4_exception IPv4 over gue4: PMTU exceptions
- pmtu_ipv6_gue4_exception IPv6 over gue4: PMTU exceptions
- pmtu_ipv4_gue6_exception IPv4 over gue6: PMTU exceptions
- pmtu_ipv6_gue6_exception IPv6 over gue6: PMTU exceptions
- pmtu_vti6_exception vti6: PMTU exceptions
- pmtu_vti4_exception vti4: PMTU exceptions
- pmtu_vti4_default_mtu vti4: default MTU assignment
- pmtu_vti6_default_mtu vti6: default MTU assignment
- pmtu_vti4_link_add_mtu vti4: MTU setting on link creation
- pmtu_vti6_link_add_mtu vti6: MTU setting on link creation
- pmtu_vti6_link_change_mtu vti6: MTU changes on link changes
- cleanup_ipv4_exception ipv4: cleanup of cached exceptions
- cleanup_ipv6_exception ipv6: cleanup of cached exceptions"
-
-NS_A="ns-$(mktemp -u XXXXXX)"
-NS_B="ns-$(mktemp -u XXXXXX)"
-NS_R1="ns-$(mktemp -u XXXXXX)"
-NS_R2="ns-$(mktemp -u XXXXXX)"
+ pmtu_ipv4_exception ipv4: PMTU exceptions 1
+ pmtu_ipv6_exception ipv6: PMTU exceptions 1
+ pmtu_ipv4_vxlan4_exception IPv4 over vxlan4: PMTU exceptions 1
+ pmtu_ipv6_vxlan4_exception IPv6 over vxlan4: PMTU exceptions 1
+ pmtu_ipv4_vxlan6_exception IPv4 over vxlan6: PMTU exceptions 1
+ pmtu_ipv6_vxlan6_exception IPv6 over vxlan6: PMTU exceptions 1
+ pmtu_ipv4_geneve4_exception IPv4 over geneve4: PMTU exceptions 1
+ pmtu_ipv6_geneve4_exception IPv6 over geneve4: PMTU exceptions 1
+ pmtu_ipv4_geneve6_exception IPv4 over geneve6: PMTU exceptions 1
+ pmtu_ipv6_geneve6_exception IPv6 over geneve6: PMTU exceptions 1
+ pmtu_ipv4_fou4_exception IPv4 over fou4: PMTU exceptions 1
+ pmtu_ipv6_fou4_exception IPv6 over fou4: PMTU exceptions 1
+ pmtu_ipv4_fou6_exception IPv4 over fou6: PMTU exceptions 1
+ pmtu_ipv6_fou6_exception IPv6 over fou6: PMTU exceptions 1
+ pmtu_ipv4_gue4_exception IPv4 over gue4: PMTU exceptions 1
+ pmtu_ipv6_gue4_exception IPv6 over gue4: PMTU exceptions 1
+ pmtu_ipv4_gue6_exception IPv4 over gue6: PMTU exceptions 1
+ pmtu_ipv6_gue6_exception IPv6 over gue6: PMTU exceptions 1
+ pmtu_vti6_exception vti6: PMTU exceptions 0
+ pmtu_vti4_exception vti4: PMTU exceptions 0
+ pmtu_vti4_default_mtu vti4: default MTU assignment 0
+ pmtu_vti6_default_mtu vti6: default MTU assignment 0
+ pmtu_vti4_link_add_mtu vti4: MTU setting on link creation 0
+ pmtu_vti6_link_add_mtu vti6: MTU setting on link creation 0
+ pmtu_vti6_link_change_mtu vti6: MTU changes on link changes 0
+ cleanup_ipv4_exception ipv4: cleanup of cached exceptions 1
+ cleanup_ipv6_exception ipv6: cleanup of cached exceptions 1
+ list_flush_ipv4_exception ipv4: list and flush cached exceptions 1
+ list_flush_ipv6_exception ipv6: list and flush cached exceptions 1"
+
+NS_A="ns-A"
+NS_B="ns-B"
+NS_R1="ns-R1"
+NS_R2="ns-R2"
ns_a="ip netns exec ${NS_A}"
ns_b="ip netns exec ${NS_B}"
ns_r1="ip netns exec ${NS_R1}"
@@ -194,6 +205,30 @@ routes="
B default ${prefix6}:${b_r1}::2
"
+USE_NH="no"
+# ns family nh id destination gateway
+nexthops="
+ A 4 41 ${prefix4}.${a_r1}.2 veth_A-R1
+ A 4 42 ${prefix4}.${a_r2}.2 veth_A-R2
+ B 4 41 ${prefix4}.${b_r1}.2 veth_B-R1
+
+ A 6 61 ${prefix6}:${a_r1}::2 veth_A-R1
+ A 6 62 ${prefix6}:${a_r2}::2 veth_A-R2
+ B 6 61 ${prefix6}:${b_r1}::2 veth_B-R1
+"
+
+# nexthop id correlates to id in nexthops config above
+# ns family prefix nh id
+routes_nh="
+ A 4 default 41
+ A 4 ${prefix4}.${b_r2}.1 42
+ B 4 default 41
+
+ A 6 default 61
+ A 6 ${prefix6}:${b_r2}::1 62
+ B 6 default 61
+"
+
veth4_a_addr="192.168.1.1"
veth4_b_addr="192.168.1.2"
veth4_mask="24"
@@ -208,11 +243,10 @@ tunnel6_a_addr="fd00:2::a"
tunnel6_b_addr="fd00:2::b"
tunnel6_mask="64"
-dummy6_0_addr="fc00:1000::0"
-dummy6_1_addr="fc00:1001::0"
+dummy6_0_prefix="fc00:1000::"
+dummy6_1_prefix="fc00:1001::"
dummy6_mask="64"
-cleanup_done=1
err_buf=
tcpdump_pids=
@@ -430,15 +464,15 @@ setup_xfrm() {
veth_a_addr="${2}"
veth_b_addr="${3}"
- run_cmd "${ns_a} ip -${proto} xfrm state add src ${veth_a_addr} dst ${veth_b_addr} spi 0x1000 proto esp aead 'rfc4106(gcm(aes))' 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel" || return 1
- run_cmd "${ns_a} ip -${proto} xfrm state add src ${veth_b_addr} dst ${veth_a_addr} spi 0x1001 proto esp aead 'rfc4106(gcm(aes))' 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel"
- run_cmd "${ns_a} ip -${proto} xfrm policy add dir out mark 10 tmpl src ${veth_a_addr} dst ${veth_b_addr} proto esp mode tunnel"
- run_cmd "${ns_a} ip -${proto} xfrm policy add dir in mark 10 tmpl src ${veth_b_addr} dst ${veth_a_addr} proto esp mode tunnel"
+ run_cmd ${ns_a} ip -${proto} xfrm state add src ${veth_a_addr} dst ${veth_b_addr} spi 0x1000 proto esp aead 'rfc4106(gcm(aes))' 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel || return 1
+ run_cmd ${ns_a} ip -${proto} xfrm state add src ${veth_b_addr} dst ${veth_a_addr} spi 0x1001 proto esp aead 'rfc4106(gcm(aes))' 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel
+ run_cmd ${ns_a} ip -${proto} xfrm policy add dir out mark 10 tmpl src ${veth_a_addr} dst ${veth_b_addr} proto esp mode tunnel
+ run_cmd ${ns_a} ip -${proto} xfrm policy add dir in mark 10 tmpl src ${veth_b_addr} dst ${veth_a_addr} proto esp mode tunnel
- run_cmd "${ns_b} ip -${proto} xfrm state add src ${veth_a_addr} dst ${veth_b_addr} spi 0x1000 proto esp aead 'rfc4106(gcm(aes))' 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel"
- run_cmd "${ns_b} ip -${proto} xfrm state add src ${veth_b_addr} dst ${veth_a_addr} spi 0x1001 proto esp aead 'rfc4106(gcm(aes))' 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel"
- run_cmd "${ns_b} ip -${proto} xfrm policy add dir out mark 10 tmpl src ${veth_b_addr} dst ${veth_a_addr} proto esp mode tunnel"
- run_cmd "${ns_b} ip -${proto} xfrm policy add dir in mark 10 tmpl src ${veth_a_addr} dst ${veth_b_addr} proto esp mode tunnel"
+ run_cmd ${ns_b} ip -${proto} xfrm state add src ${veth_a_addr} dst ${veth_b_addr} spi 0x1000 proto esp aead 'rfc4106(gcm(aes))' 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel
+ run_cmd ${ns_b} ip -${proto} xfrm state add src ${veth_b_addr} dst ${veth_a_addr} spi 0x1001 proto esp aead 'rfc4106(gcm(aes))' 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel
+ run_cmd ${ns_b} ip -${proto} xfrm policy add dir out mark 10 tmpl src ${veth_b_addr} dst ${veth_a_addr} proto esp mode tunnel
+ run_cmd ${ns_b} ip -${proto} xfrm policy add dir in mark 10 tmpl src ${veth_a_addr} dst ${veth_b_addr} proto esp mode tunnel
}
setup_xfrm4() {
@@ -449,6 +483,50 @@ setup_xfrm6() {
setup_xfrm 6 ${veth6_a_addr} ${veth6_b_addr}
}
+setup_routing_old() {
+ for i in ${routes}; do
+ [ "${ns}" = "" ] && ns="${i}" && continue
+ [ "${addr}" = "" ] && addr="${i}" && continue
+ [ "${gw}" = "" ] && gw="${i}"
+
+ ns_name="$(nsname ${ns})"
+
+ ip -n ${ns_name} route add ${addr} via ${gw}
+
+ ns=""; addr=""; gw=""
+ done
+}
+
+setup_routing_new() {
+ for i in ${nexthops}; do
+ [ "${ns}" = "" ] && ns="${i}" && continue
+ [ "${fam}" = "" ] && fam="${i}" && continue
+ [ "${nhid}" = "" ] && nhid="${i}" && continue
+ [ "${gw}" = "" ] && gw="${i}" && continue
+ [ "${dev}" = "" ] && dev="${i}"
+
+ ns_name="$(nsname ${ns})"
+
+ ip -n ${ns_name} -${fam} nexthop add id ${nhid} via ${gw} dev ${dev}
+
+ ns=""; fam=""; nhid=""; gw=""; dev=""
+
+ done
+
+ for i in ${routes_nh}; do
+ [ "${ns}" = "" ] && ns="${i}" && continue
+ [ "${fam}" = "" ] && fam="${i}" && continue
+ [ "${addr}" = "" ] && addr="${i}" && continue
+ [ "${nhid}" = "" ] && nhid="${i}"
+
+ ns_name="$(nsname ${ns})"
+
+ ip -n ${ns_name} -${fam} route add ${addr} nhid ${nhid}
+
+ ns=""; fam=""; addr=""; nhid=""
+ done
+}
+
setup_routing() {
for i in ${NS_R1} ${NS_R2}; do
ip netns exec ${i} sysctl -q net/ipv4/ip_forward=1
@@ -479,23 +557,19 @@ setup_routing() {
ns=""; peer=""; segment=""
done
- for i in ${routes}; do
- [ "${ns}" = "" ] && ns="${i}" && continue
- [ "${addr}" = "" ] && addr="${i}" && continue
- [ "${gw}" = "" ] && gw="${i}"
-
- ns_name="$(nsname ${ns})"
-
- ip -n ${ns_name} route add ${addr} via ${gw}
+ if [ "$USE_NH" = "yes" ]; then
+ setup_routing_new
+ else
+ setup_routing_old
+ fi
- ns=""; addr=""; gw=""
- done
+ return 0
}
setup() {
[ "$(id -u)" -ne 0 ] && echo " need to run as root" && return $ksft_skip
- cleanup_done=0
+ cleanup
for arg do
eval setup_${arg} || { echo " ${arg} not supported"; return 1; }
done
@@ -519,11 +593,9 @@ cleanup() {
done
tcpdump_pids=
- [ ${cleanup_done} -eq 1 ] && return
for n in ${NS_A} ${NS_B} ${NS_R1} ${NS_R2}; do
ip netns del ${n} 2> /dev/null
done
- cleanup_done=1
}
mtu() {
@@ -1005,13 +1077,13 @@ test_pmtu_vti6_link_change_mtu() {
run_cmd ${ns_a} ip link set dummy0 up
run_cmd ${ns_a} ip link set dummy1 up
- run_cmd ${ns_a} ip addr add ${dummy6_0_addr}/${dummy6_mask} dev dummy0
- run_cmd ${ns_a} ip addr add ${dummy6_1_addr}/${dummy6_mask} dev dummy1
+ run_cmd ${ns_a} ip addr add ${dummy6_0_prefix}1/${dummy6_mask} dev dummy0
+ run_cmd ${ns_a} ip addr add ${dummy6_1_prefix}1/${dummy6_mask} dev dummy1
fail=0
# Create vti6 interface bound to device, passing MTU, check it
- run_cmd ${ns_a} ip link add vti6_a mtu 1300 type vti6 remote ${dummy6_0_addr} local ${dummy6_0_addr}
+ run_cmd ${ns_a} ip link add vti6_a mtu 1300 type vti6 remote ${dummy6_0_prefix}2 local ${dummy6_0_prefix}1
mtu="$(link_get_mtu "${ns_a}" vti6_a)"
if [ ${mtu} -ne 1300 ]; then
err " vti6 MTU ${mtu} doesn't match configured value 1300"
@@ -1020,7 +1092,7 @@ test_pmtu_vti6_link_change_mtu() {
# Move to another device with different MTU, without passing MTU, check
# MTU is adjusted
- run_cmd ${ns_a} ip link set vti6_a type vti6 remote ${dummy6_1_addr} local ${dummy6_1_addr}
+ run_cmd ${ns_a} ip link set vti6_a type vti6 remote ${dummy6_1_prefix}2 local ${dummy6_1_prefix}1
mtu="$(link_get_mtu "${ns_a}" vti6_a)"
if [ ${mtu} -ne $((3000 - 40)) ]; then
err " vti MTU ${mtu} is not dummy MTU 3000 minus IPv6 header length"
@@ -1028,7 +1100,7 @@ test_pmtu_vti6_link_change_mtu() {
fi
# Move it back, passing MTU, check MTU is not overridden
- run_cmd ${ns_a} ip link set vti6_a mtu 1280 type vti6 remote ${dummy6_0_addr} local ${dummy6_0_addr}
+ run_cmd ${ns_a} ip link set vti6_a mtu 1280 type vti6 remote ${dummy6_0_prefix}2 local ${dummy6_0_prefix}1
mtu="$(link_get_mtu "${ns_a}" vti6_a)"
if [ ${mtu} -ne 1280 ]; then
err " vti6 MTU ${mtu} doesn't match configured value 1280"
@@ -1093,6 +1165,158 @@ test_cleanup_ipv4_exception() {
test_cleanup_vxlanX_exception 4
}
+run_test() {
+ (
+ tname="$1"
+ tdesc="$2"
+
+ unset IFS
+
+ if [ "$VERBOSE" = "1" ]; then
+ printf "\n##########################################################################\n\n"
+ fi
+
+ eval test_${tname}
+ ret=$?
+
+ if [ $ret -eq 0 ]; then
+ printf "TEST: %-60s [ OK ]\n" "${tdesc}"
+ elif [ $ret -eq 1 ]; then
+ printf "TEST: %-60s [FAIL]\n" "${tdesc}"
+ if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
+ echo
+ echo "Pausing. Hit enter to continue"
+ read a
+ fi
+ err_flush
+ exit 1
+ elif [ $ret -eq 2 ]; then
+ printf "TEST: %-60s [SKIP]\n" "${tdesc}"
+ err_flush
+ fi
+
+ return $ret
+ )
+ ret=$?
+ [ $ret -ne 0 ] && exitcode=1
+
+ return $ret
+}
+
+run_test_nh() {
+ tname="$1"
+ tdesc="$2"
+
+ USE_NH=yes
+ run_test "${tname}" "${tdesc} - nexthop objects"
+ USE_NH=no
+}
+
+test_list_flush_ipv4_exception() {
+ setup namespaces routing || return 2
+ trace "${ns_a}" veth_A-R1 "${ns_r1}" veth_R1-A \
+ "${ns_r1}" veth_R1-B "${ns_b}" veth_B-R1 \
+ "${ns_a}" veth_A-R2 "${ns_r2}" veth_R2-A \
+ "${ns_r2}" veth_R2-B "${ns_b}" veth_B-R2
+
+ dst_prefix1="${prefix4}.${b_r1}."
+ dst2="${prefix4}.${b_r2}.1"
+
+ # Set up initial MTU values
+ mtu "${ns_a}" veth_A-R1 2000
+ mtu "${ns_r1}" veth_R1-A 2000
+ mtu "${ns_r1}" veth_R1-B 1500
+ mtu "${ns_b}" veth_B-R1 1500
+
+ mtu "${ns_a}" veth_A-R2 2000
+ mtu "${ns_r2}" veth_R2-A 2000
+ mtu "${ns_r2}" veth_R2-B 1500
+ mtu "${ns_b}" veth_B-R2 1500
+
+ fail=0
+
+ # Add 100 addresses for veth endpoint on B reached by default A route
+ for i in $(seq 100 199); do
+ run_cmd ${ns_b} ip addr add "${dst_prefix1}${i}" dev veth_B-R1
+ done
+
+ # Create 100 cached route exceptions for path via R1, one via R2. Note
+ # that with IPv4 we need to actually cause a route lookup that matches
+ # the exception caused by ICMP, in order to actually have a cached
+ # route, so we need to ping each destination twice
+ for i in $(seq 100 199); do
+ run_cmd ${ns_a} ping -q -M want -i 0.1 -c 2 -s 1800 "${dst_prefix1}${i}"
+ done
+ run_cmd ${ns_a} ping -q -M want -i 0.1 -c 2 -s 1800 "${dst2}"
+
+ # Each exception is printed as two lines
+ if [ "$(${ns_a} ip route list cache | wc -l)" -ne 202 ]; then
+ err " can't list cached exceptions"
+ fail=1
+ fi
+
+ run_cmd ${ns_a} ip route flush cache
+ pmtu1="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst_prefix}1)"
+ pmtu2="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst_prefix}2)"
+ if [ -n "${pmtu1}" ] || [ -n "${pmtu2}" ] || \
+ [ -n "$(${ns_a} ip route list cache)" ]; then
+ err " can't flush cached exceptions"
+ fail=1
+ fi
+
+ return ${fail}
+}
+
+test_list_flush_ipv6_exception() {
+ setup namespaces routing || return 2
+ trace "${ns_a}" veth_A-R1 "${ns_r1}" veth_R1-A \
+ "${ns_r1}" veth_R1-B "${ns_b}" veth_B-R1 \
+ "${ns_a}" veth_A-R2 "${ns_r2}" veth_R2-A \
+ "${ns_r2}" veth_R2-B "${ns_b}" veth_B-R2
+
+ dst_prefix1="${prefix6}:${b_r1}::"
+ dst2="${prefix6}:${b_r2}::1"
+
+ # Set up initial MTU values
+ mtu "${ns_a}" veth_A-R1 2000
+ mtu "${ns_r1}" veth_R1-A 2000
+ mtu "${ns_r1}" veth_R1-B 1500
+ mtu "${ns_b}" veth_B-R1 1500
+
+ mtu "${ns_a}" veth_A-R2 2000
+ mtu "${ns_r2}" veth_R2-A 2000
+ mtu "${ns_r2}" veth_R2-B 1500
+ mtu "${ns_b}" veth_B-R2 1500
+
+ fail=0
+
+ # Add 100 addresses for veth endpoint on B reached by default A route
+ for i in $(seq 100 199); do
+ run_cmd ${ns_b} ip addr add "${dst_prefix1}${i}" dev veth_B-R1
+ done
+
+ # Create 100 cached route exceptions for path via R1, one via R2
+ for i in $(seq 100 199); do
+ run_cmd ${ns_a} ping -q -M want -i 0.1 -w 1 -s 1800 "${dst_prefix1}${i}"
+ done
+ run_cmd ${ns_a} ping -q -M want -i 0.1 -w 1 -s 1800 "${dst2}"
+ if [ "$(${ns_a} ip -6 route list cache | wc -l)" -ne 101 ]; then
+ err " can't list cached exceptions"
+ fail=1
+ fi
+
+ run_cmd ${ns_a} ip -6 route flush cache
+ pmtu1="$(route_get_dst_pmtu_from_exception "${ns_a}" "${dst_prefix1}100")"
+ pmtu2="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst2})"
+ if [ -n "${pmtu1}" ] || [ -n "${pmtu2}" ] || \
+ [ -n "$(${ns_a} ip -6 route list cache)" ]; then
+ err " can't flush cached exceptions"
+ fail=1
+ fi
+
+ return ${fail}
+}
+
usage() {
echo
echo "$0 [OPTIONS] [TEST]..."
@@ -1136,8 +1360,23 @@ done
trap cleanup EXIT
+# start clean
+cleanup
+
+HAVE_NH=no
+ip nexthop ls >/dev/null 2>&1
+[ $? -eq 0 ] && HAVE_NH=yes
+
+name=""
+desc=""
+rerun_nh=0
for t in ${tests}; do
- [ $desc -eq 0 ] && name="${t}" && desc=1 && continue || desc=0
+ [ "${name}" = "" ] && name="${t}" && continue
+ [ "${desc}" = "" ] && desc="${t}" && continue
+
+ if [ "${HAVE_NH}" = "yes" ]; then
+ rerun_nh="${t}"
+ fi
run_this=1
for arg do
@@ -1145,36 +1384,18 @@ for t in ${tests}; do
[ "${arg}" = "${name}" ] && run_this=1 && break
run_this=0
done
- [ $run_this -eq 0 ] && continue
-
- (
- unset IFS
+ if [ $run_this -eq 1 ]; then
+ run_test "${name}" "${desc}"
+ # if test was skipped no need to retry with nexthop objects
+ [ $? -eq 2 ] && rerun_nh=0
- if [ "$VERBOSE" = "1" ]; then
- printf "\n##########################################################################\n\n"
+ if [ "${rerun_nh}" = "1" ]; then
+ run_test_nh "${name}" "${desc}"
fi
-
- eval test_${name}
- ret=$?
- cleanup
-
- if [ $ret -eq 0 ]; then
- printf "TEST: %-60s [ OK ]\n" "${t}"
- elif [ $ret -eq 1 ]; then
- printf "TEST: %-60s [FAIL]\n" "${t}"
- if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
- echo
- echo "Pausing. Hit enter to continue"
- read a
- fi
- err_flush
- exit 1
- elif [ $ret -eq 2 ]; then
- printf "TEST: %-60s [SKIP]\n" "${t}"
- err_flush
- fi
- )
- [ $? -ne 0 ] && exitcode=1
+ fi
+ name=""
+ desc=""
+ rerun_nh=0
done
exit ${exitcode}
diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c
index bd9b9632c72b..8c8c7d79c38d 100644
--- a/tools/testing/selftests/net/psock_fanout.c
+++ b/tools/testing/selftests/net/psock_fanout.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2013 Google Inc.
* Author: Willem de Bruijn (willemb@google.com)
@@ -24,21 +25,6 @@
*
* Todo:
* - functionality: PACKET_FANOUT_FLAG_DEFRAG
- *
- * License (GPLv2):
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _GNU_SOURCE /* for sched_setaffinity */
diff --git a/tools/testing/selftests/net/psock_lib.h b/tools/testing/selftests/net/psock_lib.h
index 7d990d6c861b..faa884385c45 100644
--- a/tools/testing/selftests/net/psock_lib.h
+++ b/tools/testing/selftests/net/psock_lib.h
@@ -1,22 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2013 Google Inc.
* Author: Willem de Bruijn <willemb@google.com>
* Daniel Borkmann <dborkman@redhat.com>
- *
- * License (GPLv2):
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef PSOCK_LIB_H
diff --git a/tools/testing/selftests/net/psock_tpacket.c b/tools/testing/selftests/net/psock_tpacket.c
index 7ec4fa4d55dc..404a2ce759ab 100644
--- a/tools/testing/selftests/net/psock_tpacket.c
+++ b/tools/testing/selftests/net/psock_tpacket.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2013 Red Hat, Inc.
* Author: Daniel Borkmann <dborkman@redhat.com>
@@ -19,21 +20,6 @@
* - TPACKET_V1: RX_RING, TX_RING
* - TPACKET_V2: RX_RING, TX_RING
* - TPACKET_V3: RX_RING
- *
- * License (GPLv2):
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
diff --git a/tools/testing/selftests/net/route_localnet.sh b/tools/testing/selftests/net/route_localnet.sh
new file mode 100755
index 000000000000..116bfeab72fa
--- /dev/null
+++ b/tools/testing/selftests/net/route_localnet.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Run a couple of tests when route_localnet = 1.
+
+readonly PEER_NS="ns-peer-$(mktemp -u XXXXXX)"
+
+setup() {
+ ip netns add "${PEER_NS}"
+ ip -netns "${PEER_NS}" link set dev lo up
+ ip link add name veth0 type veth peer name veth1
+ ip link set dev veth0 up
+ ip link set dev veth1 netns "${PEER_NS}"
+
+ # Enable route_localnet and delete useless route 127.0.0.0/8.
+ sysctl -w net.ipv4.conf.veth0.route_localnet=1
+ ip netns exec "${PEER_NS}" sysctl -w net.ipv4.conf.veth1.route_localnet=1
+ ip route del 127.0.0.0/8 dev lo table local
+ ip netns exec "${PEER_NS}" ip route del 127.0.0.0/8 dev lo table local
+
+ ifconfig veth0 127.25.3.4/24 up
+ ip netns exec "${PEER_NS}" ifconfig veth1 127.25.3.14/24 up
+
+ ip route flush cache
+ ip netns exec "${PEER_NS}" ip route flush cache
+}
+
+cleanup() {
+ ip link del veth0
+ ip route add local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
+ local -r ns="$(ip netns list|grep $PEER_NS)"
+ [ -n "$ns" ] && ip netns del $ns 2>/dev/null
+}
+
+# Run test when arp_announce = 2.
+run_arp_announce_test() {
+ echo "run arp_announce test"
+ setup
+
+ sysctl -w net.ipv4.conf.veth0.arp_announce=2
+ ip netns exec "${PEER_NS}" sysctl -w net.ipv4.conf.veth1.arp_announce=2
+ ping -c5 -I veth0 127.25.3.14
+ if [ $? -ne 0 ];then
+ echo "failed"
+ else
+ echo "ok"
+ fi
+
+ cleanup
+}
+
+# Run test when arp_ignore = 3.
+run_arp_ignore_test() {
+ echo "run arp_ignore test"
+ setup
+
+ sysctl -w net.ipv4.conf.veth0.arp_ignore=3
+ ip netns exec "${PEER_NS}" sysctl -w net.ipv4.conf.veth1.arp_ignore=3
+ ping -c5 -I veth0 127.25.3.14
+ if [ $? -ne 0 ];then
+ echo "failed"
+ else
+ echo "ok"
+ fi
+
+ cleanup
+}
+
+run_all_tests() {
+ run_arp_announce_test
+ run_arp_ignore_test
+}
+
+run_all_tests
diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh
index b25c9fe019d2..bdbf4b3125b6 100755
--- a/tools/testing/selftests/net/rtnetlink.sh
+++ b/tools/testing/selftests/net/rtnetlink.sh
@@ -249,6 +249,45 @@ kci_test_route_get()
echo "PASS: route get"
}
+kci_test_addrlft()
+{
+ for i in $(seq 10 100) ;do
+ lft=$(((RANDOM%3) + 1))
+ ip addr add 10.23.11.$i/32 dev "$devdummy" preferred_lft $lft valid_lft $((lft+1))
+ check_err $?
+ done
+
+ sleep 5
+
+ ip addr show dev "$devdummy" | grep "10.23.11."
+ if [ $? -eq 0 ]; then
+ echo "FAIL: preferred_lft addresses remaining"
+ check_err 1
+ return
+ fi
+
+ echo "PASS: preferred_lft addresses have expired"
+}
+
+kci_test_promote_secondaries()
+{
+ promote=$(sysctl -n net.ipv4.conf.$devdummy.promote_secondaries)
+
+ sysctl -q net.ipv4.conf.$devdummy.promote_secondaries=1
+
+ for i in $(seq 2 254);do
+ IP="10.23.11.$i"
+ ip -f inet addr add $IP/16 brd + dev "$devdummy"
+ ifconfig "$devdummy" $IP netmask 255.255.0.0
+ done
+
+ ip addr flush dev "$devdummy"
+
+ [ $promote -eq 0 ] && sysctl -q net.ipv4.conf.$devdummy.promote_secondaries=0
+
+ echo "PASS: promote_secondaries complete"
+}
+
kci_test_addrlabel()
{
ret=0
@@ -699,13 +738,17 @@ kci_test_ipsec_offload()
sysfsd=/sys/kernel/debug/netdevsim/netdevsim0/ports/0/
sysfsf=$sysfsd/ipsec
sysfsnet=/sys/bus/netdevsim/devices/netdevsim0/net/
+ probed=false
# setup netdevsim since dummydev doesn't have offload support
- modprobe netdevsim
- check_err $?
- if [ $ret -ne 0 ]; then
- echo "FAIL: ipsec_offload can't load netdevsim"
- return 1
+ if [ ! -w /sys/bus/netdevsim/new_device ] ; then
+ modprobe -q netdevsim
+ check_err $?
+ if [ $ret -ne 0 ]; then
+ echo "SKIP: ipsec_offload can't load netdevsim"
+ return $ksft_skip
+ fi
+ probed=true
fi
echo "0" > /sys/bus/netdevsim/new_device
@@ -785,7 +828,7 @@ EOF
fi
# clean up any leftovers
- rmmod netdevsim
+ $probed && rmmod netdevsim
if [ $ret -ne 0 ]; then
echo "FAIL: ipsec_offload"
@@ -1140,6 +1183,8 @@ kci_test_rtnl()
kci_test_polrouting
kci_test_route_get
+ kci_test_addrlft
+ kci_test_promote_secondaries
kci_test_tc
kci_test_gre
kci_test_gretap
diff --git a/tools/testing/selftests/net/run_afpackettests b/tools/testing/selftests/net/run_afpackettests
index ea5938ec009a..8b42e8b04e0f 100755
--- a/tools/testing/selftests/net/run_afpackettests
+++ b/tools/testing/selftests/net/run_afpackettests
@@ -21,12 +21,16 @@ fi
echo "--------------------"
echo "running psock_tpacket test"
echo "--------------------"
-./in_netns.sh ./psock_tpacket
-if [ $? -ne 0 ]; then
- echo "[FAIL]"
- ret=1
+if [ -f /proc/kallsyms ]; then
+ ./in_netns.sh ./psock_tpacket
+ if [ $? -ne 0 ]; then
+ echo "[FAIL]"
+ ret=1
+ else
+ echo "[PASS]"
+ fi
else
- echo "[PASS]"
+ echo "[SKIP] CONFIG_KALLSYMS not enabled"
fi
echo "--------------------"
diff --git a/tools/testing/selftests/net/so_txtime.c b/tools/testing/selftests/net/so_txtime.c
new file mode 100644
index 000000000000..53f598f06647
--- /dev/null
+++ b/tools/testing/selftests/net/so_txtime.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test the SO_TXTIME API
+ *
+ * Takes two streams of { payload, delivery time }[], one input and one output.
+ * Sends the input stream and verifies arrival matches the output stream.
+ * The two streams can differ due to out-of-order delivery and drops.
+ */
+
+#define _GNU_SOURCE
+
+#include <arpa/inet.h>
+#include <error.h>
+#include <errno.h>
+#include <linux/net_tstamp.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+static int cfg_clockid = CLOCK_TAI;
+static bool cfg_do_ipv4;
+static bool cfg_do_ipv6;
+static uint16_t cfg_port = 8000;
+static int cfg_variance_us = 2000;
+
+static uint64_t glob_tstart;
+
+/* encode one timed transmission (of a 1B payload) */
+struct timed_send {
+ char data;
+ int64_t delay_us;
+};
+
+#define MAX_NUM_PKT 8
+static struct timed_send cfg_in[MAX_NUM_PKT];
+static struct timed_send cfg_out[MAX_NUM_PKT];
+static int cfg_num_pkt;
+
+static uint64_t gettime_ns(void)
+{
+ struct timespec ts;
+
+ if (clock_gettime(cfg_clockid, &ts))
+ error(1, errno, "gettime");
+
+ return ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec;
+}
+
+static void do_send_one(int fdt, struct timed_send *ts)
+{
+ char control[CMSG_SPACE(sizeof(uint64_t))];
+ struct msghdr msg = {0};
+ struct iovec iov = {0};
+ struct cmsghdr *cm;
+ uint64_t tdeliver;
+ int ret;
+
+ iov.iov_base = &ts->data;
+ iov.iov_len = 1;
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ if (ts->delay_us >= 0) {
+ memset(control, 0, sizeof(control));
+ msg.msg_control = &control;
+ msg.msg_controllen = sizeof(control);
+
+ tdeliver = glob_tstart + ts->delay_us * 1000;
+
+ cm = CMSG_FIRSTHDR(&msg);
+ cm->cmsg_level = SOL_SOCKET;
+ cm->cmsg_type = SCM_TXTIME;
+ cm->cmsg_len = CMSG_LEN(sizeof(tdeliver));
+ memcpy(CMSG_DATA(cm), &tdeliver, sizeof(tdeliver));
+ }
+
+ ret = sendmsg(fdt, &msg, 0);
+ if (ret == -1)
+ error(1, errno, "write");
+ if (ret == 0)
+ error(1, 0, "write: 0B");
+
+}
+
+static void do_recv_one(int fdr, struct timed_send *ts)
+{
+ int64_t tstop, texpect;
+ char rbuf[2];
+ int ret;
+
+ ret = recv(fdr, rbuf, sizeof(rbuf), 0);
+ if (ret == -1)
+ error(1, errno, "read");
+ if (ret != 1)
+ error(1, 0, "read: %dB", ret);
+
+ tstop = (gettime_ns() - glob_tstart) / 1000;
+ texpect = ts->delay_us >= 0 ? ts->delay_us : 0;
+
+ fprintf(stderr, "payload:%c delay:%ld expected:%ld (us)\n",
+ rbuf[0], tstop, texpect);
+
+ if (rbuf[0] != ts->data)
+ error(1, 0, "payload mismatch. expected %c", ts->data);
+
+ if (labs(tstop - texpect) > cfg_variance_us)
+ error(1, 0, "exceeds variance (%d us)", cfg_variance_us);
+}
+
+static void do_recv_verify_empty(int fdr)
+{
+ char rbuf[1];
+ int ret;
+
+ ret = recv(fdr, rbuf, sizeof(rbuf), 0);
+ if (ret != -1 || errno != EAGAIN)
+ error(1, 0, "recv: not empty as expected (%d, %d)", ret, errno);
+}
+
+static void setsockopt_txtime(int fd)
+{
+ struct sock_txtime so_txtime_val = { .clockid = cfg_clockid };
+ struct sock_txtime so_txtime_val_read = { 0 };
+ socklen_t vallen = sizeof(so_txtime_val);
+
+ if (setsockopt(fd, SOL_SOCKET, SO_TXTIME,
+ &so_txtime_val, sizeof(so_txtime_val)))
+ error(1, errno, "setsockopt txtime");
+
+ if (getsockopt(fd, SOL_SOCKET, SO_TXTIME,
+ &so_txtime_val_read, &vallen))
+ error(1, errno, "getsockopt txtime");
+
+ if (vallen != sizeof(so_txtime_val) ||
+ memcmp(&so_txtime_val, &so_txtime_val_read, vallen))
+ error(1, 0, "getsockopt txtime: mismatch");
+}
+
+static int setup_tx(struct sockaddr *addr, socklen_t alen)
+{
+ int fd;
+
+ fd = socket(addr->sa_family, SOCK_DGRAM, 0);
+ if (fd == -1)
+ error(1, errno, "socket t");
+
+ if (connect(fd, addr, alen))
+ error(1, errno, "connect");
+
+ setsockopt_txtime(fd);
+
+ return fd;
+}
+
+static int setup_rx(struct sockaddr *addr, socklen_t alen)
+{
+ struct timeval tv = { .tv_usec = 100 * 1000 };
+ int fd;
+
+ fd = socket(addr->sa_family, SOCK_DGRAM, 0);
+ if (fd == -1)
+ error(1, errno, "socket r");
+
+ if (bind(fd, addr, alen))
+ error(1, errno, "bind");
+
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))
+ error(1, errno, "setsockopt rcv timeout");
+
+ return fd;
+}
+
+static void do_test(struct sockaddr *addr, socklen_t alen)
+{
+ int fdt, fdr, i;
+
+ fprintf(stderr, "\nSO_TXTIME ipv%c clock %s\n",
+ addr->sa_family == PF_INET ? '4' : '6',
+ cfg_clockid == CLOCK_TAI ? "tai" : "monotonic");
+
+ fdt = setup_tx(addr, alen);
+ fdr = setup_rx(addr, alen);
+
+ glob_tstart = gettime_ns();
+
+ for (i = 0; i < cfg_num_pkt; i++)
+ do_send_one(fdt, &cfg_in[i]);
+ for (i = 0; i < cfg_num_pkt; i++)
+ do_recv_one(fdr, &cfg_out[i]);
+
+ do_recv_verify_empty(fdr);
+
+ if (close(fdr))
+ error(1, errno, "close r");
+ if (close(fdt))
+ error(1, errno, "close t");
+}
+
+static int parse_io(const char *optarg, struct timed_send *array)
+{
+ char *arg, *tok;
+ int aoff = 0;
+
+ arg = strdup(optarg);
+ if (!arg)
+ error(1, errno, "strdup");
+
+ while ((tok = strtok(arg, ","))) {
+ arg = NULL; /* only pass non-zero on first call */
+
+ if (aoff / 2 == MAX_NUM_PKT)
+ error(1, 0, "exceeds max pkt count (%d)", MAX_NUM_PKT);
+
+ if (aoff & 1) { /* parse delay */
+ array->delay_us = strtol(tok, NULL, 0) * 1000;
+ array++;
+ } else { /* parse character */
+ array->data = tok[0];
+ }
+
+ aoff++;
+ }
+
+ free(arg);
+
+ return aoff / 2;
+}
+
+static void parse_opts(int argc, char **argv)
+{
+ int c, ilen, olen;
+
+ while ((c = getopt(argc, argv, "46c:")) != -1) {
+ switch (c) {
+ case '4':
+ cfg_do_ipv4 = true;
+ break;
+ case '6':
+ cfg_do_ipv6 = true;
+ break;
+ case 'c':
+ if (!strcmp(optarg, "tai"))
+ cfg_clockid = CLOCK_TAI;
+ else if (!strcmp(optarg, "monotonic") ||
+ !strcmp(optarg, "mono"))
+ cfg_clockid = CLOCK_MONOTONIC;
+ else
+ error(1, 0, "unknown clock id %s", optarg);
+ break;
+ default:
+ error(1, 0, "parse error at %d", optind);
+ }
+ }
+
+ if (argc - optind != 2)
+ error(1, 0, "Usage: %s [-46] -c <clock> <in> <out>", argv[0]);
+
+ ilen = parse_io(argv[optind], cfg_in);
+ olen = parse_io(argv[optind + 1], cfg_out);
+ if (ilen != olen)
+ error(1, 0, "i/o streams len mismatch (%d, %d)\n", ilen, olen);
+ cfg_num_pkt = ilen;
+}
+
+int main(int argc, char **argv)
+{
+ parse_opts(argc, argv);
+
+ if (cfg_do_ipv6) {
+ struct sockaddr_in6 addr6 = {0};
+
+ addr6.sin6_family = AF_INET6;
+ addr6.sin6_port = htons(cfg_port);
+ addr6.sin6_addr = in6addr_loopback;
+ do_test((void *)&addr6, sizeof(addr6));
+ }
+
+ if (cfg_do_ipv4) {
+ struct sockaddr_in addr4 = {0};
+
+ addr4.sin_family = AF_INET;
+ addr4.sin_port = htons(cfg_port);
+ addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ do_test((void *)&addr4, sizeof(addr4));
+ }
+
+ return 0;
+}
diff --git a/tools/testing/selftests/net/so_txtime.sh b/tools/testing/selftests/net/so_txtime.sh
new file mode 100755
index 000000000000..5aa519328a5b
--- /dev/null
+++ b/tools/testing/selftests/net/so_txtime.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Regression tests for the SO_TXTIME interface
+
+# Run in network namespace
+if [[ $# -eq 0 ]]; then
+ ./in_netns.sh $0 __subprocess
+ exit $?
+fi
+
+set -e
+
+tc qdisc add dev lo root fq
+./so_txtime -4 -6 -c mono a,-1 a,-1
+./so_txtime -4 -6 -c mono a,0 a,0
+./so_txtime -4 -6 -c mono a,10 a,10
+./so_txtime -4 -6 -c mono a,10,b,20 a,10,b,20
+./so_txtime -4 -6 -c mono a,20,b,10 b,20,a,20
+
+if tc qdisc replace dev lo root etf clockid CLOCK_TAI delta 200000; then
+ ! ./so_txtime -4 -6 -c tai a,-1 a,-1
+ ! ./so_txtime -4 -6 -c tai a,0 a,0
+ ./so_txtime -4 -6 -c tai a,10 a,10
+ ./so_txtime -4 -6 -c tai a,10,b,20 a,10,b,20
+ ./so_txtime -4 -6 -c tai a,20,b,10 b,10,a,20
+else
+ echo "tc ($(tc -V)) does not support qdisc etf. skipping"
+fi
+
+echo OK. All tests passed
diff --git a/tools/testing/selftests/net/tcp_fastopen_backup_key.c b/tools/testing/selftests/net/tcp_fastopen_backup_key.c
new file mode 100644
index 000000000000..9c55ec44fc43
--- /dev/null
+++ b/tools/testing/selftests/net/tcp_fastopen_backup_key.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Test key rotation for TFO.
+ * New keys are 'rotated' in two steps:
+ * 1) Add new key as the 'backup' key 'behind' the primary key
+ * 2) Make new key the primary by swapping the backup and primary keys
+ *
+ * The rotation is done in stages using multiple sockets bound
+ * to the same port via SO_REUSEPORT. This simulates key rotation
+ * behind say a load balancer. We verify that across the rotation
+ * there are no cases in which a cookie is not accepted by verifying
+ * that TcpExtTCPFastOpenPassiveFail remains 0.
+ */
+#define _GNU_SOURCE
+#include <arpa/inet.h>
+#include <errno.h>
+#include <error.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <unistd.h>
+#include <netinet/tcp.h>
+#include <fcntl.h>
+#include <time.h>
+
+#ifndef TCP_FASTOPEN_KEY
+#define TCP_FASTOPEN_KEY 33
+#endif
+
+#define N_LISTEN 10
+#define PROC_FASTOPEN_KEY "/proc/sys/net/ipv4/tcp_fastopen_key"
+#define KEY_LENGTH 16
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#endif
+
+static bool do_ipv6;
+static bool do_sockopt;
+static bool do_rotate;
+static int key_len = KEY_LENGTH;
+static int rcv_fds[N_LISTEN];
+static int proc_fd;
+static const char *IP4_ADDR = "127.0.0.1";
+static const char *IP6_ADDR = "::1";
+static const int PORT = 8891;
+
+static void get_keys(int fd, uint32_t *keys)
+{
+ char buf[128];
+ socklen_t len = KEY_LENGTH * 2;
+
+ if (do_sockopt) {
+ if (getsockopt(fd, SOL_TCP, TCP_FASTOPEN_KEY, keys, &len))
+ error(1, errno, "Unable to get key");
+ return;
+ }
+ lseek(proc_fd, 0, SEEK_SET);
+ if (read(proc_fd, buf, sizeof(buf)) <= 0)
+ error(1, errno, "Unable to read %s", PROC_FASTOPEN_KEY);
+ if (sscanf(buf, "%x-%x-%x-%x,%x-%x-%x-%x", keys, keys + 1, keys + 2,
+ keys + 3, keys + 4, keys + 5, keys + 6, keys + 7) != 8)
+ error(1, 0, "Unable to parse %s", PROC_FASTOPEN_KEY);
+}
+
+static void set_keys(int fd, uint32_t *keys)
+{
+ char buf[128];
+
+ if (do_sockopt) {
+ if (setsockopt(fd, SOL_TCP, TCP_FASTOPEN_KEY, keys,
+ key_len))
+ error(1, errno, "Unable to set key");
+ return;
+ }
+ if (do_rotate)
+ snprintf(buf, 128, "%08x-%08x-%08x-%08x,%08x-%08x-%08x-%08x",
+ keys[0], keys[1], keys[2], keys[3], keys[4], keys[5],
+ keys[6], keys[7]);
+ else
+ snprintf(buf, 128, "%08x-%08x-%08x-%08x",
+ keys[0], keys[1], keys[2], keys[3]);
+ lseek(proc_fd, 0, SEEK_SET);
+ if (write(proc_fd, buf, sizeof(buf)) <= 0)
+ error(1, errno, "Unable to write %s", PROC_FASTOPEN_KEY);
+}
+
+static void build_rcv_fd(int family, int proto, int *rcv_fds)
+{
+ struct sockaddr_in addr4 = {0};
+ struct sockaddr_in6 addr6 = {0};
+ struct sockaddr *addr;
+ int opt = 1, i, sz;
+ int qlen = 100;
+ uint32_t keys[8];
+
+ switch (family) {
+ case AF_INET:
+ addr4.sin_family = family;
+ addr4.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr4.sin_port = htons(PORT);
+ sz = sizeof(addr4);
+ addr = (struct sockaddr *)&addr4;
+ break;
+ case AF_INET6:
+ addr6.sin6_family = AF_INET6;
+ addr6.sin6_addr = in6addr_any;
+ addr6.sin6_port = htons(PORT);
+ sz = sizeof(addr6);
+ addr = (struct sockaddr *)&addr6;
+ break;
+ default:
+ error(1, 0, "Unsupported family %d", family);
+ /* clang does not recognize error() above as terminating
+ * the program, so it complains that saddr, sz are
+ * not initialized when this code path is taken. Silence it.
+ */
+ return;
+ }
+ for (i = 0; i < ARRAY_SIZE(keys); i++)
+ keys[i] = rand();
+ for (i = 0; i < N_LISTEN; i++) {
+ rcv_fds[i] = socket(family, proto, 0);
+ if (rcv_fds[i] < 0)
+ error(1, errno, "failed to create receive socket");
+ if (setsockopt(rcv_fds[i], SOL_SOCKET, SO_REUSEPORT, &opt,
+ sizeof(opt)))
+ error(1, errno, "failed to set SO_REUSEPORT");
+ if (bind(rcv_fds[i], addr, sz))
+ error(1, errno, "failed to bind receive socket");
+ if (setsockopt(rcv_fds[i], SOL_TCP, TCP_FASTOPEN, &qlen,
+ sizeof(qlen)))
+ error(1, errno, "failed to set TCP_FASTOPEN");
+ set_keys(rcv_fds[i], keys);
+ if (proto == SOCK_STREAM && listen(rcv_fds[i], 10))
+ error(1, errno, "failed to listen on receive port");
+ }
+}
+
+static int connect_and_send(int family, int proto)
+{
+ struct sockaddr_in saddr4 = {0};
+ struct sockaddr_in daddr4 = {0};
+ struct sockaddr_in6 saddr6 = {0};
+ struct sockaddr_in6 daddr6 = {0};
+ struct sockaddr *saddr, *daddr;
+ int fd, sz, ret;
+ char data[1];
+
+ switch (family) {
+ case AF_INET:
+ saddr4.sin_family = AF_INET;
+ saddr4.sin_addr.s_addr = htonl(INADDR_ANY);
+ saddr4.sin_port = 0;
+
+ daddr4.sin_family = AF_INET;
+ if (!inet_pton(family, IP4_ADDR, &daddr4.sin_addr.s_addr))
+ error(1, errno, "inet_pton failed: %s", IP4_ADDR);
+ daddr4.sin_port = htons(PORT);
+
+ sz = sizeof(saddr4);
+ saddr = (struct sockaddr *)&saddr4;
+ daddr = (struct sockaddr *)&daddr4;
+ break;
+ case AF_INET6:
+ saddr6.sin6_family = AF_INET6;
+ saddr6.sin6_addr = in6addr_any;
+
+ daddr6.sin6_family = AF_INET6;
+ if (!inet_pton(family, IP6_ADDR, &daddr6.sin6_addr))
+ error(1, errno, "inet_pton failed: %s", IP6_ADDR);
+ daddr6.sin6_port = htons(PORT);
+
+ sz = sizeof(saddr6);
+ saddr = (struct sockaddr *)&saddr6;
+ daddr = (struct sockaddr *)&daddr6;
+ break;
+ default:
+ error(1, 0, "Unsupported family %d", family);
+ /* clang does not recognize error() above as terminating
+ * the program, so it complains that saddr, daddr, sz are
+ * not initialized when this code path is taken. Silence it.
+ */
+ return -1;
+ }
+ fd = socket(family, proto, 0);
+ if (fd < 0)
+ error(1, errno, "failed to create send socket");
+ if (bind(fd, saddr, sz))
+ error(1, errno, "failed to bind send socket");
+ data[0] = 'a';
+ ret = sendto(fd, data, 1, MSG_FASTOPEN, daddr, sz);
+ if (ret != 1)
+ error(1, errno, "failed to sendto");
+
+ return fd;
+}
+
+static bool is_listen_fd(int fd)
+{
+ int i;
+
+ for (i = 0; i < N_LISTEN; i++) {
+ if (rcv_fds[i] == fd)
+ return true;
+ }
+ return false;
+}
+
+static void rotate_key(int fd)
+{
+ static int iter;
+ static uint32_t new_key[4];
+ uint32_t keys[8];
+ uint32_t tmp_key[4];
+ int i;
+
+ if (iter < N_LISTEN) {
+ /* first set new key as backups */
+ if (iter == 0) {
+ for (i = 0; i < ARRAY_SIZE(new_key); i++)
+ new_key[i] = rand();
+ }
+ get_keys(fd, keys);
+ memcpy(keys + 4, new_key, KEY_LENGTH);
+ set_keys(fd, keys);
+ } else {
+ /* swap the keys */
+ get_keys(fd, keys);
+ memcpy(tmp_key, keys + 4, KEY_LENGTH);
+ memcpy(keys + 4, keys, KEY_LENGTH);
+ memcpy(keys, tmp_key, KEY_LENGTH);
+ set_keys(fd, keys);
+ }
+ if (++iter >= (N_LISTEN * 2))
+ iter = 0;
+}
+
+static void run_one_test(int family)
+{
+ struct epoll_event ev;
+ int i, send_fd;
+ int n_loops = 10000;
+ int rotate_key_fd = 0;
+ int key_rotate_interval = 50;
+ int fd, epfd;
+ char buf[1];
+
+ build_rcv_fd(family, SOCK_STREAM, rcv_fds);
+ epfd = epoll_create(1);
+ if (epfd < 0)
+ error(1, errno, "failed to create epoll");
+ ev.events = EPOLLIN;
+ for (i = 0; i < N_LISTEN; i++) {
+ ev.data.fd = rcv_fds[i];
+ if (epoll_ctl(epfd, EPOLL_CTL_ADD, rcv_fds[i], &ev))
+ error(1, errno, "failed to register sock epoll");
+ }
+ while (n_loops--) {
+ send_fd = connect_and_send(family, SOCK_STREAM);
+ if (do_rotate && ((n_loops % key_rotate_interval) == 0)) {
+ rotate_key(rcv_fds[rotate_key_fd]);
+ if (++rotate_key_fd >= N_LISTEN)
+ rotate_key_fd = 0;
+ }
+ while (1) {
+ i = epoll_wait(epfd, &ev, 1, -1);
+ if (i < 0)
+ error(1, errno, "epoll_wait failed");
+ if (is_listen_fd(ev.data.fd)) {
+ fd = accept(ev.data.fd, NULL, NULL);
+ if (fd < 0)
+ error(1, errno, "failed to accept");
+ ev.data.fd = fd;
+ if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev))
+ error(1, errno, "failed epoll add");
+ continue;
+ }
+ i = recv(ev.data.fd, buf, sizeof(buf), 0);
+ if (i != 1)
+ error(1, errno, "failed recv data");
+ if (epoll_ctl(epfd, EPOLL_CTL_DEL, ev.data.fd, NULL))
+ error(1, errno, "failed epoll del");
+ close(ev.data.fd);
+ break;
+ }
+ close(send_fd);
+ }
+ for (i = 0; i < N_LISTEN; i++)
+ close(rcv_fds[i]);
+}
+
+static void parse_opts(int argc, char **argv)
+{
+ int c;
+
+ while ((c = getopt(argc, argv, "46sr")) != -1) {
+ switch (c) {
+ case '4':
+ do_ipv6 = false;
+ break;
+ case '6':
+ do_ipv6 = true;
+ break;
+ case 's':
+ do_sockopt = true;
+ break;
+ case 'r':
+ do_rotate = true;
+ key_len = KEY_LENGTH * 2;
+ break;
+ default:
+ error(1, 0, "%s: parse error", argv[0]);
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ parse_opts(argc, argv);
+ proc_fd = open(PROC_FASTOPEN_KEY, O_RDWR);
+ if (proc_fd < 0)
+ error(1, errno, "Unable to open %s", PROC_FASTOPEN_KEY);
+ srand(time(NULL));
+ if (do_ipv6)
+ run_one_test(AF_INET6);
+ else
+ run_one_test(AF_INET);
+ close(proc_fd);
+ fprintf(stderr, "PASS\n");
+ return 0;
+}
diff --git a/tools/testing/selftests/net/tcp_fastopen_backup_key.sh b/tools/testing/selftests/net/tcp_fastopen_backup_key.sh
new file mode 100755
index 000000000000..f6e65674b83c
--- /dev/null
+++ b/tools/testing/selftests/net/tcp_fastopen_backup_key.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# rotate TFO keys for ipv4/ipv6 and verify that the client does
+# not present an invalid cookie.
+
+set +x
+set -e
+
+readonly NETNS="ns-$(mktemp -u XXXXXX)"
+
+setup() {
+ ip netns add "${NETNS}"
+ ip -netns "${NETNS}" link set lo up
+ ip netns exec "${NETNS}" sysctl -w net.ipv4.tcp_fastopen=3 \
+ >/dev/null 2>&1
+}
+
+cleanup() {
+ ip netns del "${NETNS}"
+}
+
+trap cleanup EXIT
+setup
+
+do_test() {
+ # flush routes before each run, otherwise successive runs can
+ # initially present an old TFO cookie
+ ip netns exec "${NETNS}" ip tcp_metrics flush
+ ip netns exec "${NETNS}" ./tcp_fastopen_backup_key "$1"
+ val=$(ip netns exec "${NETNS}" nstat -az | \
+ grep TcpExtTCPFastOpenPassiveFail | awk '{print $2}')
+ if [ "$val" != 0 ]; then
+ echo "FAIL: TcpExtTCPFastOpenPassiveFail non-zero"
+ return 1
+ fi
+}
+
+do_test "-4"
+do_test "-6"
+do_test "-4"
+do_test "-6"
+do_test "-4s"
+do_test "-6s"
+do_test "-4s"
+do_test "-6s"
+do_test "-4r"
+do_test "-6r"
+do_test "-4r"
+do_test "-6r"
+do_test "-4sr"
+do_test "-6sr"
+do_test "-4sr"
+do_test "-6sr"
+echo "all tests done"
diff --git a/tools/testing/selftests/net/tcp_inq.c b/tools/testing/selftests/net/tcp_inq.c
index d044b29ddabc..bd6a9c7a3e8a 100644
--- a/tools/testing/selftests/net/tcp_inq.c
+++ b/tools/testing/selftests/net/tcp_inq.c
@@ -1,19 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2018 Google Inc.
* Author: Soheil Hassas Yeganeh (soheil@google.com)
*
* Simple example on how to use TCP_INQ and TCP_CM_INQ.
- *
- * License (GPLv2):
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
- * more details.
*/
#define _GNU_SOURCE
diff --git a/tools/testing/selftests/net/tcp_mmap.c b/tools/testing/selftests/net/tcp_mmap.c
index e8c5dff448eb..31ced79f4f25 100644
--- a/tools/testing/selftests/net/tcp_mmap.c
+++ b/tools/testing/selftests/net/tcp_mmap.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2018 Google Inc.
* Author: Eric Dumazet (edumazet@google.com)
@@ -44,21 +45,6 @@
* cpu usage user:0.046 sys:3.559, 110.016 usec per MB, 65529 c-switches
* received 32768 MB (99.9939 % mmap'ed) in 7.43764 s, 36.9577 Gbit
* cpu usage user:0.035 sys:3.467, 106.873 usec per MB, 65530 c-switches
- *
- * License (GPLv2):
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _GNU_SOURCE
#include <pthread.h>
diff --git a/tools/testing/selftests/net/test_blackhole_dev.sh b/tools/testing/selftests/net/test_blackhole_dev.sh
new file mode 100755
index 000000000000..3119b80e711f
--- /dev/null
+++ b/tools/testing/selftests/net/test_blackhole_dev.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# Runs blackhole-dev test using blackhole-dev kernel module
+
+if /sbin/modprobe -q test_blackhole_dev ; then
+ /sbin/modprobe -q -r test_blackhole_dev;
+ echo "test_blackhole_dev: ok";
+else
+ echo "test_blackhole_dev: [FAIL]";
+ exit 1;
+fi
diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c
index 47ddfc154036..4c285b6e1db8 100644
--- a/tools/testing/selftests/net/tls.c
+++ b/tools/testing/selftests/net/tls.c
@@ -25,6 +25,80 @@
#define TLS_PAYLOAD_MAX_LEN 16384
#define SOL_TLS 282
+#ifndef ENOTSUPP
+#define ENOTSUPP 524
+#endif
+
+FIXTURE(tls_basic)
+{
+ int fd, cfd;
+ bool notls;
+};
+
+FIXTURE_SETUP(tls_basic)
+{
+ struct sockaddr_in addr;
+ socklen_t len;
+ int sfd, ret;
+
+ self->notls = false;
+ len = sizeof(addr);
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = 0;
+
+ self->fd = socket(AF_INET, SOCK_STREAM, 0);
+ sfd = socket(AF_INET, SOCK_STREAM, 0);
+
+ ret = bind(sfd, &addr, sizeof(addr));
+ ASSERT_EQ(ret, 0);
+ ret = listen(sfd, 10);
+ ASSERT_EQ(ret, 0);
+
+ ret = getsockname(sfd, &addr, &len);
+ ASSERT_EQ(ret, 0);
+
+ ret = connect(self->fd, &addr, sizeof(addr));
+ ASSERT_EQ(ret, 0);
+
+ self->cfd = accept(sfd, &addr, &len);
+ ASSERT_GE(self->cfd, 0);
+
+ close(sfd);
+
+ ret = setsockopt(self->fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
+ if (ret != 0) {
+ ASSERT_EQ(errno, ENOENT);
+ self->notls = true;
+ printf("Failure setting TCP_ULP, testing without tls\n");
+ return;
+ }
+
+ ret = setsockopt(self->cfd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
+ ASSERT_EQ(ret, 0);
+}
+
+FIXTURE_TEARDOWN(tls_basic)
+{
+ close(self->fd);
+ close(self->cfd);
+}
+
+/* Send some data through with ULP but no keys */
+TEST_F(tls_basic, base_base)
+{
+ char const *test_str = "test_read";
+ int send_len = 10;
+ char buf[10];
+
+ ASSERT_EQ(strlen(test_str) + 1, send_len);
+
+ EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
+ EXPECT_NE(recv(self->cfd, buf, send_len, 0), -1);
+ EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
+};
+
FIXTURE(tls)
{
int fd, cfd;
@@ -165,6 +239,16 @@ TEST_F(tls, msg_more)
EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
}
+TEST_F(tls, msg_more_unsent)
+{
+ char const *test_str = "test_read";
+ int send_len = 10;
+ char buf[10];
+
+ EXPECT_EQ(send(self->fd, test_str, send_len, MSG_MORE), send_len);
+ EXPECT_EQ(recv(self->cfd, buf, send_len, MSG_DONTWAIT), -1);
+}
+
TEST_F(tls, sendmsg_single)
{
struct msghdr msg;
@@ -442,6 +526,21 @@ TEST_F(tls, multiple_send_single_recv)
EXPECT_EQ(memcmp(send_mem, recv_mem + send_len, send_len), 0);
}
+TEST_F(tls, single_send_multiple_recv_non_align)
+{
+ const unsigned int total_len = 15;
+ const unsigned int recv_len = 10;
+ char recv_mem[recv_len * 2];
+ char send_mem[total_len];
+
+ EXPECT_GE(send(self->fd, send_mem, total_len, 0), 0);
+ memset(recv_mem, 0, total_len);
+
+ EXPECT_EQ(recv(self->cfd, recv_mem, recv_len, 0), recv_len);
+ EXPECT_EQ(recv(self->cfd, recv_mem + recv_len, recv_len, 0), 5);
+ EXPECT_EQ(memcmp(send_mem, recv_mem, total_len), 0);
+}
+
TEST_F(tls, recv_partial)
{
char const *test_str = "test_read_partial";
@@ -575,6 +674,61 @@ TEST_F(tls, recv_peek_large_buf_mult_recs)
EXPECT_EQ(memcmp(test_str, buf, len), 0);
}
+TEST_F(tls, recv_lowat)
+{
+ char send_mem[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ char recv_mem[20];
+ int lowat = 8;
+
+ EXPECT_EQ(send(self->fd, send_mem, 10, 0), 10);
+ EXPECT_EQ(send(self->fd, send_mem, 5, 0), 5);
+
+ memset(recv_mem, 0, 20);
+ EXPECT_EQ(setsockopt(self->cfd, SOL_SOCKET, SO_RCVLOWAT,
+ &lowat, sizeof(lowat)), 0);
+ EXPECT_EQ(recv(self->cfd, recv_mem, 1, MSG_WAITALL), 1);
+ EXPECT_EQ(recv(self->cfd, recv_mem + 1, 6, MSG_WAITALL), 6);
+ EXPECT_EQ(recv(self->cfd, recv_mem + 7, 10, 0), 8);
+
+ EXPECT_EQ(memcmp(send_mem, recv_mem, 10), 0);
+ EXPECT_EQ(memcmp(send_mem, recv_mem + 10, 5), 0);
+}
+
+TEST_F(tls, bidir)
+{
+ char const *test_str = "test_read";
+ int send_len = 10;
+ char buf[10];
+ int ret;
+
+ if (!self->notls) {
+ struct tls12_crypto_info_aes_gcm_128 tls12;
+
+ memset(&tls12, 0, sizeof(tls12));
+ tls12.info.version = TLS_1_3_VERSION;
+ tls12.info.cipher_type = TLS_CIPHER_AES_GCM_128;
+
+ ret = setsockopt(self->fd, SOL_TLS, TLS_RX, &tls12,
+ sizeof(tls12));
+ ASSERT_EQ(ret, 0);
+
+ ret = setsockopt(self->cfd, SOL_TLS, TLS_TX, &tls12,
+ sizeof(tls12));
+ ASSERT_EQ(ret, 0);
+ }
+
+ ASSERT_EQ(strlen(test_str) + 1, send_len);
+
+ EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
+ EXPECT_NE(recv(self->cfd, buf, send_len, 0), -1);
+ EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
+
+ memset(buf, 0, sizeof(buf));
+
+ EXPECT_EQ(send(self->cfd, test_str, send_len, 0), send_len);
+ EXPECT_NE(recv(self->fd, buf, send_len, 0), -1);
+ EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
+};
TEST_F(tls, pollin)
{
@@ -610,6 +764,32 @@ TEST_F(tls, poll_wait)
EXPECT_EQ(recv(self->cfd, recv_mem, send_len, MSG_WAITALL), send_len);
}
+TEST_F(tls, poll_wait_split)
+{
+ struct pollfd fd = { 0, 0, 0 };
+ char send_mem[20] = {};
+ char recv_mem[15];
+
+ fd.fd = self->cfd;
+ fd.events = POLLIN;
+ /* Send 20 bytes */
+ EXPECT_EQ(send(self->fd, send_mem, sizeof(send_mem), 0),
+ sizeof(send_mem));
+ /* Poll with inf. timeout */
+ EXPECT_EQ(poll(&fd, 1, -1), 1);
+ EXPECT_EQ(fd.revents & POLLIN, 1);
+ EXPECT_EQ(recv(self->cfd, recv_mem, sizeof(recv_mem), MSG_WAITALL),
+ sizeof(recv_mem));
+
+ /* Now the remaining 5 bytes of record data are in TLS ULP */
+ fd.fd = self->cfd;
+ fd.events = POLLIN;
+ EXPECT_EQ(poll(&fd, 1, -1), 1);
+ EXPECT_EQ(fd.revents & POLLIN, 1);
+ EXPECT_EQ(recv(self->cfd, recv_mem, sizeof(recv_mem), 0),
+ sizeof(send_mem) - sizeof(recv_mem));
+}
+
TEST_F(tls, blocking)
{
size_t data = 100000;
@@ -777,6 +957,109 @@ TEST_F(tls, control_msg)
EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
}
+TEST_F(tls, shutdown)
+{
+ char const *test_str = "test_read";
+ int send_len = 10;
+ char buf[10];
+
+ ASSERT_EQ(strlen(test_str) + 1, send_len);
+
+ EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len);
+ EXPECT_NE(recv(self->cfd, buf, send_len, 0), -1);
+ EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
+
+ shutdown(self->fd, SHUT_RDWR);
+ shutdown(self->cfd, SHUT_RDWR);
+}
+
+TEST_F(tls, shutdown_unsent)
+{
+ char const *test_str = "test_read";
+ int send_len = 10;
+
+ EXPECT_EQ(send(self->fd, test_str, send_len, MSG_MORE), send_len);
+
+ shutdown(self->fd, SHUT_RDWR);
+ shutdown(self->cfd, SHUT_RDWR);
+}
+
+TEST_F(tls, shutdown_reuse)
+{
+ struct sockaddr_in addr;
+ int ret;
+
+ shutdown(self->fd, SHUT_RDWR);
+ shutdown(self->cfd, SHUT_RDWR);
+ close(self->cfd);
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = 0;
+
+ ret = bind(self->fd, &addr, sizeof(addr));
+ EXPECT_EQ(ret, 0);
+ ret = listen(self->fd, 10);
+ EXPECT_EQ(ret, -1);
+ EXPECT_EQ(errno, EINVAL);
+
+ ret = connect(self->fd, &addr, sizeof(addr));
+ EXPECT_EQ(ret, -1);
+ EXPECT_EQ(errno, EISCONN);
+}
+
+TEST(non_established) {
+ struct tls12_crypto_info_aes_gcm_256 tls12;
+ struct sockaddr_in addr;
+ int sfd, ret, fd;
+ socklen_t len;
+
+ len = sizeof(addr);
+
+ memset(&tls12, 0, sizeof(tls12));
+ tls12.info.version = TLS_1_2_VERSION;
+ tls12.info.cipher_type = TLS_CIPHER_AES_GCM_256;
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = 0;
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ sfd = socket(AF_INET, SOCK_STREAM, 0);
+
+ ret = bind(sfd, &addr, sizeof(addr));
+ ASSERT_EQ(ret, 0);
+ ret = listen(sfd, 10);
+ ASSERT_EQ(ret, 0);
+
+ ret = setsockopt(fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
+ EXPECT_EQ(ret, -1);
+ /* TLS ULP not supported */
+ if (errno == ENOENT)
+ return;
+ EXPECT_EQ(errno, ENOTSUPP);
+
+ ret = setsockopt(sfd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
+ EXPECT_EQ(ret, -1);
+ EXPECT_EQ(errno, ENOTSUPP);
+
+ ret = getsockname(sfd, &addr, &len);
+ ASSERT_EQ(ret, 0);
+
+ ret = connect(fd, &addr, sizeof(addr));
+ ASSERT_EQ(ret, 0);
+
+ ret = setsockopt(fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
+ ASSERT_EQ(ret, 0);
+
+ ret = setsockopt(fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
+ EXPECT_EQ(ret, -1);
+ EXPECT_EQ(errno, EEXIST);
+
+ close(fd);
+ close(sfd);
+}
+
TEST(keysizes) {
struct tls12_crypto_info_aes_gcm_256 tls12;
struct sockaddr_in addr;
diff --git a/tools/testing/selftests/net/txring_overwrite.c b/tools/testing/selftests/net/txring_overwrite.c
index fd8b1c663c39..7d9ea039450a 100644
--- a/tools/testing/selftests/net/txring_overwrite.c
+++ b/tools/testing/selftests/net/txring_overwrite.c
@@ -113,7 +113,7 @@ static int setup_tx(char **ring)
*ring = mmap(0, req.tp_block_size * req.tp_block_nr,
PROT_READ | PROT_WRITE, MAP_SHARED, fdt, 0);
- if (!*ring)
+ if (*ring == MAP_FAILED)
error(1, errno, "mmap");
return fdt;
diff --git a/tools/testing/selftests/net/udpgso_bench.sh b/tools/testing/selftests/net/udpgso_bench.sh
index 5670a9ffd8eb..80b5d352702e 100755
--- a/tools/testing/selftests/net/udpgso_bench.sh
+++ b/tools/testing/selftests/net/udpgso_bench.sh
@@ -3,6 +3,48 @@
#
# Run a series of udpgso benchmarks
+readonly GREEN='\033[0;92m'
+readonly YELLOW='\033[0;33m'
+readonly RED='\033[0;31m'
+readonly NC='\033[0m' # No Color
+
+readonly KSFT_PASS=0
+readonly KSFT_FAIL=1
+readonly KSFT_SKIP=4
+
+num_pass=0
+num_err=0
+num_skip=0
+
+kselftest_test_exitcode() {
+ local -r exitcode=$1
+
+ if [[ ${exitcode} -eq ${KSFT_PASS} ]]; then
+ num_pass=$(( $num_pass + 1 ))
+ elif [[ ${exitcode} -eq ${KSFT_SKIP} ]]; then
+ num_skip=$(( $num_skip + 1 ))
+ else
+ num_err=$(( $num_err + 1 ))
+ fi
+}
+
+kselftest_exit() {
+ echo -e "$(basename $0): PASS=${num_pass} SKIP=${num_skip} FAIL=${num_err}"
+
+ if [[ $num_err -ne 0 ]]; then
+ echo -e "$(basename $0): ${RED}FAIL${NC}"
+ exit ${KSFT_FAIL}
+ fi
+
+ if [[ $num_skip -ne 0 ]]; then
+ echo -e "$(basename $0): ${YELLOW}SKIP${NC}"
+ exit ${KSFT_SKIP}
+ fi
+
+ echo -e "$(basename $0): ${GREEN}PASS${NC}"
+ exit ${KSFT_PASS}
+}
+
wake_children() {
local -r jobs="$(jobs -p)"
@@ -25,6 +67,7 @@ run_in_netns() {
local -r args=$@
./in_netns.sh $0 __subprocess ${args}
+ kselftest_test_exitcode $?
}
run_udp() {
@@ -38,6 +81,18 @@ run_udp() {
echo "udp gso zerocopy"
run_in_netns ${args} -S 0 -z
+
+ echo "udp gso timestamp"
+ run_in_netns ${args} -S 0 -T
+
+ echo "udp gso zerocopy audit"
+ run_in_netns ${args} -S 0 -z -a
+
+ echo "udp gso timestamp audit"
+ run_in_netns ${args} -S 0 -T -a
+
+ echo "udp gso zerocopy timestamp audit"
+ run_in_netns ${args} -S 0 -T -z -a
}
run_tcp() {
@@ -48,10 +103,15 @@ run_tcp() {
echo "tcp zerocopy"
run_in_netns ${args} -t -z
+
+ # excluding for now because test fails intermittently
+ # add -P option to include poll() to reduce possibility of lost messages
+ #echo "tcp zerocopy audit"
+ #run_in_netns ${args} -t -z -P -a
}
run_all() {
- local -r core_args="-l 4"
+ local -r core_args="-l 3"
local -r ipv4_args="${core_args} -4 -D 127.0.0.1"
local -r ipv6_args="${core_args} -6 -D ::1"
@@ -66,6 +126,7 @@ run_all() {
if [[ $# -eq 0 ]]; then
run_all
+ kselftest_exit
elif [[ $1 == "__subprocess" ]]; then
shift
run_one $@
diff --git a/tools/testing/selftests/net/udpgso_bench_tx.c b/tools/testing/selftests/net/udpgso_bench_tx.c
index 4074538b5df5..ada99496634a 100644
--- a/tools/testing/selftests/net/udpgso_bench_tx.c
+++ b/tools/testing/selftests/net/udpgso_bench_tx.c
@@ -5,6 +5,8 @@
#include <arpa/inet.h>
#include <errno.h>
#include <error.h>
+#include <linux/errqueue.h>
+#include <linux/net_tstamp.h>
#include <netinet/if_ether.h>
#include <netinet/in.h>
#include <netinet/ip.h>
@@ -19,9 +21,12 @@
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
+#include <sys/poll.h>
#include <sys/types.h>
#include <unistd.h>
+#include "../kselftest.h"
+
#ifndef ETH_MAX_MTU
#define ETH_MAX_MTU 0xFFFFU
#endif
@@ -34,10 +39,18 @@
#define SO_ZEROCOPY 60
#endif
+#ifndef SO_EE_ORIGIN_ZEROCOPY
+#define SO_EE_ORIGIN_ZEROCOPY 5
+#endif
+
#ifndef MSG_ZEROCOPY
#define MSG_ZEROCOPY 0x4000000
#endif
+#ifndef ENOTSUPP
+#define ENOTSUPP 524
+#endif
+
#define NUM_PKT 100
static bool cfg_cache_trash;
@@ -48,12 +61,24 @@ static uint16_t cfg_mss;
static int cfg_payload_len = (1472 * 42);
static int cfg_port = 8000;
static int cfg_runtime_ms = -1;
+static bool cfg_poll;
static bool cfg_segment;
static bool cfg_sendmmsg;
static bool cfg_tcp;
+static uint32_t cfg_tx_ts = SOF_TIMESTAMPING_TX_SOFTWARE;
+static bool cfg_tx_tstamp;
+static bool cfg_audit;
+static bool cfg_verbose;
static bool cfg_zerocopy;
static int cfg_msg_nr;
static uint16_t cfg_gso_size;
+static unsigned long total_num_msgs;
+static unsigned long total_num_sends;
+static unsigned long stat_tx_ts;
+static unsigned long stat_tx_ts_errors;
+static unsigned long tstart;
+static unsigned long tend;
+static unsigned long stat_zcopies;
static socklen_t cfg_alen;
static struct sockaddr_storage cfg_dst_addr;
@@ -110,23 +135,125 @@ static void setup_sockaddr(int domain, const char *str_addr, void *sockaddr)
}
}
-static void flush_zerocopy(int fd)
+static void flush_cmsg(struct cmsghdr *cmsg)
+{
+ struct sock_extended_err *err;
+ struct scm_timestamping *tss;
+ __u32 lo;
+ __u32 hi;
+ int i;
+
+ switch (cmsg->cmsg_level) {
+ case SOL_SOCKET:
+ if (cmsg->cmsg_type == SO_TIMESTAMPING) {
+ i = (cfg_tx_ts == SOF_TIMESTAMPING_TX_HARDWARE) ? 2 : 0;
+ tss = (struct scm_timestamping *)CMSG_DATA(cmsg);
+ if (tss->ts[i].tv_sec == 0)
+ stat_tx_ts_errors++;
+ } else {
+ error(1, 0, "unknown SOL_SOCKET cmsg type=%u\n",
+ cmsg->cmsg_type);
+ }
+ break;
+ case SOL_IP:
+ case SOL_IPV6:
+ switch (cmsg->cmsg_type) {
+ case IP_RECVERR:
+ case IPV6_RECVERR:
+ {
+ err = (struct sock_extended_err *)CMSG_DATA(cmsg);
+ switch (err->ee_origin) {
+ case SO_EE_ORIGIN_TIMESTAMPING:
+ /* Got a TX timestamp from error queue */
+ stat_tx_ts++;
+ break;
+ case SO_EE_ORIGIN_ICMP:
+ case SO_EE_ORIGIN_ICMP6:
+ if (cfg_verbose)
+ fprintf(stderr,
+ "received ICMP error: type=%u, code=%u\n",
+ err->ee_type, err->ee_code);
+ break;
+ case SO_EE_ORIGIN_ZEROCOPY:
+ {
+ lo = err->ee_info;
+ hi = err->ee_data;
+ /* range of IDs acknowledged */
+ stat_zcopies += hi - lo + 1;
+ break;
+ }
+ case SO_EE_ORIGIN_LOCAL:
+ if (cfg_verbose)
+ fprintf(stderr,
+ "received packet with local origin: %u\n",
+ err->ee_origin);
+ break;
+ default:
+ error(0, 1, "received packet with origin: %u",
+ err->ee_origin);
+ }
+ break;
+ }
+ default:
+ error(0, 1, "unknown IP msg type=%u\n",
+ cmsg->cmsg_type);
+ break;
+ }
+ break;
+ default:
+ error(0, 1, "unknown cmsg level=%u\n",
+ cmsg->cmsg_level);
+ }
+}
+
+static void flush_errqueue_recv(int fd)
{
- struct msghdr msg = {0}; /* flush */
+ char control[CMSG_SPACE(sizeof(struct scm_timestamping)) +
+ CMSG_SPACE(sizeof(struct sock_extended_err)) +
+ CMSG_SPACE(sizeof(struct sockaddr_in6))] = {0};
+ struct msghdr msg = {0};
+ struct cmsghdr *cmsg;
int ret;
while (1) {
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
if (ret == -1 && errno == EAGAIN)
break;
if (ret == -1)
error(1, errno, "errqueue");
- if (msg.msg_flags != (MSG_ERRQUEUE | MSG_CTRUNC))
+ if (msg.msg_flags != MSG_ERRQUEUE)
error(1, 0, "errqueue: flags 0x%x\n", msg.msg_flags);
+ if (cfg_audit) {
+ for (cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg;
+ cmsg = CMSG_NXTHDR(&msg, cmsg))
+ flush_cmsg(cmsg);
+ }
msg.msg_flags = 0;
}
}
+static void flush_errqueue(int fd, const bool do_poll)
+{
+ if (do_poll) {
+ struct pollfd fds = {0};
+ int ret;
+
+ fds.fd = fd;
+ ret = poll(&fds, 1, 500);
+ if (ret == 0) {
+ if (cfg_verbose)
+ fprintf(stderr, "poll timeout\n");
+ } else if (ret < 0) {
+ error(1, errno, "poll");
+ }
+ }
+
+ flush_errqueue_recv(fd);
+}
+
static int send_tcp(int fd, char *data)
{
int ret, done = 0, count = 0;
@@ -168,16 +295,40 @@ static int send_udp(int fd, char *data)
return count;
}
+static void send_ts_cmsg(struct cmsghdr *cm)
+{
+ uint32_t *valp;
+
+ cm->cmsg_level = SOL_SOCKET;
+ cm->cmsg_type = SO_TIMESTAMPING;
+ cm->cmsg_len = CMSG_LEN(sizeof(cfg_tx_ts));
+ valp = (void *)CMSG_DATA(cm);
+ *valp = cfg_tx_ts;
+}
+
static int send_udp_sendmmsg(int fd, char *data)
{
+ char control[CMSG_SPACE(sizeof(cfg_tx_ts))] = {0};
const int max_nr_msg = ETH_MAX_MTU / ETH_DATA_LEN;
struct mmsghdr mmsgs[max_nr_msg];
struct iovec iov[max_nr_msg];
unsigned int off = 0, left;
+ size_t msg_controllen = 0;
int i = 0, ret;
memset(mmsgs, 0, sizeof(mmsgs));
+ if (cfg_tx_tstamp) {
+ struct msghdr msg = {0};
+ struct cmsghdr *cmsg;
+
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ send_ts_cmsg(cmsg);
+ msg_controllen += CMSG_SPACE(sizeof(cfg_tx_ts));
+ }
+
left = cfg_payload_len;
while (left) {
if (i == max_nr_msg)
@@ -189,6 +340,13 @@ static int send_udp_sendmmsg(int fd, char *data)
mmsgs[i].msg_hdr.msg_iov = iov + i;
mmsgs[i].msg_hdr.msg_iovlen = 1;
+ mmsgs[i].msg_hdr.msg_name = (void *)&cfg_dst_addr;
+ mmsgs[i].msg_hdr.msg_namelen = cfg_alen;
+ if (msg_controllen) {
+ mmsgs[i].msg_hdr.msg_control = control;
+ mmsgs[i].msg_hdr.msg_controllen = msg_controllen;
+ }
+
off += iov[i].iov_len;
left -= iov[i].iov_len;
i++;
@@ -214,9 +372,12 @@ static void send_udp_segment_cmsg(struct cmsghdr *cm)
static int send_udp_segment(int fd, char *data)
{
- char control[CMSG_SPACE(sizeof(cfg_gso_size))] = {0};
+ char control[CMSG_SPACE(sizeof(cfg_gso_size)) +
+ CMSG_SPACE(sizeof(cfg_tx_ts))] = {0};
struct msghdr msg = {0};
struct iovec iov = {0};
+ size_t msg_controllen;
+ struct cmsghdr *cmsg;
int ret;
iov.iov_base = data;
@@ -227,8 +388,16 @@ static int send_udp_segment(int fd, char *data)
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
- send_udp_segment_cmsg(CMSG_FIRSTHDR(&msg));
+ cmsg = CMSG_FIRSTHDR(&msg);
+ send_udp_segment_cmsg(cmsg);
+ msg_controllen = CMSG_SPACE(sizeof(cfg_mss));
+ if (cfg_tx_tstamp) {
+ cmsg = CMSG_NXTHDR(&msg, cmsg);
+ send_ts_cmsg(cmsg);
+ msg_controllen += CMSG_SPACE(sizeof(cfg_tx_ts));
+ }
+ msg.msg_controllen = msg_controllen;
msg.msg_name = (void *)&cfg_dst_addr;
msg.msg_namelen = cfg_alen;
@@ -243,7 +412,7 @@ static int send_udp_segment(int fd, char *data)
static void usage(const char *filepath)
{
- error(1, 0, "Usage: %s [-46cmtuz] [-C cpu] [-D dst ip] [-l secs] [-m messagenr] [-p port] [-s sendsize] [-S gsosize]",
+ error(1, 0, "Usage: %s [-46acmHPtTuvz] [-C cpu] [-D dst ip] [-l secs] [-M messagenr] [-p port] [-s sendsize] [-S gsosize]",
filepath);
}
@@ -252,7 +421,7 @@ static void parse_opts(int argc, char **argv)
int max_len, hdrlen;
int c;
- while ((c = getopt(argc, argv, "46cC:D:l:mM:p:s:S:tuz")) != -1) {
+ while ((c = getopt(argc, argv, "46acC:D:Hl:mM:p:s:PS:tTuvz")) != -1) {
switch (c) {
case '4':
if (cfg_family != PF_UNSPEC)
@@ -266,6 +435,9 @@ static void parse_opts(int argc, char **argv)
cfg_family = PF_INET6;
cfg_alen = sizeof(struct sockaddr_in6);
break;
+ case 'a':
+ cfg_audit = true;
+ break;
case 'c':
cfg_cache_trash = true;
break;
@@ -287,6 +459,9 @@ static void parse_opts(int argc, char **argv)
case 'p':
cfg_port = strtoul(optarg, NULL, 0);
break;
+ case 'P':
+ cfg_poll = true;
+ break;
case 's':
cfg_payload_len = strtoul(optarg, NULL, 0);
break;
@@ -294,12 +469,22 @@ static void parse_opts(int argc, char **argv)
cfg_gso_size = strtoul(optarg, NULL, 0);
cfg_segment = true;
break;
+ case 'H':
+ cfg_tx_ts = SOF_TIMESTAMPING_TX_HARDWARE;
+ cfg_tx_tstamp = true;
+ break;
case 't':
cfg_tcp = true;
break;
+ case 'T':
+ cfg_tx_tstamp = true;
+ break;
case 'u':
cfg_connected = false;
break;
+ case 'v':
+ cfg_verbose = true;
+ break;
case 'z':
cfg_zerocopy = true;
break;
@@ -315,6 +500,8 @@ static void parse_opts(int argc, char **argv)
error(1, 0, "connectionless tcp makes no sense");
if (cfg_segment && cfg_sendmmsg)
error(1, 0, "cannot combine segment offload and sendmmsg");
+ if (cfg_tx_tstamp && !(cfg_segment || cfg_sendmmsg))
+ error(1, 0, "Options -T and -H require either -S or -m option");
if (cfg_family == PF_INET)
hdrlen = sizeof(struct iphdr) + sizeof(struct udphdr);
@@ -349,11 +536,80 @@ static void set_pmtu_discover(int fd, bool is_ipv4)
error(1, errno, "setsockopt path mtu");
}
+static void set_tx_timestamping(int fd)
+{
+ int val = SOF_TIMESTAMPING_OPT_CMSG | SOF_TIMESTAMPING_OPT_ID |
+ SOF_TIMESTAMPING_OPT_TSONLY;
+
+ if (cfg_tx_ts == SOF_TIMESTAMPING_TX_SOFTWARE)
+ val |= SOF_TIMESTAMPING_SOFTWARE;
+ else
+ val |= SOF_TIMESTAMPING_RAW_HARDWARE;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val)))
+ error(1, errno, "setsockopt tx timestamping");
+}
+
+static void print_audit_report(unsigned long num_msgs, unsigned long num_sends)
+{
+ unsigned long tdelta;
+
+ tdelta = tend - tstart;
+ if (!tdelta)
+ return;
+
+ fprintf(stderr, "Summary over %lu.%03lu seconds...\n",
+ tdelta / 1000, tdelta % 1000);
+ fprintf(stderr,
+ "sum %s tx: %6lu MB/s %10lu calls (%lu/s) %10lu msgs (%lu/s)\n",
+ cfg_tcp ? "tcp" : "udp",
+ ((num_msgs * cfg_payload_len) >> 10) / tdelta,
+ num_sends, num_sends * 1000 / tdelta,
+ num_msgs, num_msgs * 1000 / tdelta);
+
+ if (cfg_tx_tstamp) {
+ if (stat_tx_ts_errors)
+ error(1, 0,
+ "Expected clean TX Timestamps: %9lu msgs received %6lu errors",
+ stat_tx_ts, stat_tx_ts_errors);
+ if (stat_tx_ts != num_sends)
+ error(1, 0,
+ "Unexpected number of TX Timestamps: %9lu expected %9lu received",
+ num_sends, stat_tx_ts);
+ fprintf(stderr,
+ "Tx Timestamps: %19lu received %17lu errors\n",
+ stat_tx_ts, stat_tx_ts_errors);
+ }
+
+ if (cfg_zerocopy) {
+ if (stat_zcopies != num_sends)
+ error(1, 0, "Unexpected number of Zerocopy completions: %9lu expected %9lu received",
+ num_sends, stat_zcopies);
+ fprintf(stderr,
+ "Zerocopy acks: %19lu\n",
+ stat_zcopies);
+ }
+}
+
+static void print_report(unsigned long num_msgs, unsigned long num_sends)
+{
+ fprintf(stderr,
+ "%s tx: %6lu MB/s %8lu calls/s %6lu msg/s\n",
+ cfg_tcp ? "tcp" : "udp",
+ (num_msgs * cfg_payload_len) >> 20,
+ num_sends, num_msgs);
+
+ if (cfg_audit) {
+ total_num_msgs += num_msgs;
+ total_num_sends += num_sends;
+ }
+}
+
int main(int argc, char **argv)
{
unsigned long num_msgs, num_sends;
unsigned long tnow, treport, tstop;
- int fd, i, val;
+ int fd, i, val, ret;
parse_opts(argc, argv);
@@ -373,8 +629,16 @@ int main(int argc, char **argv)
if (cfg_zerocopy) {
val = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val)))
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY,
+ &val, sizeof(val));
+ if (ret) {
+ if (errno == ENOPROTOOPT || errno == ENOTSUPP) {
+ fprintf(stderr, "SO_ZEROCOPY not supported");
+ exit(KSFT_SKIP);
+ }
error(1, errno, "setsockopt zerocopy");
+ }
}
if (cfg_connected &&
@@ -384,8 +648,13 @@ int main(int argc, char **argv)
if (cfg_segment)
set_pmtu_discover(fd, cfg_family == PF_INET);
+ if (cfg_tx_tstamp)
+ set_tx_timestamping(fd);
+
num_msgs = num_sends = 0;
tnow = gettimeofday_ms();
+ tstart = tnow;
+ tend = tnow;
tstop = tnow + cfg_runtime_ms;
treport = tnow + 1000;
@@ -400,19 +669,15 @@ int main(int argc, char **argv)
else
num_sends += send_udp(fd, buf[i]);
num_msgs++;
- if (cfg_zerocopy && ((num_msgs & 0xF) == 0))
- flush_zerocopy(fd);
+ if ((cfg_zerocopy && ((num_msgs & 0xF) == 0)) || cfg_tx_tstamp)
+ flush_errqueue(fd, cfg_poll);
if (cfg_msg_nr && num_msgs >= cfg_msg_nr)
break;
tnow = gettimeofday_ms();
- if (tnow > treport) {
- fprintf(stderr,
- "%s tx: %6lu MB/s %8lu calls/s %6lu msg/s\n",
- cfg_tcp ? "tcp" : "udp",
- (num_msgs * cfg_payload_len) >> 20,
- num_sends, num_msgs);
+ if (tnow >= treport) {
+ print_report(num_msgs, num_sends);
num_msgs = num_sends = 0;
treport = tnow + 1000;
}
@@ -423,8 +688,18 @@ int main(int argc, char **argv)
} while (!interrupted && (cfg_runtime_ms == -1 || tnow < tstop));
+ if (cfg_zerocopy || cfg_tx_tstamp)
+ flush_errqueue(fd, true);
+
if (close(fd))
error(1, errno, "close");
+ if (cfg_audit) {
+ tend = tnow;
+ total_num_msgs += num_msgs;
+ total_num_sends += num_sends;
+ print_audit_report(total_num_msgs, total_num_sends);
+ }
+
return 0;
}
diff --git a/tools/testing/selftests/net/xfrm_policy.sh b/tools/testing/selftests/net/xfrm_policy.sh
index 71d7fdc513c1..7a1bf94c5bd3 100755
--- a/tools/testing/selftests/net/xfrm_policy.sh
+++ b/tools/testing/selftests/net/xfrm_policy.sh
@@ -106,6 +106,13 @@ do_overlap()
#
# 10.0.0.0/24 and 10.0.1.0/24 nodes have been merged as 10.0.0.0/23.
ip -net $ns xfrm policy add src 10.1.0.0/24 dst 10.0.0.0/23 dir fwd priority 200 action block
+
+ # similar to above: add policies (with partially random address), with shrinking prefixes.
+ for p in 29 28 27;do
+ for k in $(seq 1 32); do
+ ip -net $ns xfrm policy add src 10.253.1.$((RANDOM%255))/$p dst 10.254.1.$((RANDOM%255))/$p dir fwd priority $((200+k)) action block 2>/dev/null
+ done
+ done
}
do_esp_policy_get_check() {
@@ -257,6 +264,29 @@ check_exceptions()
return $lret
}
+check_hthresh_repeat()
+{
+ local log=$1
+ i=0
+
+ for i in $(seq 1 10);do
+ ip -net ns1 xfrm policy update src e000:0001::0000 dst ff01::0014:0000:0001 dir in tmpl src :: dst :: proto esp mode tunnel priority 100 action allow || break
+ ip -net ns1 xfrm policy set hthresh6 0 28 || break
+
+ ip -net ns1 xfrm policy update src e000:0001::0000 dst ff01::01 dir in tmpl src :: dst :: proto esp mode tunnel priority 100 action allow || break
+ ip -net ns1 xfrm policy set hthresh6 0 28 || break
+ done
+
+ if [ $i -ne 10 ] ;then
+ echo "FAIL: $log" 1>&2
+ ret=1
+ return 1
+ fi
+
+ echo "PASS: $log"
+ return 0
+}
+
#check for needed privileges
if [ "$(id -u)" -ne 0 ];then
echo "SKIP: Need root privileges"
@@ -404,7 +434,9 @@ for n in ns3 ns4;do
ip -net $n xfrm policy set hthresh4 32 32 hthresh6 128 128
sleep $((RANDOM%5))
done
-check_exceptions "exceptions and block policies after hresh change to normal"
+check_exceptions "exceptions and block policies after htresh change to normal"
+
+check_hthresh_repeat "policies with repeated htresh change"
for i in 1 2 3 4;do ip netns del ns$i;done
diff --git a/tools/testing/selftests/netfilter/Makefile b/tools/testing/selftests/netfilter/Makefile
index 3e6d1bcc2894..4144984ebee5 100644
--- a/tools/testing/selftests/netfilter/Makefile
+++ b/tools/testing/selftests/netfilter/Makefile
@@ -2,6 +2,6 @@
# Makefile for netfilter selftests
TEST_PROGS := nft_trans_stress.sh nft_nat.sh bridge_brouter.sh \
- conntrack_icmp_related.sh
+ conntrack_icmp_related.sh nft_flowtable.sh
include ../lib.mk
diff --git a/tools/testing/selftests/netfilter/nft_flowtable.sh b/tools/testing/selftests/netfilter/nft_flowtable.sh
new file mode 100755
index 000000000000..16571ac1dab4
--- /dev/null
+++ b/tools/testing/selftests/netfilter/nft_flowtable.sh
@@ -0,0 +1,372 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# This tests basic flowtable functionality.
+# Creates following topology:
+#
+# Originator (MTU 9000) <-Router1-> MTU 1500 <-Router2-> Responder (MTU 2000)
+# Router1 is the one doing flow offloading, Router2 has no special
+# purpose other than having a link that is smaller than either Originator
+# and responder, i.e. TCPMSS announced values are too large and will still
+# result in fragmentation and/or PMTU discovery.
+
+# Kselftest framework requirement - SKIP code is 4.
+ksft_skip=4
+ret=0
+
+ns1in=""
+ns2in=""
+ns1out=""
+ns2out=""
+
+log_netns=$(sysctl -n net.netfilter.nf_log_all_netns)
+
+nft --version > /dev/null 2>&1
+if [ $? -ne 0 ];then
+ echo "SKIP: Could not run test without nft tool"
+ exit $ksft_skip
+fi
+
+ip -Version > /dev/null 2>&1
+if [ $? -ne 0 ];then
+ echo "SKIP: Could not run test without ip tool"
+ exit $ksft_skip
+fi
+
+which nc > /dev/null 2>&1
+if [ $? -ne 0 ];then
+ echo "SKIP: Could not run test without nc (netcat)"
+ exit $ksft_skip
+fi
+
+ip netns add nsr1
+if [ $? -ne 0 ];then
+ echo "SKIP: Could not create net namespace"
+ exit $ksft_skip
+fi
+
+ip netns add ns1
+ip netns add ns2
+
+ip netns add nsr2
+
+cleanup() {
+ for i in 1 2; do
+ ip netns del ns$i
+ ip netns del nsr$i
+ done
+
+ rm -f "$ns1in" "$ns1out"
+ rm -f "$ns2in" "$ns2out"
+
+ [ $log_netns -eq 0 ] && sysctl -q net.netfilter.nf_log_all_netns=$log_netns
+}
+
+trap cleanup EXIT
+
+sysctl -q net.netfilter.nf_log_all_netns=1
+
+ip link add veth0 netns nsr1 type veth peer name eth0 netns ns1
+ip link add veth1 netns nsr1 type veth peer name veth0 netns nsr2
+
+ip link add veth1 netns nsr2 type veth peer name eth0 netns ns2
+
+for dev in lo veth0 veth1; do
+ for i in 1 2; do
+ ip -net nsr$i link set $dev up
+ done
+done
+
+ip -net nsr1 addr add 10.0.1.1/24 dev veth0
+ip -net nsr1 addr add dead:1::1/64 dev veth0
+
+ip -net nsr2 addr add 10.0.2.1/24 dev veth1
+ip -net nsr2 addr add dead:2::1/64 dev veth1
+
+# set different MTUs so we need to push packets coming from ns1 (large MTU)
+# to ns2 (smaller MTU) to stack either to perform fragmentation (ip_no_pmtu_disc=1),
+# or to do PTMU discovery (send ICMP error back to originator).
+# ns2 is going via nsr2 with a smaller mtu, so that TCPMSS announced by both peers
+# is NOT the lowest link mtu.
+
+ip -net nsr1 link set veth0 mtu 9000
+ip -net ns1 link set eth0 mtu 9000
+
+ip -net nsr2 link set veth1 mtu 2000
+ip -net ns2 link set eth0 mtu 2000
+
+# transfer-net between nsr1 and nsr2.
+# these addresses are not used for connections.
+ip -net nsr1 addr add 192.168.10.1/24 dev veth1
+ip -net nsr1 addr add fee1:2::1/64 dev veth1
+
+ip -net nsr2 addr add 192.168.10.2/24 dev veth0
+ip -net nsr2 addr add fee1:2::2/64 dev veth0
+
+for i in 1 2; do
+ ip netns exec nsr$i sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
+ ip netns exec nsr$i sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
+
+ ip -net ns$i link set lo up
+ ip -net ns$i link set eth0 up
+ ip -net ns$i addr add 10.0.$i.99/24 dev eth0
+ ip -net ns$i route add default via 10.0.$i.1
+ ip -net ns$i addr add dead:$i::99/64 dev eth0
+ ip -net ns$i route add default via dead:$i::1
+ ip netns exec ns$i sysctl net.ipv4.tcp_no_metrics_save=1 > /dev/null
+
+ # don't set ip DF bit for first two tests
+ ip netns exec ns$i sysctl net.ipv4.ip_no_pmtu_disc=1 > /dev/null
+done
+
+ip -net nsr1 route add default via 192.168.10.2
+ip -net nsr2 route add default via 192.168.10.1
+
+ip netns exec nsr1 nft -f - <<EOF
+table inet filter {
+ flowtable f1 {
+ hook ingress priority 0
+ devices = { veth0, veth1 }
+ }
+
+ chain forward {
+ type filter hook forward priority 0; policy drop;
+
+ # flow offloaded? Tag ct with mark 1, so we can detect when it fails.
+ meta oif "veth1" tcp dport 12345 flow offload @f1 counter
+
+ # use packet size to trigger 'should be offloaded by now'.
+ # otherwise, if 'flow offload' expression never offloads, the
+ # test will pass.
+ tcp dport 12345 meta length gt 200 ct mark set 1 counter
+
+ # this turns off flow offloading internally, so expect packets again
+ tcp flags fin,rst ct mark set 0 accept
+
+ # this allows large packets from responder, we need this as long
+ # as PMTUd is off.
+ # This rule is deleted for the last test, when we expect PMTUd
+ # to kick in and ensure all packets meet mtu requirements.
+ meta length gt 1500 accept comment something-to-grep-for
+
+ # next line blocks connection w.o. working offload.
+ # we only do this for reverse dir, because we expect packets to
+ # enter slow path due to MTU mismatch of veth0 and veth1.
+ tcp sport 12345 ct mark 1 counter log prefix "mark failure " drop
+
+ ct state established,related accept
+
+ # for packets that we can't offload yet, i.e. SYN (any ct that is not confirmed)
+ meta length lt 200 oif "veth1" tcp dport 12345 counter accept
+
+ meta nfproto ipv4 meta l4proto icmp accept
+ meta nfproto ipv6 meta l4proto icmpv6 accept
+ }
+}
+EOF
+
+if [ $? -ne 0 ]; then
+ echo "SKIP: Could not load nft ruleset"
+ exit $ksft_skip
+fi
+
+# test basic connectivity
+ip netns exec ns1 ping -c 1 -q 10.0.2.99 > /dev/null
+if [ $? -ne 0 ];then
+ echo "ERROR: ns1 cannot reach ns2" 1>&2
+ bash
+ exit 1
+fi
+
+ip netns exec ns2 ping -c 1 -q 10.0.1.99 > /dev/null
+if [ $? -ne 0 ];then
+ echo "ERROR: ns2 cannot reach ns1" 1>&2
+ exit 1
+fi
+
+if [ $ret -eq 0 ];then
+ echo "PASS: netns routing/connectivity: ns1 can reach ns2"
+fi
+
+ns1in=$(mktemp)
+ns1out=$(mktemp)
+ns2in=$(mktemp)
+ns2out=$(mktemp)
+
+make_file()
+{
+ name=$1
+ who=$2
+
+ SIZE=$((RANDOM % (1024 * 8)))
+ TSIZE=$((SIZE * 1024))
+
+ dd if=/dev/urandom of="$name" bs=1024 count=$SIZE 2> /dev/null
+
+ SIZE=$((RANDOM % 1024))
+ SIZE=$((SIZE + 128))
+ TSIZE=$((TSIZE + SIZE))
+ dd if=/dev/urandom conf=notrunc of="$name" bs=1 count=$SIZE 2> /dev/null
+}
+
+check_transfer()
+{
+ in=$1
+ out=$2
+ what=$3
+
+ cmp "$in" "$out" > /dev/null 2>&1
+ if [ $? -ne 0 ] ;then
+ echo "FAIL: file mismatch for $what" 1>&2
+ ls -l "$in"
+ ls -l "$out"
+ return 1
+ fi
+
+ return 0
+}
+
+test_tcp_forwarding()
+{
+ local nsa=$1
+ local nsb=$2
+ local lret=0
+
+ ip netns exec $nsb nc -w 5 -l -p 12345 < "$ns2in" > "$ns2out" &
+ lpid=$!
+
+ sleep 1
+ ip netns exec $nsa nc -w 4 10.0.2.99 12345 < "$ns1in" > "$ns1out" &
+ cpid=$!
+
+ sleep 3
+
+ kill $lpid
+ kill $cpid
+ wait
+
+ check_transfer "$ns1in" "$ns2out" "ns1 -> ns2"
+ if [ $? -ne 0 ];then
+ lret=1
+ fi
+
+ check_transfer "$ns2in" "$ns1out" "ns1 <- ns2"
+ if [ $? -ne 0 ];then
+ lret=1
+ fi
+
+ return $lret
+}
+
+make_file "$ns1in" "ns1"
+make_file "$ns2in" "ns2"
+
+# First test:
+# No PMTU discovery, nsr1 is expected to fragment packets from ns1 to ns2 as needed.
+test_tcp_forwarding ns1 ns2
+if [ $? -eq 0 ] ;then
+ echo "PASS: flow offloaded for ns1/ns2"
+else
+ echo "FAIL: flow offload for ns1/ns2:" 1>&2
+ ip netns exec nsr1 nft list ruleset
+ ret=1
+fi
+
+# delete default route, i.e. ns2 won't be able to reach ns1 and
+# will depend on ns1 being masqueraded in nsr1.
+# expect ns1 has nsr1 address.
+ip -net ns2 route del default via 10.0.2.1
+ip -net ns2 route del default via dead:2::1
+ip -net ns2 route add 192.168.10.1 via 10.0.2.1
+
+# Second test:
+# Same, but with NAT enabled.
+ip netns exec nsr1 nft -f - <<EOF
+table ip nat {
+ chain postrouting {
+ type nat hook postrouting priority 0; policy accept;
+ meta oifname "veth1" masquerade
+ }
+}
+EOF
+
+test_tcp_forwarding ns1 ns2
+
+if [ $? -eq 0 ] ;then
+ echo "PASS: flow offloaded for ns1/ns2 with NAT"
+else
+ echo "FAIL: flow offload for ns1/ns2 with NAT" 1>&2
+ ip netns exec nsr1 nft list ruleset
+ ret=1
+fi
+
+# Third test:
+# Same as second test, but with PMTU discovery enabled.
+handle=$(ip netns exec nsr1 nft -a list table inet filter | grep something-to-grep-for | cut -d \# -f 2)
+
+ip netns exec nsr1 nft delete rule inet filter forward $handle
+if [ $? -ne 0 ] ;then
+ echo "FAIL: Could not delete large-packet accept rule"
+ exit 1
+fi
+
+ip netns exec ns1 sysctl net.ipv4.ip_no_pmtu_disc=0 > /dev/null
+ip netns exec ns2 sysctl net.ipv4.ip_no_pmtu_disc=0 > /dev/null
+
+test_tcp_forwarding ns1 ns2
+if [ $? -eq 0 ] ;then
+ echo "PASS: flow offloaded for ns1/ns2 with NAT and pmtu discovery"
+else
+ echo "FAIL: flow offload for ns1/ns2 with NAT and pmtu discovery" 1>&2
+ ip netns exec nsr1 nft list ruleset
+fi
+
+KEY_SHA="0x"$(ps -xaf | sha1sum | cut -d " " -f 1)
+KEY_AES="0x"$(ps -xaf | md5sum | cut -d " " -f 1)
+SPI1=$RANDOM
+SPI2=$RANDOM
+
+if [ $SPI1 -eq $SPI2 ]; then
+ SPI2=$((SPI2+1))
+fi
+
+do_esp() {
+ local ns=$1
+ local me=$2
+ local remote=$3
+ local lnet=$4
+ local rnet=$5
+ local spi_out=$6
+ local spi_in=$7
+
+ ip -net $ns xfrm state add src $remote dst $me proto esp spi $spi_in enc aes $KEY_AES auth sha1 $KEY_SHA mode tunnel sel src $rnet dst $lnet
+ ip -net $ns xfrm state add src $me dst $remote proto esp spi $spi_out enc aes $KEY_AES auth sha1 $KEY_SHA mode tunnel sel src $lnet dst $rnet
+
+ # to encrypt packets as they go out (includes forwarded packets that need encapsulation)
+ ip -net $ns xfrm policy add src $lnet dst $rnet dir out tmpl src $me dst $remote proto esp mode tunnel priority 1 action allow
+ # to fwd decrypted packets after esp processing:
+ ip -net $ns xfrm policy add src $rnet dst $lnet dir fwd tmpl src $remote dst $me proto esp mode tunnel priority 1 action allow
+
+}
+
+do_esp nsr1 192.168.10.1 192.168.10.2 10.0.1.0/24 10.0.2.0/24 $SPI1 $SPI2
+
+do_esp nsr2 192.168.10.2 192.168.10.1 10.0.2.0/24 10.0.1.0/24 $SPI2 $SPI1
+
+ip netns exec nsr1 nft delete table ip nat
+
+# restore default routes
+ip -net ns2 route del 192.168.10.1 via 10.0.2.1
+ip -net ns2 route add default via 10.0.2.1
+ip -net ns2 route add default via dead:2::1
+
+test_tcp_forwarding ns1 ns2
+if [ $? -eq 0 ] ;then
+ echo "PASS: ipsec tunnel mode for ns1/ns2"
+else
+ echo "FAIL: ipsec tunnel mode for ns1/ns2"
+ ip netns exec nsr1 nft list ruleset 1>&2
+ ip netns exec nsr1 cat /proc/net/xfrm_stat 1>&2
+fi
+
+exit $ret
diff --git a/tools/testing/selftests/netfilter/nft_nat.sh b/tools/testing/selftests/netfilter/nft_nat.sh
index 21159f5f3362..1be55e705780 100755
--- a/tools/testing/selftests/netfilter/nft_nat.sh
+++ b/tools/testing/selftests/netfilter/nft_nat.sh
@@ -8,6 +8,11 @@ ksft_skip=4
ret=0
test_inet_nat=true
+cleanup()
+{
+ for i in 0 1 2; do ip netns del ns$i;done
+}
+
nft --version > /dev/null 2>&1
if [ $? -ne 0 ];then
echo "SKIP: Could not run test without nft tool"
@@ -21,10 +26,21 @@ if [ $? -ne 0 ];then
fi
ip netns add ns0
+if [ $? -ne 0 ];then
+ echo "SKIP: Could not create net namespace"
+ exit $ksft_skip
+fi
+
+trap cleanup EXIT
+
ip netns add ns1
ip netns add ns2
-ip link add veth0 netns ns0 type veth peer name eth0 netns ns1
+ip link add veth0 netns ns0 type veth peer name eth0 netns ns1 > /dev/null 2>&1
+if [ $? -ne 0 ];then
+ echo "SKIP: No virtual ethernet pair device support in kernel"
+ exit $ksft_skip
+fi
ip link add veth1 netns ns0 type veth peer name eth0 netns ns2
ip -net ns0 link set lo up
@@ -347,7 +363,7 @@ EOF
test_masquerade6()
{
local family=$1
- local natflags=$1
+ local natflags=$2
local lret=0
ip netns exec ns0 sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
@@ -392,18 +408,13 @@ EOF
ip netns exec ns2 ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
if [ $? -ne 0 ] ; then
-<<<<<<< HEAD
- echo "ERROR: cannot ping ns1 from ns2 with active $family masquerading"
-=======
- echo "ERROR: cannot ping ns1 from ns2 with active ipv6 masquerade $natflags"
->>>>>>> cd8dead0c39457e58ec1d36db93aedca811d48f1
+ echo "ERROR: cannot ping ns1 from ns2 with active $family masquerade $natflags"
lret=1
fi
# ns1 should have seen packets from ns0, due to masquerade
expect="packets 1 bytes 104"
for dir in "in6" "out6" ; do
-
cnt=$(ip netns exec ns1 nft list counter inet filter ns0${dir} | grep -q "$expect")
if [ $? -ne 0 ]; then
bad_counter ns1 ns0$dir "$expect"
@@ -433,38 +444,27 @@ EOF
fi
done
-<<<<<<< HEAD
- ip netns exec ns0 nft flush chain $family nat postrouting
-=======
ip netns exec ns2 ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
if [ $? -ne 0 ] ; then
echo "ERROR: cannot ping ns1 from ns2 with active ipv6 masquerade $natflags (attempt 2)"
lret=1
fi
- ip netns exec ns0 nft flush chain ip6 nat postrouting
->>>>>>> cd8dead0c39457e58ec1d36db93aedca811d48f1
+ ip netns exec ns0 nft flush chain $family nat postrouting
if [ $? -ne 0 ]; then
echo "ERROR: Could not flush $family nat postrouting" 1>&2
lret=1
fi
-<<<<<<< HEAD
- test $lret -eq 0 && echo "PASS: $family IPv6 masquerade for ns2"
-=======
- test $lret -eq 0 && echo "PASS: IPv6 masquerade $natflags for ns2"
->>>>>>> cd8dead0c39457e58ec1d36db93aedca811d48f1
+ test $lret -eq 0 && echo "PASS: $family IPv6 masquerade $natflags for ns2"
return $lret
}
test_masquerade()
{
-<<<<<<< HEAD
local family=$1
-=======
- local natflags=$1
->>>>>>> cd8dead0c39457e58ec1d36db93aedca811d48f1
+ local natflags=$2
local lret=0
ip netns exec ns0 sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
@@ -509,11 +509,7 @@ EOF
ip netns exec ns2 ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
if [ $? -ne 0 ] ; then
-<<<<<<< HEAD
- echo "ERROR: cannot ping ns1 from ns2 with active $family masquerading"
-=======
- echo "ERROR: cannot ping ns1 from ns2 with active ip masquere $natflags"
->>>>>>> cd8dead0c39457e58ec1d36db93aedca811d48f1
+ echo "ERROR: cannot ping ns1 from ns2 with active $family masquerade $natflags"
lret=1
fi
@@ -549,27 +545,19 @@ EOF
fi
done
-<<<<<<< HEAD
- ip netns exec ns0 nft flush chain $family nat postrouting
-=======
ip netns exec ns2 ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
if [ $? -ne 0 ] ; then
echo "ERROR: cannot ping ns1 from ns2 with active ip masquerade $natflags (attempt 2)"
lret=1
fi
- ip netns exec ns0 nft flush chain ip nat postrouting
->>>>>>> cd8dead0c39457e58ec1d36db93aedca811d48f1
+ ip netns exec ns0 nft flush chain $family nat postrouting
if [ $? -ne 0 ]; then
echo "ERROR: Could not flush $family nat postrouting" 1>&2
lret=1
fi
-<<<<<<< HEAD
- test $lret -eq 0 && echo "PASS: $family IP masquerade for ns2"
-=======
- test $lret -eq 0 && echo "PASS: IP masquerade $natflags for ns2"
->>>>>>> cd8dead0c39457e58ec1d36db93aedca811d48f1
+ test $lret -eq 0 && echo "PASS: $family IP masquerade $natflags for ns2"
return $lret
}
@@ -842,21 +830,14 @@ reset_counters
$test_inet_nat && test_local_dnat inet
$test_inet_nat && test_local_dnat6 inet
+for flags in "" "fully-random"; do
reset_counters
-<<<<<<< HEAD
-test_masquerade ip
-test_masquerade6 ip6
+test_masquerade ip $flags
+test_masquerade6 ip6 $flags
reset_counters
-$test_inet_nat && test_masquerade inet
-$test_inet_nat && test_masquerade6 inet
-=======
-test_masquerade ""
-test_masquerade6 ""
-
-reset_counters
-test_masquerade "fully-random"
-test_masquerade6 "fully-random"
->>>>>>> cd8dead0c39457e58ec1d36db93aedca811d48f1
+$test_inet_nat && test_masquerade inet $flags
+$test_inet_nat && test_masquerade6 inet $flags
+done
reset_counters
test_redirect ip
@@ -865,6 +846,4 @@ reset_counters
$test_inet_nat && test_redirect inet
$test_inet_nat && test_redirect6 inet
-for i in 0 1 2; do ip netns del ns$i;done
-
exit $ret
diff --git a/tools/testing/selftests/networking/timestamping/timestamping.c b/tools/testing/selftests/networking/timestamping/timestamping.c
index 5cdfd743447b..aca3491174a1 100644
--- a/tools/testing/selftests/networking/timestamping/timestamping.c
+++ b/tools/testing/selftests/networking/timestamping/timestamping.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* This program demonstrates how the various time stamping features in
* the Linux kernel work. It emulates the behavior of a PTP
@@ -14,19 +15,6 @@
*
* Copyright (C) 2009 Intel Corporation.
* Author: Patrick Ohly <patrick.ohly@intel.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
@@ -44,6 +32,7 @@
#include <asm/types.h>
#include <linux/net_tstamp.h>
#include <linux/errqueue.h>
+#include <linux/sockios.h>
#ifndef SO_TIMESTAMPING
# define SO_TIMESTAMPING 37
@@ -54,14 +43,6 @@
# define SO_TIMESTAMPNS 35
#endif
-#ifndef SIOCGSTAMPNS
-# define SIOCGSTAMPNS 0x8907
-#endif
-
-#ifndef SIOCSHWTSTAMP
-# define SIOCSHWTSTAMP 0x89b0
-#endif
-
static void usage(const char *error)
{
if (error)
diff --git a/tools/testing/selftests/networking/timestamping/txtimestamp.c b/tools/testing/selftests/networking/timestamping/txtimestamp.c
index d1bbafb16f47..7e386be47120 100644
--- a/tools/testing/selftests/networking/timestamping/txtimestamp.c
+++ b/tools/testing/selftests/networking/timestamping/txtimestamp.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014 Google Inc.
* Author: willemb@google.com (Willem de Bruijn)
@@ -14,20 +15,6 @@
*
* This test requires a dummy TCP server.
* A simple `nc6 [-u] -l -p $DESTPORT` will do
- *
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _GNU_SOURCE
diff --git a/tools/testing/selftests/nsfs/Makefile b/tools/testing/selftests/nsfs/Makefile
index 9ff7c7f80625..dd9bd50b7b93 100644
--- a/tools/testing/selftests/nsfs/Makefile
+++ b/tools/testing/selftests/nsfs/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
TEST_GEN_PROGS := owner pidns
CFLAGS := -Wall -Werror
diff --git a/tools/testing/selftests/ntb/ntb_test.sh b/tools/testing/selftests/ntb/ntb_test.sh
index 08cbfbbc7029..9c60337317c6 100755
--- a/tools/testing/selftests/ntb/ntb_test.sh
+++ b/tools/testing/selftests/ntb/ntb_test.sh
@@ -1,16 +1,7 @@
#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (c) 2016 Microsemi. All Rights Reserved.
#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as
-# published by the Free Software Foundation; either version 2 of
-# the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it would be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
# Author: Logan Gunthorpe <logang@deltatee.com>
REMOTE_HOST=
@@ -87,10 +78,10 @@ set -e
function _modprobe()
{
- modprobe "$@"
+ modprobe "$@" || return 1
if [[ "$REMOTE_HOST" != "" ]]; then
- ssh "$REMOTE_HOST" modprobe "$@"
+ ssh "$REMOTE_HOST" modprobe "$@" || return 1
fi
}
@@ -451,6 +442,30 @@ function pingpong_test()
echo " Passed"
}
+function msi_test()
+{
+ LOC=$1
+ REM=$2
+
+ write_file 1 $LOC/ready
+
+ echo "Running MSI interrupt tests on: $(subdirname $LOC) / $(subdirname $REM)"
+
+ CNT=$(read_file "$LOC/count")
+ for ((i = 0; i < $CNT; i++)); do
+ START=$(read_file $REM/../irq${i}_occurrences)
+ write_file $i $LOC/trigger
+ END=$(read_file $REM/../irq${i}_occurrences)
+
+ if [[ $(($END - $START)) != 1 ]]; then
+ echo "MSI did not trigger the interrupt on the remote side!" >&2
+ exit 1
+ fi
+ done
+
+ echo " Passed"
+}
+
function perf_test()
{
USE_DMA=$1
@@ -529,6 +544,29 @@ function ntb_pingpong_tests()
_modprobe -r ntb_pingpong
}
+function ntb_msi_tests()
+{
+ LOCAL_MSI="$DEBUGFS/ntb_msi_test/$LOCAL_DEV"
+ REMOTE_MSI="$REMOTE_HOST:$DEBUGFS/ntb_msi_test/$REMOTE_DEV"
+
+ echo "Starting ntb_msi_test tests..."
+
+ if ! _modprobe ntb_msi_test 2> /dev/null; then
+ echo " Not doing MSI tests seeing the module is not available."
+ return
+ fi
+
+ port_test $LOCAL_MSI $REMOTE_MSI
+
+ LOCAL_PEER="$LOCAL_MSI/peer$LOCAL_PIDX"
+ REMOTE_PEER="$REMOTE_MSI/peer$REMOTE_PIDX"
+
+ msi_test $LOCAL_PEER $REMOTE_PEER
+ msi_test $REMOTE_PEER $LOCAL_PEER
+
+ _modprobe -r ntb_msi_test
+}
+
function ntb_perf_tests()
{
LOCAL_PERF="$DEBUGFS/ntb_perf/$LOCAL_DEV"
@@ -550,6 +588,7 @@ function cleanup()
_modprobe -r ntb_perf 2> /dev/null
_modprobe -r ntb_pingpong 2> /dev/null
_modprobe -r ntb_transport 2> /dev/null
+ _modprobe -r ntb_msi_test 2> /dev/null
set -e
}
@@ -586,5 +625,7 @@ ntb_tool_tests
echo
ntb_pingpong_tests
echo
+ntb_msi_tests
+echo
ntb_perf_tests
echo
diff --git a/tools/testing/selftests/pidfd/.gitignore b/tools/testing/selftests/pidfd/.gitignore
index 822a1e63d045..8d069490e17b 100644
--- a/tools/testing/selftests/pidfd/.gitignore
+++ b/tools/testing/selftests/pidfd/.gitignore
@@ -1 +1,4 @@
+pidfd_open_test
+pidfd_poll_test
pidfd_test
+pidfd_wait
diff --git a/tools/testing/selftests/pidfd/Makefile b/tools/testing/selftests/pidfd/Makefile
index deaf8073bc06..464c9b76148f 100644
--- a/tools/testing/selftests/pidfd/Makefile
+++ b/tools/testing/selftests/pidfd/Makefile
@@ -1,6 +1,7 @@
-CFLAGS += -g -I../../../../usr/include/
+# SPDX-License-Identifier: GPL-2.0-only
+CFLAGS += -g -I../../../../usr/include/ -lpthread
-TEST_GEN_PROGS := pidfd_test
+TEST_GEN_PROGS := pidfd_test pidfd_open_test pidfd_poll_test pidfd_wait
include ../lib.mk
diff --git a/tools/testing/selftests/pidfd/pidfd.h b/tools/testing/selftests/pidfd/pidfd.h
new file mode 100644
index 000000000000..c6bc68329f4b
--- /dev/null
+++ b/tools/testing/selftests/pidfd/pidfd.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __PIDFD_H
+#define __PIDFD_H
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syscall.h>
+#include <sys/mount.h>
+
+#include "../kselftest.h"
+
+#ifndef P_PIDFD
+#define P_PIDFD 3
+#endif
+
+#ifndef CLONE_PIDFD
+#define CLONE_PIDFD 0x00001000
+#endif
+
+#ifndef __NR_pidfd_open
+#define __NR_pidfd_open -1
+#endif
+
+#ifndef __NR_pidfd_send_signal
+#define __NR_pidfd_send_signal -1
+#endif
+
+#ifndef __NR_clone3
+#define __NR_clone3 -1
+#endif
+
+/*
+ * The kernel reserves 300 pids via RESERVED_PIDS in kernel/pid.c
+ * That means, when it wraps around any pid < 300 will be skipped.
+ * So we need to use a pid > 300 in order to test recycling.
+ */
+#define PID_RECYCLE 1000
+
+/*
+ * Define a few custom error codes for the child process to clearly indicate
+ * what is happening. This way we can tell the difference between a system
+ * error, a test error, etc.
+ */
+#define PIDFD_PASS 0
+#define PIDFD_FAIL 1
+#define PIDFD_ERROR 2
+#define PIDFD_SKIP 3
+#define PIDFD_XFAIL 4
+
+int wait_for_pid(pid_t pid)
+{
+ int status, ret;
+
+again:
+ ret = waitpid(pid, &status, 0);
+ if (ret == -1) {
+ if (errno == EINTR)
+ goto again;
+
+ return -1;
+ }
+
+ if (!WIFEXITED(status))
+ return -1;
+
+ return WEXITSTATUS(status);
+}
+
+static inline int sys_pidfd_open(pid_t pid, unsigned int flags)
+{
+ return syscall(__NR_pidfd_open, pid, flags);
+}
+
+static inline int sys_pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
+ unsigned int flags)
+{
+ return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
+}
+
+#endif /* __PIDFD_H */
diff --git a/tools/testing/selftests/pidfd/pidfd_open_test.c b/tools/testing/selftests/pidfd/pidfd_open_test.c
new file mode 100644
index 000000000000..b9fe75fc3e51
--- /dev/null
+++ b/tools/testing/selftests/pidfd/pidfd_open_test.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syscall.h>
+#include <sys/mount.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "pidfd.h"
+#include "../kselftest.h"
+
+static int safe_int(const char *numstr, int *converted)
+{
+ char *err = NULL;
+ long sli;
+
+ errno = 0;
+ sli = strtol(numstr, &err, 0);
+ if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN))
+ return -ERANGE;
+
+ if (errno != 0 && sli == 0)
+ return -EINVAL;
+
+ if (err == numstr || *err != '\0')
+ return -EINVAL;
+
+ if (sli > INT_MAX || sli < INT_MIN)
+ return -ERANGE;
+
+ *converted = (int)sli;
+ return 0;
+}
+
+static int char_left_gc(const char *buffer, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (buffer[i] == ' ' ||
+ buffer[i] == '\t')
+ continue;
+
+ return i;
+ }
+
+ return 0;
+}
+
+static int char_right_gc(const char *buffer, size_t len)
+{
+ int i;
+
+ for (i = len - 1; i >= 0; i--) {
+ if (buffer[i] == ' ' ||
+ buffer[i] == '\t' ||
+ buffer[i] == '\n' ||
+ buffer[i] == '\0')
+ continue;
+
+ return i + 1;
+ }
+
+ return 0;
+}
+
+static char *trim_whitespace_in_place(char *buffer)
+{
+ buffer += char_left_gc(buffer, strlen(buffer));
+ buffer[char_right_gc(buffer, strlen(buffer))] = '\0';
+ return buffer;
+}
+
+static pid_t get_pid_from_fdinfo_file(int pidfd, const char *key, size_t keylen)
+{
+ int ret;
+ char path[512];
+ FILE *f;
+ size_t n = 0;
+ pid_t result = -1;
+ char *line = NULL;
+
+ snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", pidfd);
+
+ f = fopen(path, "re");
+ if (!f)
+ return -1;
+
+ while (getline(&line, &n, f) != -1) {
+ char *numstr;
+
+ if (strncmp(line, key, keylen))
+ continue;
+
+ numstr = trim_whitespace_in_place(line + 4);
+ ret = safe_int(numstr, &result);
+ if (ret < 0)
+ goto out;
+
+ break;
+ }
+
+out:
+ free(line);
+ fclose(f);
+ return result;
+}
+
+int main(int argc, char **argv)
+{
+ int pidfd = -1, ret = 1;
+ pid_t pid;
+
+ ksft_set_plan(3);
+
+ pidfd = sys_pidfd_open(-1, 0);
+ if (pidfd >= 0) {
+ ksft_print_msg(
+ "%s - succeeded to open pidfd for invalid pid -1\n",
+ strerror(errno));
+ goto on_error;
+ }
+ ksft_test_result_pass("do not allow invalid pid test: passed\n");
+
+ pidfd = sys_pidfd_open(getpid(), 1);
+ if (pidfd >= 0) {
+ ksft_print_msg(
+ "%s - succeeded to open pidfd with invalid flag value specified\n",
+ strerror(errno));
+ goto on_error;
+ }
+ ksft_test_result_pass("do not allow invalid flag test: passed\n");
+
+ pidfd = sys_pidfd_open(getpid(), 0);
+ if (pidfd < 0) {
+ ksft_print_msg("%s - failed to open pidfd\n", strerror(errno));
+ goto on_error;
+ }
+ ksft_test_result_pass("open a new pidfd test: passed\n");
+
+ pid = get_pid_from_fdinfo_file(pidfd, "Pid:", sizeof("Pid:") - 1);
+ ksft_print_msg("pidfd %d refers to process with pid %d\n", pidfd, pid);
+
+ ret = 0;
+
+on_error:
+ if (pidfd >= 0)
+ close(pidfd);
+
+ return !ret ? ksft_exit_pass() : ksft_exit_fail();
+}
diff --git a/tools/testing/selftests/pidfd/pidfd_poll_test.c b/tools/testing/selftests/pidfd/pidfd_poll_test.c
new file mode 100644
index 000000000000..4b115444dfe9
--- /dev/null
+++ b/tools/testing/selftests/pidfd/pidfd_poll_test.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syscall.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "pidfd.h"
+#include "../kselftest.h"
+
+static bool timeout;
+
+static void handle_alarm(int sig)
+{
+ timeout = true;
+}
+
+int main(int argc, char **argv)
+{
+ struct pollfd fds;
+ int iter, nevents;
+ int nr_iterations = 10000;
+
+ fds.events = POLLIN;
+
+ if (argc > 2)
+ ksft_exit_fail_msg("Unexpected command line argument\n");
+
+ if (argc == 2) {
+ nr_iterations = atoi(argv[1]);
+ if (nr_iterations <= 0)
+ ksft_exit_fail_msg("invalid input parameter %s\n",
+ argv[1]);
+ }
+
+ ksft_print_msg("running pidfd poll test for %d iterations\n",
+ nr_iterations);
+
+ for (iter = 0; iter < nr_iterations; iter++) {
+ int pidfd;
+ int child_pid = fork();
+
+ if (child_pid < 0) {
+ if (errno == EAGAIN) {
+ iter--;
+ continue;
+ }
+ ksft_exit_fail_msg(
+ "%s - failed to fork a child process\n",
+ strerror(errno));
+ }
+
+ if (child_pid == 0) {
+ /* Child process just sleeps for a min and exits */
+ sleep(60);
+ exit(EXIT_SUCCESS);
+ }
+
+ /* Parent kills the child and waits for its death */
+ pidfd = sys_pidfd_open(child_pid, 0);
+ if (pidfd < 0)
+ ksft_exit_fail_msg("%s - pidfd_open failed\n",
+ strerror(errno));
+
+ /* Setup 3 sec alarm - plenty of time */
+ if (signal(SIGALRM, handle_alarm) == SIG_ERR)
+ ksft_exit_fail_msg("%s - signal failed\n",
+ strerror(errno));
+ alarm(3);
+
+ /* Send SIGKILL to the child */
+ if (sys_pidfd_send_signal(pidfd, SIGKILL, NULL, 0))
+ ksft_exit_fail_msg("%s - pidfd_send_signal failed\n",
+ strerror(errno));
+
+ /* Wait for the death notification */
+ fds.fd = pidfd;
+ nevents = poll(&fds, 1, -1);
+
+ /* Check for error conditions */
+ if (nevents < 0)
+ ksft_exit_fail_msg("%s - poll failed\n",
+ strerror(errno));
+
+ if (nevents != 1)
+ ksft_exit_fail_msg("unexpected poll result: %d\n",
+ nevents);
+
+ if (!(fds.revents & POLLIN))
+ ksft_exit_fail_msg(
+ "unexpected event type received: 0x%x\n",
+ fds.revents);
+
+ if (timeout)
+ ksft_exit_fail_msg(
+ "death notification wait timeout\n");
+
+ close(pidfd);
+ /* Wait for child to prevent zombies */
+ if (waitpid(child_pid, NULL, 0) < 0)
+ ksft_exit_fail_msg("%s - waitpid failed\n",
+ strerror(errno));
+
+ }
+
+ ksft_test_result_pass("pidfd poll test: pass\n");
+ return ksft_exit_pass();
+}
diff --git a/tools/testing/selftests/pidfd/pidfd_test.c b/tools/testing/selftests/pidfd/pidfd_test.c
index 5bae1792e3d6..7aff2d3b42c0 100644
--- a/tools/testing/selftests/pidfd/pidfd_test.c
+++ b/tools/testing/selftests/pidfd/pidfd_test.c
@@ -4,22 +4,39 @@
#include <errno.h>
#include <fcntl.h>
#include <linux/types.h>
+#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syscall.h>
+#include <sys/epoll.h>
+#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/wait.h>
+#include <time.h>
#include <unistd.h>
+#include "pidfd.h"
#include "../kselftest.h"
-static inline int sys_pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
- unsigned int flags)
+#define str(s) _str(s)
+#define _str(s) #s
+#define CHILD_THREAD_MIN_WAIT 3 /* seconds */
+
+#define MAX_EVENTS 5
+
+static pid_t pidfd_clone(int flags, int *pidfd, int (*fn)(void *))
{
- return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
+ size_t stack_size = 1024;
+ char *stack[1024] = { 0 };
+
+#ifdef __ia64__
+ return __clone2(fn, stack, stack_size, flags | SIGCHLD, NULL, pidfd);
+#else
+ return clone(fn, stack + stack_size, flags | SIGCHLD, NULL, pidfd);
+#endif
}
static int signal_received;
@@ -62,28 +79,6 @@ static int test_pidfd_send_signal_simple_success(void)
return 0;
}
-static int wait_for_pid(pid_t pid)
-{
- int status, ret;
-
-again:
- ret = waitpid(pid, &status, 0);
- if (ret == -1) {
- if (errno == EINTR)
- goto again;
-
- return -1;
- }
-
- if (ret != pid)
- goto again;
-
- if (!WIFEXITED(status))
- return -1;
-
- return WEXITSTATUS(status);
-}
-
static int test_pidfd_send_signal_exited_fail(void)
{
int pidfd, ret, saved_errno;
@@ -129,13 +124,6 @@ static int test_pidfd_send_signal_exited_fail(void)
}
/*
- * The kernel reserves 300 pids via RESERVED_PIDS in kernel/pid.c
- * That means, when it wraps around any pid < 300 will be skipped.
- * So we need to use a pid > 300 in order to test recycling.
- */
-#define PID_RECYCLE 1000
-
-/*
* Maximum number of cycles we allow. This is equivalent to PID_MAX_DEFAULT.
* If users set a higher limit or we have cycled PIDFD_MAX_DEFAULT number of
* times then we skip the test to not go into an infinite loop or block for a
@@ -143,17 +131,6 @@ static int test_pidfd_send_signal_exited_fail(void)
*/
#define PIDFD_MAX_DEFAULT 0x8000
-/*
- * Define a few custom error codes for the child process to clearly indicate
- * what is happening. This way we can tell the difference between a system
- * error, a test error, etc.
- */
-#define PIDFD_PASS 0
-#define PIDFD_FAIL 1
-#define PIDFD_ERROR 2
-#define PIDFD_SKIP 3
-#define PIDFD_XFAIL 4
-
static int test_pidfd_send_signal_recycled_pid_fail(void)
{
int i, ret;
@@ -348,13 +325,9 @@ static int test_pidfd_send_signal_syscall_support(void)
ret = sys_pidfd_send_signal(pidfd, 0, NULL, 0);
if (ret < 0) {
- /*
- * pidfd_send_signal() will currently return ENOSYS when
- * CONFIG_PROC_FS is not set.
- */
if (errno == ENOSYS)
ksft_exit_skip(
- "%s test: pidfd_send_signal() syscall not supported (Ensure that CONFIG_PROC_FS=y is set)\n",
+ "%s test: pidfd_send_signal() syscall not supported\n",
test_name);
ksft_exit_fail_msg("%s test: Failed to send signal\n",
@@ -368,11 +341,192 @@ static int test_pidfd_send_signal_syscall_support(void)
return 0;
}
+static void *test_pidfd_poll_exec_thread(void *priv)
+{
+ ksft_print_msg("Child Thread: starting. pid %d tid %d ; and sleeping\n",
+ getpid(), syscall(SYS_gettid));
+ ksft_print_msg("Child Thread: doing exec of sleep\n");
+
+ execl("/bin/sleep", "sleep", str(CHILD_THREAD_MIN_WAIT), (char *)NULL);
+
+ ksft_print_msg("Child Thread: DONE. pid %d tid %d\n",
+ getpid(), syscall(SYS_gettid));
+ return NULL;
+}
+
+static void poll_pidfd(const char *test_name, int pidfd)
+{
+ int c;
+ int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+ struct epoll_event event, events[MAX_EVENTS];
+
+ if (epoll_fd == -1)
+ ksft_exit_fail_msg("%s test: Failed to create epoll file descriptor "
+ "(errno %d)\n",
+ test_name, errno);
+
+ event.events = EPOLLIN;
+ event.data.fd = pidfd;
+
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pidfd, &event)) {
+ ksft_exit_fail_msg("%s test: Failed to add epoll file descriptor "
+ "(errno %d)\n",
+ test_name, errno);
+ }
+
+ c = epoll_wait(epoll_fd, events, MAX_EVENTS, 5000);
+ if (c != 1 || !(events[0].events & EPOLLIN))
+ ksft_exit_fail_msg("%s test: Unexpected epoll_wait result (c=%d, events=%x) ",
+ "(errno %d)\n",
+ test_name, c, events[0].events, errno);
+
+ close(epoll_fd);
+ return;
+
+}
+
+static int child_poll_exec_test(void *args)
+{
+ pthread_t t1;
+
+ ksft_print_msg("Child (pidfd): starting. pid %d tid %d\n", getpid(),
+ syscall(SYS_gettid));
+ pthread_create(&t1, NULL, test_pidfd_poll_exec_thread, NULL);
+ /*
+ * Exec in the non-leader thread will destroy the leader immediately.
+ * If the wait in the parent returns too soon, the test fails.
+ */
+ while (1)
+ sleep(1);
+}
+
+static void test_pidfd_poll_exec(int use_waitpid)
+{
+ int pid, pidfd = 0;
+ int status, ret;
+ pthread_t t1;
+ time_t prog_start = time(NULL);
+ const char *test_name = "pidfd_poll check for premature notification on child thread exec";
+
+ ksft_print_msg("Parent: pid: %d\n", getpid());
+ pid = pidfd_clone(CLONE_PIDFD, &pidfd, child_poll_exec_test);
+ if (pid < 0)
+ ksft_exit_fail_msg("%s test: pidfd_clone failed (ret %d, errno %d)\n",
+ test_name, pid, errno);
+
+ ksft_print_msg("Parent: Waiting for Child (%d) to complete.\n", pid);
+
+ if (use_waitpid) {
+ ret = waitpid(pid, &status, 0);
+ if (ret == -1)
+ ksft_print_msg("Parent: error\n");
+
+ if (ret == pid)
+ ksft_print_msg("Parent: Child process waited for.\n");
+ } else {
+ poll_pidfd(test_name, pidfd);
+ }
+
+ time_t prog_time = time(NULL) - prog_start;
+
+ ksft_print_msg("Time waited for child: %lu\n", prog_time);
+
+ close(pidfd);
+
+ if (prog_time < CHILD_THREAD_MIN_WAIT || prog_time > CHILD_THREAD_MIN_WAIT + 2)
+ ksft_exit_fail_msg("%s test: Failed\n", test_name);
+ else
+ ksft_test_result_pass("%s test: Passed\n", test_name);
+}
+
+static void *test_pidfd_poll_leader_exit_thread(void *priv)
+{
+ ksft_print_msg("Child Thread: starting. pid %d tid %d ; and sleeping\n",
+ getpid(), syscall(SYS_gettid));
+ sleep(CHILD_THREAD_MIN_WAIT);
+ ksft_print_msg("Child Thread: DONE. pid %d tid %d\n", getpid(), syscall(SYS_gettid));
+ return NULL;
+}
+
+static time_t *child_exit_secs;
+static int child_poll_leader_exit_test(void *args)
+{
+ pthread_t t1, t2;
+
+ ksft_print_msg("Child: starting. pid %d tid %d\n", getpid(), syscall(SYS_gettid));
+ pthread_create(&t1, NULL, test_pidfd_poll_leader_exit_thread, NULL);
+ pthread_create(&t2, NULL, test_pidfd_poll_leader_exit_thread, NULL);
+
+ /*
+ * glibc exit calls exit_group syscall, so explicity call exit only
+ * so that only the group leader exits, leaving the threads alone.
+ */
+ *child_exit_secs = time(NULL);
+ syscall(SYS_exit, 0);
+}
+
+static void test_pidfd_poll_leader_exit(int use_waitpid)
+{
+ int pid, pidfd = 0;
+ int status, ret;
+ time_t prog_start = time(NULL);
+ const char *test_name = "pidfd_poll check for premature notification on non-empty"
+ "group leader exit";
+
+ child_exit_secs = mmap(NULL, sizeof *child_exit_secs, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+
+ if (child_exit_secs == MAP_FAILED)
+ ksft_exit_fail_msg("%s test: mmap failed (errno %d)\n",
+ test_name, errno);
+
+ ksft_print_msg("Parent: pid: %d\n", getpid());
+ pid = pidfd_clone(CLONE_PIDFD, &pidfd, child_poll_leader_exit_test);
+ if (pid < 0)
+ ksft_exit_fail_msg("%s test: pidfd_clone failed (ret %d, errno %d)\n",
+ test_name, pid, errno);
+
+ ksft_print_msg("Parent: Waiting for Child (%d) to complete.\n", pid);
+
+ if (use_waitpid) {
+ ret = waitpid(pid, &status, 0);
+ if (ret == -1)
+ ksft_print_msg("Parent: error\n");
+ } else {
+ /*
+ * This sleep tests for the case where if the child exits, and is in
+ * EXIT_ZOMBIE, but the thread group leader is non-empty, then the poll
+ * doesn't prematurely return even though there are active threads
+ */
+ sleep(1);
+ poll_pidfd(test_name, pidfd);
+ }
+
+ if (ret == pid)
+ ksft_print_msg("Parent: Child process waited for.\n");
+
+ time_t since_child_exit = time(NULL) - *child_exit_secs;
+
+ ksft_print_msg("Time since child exit: %lu\n", since_child_exit);
+
+ close(pidfd);
+
+ if (since_child_exit < CHILD_THREAD_MIN_WAIT ||
+ since_child_exit > CHILD_THREAD_MIN_WAIT + 2)
+ ksft_exit_fail_msg("%s test: Failed\n", test_name);
+ else
+ ksft_test_result_pass("%s test: Passed\n", test_name);
+}
+
int main(int argc, char **argv)
{
ksft_print_header();
ksft_set_plan(4);
+ test_pidfd_poll_exec(0);
+ test_pidfd_poll_exec(1);
+ test_pidfd_poll_leader_exit(0);
+ test_pidfd_poll_leader_exit(1);
test_pidfd_send_signal_syscall_support();
test_pidfd_send_signal_simple_success();
test_pidfd_send_signal_exited_fail();
diff --git a/tools/testing/selftests/pidfd/pidfd_wait.c b/tools/testing/selftests/pidfd/pidfd_wait.c
new file mode 100644
index 000000000000..7079f8eef792
--- /dev/null
+++ b/tools/testing/selftests/pidfd/pidfd_wait.c
@@ -0,0 +1,271 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sched.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "pidfd.h"
+#include "../kselftest.h"
+
+#define ptr_to_u64(ptr) ((__u64)((uintptr_t)(ptr)))
+
+static pid_t sys_clone3(struct clone_args *args)
+{
+ return syscall(__NR_clone3, args, sizeof(struct clone_args));
+}
+
+static int sys_waitid(int which, pid_t pid, siginfo_t *info, int options,
+ struct rusage *ru)
+{
+ return syscall(__NR_waitid, which, pid, info, options, ru);
+}
+
+static int test_pidfd_wait_simple(void)
+{
+ const char *test_name = "pidfd wait simple";
+ int pidfd = -1, status = 0;
+ pid_t parent_tid = -1;
+ struct clone_args args = {
+ .parent_tid = ptr_to_u64(&parent_tid),
+ .pidfd = ptr_to_u64(&pidfd),
+ .flags = CLONE_PIDFD | CLONE_PARENT_SETTID,
+ .exit_signal = SIGCHLD,
+ };
+ int ret;
+ pid_t pid;
+ siginfo_t info = {
+ .si_signo = 0,
+ };
+
+ pidfd = open("/proc/self", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
+ if (pidfd < 0)
+ ksft_exit_fail_msg("%s test: failed to open /proc/self %s\n",
+ test_name, strerror(errno));
+
+ pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
+ if (pid == 0)
+ ksft_exit_fail_msg(
+ "%s test: succeeded to wait on invalid pidfd %s\n",
+ test_name, strerror(errno));
+ close(pidfd);
+ pidfd = -1;
+
+ pidfd = open("/dev/null", O_RDONLY | O_CLOEXEC);
+ if (pidfd == 0)
+ ksft_exit_fail_msg("%s test: failed to open /dev/null %s\n",
+ test_name, strerror(errno));
+
+ pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
+ if (pid == 0)
+ ksft_exit_fail_msg(
+ "%s test: succeeded to wait on invalid pidfd %s\n",
+ test_name, strerror(errno));
+ close(pidfd);
+ pidfd = -1;
+
+ pid = sys_clone3(&args);
+ if (pid < 0)
+ ksft_exit_fail_msg("%s test: failed to create new process %s\n",
+ test_name, strerror(errno));
+
+ if (pid == 0)
+ exit(EXIT_SUCCESS);
+
+ pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
+ if (pid < 0)
+ ksft_exit_fail_msg(
+ "%s test: failed to wait on process with pid %d and pidfd %d: %s\n",
+ test_name, parent_tid, pidfd, strerror(errno));
+
+ if (!WIFEXITED(info.si_status) || WEXITSTATUS(info.si_status))
+ ksft_exit_fail_msg(
+ "%s test: unexpected status received after waiting on process with pid %d and pidfd %d: %s\n",
+ test_name, parent_tid, pidfd, strerror(errno));
+ close(pidfd);
+
+ if (info.si_signo != SIGCHLD)
+ ksft_exit_fail_msg(
+ "%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
+ test_name, info.si_signo, parent_tid, pidfd,
+ strerror(errno));
+
+ if (info.si_code != CLD_EXITED)
+ ksft_exit_fail_msg(
+ "%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
+ test_name, info.si_code, parent_tid, pidfd,
+ strerror(errno));
+
+ if (info.si_pid != parent_tid)
+ ksft_exit_fail_msg(
+ "%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
+ test_name, info.si_pid, parent_tid, pidfd,
+ strerror(errno));
+
+ ksft_test_result_pass("%s test: Passed\n", test_name);
+ return 0;
+}
+
+static int test_pidfd_wait_states(void)
+{
+ const char *test_name = "pidfd wait states";
+ int pidfd = -1, status = 0;
+ pid_t parent_tid = -1;
+ struct clone_args args = {
+ .parent_tid = ptr_to_u64(&parent_tid),
+ .pidfd = ptr_to_u64(&pidfd),
+ .flags = CLONE_PIDFD | CLONE_PARENT_SETTID,
+ .exit_signal = SIGCHLD,
+ };
+ int ret;
+ pid_t pid;
+ siginfo_t info = {
+ .si_signo = 0,
+ };
+
+ pid = sys_clone3(&args);
+ if (pid < 0)
+ ksft_exit_fail_msg("%s test: failed to create new process %s\n",
+ test_name, strerror(errno));
+
+ if (pid == 0) {
+ kill(getpid(), SIGSTOP);
+ kill(getpid(), SIGSTOP);
+ exit(EXIT_SUCCESS);
+ }
+
+ ret = sys_waitid(P_PIDFD, pidfd, &info, WSTOPPED, NULL);
+ if (ret < 0)
+ ksft_exit_fail_msg(
+ "%s test: failed to wait on WSTOPPED process with pid %d and pidfd %d: %s\n",
+ test_name, parent_tid, pidfd, strerror(errno));
+
+ if (info.si_signo != SIGCHLD)
+ ksft_exit_fail_msg(
+ "%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
+ test_name, info.si_signo, parent_tid, pidfd,
+ strerror(errno));
+
+ if (info.si_code != CLD_STOPPED)
+ ksft_exit_fail_msg(
+ "%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
+ test_name, info.si_code, parent_tid, pidfd,
+ strerror(errno));
+
+ if (info.si_pid != parent_tid)
+ ksft_exit_fail_msg(
+ "%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
+ test_name, info.si_pid, parent_tid, pidfd,
+ strerror(errno));
+
+ ret = sys_pidfd_send_signal(pidfd, SIGCONT, NULL, 0);
+ if (ret < 0)
+ ksft_exit_fail_msg(
+ "%s test: failed to send signal to process with pid %d and pidfd %d: %s\n",
+ test_name, parent_tid, pidfd, strerror(errno));
+
+ ret = sys_waitid(P_PIDFD, pidfd, &info, WCONTINUED, NULL);
+ if (ret < 0)
+ ksft_exit_fail_msg(
+ "%s test: failed to wait WCONTINUED on process with pid %d and pidfd %d: %s\n",
+ test_name, parent_tid, pidfd, strerror(errno));
+
+ if (info.si_signo != SIGCHLD)
+ ksft_exit_fail_msg(
+ "%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
+ test_name, info.si_signo, parent_tid, pidfd,
+ strerror(errno));
+
+ if (info.si_code != CLD_CONTINUED)
+ ksft_exit_fail_msg(
+ "%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
+ test_name, info.si_code, parent_tid, pidfd,
+ strerror(errno));
+
+ if (info.si_pid != parent_tid)
+ ksft_exit_fail_msg(
+ "%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
+ test_name, info.si_pid, parent_tid, pidfd,
+ strerror(errno));
+
+ ret = sys_waitid(P_PIDFD, pidfd, &info, WUNTRACED, NULL);
+ if (ret < 0)
+ ksft_exit_fail_msg(
+ "%s test: failed to wait on WUNTRACED process with pid %d and pidfd %d: %s\n",
+ test_name, parent_tid, pidfd, strerror(errno));
+
+ if (info.si_signo != SIGCHLD)
+ ksft_exit_fail_msg(
+ "%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
+ test_name, info.si_signo, parent_tid, pidfd,
+ strerror(errno));
+
+ if (info.si_code != CLD_STOPPED)
+ ksft_exit_fail_msg(
+ "%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
+ test_name, info.si_code, parent_tid, pidfd,
+ strerror(errno));
+
+ if (info.si_pid != parent_tid)
+ ksft_exit_fail_msg(
+ "%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
+ test_name, info.si_pid, parent_tid, pidfd,
+ strerror(errno));
+
+ ret = sys_pidfd_send_signal(pidfd, SIGKILL, NULL, 0);
+ if (ret < 0)
+ ksft_exit_fail_msg(
+ "%s test: failed to send SIGKILL to process with pid %d and pidfd %d: %s\n",
+ test_name, parent_tid, pidfd, strerror(errno));
+
+ ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
+ if (ret < 0)
+ ksft_exit_fail_msg(
+ "%s test: failed to wait on WEXITED process with pid %d and pidfd %d: %s\n",
+ test_name, parent_tid, pidfd, strerror(errno));
+
+ if (info.si_signo != SIGCHLD)
+ ksft_exit_fail_msg(
+ "%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
+ test_name, info.si_signo, parent_tid, pidfd,
+ strerror(errno));
+
+ if (info.si_code != CLD_KILLED)
+ ksft_exit_fail_msg(
+ "%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
+ test_name, info.si_code, parent_tid, pidfd,
+ strerror(errno));
+
+ if (info.si_pid != parent_tid)
+ ksft_exit_fail_msg(
+ "%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
+ test_name, info.si_pid, parent_tid, pidfd,
+ strerror(errno));
+
+ close(pidfd);
+
+ ksft_test_result_pass("%s test: Passed\n", test_name);
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ ksft_print_header();
+ ksft_set_plan(2);
+
+ test_pidfd_wait_simple();
+ test_pidfd_wait_states();
+
+ return ksft_exit_pass();
+}
diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile
index b3ad909aefbc..644770c3b754 100644
--- a/tools/testing/selftests/powerpc/Makefile
+++ b/tools/testing/selftests/powerpc/Makefile
@@ -26,6 +26,7 @@ SUB_DIRS = alignment \
switch_endian \
syscalls \
tm \
+ eeh \
vphn \
math \
ptrace \
diff --git a/tools/testing/selftests/powerpc/alignment/Makefile b/tools/testing/selftests/powerpc/alignment/Makefile
index d056486f49de..93e9af37449d 100644
--- a/tools/testing/selftests/powerpc/alignment/Makefile
+++ b/tools/testing/selftests/powerpc/alignment/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
TEST_GEN_PROGS := copy_first_unaligned alignment_handler
top_srcdir = ../../../../..
diff --git a/tools/testing/selftests/powerpc/alignment/alignment_handler.c b/tools/testing/selftests/powerpc/alignment/alignment_handler.c
index 169a8b9719fb..0453c50c949c 100644
--- a/tools/testing/selftests/powerpc/alignment/alignment_handler.c
+++ b/tools/testing/selftests/powerpc/alignment/alignment_handler.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Test the powerpc alignment handler on POWER8/POWER9
*
* Copyright (C) 2017 IBM Corporation (Michael Neuling, Andrew Donnellan)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
/*
diff --git a/tools/testing/selftests/powerpc/alignment/copy_first_unaligned.c b/tools/testing/selftests/powerpc/alignment/copy_first_unaligned.c
index 5a9589987702..db4e8c680500 100644
--- a/tools/testing/selftests/powerpc/alignment/copy_first_unaligned.c
+++ b/tools/testing/selftests/powerpc/alignment/copy_first_unaligned.c
@@ -1,14 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2016, Chris Smart, IBM Corporation.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* Calls to copy_first which are not 128-byte aligned should be
* caught and sent a SIGBUS.
- *
*/
#include <signal.h>
diff --git a/tools/testing/selftests/powerpc/benchmarks/context_switch.c b/tools/testing/selftests/powerpc/benchmarks/context_switch.c
index 87f1f0252299..a2e8c9da7fa5 100644
--- a/tools/testing/selftests/powerpc/benchmarks/context_switch.c
+++ b/tools/testing/selftests/powerpc/benchmarks/context_switch.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Context switch microbenchmark.
*
* Copyright (C) 2015 Anton Blanchard <anton@au.ibm.com>, IBM
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#define _GNU_SOURCE
diff --git a/tools/testing/selftests/powerpc/benchmarks/futex_bench.c b/tools/testing/selftests/powerpc/benchmarks/futex_bench.c
index d58e4dc50fcd..017057090490 100644
--- a/tools/testing/selftests/powerpc/benchmarks/futex_bench.c
+++ b/tools/testing/selftests/powerpc/benchmarks/futex_bench.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2016, Anton Blanchard, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#define _GNU_SOURCE
diff --git a/tools/testing/selftests/powerpc/benchmarks/gettimeofday.c b/tools/testing/selftests/powerpc/benchmarks/gettimeofday.c
index 3af3c21e8036..6b415683357b 100644
--- a/tools/testing/selftests/powerpc/benchmarks/gettimeofday.c
+++ b/tools/testing/selftests/powerpc/benchmarks/gettimeofday.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2015, Anton Blanchard, IBM Corp.
- * Licensed under GPLv2.
*/
#include <sys/time.h>
diff --git a/tools/testing/selftests/powerpc/benchmarks/mmap_bench.c b/tools/testing/selftests/powerpc/benchmarks/mmap_bench.c
index 033de0560d99..2525adf64342 100644
--- a/tools/testing/selftests/powerpc/benchmarks/mmap_bench.c
+++ b/tools/testing/selftests/powerpc/benchmarks/mmap_bench.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2016, Anton Blanchard, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <stdio.h>
diff --git a/tools/testing/selftests/powerpc/benchmarks/null_syscall.c b/tools/testing/selftests/powerpc/benchmarks/null_syscall.c
index 908de689a902..579f0215c6e7 100644
--- a/tools/testing/selftests/powerpc/benchmarks/null_syscall.c
+++ b/tools/testing/selftests/powerpc/benchmarks/null_syscall.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Test null syscall performance
*
* Copyright (C) 2009-2015 Anton Blanchard, IBM
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#define NR_LOOPS 10000000
diff --git a/tools/testing/selftests/powerpc/cache_shape/cache_shape.c b/tools/testing/selftests/powerpc/cache_shape/cache_shape.c
index 29ec07eba7f9..171b6c9480eb 100644
--- a/tools/testing/selftests/powerpc/cache_shape/cache_shape.c
+++ b/tools/testing/selftests/powerpc/cache_shape/cache_shape.c
@@ -1,10 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2017, Michael Ellerman, IBM Corp.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include <elf.h>
diff --git a/tools/testing/selftests/powerpc/copyloops/.gitignore b/tools/testing/selftests/powerpc/copyloops/.gitignore
index ce12cd0e2967..12ef5b031974 100644
--- a/tools/testing/selftests/powerpc/copyloops/.gitignore
+++ b/tools/testing/selftests/powerpc/copyloops/.gitignore
@@ -1,13 +1,14 @@
copyuser_64_t0
copyuser_64_t1
copyuser_64_t2
-copyuser_power7_t0
-copyuser_power7_t1
+copyuser_p7_t0
+copyuser_p7_t1
memcpy_64_t0
memcpy_64_t1
memcpy_64_t2
-memcpy_power7_t0
-memcpy_power7_t1
+memcpy_p7_t0
+memcpy_p7_t1
copyuser_64_exc_t0
copyuser_64_exc_t1
copyuser_64_exc_t2
+memcpy_mcsafe_64
diff --git a/tools/testing/selftests/powerpc/copyloops/Makefile b/tools/testing/selftests/powerpc/copyloops/Makefile
index 44574f3818b3..0917983a1c78 100644
--- a/tools/testing/selftests/powerpc/copyloops/Makefile
+++ b/tools/testing/selftests/powerpc/copyloops/Makefile
@@ -12,7 +12,7 @@ ASFLAGS = $(CFLAGS) -Wa,-mpower4
TEST_GEN_PROGS := copyuser_64_t0 copyuser_64_t1 copyuser_64_t2 \
copyuser_p7_t0 copyuser_p7_t1 \
memcpy_64_t0 memcpy_64_t1 memcpy_64_t2 \
- memcpy_p7_t0 memcpy_p7_t1 \
+ memcpy_p7_t0 memcpy_p7_t1 memcpy_mcsafe_64 \
copyuser_64_exc_t0 copyuser_64_exc_t1 copyuser_64_exc_t2
EXTRA_SOURCES := validate.c ../harness.c stubs.S
@@ -45,6 +45,11 @@ $(OUTPUT)/memcpy_p7_t%: memcpy_power7.S $(EXTRA_SOURCES)
-D SELFTEST_CASE=$(subst memcpy_p7_t,,$(notdir $@)) \
-o $@ $^
+$(OUTPUT)/memcpy_mcsafe_64: memcpy_mcsafe_64.S $(EXTRA_SOURCES)
+ $(CC) $(CPPFLAGS) $(CFLAGS) \
+ -D COPY_LOOP=test_memcpy_mcsafe \
+ -o $@ $^
+
$(OUTPUT)/copyuser_64_exc_t%: copyuser_64.S exc_validate.c ../harness.c \
copy_tofrom_user_reference.S stubs.S
$(CC) $(CPPFLAGS) $(CFLAGS) \
diff --git a/tools/testing/selftests/powerpc/copyloops/asm/export.h b/tools/testing/selftests/powerpc/copyloops/asm/export.h
index 05c1663c89b0..e6b80d5fbd14 100644
--- a/tools/testing/selftests/powerpc/copyloops/asm/export.h
+++ b/tools/testing/selftests/powerpc/copyloops/asm/export.h
@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 */
#define EXPORT_SYMBOL(x)
+#define EXPORT_SYMBOL_GPL(x)
#define EXPORT_SYMBOL_KASAN(x)
diff --git a/tools/testing/selftests/powerpc/copyloops/memcpy_mcsafe_64.S b/tools/testing/selftests/powerpc/copyloops/memcpy_mcsafe_64.S
new file mode 120000
index 000000000000..f0feef3062f6
--- /dev/null
+++ b/tools/testing/selftests/powerpc/copyloops/memcpy_mcsafe_64.S
@@ -0,0 +1 @@
+../../../../../arch/powerpc/lib/memcpy_mcsafe_64.S \ No newline at end of file
diff --git a/tools/testing/selftests/powerpc/dscr/dscr.h b/tools/testing/selftests/powerpc/dscr/dscr.h
index cdb840bc54f2..13e9b9e28e2c 100644
--- a/tools/testing/selftests/powerpc/dscr/dscr.h
+++ b/tools/testing/selftests/powerpc/dscr/dscr.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* POWER Data Stream Control Register (DSCR)
*
@@ -6,10 +7,6 @@
*
* Copyright 2012, Anton Blanchard, IBM Corporation.
* Copyright 2015, Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
*/
#ifndef _SELFTESTS_POWERPC_DSCR_DSCR_H
#define _SELFTESTS_POWERPC_DSCR_DSCR_H
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_default_test.c b/tools/testing/selftests/powerpc/dscr/dscr_default_test.c
index 9e1a37e93b63..288a4e2ad156 100644
--- a/tools/testing/selftests/powerpc/dscr/dscr_default_test.c
+++ b/tools/testing/selftests/powerpc/dscr/dscr_default_test.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* POWER Data Stream Control Register (DSCR) default test
*
@@ -7,10 +8,6 @@
*
* Copyright 2012, Anton Blanchard, IBM Corporation.
* Copyright 2015, Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
*/
#include "dscr.h"
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_explicit_test.c b/tools/testing/selftests/powerpc/dscr/dscr_explicit_test.c
index ad9c3ec26048..aefcd8d8759b 100644
--- a/tools/testing/selftests/powerpc/dscr/dscr_explicit_test.c
+++ b/tools/testing/selftests/powerpc/dscr/dscr_explicit_test.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* POWER Data Stream Control Register (DSCR) explicit test
*
@@ -13,10 +14,6 @@
*
* Copyright 2012, Anton Blanchard, IBM Corporation.
* Copyright 2015, Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
*/
#include "dscr.h"
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_inherit_exec_test.c b/tools/testing/selftests/powerpc/dscr/dscr_inherit_exec_test.c
index c8c240accc0c..7c1cb46397c6 100644
--- a/tools/testing/selftests/powerpc/dscr/dscr_inherit_exec_test.c
+++ b/tools/testing/selftests/powerpc/dscr/dscr_inherit_exec_test.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* POWER Data Stream Control Register (DSCR) fork exec test
*
@@ -12,10 +13,6 @@
*
* Copyright 2012, Anton Blanchard, IBM Corporation.
* Copyright 2015, Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
*/
#include "dscr.h"
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_inherit_test.c b/tools/testing/selftests/powerpc/dscr/dscr_inherit_test.c
index 3e5a6d195e9a..04297a69ab59 100644
--- a/tools/testing/selftests/powerpc/dscr/dscr_inherit_test.c
+++ b/tools/testing/selftests/powerpc/dscr/dscr_inherit_test.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* POWER Data Stream Control Register (DSCR) fork test
*
@@ -13,10 +14,6 @@
*
* Copyright 2012, Anton Blanchard, IBM Corporation.
* Copyright 2015, Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
*/
#include "dscr.h"
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c b/tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c
index 1899bd85121f..02f6b4efde14 100644
--- a/tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c
+++ b/tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* POWER Data Stream Control Register (DSCR) sysfs interface test
*
@@ -6,10 +7,6 @@
* well verified from their sysfs interfaces.
*
* Copyright 2015, Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
*/
#include "dscr.h"
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_sysfs_thread_test.c b/tools/testing/selftests/powerpc/dscr/dscr_sysfs_thread_test.c
index ad97b592eccc..37be2c25f277 100644
--- a/tools/testing/selftests/powerpc/dscr/dscr_sysfs_thread_test.c
+++ b/tools/testing/selftests/powerpc/dscr/dscr_sysfs_thread_test.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* POWER Data Stream Control Register (DSCR) sysfs thread test
*
@@ -7,10 +8,6 @@
* executing on individual CPUs on the system.
*
* Copyright 2015, Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
*/
#define _GNU_SOURCE
#include "dscr.h"
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_user_test.c b/tools/testing/selftests/powerpc/dscr/dscr_user_test.c
index 77d16b5e7dca..eaf785d11eed 100644
--- a/tools/testing/selftests/powerpc/dscr/dscr_user_test.c
+++ b/tools/testing/selftests/powerpc/dscr/dscr_user_test.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* POWER Data Stream Control Register (DSCR) SPR test
*
@@ -14,10 +15,6 @@
*
* Copyright 2013, Anton Blanchard, IBM Corporation.
* Copyright 2015, Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
*/
#include "dscr.h"
diff --git a/tools/testing/selftests/powerpc/eeh/Makefile b/tools/testing/selftests/powerpc/eeh/Makefile
new file mode 100644
index 000000000000..b397babd569b
--- /dev/null
+++ b/tools/testing/selftests/powerpc/eeh/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+noarg:
+ $(MAKE) -C ../
+
+TEST_PROGS := eeh-basic.sh
+TEST_FILES := eeh-functions.sh
+
+top_srcdir = ../../../../..
+include ../../lib.mk
diff --git a/tools/testing/selftests/powerpc/eeh/eeh-basic.sh b/tools/testing/selftests/powerpc/eeh/eeh-basic.sh
new file mode 100755
index 000000000000..f988d2f42e8f
--- /dev/null
+++ b/tools/testing/selftests/powerpc/eeh/eeh-basic.sh
@@ -0,0 +1,82 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
+
+. ./eeh-functions.sh
+
+if ! eeh_supported ; then
+ echo "EEH not supported on this system, skipping"
+ exit 0;
+fi
+
+if [ ! -e "/sys/kernel/debug/powerpc/eeh_dev_check" ] && \
+ [ ! -e "/sys/kernel/debug/powerpc/eeh_dev_break" ] ; then
+ echo "debugfs EEH testing files are missing. Is debugfs mounted?"
+ exit 1;
+fi
+
+pre_lspci=`mktemp`
+lspci > $pre_lspci
+
+# Bump the max freeze count to something absurd so we don't
+# trip over it while breaking things.
+echo 5000 > /sys/kernel/debug/powerpc/eeh_max_freezes
+
+# record the devices that we break in here. Assuming everything
+# goes to plan we should get them back once the recover process
+# is finished.
+devices=""
+
+# Build up a list of candidate devices.
+for dev in `ls -1 /sys/bus/pci/devices/ | grep '\.0$'` ; do
+ # skip bridges since we can't recover them (yet...)
+ if [ -e "/sys/bus/pci/devices/$dev/pci_bus" ] ; then
+ echo "$dev, Skipped: bridge"
+ continue;
+ fi
+
+ # Skip VFs for now since we don't have a reliable way
+ # to break them.
+ if [ -e "/sys/bus/pci/devices/$dev/physfn" ] ; then
+ echo "$dev, Skipped: virtfn"
+ continue;
+ fi
+
+ # Don't inject errosr into an already-frozen PE. This happens with
+ # PEs that contain multiple PCI devices (e.g. multi-function cards)
+ # and injecting new errors during the recovery process will probably
+ # result in the recovery failing and the device being marked as
+ # failed.
+ if ! pe_ok $dev ; then
+ echo "$dev, Skipped: Bad initial PE state"
+ continue;
+ fi
+
+ echo "$dev, Added"
+
+ # Add to this list of device to check
+ devices="$devices $dev"
+done
+
+dev_count="$(echo $devices | wc -w)"
+echo "Found ${dev_count} breakable devices..."
+
+failed=0
+for dev in $devices ; do
+ echo "Breaking $dev..."
+
+ if ! pe_ok $dev ; then
+ echo "Skipping $dev, Initial PE state is not ok"
+ failed="$((failed + 1))"
+ continue;
+ fi
+
+ if ! eeh_one_dev $dev ; then
+ failed="$((failed + 1))"
+ fi
+done
+
+echo "$failed devices failed to recover ($dev_count tested)"
+lspci | diff -u $pre_lspci -
+rm -f $pre_lspci
+
+exit $failed
diff --git a/tools/testing/selftests/powerpc/eeh/eeh-functions.sh b/tools/testing/selftests/powerpc/eeh/eeh-functions.sh
new file mode 100755
index 000000000000..26112ab5cdf4
--- /dev/null
+++ b/tools/testing/selftests/powerpc/eeh/eeh-functions.sh
@@ -0,0 +1,76 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
+
+pe_ok() {
+ local dev="$1"
+ local path="/sys/bus/pci/devices/$dev/eeh_pe_state"
+
+ if ! [ -e "$path" ] ; then
+ return 1;
+ fi
+
+ local fw_state="$(cut -d' ' -f1 < $path)"
+ local sw_state="$(cut -d' ' -f2 < $path)"
+
+ # If EEH_PE_ISOLATED or EEH_PE_RECOVERING are set then the PE is in an
+ # error state or being recovered. Either way, not ok.
+ if [ "$((sw_state & 0x3))" -ne 0 ] ; then
+ return 1
+ fi
+
+ # A functioning PE should have the EEH_STATE_MMIO_ACTIVE and
+ # EEH_STATE_DMA_ACTIVE flags set. For some goddamn stupid reason
+ # the platform backends set these when the PE is in reset. The
+ # RECOVERING check above should stop any false positives though.
+ if [ "$((fw_state & 0x18))" -ne "$((0x18))" ] ; then
+ return 1
+ fi
+
+ return 0;
+}
+
+eeh_supported() {
+ test -e /proc/powerpc/eeh && \
+ grep -q 'EEH Subsystem is enabled' /proc/powerpc/eeh
+}
+
+eeh_one_dev() {
+ local dev="$1"
+
+ # Using this function from the command line is sometimes useful for
+ # testing so check that the argument is a well-formed sysfs device
+ # name.
+ if ! test -e /sys/bus/pci/devices/$dev/ ; then
+ echo "Error: '$dev' must be a sysfs device name (DDDD:BB:DD.F)"
+ return 1;
+ fi
+
+ # Break it
+ echo $dev >/sys/kernel/debug/powerpc/eeh_dev_break
+
+ # Force an EEH device check. If the kernel has already
+ # noticed the EEH (due to a driver poll or whatever), this
+ # is a no-op.
+ echo $dev >/sys/kernel/debug/powerpc/eeh_dev_check
+
+ # Enforce a 30s timeout for recovery. Even the IPR, which is infamously
+ # slow to reset, should recover within 30s.
+ max_wait=30
+
+ for i in `seq 0 ${max_wait}` ; do
+ if pe_ok $dev ; then
+ break;
+ fi
+ echo "$dev, waited $i/${max_wait}"
+ sleep 1
+ done
+
+ if ! pe_ok $dev ; then
+ echo "$dev, Failed to recover!"
+ return 1;
+ fi
+
+ echo "$dev, Recovered after $i seconds"
+ return 0;
+}
+
diff --git a/tools/testing/selftests/powerpc/harness.c b/tools/testing/selftests/powerpc/harness.c
index ba89353abfcc..0ad4f12b3d43 100644
--- a/tools/testing/selftests/powerpc/harness.c
+++ b/tools/testing/selftests/powerpc/harness.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2013, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <errno.h>
diff --git a/tools/testing/selftests/powerpc/include/fpu_asm.h b/tools/testing/selftests/powerpc/include/fpu_asm.h
index 6a387d255e27..58ac2ce33505 100644
--- a/tools/testing/selftests/powerpc/include/fpu_asm.h
+++ b/tools/testing/selftests/powerpc/include/fpu_asm.h
@@ -1,10 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2016, Cyril Bur, IBM Corp.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#ifndef _SELFTESTS_POWERPC_FPU_ASM_H
diff --git a/tools/testing/selftests/powerpc/include/gpr_asm.h b/tools/testing/selftests/powerpc/include/gpr_asm.h
index f6f38852d3a0..5db74f5c6131 100644
--- a/tools/testing/selftests/powerpc/include/gpr_asm.h
+++ b/tools/testing/selftests/powerpc/include/gpr_asm.h
@@ -1,10 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2016, Cyril Bur, IBM Corp.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#ifndef _SELFTESTS_POWERPC_GPR_ASM_H
diff --git a/tools/testing/selftests/powerpc/include/reg.h b/tools/testing/selftests/powerpc/include/reg.h
index 1e797ae396ee..022c5076b2c5 100644
--- a/tools/testing/selftests/powerpc/include/reg.h
+++ b/tools/testing/selftests/powerpc/include/reg.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#ifndef _SELFTESTS_POWERPC_REG_H
diff --git a/tools/testing/selftests/powerpc/include/subunit.h b/tools/testing/selftests/powerpc/include/subunit.h
index 9c6c4e901ab6..068d55fdf80f 100644
--- a/tools/testing/selftests/powerpc/include/subunit.h
+++ b/tools/testing/selftests/powerpc/include/subunit.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2013, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#ifndef _SELFTESTS_POWERPC_SUBUNIT_H
diff --git a/tools/testing/selftests/powerpc/include/utils.h b/tools/testing/selftests/powerpc/include/utils.h
index 7636bf45d5d5..0e2b2e6284ac 100644
--- a/tools/testing/selftests/powerpc/include/utils.h
+++ b/tools/testing/selftests/powerpc/include/utils.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2013, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#ifndef _SELFTESTS_POWERPC_UTILS_H
diff --git a/tools/testing/selftests/powerpc/include/vmx_asm.h b/tools/testing/selftests/powerpc/include/vmx_asm.h
index 2eaaeca9cf1d..ad9fb1b4069d 100644
--- a/tools/testing/selftests/powerpc/include/vmx_asm.h
+++ b/tools/testing/selftests/powerpc/include/vmx_asm.h
@@ -1,10 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2015, Cyril Bur, IBM Corp.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include "basic_asm.h"
diff --git a/tools/testing/selftests/powerpc/include/vsx_asm.h b/tools/testing/selftests/powerpc/include/vsx_asm.h
index 54064ced9e95..434ca2f9bfae 100644
--- a/tools/testing/selftests/powerpc/include/vsx_asm.h
+++ b/tools/testing/selftests/powerpc/include/vsx_asm.h
@@ -1,10 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2015, Cyril Bur, IBM Corp.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include "basic_asm.h"
diff --git a/tools/testing/selftests/powerpc/lib/reg.S b/tools/testing/selftests/powerpc/lib/reg.S
index 0dc44f0da065..9304ea7d59b9 100644
--- a/tools/testing/selftests/powerpc/lib/reg.S
+++ b/tools/testing/selftests/powerpc/lib/reg.S
@@ -1,12 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* test helper assembly functions
*
* Copyright (C) 2016 Simon Guo, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include <ppc-asm.h>
#include "reg.h"
diff --git a/tools/testing/selftests/powerpc/math/fpu_asm.S b/tools/testing/selftests/powerpc/math/fpu_asm.S
index 8a04bb117b69..9dc0c158f871 100644
--- a/tools/testing/selftests/powerpc/math/fpu_asm.S
+++ b/tools/testing/selftests/powerpc/math/fpu_asm.S
@@ -1,10 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2015, Cyril Bur, IBM Corp.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include "basic_asm.h"
diff --git a/tools/testing/selftests/powerpc/math/fpu_preempt.c b/tools/testing/selftests/powerpc/math/fpu_preempt.c
index 0f85b79d883d..5235bdc8c0b1 100644
--- a/tools/testing/selftests/powerpc/math/fpu_preempt.c
+++ b/tools/testing/selftests/powerpc/math/fpu_preempt.c
@@ -1,11 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2015, Cyril Bur, IBM Corp.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* This test attempts to see if the FPU registers change across preemption.
* Two things should be noted here a) The check_fpu function in asm only checks
* the non volatile registers as it is reused from the syscall test b) There is
diff --git a/tools/testing/selftests/powerpc/math/fpu_signal.c b/tools/testing/selftests/powerpc/math/fpu_signal.c
index 888aa51b4204..7b1addd50420 100644
--- a/tools/testing/selftests/powerpc/math/fpu_signal.c
+++ b/tools/testing/selftests/powerpc/math/fpu_signal.c
@@ -1,11 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2015, Cyril Bur, IBM Corp.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* This test attempts to see if the FPU registers are correctly reported in a
* signal context. Each worker just spins checking its FPU registers, at some
* point a signal will interrupt it and C code will check the signal context
diff --git a/tools/testing/selftests/powerpc/math/fpu_syscall.c b/tools/testing/selftests/powerpc/math/fpu_syscall.c
index 949e6721256d..694f225c7e45 100644
--- a/tools/testing/selftests/powerpc/math/fpu_syscall.c
+++ b/tools/testing/selftests/powerpc/math/fpu_syscall.c
@@ -1,11 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2015, Cyril Bur, IBM Corp.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* This test attempts to see if the FPU registers change across a syscall (fork).
*/
diff --git a/tools/testing/selftests/powerpc/math/vmx_asm.S b/tools/testing/selftests/powerpc/math/vmx_asm.S
index cb1e5ae1be99..11b0704c597d 100644
--- a/tools/testing/selftests/powerpc/math/vmx_asm.S
+++ b/tools/testing/selftests/powerpc/math/vmx_asm.S
@@ -1,10 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2015, Cyril Bur, IBM Corp.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include "basic_asm.h"
diff --git a/tools/testing/selftests/powerpc/math/vmx_preempt.c b/tools/testing/selftests/powerpc/math/vmx_preempt.c
index 9ef376c55b13..2e059f154e77 100644
--- a/tools/testing/selftests/powerpc/math/vmx_preempt.c
+++ b/tools/testing/selftests/powerpc/math/vmx_preempt.c
@@ -1,11 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2015, Cyril Bur, IBM Corp.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* This test attempts to see if the VMX registers change across preemption.
* Two things should be noted here a) The check_vmx function in asm only checks
* the non volatile registers as it is reused from the syscall test b) There is
diff --git a/tools/testing/selftests/powerpc/math/vmx_signal.c b/tools/testing/selftests/powerpc/math/vmx_signal.c
index 671d7533a557..785a48e0976f 100644
--- a/tools/testing/selftests/powerpc/math/vmx_signal.c
+++ b/tools/testing/selftests/powerpc/math/vmx_signal.c
@@ -1,11 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2015, Cyril Bur, IBM Corp.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* This test attempts to see if the VMX registers are correctly reported in a
* signal context. Each worker just spins checking its VMX registers, at some
* point a signal will interrupt it and C code will check the signal context
diff --git a/tools/testing/selftests/powerpc/math/vmx_syscall.c b/tools/testing/selftests/powerpc/math/vmx_syscall.c
index a017918ee1ca..9ee293cc868e 100644
--- a/tools/testing/selftests/powerpc/math/vmx_syscall.c
+++ b/tools/testing/selftests/powerpc/math/vmx_syscall.c
@@ -1,11 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2015, Cyril Bur, IBM Corp.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* This test attempts to see if the VMX registers change across a syscall (fork).
*/
diff --git a/tools/testing/selftests/powerpc/math/vsx_asm.S b/tools/testing/selftests/powerpc/math/vsx_asm.S
index 8f431f6abc49..ffc165d984cc 100644
--- a/tools/testing/selftests/powerpc/math/vsx_asm.S
+++ b/tools/testing/selftests/powerpc/math/vsx_asm.S
@@ -1,10 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2015, Cyril Bur, IBM Corp.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include "basic_asm.h"
diff --git a/tools/testing/selftests/powerpc/math/vsx_preempt.c b/tools/testing/selftests/powerpc/math/vsx_preempt.c
index 6387f03a0a6a..63de9c6e2cd3 100644
--- a/tools/testing/selftests/powerpc/math/vsx_preempt.c
+++ b/tools/testing/selftests/powerpc/math/vsx_preempt.c
@@ -1,11 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2015, Cyril Bur, IBM Corp.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* This test attempts to see if the VSX registers change across preemption.
* There is no way to be sure preemption happened so this test just
* uses many threads and a long wait. As such, a successful test
diff --git a/tools/testing/selftests/powerpc/mm/.gitignore b/tools/testing/selftests/powerpc/mm/.gitignore
index ba919308fe30..7101ffd08d66 100644
--- a/tools/testing/selftests/powerpc/mm/.gitignore
+++ b/tools/testing/selftests/powerpc/mm/.gitignore
@@ -3,4 +3,5 @@ subpage_prot
tempfile
prot_sao
segv_errors
-wild_bctr \ No newline at end of file
+wild_bctr
+large_vm_fork_separation
diff --git a/tools/testing/selftests/powerpc/mm/Makefile b/tools/testing/selftests/powerpc/mm/Makefile
index 43d68420e363..f1fbc15800c4 100644
--- a/tools/testing/selftests/powerpc/mm/Makefile
+++ b/tools/testing/selftests/powerpc/mm/Makefile
@@ -2,7 +2,8 @@
noarg:
$(MAKE) -C ../
-TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao segv_errors wild_bctr
+TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao segv_errors wild_bctr \
+ large_vm_fork_separation
TEST_GEN_FILES := tempfile
top_srcdir = ../../../../..
@@ -13,6 +14,7 @@ $(TEST_GEN_PROGS): ../harness.c
$(OUTPUT)/prot_sao: ../utils.c
$(OUTPUT)/wild_bctr: CFLAGS += -m64
+$(OUTPUT)/large_vm_fork_separation: CFLAGS += -m64
$(OUTPUT)/tempfile:
dd if=/dev/zero of=$@ bs=64k count=1
diff --git a/tools/testing/selftests/powerpc/mm/large_vm_fork_separation.c b/tools/testing/selftests/powerpc/mm/large_vm_fork_separation.c
new file mode 100644
index 000000000000..2363a7f3ab0d
--- /dev/null
+++ b/tools/testing/selftests/powerpc/mm/large_vm_fork_separation.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright 2019, Michael Ellerman, IBM Corp.
+//
+// Test that allocating memory beyond the memory limit and then forking is
+// handled correctly, ie. the child is able to access the mappings beyond the
+// memory limit and the child's writes are not visible to the parent.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+
+#ifndef MAP_FIXED_NOREPLACE
+#define MAP_FIXED_NOREPLACE MAP_FIXED // "Should be safe" above 512TB
+#endif
+
+
+static int test(void)
+{
+ int p2c[2], c2p[2], rc, status, c, *p;
+ unsigned long page_size;
+ pid_t pid;
+
+ page_size = sysconf(_SC_PAGESIZE);
+ SKIP_IF(page_size != 65536);
+
+ // Create a mapping at 512TB to allocate an extended_id
+ p = mmap((void *)(512ul << 40), page_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0);
+ if (p == MAP_FAILED) {
+ perror("mmap");
+ printf("Error: couldn't mmap(), confirm kernel has 4TB support?\n");
+ return 1;
+ }
+
+ printf("parent writing %p = 1\n", p);
+ *p = 1;
+
+ FAIL_IF(pipe(p2c) == -1 || pipe(c2p) == -1);
+
+ pid = fork();
+ if (pid == 0) {
+ FAIL_IF(read(p2c[0], &c, 1) != 1);
+
+ pid = getpid();
+ printf("child writing %p = %d\n", p, pid);
+ *p = pid;
+
+ FAIL_IF(write(c2p[1], &c, 1) != 1);
+ FAIL_IF(read(p2c[0], &c, 1) != 1);
+ exit(0);
+ }
+
+ c = 0;
+ FAIL_IF(write(p2c[1], &c, 1) != 1);
+ FAIL_IF(read(c2p[0], &c, 1) != 1);
+
+ // Prevent compiler optimisation
+ barrier();
+
+ rc = 0;
+ printf("parent reading %p = %d\n", p, *p);
+ if (*p != 1) {
+ printf("Error: BUG! parent saw child's write! *p = %d\n", *p);
+ rc = 1;
+ }
+
+ FAIL_IF(write(p2c[1], &c, 1) != 1);
+ FAIL_IF(waitpid(pid, &status, 0) == -1);
+ FAIL_IF(!WIFEXITED(status) || WEXITSTATUS(status));
+
+ if (rc == 0)
+ printf("success: test completed OK\n");
+
+ return rc;
+}
+
+int main(void)
+{
+ return test_harness(test, "large_vm_fork_separation");
+}
diff --git a/tools/testing/selftests/powerpc/mm/prot_sao.c b/tools/testing/selftests/powerpc/mm/prot_sao.c
index 611530d43fa9..e2eed65b7735 100644
--- a/tools/testing/selftests/powerpc/mm/prot_sao.c
+++ b/tools/testing/selftests/powerpc/mm/prot_sao.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2016, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <stdio.h>
diff --git a/tools/testing/selftests/powerpc/pmu/count_instructions.c b/tools/testing/selftests/powerpc/pmu/count_instructions.c
index 4622117b24c0..a3984ef1e96a 100644
--- a/tools/testing/selftests/powerpc/pmu/count_instructions.c
+++ b/tools/testing/selftests/powerpc/pmu/count_instructions.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2013, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#define _GNU_SOURCE
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c b/tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c
index 94110b1dcd3d..a2d7b0e3dca9 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <stdbool.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/busy_loop.S b/tools/testing/selftests/powerpc/pmu/ebb/busy_loop.S
index c7e4093f1cd3..4866a3a76d22 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/busy_loop.S
+++ b/tools/testing/selftests/powerpc/pmu/ebb/busy_loop.S
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <ppc-asm.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c b/tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c
index ac18cf617dd6..ca9aeb0d8272 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <stdio.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c
index f0632e7fdf29..3cd33eb51e5e 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <signal.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c
index 33e56a2342e5..8466ef9d7de8 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <signal.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c
index 7c57a8d79535..bc893813483e 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <stdio.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c
index ecf5ee3283a3..dcd351d20328 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <stdio.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_mmcr2_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_mmcr2_test.c
index c0faba520b35..94c99c12c0f2 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_mmcr2_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_mmcr2_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <stdio.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb.c
index 46681fec549b..dfbc5c3ad52d 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/ebb.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#define _GNU_SOURCE /* For CPU_ZERO etc. */
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb.h b/tools/testing/selftests/powerpc/pmu/ebb/ebb.h
index f87e761f82d0..b5bc2b616075 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/ebb.h
+++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#ifndef _SELFTESTS_POWERPC_PMU_EBB_EBB_H
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S b/tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S
index 14274ea206e5..c170398de91a 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S
+++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <ppc-asm.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c
index 1e7b7fe2396b..8980f054d8d9 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <signal.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c
index a991d2ea8d0a..ca2f7d729155 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <signal.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c
index af20a2b363aa..4d822cb3589c 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <signal.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c b/tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c
index 7762ab26e5ac..6e6dd0bce1f9 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <stdio.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/fixed_instruction_loop.S b/tools/testing/selftests/powerpc/pmu/ebb/fixed_instruction_loop.S
index b866a0581d32..08a7b5f133b9 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/fixed_instruction_loop.S
+++ b/tools/testing/selftests/powerpc/pmu/ebb/fixed_instruction_loop.S
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <ppc-asm.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c b/tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c
index af1b80265076..2b25b55452d9 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <signal.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c b/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c
index 35a3426e341c..eed338b18e11 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#define _GNU_SOURCE
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c b/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c
index 2ed7ad33f7a3..ac3e6e182614 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <sched.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c b/tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c
index 6ff8c8ff27d6..b8242e9d97d2 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <stdio.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c b/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c
index 037cb6154f36..a05c0e18ded6 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <stdbool.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/no_handler_test.c b/tools/testing/selftests/powerpc/pmu/ebb/no_handler_test.c
index 8341d7778d5e..fc5bf4870d8e 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/no_handler_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/no_handler_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <stdio.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c b/tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c
index c5fa64790c22..153ebc92234f 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <sched.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c b/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c
index 30e1ac62e8cb..eadad75ed7e6 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <stdio.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/reg_access_test.c b/tools/testing/selftests/powerpc/pmu/ebb/reg_access_test.c
index f923228bca22..bd1ace9a055d 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/reg_access_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/reg_access_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <stdio.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/task_event_pinned_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/task_event_pinned_vs_ebb_test.c
index 1846f4e84635..0aa2aefd36d4 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/task_event_pinned_vs_ebb_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/task_event_pinned_vs_ebb_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <signal.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/task_event_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/task_event_vs_ebb_test.c
index e3bc6e92a6a5..3e9d95ad9dfe 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/task_event_vs_ebb_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/task_event_vs_ebb_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <signal.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/trace.c b/tools/testing/selftests/powerpc/pmu/ebb/trace.c
index 251e66ab2aa7..0c59f66a6fb2 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/trace.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/trace.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <errno.h>
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/trace.h b/tools/testing/selftests/powerpc/pmu/ebb/trace.h
index 926458e28c8b..7c0fb5d2bdb1 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/trace.h
+++ b/tools/testing/selftests/powerpc/pmu/ebb/trace.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#ifndef _SELFTESTS_POWERPC_PMU_EBB_TRACE_H
diff --git a/tools/testing/selftests/powerpc/pmu/event.c b/tools/testing/selftests/powerpc/pmu/event.c
index 184b36807d48..48e3a413b15d 100644
--- a/tools/testing/selftests/powerpc/pmu/event.c
+++ b/tools/testing/selftests/powerpc/pmu/event.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2013, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#define _GNU_SOURCE
diff --git a/tools/testing/selftests/powerpc/pmu/event.h b/tools/testing/selftests/powerpc/pmu/event.h
index a0ea6b1eef73..302eaab51706 100644
--- a/tools/testing/selftests/powerpc/pmu/event.h
+++ b/tools/testing/selftests/powerpc/pmu/event.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2013, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#ifndef _SELFTESTS_POWERPC_PMU_EVENT_H
diff --git a/tools/testing/selftests/powerpc/pmu/l3_bank_test.c b/tools/testing/selftests/powerpc/pmu/l3_bank_test.c
index 77472f31441e..a96d512a18c4 100644
--- a/tools/testing/selftests/powerpc/pmu/l3_bank_test.c
+++ b/tools/testing/selftests/powerpc/pmu/l3_bank_test.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <stdio.h>
diff --git a/tools/testing/selftests/powerpc/pmu/lib.c b/tools/testing/selftests/powerpc/pmu/lib.c
index 5bf5dd40822b..88690b97b7b9 100644
--- a/tools/testing/selftests/powerpc/pmu/lib.c
+++ b/tools/testing/selftests/powerpc/pmu/lib.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#define _GNU_SOURCE /* For CPU_ZERO etc. */
diff --git a/tools/testing/selftests/powerpc/pmu/lib.h b/tools/testing/selftests/powerpc/pmu/lib.h
index 0213af4ff332..fa12e7d0b4d3 100644
--- a/tools/testing/selftests/powerpc/pmu/lib.h
+++ b/tools/testing/selftests/powerpc/pmu/lib.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#ifndef __SELFTESTS_POWERPC_PMU_LIB_H
diff --git a/tools/testing/selftests/powerpc/pmu/loop.S b/tools/testing/selftests/powerpc/pmu/loop.S
index 20c1f0876c47..8cc9b5e2c9de 100644
--- a/tools/testing/selftests/powerpc/pmu/loop.S
+++ b/tools/testing/selftests/powerpc/pmu/loop.S
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2013, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#include <ppc-asm.h>
diff --git a/tools/testing/selftests/powerpc/pmu/per_event_excludes.c b/tools/testing/selftests/powerpc/pmu/per_event_excludes.c
index fddbbc9cae2f..2756fe2efdc5 100644
--- a/tools/testing/selftests/powerpc/pmu/per_event_excludes.c
+++ b/tools/testing/selftests/powerpc/pmu/per_event_excludes.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#define _GNU_SOURCE
diff --git a/tools/testing/selftests/powerpc/primitives/Makefile b/tools/testing/selftests/powerpc/primitives/Makefile
index ea2b7bd09e36..9b9491a63213 100644
--- a/tools/testing/selftests/powerpc/primitives/Makefile
+++ b/tools/testing/selftests/powerpc/primitives/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
CFLAGS += -I$(CURDIR)
TEST_GEN_PROGS := load_unaligned_zeropad
diff --git a/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c b/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c
index ee1e9ca22f0d..1439c8c7ff38 100644
--- a/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c
+++ b/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Userspace test harness for load_unaligned_zeropad. Creates two
* pages and uses mprotect to prevent access to the second page and
@@ -8,11 +9,6 @@
* performed while access to the second page is enabled via mprotect.
*
* Copyright (C) 2014 Anton Blanchard <anton@au.ibm.com>, IBM
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include <stdlib.h>
diff --git a/tools/testing/selftests/powerpc/ptrace/.gitignore b/tools/testing/selftests/powerpc/ptrace/.gitignore
index 07ec449a2767..dce19f221c46 100644
--- a/tools/testing/selftests/powerpc/ptrace/.gitignore
+++ b/tools/testing/selftests/powerpc/ptrace/.gitignore
@@ -10,3 +10,6 @@ ptrace-tm-spd-vsx
ptrace-tm-spr
ptrace-hwbreak
perf-hwbreak
+core-pkey
+ptrace-pkey
+ptrace-syscall
diff --git a/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c b/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c
index 60df0b5e628a..200337daec42 100644
--- a/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c
+++ b/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* perf events self profiling example test case for hw breakpoints.
*
@@ -14,11 +15,6 @@
* http://ozlabs.org/~anton/junkcode/perf_events_example1.c
*
* Copyright (C) 2018 Michael Neuling, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include <unistd.h>
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c
index ca29fafeed5d..17cd480c8780 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Ptrace test for GPR/FPR registers
*
* Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include "ptrace.h"
#include "ptrace-gpr.h"
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.h b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.h
index e30fef63824c..c5cd53181e2e 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.h
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.h
@@ -1,10 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#define GPR_1 1
#define GPR_2 2
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tar.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tar.c
index f9b5069db89b..58cb1a860cc9 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-tar.c
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tar.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Ptrace test for TAR, PPR, DSCR registers
*
* Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include "ptrace.h"
#include "ptrace-tar.h"
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tar.h b/tools/testing/selftests/powerpc/ptrace/ptrace-tar.h
index aed0aac716d2..d6a4c0aab73d 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-tar.h
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tar.h
@@ -1,10 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#define TAR_1 10
#define TAR_2 20
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c
index a08a91594dbe..82f7bdc2e5e6 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Ptrace test for GPR/FPR registers in TM context
*
* Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include "ptrace.h"
#include "ptrace-gpr.h"
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-gpr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-gpr.c
index dbdffa2e2c82..ad65be6e8e85 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-gpr.c
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-gpr.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Ptrace test for GPR/FPR registers in TM Suspend context
*
* Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include "ptrace.h"
#include "ptrace-gpr.h"
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c
index f47174746231..25e23e73c72e 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Ptrace test for TAR, PPR, DSCR registers in the TM Suspend context
*
* Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include "ptrace.h"
#include "tm.h"
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c
index 18a685bf6a09..f603fe5a445b 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Ptrace test for VMX/VSX registers in the TM Suspend context
*
* Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include "ptrace.h"
#include "tm.h"
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c
index ba04999254e3..068bfed2e606 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Ptrace test TM SPR registers
*
* Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include "ptrace.h"
#include "tm.h"
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c
index f70023b25e6e..e0d37f07bdeb 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Ptrace test for TAR, PPR, DSCR registers in the TM context
*
* Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include "ptrace.h"
#include "tm.h"
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c
index dfba80058977..8027457b97b7 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Ptrace test for VMX/VSX registers in the TM context
*
* Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include "ptrace.h"
#include "tm.h"
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c b/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c
index 04084ee7d27b..c4fe0e893306 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Ptrace test for VMX/VSX registers
*
* Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include "ptrace.h"
#include "ptrace-vsx.h"
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.h b/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.h
index f4e4b427c9d9..6633485210b6 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.h
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.h
@@ -1,10 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#define VEC_MAX 128
#define VSX_MAX 32
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace.h b/tools/testing/selftests/powerpc/ptrace/ptrace.h
index 34201cfa8335..5181ad9b4b6c 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace.h
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace.h
@@ -1,12 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Ptrace interface test helper functions
*
* Copyright (C) 2015 Anshuman Khandual, IBM Corporation.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include <inttypes.h>
#include <unistd.h>
diff --git a/tools/testing/selftests/powerpc/scripts/hmi.sh b/tools/testing/selftests/powerpc/scripts/hmi.sh
index 83fb253ae3bd..dcdb392e8427 100755
--- a/tools/testing/selftests/powerpc/scripts/hmi.sh
+++ b/tools/testing/selftests/powerpc/scripts/hmi.sh
@@ -1,15 +1,8 @@
#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
#
# Copyright 2015, Daniel Axtens, IBM Corporation
#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; version 2 of the License.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
# do we have ./getscom, ./putscom?
diff --git a/tools/testing/selftests/powerpc/security/.gitignore b/tools/testing/selftests/powerpc/security/.gitignore
new file mode 100644
index 000000000000..0b969fba3beb
--- /dev/null
+++ b/tools/testing/selftests/powerpc/security/.gitignore
@@ -0,0 +1 @@
+rfi_flush
diff --git a/tools/testing/selftests/powerpc/signal/signal.S b/tools/testing/selftests/powerpc/signal/signal.S
index 322f2f1fc327..228fba49935d 100644
--- a/tools/testing/selftests/powerpc/signal/signal.S
+++ b/tools/testing/selftests/powerpc/signal/signal.S
@@ -1,10 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2015, Cyril Bur, IBM Corp.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include "basic_asm.h"
diff --git a/tools/testing/selftests/powerpc/signal/signal.c b/tools/testing/selftests/powerpc/signal/signal.c
index e7dedd28b3c2..766e484d984b 100644
--- a/tools/testing/selftests/powerpc/signal/signal.c
+++ b/tools/testing/selftests/powerpc/signal/signal.c
@@ -1,11 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2016, Cyril Bur, IBM Corp.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* Sending one self a signal should always get delivered.
*/
diff --git a/tools/testing/selftests/powerpc/signal/signal_tm.c b/tools/testing/selftests/powerpc/signal/signal_tm.c
index 2e7451a37cc6..5bf2224ef7f2 100644
--- a/tools/testing/selftests/powerpc/signal/signal_tm.c
+++ b/tools/testing/selftests/powerpc/signal/signal_tm.c
@@ -1,11 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2016, Cyril Bur, IBM Corp.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* Sending one self a signal should always get delivered.
*/
diff --git a/tools/testing/selftests/powerpc/stringloops/.gitignore b/tools/testing/selftests/powerpc/stringloops/.gitignore
index 0b43da74ee46..31a17e0ba884 100644
--- a/tools/testing/selftests/powerpc/stringloops/.gitignore
+++ b/tools/testing/selftests/powerpc/stringloops/.gitignore
@@ -1 +1,4 @@
-memcmp
+memcmp_64
+memcmp_32
+strlen
+strlen_32
diff --git a/tools/testing/selftests/powerpc/stringloops/asm/ppc-opcode.h b/tools/testing/selftests/powerpc/stringloops/asm/ppc-opcode.h
index 9de413c0c2cb..3edd1a1d9128 100644
--- a/tools/testing/selftests/powerpc/stringloops/asm/ppc-opcode.h
+++ b/tools/testing/selftests/powerpc/stringloops/asm/ppc-opcode.h
@@ -1,11 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2009 Freescale Semiconductor, Inc.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* provides masks and opcode images for use by code generation, emulation
* and for instructions that older assemblers might not know about
*/
diff --git a/tools/testing/selftests/powerpc/stringloops/asm/ppc_asm.h b/tools/testing/selftests/powerpc/stringloops/asm/ppc_asm.h
index d2c0a911f55e..2b488b78c4f2 100644
--- a/tools/testing/selftests/powerpc/stringloops/asm/ppc_asm.h
+++ b/tools/testing/selftests/powerpc/stringloops/asm/ppc_asm.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PPC_ASM_H
-#define __PPC_ASM_H
+#define _PPC_ASM_H
#include <ppc-asm.h>
#ifndef r1
diff --git a/tools/testing/selftests/powerpc/syscalls/Makefile b/tools/testing/selftests/powerpc/syscalls/Makefile
index 161b8846336f..01b22775ca87 100644
--- a/tools/testing/selftests/powerpc/syscalls/Makefile
+++ b/tools/testing/selftests/powerpc/syscalls/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
TEST_GEN_PROGS := ipc_unmuxed
CFLAGS += -I../../../../../usr/include
diff --git a/tools/testing/selftests/powerpc/syscalls/ipc_unmuxed.c b/tools/testing/selftests/powerpc/syscalls/ipc_unmuxed.c
index 2ac02706f8c8..4c582524aeb3 100644
--- a/tools/testing/selftests/powerpc/syscalls/ipc_unmuxed.c
+++ b/tools/testing/selftests/powerpc/syscalls/ipc_unmuxed.c
@@ -1,11 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2015, Michael Ellerman, IBM Corp.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* This test simply tests that certain syscalls are implemented. It doesn't
* actually exercise their logic in any way.
*/
diff --git a/tools/testing/selftests/powerpc/tm/tm-exec.c b/tools/testing/selftests/powerpc/tm/tm-exec.c
index 3d27fa0ece04..260cfdb97d23 100644
--- a/tools/testing/selftests/powerpc/tm/tm-exec.c
+++ b/tools/testing/selftests/powerpc/tm/tm-exec.c
@@ -1,11 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2016, Cyril Bur, IBM Corp.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
* Syscalls can be performed provided the transactions are suspended.
* The exec() class of syscall is unique as a new process is loaded.
*
diff --git a/tools/testing/selftests/powerpc/tm/tm-fork.c b/tools/testing/selftests/powerpc/tm/tm-fork.c
index 8d48579b7778..6efa5a685a77 100644
--- a/tools/testing/selftests/powerpc/tm/tm-fork.c
+++ b/tools/testing/selftests/powerpc/tm/tm-fork.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2015, Michael Neuling, IBM Corp.
- * Licensed under GPLv2.
*
* Edited: Rashmica Gupta, Nov 2015
*
diff --git a/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-fpu.c b/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-fpu.c
index c760debbd5ad..254f912ad611 100644
--- a/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-fpu.c
+++ b/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-fpu.c
@@ -1,19 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2016, Cyril Bur, IBM Corp.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- *
* Test the kernel's signal frame code.
*
* The kernel sets up two sets of ucontexts if the signal was to be
- * delivered while the thread was in a transaction.
+ * delivered while the thread was in a transaction (referred too as
+ * first and second contexts).
* Expected behaviour is that the checkpointed state is in the user
- * context passed to the signal handler. The speculated state can be
- * accessed with the uc_link pointer.
+ * context passed to the signal handler (first context). The speculated
+ * state can be accessed with the uc_link pointer (second context).
*
* The rationale for this is that if TM unaware code (which linked
* against TM libs) installs a signal handler it will not know of the
@@ -33,17 +29,20 @@
#define MAX_ATTEMPT 500000
-#define NV_FPU_REGS 18
+#define NV_FPU_REGS 18 /* Number of non-volatile FP registers */
+#define FPR14 14 /* First non-volatile FP register to check in f14-31 subset */
long tm_signal_self_context_load(pid_t pid, long *gprs, double *fps, vector int *vms, vector int *vss);
-/* Be sure there are 2x as many as there are NV FPU regs (2x18) */
+/* Test only non-volatile registers, i.e. 18 fpr registers from f14 to f31 */
static double fps[] = {
+ /* First context will be set with these values, i.e. non-speculative */
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ /* Second context will be set with these values, i.e. speculative */
-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18
};
-static sig_atomic_t fail;
+static sig_atomic_t fail, broken;
static void signal_usr1(int signum, siginfo_t *info, void *uc)
{
@@ -51,11 +50,24 @@ static void signal_usr1(int signum, siginfo_t *info, void *uc)
ucontext_t *ucp = uc;
ucontext_t *tm_ucp = ucp->uc_link;
- for (i = 0; i < NV_FPU_REGS && !fail; i++) {
- fail = (ucp->uc_mcontext.fp_regs[i + 14] != fps[i]);
- fail |= (tm_ucp->uc_mcontext.fp_regs[i + 14] != fps[i + NV_FPU_REGS]);
- if (fail)
- printf("Failed on %d FP %g or %g\n", i, ucp->uc_mcontext.fp_regs[i + 14], tm_ucp->uc_mcontext.fp_regs[i + 14]);
+ for (i = 0; i < NV_FPU_REGS; i++) {
+ /* Check first context. Print all mismatches. */
+ fail = (ucp->uc_mcontext.fp_regs[FPR14 + i] != fps[i]);
+ if (fail) {
+ broken = 1;
+ printf("FPR%d (1st context) == %g instead of %g (expected)\n",
+ FPR14 + i, ucp->uc_mcontext.fp_regs[FPR14 + i], fps[i]);
+ }
+ }
+
+ for (i = 0; i < NV_FPU_REGS; i++) {
+ /* Check second context. Print all mismatches. */
+ fail = (tm_ucp->uc_mcontext.fp_regs[FPR14 + i] != fps[NV_FPU_REGS + i]);
+ if (fail) {
+ broken = 1;
+ printf("FPR%d (2nd context) == %g instead of %g (expected)\n",
+ FPR14 + i, tm_ucp->uc_mcontext.fp_regs[FPR14 + i], fps[NV_FPU_REGS + i]);
+ }
}
}
@@ -77,13 +89,19 @@ static int tm_signal_context_chk_fpu()
}
i = 0;
- while (i < MAX_ATTEMPT && !fail) {
+ while (i < MAX_ATTEMPT && !broken) {
+ /*
+ * tm_signal_self_context_load will set both first and second
+ * contexts accordingly to the values passed through non-NULL
+ * array pointers to it, in that case 'fps', and invoke the
+ * signal handler installed for SIGUSR1.
+ */
rc = tm_signal_self_context_load(pid, NULL, fps, NULL, NULL);
FAIL_IF(rc != pid);
i++;
}
- return fail;
+ return (broken);
}
int main(void)
diff --git a/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-gpr.c b/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-gpr.c
index df91330a08ef..0cc680f61828 100644
--- a/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-gpr.c
+++ b/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-gpr.c
@@ -1,19 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2016, Cyril Bur, IBM Corp.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- *
* Test the kernel's signal frame code.
*
* The kernel sets up two sets of ucontexts if the signal was to be
- * delivered while the thread was in a transaction.
+ * delivered while the thread was in a transaction (referred too as
+ * first and second contexts).
* Expected behaviour is that the checkpointed state is in the user
- * context passed to the signal handler. The speculated state can be
- * accessed with the uc_link pointer.
+ * context passed to the signal handler (first context). The speculated
+ * state can be accessed with the uc_link pointer (second context).
*
* The rationale for this is that if TM unaware code (which linked
* against TM libs) installs a signal handler it will not know of the
@@ -33,14 +29,22 @@
#define MAX_ATTEMPT 500000
-#define NV_GPR_REGS 18
+#define NV_GPR_REGS 18 /* Number of non-volatile GPR registers */
+#define R14 14 /* First non-volatile register to check in r14-r31 subset */
long tm_signal_self_context_load(pid_t pid, long *gprs, double *fps, vector int *vms, vector int *vss);
-static sig_atomic_t fail;
+static sig_atomic_t fail, broken;
-static long gps[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
- -1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18};
+/* Test only non-volatile general purpose registers, i.e. r14-r31 */
+static long gprs[] = {
+ /* First context will be set with these values, i.e. non-speculative */
+ /* R14, R15, ... */
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ /* Second context will be set with these values, i.e. speculative */
+ /* R14, R15, ... */
+ -1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18
+};
static void signal_usr1(int signum, siginfo_t *info, void *uc)
{
@@ -48,12 +52,24 @@ static void signal_usr1(int signum, siginfo_t *info, void *uc)
ucontext_t *ucp = uc;
ucontext_t *tm_ucp = ucp->uc_link;
- for (i = 0; i < NV_GPR_REGS && !fail; i++) {
- fail = (ucp->uc_mcontext.gp_regs[i + 14] != gps[i]);
- fail |= (tm_ucp->uc_mcontext.gp_regs[i + 14] != gps[i + NV_GPR_REGS]);
- if (fail)
- printf("Failed on %d GPR %lu or %lu\n", i,
- ucp->uc_mcontext.gp_regs[i + 14], tm_ucp->uc_mcontext.gp_regs[i + 14]);
+ /* Check first context. Print all mismatches. */
+ for (i = 0; i < NV_GPR_REGS; i++) {
+ fail = (ucp->uc_mcontext.gp_regs[R14 + i] != gprs[i]);
+ if (fail) {
+ broken = 1;
+ printf("GPR%d (1st context) == %lu instead of %lu (expected)\n",
+ R14 + i, ucp->uc_mcontext.gp_regs[R14 + i], gprs[i]);
+ }
+ }
+
+ /* Check second context. Print all mismatches. */
+ for (i = 0; i < NV_GPR_REGS; i++) {
+ fail = (tm_ucp->uc_mcontext.gp_regs[R14 + i] != gprs[NV_GPR_REGS + i]);
+ if (fail) {
+ broken = 1;
+ printf("GPR%d (2nd context) == %lu instead of %lu (expected)\n",
+ R14 + i, tm_ucp->uc_mcontext.gp_regs[R14 + i], gprs[NV_GPR_REGS + i]);
+ }
}
}
@@ -75,13 +91,19 @@ static int tm_signal_context_chk_gpr()
}
i = 0;
- while (i < MAX_ATTEMPT && !fail) {
- rc = tm_signal_self_context_load(pid, gps, NULL, NULL, NULL);
+ while (i < MAX_ATTEMPT && !broken) {
+ /*
+ * tm_signal_self_context_load will set both first and second
+ * contexts accordingly to the values passed through non-NULL
+ * array pointers to it, in that case 'gprs', and invoke the
+ * signal handler installed for SIGUSR1.
+ */
+ rc = tm_signal_self_context_load(pid, gprs, NULL, NULL, NULL);
FAIL_IF(rc != pid);
i++;
}
- return fail;
+ return broken;
}
int main(void)
diff --git a/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-vmx.c b/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-vmx.c
index f0ee55fd5185..b6d52730a0d8 100644
--- a/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-vmx.c
+++ b/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-vmx.c
@@ -1,19 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2016, Cyril Bur, IBM Corp.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- *
* Test the kernel's signal frame code.
*
* The kernel sets up two sets of ucontexts if the signal was to be
- * delivered while the thread was in a transaction.
+ * delivered while the thread was in a transaction (referred too as
+ * first and second contexts).
* Expected behaviour is that the checkpointed state is in the user
- * context passed to the signal handler. The speculated state can be
- * accessed with the uc_link pointer.
+ * context passed to the signal handler (first context). The speculated
+ * state can be accessed with the uc_link pointer (second context).
*
* The rationale for this is that if TM unaware code (which linked
* against TM libs) installs a signal handler it will not know of the
@@ -34,18 +30,24 @@
#define MAX_ATTEMPT 500000
-#define NV_VMX_REGS 12
+#define NV_VMX_REGS 12 /* Number of non-volatile VMX registers */
+#define VMX20 20 /* First non-volatile register to check in vr20-31 subset */
long tm_signal_self_context_load(pid_t pid, long *gprs, double *fps, vector int *vms, vector int *vss);
-static sig_atomic_t fail;
+static sig_atomic_t fail, broken;
+/* Test only non-volatile registers, i.e. 12 vmx registers from vr20 to vr31 */
vector int vms[] = {
- {1, 2, 3, 4 },{5, 6, 7, 8 },{9, 10,11,12},
+ /* First context will be set with these values, i.e. non-speculative */
+ /* VMX20 , VMX21 , ... */
+ { 1, 2, 3, 4},{ 5, 6, 7, 8},{ 9,10,11,12},
{13,14,15,16},{17,18,19,20},{21,22,23,24},
{25,26,27,28},{29,30,31,32},{33,34,35,36},
{37,38,39,40},{41,42,43,44},{45,46,47,48},
- {-1, -2, -3, -4}, {-5, -6, -7, -8}, {-9, -10,-11,-12},
+ /* Second context will be set with these values, i.e. speculative */
+ /* VMX20 , VMX21 , ... */
+ { -1, -2, -3, -4},{ -5, -6, -7, -8},{ -9,-10,-11,-12},
{-13,-14,-15,-16},{-17,-18,-19,-20},{-21,-22,-23,-24},
{-25,-26,-27,-28},{-29,-30,-31,-32},{-33,-34,-35,-36},
{-37,-38,-39,-40},{-41,-42,-43,-44},{-45,-46,-47,-48}
@@ -53,26 +55,43 @@ vector int vms[] = {
static void signal_usr1(int signum, siginfo_t *info, void *uc)
{
- int i;
+ int i, j;
ucontext_t *ucp = uc;
ucontext_t *tm_ucp = ucp->uc_link;
- for (i = 0; i < NV_VMX_REGS && !fail; i++) {
- fail = memcmp(ucp->uc_mcontext.v_regs->vrregs[i + 20],
+ for (i = 0; i < NV_VMX_REGS; i++) {
+ /* Check first context. Print all mismatches. */
+ fail = memcmp(ucp->uc_mcontext.v_regs->vrregs[VMX20 + i],
&vms[i], sizeof(vector int));
- fail |= memcmp(tm_ucp->uc_mcontext.v_regs->vrregs[i + 20],
- &vms[i + NV_VMX_REGS], sizeof (vector int));
-
if (fail) {
- int j;
+ broken = 1;
+ printf("VMX%d (1st context) == 0x", VMX20 + i);
+ /* Print actual value in first context. */
+ for (j = 0; j < 4; j++)
+ printf("%08x", ucp->uc_mcontext.v_regs->vrregs[VMX20 + i][j]);
+ printf(" instead of 0x");
+ /* Print expected value. */
+ for (j = 0; j < 4; j++)
+ printf("%08x", vms[i][j]);
+ printf(" (expected)\n");
+ }
+ }
- fprintf(stderr, "Failed on %d vmx 0x", i);
+ for (i = 0; i < NV_VMX_REGS; i++) {
+ /* Check second context. Print all mismatches. */
+ fail = memcmp(tm_ucp->uc_mcontext.v_regs->vrregs[VMX20 + i],
+ &vms[NV_VMX_REGS + i], sizeof (vector int));
+ if (fail) {
+ broken = 1;
+ printf("VMX%d (2nd context) == 0x", NV_VMX_REGS + i);
+ /* Print actual value in second context. */
+ for (j = 0; j < 4; j++)
+ printf("%08x", tm_ucp->uc_mcontext.v_regs->vrregs[VMX20 + i][j]);
+ printf(" instead of 0x");
+ /* Print expected value. */
for (j = 0; j < 4; j++)
- fprintf(stderr, "%04x", ucp->uc_mcontext.v_regs->vrregs[i + 20][j]);
- fprintf(stderr, " vs 0x");
- for (j = 0 ; j < 4; j++)
- fprintf(stderr, "%04x", tm_ucp->uc_mcontext.v_regs->vrregs[i + 20][j]);
- fprintf(stderr, "\n");
+ printf("%08x", vms[NV_VMX_REGS + i][j]);
+ printf(" (expected)\n");
}
}
}
@@ -95,13 +114,19 @@ static int tm_signal_context_chk()
}
i = 0;
- while (i < MAX_ATTEMPT && !fail) {
+ while (i < MAX_ATTEMPT && !broken) {
+ /*
+ * tm_signal_self_context_load will set both first and second
+ * contexts accordingly to the values passed through non-NULL
+ * array pointers to it, in that case 'vms', and invoke the
+ * signal handler installed for SIGUSR1.
+ */
rc = tm_signal_self_context_load(pid, NULL, NULL, vms, NULL);
FAIL_IF(rc != pid);
i++;
}
- return fail;
+ return (broken);
}
int main(void)
diff --git a/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-vsx.c b/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-vsx.c
index b99c3d835957..8e25e2072ecd 100644
--- a/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-vsx.c
+++ b/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-vsx.c
@@ -1,19 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2016, Cyril Bur, IBM Corp.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- *
* Test the kernel's signal frame code.
*
* The kernel sets up two sets of ucontexts if the signal was to be
- * delivered while the thread was in a transaction.
+ * delivered while the thread was in a transaction (referred too as
+ * first and second contexts).
* Expected behaviour is that the checkpointed state is in the user
- * context passed to the signal handler. The speculated state can be
- * accessed with the uc_link pointer.
+ * context passed to the signal handler (first context). The speculated
+ * state can be accessed with the uc_link pointer (second context).
*
* The rationale for this is that if TM unaware code (which linked
* against TM libs) installs a signal handler it will not know of the
@@ -34,17 +30,24 @@
#define MAX_ATTEMPT 500000
-#define NV_VSX_REGS 12
+#define NV_VSX_REGS 12 /* Number of VSX registers to check. */
+#define VSX20 20 /* First VSX register to check in vsr20-vsr31 subset */
+#define FPR20 20 /* FPR20 overlaps VSX20 most significant doubleword */
long tm_signal_self_context_load(pid_t pid, long *gprs, double *fps, vector int *vms, vector int *vss);
-static sig_atomic_t fail;
+static sig_atomic_t fail, broken;
-vector int vss[] = {
- {1, 2, 3, 4 },{5, 6, 7, 8 },{9, 10,11,12},
+/* Test only 12 vsx registers from vsr20 to vsr31 */
+vector int vsxs[] = {
+ /* First context will be set with these values, i.e. non-speculative */
+ /* VSX20 , VSX21 , ... */
+ { 1, 2, 3, 4},{ 5, 6, 7, 8},{ 9,10,11,12},
{13,14,15,16},{17,18,19,20},{21,22,23,24},
{25,26,27,28},{29,30,31,32},{33,34,35,36},
{37,38,39,40},{41,42,43,44},{45,46,47,48},
+ /* Second context will be set with these values, i.e. speculative */
+ /* VSX20 , VSX21 , ... */
{-1, -2, -3, -4 },{-5, -6, -7, -8 },{-9, -10,-11,-12},
{-13,-14,-15,-16},{-17,-18,-19,-20},{-21,-22,-23,-24},
{-25,-26,-27,-28},{-29,-30,-31,-32},{-33,-34,-35,-36},
@@ -53,41 +56,91 @@ vector int vss[] = {
static void signal_usr1(int signum, siginfo_t *info, void *uc)
{
- int i;
- uint8_t vsc[sizeof(vector int)];
- uint8_t vst[sizeof(vector int)];
+ int i, j;
+ uint8_t vsx[sizeof(vector int)];
+ uint8_t vsx_tm[sizeof(vector int)];
ucontext_t *ucp = uc;
ucontext_t *tm_ucp = ucp->uc_link;
/*
- * The other half of the VSX regs will be after v_regs.
+ * FP registers and VMX registers overlap the VSX registers.
+ *
+ * FP registers (f0-31) overlap the most significant 64 bits of VSX
+ * registers vsr0-31, whilst VMX registers vr0-31, being 128-bit like
+ * the VSX registers, overlap fully the other half of VSX registers,
+ * i.e. vr0-31 overlaps fully vsr32-63.
+ *
+ * Due to compatibility and historical reasons (VMX/Altivec support
+ * appeared first on the architecture), VMX registers vr0-31 (so VSX
+ * half vsr32-63 too) are stored right after the v_regs pointer, in an
+ * area allocated for 'vmx_reverse' array (please see
+ * arch/powerpc/include/uapi/asm/sigcontext.h for details about the
+ * mcontext_t structure on Power).
+ *
+ * The other VSX half (vsr0-31) is hence stored below vr0-31/vsr32-63
+ * registers, but only the least significant 64 bits of vsr0-31. The
+ * most significant 64 bits of vsr0-31 (f0-31), as it overlaps the FP
+ * registers, is kept in fp_regs.
+ *
+ * v_regs is a 16 byte aligned pointer at the start of vmx_reserve
+ * (vmx_reserve may or may not be 16 aligned) where the v_regs structure
+ * exists, so v_regs points to where vr0-31 / vsr32-63 registers are
+ * fully stored. Since v_regs type is elf_vrregset_t, v_regs + 1
+ * skips all the slots used to store vr0-31 / vsr32-64 and points to
+ * part of one VSX half, i.e. v_regs + 1 points to the least significant
+ * 64 bits of vsr0-31. The other part of this half (the most significant
+ * part of vsr0-31) is stored in fp_regs.
*
- * In short, vmx_reserve array holds everything. v_regs is a 16
- * byte aligned pointer at the start of vmx_reserve (vmx_reserve
- * may or may not be 16 aligned) where the v_regs structure exists.
- * (half of) The VSX regsters are directly after v_regs so the
- * easiest way to find them below.
*/
+ /* Get pointer to least significant doubleword of vsr0-31 */
long *vsx_ptr = (long *)(ucp->uc_mcontext.v_regs + 1);
long *tm_vsx_ptr = (long *)(tm_ucp->uc_mcontext.v_regs + 1);
- for (i = 0; i < NV_VSX_REGS && !fail; i++) {
- memcpy(vsc, &ucp->uc_mcontext.fp_regs[i + 20], 8);
- memcpy(vsc + 8, &vsx_ptr[20 + i], 8);
- fail = memcmp(vsc, &vss[i], sizeof(vector int));
- memcpy(vst, &tm_ucp->uc_mcontext.fp_regs[i + 20], 8);
- memcpy(vst + 8, &tm_vsx_ptr[20 + i], 8);
- fail |= memcmp(vst, &vss[i + NV_VSX_REGS], sizeof(vector int));
- if (fail) {
- int j;
+ /* Check first context. Print all mismatches. */
+ for (i = 0; i < NV_VSX_REGS; i++) {
+ /*
+ * Copy VSX most significant doubleword from fp_regs and
+ * copy VSX least significant one from 64-bit slots below
+ * saved VMX registers.
+ */
+ memcpy(vsx, &ucp->uc_mcontext.fp_regs[FPR20 + i], 8);
+ memcpy(vsx + 8, &vsx_ptr[VSX20 + i], 8);
- fprintf(stderr, "Failed on %d vsx 0x", i);
+ fail = memcmp(vsx, &vsxs[i], sizeof(vector int));
+
+ if (fail) {
+ broken = 1;
+ printf("VSX%d (1st context) == 0x", VSX20 + i);
for (j = 0; j < 16; j++)
- fprintf(stderr, "%02x", vsc[j]);
- fprintf(stderr, " vs 0x");
+ printf("%02x", vsx[j]);
+ printf(" instead of 0x");
+ for (j = 0; j < 4; j++)
+ printf("%08x", vsxs[i][j]);
+ printf(" (expected)\n");
+ }
+ }
+
+ /* Check second context. Print all mismatches. */
+ for (i = 0; i < NV_VSX_REGS; i++) {
+ /*
+ * Copy VSX most significant doubleword from fp_regs and
+ * copy VSX least significant one from 64-bit slots below
+ * saved VMX registers.
+ */
+ memcpy(vsx_tm, &tm_ucp->uc_mcontext.fp_regs[FPR20 + i], 8);
+ memcpy(vsx_tm + 8, &tm_vsx_ptr[VSX20 + i], 8);
+
+ fail = memcmp(vsx_tm, &vsxs[NV_VSX_REGS + i], sizeof(vector int));
+
+ if (fail) {
+ broken = 1;
+ printf("VSX%d (2nd context) == 0x", VSX20 + i);
for (j = 0; j < 16; j++)
- fprintf(stderr, "%02x", vst[j]);
- fprintf(stderr, "\n");
+ printf("%02x", vsx_tm[j]);
+ printf(" instead of 0x");
+ for (j = 0; j < 4; j++)
+ printf("%08x", vsxs[NV_VSX_REGS + i][j]);
+ printf("(expected)\n");
}
}
}
@@ -110,13 +163,19 @@ static int tm_signal_context_chk()
}
i = 0;
- while (i < MAX_ATTEMPT && !fail) {
- rc = tm_signal_self_context_load(pid, NULL, NULL, NULL, vss);
+ while (i < MAX_ATTEMPT && !broken) {
+ /*
+ * tm_signal_self_context_load will set both first and second
+ * contexts accordingly to the values passed through non-NULL
+ * array pointers to it, in that case 'vsxs', and invoke the
+ * signal handler installed for SIGUSR1.
+ */
+ rc = tm_signal_self_context_load(pid, NULL, NULL, NULL, vsxs);
FAIL_IF(rc != pid);
i++;
}
- return fail;
+ return (broken);
}
int main(void)
diff --git a/tools/testing/selftests/powerpc/tm/tm-signal-msr-resv.c b/tools/testing/selftests/powerpc/tm/tm-signal-msr-resv.c
index 8c54d18b3e9a..4a61e9bd12b4 100644
--- a/tools/testing/selftests/powerpc/tm/tm-signal-msr-resv.c
+++ b/tools/testing/selftests/powerpc/tm/tm-signal-msr-resv.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2015, Michael Neuling, IBM Corp.
- * Licensed under GPLv2.
*
* Test the kernel's signal return code to ensure that it doesn't
* crash when both the transactional and suspend MSR bits are set in
diff --git a/tools/testing/selftests/powerpc/tm/tm-signal-stack.c b/tools/testing/selftests/powerpc/tm/tm-signal-stack.c
index 1f0eb567438d..cdcf8c5bbbc7 100644
--- a/tools/testing/selftests/powerpc/tm/tm-signal-stack.c
+++ b/tools/testing/selftests/powerpc/tm/tm-signal-stack.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2015, Michael Neuling, IBM Corp.
- * Licensed under GPLv2.
*
* Test the kernel's signal delievery code to ensure that we don't
* trelaim twice in the kernel signal delivery code. This can happen
diff --git a/tools/testing/selftests/powerpc/tm/tm-signal.S b/tools/testing/selftests/powerpc/tm/tm-signal.S
index 506a4ebaf3ae..c80c9136601b 100644
--- a/tools/testing/selftests/powerpc/tm/tm-signal.S
+++ b/tools/testing/selftests/powerpc/tm/tm-signal.S
@@ -1,10 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2015, Cyril Bur, IBM Corp.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
*/
#include "basic_asm.h"
diff --git a/tools/testing/selftests/powerpc/tm/tm-syscall.c b/tools/testing/selftests/powerpc/tm/tm-syscall.c
index 454b965a2db3..becb8207b432 100644
--- a/tools/testing/selftests/powerpc/tm/tm-syscall.c
+++ b/tools/testing/selftests/powerpc/tm/tm-syscall.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2015, Sam Bobroff, IBM Corp.
- * Licensed under GPLv2.
*
* Test the kernel's system call code to ensure that a system call
* made from within an active HTM transaction is aborted with the
diff --git a/tools/testing/selftests/powerpc/tm/tm-tar.c b/tools/testing/selftests/powerpc/tm/tm-tar.c
index f31fe5a28ddb..03be8c47292b 100644
--- a/tools/testing/selftests/powerpc/tm/tm-tar.c
+++ b/tools/testing/selftests/powerpc/tm/tm-tar.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2015, Michael Neuling, IBM Corp.
- * Licensed under GPLv2.
* Original: Michael Neuling 19/7/2013
* Edited: Rashmica Gupta 01/12/2015
*
diff --git a/tools/testing/selftests/powerpc/tm/tm-tmspr.c b/tools/testing/selftests/powerpc/tm/tm-tmspr.c
index df1d7d4b1c89..17becf3dcee4 100644
--- a/tools/testing/selftests/powerpc/tm/tm-tmspr.c
+++ b/tools/testing/selftests/powerpc/tm/tm-tmspr.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2015, Michael Neuling, IBM Corp.
- * Licensed under GPLv2.
*
* Original: Michael Neuling 3/4/2014
* Modified: Rashmica Gupta 8/12/2015
@@ -21,7 +21,6 @@
* (a) begin transaction
* (b) abort transaction
* (c) check TEXASR to see if FS has been corrupted
- *
*/
#define _GNU_SOURCE
diff --git a/tools/testing/selftests/powerpc/tm/tm-trap.c b/tools/testing/selftests/powerpc/tm/tm-trap.c
index 179d592f0073..601f0c1d450d 100644
--- a/tools/testing/selftests/powerpc/tm/tm-trap.c
+++ b/tools/testing/selftests/powerpc/tm/tm-trap.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2017, Gustavo Romero, IBM Corp.
- * Licensed under GPLv2.
*
* Check if thread endianness is flipped inadvertently to BE on trap
* caught in TM whilst MSR.FP and MSR.VEC are zero (i.e. just after
diff --git a/tools/testing/selftests/powerpc/tm/tm-unavailable.c b/tools/testing/selftests/powerpc/tm/tm-unavailable.c
index 09894f4ff62e..2ca2fccb0a3e 100644
--- a/tools/testing/selftests/powerpc/tm/tm-unavailable.c
+++ b/tools/testing/selftests/powerpc/tm/tm-unavailable.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2017, Gustavo Romero, Breno Leitao, Cyril Bur, IBM Corp.
- * Licensed under GPLv2.
*
* Force FP, VEC and VSX unavailable exception during transaction in all
* possible scenarios regarding the MSR.FP and MSR.VEC state, e.g. when FP
diff --git a/tools/testing/selftests/powerpc/tm/tm-vmx-unavail.c b/tools/testing/selftests/powerpc/tm/tm-vmx-unavail.c
index 137185ba4937..e2a0c07e8362 100644
--- a/tools/testing/selftests/powerpc/tm/tm-vmx-unavail.c
+++ b/tools/testing/selftests/powerpc/tm/tm-vmx-unavail.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2017, Michael Neuling, IBM Corp.
- * Licensed under GPLv2.
* Original: Breno Leitao <brenohl@br.ibm.com> &
* Gustavo Bueno Romero <gromero@br.ibm.com>
* Edited: Michael Neuling
diff --git a/tools/testing/selftests/powerpc/tm/tm-vmxcopy.c b/tools/testing/selftests/powerpc/tm/tm-vmxcopy.c
index fe52811584ae..c1e788a6df47 100644
--- a/tools/testing/selftests/powerpc/tm/tm-vmxcopy.c
+++ b/tools/testing/selftests/powerpc/tm/tm-vmxcopy.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2015, Michael Neuling, IBM Corp.
- * Licensed under GPLv2.
*
* Original: Michael Neuling 4/12/2013
* Edited: Rashmica Gupta 4/12/2015
@@ -79,7 +79,7 @@ int test_vmxcopy()
"5:;"
"stxvd2x 40,0,%[vecoutptr];"
- : [res]"=r"(aborted)
+ : [res]"=&r"(aborted)
: [vecinptr]"r"(&vecin),
[vecoutptr]"r"(&vecout),
[map]"r"(a)
diff --git a/tools/testing/selftests/powerpc/tm/tm.h b/tools/testing/selftests/powerpc/tm/tm.h
index 5518b1d4ef8b..c402464b038f 100644
--- a/tools/testing/selftests/powerpc/tm/tm.h
+++ b/tools/testing/selftests/powerpc/tm/tm.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2015, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#ifndef _SELFTESTS_POWERPC_TM_TM_H
@@ -55,7 +55,8 @@ static inline bool failure_is_unavailable(void)
static inline bool failure_is_reschedule(void)
{
if ((failure_code() & TM_CAUSE_RESCHED) == TM_CAUSE_RESCHED ||
- (failure_code() & TM_CAUSE_KVM_RESCHED) == TM_CAUSE_KVM_RESCHED)
+ (failure_code() & TM_CAUSE_KVM_RESCHED) == TM_CAUSE_KVM_RESCHED ||
+ (failure_code() & TM_CAUSE_KVM_FAC_UNAV) == TM_CAUSE_KVM_FAC_UNAV)
return true;
return false;
diff --git a/tools/testing/selftests/powerpc/utils.c b/tools/testing/selftests/powerpc/utils.c
index ed62f4153d3e..c02d24835db4 100644
--- a/tools/testing/selftests/powerpc/utils.c
+++ b/tools/testing/selftests/powerpc/utils.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2013-2015, Michael Ellerman, IBM Corp.
- * Licensed under GPLv2.
*/
#define _GNU_SOURCE /* For CPU_ZERO etc. */
diff --git a/tools/testing/selftests/powerpc/vphn/Makefile b/tools/testing/selftests/powerpc/vphn/Makefile
index fb82068c9fda..cf65cbf33085 100644
--- a/tools/testing/selftests/powerpc/vphn/Makefile
+++ b/tools/testing/selftests/powerpc/vphn/Makefile
@@ -1,6 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
TEST_GEN_PROGS := test-vphn
-CFLAGS += -m64
+CFLAGS += -m64 -I$(CURDIR)
top_srcdir = ../../../../..
include ../../lib.mk
diff --git a/tools/testing/selftests/powerpc/vphn/asm/lppaca.h b/tools/testing/selftests/powerpc/vphn/asm/lppaca.h
new file mode 120000
index 000000000000..942b1d00999c
--- /dev/null
+++ b/tools/testing/selftests/powerpc/vphn/asm/lppaca.h
@@ -0,0 +1 @@
+../../../../../../arch/powerpc/include/asm/lppaca.h \ No newline at end of file
diff --git a/tools/testing/selftests/powerpc/vphn/vphn.c b/tools/testing/selftests/powerpc/vphn/vphn.c
index 1d1f5f2be3b2..5b5fbddccabd 120000
--- a/tools/testing/selftests/powerpc/vphn/vphn.c
+++ b/tools/testing/selftests/powerpc/vphn/vphn.c
@@ -1 +1 @@
-../../../../../arch/powerpc/mm/book3s64/vphn.c \ No newline at end of file
+../../../../../arch/powerpc/platforms/pseries/vphn.c \ No newline at end of file
diff --git a/tools/testing/selftests/powerpc/vphn/vphn.h b/tools/testing/selftests/powerpc/vphn/vphn.h
deleted file mode 120000
index 45fe160f8288..000000000000
--- a/tools/testing/selftests/powerpc/vphn/vphn.h
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../arch/powerpc/mm/book3s64/vphn.h \ No newline at end of file
diff --git a/tools/testing/selftests/proc/.gitignore b/tools/testing/selftests/proc/.gitignore
index 444ad39d3700..66fab4c58ed4 100644
--- a/tools/testing/selftests/proc/.gitignore
+++ b/tools/testing/selftests/proc/.gitignore
@@ -12,4 +12,5 @@
/read
/self
/setns-dcache
+/setns-sysvipc
/thread-self
diff --git a/tools/testing/selftests/proc/Makefile b/tools/testing/selftests/proc/Makefile
index 5163dc887aa3..a8ed0f684829 100644
--- a/tools/testing/selftests/proc/Makefile
+++ b/tools/testing/selftests/proc/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
CFLAGS += -Wall -O2 -Wno-unused-function
CFLAGS += -D_GNU_SOURCE
@@ -16,6 +17,7 @@ TEST_GEN_PROGS += proc-uptime-002
TEST_GEN_PROGS += read
TEST_GEN_PROGS += self
TEST_GEN_PROGS += setns-dcache
+TEST_GEN_PROGS += setns-sysvipc
TEST_GEN_PROGS += thread-self
include ../lib.mk
diff --git a/tools/testing/selftests/proc/proc-pid-vm.c b/tools/testing/selftests/proc/proc-pid-vm.c
index 853aa164a401..18a3bde8bc96 100644
--- a/tools/testing/selftests/proc/proc-pid-vm.c
+++ b/tools/testing/selftests/proc/proc-pid-vm.c
@@ -215,6 +215,11 @@ static const char str_vsyscall[] =
"ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]\n";
#ifdef __x86_64__
+static void sigaction_SIGSEGV(int _, siginfo_t *__, void *___)
+{
+ _exit(1);
+}
+
/*
* vsyscall page can't be unmapped, probe it with memory load.
*/
@@ -231,11 +236,19 @@ static void vsyscall(void)
if (pid == 0) {
struct rlimit rlim = {0, 0};
(void)setrlimit(RLIMIT_CORE, &rlim);
+
+ /* Hide "segfault at ffffffffff600000" messages. */
+ struct sigaction act;
+ memset(&act, 0, sizeof(struct sigaction));
+ act.sa_flags = SA_SIGINFO;
+ act.sa_sigaction = sigaction_SIGSEGV;
+ (void)sigaction(SIGSEGV, &act, NULL);
+
*(volatile int *)0xffffffffff600000UL;
exit(0);
}
- wait(&wstatus);
- if (WIFEXITED(wstatus)) {
+ waitpid(pid, &wstatus, 0);
+ if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) {
g_vsyscall = true;
}
}
diff --git a/tools/testing/selftests/proc/setns-sysvipc.c b/tools/testing/selftests/proc/setns-sysvipc.c
new file mode 100644
index 000000000000..903890c5e587
--- /dev/null
+++ b/tools/testing/selftests/proc/setns-sysvipc.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright © 2019 Alexey Dobriyan <adobriyan@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Test that setns(CLONE_NEWIPC) points to new /proc/sysvipc content even
+ * if old one is in dcache.
+ */
+#undef NDEBUG
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+static pid_t pid = -1;
+
+static void f(void)
+{
+ if (pid > 0) {
+ kill(pid, SIGTERM);
+ }
+}
+
+int main(void)
+{
+ int fd[2];
+ char _ = 0;
+ int nsfd;
+
+ atexit(f);
+
+ /* Check for priviledges and syscall availability straight away. */
+ if (unshare(CLONE_NEWIPC) == -1) {
+ if (errno == ENOSYS || errno == EPERM) {
+ return 4;
+ }
+ return 1;
+ }
+ /* Distinguisher between two otherwise empty IPC namespaces. */
+ if (shmget(IPC_PRIVATE, 1, IPC_CREAT) == -1) {
+ return 1;
+ }
+
+ if (pipe(fd) == -1) {
+ return 1;
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ return 1;
+ }
+
+ if (pid == 0) {
+ if (unshare(CLONE_NEWIPC) == -1) {
+ return 1;
+ }
+
+ if (write(fd[1], &_, 1) != 1) {
+ return 1;
+ }
+
+ pause();
+
+ return 0;
+ }
+
+ if (read(fd[0], &_, 1) != 1) {
+ return 1;
+ }
+
+ {
+ char buf[64];
+ snprintf(buf, sizeof(buf), "/proc/%u/ns/ipc", pid);
+ nsfd = open(buf, O_RDONLY);
+ if (nsfd == -1) {
+ return 1;
+ }
+ }
+
+ /* Reliably pin dentry into dcache. */
+ (void)open("/proc/sysvipc/shm", O_RDONLY);
+
+ if (setns(nsfd, CLONE_NEWIPC) == -1) {
+ return 1;
+ }
+
+ kill(pid, SIGTERM);
+ pid = 0;
+
+ {
+ char buf[4096];
+ ssize_t rv;
+ int fd;
+
+ fd = open("/proc/sysvipc/shm", O_RDONLY);
+ if (fd == -1) {
+ return 1;
+ }
+
+#define S32 " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime rss swap\n"
+#define S64 " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime rss swap\n"
+ rv = read(fd, buf, sizeof(buf));
+ if (rv == strlen(S32)) {
+ assert(memcmp(buf, S32, strlen(S32)) == 0);
+ } else if (rv == strlen(S64)) {
+ assert(memcmp(buf, S64, strlen(S64)) == 0);
+ } else {
+ assert(0);
+ }
+ }
+
+ return 0;
+}
diff --git a/tools/testing/selftests/pstore/common_tests b/tools/testing/selftests/pstore/common_tests
index 3ea64d7cf1cd..4509f0cc9c91 100755
--- a/tools/testing/selftests/pstore/common_tests
+++ b/tools/testing/selftests/pstore/common_tests
@@ -1,11 +1,11 @@
#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
# common_tests - Shell script commonly used by pstore test scripts
#
# Copyright (C) Hitachi Ltd., 2015
# Written by Hiraku Toyooka <hiraku.toyooka.gu@hitachi.com>
#
-# Released under the terms of the GPL v2.
# Utilities
errexit() { # message
diff --git a/tools/testing/selftests/pstore/pstore_crash_test b/tools/testing/selftests/pstore/pstore_crash_test
index 1a4afe5c12b6..2a329bbb4aca 100755
--- a/tools/testing/selftests/pstore/pstore_crash_test
+++ b/tools/testing/selftests/pstore/pstore_crash_test
@@ -1,11 +1,11 @@
#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
# pstore_crash_test - Pstore test shell script which causes crash and reboot
#
# Copyright (C) Hitachi Ltd., 2015
# Written by Hiraku Toyooka <hiraku.toyooka.gu@hitachi.com>
#
-# Released under the terms of the GPL v2.
# exit if pstore backend is not registered
. ./common_tests
diff --git a/tools/testing/selftests/pstore/pstore_post_reboot_tests b/tools/testing/selftests/pstore/pstore_post_reboot_tests
index 22f8df1ad7d4..d6da5e86efbf 100755
--- a/tools/testing/selftests/pstore/pstore_post_reboot_tests
+++ b/tools/testing/selftests/pstore/pstore_post_reboot_tests
@@ -1,11 +1,11 @@
#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
# pstore_post_reboot_tests - Check pstore's behavior after crash/reboot
#
# Copyright (C) Hitachi Ltd., 2015
# Written by Hiraku Toyooka <hiraku.toyooka.gu@hitachi.com>
#
-# Released under the terms of the GPL v2.
# Kselftest framework requirement - SKIP code is 4.
ksft_skip=4
diff --git a/tools/testing/selftests/pstore/pstore_tests b/tools/testing/selftests/pstore/pstore_tests
index f25d2a349a60..1cef54458aff 100755
--- a/tools/testing/selftests/pstore/pstore_tests
+++ b/tools/testing/selftests/pstore/pstore_tests
@@ -1,11 +1,11 @@
#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
# pstore_tests - Check pstore's behavior before crash/reboot
#
# Copyright (C) Hitachi Ltd., 2015
# Written by Hiraku Toyooka <hiraku.toyooka.gu@hitachi.com>
#
-# Released under the terms of the GPL v2.
. ./common_tests
diff --git a/tools/testing/selftests/ptp/phc.sh b/tools/testing/selftests/ptp/phc.sh
new file mode 100755
index 000000000000..ac6e5a6e1d3a
--- /dev/null
+++ b/tools/testing/selftests/ptp/phc.sh
@@ -0,0 +1,166 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+ALL_TESTS="
+ settime
+ adjtime
+ adjfreq
+"
+DEV=$1
+
+##############################################################################
+# Sanity checks
+
+if [[ "$(id -u)" -ne 0 ]]; then
+ echo "SKIP: need root privileges"
+ exit 0
+fi
+
+if [[ "$DEV" == "" ]]; then
+ echo "SKIP: PTP device not provided"
+ exit 0
+fi
+
+require_command()
+{
+ local cmd=$1; shift
+
+ if [[ ! -x "$(command -v "$cmd")" ]]; then
+ echo "SKIP: $cmd not installed"
+ exit 1
+ fi
+}
+
+phc_sanity()
+{
+ phc_ctl $DEV get &> /dev/null
+
+ if [ $? != 0 ]; then
+ echo "SKIP: unknown clock $DEV: No such device"
+ exit 1
+ fi
+}
+
+require_command phc_ctl
+phc_sanity
+
+##############################################################################
+# Helpers
+
+# Exit status to return at the end. Set in case one of the tests fails.
+EXIT_STATUS=0
+# Per-test return value. Clear at the beginning of each test.
+RET=0
+
+check_err()
+{
+ local err=$1
+
+ if [[ $RET -eq 0 && $err -ne 0 ]]; then
+ RET=$err
+ fi
+}
+
+log_test()
+{
+ local test_name=$1
+
+ if [[ $RET -ne 0 ]]; then
+ EXIT_STATUS=1
+ printf "TEST: %-60s [FAIL]\n" "$test_name"
+ return 1
+ fi
+
+ printf "TEST: %-60s [ OK ]\n" "$test_name"
+ return 0
+}
+
+tests_run()
+{
+ local current_test
+
+ for current_test in ${TESTS:-$ALL_TESTS}; do
+ $current_test
+ done
+}
+
+##############################################################################
+# Tests
+
+settime_do()
+{
+ local res
+
+ res=$(phc_ctl $DEV set 0 wait 120.5 get 2> /dev/null \
+ | awk '/clock time is/{print $5}' \
+ | awk -F. '{print $1}')
+
+ (( res == 120 ))
+}
+
+adjtime_do()
+{
+ local res
+
+ res=$(phc_ctl $DEV set 0 adj 10 get 2> /dev/null \
+ | awk '/clock time is/{print $5}' \
+ | awk -F. '{print $1}')
+
+ (( res == 10 ))
+}
+
+adjfreq_do()
+{
+ local res
+
+ # Set the clock to be 1% faster
+ res=$(phc_ctl $DEV freq 10000000 set 0 wait 100.5 get 2> /dev/null \
+ | awk '/clock time is/{print $5}' \
+ | awk -F. '{print $1}')
+
+ (( res == 101 ))
+}
+
+##############################################################################
+
+cleanup()
+{
+ phc_ctl $DEV freq 0.0 &> /dev/null
+ phc_ctl $DEV set &> /dev/null
+}
+
+settime()
+{
+ RET=0
+
+ settime_do
+ check_err $?
+ log_test "settime"
+ cleanup
+}
+
+adjtime()
+{
+ RET=0
+
+ adjtime_do
+ check_err $?
+ log_test "adjtime"
+ cleanup
+}
+
+adjfreq()
+{
+ RET=0
+
+ adjfreq_do
+ check_err $?
+ log_test "adjfreq"
+ cleanup
+}
+
+trap cleanup EXIT
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/ptp/testptp.c b/tools/testing/selftests/ptp/testptp.c
index a5d8f0ab0da0..bd4a7247b44f 100644
--- a/tools/testing/selftests/ptp/testptp.c
+++ b/tools/testing/selftests/ptp/testptp.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PTP 1588 clock support - User space test program
*
* Copyright (C) 2010 OMICRON electronics GmbH
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define _GNU_SOURCE
#define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */
@@ -63,30 +50,6 @@ static clockid_t get_clockid(int fd)
return (((unsigned int) ~fd) << 3) | CLOCKFD;
}
-static void handle_alarm(int s)
-{
- printf("received signal %d\n", s);
-}
-
-static int install_handler(int signum, void (*handler)(int))
-{
- struct sigaction action;
- sigset_t mask;
-
- /* Unblock the signal. */
- sigemptyset(&mask);
- sigaddset(&mask, signum);
- sigprocmask(SIG_UNBLOCK, &mask, NULL);
-
- /* Install the signal handler. */
- action.sa_handler = handler;
- action.sa_flags = 0;
- sigemptyset(&action.sa_mask);
- sigaction(signum, &action, NULL);
-
- return 0;
-}
-
static long ppb_to_scaled_ppm(int ppb)
{
/*
@@ -112,8 +75,6 @@ static void usage(char *progname)
{
fprintf(stderr,
"usage: %s [options]\n"
- " -a val request a one-shot alarm after 'val' seconds\n"
- " -A val request a periodic alarm every 'val' seconds\n"
" -c query the ptp clock's capabilities\n"
" -d name device to open\n"
" -e val read 'val' external time stamp events\n"
@@ -148,15 +109,9 @@ int main(int argc, char *argv[])
struct ptp_pin_desc desc;
struct timespec ts;
struct timex tx;
-
- static timer_t timerid;
- struct itimerspec timeout;
- struct sigevent sigevent;
-
struct ptp_clock_time *pct;
struct ptp_sys_offset *sysoff;
-
char *progname;
unsigned int i;
int c, cnt, fd;
@@ -170,10 +125,8 @@ int main(int argc, char *argv[])
int gettime = 0;
int index = 0;
int list_pins = 0;
- int oneshot = 0;
int pct_offset = 0;
int n_samples = 0;
- int periodic = 0;
int perout = -1;
int pin_index = -1, pin_func;
int pps = -1;
@@ -185,14 +138,8 @@ int main(int argc, char *argv[])
progname = strrchr(argv[0], '/');
progname = progname ? 1+progname : argv[0];
- while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghi:k:lL:p:P:sSt:T:v"))) {
+ while (EOF != (c = getopt(argc, argv, "cd:e:f:ghi:k:lL:p:P:sSt:T:v"))) {
switch (c) {
- case 'a':
- oneshot = atoi(optarg);
- break;
- case 'A':
- periodic = atoi(optarg);
- break;
case 'c':
capabilities = 1;
break;
@@ -393,49 +340,6 @@ int main(int argc, char *argv[])
}
}
- if (oneshot) {
- install_handler(SIGALRM, handle_alarm);
- /* Create a timer. */
- sigevent.sigev_notify = SIGEV_SIGNAL;
- sigevent.sigev_signo = SIGALRM;
- if (timer_create(clkid, &sigevent, &timerid)) {
- perror("timer_create");
- return -1;
- }
- /* Start the timer. */
- memset(&timeout, 0, sizeof(timeout));
- timeout.it_value.tv_sec = oneshot;
- if (timer_settime(timerid, 0, &timeout, NULL)) {
- perror("timer_settime");
- return -1;
- }
- pause();
- timer_delete(timerid);
- }
-
- if (periodic) {
- install_handler(SIGALRM, handle_alarm);
- /* Create a timer. */
- sigevent.sigev_notify = SIGEV_SIGNAL;
- sigevent.sigev_signo = SIGALRM;
- if (timer_create(clkid, &sigevent, &timerid)) {
- perror("timer_create");
- return -1;
- }
- /* Start the timer. */
- memset(&timeout, 0, sizeof(timeout));
- timeout.it_interval.tv_sec = periodic;
- timeout.it_value.tv_sec = periodic;
- if (timer_settime(timerid, 0, &timeout, NULL)) {
- perror("timer_settime");
- return -1;
- }
- while (1) {
- pause();
- }
- timer_delete(timerid);
- }
-
if (perout >= 0) {
if (clock_gettime(clkid, &ts)) {
perror("clock_gettime");
diff --git a/tools/testing/selftests/ptrace/.gitignore b/tools/testing/selftests/ptrace/.gitignore
index b3e59d41fd82..cfcc49a7def7 100644
--- a/tools/testing/selftests/ptrace/.gitignore
+++ b/tools/testing/selftests/ptrace/.gitignore
@@ -1 +1,2 @@
+get_syscall_info
peeksiginfo
diff --git a/tools/testing/selftests/ptrace/Makefile b/tools/testing/selftests/ptrace/Makefile
index 8a2bc5562179..c0b7f89f0930 100644
--- a/tools/testing/selftests/ptrace/Makefile
+++ b/tools/testing/selftests/ptrace/Makefile
@@ -1,5 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
CFLAGS += -iquote../../../../include/uapi -Wall
-TEST_GEN_PROGS := peeksiginfo
+TEST_GEN_PROGS := get_syscall_info peeksiginfo
include ../lib.mk
diff --git a/tools/testing/selftests/ptrace/get_syscall_info.c b/tools/testing/selftests/ptrace/get_syscall_info.c
new file mode 100644
index 000000000000..5bcd1c7b5be6
--- /dev/null
+++ b/tools/testing/selftests/ptrace/get_syscall_info.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
+ * All rights reserved.
+ *
+ * Check whether PTRACE_GET_SYSCALL_INFO semantics implemented in the kernel
+ * matches userspace expectations.
+ */
+
+#include "../kselftest_harness.h"
+#include <err.h>
+#include <signal.h>
+#include <asm/unistd.h>
+#include "linux/ptrace.h"
+
+static int
+kill_tracee(pid_t pid)
+{
+ if (!pid)
+ return 0;
+
+ int saved_errno = errno;
+
+ int rc = kill(pid, SIGKILL);
+
+ errno = saved_errno;
+ return rc;
+}
+
+static long
+sys_ptrace(int request, pid_t pid, unsigned long addr, unsigned long data)
+{
+ return syscall(__NR_ptrace, request, pid, addr, data);
+}
+
+#define LOG_KILL_TRACEE(fmt, ...) \
+ do { \
+ kill_tracee(pid); \
+ TH_LOG("wait #%d: " fmt, \
+ ptrace_stop, ##__VA_ARGS__); \
+ } while (0)
+
+TEST(get_syscall_info)
+{
+ static const unsigned long args[][7] = {
+ /* a sequence of architecture-agnostic syscalls */
+ {
+ __NR_chdir,
+ (unsigned long) "",
+ 0xbad1fed1,
+ 0xbad2fed2,
+ 0xbad3fed3,
+ 0xbad4fed4,
+ 0xbad5fed5
+ },
+ {
+ __NR_gettid,
+ 0xcaf0bea0,
+ 0xcaf1bea1,
+ 0xcaf2bea2,
+ 0xcaf3bea3,
+ 0xcaf4bea4,
+ 0xcaf5bea5
+ },
+ {
+ __NR_exit_group,
+ 0,
+ 0xfac1c0d1,
+ 0xfac2c0d2,
+ 0xfac3c0d3,
+ 0xfac4c0d4,
+ 0xfac5c0d5
+ }
+ };
+ const unsigned long *exp_args;
+
+ pid_t pid = fork();
+
+ ASSERT_LE(0, pid) {
+ TH_LOG("fork: %m");
+ }
+
+ if (pid == 0) {
+ /* get the pid before PTRACE_TRACEME */
+ pid = getpid();
+ ASSERT_EQ(0, sys_ptrace(PTRACE_TRACEME, 0, 0, 0)) {
+ TH_LOG("PTRACE_TRACEME: %m");
+ }
+ ASSERT_EQ(0, kill(pid, SIGSTOP)) {
+ /* cannot happen */
+ TH_LOG("kill SIGSTOP: %m");
+ }
+ for (unsigned int i = 0; i < ARRAY_SIZE(args); ++i) {
+ syscall(args[i][0],
+ args[i][1], args[i][2], args[i][3],
+ args[i][4], args[i][5], args[i][6]);
+ }
+ /* unreachable */
+ _exit(1);
+ }
+
+ const struct {
+ unsigned int is_error;
+ int rval;
+ } *exp_param, exit_param[] = {
+ { 1, -ENOENT }, /* chdir */
+ { 0, pid } /* gettid */
+ };
+
+ unsigned int ptrace_stop;
+
+ for (ptrace_stop = 0; ; ++ptrace_stop) {
+ struct ptrace_syscall_info info = {
+ .op = 0xff /* invalid PTRACE_SYSCALL_INFO_* op */
+ };
+ const size_t size = sizeof(info);
+ const int expected_none_size =
+ (void *) &info.entry - (void *) &info;
+ const int expected_entry_size =
+ (void *) &info.entry.args[6] - (void *) &info;
+ const int expected_exit_size =
+ (void *) (&info.exit.is_error + 1) -
+ (void *) &info;
+ int status;
+ long rc;
+
+ ASSERT_EQ(pid, wait(&status)) {
+ /* cannot happen */
+ LOG_KILL_TRACEE("wait: %m");
+ }
+ if (WIFEXITED(status)) {
+ pid = 0; /* the tracee is no more */
+ ASSERT_EQ(0, WEXITSTATUS(status));
+ break;
+ }
+ ASSERT_FALSE(WIFSIGNALED(status)) {
+ pid = 0; /* the tracee is no more */
+ LOG_KILL_TRACEE("unexpected signal %u",
+ WTERMSIG(status));
+ }
+ ASSERT_TRUE(WIFSTOPPED(status)) {
+ /* cannot happen */
+ LOG_KILL_TRACEE("unexpected wait status %#x", status);
+ }
+
+ switch (WSTOPSIG(status)) {
+ case SIGSTOP:
+ ASSERT_EQ(0, ptrace_stop) {
+ LOG_KILL_TRACEE("unexpected signal stop");
+ }
+ ASSERT_EQ(0, sys_ptrace(PTRACE_SETOPTIONS, pid, 0,
+ PTRACE_O_TRACESYSGOOD)) {
+ LOG_KILL_TRACEE("PTRACE_SETOPTIONS: %m");
+ }
+ ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
+ pid, size,
+ (unsigned long) &info))) {
+ LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
+ }
+ ASSERT_EQ(expected_none_size, rc) {
+ LOG_KILL_TRACEE("signal stop mismatch");
+ }
+ ASSERT_EQ(PTRACE_SYSCALL_INFO_NONE, info.op) {
+ LOG_KILL_TRACEE("signal stop mismatch");
+ }
+ ASSERT_TRUE(info.arch) {
+ LOG_KILL_TRACEE("signal stop mismatch");
+ }
+ ASSERT_TRUE(info.instruction_pointer) {
+ LOG_KILL_TRACEE("signal stop mismatch");
+ }
+ ASSERT_TRUE(info.stack_pointer) {
+ LOG_KILL_TRACEE("signal stop mismatch");
+ }
+ break;
+
+ case SIGTRAP | 0x80:
+ ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
+ pid, size,
+ (unsigned long) &info))) {
+ LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
+ }
+ switch (ptrace_stop) {
+ case 1: /* entering chdir */
+ case 3: /* entering gettid */
+ case 5: /* entering exit_group */
+ exp_args = args[ptrace_stop / 2];
+ ASSERT_EQ(expected_entry_size, rc) {
+ LOG_KILL_TRACEE("entry stop mismatch");
+ }
+ ASSERT_EQ(PTRACE_SYSCALL_INFO_ENTRY, info.op) {
+ LOG_KILL_TRACEE("entry stop mismatch");
+ }
+ ASSERT_TRUE(info.arch) {
+ LOG_KILL_TRACEE("entry stop mismatch");
+ }
+ ASSERT_TRUE(info.instruction_pointer) {
+ LOG_KILL_TRACEE("entry stop mismatch");
+ }
+ ASSERT_TRUE(info.stack_pointer) {
+ LOG_KILL_TRACEE("entry stop mismatch");
+ }
+ ASSERT_EQ(exp_args[0], info.entry.nr) {
+ LOG_KILL_TRACEE("entry stop mismatch");
+ }
+ ASSERT_EQ(exp_args[1], info.entry.args[0]) {
+ LOG_KILL_TRACEE("entry stop mismatch");
+ }
+ ASSERT_EQ(exp_args[2], info.entry.args[1]) {
+ LOG_KILL_TRACEE("entry stop mismatch");
+ }
+ ASSERT_EQ(exp_args[3], info.entry.args[2]) {
+ LOG_KILL_TRACEE("entry stop mismatch");
+ }
+ ASSERT_EQ(exp_args[4], info.entry.args[3]) {
+ LOG_KILL_TRACEE("entry stop mismatch");
+ }
+ ASSERT_EQ(exp_args[5], info.entry.args[4]) {
+ LOG_KILL_TRACEE("entry stop mismatch");
+ }
+ ASSERT_EQ(exp_args[6], info.entry.args[5]) {
+ LOG_KILL_TRACEE("entry stop mismatch");
+ }
+ break;
+ case 2: /* exiting chdir */
+ case 4: /* exiting gettid */
+ exp_param = &exit_param[ptrace_stop / 2 - 1];
+ ASSERT_EQ(expected_exit_size, rc) {
+ LOG_KILL_TRACEE("exit stop mismatch");
+ }
+ ASSERT_EQ(PTRACE_SYSCALL_INFO_EXIT, info.op) {
+ LOG_KILL_TRACEE("exit stop mismatch");
+ }
+ ASSERT_TRUE(info.arch) {
+ LOG_KILL_TRACEE("exit stop mismatch");
+ }
+ ASSERT_TRUE(info.instruction_pointer) {
+ LOG_KILL_TRACEE("exit stop mismatch");
+ }
+ ASSERT_TRUE(info.stack_pointer) {
+ LOG_KILL_TRACEE("exit stop mismatch");
+ }
+ ASSERT_EQ(exp_param->is_error,
+ info.exit.is_error) {
+ LOG_KILL_TRACEE("exit stop mismatch");
+ }
+ ASSERT_EQ(exp_param->rval, info.exit.rval) {
+ LOG_KILL_TRACEE("exit stop mismatch");
+ }
+ break;
+ default:
+ LOG_KILL_TRACEE("unexpected syscall stop");
+ abort();
+ }
+ break;
+
+ default:
+ LOG_KILL_TRACEE("unexpected stop signal %#x",
+ WSTOPSIG(status));
+ abort();
+ }
+
+ ASSERT_EQ(0, sys_ptrace(PTRACE_SYSCALL, pid, 0, 0)) {
+ LOG_KILL_TRACEE("PTRACE_SYSCALL: %m");
+ }
+ }
+
+ ASSERT_EQ(ARRAY_SIZE(args) * 2, ptrace_stop);
+}
+
+TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/rcutorture/Makefile b/tools/testing/selftests/rcutorture/Makefile
new file mode 100644
index 000000000000..5202dc666206
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0+
+all:
+ ( cd ../../../..; tools/testing/selftests/rcutorture/bin/kvm.sh --duration 10 --configs TREE01 )
diff --git a/tools/testing/selftests/rcutorture/bin/configinit.sh b/tools/testing/selftests/rcutorture/bin/configinit.sh
index 40359486b3a8..93e80a42249a 100755
--- a/tools/testing/selftests/rcutorture/bin/configinit.sh
+++ b/tools/testing/selftests/rcutorture/bin/configinit.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0+
#
-# Usage: configinit.sh config-spec-file build-output-dir results-dir
+# Usage: configinit.sh config-spec-file results-dir
#
# Create a .config file from the spec file. Run from the kernel source tree.
# Exits with 0 if all went well, with 1 if all went well but the config
@@ -11,10 +11,6 @@
# desired settings, for example, "CONFIG_NO_HZ=y". For best results,
# this should be a full pathname.
#
-# The second argument is a optional path to a build output directory,
-# for example, "O=/tmp/foo". If this argument is omitted, the .config
-# file will be generated directly in the current directory.
-#
# Copyright (C) IBM Corporation, 2013
#
# Authors: Paul E. McKenney <paulmck@linux.ibm.com>
@@ -26,34 +22,23 @@ mkdir $T
# Capture config spec file.
c=$1
-buildloc=$2
-resdir=$3
-builddir=
-if echo $buildloc | grep -q '^O='
-then
- builddir=`echo $buildloc | sed -e 's/^O=//'`
- if test ! -d $builddir
- then
- mkdir $builddir
- fi
-else
- echo Bad build directory: \"$buildloc\"
- exit 2
-fi
+resdir=$2
sed -e 's/^\(CONFIG[0-9A-Z_]*\)=.*$/grep -v "^# \1" |/' < $c > $T/u.sh
sed -e 's/^\(CONFIG[0-9A-Z_]*=\).*$/grep -v \1 |/' < $c >> $T/u.sh
grep '^grep' < $T/u.sh > $T/upd.sh
echo "cat - $c" >> $T/upd.sh
-make mrproper
-make $buildloc distclean > $resdir/Make.distclean 2>&1
-make $buildloc $TORTURE_DEFCONFIG > $resdir/Make.defconfig.out 2>&1
-mv $builddir/.config $builddir/.config.sav
-sh $T/upd.sh < $builddir/.config.sav > $builddir/.config
-cp $builddir/.config $builddir/.config.new
-yes '' | make $buildloc oldconfig > $resdir/Make.oldconfig.out 2> $resdir/Make.oldconfig.err
+if test -z "$TORTURE_TRUST_MAKE"
+then
+ make clean > $resdir/Make.clean 2>&1
+fi
+make $TORTURE_DEFCONFIG > $resdir/Make.defconfig.out 2>&1
+mv .config .config.sav
+sh $T/upd.sh < .config.sav > .config
+cp .config .config.new
+yes '' | make oldconfig > $resdir/Make.oldconfig.out 2> $resdir/Make.oldconfig.err
# verify new config matches specification.
-configcheck.sh $builddir/.config $c
+configcheck.sh .config $c
exit 0
diff --git a/tools/testing/selftests/rcutorture/bin/cpus2use.sh b/tools/testing/selftests/rcutorture/bin/cpus2use.sh
index ff7102212703..4e9485590c10 100755
--- a/tools/testing/selftests/rcutorture/bin/cpus2use.sh
+++ b/tools/testing/selftests/rcutorture/bin/cpus2use.sh
@@ -9,6 +9,11 @@
#
# Authors: Paul E. McKenney <paulmck@linux.ibm.com>
+if test -n "$TORTURE_ALLOTED_CPUS"
+then
+ echo $TORTURE_ALLOTED_CPUS
+ exit 0
+fi
ncpus=`grep '^processor' /proc/cpuinfo | wc -l`
idlecpus=`mpstat | tail -1 | \
awk -v ncpus=$ncpus '{ print ncpus * ($7 + $NF) / 100 }'`
diff --git a/tools/testing/selftests/rcutorture/bin/functions.sh b/tools/testing/selftests/rcutorture/bin/functions.sh
index 6bcb8b5b2ff2..c3a49fb4d6f6 100644
--- a/tools/testing/selftests/rcutorture/bin/functions.sh
+++ b/tools/testing/selftests/rcutorture/bin/functions.sh
@@ -172,7 +172,7 @@ identify_qemu_append () {
local console=ttyS0
case "$1" in
qemu-system-x86_64|qemu-system-i386)
- echo noapic selinux=0 initcall_debug debug
+ echo selinux=0 initcall_debug debug
;;
qemu-system-aarch64)
console=ttyAMA0
@@ -191,8 +191,19 @@ identify_qemu_append () {
# Output arguments for qemu arguments based on the TORTURE_QEMU_MAC
# and TORTURE_QEMU_INTERACTIVE environment variables.
identify_qemu_args () {
+ local KVM_CPU=""
+ case "$1" in
+ qemu-system-x86_64)
+ KVM_CPU=kvm64
+ ;;
+ qemu-system-i386)
+ KVM_CPU=kvm32
+ ;;
+ esac
case "$1" in
qemu-system-x86_64|qemu-system-i386)
+ echo -machine q35,accel=kvm
+ echo -cpu ${KVM_CPU}
;;
qemu-system-aarch64)
echo -machine virt,gic-version=host -cpu host
diff --git a/tools/testing/selftests/rcutorture/bin/jitter.sh b/tools/testing/selftests/rcutorture/bin/jitter.sh
index 435b60933985..dc49a3ba6111 100755
--- a/tools/testing/selftests/rcutorture/bin/jitter.sh
+++ b/tools/testing/selftests/rcutorture/bin/jitter.sh
@@ -34,10 +34,15 @@ do
exit 0;
fi
- # Set affinity to randomly selected CPU
- cpus=`ls /sys/devices/system/cpu/*/online |
- sed -e 's,/[^/]*$,,' -e 's/^[^0-9]*//' |
- grep -v '^0*$'`
+ # Set affinity to randomly selected online CPU
+ cpus=`grep 1 /sys/devices/system/cpu/*/online |
+ sed -e 's,/[^/]*$,,' -e 's/^[^0-9]*//'`
+
+ # Do not leave out poor old cpu0 which may not be hot-pluggable
+ if [ ! -f "/sys/devices/system/cpu/cpu0/online" ]; then
+ cpus="0 $cpus"
+ fi
+
cpumask=`awk -v cpus="$cpus" -v me=$me -v n=$n 'BEGIN {
srand(n + me + systime());
ncpus = split(cpus, ca);
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-build.sh b/tools/testing/selftests/rcutorture/bin/kvm-build.sh
index c27a0bbb9c02..18d6518504ee 100755
--- a/tools/testing/selftests/rcutorture/bin/kvm-build.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm-build.sh
@@ -3,7 +3,7 @@
#
# Build a kvm-ready Linux kernel from the tree in the current directory.
#
-# Usage: kvm-build.sh config-template build-dir resdir
+# Usage: kvm-build.sh config-template resdir
#
# Copyright (C) IBM Corporation, 2011
#
@@ -15,8 +15,7 @@ then
echo "kvm-build.sh :$config_template: Not a readable file"
exit 1
fi
-builddir=${2}
-resdir=${3}
+resdir=${2}
T=${TMPDIR-/tmp}/test-linux.sh.$$
trap 'rm -rf $T' 0
@@ -29,14 +28,14 @@ CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_CONSOLE=y
___EOF___
-configinit.sh $T/config O=$builddir $resdir
+configinit.sh $T/config $resdir
retval=$?
if test $retval -gt 1
then
exit 2
fi
ncpus=`cpus2use.sh`
-make O=$builddir -j$ncpus $TORTURE_KMAKE_ARG > $resdir/Make.out 2>&1
+make -j$ncpus $TORTURE_KMAKE_ARG > $resdir/Make.out 2>&1
retval=$?
if test $retval -ne 0 || grep "rcu[^/]*": < $resdir/Make.out | egrep -q "Stop|Error|error:|warning:" || egrep -q "Stop|Error|error:" < $resdir/Make.out
then
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh b/tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh
index 8426fe1f15ee..1871d00bccd7 100755
--- a/tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh
@@ -11,6 +11,7 @@
#
# The "directory" above should end with the date/time directory, for example,
# "tools/testing/selftests/rcutorture/res/2018.02.25-14:27:27".
+# Returns error status reflecting the success (or not) of the specified run.
#
# Copyright (C) IBM Corporation, 2018
#
@@ -56,6 +57,8 @@ done
if test -n "$files"
then
$editor $files
+ exit 1
else
echo No errors in console logs.
+ exit 0
fi
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh
index 2adde6aaafdb..e5edd5198725 100755
--- a/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh
@@ -7,6 +7,8 @@
#
# Usage: kvm-recheck.sh resdir ...
#
+# Returns status reflecting the success or not of the last run specified.
+#
# Copyright (C) IBM Corporation, 2011
#
# Authors: Paul E. McKenney <paulmck@linux.ibm.com>
@@ -28,8 +30,16 @@ do
TORTURE_SUITE="`cat $i/../TORTURE_SUITE`"
rm -f $i/console.log.*.diags
kvm-recheck-${TORTURE_SUITE}.sh $i
- if test -f "$i/console.log"
+ if test -f "$i/qemu-retval" && test "`cat $i/qemu-retval`" -ne 0 && test "`cat $i/qemu-retval`" -ne 137
+ then
+ echo QEMU error, output:
+ cat $i/qemu-output
+ elif test -f "$i/console.log"
then
+ if test -f "$i/qemu-retval" && test "`cat $i/qemu-retval`" -eq 137
+ then
+ echo QEMU killed
+ fi
configcheck.sh $i/.config $i/ConfigFragment
if test -r $i/Make.oldconfig.err
then
@@ -58,3 +68,4 @@ do
fi
done
done
+EDITOR=echo kvm-find-errors.sh "${@: -1}" > /dev/null 2>&1
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh
index 0eb1ec16d78a..33c669619736 100755
--- a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh
@@ -36,11 +36,6 @@ config_template=${1}
config_dir=`echo $config_template | sed -e 's,/[^/]*$,,'`
title=`echo $config_template | sed -e 's/^.*\///'`
builddir=${2}
-if test -z "$builddir" -o ! -d "$builddir" -o ! -w "$builddir"
-then
- echo "kvm-test-1-run.sh :$builddir: Not a writable directory, cannot build into it"
- exit 1
-fi
resdir=${3}
if test -z "$resdir" -o ! -d "$resdir" -o ! -w "$resdir"
then
@@ -85,18 +80,18 @@ then
ln -s $base_resdir/.config $resdir # for kvm-recheck.sh
# Arch-independent indicator
touch $resdir/builtkernel
-elif kvm-build.sh $T/Kc2 $builddir $resdir
+elif kvm-build.sh $T/Kc2 $resdir
then
# Had to build a kernel for this test.
- QEMU="`identify_qemu $builddir/vmlinux`"
+ QEMU="`identify_qemu vmlinux`"
BOOT_IMAGE="`identify_boot_image $QEMU`"
- cp $builddir/vmlinux $resdir
- cp $builddir/.config $resdir
- cp $builddir/Module.symvers $resdir > /dev/null || :
- cp $builddir/System.map $resdir > /dev/null || :
+ cp vmlinux $resdir
+ cp .config $resdir
+ cp Module.symvers $resdir > /dev/null || :
+ cp System.map $resdir > /dev/null || :
if test -n "$BOOT_IMAGE"
then
- cp $builddir/$BOOT_IMAGE $resdir
+ cp $BOOT_IMAGE $resdir
KERNEL=$resdir/${BOOT_IMAGE##*/}
# Arch-independent indicator
touch $resdir/builtkernel
@@ -107,7 +102,7 @@ then
parse-build.sh $resdir/Make.out $title
else
# Build failed.
- cp $builddir/.config $resdir || :
+ cp .config $resdir || :
echo Build failed, not running KVM, see $resdir.
if test -f $builddir.wait
then
@@ -165,7 +160,7 @@ then
fi
echo "NOTE: $QEMU either did not run or was interactive" > $resdir/console.log
echo $QEMU $qemu_args -m $TORTURE_QEMU_MEM -kernel $KERNEL -append \"$qemu_append $boot_args\" > $resdir/qemu-cmd
-( $QEMU $qemu_args -m $TORTURE_QEMU_MEM -kernel $KERNEL -append "$qemu_append $boot_args"& echo $! > $resdir/qemu_pid; wait `cat $resdir/qemu_pid`; echo $? > $resdir/qemu-retval ) &
+( $QEMU $qemu_args -m $TORTURE_QEMU_MEM -kernel $KERNEL -append "$qemu_append $boot_args" > $resdir/qemu-output 2>&1 & echo $! > $resdir/qemu_pid; wait `cat $resdir/qemu_pid`; echo $? > $resdir/qemu-retval ) &
commandcompleted=0
sleep 10 # Give qemu's pid a chance to reach the file
if test -s "$resdir/qemu_pid"
@@ -232,7 +227,7 @@ then
must_continue=yes
fi
last_ts="`tail $resdir/console.log | grep '^\[ *[0-9]\+\.[0-9]\+]' | tail -1 | sed -e 's/^\[ *//' -e 's/\..*$//'`"
- if test -z "last_ts"
+ if test -z "$last_ts"
then
last_ts=0
fi
diff --git a/tools/testing/selftests/rcutorture/bin/kvm.sh b/tools/testing/selftests/rcutorture/bin/kvm.sh
index 8f1e337b9b54..72518580df23 100755
--- a/tools/testing/selftests/rcutorture/bin/kvm.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm.sh
@@ -24,6 +24,7 @@ dur=$((30*60))
dryrun=""
KVM="`pwd`/tools/testing/selftests/rcutorture"; export KVM
PATH=${KVM}/bin:$PATH; export PATH
+TORTURE_ALLOTED_CPUS=""
TORTURE_DEFCONFIG=defconfig
TORTURE_BOOT_IMAGE=""
TORTURE_INITRD="$KVM/initrd"; export TORTURE_INITRD
@@ -32,6 +33,7 @@ TORTURE_KMAKE_ARG=""
TORTURE_QEMU_MEM=512
TORTURE_SHUTDOWN_GRACE=180
TORTURE_SUITE=rcu
+TORTURE_TRUST_MAKE=""
resdir=""
configs=""
cpus=0
@@ -62,6 +64,7 @@ usage () {
echo " --qemu-cmd qemu-system-..."
echo " --results absolute-pathname"
echo " --torture rcu"
+ echo " --trust-make"
exit 1
}
@@ -89,6 +92,7 @@ do
--cpus)
checkarg --cpus "(number)" "$#" "$2" '^[0-9]*$' '^--'
cpus=$2
+ TORTURE_ALLOTED_CPUS="$2"
shift
;;
--datestamp)
@@ -173,6 +177,9 @@ do
jitter=0
fi
;;
+ --trust-make)
+ TORTURE_TRUST_MAKE="y"
+ ;;
*)
echo Unknown argument $1
usage
@@ -285,6 +292,7 @@ cat << ___EOF___ > $T/script
CONFIGFRAG="$CONFIGFRAG"; export CONFIGFRAG
KVM="$KVM"; export KVM
PATH="$PATH"; export PATH
+TORTURE_ALLOTED_CPUS="$TORTURE_ALLOTED_CPUS"; export TORTURE_ALLOTED_CPUS
TORTURE_BOOT_IMAGE="$TORTURE_BOOT_IMAGE"; export TORTURE_BOOT_IMAGE
TORTURE_BUILDONLY="$TORTURE_BUILDONLY"; export TORTURE_BUILDONLY
TORTURE_DEFCONFIG="$TORTURE_DEFCONFIG"; export TORTURE_DEFCONFIG
@@ -297,6 +305,7 @@ TORTURE_QEMU_MAC="$TORTURE_QEMU_MAC"; export TORTURE_QEMU_MAC
TORTURE_QEMU_MEM="$TORTURE_QEMU_MEM"; export TORTURE_QEMU_MEM
TORTURE_SHUTDOWN_GRACE="$TORTURE_SHUTDOWN_GRACE"; export TORTURE_SHUTDOWN_GRACE
TORTURE_SUITE="$TORTURE_SUITE"; export TORTURE_SUITE
+TORTURE_TRUST_MAKE="$TORTURE_TRUST_MAKE"; export TORTURE_TRUST_MAKE
if ! test -e $resdir
then
mkdir -p "$resdir" || :
@@ -342,7 +351,7 @@ function dump(first, pastlast, batchnum)
print "needqemurun="
jn=1
for (j = first; j < pastlast; j++) {
- builddir=KVM "/b1"
+ builddir=KVM "/b" j - first + 1
cpusr[jn] = cpus[j];
if (cfrep[cf[j]] == "") {
cfr[jn] = cf[j];
@@ -358,7 +367,6 @@ function dump(first, pastlast, batchnum)
print "echo ", cfr[jn], cpusr[jn] ovf ": Starting build. `date` | tee -a " rd "log";
print "rm -f " builddir ".*";
print "touch " builddir ".wait";
- print "mkdir " builddir " > /dev/null 2>&1 || :";
print "mkdir " rd cfr[jn] " || :";
print "kvm-test-1-run.sh " CONFIGDIR cf[j], builddir, rd cfr[jn], dur " \"" TORTURE_QEMU_ARG "\" \"" TORTURE_BOOTARGS "\" > " rd cfr[jn] "/kvm-test-1-run.sh.out 2>&1 &"
print "echo ", cfr[jn], cpusr[jn] ovf ": Waiting for build to complete. `date` | tee -a " rd "log";
@@ -464,3 +472,5 @@ else
fi
# Tracing: trace_event=rcu:rcu_grace_period,rcu:rcu_future_grace_period,rcu:rcu_grace_period_init,rcu:rcu_nocb_wake,rcu:rcu_preempt_task,rcu:rcu_unlock_preempted_task,rcu:rcu_quiescent_state_report,rcu:rcu_fqs,rcu:rcu_callback,rcu:rcu_kfree_callback,rcu:rcu_batch_start,rcu:rcu_invoke_callback,rcu:rcu_invoke_kfree_callback,rcu:rcu_batch_end,rcu:rcu_torture_read,rcu:rcu_barrier
+# Function-graph tracing: ftrace=function_graph ftrace_graph_filter=sched_setaffinity,migration_cpu_stop
+# Also --kconfig "CONFIG_FUNCTION_TRACER=y CONFIG_FUNCTION_GRAPH_TRACER=y"
diff --git a/tools/testing/selftests/rcutorture/bin/parse-build.sh b/tools/testing/selftests/rcutorture/bin/parse-build.sh
index 0701b3bf6ade..09155c15ea65 100755
--- a/tools/testing/selftests/rcutorture/bin/parse-build.sh
+++ b/tools/testing/selftests/rcutorture/bin/parse-build.sh
@@ -21,7 +21,7 @@ mkdir $T
. functions.sh
-if grep -q CC < $F
+if grep -q CC < $F || test -n "$TORTURE_TRUST_MAKE"
then
:
else
diff --git a/tools/testing/selftests/rcutorture/bin/parse-console.sh b/tools/testing/selftests/rcutorture/bin/parse-console.sh
index 4508373a922f..4bf62d7b1cbc 100755
--- a/tools/testing/selftests/rcutorture/bin/parse-console.sh
+++ b/tools/testing/selftests/rcutorture/bin/parse-console.sh
@@ -106,6 +106,7 @@ fi | tee -a $file.diags
egrep 'Badness|WARNING:|Warn|BUG|===========|Call Trace:|Oops:|detected stalls on CPUs/tasks:|self-detected stall on CPU|Stall ended before state dump start|\?\?\? Writer stall state|rcu_.*kthread starved for' < $file |
grep -v 'ODEBUG: ' |
+grep -v 'This means that this is a DEBUG kernel and it is' |
grep -v 'Warning: unable to open an initial console' > $T.diags
if test -s $T.diags
then
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/CFcommon b/tools/testing/selftests/rcutorture/configs/rcu/CFcommon
index d2d2a86139db..e19a444a0684 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/CFcommon
+++ b/tools/testing/selftests/rcutorture/configs/rcu/CFcommon
@@ -1,2 +1,5 @@
CONFIG_RCU_TORTURE_TEST=y
CONFIG_PRINTK_TIME=y
+CONFIG_HYPERVISOR_GUEST=y
+CONFIG_PARAVIRT=y
+CONFIG_KVM_GUEST=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot
index ea47da95374b..d6da9a61d44a 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot
@@ -3,3 +3,4 @@ rcutree.gp_preinit_delay=3
rcutree.gp_init_delay=3
rcutree.gp_cleanup_delay=3
rcu_nocbs=0
+rcutorture.fwd_progress=0
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE03.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE03.boot
index 5c3213cc3ad7..1c218944b1e9 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE03.boot
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE03.boot
@@ -3,3 +3,4 @@ rcutree.gp_preinit_delay=12
rcutree.gp_init_delay=3
rcutree.gp_cleanup_delay=3
rcutree.kthread_prio=2
+threadirqs
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TRIVIAL b/tools/testing/selftests/rcutorture/configs/rcu/TRIVIAL
new file mode 100644
index 000000000000..4d8eb5bfb6f6
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TRIVIAL
@@ -0,0 +1,14 @@
+CONFIG_SMP=y
+CONFIG_NR_CPUS=8
+CONFIG_PREEMPT_NONE=y
+CONFIG_PREEMPT_VOLUNTARY=n
+CONFIG_PREEMPT=n
+CONFIG_HZ_PERIODIC=n
+CONFIG_NO_HZ_IDLE=y
+CONFIG_NO_HZ_FULL=n
+CONFIG_HOTPLUG_CPU=n
+CONFIG_SUSPEND=n
+CONFIG_HIBERNATION=n
+CONFIG_DEBUG_LOCK_ALLOC=n
+CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
+CONFIG_RCU_EXPERT=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TRIVIAL.boot b/tools/testing/selftests/rcutorture/configs/rcu/TRIVIAL.boot
new file mode 100644
index 000000000000..7017f5f5a55f
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TRIVIAL.boot
@@ -0,0 +1,3 @@
+rcutorture.torture_type=trivial
+rcutorture.onoff_interval=0
+rcutorture.shuffle_interval=0
diff --git a/tools/testing/selftests/rseq/rseq-arm.h b/tools/testing/selftests/rseq/rseq-arm.h
index 84f28f147fb6..5943c816c07c 100644
--- a/tools/testing/selftests/rseq/rseq-arm.h
+++ b/tools/testing/selftests/rseq/rseq-arm.h
@@ -6,6 +6,8 @@
*/
/*
+ * - ARM little endian
+ *
* RSEQ_SIG uses the udf A32 instruction with an uncommon immediate operand
* value 0x5de3. This traps if user-space reaches this instruction by mistake,
* and the uncommon operand ensures the kernel does not move the instruction
@@ -22,36 +24,40 @@
* def3 udf #243 ; 0xf3
* e7f5 b.n <7f5>
*
- * pre-ARMv6 big endian code:
- * e7f5 b.n <7f5>
- * def3 udf #243 ; 0xf3
+ * - ARMv6+ big endian (BE8):
*
* ARMv6+ -mbig-endian generates mixed endianness code vs data: little-endian
- * code and big-endian data. Ensure the RSEQ_SIG data signature matches code
- * endianness. Prior to ARMv6, -mbig-endian generates big-endian code and data
- * (which match), so there is no need to reverse the endianness of the data
- * representation of the signature. However, the choice between BE32 and BE8
- * is done by the linker, so we cannot know whether code and data endianness
- * will be mixed before the linker is invoked.
+ * code and big-endian data. The data value of the signature needs to have its
+ * byte order reversed to generate the trap instruction:
+ *
+ * Data: 0xf3def5e7
+ *
+ * Translates to this A32 instruction pattern:
+ *
+ * e7f5def3 udf #24035 ; 0x5de3
+ *
+ * Translates to this T16 instruction pattern:
+ *
+ * def3 udf #243 ; 0xf3
+ * e7f5 b.n <7f5>
+ *
+ * - Prior to ARMv6 big endian (BE32):
+ *
+ * Prior to ARMv6, -mbig-endian generates big-endian code and data
+ * (which match), so the endianness of the data representation of the
+ * signature should not be reversed. However, the choice between BE32
+ * and BE8 is done by the linker, so we cannot know whether code and
+ * data endianness will be mixed before the linker is invoked. So rather
+ * than try to play tricks with the linker, the rseq signature is simply
+ * data (not a trap instruction) prior to ARMv6 on big endian. This is
+ * why the signature is expressed as data (.word) rather than as
+ * instruction (.inst) in assembler.
*/
-#define RSEQ_SIG_CODE 0xe7f5def3
-
-#ifndef __ASSEMBLER__
-
-#define RSEQ_SIG_DATA \
- ({ \
- int sig; \
- asm volatile ("b 2f\n\t" \
- "1: .inst " __rseq_str(RSEQ_SIG_CODE) "\n\t" \
- "2:\n\t" \
- "ldr %[sig], 1b\n\t" \
- : [sig] "=r" (sig)); \
- sig; \
- })
-
-#define RSEQ_SIG RSEQ_SIG_DATA
-
+#ifdef __ARMEB__
+#define RSEQ_SIG 0xf3def5e7 /* udf #24035 ; 0x5de3 (ARMv6+) */
+#else
+#define RSEQ_SIG 0xe7f5def3 /* udf #24035 ; 0x5de3 */
#endif
#define rseq_smp_mb() __asm__ __volatile__ ("dmb" ::: "memory", "cc")
@@ -125,8 +131,7 @@ do { \
__rseq_str(table_label) ":\n\t" \
".word " __rseq_str(version) ", " __rseq_str(flags) "\n\t" \
".word " __rseq_str(start_ip) ", 0x0, " __rseq_str(post_commit_offset) ", 0x0, " __rseq_str(abort_ip) ", 0x0\n\t" \
- ".arm\n\t" \
- ".inst " __rseq_str(RSEQ_SIG_CODE) "\n\t" \
+ ".word " __rseq_str(RSEQ_SIG) "\n\t" \
__rseq_str(label) ":\n\t" \
teardown \
"b %l[" __rseq_str(abort_label) "]\n\t"
diff --git a/tools/testing/selftests/rtc/rtctest.c b/tools/testing/selftests/rtc/rtctest.c
index b2065536d407..66af608fb4c6 100644
--- a/tools/testing/selftests/rtc/rtctest.c
+++ b/tools/testing/selftests/rtc/rtctest.c
@@ -49,7 +49,7 @@ TEST_F(rtc, date_read) {
rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
}
-TEST_F(rtc, uie_read) {
+TEST_F_TIMEOUT(rtc, uie_read, NUM_UIE + 2) {
int i, rc, irq = 0;
unsigned long data;
@@ -211,7 +211,7 @@ TEST_F(rtc, alarm_wkalm_set) {
ASSERT_EQ(new, secs);
}
-TEST_F(rtc, alarm_alm_set_minute) {
+TEST_F_TIMEOUT(rtc, alarm_alm_set_minute, 65) {
struct timeval tv = { .tv_sec = 62 };
unsigned long data;
struct rtc_time tm;
@@ -264,7 +264,7 @@ TEST_F(rtc, alarm_alm_set_minute) {
ASSERT_EQ(new, secs);
}
-TEST_F(rtc, alarm_wkalm_set_minute) {
+TEST_F_TIMEOUT(rtc, alarm_wkalm_set_minute, 65) {
struct timeval tv = { .tv_sec = 62 };
struct rtc_wkalrm alarm = { 0 };
struct rtc_time tm;
diff --git a/tools/testing/selftests/rtc/setdate.c b/tools/testing/selftests/rtc/setdate.c
index 2cb78489eca4..b303890b3de2 100644
--- a/tools/testing/selftests/rtc/setdate.c
+++ b/tools/testing/selftests/rtc/setdate.c
@@ -1,18 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* Real Time Clock Driver Test
* by: Benjamin Gaignard (benjamin.gaignard@linaro.org)
*
* To build
* gcc rtctest_setdate.c -o rtctest_setdate
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <stdio.h>
diff --git a/tools/testing/selftests/safesetid/safesetid-test.c b/tools/testing/selftests/safesetid/safesetid-test.c
index 892c8e8b1b8b..8f40c6ecdad1 100644
--- a/tools/testing/selftests/safesetid/safesetid-test.c
+++ b/tools/testing/selftests/safesetid/safesetid-test.c
@@ -142,23 +142,19 @@ static void ensure_securityfs_mounted(void)
static void write_policies(void)
{
+ static char *policy_str =
+ "1:2\n"
+ "1:3\n"
+ "2:2\n"
+ "3:3\n";
ssize_t written;
int fd;
fd = open(add_whitelist_policy_file, O_WRONLY);
if (fd < 0)
die("cant open add_whitelist_policy file\n");
- written = write(fd, "1:2", strlen("1:2"));
- if (written != strlen("1:2")) {
- if (written >= 0) {
- die("short write to %s\n", add_whitelist_policy_file);
- } else {
- die("write to %s failed: %s\n",
- add_whitelist_policy_file, strerror(errno));
- }
- }
- written = write(fd, "1:3", strlen("1:3"));
- if (written != strlen("1:3")) {
+ written = write(fd, policy_str, strlen(policy_str));
+ if (written != strlen(policy_str)) {
if (written >= 0) {
die("short write to %s\n", add_whitelist_policy_file);
} else {
diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c
index 0fad0dc62338..6ef7f16c4cf5 100644
--- a/tools/testing/selftests/seccomp/seccomp_bpf.c
+++ b/tools/testing/selftests/seccomp/seccomp_bpf.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by the GPLv2 license.
*
* Test code for seccomp bpf.
*/
@@ -1775,13 +1775,18 @@ void tracer_ptrace(struct __test_metadata *_metadata, pid_t tracee,
unsigned long msg;
static bool entry;
- /* Make sure we got an empty message. */
+ /*
+ * The traditional way to tell PTRACE_SYSCALL entry/exit
+ * is by counting.
+ */
+ entry = !entry;
+
+ /* Make sure we got an appropriate message. */
ret = ptrace(PTRACE_GETEVENTMSG, tracee, NULL, &msg);
EXPECT_EQ(0, ret);
- EXPECT_EQ(0, msg);
+ EXPECT_EQ(entry ? PTRACE_EVENTMSG_SYSCALL_ENTRY
+ : PTRACE_EVENTMSG_SYSCALL_EXIT, msg);
- /* The only way to tell PTRACE_SYSCALL entry/exit is by counting. */
- entry = !entry;
if (!entry)
return;
diff --git a/tools/testing/selftests/sigaltstack/Makefile b/tools/testing/selftests/sigaltstack/Makefile
index f68fbf80d8be..3e96d5d47036 100644
--- a/tools/testing/selftests/sigaltstack/Makefile
+++ b/tools/testing/selftests/sigaltstack/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
CFLAGS = -Wall
TEST_GEN_PROGS = sas
diff --git a/tools/testing/selftests/size/Makefile b/tools/testing/selftests/size/Makefile
index 4685b3e421fc..b87facc00a6e 100644
--- a/tools/testing/selftests/size/Makefile
+++ b/tools/testing/selftests/size/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
CFLAGS := -static -ffreestanding -nostartfiles -s
TEST_GEN_PROGS := get_size
diff --git a/tools/testing/selftests/size/get_size.c b/tools/testing/selftests/size/get_size.c
index d4b59ab979a0..2ad45b944355 100644
--- a/tools/testing/selftests/size/get_size.c
+++ b/tools/testing/selftests/size/get_size.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2014 Sony Mobile Communications Inc.
*
- * Licensed under the terms of the GNU GPL License version 2
- *
* Selftest for runtime system size
*
* Prints the amount of RAM that the currently running system is using.
diff --git a/tools/testing/selftests/static_keys/Makefile b/tools/testing/selftests/static_keys/Makefile
index 9cdadf37f114..aa64104c7860 100644
--- a/tools/testing/selftests/static_keys/Makefile
+++ b/tools/testing/selftests/static_keys/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# Makefile for static keys selftests
# No binaries, but make sure arg-less "make" doesn't trigger "run_tests"
diff --git a/tools/testing/selftests/sysctl/Makefile b/tools/testing/selftests/sysctl/Makefile
index 95c320b354e8..110301f9f5be 100644
--- a/tools/testing/selftests/sysctl/Makefile
+++ b/tools/testing/selftests/sysctl/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# Makefile for sysctl selftests.
# Expects kernel.sysctl_writes_strict=1.
diff --git a/tools/testing/selftests/tc-testing/README b/tools/testing/selftests/tc-testing/README
index f9281e8aa313..b0954c873e2f 100644
--- a/tools/testing/selftests/tc-testing/README
+++ b/tools/testing/selftests/tc-testing/README
@@ -12,10 +12,10 @@ REQUIREMENTS
* Minimum Python version of 3.4. Earlier 3.X versions may work but are not
guaranteed.
-* The kernel must have network namespace support
+* The kernel must have network namespace support if using nsPlugin
* The kernel must have veth support available, as a veth pair is created
- prior to running the tests.
+ prior to running the tests when using nsPlugin.
* The kernel must have the appropriate infrastructure enabled to run all tdc
unit tests. See the config file in this directory for minimum required
@@ -53,8 +53,12 @@ commands being tested must be run as root. The code that enforces
execution by root uid has been moved into a plugin (see PLUGIN
ARCHITECTURE, below).
-If nsPlugin is linked, all tests are executed inside a network
-namespace to prevent conflicts within the host.
+Tests that use a network device should have nsPlugin.py listed as a
+requirement for that test. nsPlugin executes all commands within a
+network namespace and creates a veth pair which may be used in those test
+cases. To disable execution within the namespace, pass the -N option
+to tdc when starting a test run; the veth pair will still be created
+by the plugin.
Running tdc without any arguments will run all tests. Refer to the section
on command line arguments for more information, or run:
@@ -124,7 +128,9 @@ optional arguments:
-v, --verbose Show the commands that are being run
-N, --notap Suppress tap results for command under test
-d DEVICE, --device DEVICE
- Execute the test case in flower category
+ Execute test cases that use a physical device, where
+ DEVICE is its name. (If not defined, tests that require
+ a physical device will be skipped)
-P, --pause Pause execution just before post-suite stage
selection:
@@ -154,8 +160,8 @@ action:
netns:
options for nsPlugin (run commands in net namespace)
- -n, --namespace
- Run commands in namespace as specified in tdc_config.py
+ -N, --no-namespace
+ Do not run commands in a network namespace.
valgrind:
options for valgrindPlugin (run command under test under Valgrind)
@@ -171,7 +177,8 @@ was in the tdc.py script has been moved into the plugins.
The plugins are in the directory plugin-lib. The are executed from
directory plugins. Put symbolic links from plugins to plugin-lib,
-and name them according to the order you want them to run.
+and name them according to the order you want them to run. This is not
+necessary if a test case being run requires a specific plugin to work.
Example:
@@ -223,7 +230,8 @@ directory:
- rootPlugin.py:
implements the enforcement of running as root
- nsPlugin.py:
- sets up a network namespace and runs all commands in that namespace
+ sets up a network namespace and runs all commands in that namespace,
+ while also setting up dummy devices to be used in testing.
- valgrindPlugin.py
runs each command in the execute stage under valgrind,
and checks for leaks.
diff --git a/tools/testing/selftests/tc-testing/TdcPlugin.py b/tools/testing/selftests/tc-testing/TdcPlugin.py
index b980a565fa89..79f3ca8617c9 100644
--- a/tools/testing/selftests/tc-testing/TdcPlugin.py
+++ b/tools/testing/selftests/tc-testing/TdcPlugin.py
@@ -18,12 +18,11 @@ class TdcPlugin:
if self.args.verbose > 1:
print(' -- {}.post_suite'.format(self.sub_class))
- def pre_case(self, testid, test_name, test_skip):
+ def pre_case(self, caseinfo, test_skip):
'''run commands before test_runner does one test'''
if self.args.verbose > 1:
print(' -- {}.pre_case'.format(self.sub_class))
- self.args.testid = testid
- self.args.test_name = test_name
+ self.args.caseinfo = caseinfo
self.args.test_skip = test_skip
def post_case(self):
diff --git a/tools/testing/selftests/tc-testing/config b/tools/testing/selftests/tc-testing/config
index 203302065458..7c551968d184 100644
--- a/tools/testing/selftests/tc-testing/config
+++ b/tools/testing/selftests/tc-testing/config
@@ -38,11 +38,12 @@ CONFIG_NET_ACT_CSUM=m
CONFIG_NET_ACT_VLAN=m
CONFIG_NET_ACT_BPF=m
CONFIG_NET_ACT_CONNMARK=m
+CONFIG_NET_ACT_CTINFO=m
CONFIG_NET_ACT_SKBMOD=m
CONFIG_NET_ACT_IFE=m
CONFIG_NET_ACT_TUNNEL_KEY=m
+CONFIG_NET_ACT_MPLS=m
CONFIG_NET_IFE_SKBMARK=m
CONFIG_NET_IFE_SKBPRIO=m
CONFIG_NET_IFE_SKBTCINDEX=m
-CONFIG_NET_CLS_IND=y
CONFIG_NET_SCH_FIFO=y
diff --git a/tools/testing/selftests/tc-testing/creating-testcases/scapy-example.json b/tools/testing/selftests/tc-testing/creating-testcases/scapy-example.json
new file mode 100644
index 000000000000..5a9377b72d7f
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/creating-testcases/scapy-example.json
@@ -0,0 +1,98 @@
+[
+ {
+ "id": "b1e9",
+ "name": "Test matching of source IP",
+ "category": [
+ "actions",
+ "scapy"
+ ],
+ "plugins": {
+ "requires": [
+ "nsPlugin",
+ "scapyPlugin"
+ ]
+ },
+ "setup": [
+ [
+ "$TC qdisc del dev $DEV1 ingress",
+ 0,
+ 1,
+ 2,
+ 255
+ ],
+ "$TC qdisc add dev $DEV1 ingress"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: prio 3 protocol ip flower src_ip 16.61.16.61 flowid 1:1 action ok",
+ "scapy": {
+ "iface": "$DEV0",
+ "count": 1,
+ "packet": "Ether(type=0x800)/IP(src='16.61.16.61')/ICMP()"
+ },
+ "expExitCode": "0",
+ "verifyCmd": "$TC -s -j filter ls dev $DEV1 ingress prio 3",
+ "matchJSON": [
+ {
+ "path": [
+ 1,
+ "options",
+ "actions",
+ 0,
+ "stats",
+ "packets"
+ ],
+ "value": 1
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DEV1 ingress"
+ ]
+ },
+ {
+ "id": "e9c4",
+ "name": "Test matching of source IP with wrong count",
+ "category": [
+ "actions",
+ "scapy"
+ ],
+ "plugins": {
+ "requires": [
+ "nsPlugin",
+ "scapyPlugin"
+ ]
+ },
+ "setup": [
+ [
+ "$TC qdisc del dev $DEV1 ingress",
+ 0,
+ 1,
+ 2,
+ 255
+ ],
+ "$TC qdisc add dev $DEV1 ingress"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: prio 3 protocol ip flower src_ip 16.61.16.61 flowid 1:1 action ok",
+ "scapy": {
+ "iface": "$DEV0",
+ "count": 3,
+ "packet": "Ether(type=0x800)/IP(src='16.61.16.61')/ICMP()"
+ },
+ "expExitCode": "0",
+ "verifyCmd": "$TC -s -j filter ls dev $DEV1 parent ffff:",
+ "matchJSON": [
+ {
+ "path": [
+ 1,
+ "options",
+ "actions",
+ 0,
+ "stats",
+ "packets"
+ ],
+ "value": 1
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DEV1 ingress"
+ ]
+ }
+]
diff --git a/tools/testing/selftests/tc-testing/plugin-lib/buildebpfPlugin.py b/tools/testing/selftests/tc-testing/plugin-lib/buildebpfPlugin.py
index 9f0ba10c44b4..e98c36750fae 100644
--- a/tools/testing/selftests/tc-testing/plugin-lib/buildebpfPlugin.py
+++ b/tools/testing/selftests/tc-testing/plugin-lib/buildebpfPlugin.py
@@ -34,8 +34,9 @@ class SubPlugin(TdcPlugin):
'buildebpf',
'options for buildebpfPlugin')
self.argparser_group.add_argument(
- '-B', '--buildebpf', action='store_true',
- help='build eBPF programs')
+ '--nobuildebpf', action='store_false', default=True,
+ dest='buildebpf',
+ help='Don\'t build eBPF programs')
return self.argparser
diff --git a/tools/testing/selftests/tc-testing/plugin-lib/nsPlugin.py b/tools/testing/selftests/tc-testing/plugin-lib/nsPlugin.py
index a194b1af2b30..9539cffa9e5e 100644
--- a/tools/testing/selftests/tc-testing/plugin-lib/nsPlugin.py
+++ b/tools/testing/selftests/tc-testing/plugin-lib/nsPlugin.py
@@ -18,6 +18,8 @@ class SubPlugin(TdcPlugin):
if self.args.namespace:
self._ns_create()
+ else:
+ self._ports_create()
def post_suite(self, index):
'''run commands after test_runner goes into a test loop'''
@@ -27,6 +29,8 @@ class SubPlugin(TdcPlugin):
if self.args.namespace:
self._ns_destroy()
+ else:
+ self._ports_destroy()
def add_args(self, parser):
super().add_args(parser)
@@ -34,8 +38,8 @@ class SubPlugin(TdcPlugin):
'netns',
'options for nsPlugin(run commands in net namespace)')
self.argparser_group.add_argument(
- '-n', '--namespace', action='store_true',
- help='Run commands in namespace')
+ '-N', '--no-namespace', action='store_false', default=True,
+ dest='namespace', help='Don\'t run commands in namespace')
return self.argparser
def adjust_command(self, stage, command):
@@ -60,7 +64,7 @@ class SubPlugin(TdcPlugin):
cmdlist.insert(0, self.args.NAMES['NS'])
cmdlist.insert(0, 'exec')
cmdlist.insert(0, 'netns')
- cmdlist.insert(0, 'ip')
+ cmdlist.insert(0, self.args.NAMES['IP'])
else:
pass
@@ -73,26 +77,36 @@ class SubPlugin(TdcPlugin):
print('adjust_command: return command [{}]'.format(command))
return command
+ def _ports_create(self):
+ cmd = '$IP link add $DEV0 type veth peer name $DEV1'
+ self._exec_cmd('pre', cmd)
+ cmd = '$IP link set $DEV0 up'
+ self._exec_cmd('pre', cmd)
+ if not self.args.namespace:
+ cmd = '$IP link set $DEV1 up'
+ self._exec_cmd('pre', cmd)
+
+ def _ports_destroy(self):
+ cmd = '$IP link del $DEV0'
+ self._exec_cmd('post', cmd)
+
def _ns_create(self):
'''
Create the network namespace in which the tests will be run and set up
the required network devices for it.
'''
+ self._ports_create()
if self.args.namespace:
- cmd = 'ip netns add {}'.format(self.args.NAMES['NS'])
- self._exec_cmd('pre', cmd)
- cmd = 'ip link add $DEV0 type veth peer name $DEV1'
- self._exec_cmd('pre', cmd)
- cmd = 'ip link set $DEV1 netns {}'.format(self.args.NAMES['NS'])
+ cmd = '$IP netns add {}'.format(self.args.NAMES['NS'])
self._exec_cmd('pre', cmd)
- cmd = 'ip link set $DEV0 up'
+ cmd = '$IP link set $DEV1 netns {}'.format(self.args.NAMES['NS'])
self._exec_cmd('pre', cmd)
- cmd = 'ip -n {} link set $DEV1 up'.format(self.args.NAMES['NS'])
+ cmd = '$IP -n {} link set $DEV1 up'.format(self.args.NAMES['NS'])
self._exec_cmd('pre', cmd)
if self.args.device:
- cmd = 'ip link set $DEV2 netns {}'.format(self.args.NAMES['NS'])
+ cmd = '$IP link set $DEV2 netns {}'.format(self.args.NAMES['NS'])
self._exec_cmd('pre', cmd)
- cmd = 'ip -n {} link set $DEV2 up'.format(self.args.NAMES['NS'])
+ cmd = '$IP -n {} link set $DEV2 up'.format(self.args.NAMES['NS'])
self._exec_cmd('pre', cmd)
def _ns_destroy(self):
@@ -101,7 +115,7 @@ class SubPlugin(TdcPlugin):
devices as well)
'''
if self.args.namespace:
- cmd = 'ip netns delete {}'.format(self.args.NAMES['NS'])
+ cmd = '$IP netns delete {}'.format(self.args.NAMES['NS'])
self._exec_cmd('post', cmd)
def _exec_cmd(self, stage, command):
diff --git a/tools/testing/selftests/tc-testing/plugin-lib/scapyPlugin.py b/tools/testing/selftests/tc-testing/plugin-lib/scapyPlugin.py
new file mode 100644
index 000000000000..229ee185b27e
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/plugin-lib/scapyPlugin.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+
+import os
+import signal
+from string import Template
+import subprocess
+import time
+from TdcPlugin import TdcPlugin
+
+from tdc_config import *
+
+try:
+ from scapy.all import *
+except ImportError:
+ print("Unable to import the scapy python module.")
+ print("\nIf not already installed, you may do so with:")
+ print("\t\tpip3 install scapy==2.4.2")
+ exit(1)
+
+class SubPlugin(TdcPlugin):
+ def __init__(self):
+ self.sub_class = 'scapy/SubPlugin'
+ super().__init__()
+
+ def post_execute(self):
+ if 'scapy' not in self.args.caseinfo:
+ if self.args.verbose:
+ print('{}.post_execute: no scapy info in test case'.format(self.sub_class))
+ return
+
+ # Check for required fields
+ scapyinfo = self.args.caseinfo['scapy']
+ scapy_keys = ['iface', 'count', 'packet']
+ missing_keys = []
+ keyfail = False
+ for k in scapy_keys:
+ if k not in scapyinfo:
+ keyfail = True
+ missing_keys.add(k)
+ if keyfail:
+ print('{}: Scapy block present in the test, but is missing info:'
+ .format(self.sub_class))
+ print('{}'.format(missing_keys))
+
+ pkt = eval(scapyinfo['packet'])
+ if '$' in scapyinfo['iface']:
+ tpl = Template(scapyinfo['iface'])
+ scapyinfo['iface'] = tpl.safe_substitute(NAMES)
+ for count in range(scapyinfo['count']):
+ sendp(pkt, iface=scapyinfo['iface'])
diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/bpf.json b/tools/testing/selftests/tc-testing/tc-tests/actions/bpf.json
index b074ea9b6fe8..47a3082b6661 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/actions/bpf.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/bpf.json
@@ -54,6 +54,9 @@
"actions",
"bpf"
],
+ "plugins": {
+ "requires": "buildebpfPlugin"
+ },
"setup": [
[
"$TC action flush action bpf",
@@ -78,6 +81,9 @@
"actions",
"bpf"
],
+ "plugins": {
+ "requires": "buildebpfPlugin"
+ },
"setup": [
[
"$TC action flush action bpf",
diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/ct.json b/tools/testing/selftests/tc-testing/tc-tests/actions/ct.json
new file mode 100644
index 000000000000..62b82fe10c89
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/ct.json
@@ -0,0 +1,314 @@
+[
+ {
+ "id": "696a",
+ "name": "Add simple ct action",
+ "category": [
+ "actions",
+ "ct"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action ct",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action ct index 42",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action ct",
+ "matchPattern": "action order [0-9]*: ct zone 0 pipe.*index 42 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action ct"
+ ]
+ },
+ {
+ "id": "9f20",
+ "name": "Add ct clear action",
+ "category": [
+ "actions",
+ "ct"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action ct",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action ct clear index 42",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action ct",
+ "matchPattern": "action order [0-9]*: ct clear pipe.*index 42 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action ct"
+ ]
+ },
+ {
+ "id": "5bea",
+ "name": "Try ct with zone",
+ "category": [
+ "actions",
+ "ct"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action ct",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action ct zone 404 index 42",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action ct",
+ "matchPattern": "action order [0-9]*: ct zone 404 pipe.*index 42 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action ct"
+ ]
+ },
+ {
+ "id": "d5d6",
+ "name": "Try ct with zone, commit",
+ "category": [
+ "actions",
+ "ct"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action ct",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action ct zone 404 commit index 42",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action ct",
+ "matchPattern": "action order [0-9]*: ct commit zone 404 pipe.*index 42 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action ct"
+ ]
+ },
+ {
+ "id": "029f",
+ "name": "Try ct with zone, commit, mark",
+ "category": [
+ "actions",
+ "ct"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action ct",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action ct zone 404 commit mark 0x42 index 42",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action ct",
+ "matchPattern": "action order [0-9]*: ct commit mark 66 zone 404 pipe.*index 42 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action ct"
+ ]
+ },
+ {
+ "id": "a58d",
+ "name": "Try ct with zone, commit, mark, nat",
+ "category": [
+ "actions",
+ "ct"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action ct",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action ct zone 404 commit mark 0x42 nat src addr 5.5.5.7 index 42",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action ct",
+ "matchPattern": "action order [0-9]*: ct commit mark 66 zone 404 nat src addr 5.5.5.7 pipe.*index 42 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action ct"
+ ]
+ },
+ {
+ "id": "901b",
+ "name": "Try ct with full nat ipv4 range syntax",
+ "category": [
+ "actions",
+ "ct"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action ct",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action ct commit nat src addr 5.5.5.7-5.5.6.0 port 1000-2000 index 44",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action ct",
+ "matchPattern": "action order [0-9]*: ct commit zone 0 nat src addr 5.5.5.7-5.5.6.0 port 1000-2000 pipe.*index 44 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action ct"
+ ]
+ },
+ {
+ "id": "072b",
+ "name": "Try ct with full nat ipv6 syntax",
+ "category": [
+ "actions",
+ "ct"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action ct",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action ct commit nat src addr 2001::1 port 1000-2000 index 44",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action ct",
+ "matchPattern": "action order [0-9]*: ct commit zone 0 nat src addr 2001::1 port 1000-2000 pipe.*index 44 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action ct"
+ ]
+ },
+ {
+ "id": "3420",
+ "name": "Try ct with full nat ipv6 range syntax",
+ "category": [
+ "actions",
+ "ct"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action ct",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action ct commit nat src addr 2001::1-2001::10 port 1000-2000 index 44",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action ct",
+ "matchPattern": "action order [0-9]*: ct commit zone 0 nat src addr 2001::1-2001::10 port 1000-2000 pipe.*index 44 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action ct"
+ ]
+ },
+ {
+ "id": "4470",
+ "name": "Try ct with full nat ipv6 range syntax + force",
+ "category": [
+ "actions",
+ "ct"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action ct",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action ct commit force nat src addr 2001::1-2001::10 port 1000-2000 index 44",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action ct",
+ "matchPattern": "action order [0-9]*: ct commit force zone 0 nat src addr 2001::1-2001::10 port 1000-2000 pipe.*index 44 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action ct"
+ ]
+ },
+ {
+ "id": "5d88",
+ "name": "Try ct with label",
+ "category": [
+ "actions",
+ "ct"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action ct",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action ct label 123123 index 44",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action ct",
+ "matchPattern": "action order [0-9]*: ct zone 0 label 12312300000000000000000000000000 pipe.*index 44 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action ct"
+ ]
+ },
+ {
+ "id": "04d4",
+ "name": "Try ct with label with mask",
+ "category": [
+ "actions",
+ "ct"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action ct",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action ct label 12312300000000000000000000000001/ffffffff000000000000000000000001 index 44",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action ct",
+ "matchPattern": "action order [0-9]*: ct zone 0 label 12312300000000000000000000000001/ffffffff000000000000000000000001 pipe.*index 44 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action ct"
+ ]
+ },
+ {
+ "id": "9751",
+ "name": "Try ct with mark + mask",
+ "category": [
+ "actions",
+ "ct"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action ct",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action ct mark 0x42/0xf0 index 42",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action ct",
+ "matchPattern": "action order [0-9]*: ct mark 66/0xf0 zone 0 pipe.*index 42 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action ct"
+ ]
+ }
+]
diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json b/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json
index 6e5fb3d25681..2232b21e2510 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json
@@ -459,5 +459,99 @@
"teardown": [
"$TC actions flush action mirred"
]
+ },
+ {
+ "id": "4749",
+ "name": "Add batch of 32 mirred redirect egress actions with cookie",
+ "category": [
+ "actions",
+ "mirred"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mirred",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action mirred egress redirect dev lo index \\$i cookie aabbccddeeff112233445566778800a1 \\\"; args=\"\\$args\\$cmd\"; done && $TC actions add \\$args\"",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mirred",
+ "matchPattern": "^[ \t]+index [0-9]+ ref",
+ "matchCount": "32",
+ "teardown": [
+ "$TC actions flush action mirred"
+ ]
+ },
+ {
+ "id": "5c69",
+ "name": "Delete batch of 32 mirred redirect egress actions",
+ "category": [
+ "actions",
+ "mirred"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mirred",
+ 0,
+ 1,
+ 255
+ ],
+ "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action mirred egress redirect dev lo index \\$i \\\"; args=\\\"\\$args\\$cmd\\\"; done && $TC actions add \\$args\""
+ ],
+ "cmdUnderTest": "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action mirred index \\$i \\\"; args=\"\\$args\\$cmd\"; done && $TC actions del \\$args\"",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mirred",
+ "matchPattern": "^[ \t]+index [0-9]+ ref",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "d3c0",
+ "name": "Add batch of 32 mirred mirror ingress actions with cookie",
+ "category": [
+ "actions",
+ "mirred"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mirred",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action mirred ingress mirror dev lo index \\$i cookie aabbccddeeff112233445566778800a1 \\\"; args=\"\\$args\\$cmd\"; done && $TC actions add \\$args\"",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mirred",
+ "matchPattern": "^[ \t]+index [0-9]+ ref",
+ "matchCount": "32",
+ "teardown": [
+ "$TC actions flush action mirred"
+ ]
+ },
+ {
+ "id": "e684",
+ "name": "Delete batch of 32 mirred mirror ingress actions",
+ "category": [
+ "actions",
+ "mirred"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mirred",
+ 0,
+ 1,
+ 255
+ ],
+ "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action mirred ingress mirror dev lo index \\$i \\\"; args=\\\"\\$args\\$cmd\\\"; done && $TC actions add \\$args\""
+ ],
+ "cmdUnderTest": "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action mirred index \\$i \\\"; args=\"\\$args\\$cmd\"; done && $TC actions del \\$args\"",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mirred",
+ "matchPattern": "^[ \t]+index [0-9]+ ref",
+ "matchCount": "0",
+ "teardown": []
}
]
diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/mpls.json b/tools/testing/selftests/tc-testing/tc-tests/actions/mpls.json
new file mode 100644
index 000000000000..e31a080edc49
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/mpls.json
@@ -0,0 +1,1088 @@
+[
+ {
+ "id": "a933",
+ "name": "Add MPLS dec_ttl action with pipe opcode",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls dec_ttl pipe index 8",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*dec_ttl.*pipe.*index 8 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "08d1",
+ "name": "Add mpls dec_ttl action with pass opcode",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls dec_ttl pass index 8",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions get action mpls index 8",
+ "matchPattern": "action order [0-9]+: mpls.*dec_ttl.*pass.*index 8 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "d786",
+ "name": "Add mpls dec_ttl action with drop opcode",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls dec_ttl drop index 8",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions get action mpls index 8",
+ "matchPattern": "action order [0-9]+: mpls.*dec_ttl.*drop.*index 8 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "f334",
+ "name": "Add mpls dec_ttl action with reclassify opcode",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls dec_ttl reclassify index 8",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions get action mpls index 8",
+ "matchPattern": "action order [0-9]+: mpls.*dec_ttl.*reclassify.*index 8 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "29bd",
+ "name": "Add mpls dec_ttl action with continue opcode",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls dec_ttl continue index 8",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions get action mpls index 8",
+ "matchPattern": "action order [0-9]+: mpls.*dec_ttl.*continue.*index 8 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "48df",
+ "name": "Add mpls dec_ttl action with jump opcode",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls dec_ttl jump 10 index 8",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*jump 10.*index 8 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "62eb",
+ "name": "Add mpls dec_ttl action with trap opcode",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls dec_ttl trap index 8",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*dec_ttl trap.*index 8 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "9118",
+ "name": "Add mpls dec_ttl action with invalid opcode",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls dec_ttl foo index 8",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*dec_ttl.*foo.*index 8 ref",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "6ce1",
+ "name": "Add mpls dec_ttl action with label (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls dec_ttl label 20",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*dec_ttl.*label.*20.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "352f",
+ "name": "Add mpls dec_ttl action with tc (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls dec_ttl tc 3",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*dec_ttl.*tc.*3.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "fa1c",
+ "name": "Add mpls dec_ttl action with ttl (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls dec_ttl ttl 20",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*dec_ttl.*ttl.*20.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "6b79",
+ "name": "Add mpls dec_ttl action with bos (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls dec_ttl bos 1",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*dec_ttl.*bos.*1.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "d4c4",
+ "name": "Add mpls pop action with ip proto",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls pop protocol ipv4",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*pop.*protocol.*ip.*pipe",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "92fe",
+ "name": "Add mpls pop action with mpls proto",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls pop protocol mpls_mc",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*pop.*protocol.*mpls_mc.*pipe",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "7e23",
+ "name": "Add mpls pop action with no protocol (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls pop",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*pop.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "6182",
+ "name": "Add mpls pop action with label (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls pop protocol ipv4 label 20",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*pop.*label.*20.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "6475",
+ "name": "Add mpls pop action with tc (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls pop protocol ipv4 tc 3",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*pop.*tc.*3.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "067b",
+ "name": "Add mpls pop action with ttl (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls pop protocol ipv4 ttl 20",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*pop.*ttl.*20.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "7316",
+ "name": "Add mpls pop action with bos (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls pop protocol ipv4 bos 1",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*pop.*bos.*1.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "38cc",
+ "name": "Add mpls push action with label",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls push label 20",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*push.*protocol.*mpls_uc.*label.*20.*ttl.*[0-9]+.*pipe",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "c281",
+ "name": "Add mpls push action with mpls_mc protocol",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls push protocol mpls_mc label 20",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*push.*protocol.*mpls_mc.*label.*20.*ttl.*[0-9]+.*pipe",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "5db4",
+ "name": "Add mpls push action with label, tc and ttl",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls push label 20 tc 3 ttl 128",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*push.*protocol.*mpls_uc.*label.*20.*tc.*3.*ttl.*128.*pipe",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "16eb",
+ "name": "Add mpls push action with label and bos",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls push label 20 bos 1",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*push.*protocol.*mpls_uc.*label.*20.*bos.*1.*pipe",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "d69d",
+ "name": "Add mpls push action with no label (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls push",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*push.*protocol.*mpls_uc.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "e8e4",
+ "name": "Add mpls push action with ipv4 protocol (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls push protocol ipv4 label 20",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*push.*protocol.*mpls_uc.*label.*20.*ttl.*[0-9]+.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "ecd0",
+ "name": "Add mpls push action with out of range label (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls push label 1048576",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*push.*protocol.*mpls_uc.*label.*1048576.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "d303",
+ "name": "Add mpls push action with out of range tc (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls push label 20 tc 8",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*push.*protocol.*mpls_uc.*label.*20.*tc.*8.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "fd6e",
+ "name": "Add mpls push action with ttl of 0 (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls push label 20 ttl 0",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*push.*protocol.*mpls_uc.*label.*20.*ttl.*0.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "19e9",
+ "name": "Add mpls mod action with mpls label",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls mod label 20",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*modify.*label.*20.*pipe",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "1fde",
+ "name": "Add mpls mod action with max mpls label",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls mod label 0xfffff",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*modify.*label.*1048575.*pipe",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "0c50",
+ "name": "Add mpls mod action with mpls label exceeding max (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls mod label 0x100000",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*modify.*label.*1048576.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "10b6",
+ "name": "Add mpls mod action with mpls label of MPLS_LABEL_IMPLNULL (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls mod label 3",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*modify.*label.*3.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "57c9",
+ "name": "Add mpls mod action with mpls min tc",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls mod tc 0",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*modify.*tc.*0.*pipe",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "6872",
+ "name": "Add mpls mod action with mpls max tc",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls mod tc 7",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*modify.*tc.*7.*pipe",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "a70a",
+ "name": "Add mpls mod action with mpls tc exceeding max (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls mod tc 8",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*modify.*tc.*4.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "6ed5",
+ "name": "Add mpls mod action with mpls ttl",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls mod ttl 128",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*modify.*ttl.*128.*pipe",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "b80f",
+ "name": "Add mpls mod action with mpls max ttl",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls mod ttl 255",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*modify.*ttl.*255.*pipe",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "8864",
+ "name": "Add mpls mod action with mpls min ttl",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls mod ttl 1",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*modify.*ttl.*1.*pipe",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "6c06",
+ "name": "Add mpls mod action with mpls ttl of 0 (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls mod ttl 0",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*modify.*ttl.*0.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "b5d8",
+ "name": "Add mpls mod action with mpls ttl exceeding max (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls mod ttl 256",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*modify.*ttl.*256.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "451f",
+ "name": "Add mpls mod action with mpls max bos",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls mod bos 1",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*modify.*bos.*1.*pipe",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "a1ed",
+ "name": "Add mpls mod action with mpls min bos",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls mod bos 0",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*modify.*bos.*0.*pipe",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "3dcf",
+ "name": "Add mpls mod action with mpls bos exceeding max (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls mod bos 2",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*modify.*bos.*2.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "db7c",
+ "name": "Add mpls mod action with protocol (invalid)",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action mpls mod protocol ipv4",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*modify.*protocol.*ip.*pipe",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "b070",
+ "name": "Replace existing mpls push action with new ID",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ],
+ "$TC actions add action mpls push label 20 pipe index 12"
+ ],
+ "cmdUnderTest": "$TC actions replace action mpls push label 30 pipe index 12",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions get action mpls index 12",
+ "matchPattern": "action order [0-9]+: mpls.*push.*protocol.*mpls_uc.*label.*30.*pipe.*index 12 ref",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action mpls"
+ ]
+ },
+ {
+ "id": "6cce",
+ "name": "Delete mpls pop action",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ],
+ "$TC actions add action mpls pop protocol ipv4 index 44"
+ ],
+ "cmdUnderTest": "$TC actions del action mpls index 44",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*pop.*index 44 ref",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "d138",
+ "name": "Flush mpls actions",
+ "category": [
+ "actions",
+ "mpls"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action mpls",
+ 0,
+ 1,
+ 255
+ ],
+ "$TC actions add action mpls push label 10 index 10",
+ "$TC actions add action mpls push label 20 index 20",
+ "$TC actions add action mpls push label 30 index 30",
+ "$TC actions add action mpls push label 40 index 40"
+ ],
+ "cmdUnderTest": "$TC actions flush action mpls",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action mpls",
+ "matchPattern": "action order [0-9]+: mpls.*push.*",
+ "matchCount": "0",
+ "teardown": []
+ }
+]
diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json b/tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json
index ecd96eda7f6a..9cdd2e31ac2c 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json
@@ -24,8 +24,32 @@
]
},
{
+ "id": "c8cf",
+ "name": "Add skbedit action with 32-bit maximum mark",
+ "category": [
+ "actions",
+ "skbedit"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action skbedit",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action skbedit mark 4294967295 pipe index 1",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions get action skbedit index 1",
+ "matchPattern": "action order [0-9]*: skbedit mark 4294967295.*pipe.*index 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action skbedit"
+ ]
+ },
+ {
"id": "407b",
- "name": "Add skbedit action with invalid mark",
+ "name": "Add skbedit action with mark exceeding 32-bit maximum",
"category": [
"actions",
"skbedit"
@@ -43,6 +67,121 @@
"verifyCmd": "$TC actions list action skbedit",
"matchPattern": "action order [0-9]*: skbedit mark",
"matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "d4cd",
+ "name": "Add skbedit action with valid mark and mask",
+ "category": [
+ "actions",
+ "skbedit"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action skbedit",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action skbedit mark 1/0xaabb",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action skbedit",
+ "matchPattern": "action order [0-9]*: skbedit mark 1/0xaabb",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action skbedit"
+ ]
+ },
+ {
+ "id": "baa7",
+ "name": "Add skbedit action with valid mark and 32-bit maximum mask",
+ "category": [
+ "actions",
+ "skbedit"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action skbedit",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action skbedit mark 1/0xffffffff",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action skbedit",
+ "matchPattern": "action order [0-9]*: skbedit mark 1/0xffffffff",
+ "matchCount": "1",
+ "teardown": [
+ "$TC actions flush action skbedit"
+ ]
+ },
+ {
+ "id": "62a5",
+ "name": "Add skbedit action with valid mark and mask exceeding 32-bit maximum",
+ "category": [
+ "actions",
+ "skbedit"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action skbedit",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action skbedit mark 1/0xaabbccddeeff112233",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action skbedit",
+ "matchPattern": "action order [0-9]*: skbedit mark 1/0xaabbccddeeff112233",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "bc15",
+ "name": "Add skbedit action with valid mark and mask with invalid format",
+ "category": [
+ "actions",
+ "skbedit"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action skbedit",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action skbedit mark 1/-1234",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions list action skbedit",
+ "matchPattern": "action order [0-9]*: skbedit mark 1/-1234",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "57c2",
+ "name": "Replace skbedit action with new mask",
+ "category": [
+ "actions",
+ "skbedit"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action skbedit",
+ 0,
+ 1,
+ 255
+ ],
+ "$TC actions add action skbedit mark 1/0x11223344 index 1"
+ ],
+ "cmdUnderTest": "$TC actions replace action skbedit mark 1/0xaabb index 1",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action skbedit",
+ "matchPattern": "action order [0-9]*: skbedit mark 1/0xaabb",
+ "matchCount": "1",
"teardown": [
"$TC actions flush action skbedit"
]
@@ -121,7 +260,7 @@
},
{
"id": "985c",
- "name": "Add skbedit action with invalid queue_mapping",
+ "name": "Add skbedit action with queue_mapping exceeding 16-bit maximum",
"category": [
"actions",
"skbedit"
@@ -413,7 +552,7 @@
},
{
"id": "a6d6",
- "name": "Add skbedit action with index",
+ "name": "Add skbedit action with index at 32-bit maximum",
"category": [
"actions",
"skbedit"
@@ -426,16 +565,38 @@
255
]
],
- "cmdUnderTest": "$TC actions add action skbedit mark 808 index 4040404040",
+ "cmdUnderTest": "$TC actions add action skbedit mark 808 index 4294967295",
"expExitCode": "0",
- "verifyCmd": "$TC actions list action skbedit",
- "matchPattern": "index 4040404040",
+ "verifyCmd": "$TC actions get action skbedit index 4294967295",
+ "matchPattern": "action order [0-9]*: skbedit mark 808.*index 4294967295",
"matchCount": "1",
"teardown": [
"$TC actions flush action skbedit"
]
},
{
+ "id": "f0f4",
+ "name": "Add skbedit action with index exceeding 32-bit maximum",
+ "category": [
+ "actions",
+ "skbedit"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action skbedit",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action skbedit mark 808 pass index 4294967297",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions get action skbedit index 4294967297",
+ "matchPattern": "action order [0-9]*:.*skbedit.*mark 808.*pass.*index 4294967297",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
"id": "38f3",
"name": "Delete skbedit action",
"category": [
@@ -509,5 +670,52 @@
"teardown": [
"$TC actions flush action skbedit"
]
+ },
+ {
+ "id": "630c",
+ "name": "Add batch of 32 skbedit actions with all parameters and cookie",
+ "category": [
+ "actions",
+ "skbedit"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action skbedit",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action skbedit queue_mapping 2 priority 10 mark 7/0xaabbccdd ptype host inheritdsfield index \\$i cookie aabbccddeeff112233445566778800a1 \\\"; args=\"\\$args\\$cmd\"; done && $TC actions add \\$args\"",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action skbedit",
+ "matchPattern": "^[ \t]+index [0-9]+ ref",
+ "matchCount": "32",
+ "teardown": [
+ "$TC actions flush action skbedit"
+ ]
+ },
+ {
+ "id": "706d",
+ "name": "Delete batch of 32 skbedit actions with all parameters",
+ "category": [
+ "actions",
+ "skbedit"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action skbedit",
+ 0,
+ 1,
+ 255
+ ],
+ "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action skbedit queue_mapping 2 priority 10 mark 7/0xaabbccdd ptype host inheritdsfield index \\$i \\\"; args=\\\"\\$args\\$cmd\\\"; done && $TC actions add \\$args\""
+ ],
+ "cmdUnderTest": "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action skbedit index \\$i \\\"; args=\"\\$args\\$cmd\"; done && $TC actions del \\$args\"",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action skbedit",
+ "matchPattern": "^[ \t]+index [0-9]+ ref",
+ "matchCount": "0",
+ "teardown": []
}
]
diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/vlan.json b/tools/testing/selftests/tc-testing/tc-tests/actions/vlan.json
index cc7c7d758008..6503b1ce091f 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/actions/vlan.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/vlan.json
@@ -713,5 +713,99 @@
"teardown": [
"$TC actions flush action vlan"
]
+ },
+ {
+ "id": "294e",
+ "name": "Add batch of 32 vlan push actions with cookie",
+ "category": [
+ "actions",
+ "vlan"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action vlan",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action vlan push protocol 802.1q id 4094 priority 7 pipe index \\$i cookie aabbccddeeff112233445566778800a1 \\\"; args=\"\\$args\\$cmd\"; done && $TC actions add \\$args\"",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action vlan",
+ "matchPattern": "^[ \t]+index [0-9]+ ref",
+ "matchCount": "32",
+ "teardown": [
+ "$TC actions flush action vlan"
+ ]
+ },
+ {
+ "id": "56f7",
+ "name": "Delete batch of 32 vlan push actions",
+ "category": [
+ "actions",
+ "vlan"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action vlan",
+ 0,
+ 1,
+ 255
+ ],
+ "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action vlan push protocol 802.1q id 4094 priority 7 pipe index \\$i \\\"; args=\\\"\\$args\\$cmd\\\"; done && $TC actions add \\$args\""
+ ],
+ "cmdUnderTest": "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action vlan index \\$i \\\"; args=\"\\$args\\$cmd\"; done && $TC actions del \\$args\"",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action vlan",
+ "matchPattern": "^[ \t]+index [0-9]+ ref",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "759f",
+ "name": "Add batch of 32 vlan pop actions with cookie",
+ "category": [
+ "actions",
+ "vlan"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action vlan",
+ 0,
+ 1,
+ 255
+ ]
+ ],
+ "cmdUnderTest": "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action vlan pop continue index \\$i cookie aabbccddeeff112233445566778800a1 \\\"; args=\"\\$args\\$cmd\"; done && $TC actions add \\$args\"",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action vlan",
+ "matchPattern": "^[ \t]+index [0-9]+ ref",
+ "matchCount": "32",
+ "teardown": [
+ "$TC actions flush action vlan"
+ ]
+ },
+ {
+ "id": "c84a",
+ "name": "Delete batch of 32 vlan pop actions",
+ "category": [
+ "actions",
+ "vlan"
+ ],
+ "setup": [
+ [
+ "$TC actions flush action vlan",
+ 0,
+ 1,
+ 255
+ ],
+ "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action vlan pop index \\$i \\\"; args=\\\"\\$args\\$cmd\\\"; done && $TC actions add \\$args\""
+ ],
+ "cmdUnderTest": "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action vlan index \\$i \\\"; args=\"\\$args\\$cmd\"; done && $TC actions del \\$args\"",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions list action vlan",
+ "matchPattern": "^[ \t]+index [0-9]+ ref",
+ "matchCount": "0",
+ "teardown": []
}
]
diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/concurrency.json b/tools/testing/selftests/tc-testing/tc-tests/filters/concurrency.json
index 9002714b1851..c2a433a4737e 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/filters/concurrency.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/filters/concurrency.json
@@ -12,7 +12,7 @@
"$TC qdisc add dev $DEV2 ingress",
"./tdc_multibatch.py $DEV2 $BATCH_DIR 100000 10 add"
],
- "cmdUnderTest": "find $BATCH_DIR/add* -print | xargs -n 1 -P 10 $TC -b",
+ "cmdUnderTest": "bash -c \"find $BATCH_DIR/add* -print | xargs -n 1 -P 10 $TC -b\"",
"expExitCode": "0",
"verifyCmd": "$TC -s filter show dev $DEV2 ingress",
"matchPattern": "filter protocol ip pref 1 flower chain 0 handle",
@@ -37,7 +37,7 @@
"$TC -b $BATCH_DIR/add_0",
"./tdc_multibatch.py $DEV2 $BATCH_DIR 100000 10 del"
],
- "cmdUnderTest": "find $BATCH_DIR/del* -print | xargs -n 1 -P 10 $TC -b",
+ "cmdUnderTest": "bash -c \"find $BATCH_DIR/del* -print | xargs -n 1 -P 10 $TC -b\"",
"expExitCode": "0",
"verifyCmd": "$TC -s filter show dev $DEV2 ingress",
"matchPattern": "filter protocol ip pref 1 flower chain 0 handle",
@@ -62,7 +62,7 @@
"$TC -b $BATCH_DIR/add_0",
"./tdc_multibatch.py $DEV2 $BATCH_DIR 100000 10 replace"
],
- "cmdUnderTest": "find $BATCH_DIR/replace* -print | xargs -n 1 -P 10 $TC -b",
+ "cmdUnderTest": "bash -c \"find $BATCH_DIR/replace* -print | xargs -n 1 -P 10 $TC -b\"",
"expExitCode": "0",
"verifyCmd": "$TC -s filter show dev $DEV2 ingress",
"matchPattern": "filter protocol ip pref 1 flower chain 0 handle",
@@ -87,7 +87,7 @@
"$TC -b $BATCH_DIR/add_0",
"./tdc_multibatch.py -d $DEV2 $BATCH_DIR 100000 10 replace"
],
- "cmdUnderTest": "find $BATCH_DIR/replace* -print | xargs -n 1 -P 10 $TC -b",
+ "cmdUnderTest": "bash -c \"find $BATCH_DIR/replace* -print | xargs -n 1 -P 10 $TC -b\"",
"expExitCode": "0",
"verifyCmd": "$TC -s filter show dev $DEV2 ingress",
"matchPattern": "filter protocol ip pref 1 flower chain 0 handle",
@@ -112,7 +112,7 @@
"$TC -b $BATCH_DIR/add_0",
"./tdc_multibatch.py -d $DEV2 $BATCH_DIR 100000 10 del"
],
- "cmdUnderTest": "find $BATCH_DIR/del* -print | xargs -n 1 -P 10 $TC -f -b",
+ "cmdUnderTest": "bash -c \"find $BATCH_DIR/del* -print | xargs -n 1 -P 10 $TC -f -b\"",
"expExitCode": "123",
"verifyCmd": "$TC -s filter show dev $DEV2 ingress",
"matchPattern": "filter protocol ip pref 1 flower chain 0 handle",
@@ -134,11 +134,11 @@
"/bin/mkdir $BATCH_DIR",
"$TC qdisc add dev $DEV2 ingress",
"./tdc_multibatch.py -x init_ $DEV2 $BATCH_DIR 100000 5 add",
- "find $BATCH_DIR/init_* -print | xargs -n 1 -P 5 $TC -b",
+ "bash -c \"find $BATCH_DIR/init_* -print | xargs -n 1 -P 5 $TC -b\"",
"./tdc_multibatch.py -x par_ -a 500001 -m 5 $DEV2 $BATCH_DIR 100000 5 add",
"./tdc_multibatch.py -x par_ $DEV2 $BATCH_DIR 100000 5 del"
],
- "cmdUnderTest": "find $BATCH_DIR/par_* -print | xargs -n 1 -P 10 $TC -b",
+ "cmdUnderTest": "bash -c \"find $BATCH_DIR/par_* -print | xargs -n 1 -P 10 $TC -b\"",
"expExitCode": "0",
"verifyCmd": "$TC -s filter show dev $DEV2 ingress",
"matchPattern": "filter protocol ip pref 1 flower chain 0 handle",
@@ -160,11 +160,11 @@
"/bin/mkdir $BATCH_DIR",
"$TC qdisc add dev $DEV2 ingress",
"./tdc_multibatch.py -x init_ $DEV2 $BATCH_DIR 100000 10 add",
- "find $BATCH_DIR/init_* -print | xargs -n 1 -P 5 $TC -b",
+ "bash -c \"find $BATCH_DIR/init_* -print | xargs -n 1 -P 5 $TC -b\"",
"./tdc_multibatch.py -x par_ -a 500001 -m 5 $DEV2 $BATCH_DIR 100000 5 replace",
"./tdc_multibatch.py -x par_ $DEV2 $BATCH_DIR 100000 5 del"
],
- "cmdUnderTest": "find $BATCH_DIR/par_* -print | xargs -n 1 -P 10 $TC -b",
+ "cmdUnderTest": "bash -c \"find $BATCH_DIR/par_* -print | xargs -n 1 -P 10 $TC -b\"",
"expExitCode": "0",
"verifyCmd": "$TC -s filter show dev $DEV2 ingress",
"matchPattern": "filter protocol ip pref 1 flower chain 0 handle",
diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/fw.json b/tools/testing/selftests/tc-testing/tc-tests/filters/fw.json
index 3b97cfd7e0f8..5272049566d6 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/filters/fw.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/filters/fw.json
@@ -6,6 +6,9 @@
"filter",
"fw"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress"
],
@@ -25,6 +28,9 @@
"filter",
"fw"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress"
],
@@ -44,6 +50,114 @@
"filter",
"fw"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress"
],
@@ -57,6 +171,30 @@
]
},
{
+ "id": "c591",
+ "name": "Add fw filter with action ok by reference",
+ "__comment": "We add sleep here because action might have not been deleted by workqueue just yet. Remove this when the behaviour is fixed.",
+ "category": [
+ "filter",
+ "fw"
+ ],
+ "setup": [
+ "$TC qdisc add dev $DEV1 ingress",
+ "/bin/sleep 1",
+ "$TC actions add action gact ok index 1"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action gact index 1",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol all fw",
+ "matchPattern": "handle 0x1.*gact action pass.*index 1 ref 2 bind 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DEV1 ingress",
+ "/bin/sleep 1",
+ "$TC actions del action gact index 1"
+ ]
+ },
+ {
"id": "affe",
"name": "Add fw filter with action continue",
"category": [
@@ -76,6 +214,30 @@
]
},
{
+ "id": "38b3",
+ "name": "Add fw filter with action continue by reference",
+ "__comment": "We add sleep here because action might have not been deleted by workqueue just yet. Remove this when the behaviour is fixed.",
+ "category": [
+ "filter",
+ "fw"
+ ],
+ "setup": [
+ "$TC qdisc add dev $DEV1 ingress",
+ "/bin/sleep 1",
+ "$TC actions add action gact continue index 1"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action gact index 1",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol all fw",
+ "matchPattern": "handle 0x1.*gact action continue.*index 1 ref 2 bind 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DEV1 ingress",
+ "/bin/sleep 1",
+ "$TC actions del action gact index 1"
+ ]
+ },
+ {
"id": "28bc",
"name": "Add fw filter with action pipe",
"category": [
@@ -95,6 +257,30 @@
]
},
{
+ "id": "6753",
+ "name": "Add fw filter with action pipe by reference",
+ "__comment": "We add sleep here because action might have not been deleted by workqueue just yet.",
+ "category": [
+ "filter",
+ "fw"
+ ],
+ "setup": [
+ "$TC qdisc add dev $DEV1 ingress",
+ "/bin/sleep 1",
+ "$TC actions add action gact pipe index 1"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action gact index 1",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol all fw",
+ "matchPattern": "handle 0x1.*gact action pipe.*index 1 ref 2 bind 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DEV1 ingress",
+ "/bin/sleep 1",
+ "$TC actions del action gact index 1"
+ ]
+ },
+ {
"id": "8da2",
"name": "Add fw filter with action drop",
"category": [
@@ -114,6 +300,30 @@
]
},
{
+ "id": "6dc6",
+ "name": "Add fw filter with action drop by reference",
+ "__comment": "We add sleep here because action might have not been deleted by workqueue just yet.",
+ "category": [
+ "filter",
+ "fw"
+ ],
+ "setup": [
+ "$TC qdisc add dev $DEV1 ingress",
+ "/bin/sleep 1",
+ "$TC actions add action gact drop index 1"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action gact index 1",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol all fw",
+ "matchPattern": "handle 0x1.*gact action drop.*index 1 ref 2 bind 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DEV1 ingress",
+ "/bin/sleep 1",
+ "$TC actions del action gact index 1"
+ ]
+ },
+ {
"id": "9436",
"name": "Add fw filter with action reclassify",
"category": [
@@ -133,6 +343,30 @@
]
},
{
+ "id": "3bc2",
+ "name": "Add fw filter with action reclassify by reference",
+ "__comment": "We add sleep here because action might have not been deleted by workqueue just yet.",
+ "category": [
+ "filter",
+ "fw"
+ ],
+ "setup": [
+ "$TC qdisc add dev $DEV1 ingress",
+ "/bin/sleep 1",
+ "$TC actions add action gact reclassify index 1"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action gact index 1",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol all fw",
+ "matchPattern": "handle 0x1.*gact action reclassify.*index 1 ref 2 bind 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DEV1 ingress",
+ "/bin/sleep 1",
+ "$TC actions del action gact index 1"
+ ]
+ },
+ {
"id": "95bb",
"name": "Add fw filter with action jump 10",
"category": [
@@ -152,6 +386,30 @@
]
},
{
+ "id": "36f7",
+ "name": "Add fw filter with action jump 10 by reference",
+ "__comment": "We add sleep here because action might have not been deleted by workqueue just yet.",
+ "category": [
+ "filter",
+ "fw"
+ ],
+ "setup": [
+ "$TC qdisc add dev $DEV1 ingress",
+ "/bin/sleep 1",
+ "$TC actions add action gact jump 10 index 1"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action gact index 1",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol all fw",
+ "matchPattern": "handle 0x1.*gact action jump 10.*index 1 ref 2 bind 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DEV1 ingress",
+ "/bin/sleep 1",
+ "$TC actions del action gact index 1"
+ ]
+ },
+ {
"id": "3d74",
"name": "Add fw filter with action goto chain 5",
"category": [
@@ -728,6 +986,9 @@
"filter",
"fw"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress",
"$TC filter add dev $DEV1 parent ffff: protocol 802_3 prio 3 handle 7 fw action ok"
@@ -748,6 +1009,9 @@
"filter",
"fw"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress",
"$TC filter add dev $DEV1 parent ffff: prio 6 handle 2 fw action continue index 5"
@@ -768,6 +1032,9 @@
"filter",
"fw"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress"
],
@@ -787,6 +1054,9 @@
"filter",
"fw"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress"
],
@@ -806,6 +1076,9 @@
"filter",
"fw"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress",
"$TC filter add dev $DEV1 parent ffff: handle 5 prio 7 fw action pass",
@@ -828,6 +1101,9 @@
"filter",
"fw"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress",
"$TC filter add dev $DEV1 parent ffff: handle 5 prio 7 fw action pass",
@@ -850,6 +1126,9 @@
"filter",
"fw"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress",
"$TC filter add dev $DEV1 parent ffff: handle 5 prio 7 fw action pass",
@@ -871,6 +1150,9 @@
"filter",
"fw"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress",
"$TC filter add dev $DEV1 parent ffff: handle 1 prio 4 fw action ok",
@@ -892,6 +1174,9 @@
"filter",
"fw"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress",
"$TC filter add dev $DEV1 parent ffff: handle 4 prio 2 chain 13 fw action pipe",
@@ -913,6 +1198,9 @@
"filter",
"fw"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress",
"$TC filter add dev $DEV1 parent ffff: handle 2 prio 4 fw action drop"
@@ -933,6 +1221,9 @@
"filter",
"fw"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress",
"$TC filter add dev $DEV1 parent ffff: handle 3 prio 4 fw action continue"
@@ -953,6 +1244,9 @@
"filter",
"fw"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress",
"$TC filter add dev $DEV1 parent ffff: handle 4 prio 2 protocol arp fw action pipe"
@@ -973,6 +1267,9 @@
"filter",
"fw"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress",
"$TC filter add dev $DEV1 parent ffff: handle 4 prio 2 fw action pipe flowid 45"
@@ -993,6 +1290,9 @@
"filter",
"fw"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress",
"$TC filter add dev $DEV1 parent ffff: handle 1 prio 2 fw action ok"
@@ -1013,6 +1313,9 @@
"filter",
"fw"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress",
"$TC filter add dev $DEV1 parent ffff: handle 1 prio 2 fw action ok"
@@ -1033,6 +1336,9 @@
"filter",
"fw"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress",
"$TC filter add dev $DEV1 parent ffff: handle 1 prio 2 fw action ok index 3"
diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/matchall.json b/tools/testing/selftests/tc-testing/tc-tests/filters/matchall.json
new file mode 100644
index 000000000000..51799874a972
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/tc-tests/filters/matchall.json
@@ -0,0 +1,391 @@
+[
+ {
+ "id": "f62b",
+ "name": "Add ingress matchall filter for protocol ipv4 and action PASS",
+ "category": [
+ "filter",
+ "matchall"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY ingress"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol ip matchall action ok",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter get dev $DUMMY parent ffff: handle 1 prio 1 protocol ip matchall",
+ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*gact action pass.*ref 1 bind 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY ingress",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "7f09",
+ "name": "Add egress matchall filter for protocol ipv4 and action PASS",
+ "category": [
+ "filter",
+ "matchall"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY root handle 1: prio"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY parent 1: handle 0x1 prio 1 protocol ip matchall action ok",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter get dev $DUMMY parent 1: handle 1 prio 1 protocol ip matchall",
+ "matchPattern": "^filter parent 1: protocol ip pref 1 matchall.*handle 0x1.*gact action pass.*ref 1 bind 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY root handle 1: prio",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "0596",
+ "name": "Add ingress matchall filter for protocol ipv6 and action DROP",
+ "category": [
+ "filter",
+ "matchall"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY ingress"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol ipv6 matchall action drop",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter get dev $DUMMY parent ffff: handle 1 prio 1 protocol ipv6 matchall",
+ "matchPattern": "^filter parent ffff: protocol ipv6 pref 1 matchall.*handle 0x1.*gact action drop.*ref 1 bind 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY ingress",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "41df",
+ "name": "Add egress matchall filter for protocol ipv6 and action DROP",
+ "category": [
+ "filter",
+ "matchall"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY root handle 1: prio"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY parent 1: handle 0x1 prio 1 protocol ipv6 matchall action drop",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter get dev $DUMMY parent 1: handle 1 prio 1 protocol ipv6 matchall",
+ "matchPattern": "^filter parent 1: protocol ipv6 pref 1 matchall.*handle 0x1.*gact action drop.*ref 1 bind 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY root handle 1: prio",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "e1da",
+ "name": "Add ingress matchall filter for protocol ipv4 and action PASS with priority at 16-bit maximum",
+ "category": [
+ "filter",
+ "matchall"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY ingress"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 65535 protocol ipv4 matchall action pass",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter get dev $DUMMY parent ffff: handle 1 prio 65535 protocol ipv4 matchall",
+ "matchPattern": "^filter parent ffff: protocol ip pref 65535 matchall.*handle 0x1.*gact action pass.*ref 1 bind 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY ingress",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "3de5",
+ "name": "Add egress matchall filter for protocol ipv4 and action PASS with priority at 16-bit maximum",
+ "category": [
+ "filter",
+ "matchall"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY root handle 1: prio"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY parent 1: handle 0x1 prio 65535 protocol ipv4 matchall action pass",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter get dev $DUMMY parent 1: handle 1 prio 65535 protocol ipv4 matchall",
+ "matchPattern": "^filter parent 1: protocol ip pref 65535 matchall.*handle 0x1.*gact action pass.*ref 1 bind 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY root handle 1: prio",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "72d7",
+ "name": "Add ingress matchall filter for protocol ipv4 and action PASS with priority exceeding 16-bit maximum",
+ "category": [
+ "filter",
+ "matchall"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY ingress"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 655355 protocol ipv4 matchall action pass",
+ "expExitCode": "255",
+ "verifyCmd": "$TC filter get dev $DUMMY parent ffff: handle 1 prio 655355 protocol ipv4 matchall",
+ "matchPattern": "^filter parent ffff: protocol ip pref 655355 matchall.*handle 0x1.*gact action pass.*ref 1 bind 1",
+ "matchCount": "0",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY ingress",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "41d3",
+ "name": "Add egress matchall filter for protocol ipv4 and action PASS with priority exceeding 16-bit maximum",
+ "category": [
+ "filter",
+ "matchall"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY root handle 1: prio"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY parent 1: handle 0x1 prio 655355 protocol ipv4 matchall action pass",
+ "expExitCode": "255",
+ "verifyCmd": "$TC filter get dev $DUMMY parent 1: handle 1 prio 655355 protocol ipv4 matchall",
+ "matchPattern": "^filter parent 1: protocol ip pref 655355 matchall.*handle 0x1.*gact action pass.*ref 1 bind 1",
+ "matchCount": "0",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY root handle 1: prio",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "f755",
+ "name": "Add ingress matchall filter for all protocols and action CONTINUE with handle at 32-bit maximum",
+ "category": [
+ "filter",
+ "matchall"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY ingress"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY parent ffff: handle 0xffffffff prio 1 protocol all matchall action continue",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter get dev $DUMMY parent ffff: handle 0xffffffff prio 1 protocol all matchall",
+ "matchPattern": "^filter parent ffff: protocol all pref 1 matchall.*handle 0xffffffff.*gact action continue.*ref 1 bind 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY ingress",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "2c33",
+ "name": "Add egress matchall filter for all protocols and action CONTINUE with handle at 32-bit maximum",
+ "category": [
+ "filter",
+ "matchall"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY root handle 1: prio"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY parent 1: handle 0xffffffff prio 1 protocol all matchall action continue",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter get dev $DUMMY parent 1: handle 0xffffffff prio 1 protocol all matchall",
+ "matchPattern": "^filter parent 1: protocol all pref 1 matchall.*handle 0xffffffff.*gact action continue.*ref 1 bind 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY root handle 1: prio",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "0e4a",
+ "name": "Add ingress matchall filter for all protocols and action RECLASSIFY with skip_hw flag",
+ "category": [
+ "filter",
+ "matchall"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY ingress"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol all matchall skip_hw action reclassify",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter get dev $DUMMY parent ffff: handle 0x1 prio 1 protocol all matchall",
+ "matchPattern": "^filter parent ffff: protocol all pref 1 matchall.*handle 0x1.*skip_hw.*not_in_hw.*gact action reclassify.*ref 1 bind 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY ingress",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "7f60",
+ "name": "Add egress matchall filter for all protocols and action RECLASSIFY with skip_hw flag",
+ "category": [
+ "filter",
+ "matchall"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY root handle 1: prio"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY parent 1: handle 0x1 prio 1 protocol all matchall skip_hw action reclassify",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter get dev $DUMMY parent 1: handle 0x1 prio 1 protocol all matchall",
+ "matchPattern": "^filter parent 1: protocol all pref 1 matchall.*handle 0x1.*skip_hw.*not_in_hw.*gact action reclassify.*ref 1 bind 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY root handle 1: prio",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "8bd2",
+ "name": "Add ingress matchall filter for protocol ipv6 and action PASS with classid",
+ "category": [
+ "filter",
+ "matchall"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY ingress"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol ipv6 matchall classid 1:1 action pass",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter get dev $DUMMY parent ffff: handle 0x1 prio 1 protocol ipv6 matchall",
+ "matchPattern": "^filter parent ffff: protocol ipv6 pref 1 matchall.*handle 0x1.*flowid 1:1.*gact action pass.*ref 1 bind 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY ingress",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "2a4a",
+ "name": "Add ingress matchall filter for protocol ipv6 and action PASS with invalid classid",
+ "category": [
+ "filter",
+ "matchall"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY ingress"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol ipv6 matchall classid 6789defg action pass",
+ "expExitCode": "1",
+ "verifyCmd": "$TC filter get dev $DUMMY parent ffff: handle 0x1 prio 1 protocol ipv6 matchall",
+ "matchPattern": "^filter protocol ipv6 pref 1 matchall.*handle 0x1.*flowid 6789defg.*gact action pass.*ref 1 bind 1",
+ "matchCount": "0",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY ingress",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "eaf8",
+ "name": "Delete single ingress matchall filter",
+ "category": [
+ "filter",
+ "matchall"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY ingress",
+ "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol ipv6 matchall classid 1:2 action pass"
+ ],
+ "cmdUnderTest": "$TC filter del dev $DUMMY parent ffff: handle 0x1 prio 1 protocol ipv6 matchall",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter get dev $DUMMY parent ffff: handle 0x1 prio 1 protocol ipv6 matchall",
+ "matchPattern": "^filter protocol ipv6 pref 1 matchall.*handle 0x1.*flowid 1:2.*gact action pass.*ref 1 bind 1",
+ "matchCount": "0",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY ingress",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "76ad",
+ "name": "Delete all ingress matchall filters",
+ "category": [
+ "filter",
+ "matchall"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY ingress",
+ "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol all matchall classid 1:2 action pass",
+ "$TC filter add dev $DUMMY parent ffff: handle 0x2 prio 2 protocol all matchall classid 1:3 action pass",
+ "$TC filter add dev $DUMMY parent ffff: handle 0x3 prio 3 protocol all matchall classid 1:4 action pass",
+ "$TC filter add dev $DUMMY parent ffff: handle 0x4 prio 4 protocol all matchall classid 1:5 action pass"
+ ],
+ "cmdUnderTest": "$TC filter del dev $DUMMY parent ffff:",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter show dev $DUMMY parent ffff:",
+ "matchPattern": "^filter protocol all pref.*matchall.*handle.*flowid.*gact action pass",
+ "matchCount": "0",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY ingress",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "1eb9",
+ "name": "Delete single ingress matchall filter out of multiple",
+ "category": [
+ "filter",
+ "matchall"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY ingress",
+ "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol all matchall classid 1:2 action pass",
+ "$TC filter add dev $DUMMY parent ffff: handle 0x2 prio 2 protocol all matchall classid 1:3 action pass",
+ "$TC filter add dev $DUMMY parent ffff: handle 0x3 prio 3 protocol all matchall classid 1:4 action pass",
+ "$TC filter add dev $DUMMY parent ffff: handle 0x4 prio 4 protocol all matchall classid 1:5 action pass"
+ ],
+ "cmdUnderTest": "$TC filter del dev $DUMMY parent ffff: protocol all handle 0x2 prio 2 matchall",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter show dev $DUMMY parent ffff:",
+ "matchPattern": "^filter protocol all pref 2 matchall.*handle 0x2 flowid 1:2.*gact action pass",
+ "matchCount": "0",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY ingress",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "6d63",
+ "name": "Delete ingress matchall filter by chain ID",
+ "category": [
+ "filter",
+ "matchall"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY ingress",
+ "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol all chain 1 matchall classid 1:1 action pass",
+ "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol ipv4 chain 2 matchall classid 1:3 action continue"
+ ],
+ "cmdUnderTest": "$TC filter del dev $DUMMY parent ffff: chain 2",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter show dev $DUMMY parent ffff:",
+ "matchPattern": "^filter protocol all pref 1 matchall chain 1 handle 0x1 flowid 1:1.*gact action pass",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY ingress",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ }
+]
diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json b/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json
index e2f92cefb8d5..0f89cd50a94b 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json
@@ -6,6 +6,9 @@
"filter",
"u32"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 ingress"
],
@@ -25,6 +28,9 @@
"filter",
"matchall"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV1 clsact",
"$TC filter add dev $DEV1 protocol all pref 1 ingress handle 0x1234 matchall action ok"
@@ -39,12 +45,34 @@
]
},
{
+ "id": "2ff3",
+ "name": "Add flower with max handle and then dump it",
+ "category": [
+ "filter",
+ "flower"
+ ],
+ "setup": [
+ "$TC qdisc add dev $DEV2 ingress"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DEV2 protocol ip pref 1 parent ffff: handle 0xffffffff flower action ok",
+ "expExitCode": "0",
+ "verifyCmd": "$TC filter show dev $DEV2 ingress",
+ "matchPattern": "filter protocol ip pref 1 flower.*handle 0xffffffff",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DEV2 ingress"
+ ]
+ },
+ {
"id": "d052",
"name": "Add 1M filters with the same action",
"category": [
"filter",
"flower"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV2 ingress",
"./tdc_batch.py $DEV2 $BATCH_FILE --share_action -n 1000000"
@@ -66,6 +94,9 @@
"filter",
"flower"
],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
"setup": [
"$TC qdisc add dev $DEV2 ingress",
"$TC filter add dev $DEV2 protocol ip prio 1 parent ffff: flower dst_mac e4:11:22:11:4a:51 src_mac e4:11:22:11:4a:50 ip_proto tcp src_ip 1.1.1.1 dst_ip 2.2.2.2 action drop"
diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fifo.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fifo.json
new file mode 100644
index 000000000000..5ecd93b4c473
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fifo.json
@@ -0,0 +1,304 @@
+[
+ {
+ "id": "a519",
+ "name": "Add bfifo qdisc with system default parameters on egress",
+ "__comment": "When omitted, queue size in bfifo is calculated as: txqueuelen * (MTU + LinkLayerHdrSize), where LinkLayerHdrSize=14 for Ethernet",
+ "category": [
+ "qdisc",
+ "fifo"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root bfifo",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc bfifo 1: root.*limit [0-9]+b",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle 1: root bfifo",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "585c",
+ "name": "Add pfifo qdisc with system default parameters on egress",
+ "__comment": "When omitted, queue size in pfifo is defaulted to the interface's txqueuelen value.",
+ "category": [
+ "qdisc",
+ "fifo"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root pfifo",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc pfifo 1: root.*limit [0-9]+p",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle 1: root pfifo",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "a86e",
+ "name": "Add bfifo qdisc with system default parameters on egress with handle of maximum value",
+ "category": [
+ "qdisc",
+ "fifo"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY root handle ffff: bfifo",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc bfifo ffff: root.*limit [0-9]+b",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle ffff: root bfifo",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "9ac8",
+ "name": "Add bfifo qdisc on egress with queue size of 3000 bytes",
+ "category": [
+ "qdisc",
+ "fifo"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root bfifo limit 3000b",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc bfifo 1: root.*limit 3000b",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle 1: root bfifo",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "f4e6",
+ "name": "Add pfifo qdisc on egress with queue size of 3000 packets",
+ "category": [
+ "qdisc",
+ "fifo"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY txqueuelen 3000 type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root pfifo limit 3000",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc pfifo 1: root.*limit 3000p",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle 1: root pfifo",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "b1b1",
+ "name": "Add bfifo qdisc with system default parameters on egress with invalid handle exceeding maximum value",
+ "category": [
+ "qdisc",
+ "fifo"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY root handle 10000: bfifo",
+ "expExitCode": "255",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc bfifo 10000: root.*limit [0-9]+b",
+ "matchCount": "0",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "8d5e",
+ "name": "Add bfifo qdisc on egress with unsupported argument",
+ "category": [
+ "qdisc",
+ "fifo"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root bfifo foorbar",
+ "expExitCode": "1",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc bfifo 1: root",
+ "matchCount": "0",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "7787",
+ "name": "Add pfifo qdisc on egress with unsupported argument",
+ "category": [
+ "qdisc",
+ "fifo"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root pfifo foorbar",
+ "expExitCode": "1",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc pfifo 1: root",
+ "matchCount": "0",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "c4b6",
+ "name": "Replace bfifo qdisc on egress with new queue size",
+ "category": [
+ "qdisc",
+ "fifo"
+ ],
+ "setup": [
+ "$IP link del dev $DUMMY type dummy || /bin/true",
+ "$IP link add dev $DUMMY txqueuelen 1000 type dummy",
+ "$TC qdisc add dev $DUMMY handle 1: root bfifo"
+ ],
+ "cmdUnderTest": "$TC qdisc replace dev $DUMMY handle 1: root bfifo limit 3000b",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc bfifo 1: root.*limit 3000b",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle 1: root bfifo",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "3df6",
+ "name": "Replace pfifo qdisc on egress with new queue size",
+ "category": [
+ "qdisc",
+ "fifo"
+ ],
+ "setup": [
+ "$IP link del dev $DUMMY type dummy || /bin/true",
+ "$IP link add dev $DUMMY txqueuelen 1000 type dummy",
+ "$TC qdisc add dev $DUMMY handle 1: root pfifo"
+ ],
+ "cmdUnderTest": "$TC qdisc replace dev $DUMMY handle 1: root pfifo limit 30",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc pfifo 1: root.*limit 30p",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle 1: root pfifo",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "7a67",
+ "name": "Add bfifo qdisc on egress with queue size in invalid format",
+ "category": [
+ "qdisc",
+ "fifo"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root bfifo limit foo-bar",
+ "expExitCode": "1",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc bfifo 1: root.*limit foo-bar",
+ "matchCount": "0",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "1298",
+ "name": "Add duplicate bfifo qdisc on egress",
+ "category": [
+ "qdisc",
+ "fifo"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY handle 1: root bfifo"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root bfifo",
+ "expExitCode": "2",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc bfifo 1: root",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle 1: root bfifo",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "45a0",
+ "name": "Delete nonexistent bfifo qdisc",
+ "category": [
+ "qdisc",
+ "fifo"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc del dev $DUMMY root handle 1: bfifo",
+ "expExitCode": "2",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc bfifo 1: root",
+ "matchCount": "0",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "972b",
+ "name": "Add prio qdisc on egress with invalid format for handles",
+ "category": [
+ "qdisc",
+ "fifo"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY root handle 123^ bfifo limit 100b",
+ "expExitCode": "255",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc bfifo 123 root",
+ "matchCount": "0",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "4d39",
+ "name": "Delete bfifo qdisc twice",
+ "category": [
+ "qdisc",
+ "fifo"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY root handle 1: bfifo",
+ "$TC qdisc del dev $DUMMY root handle 1: bfifo"
+ ],
+ "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 1: root bfifo",
+ "expExitCode": "2",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc bfifo 1: root",
+ "matchCount": "0",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ }
+]
diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ingress.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ingress.json
new file mode 100644
index 000000000000..d99dba6e2b1a
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ingress.json
@@ -0,0 +1,102 @@
+[
+ {
+ "id": "9872",
+ "name": "Add ingress qdisc",
+ "category": [
+ "qdisc",
+ "ingress"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY ingress",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc ingress ffff:",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY ingress",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "5c5e",
+ "name": "Add ingress qdisc with unsupported argument",
+ "category": [
+ "qdisc",
+ "ingress"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY ingress foorbar",
+ "expExitCode": "1",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc ingress ffff:",
+ "matchCount": "0",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "74f6",
+ "name": "Add duplicate ingress qdisc",
+ "category": [
+ "qdisc",
+ "ingress"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY ingress"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY ingress",
+ "expExitCode": "2",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc ingress ffff:",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY ingress",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "f769",
+ "name": "Delete nonexistent ingress qdisc",
+ "category": [
+ "qdisc",
+ "ingress"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc del dev $DUMMY ingress",
+ "expExitCode": "2",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc ingress ffff:",
+ "matchCount": "0",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "3b88",
+ "name": "Delete ingress qdisc twice",
+ "category": [
+ "qdisc",
+ "ingress"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY ingress",
+ "$TC qdisc del dev $DUMMY ingress"
+ ],
+ "cmdUnderTest": "$TC qdisc del dev $DUMMY ingress",
+ "expExitCode": "2",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc ingress ffff:",
+ "matchCount": "0",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ }
+]
diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/prio.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/prio.json
new file mode 100644
index 000000000000..3076c02d08d6
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/prio.json
@@ -0,0 +1,276 @@
+[
+ {
+ "id": "ddd9",
+ "name": "Add prio qdisc on egress",
+ "category": [
+ "qdisc",
+ "prio"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root prio",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc prio 1: root",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle 1: root prio",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "aa71",
+ "name": "Add prio qdisc on egress with handle of maximum value",
+ "category": [
+ "qdisc",
+ "prio"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY root handle ffff: prio",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc prio ffff: root",
+ "matchCount": "1",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "db37",
+ "name": "Add prio qdisc on egress with invalid handle exceeding maximum value",
+ "category": [
+ "qdisc",
+ "prio"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY root handle 10000: prio",
+ "expExitCode": "255",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc prio 10000: root",
+ "matchCount": "0",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "39d8",
+ "name": "Add prio qdisc on egress with unsupported argument",
+ "category": [
+ "qdisc",
+ "prio"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root prio foorbar",
+ "expExitCode": "1",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc prio 1: root",
+ "matchCount": "0",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "5769",
+ "name": "Add prio qdisc on egress with 4 bands and new priomap",
+ "category": [
+ "qdisc",
+ "prio"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root prio bands 4 priomap 1 1 2 2 3 3 0 0 1 2 3 0 0 0 0 0",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc prio 1: root.*bands 4 priomap.*1 1 2 2 3 3 0 0 1 2 3 0 0 0 0 0",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle 1: root prio",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "fe0f",
+ "name": "Add prio qdisc on egress with 4 bands and priomap exceeding TC_PRIO_MAX entries",
+ "category": [
+ "qdisc",
+ "prio"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root prio bands 4 priomap 1 1 2 2 3 3 0 0 1 2 3 0 0 0 0 0 1 1",
+ "expExitCode": "1",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc prio 1: root.*bands 4 priomap.*1 1 2 2 3 3 0 0 1 2 3 0 0 0 0 0 1 1",
+ "matchCount": "0",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "1f91",
+ "name": "Add prio qdisc on egress with 4 bands and priomap's values exceeding bands number",
+ "category": [
+ "qdisc",
+ "prio"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root prio bands 4 priomap 1 1 2 2 7 5 0 0 1 2 3 0 0 0 0 0",
+ "expExitCode": "1",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc prio 1: root.*bands 4 priomap.*1 1 2 2 7 5 0 0 1 2 3 0 0 0 0 0",
+ "matchCount": "0",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "d248",
+ "name": "Add prio qdisc on egress with invalid bands value (< 2)",
+ "category": [
+ "qdisc",
+ "prio"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root prio bands 1 priomap 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
+ "expExitCode": "2",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc prio 1: root.*bands 1 priomap.*0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
+ "matchCount": "0",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "1d0e",
+ "name": "Add prio qdisc on egress with invalid bands value exceeding TCQ_PRIO_BANDS",
+ "category": [
+ "qdisc",
+ "prio"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root prio bands 1024 priomap 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16",
+ "expExitCode": "2",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc prio 1: root.*bands 1024 priomap.*1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16",
+ "matchCount": "0",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "1971",
+ "name": "Replace default prio qdisc on egress with 8 bands and new priomap",
+ "category": [
+ "qdisc",
+ "prio"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY handle 1: root prio"
+ ],
+ "cmdUnderTest": "$TC qdisc replace dev $DUMMY handle 1: root prio bands 8 priomap 1 1 2 2 3 3 4 4 5 5 6 6 7 7 0 0",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc prio 1: root.*bands 8 priomap.*1 1 2 2 3 3 4 4 5 5 6 6 7 7 0 0",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle 1: root prio",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "d88a",
+ "name": "Add duplicate prio qdisc on egress",
+ "category": [
+ "qdisc",
+ "prio"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY handle 1: root prio"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root prio",
+ "expExitCode": "2",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc prio 1: root",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle 1: root prio",
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "5948",
+ "name": "Delete nonexistent prio qdisc",
+ "category": [
+ "qdisc",
+ "prio"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc del dev $DUMMY root handle 1: prio",
+ "expExitCode": "2",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc prio 1: root",
+ "matchCount": "0",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "6c0a",
+ "name": "Add prio qdisc on egress with invalid format for handles",
+ "category": [
+ "qdisc",
+ "prio"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true"
+ ],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY root handle 123^ prio",
+ "expExitCode": "255",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc prio 123 root",
+ "matchCount": "0",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ },
+ {
+ "id": "0175",
+ "name": "Delete prio qdisc twice",
+ "category": [
+ "qdisc",
+ "prio"
+ ],
+ "setup": [
+ "$IP link add dev $DUMMY type dummy || /bin/true",
+ "$TC qdisc add dev $DUMMY root handle 1: prio",
+ "$TC qdisc del dev $DUMMY root handle 1: prio"
+ ],
+ "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 1: root prio",
+ "expExitCode": "2",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc ingress ffff:",
+ "matchCount": "0",
+ "teardown": [
+ "$IP link del dev $DUMMY type dummy"
+ ]
+ }
+]
diff --git a/tools/testing/selftests/tc-testing/tdc.py b/tools/testing/selftests/tc-testing/tdc.py
index 5cee15659e5f..e566c70e64a1 100755
--- a/tools/testing/selftests/tc-testing/tdc.py
+++ b/tools/testing/selftests/tc-testing/tdc.py
@@ -25,6 +25,9 @@ from tdc_helper import *
import TdcPlugin
from TdcResults import *
+class PluginDependencyException(Exception):
+ def __init__(self, missing_pg):
+ self.missing_pg = missing_pg
class PluginMgrTestFail(Exception):
def __init__(self, stage, output, message):
@@ -37,7 +40,7 @@ class PluginMgr:
super().__init__()
self.plugins = {}
self.plugin_instances = []
- self.args = []
+ self.failed_plugins = {}
self.argparser = argparser
# TODO, put plugins in order
@@ -53,6 +56,64 @@ class PluginMgr:
self.plugins[mn] = foo
self.plugin_instances.append(foo.SubPlugin())
+ def load_plugin(self, pgdir, pgname):
+ pgname = pgname[0:-3]
+ foo = importlib.import_module('{}.{}'.format(pgdir, pgname))
+ self.plugins[pgname] = foo
+ self.plugin_instances.append(foo.SubPlugin())
+ self.plugin_instances[-1].check_args(self.args, None)
+
+ def get_required_plugins(self, testlist):
+ '''
+ Get all required plugins from the list of test cases and return
+ all unique items.
+ '''
+ reqs = []
+ for t in testlist:
+ try:
+ if 'requires' in t['plugins']:
+ if isinstance(t['plugins']['requires'], list):
+ reqs.extend(t['plugins']['requires'])
+ else:
+ reqs.append(t['plugins']['requires'])
+ except KeyError:
+ continue
+ reqs = get_unique_item(reqs)
+ return reqs
+
+ def load_required_plugins(self, reqs, parser, args, remaining):
+ '''
+ Get all required plugins from the list of test cases and load any plugin
+ that is not already enabled.
+ '''
+ pgd = ['plugin-lib', 'plugin-lib-custom']
+ pnf = []
+
+ for r in reqs:
+ if r not in self.plugins:
+ fname = '{}.py'.format(r)
+ source_path = []
+ for d in pgd:
+ pgpath = '{}/{}'.format(d, fname)
+ if os.path.isfile(pgpath):
+ source_path.append(pgpath)
+ if len(source_path) == 0:
+ print('ERROR: unable to find required plugin {}'.format(r))
+ pnf.append(fname)
+ continue
+ elif len(source_path) > 1:
+ print('WARNING: multiple copies of plugin {} found, using version found')
+ print('at {}'.format(source_path[0]))
+ pgdir = source_path[0]
+ pgdir = pgdir.split('/')[0]
+ self.load_plugin(pgdir, fname)
+ if len(pnf) > 0:
+ raise PluginDependencyException(pnf)
+
+ parser = self.call_add_args(parser)
+ (args, remaining) = parser.parse_known_args(args=remaining, namespace=args)
+ return args
+
def call_pre_suite(self, testcount, testidlist):
for pgn_inst in self.plugin_instances:
pgn_inst.pre_suite(testcount, testidlist)
@@ -61,15 +122,15 @@ class PluginMgr:
for pgn_inst in reversed(self.plugin_instances):
pgn_inst.post_suite(index)
- def call_pre_case(self, testid, test_name, *, test_skip=False):
+ def call_pre_case(self, caseinfo, *, test_skip=False):
for pgn_inst in self.plugin_instances:
try:
- pgn_inst.pre_case(testid, test_name, test_skip)
+ pgn_inst.pre_case(caseinfo, test_skip)
except Exception as ee:
print('exception {} in call to pre_case for {} plugin'.
format(ee, pgn_inst.__class__))
print('test_ordinal is {}'.format(test_ordinal))
- print('testid is {}'.format(testid))
+ print('testid is {}'.format(caseinfo['id']))
raise
def call_post_case(self):
@@ -98,6 +159,9 @@ class PluginMgr:
command = pgn_inst.adjust_command(stage, command)
return command
+ def set_args(self, args):
+ self.args = args
+
@staticmethod
def _make_argparser(args):
self.argparser = argparse.ArgumentParser(
@@ -197,14 +261,14 @@ def run_one_test(pm, args, index, tidx):
res = TestResult(tidx['id'], tidx['name'])
res.set_result(ResultState.skip)
res.set_errormsg('Test case designated as skipped.')
- pm.call_pre_case(tidx['id'], tidx['name'], test_skip=True)
+ pm.call_pre_case(tidx, test_skip=True)
pm.call_post_execute()
return res
# populate NAMES with TESTID for this test
NAMES['TESTID'] = tidx['id']
- pm.call_pre_case(tidx['id'], tidx['name'])
+ pm.call_pre_case(tidx)
prepare_env(args, pm, 'setup', "-----> prepare stage", tidx["setup"])
if (args.verbose > 0):
@@ -292,12 +356,14 @@ def test_runner(pm, args, filtered_tests):
time.sleep(2)
for tidx in testlist:
if "flower" in tidx["category"] and args.device == None:
+ errmsg = "Tests using the DEV2 variable must define the name of a "
+ errmsg += "physical NIC with the -d option when running tdc.\n"
+ errmsg += "Test has been skipped."
if args.verbose > 1:
- print('Not executing test {} {} because DEV2 not defined'.
- format(tidx['id'], tidx['name']))
+ print(errmsg)
res = TestResult(tidx['id'], tidx['name'])
res.set_result(ResultState.skip)
- res.set_errormsg('Not executed because DEV2 is not defined')
+ res.set_errormsg(errmsg)
tsr.add_resultdata(res)
continue
try:
@@ -435,7 +501,9 @@ def set_args(parser):
choices=['none', 'xunit', 'tap'],
help='Specify the format for test results. (Default: TAP)')
parser.add_argument('-d', '--device',
- help='Execute the test case in flower category')
+ help='Execute test cases that use a physical device, ' +
+ 'where DEVICE is its name. (If not defined, tests ' +
+ 'that require a physical device will be skipped)')
parser.add_argument(
'-P', '--pause', action='store_true',
help='Pause execution just before post-suite stage')
@@ -550,6 +618,7 @@ def filter_tests_by_category(args, testlist):
return answer
+
def get_test_cases(args):
"""
If a test case file is specified, retrieve tests from that file.
@@ -611,7 +680,7 @@ def get_test_cases(args):
return allcatlist, allidlist, testcases_by_cats, alltestcases
-def set_operation_mode(pm, args):
+def set_operation_mode(pm, parser, args, remaining):
"""
Load the test case data and process remaining arguments to determine
what the script should do for this run, and call the appropriate
@@ -649,6 +718,12 @@ def set_operation_mode(pm, args):
exit(0)
if len(alltests):
+ req_plugins = pm.get_required_plugins(alltests)
+ try:
+ args = pm.load_required_plugins(req_plugins, parser, args, remaining)
+ except PluginDependencyException as pde:
+ print('The following plugins were not found:')
+ print('{}'.format(pde.missing_pg))
catresults = test_runner(pm, args, alltests)
if args.format == 'none':
print('Test results output suppression requested\n')
@@ -686,11 +761,12 @@ def main():
parser = pm.call_add_args(parser)
(args, remaining) = parser.parse_known_args()
args.NAMES = NAMES
+ pm.set_args(args)
check_default_settings(args, remaining, pm)
if args.verbose > 2:
print('args is {}'.format(args))
- set_operation_mode(pm, args)
+ set_operation_mode(pm, parser, args, remaining)
exit(0)
diff --git a/tools/testing/selftests/tc-testing/tdc_config.py b/tools/testing/selftests/tc-testing/tdc_config.py
index 942c70c041be..080709cc4297 100644
--- a/tools/testing/selftests/tc-testing/tdc_config.py
+++ b/tools/testing/selftests/tc-testing/tdc_config.py
@@ -10,10 +10,13 @@ Copyright (C) 2017 Lucas Bates <lucasb@mojatatu.com>
NAMES = {
# Substitute your own tc path here
'TC': '/sbin/tc',
+ # Substitute your own ip path here
+ 'IP': '/sbin/ip',
# Name of veth devices to be created for the namespace
'DEV0': 'v0p0',
'DEV1': 'v0p1',
'DEV2': '',
+ 'DUMMY': 'dummy1',
'BATCH_FILE': './batch.txt',
'BATCH_DIR': 'tmp',
# Length of time in seconds to wait before terminating a command
diff --git a/tools/testing/selftests/tc-testing/tdc_helper.py b/tools/testing/selftests/tc-testing/tdc_helper.py
index 9f35c96c88a0..0440d252c4c5 100644
--- a/tools/testing/selftests/tc-testing/tdc_helper.py
+++ b/tools/testing/selftests/tc-testing/tdc_helper.py
@@ -17,7 +17,10 @@ def get_categorized_testlist(alltests, ucat):
def get_unique_item(lst):
""" For a list, return a list of the unique items in the list. """
- return list(set(lst))
+ if len(lst) > 1:
+ return list(set(lst))
+ else:
+ return lst
def get_test_categories(alltests):
diff --git a/tools/testing/selftests/timers/adjtick.c b/tools/testing/selftests/timers/adjtick.c
index 0caca3a06bd2..54d8d87f36b3 100644
--- a/tools/testing/selftests/timers/adjtick.c
+++ b/tools/testing/selftests/timers/adjtick.c
@@ -136,6 +136,7 @@ int check_tick_adj(long tickval)
eppm = get_ppm_drift();
printf("%lld usec, %lld ppm", systick + (systick * eppm / MILLION), eppm);
+ fflush(stdout);
tx1.modes = 0;
adjtimex(&tx1);
diff --git a/tools/testing/selftests/timers/freq-step.c b/tools/testing/selftests/timers/freq-step.c
index 14a2b77fd012..4b76450d78d1 100644
--- a/tools/testing/selftests/timers/freq-step.c
+++ b/tools/testing/selftests/timers/freq-step.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* This test checks the response of the system clock to frequency
* steps made with adjtimex(). The frequency error and stability of
@@ -6,15 +7,6 @@
* values from the second interval exceed specified limits.
*
* Copyright (C) Miroslav Lichvar <mlichvar@redhat.com> 2017
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#include <math.h>
@@ -29,9 +21,9 @@
#define SAMPLE_READINGS 10
#define MEAN_SAMPLE_INTERVAL 0.1
#define STEP_INTERVAL 1.0
-#define MAX_PRECISION 100e-9
-#define MAX_FREQ_ERROR 10e-6
-#define MAX_STDDEV 1000e-9
+#define MAX_PRECISION 500e-9
+#define MAX_FREQ_ERROR 0.02e-6
+#define MAX_STDDEV 50e-9
#ifndef ADJ_SETOFFSET
#define ADJ_SETOFFSET 0x0100
diff --git a/tools/testing/selftests/timers/leapcrash.c b/tools/testing/selftests/timers/leapcrash.c
index 830c462f605d..dc80728ed191 100644
--- a/tools/testing/selftests/timers/leapcrash.c
+++ b/tools/testing/selftests/timers/leapcrash.c
@@ -101,6 +101,7 @@ int main(void)
}
clear_time_state();
printf(".");
+ fflush(stdout);
}
printf("[OK]\n");
return ksft_exit_pass();
diff --git a/tools/testing/selftests/timers/mqueue-lat.c b/tools/testing/selftests/timers/mqueue-lat.c
index 1867db5d6f5e..7916cf5cc6ff 100644
--- a/tools/testing/selftests/timers/mqueue-lat.c
+++ b/tools/testing/selftests/timers/mqueue-lat.c
@@ -102,6 +102,7 @@ int main(int argc, char **argv)
int ret;
printf("Mqueue latency : ");
+ fflush(stdout);
ret = mqueue_lat_test();
if (ret < 0) {
diff --git a/tools/testing/selftests/timers/nanosleep.c b/tools/testing/selftests/timers/nanosleep.c
index 8adb0bb51d4d..71b5441c2fd9 100644
--- a/tools/testing/selftests/timers/nanosleep.c
+++ b/tools/testing/selftests/timers/nanosleep.c
@@ -142,6 +142,7 @@ int main(int argc, char **argv)
continue;
printf("Nanosleep %-31s ", clockstring(clockid));
+ fflush(stdout);
length = 10;
while (length <= (NSEC_PER_SEC * 10)) {
diff --git a/tools/testing/selftests/timers/nsleep-lat.c b/tools/testing/selftests/timers/nsleep-lat.c
index c3c3dc10db17..eb3e79ed7b4a 100644
--- a/tools/testing/selftests/timers/nsleep-lat.c
+++ b/tools/testing/selftests/timers/nsleep-lat.c
@@ -155,6 +155,7 @@ int main(int argc, char **argv)
continue;
printf("nsleep latency %-26s ", clockstring(clockid));
+ fflush(stdout);
length = 10;
while (length <= (NSEC_PER_SEC * 10)) {
diff --git a/tools/testing/selftests/timers/posix_timers.c b/tools/testing/selftests/timers/posix_timers.c
index 15cf56d32155..0ba500056e63 100644
--- a/tools/testing/selftests/timers/posix_timers.c
+++ b/tools/testing/selftests/timers/posix_timers.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013 Red Hat, Inc., Frederic Weisbecker <fweisbec@redhat.com>
*
- * Licensed under the terms of the GNU GPL License version 2
- *
* Selftests for a few posix timers interface.
*
* Kernel loop code stolen from Steven Rostedt <srostedt@redhat.com>
diff --git a/tools/testing/selftests/timers/raw_skew.c b/tools/testing/selftests/timers/raw_skew.c
index dcf73c5dab6e..b41d8dd0c40c 100644
--- a/tools/testing/selftests/timers/raw_skew.c
+++ b/tools/testing/selftests/timers/raw_skew.c
@@ -112,6 +112,7 @@ int main(int argv, char **argc)
printf("WARNING: ADJ_OFFSET in progress, this will cause inaccurate results\n");
printf("Estimating clock drift: ");
+ fflush(stdout);
sleep(120);
get_monotonic_and_raw(&mon, &raw);
diff --git a/tools/testing/selftests/timers/set-tai.c b/tools/testing/selftests/timers/set-tai.c
index 70fed27d8fd3..8c4179ee2ca2 100644
--- a/tools/testing/selftests/timers/set-tai.c
+++ b/tools/testing/selftests/timers/set-tai.c
@@ -55,6 +55,7 @@ int main(int argc, char **argv)
printf("tai offset started at %i\n", ret);
printf("Checking tai offsets can be properly set: ");
+ fflush(stdout);
for (i = 1; i <= 60; i++) {
ret = set_tai(i);
ret = get_tai();
diff --git a/tools/testing/selftests/timers/set-tz.c b/tools/testing/selftests/timers/set-tz.c
index 877fd5532fee..62bd33eb16f0 100644
--- a/tools/testing/selftests/timers/set-tz.c
+++ b/tools/testing/selftests/timers/set-tz.c
@@ -65,6 +65,7 @@ int main(int argc, char **argv)
printf("tz_minuteswest started at %i, dst at %i\n", min, dst);
printf("Checking tz_minuteswest can be properly set: ");
+ fflush(stdout);
for (i = -15*60; i < 15*60; i += 30) {
ret = set_tz(i, dst);
ret = get_tz_min();
@@ -76,6 +77,7 @@ int main(int argc, char **argv)
printf("[OK]\n");
printf("Checking invalid tz_minuteswest values are caught: ");
+ fflush(stdout);
if (!set_tz(-15*60-1, dst)) {
printf("[FAILED] %i didn't return failure!\n", -15*60-1);
diff --git a/tools/testing/selftests/timers/threadtest.c b/tools/testing/selftests/timers/threadtest.c
index 759c9c06f1a0..cf3e48919874 100644
--- a/tools/testing/selftests/timers/threadtest.c
+++ b/tools/testing/selftests/timers/threadtest.c
@@ -163,6 +163,7 @@ int main(int argc, char **argv)
strftime(buf, 255, "%a, %d %b %Y %T %z", localtime(&start));
printf("%s\n", buf);
printf("Testing consistency with %i threads for %ld seconds: ", thread_count, runtime);
+ fflush(stdout);
/* spawn */
for (i = 0; i < thread_count; i++)
diff --git a/tools/testing/selftests/timers/valid-adjtimex.c b/tools/testing/selftests/timers/valid-adjtimex.c
index d9d3ab93b31a..5397de708d3c 100644
--- a/tools/testing/selftests/timers/valid-adjtimex.c
+++ b/tools/testing/selftests/timers/valid-adjtimex.c
@@ -123,6 +123,7 @@ int validate_freq(void)
/* Set the leap second insert flag */
printf("Testing ADJ_FREQ... ");
+ fflush(stdout);
for (i = 0; i < NUM_FREQ_VALID; i++) {
tx.modes = ADJ_FREQUENCY;
tx.freq = valid_freq[i];
@@ -250,6 +251,7 @@ int set_bad_offset(long sec, long usec, int use_nano)
int validate_set_offset(void)
{
printf("Testing ADJ_SETOFFSET... ");
+ fflush(stdout);
/* Test valid values */
if (set_offset(NSEC_PER_SEC - 1, 1))
diff --git a/tools/testing/selftests/tmpfs/Makefile b/tools/testing/selftests/tmpfs/Makefile
index 953c81299181..aa11ccc92e5b 100644
--- a/tools/testing/selftests/tmpfs/Makefile
+++ b/tools/testing/selftests/tmpfs/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
CFLAGS += -Wall -O2
CFLAGS += -D_GNU_SOURCE
diff --git a/tools/testing/selftests/user/Makefile b/tools/testing/selftests/user/Makefile
index d401b63c5b1a..640a40f9b72b 100644
--- a/tools/testing/selftests/user/Makefile
+++ b/tools/testing/selftests/user/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# Makefile for user memory selftests
# No binaries, but make sure arg-less "make" doesn't trigger "run_tests"
diff --git a/tools/testing/selftests/vDSO/vdso_standalone_test_x86.c b/tools/testing/selftests/vDSO/vdso_standalone_test_x86.c
index 93b0ebf8cc38..5ac4b00acfbc 100644
--- a/tools/testing/selftests/vDSO/vdso_standalone_test_x86.c
+++ b/tools/testing/selftests/vDSO/vdso_standalone_test_x86.c
@@ -1,7 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vdso_test.c: Sample code to test parse_vdso.c on x86
* Copyright (c) 2011-2014 Andy Lutomirski
- * Subject to the GNU General Public License, version 2
*
* You can amuse yourself by compiling with:
* gcc -std=gnu99 -nostdlib
diff --git a/tools/testing/selftests/vDSO/vdso_test.c b/tools/testing/selftests/vDSO/vdso_test.c
index eda53f833d8e..719d5a6bd664 100644
--- a/tools/testing/selftests/vDSO/vdso_test.c
+++ b/tools/testing/selftests/vDSO/vdso_test.c
@@ -1,7 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vdso_test.c: Sample code to test parse_vdso.c
* Copyright (c) 2014 Andy Lutomirski
- * Subject to the GNU General Public License, version 2
*
* Compile with:
* gcc -std=gnu99 vdso_test.c parse_vdso.c
diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile
index e13eb6cc8901..9534dc2bc929 100644
--- a/tools/testing/selftests/vm/Makefile
+++ b/tools/testing/selftests/vm/Makefile
@@ -1,10 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
# Makefile for vm selftests
-ifndef OUTPUT
- OUTPUT := $(shell pwd)
-endif
-
CFLAGS = -Wall -I ../../../../usr/include $(EXTRA_CFLAGS)
LDLIBS = -lrt
TEST_GEN_FILES = compaction_test
@@ -25,6 +21,8 @@ TEST_GEN_FILES += virtual_address_range
TEST_PROGS := run_vmtests
+TEST_FILES := test_vmalloc.sh
+
KSFT_KHDR_INSTALL := 1
include ../lib.mk
diff --git a/tools/testing/selftests/vm/userfaultfd.c b/tools/testing/selftests/vm/userfaultfd.c
index 5d1db824f73a..d3362777a425 100644
--- a/tools/testing/selftests/vm/userfaultfd.c
+++ b/tools/testing/selftests/vm/userfaultfd.c
@@ -1,11 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Stress userfaultfd syscall.
*
* Copyright (C) 2015 Red Hat, Inc.
*
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- *
* This test allocates two virtual areas and bounces the physical
* memory across the two virtual areas (from area_src to area_dst)
* using userfaultfd.
@@ -123,7 +121,7 @@ static void usage(void)
fprintf(stderr, "Supported <test type>: anon, hugetlb, "
"hugetlb_shared, shmem\n\n");
fprintf(stderr, "Examples:\n\n");
- fprintf(stderr, examples);
+ fprintf(stderr, "%s", examples);
exit(1);
}
diff --git a/tools/testing/selftests/vm/va_128TBswitch.c b/tools/testing/selftests/vm/va_128TBswitch.c
index e7fe734c374f..83acdff26a13 100644
--- a/tools/testing/selftests/vm/va_128TBswitch.c
+++ b/tools/testing/selftests/vm/va_128TBswitch.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Authors: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
* Authors: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2, as
- * published by the Free Software Foundation.
-
- * This program is distributed in the hope that it would be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
*/
#include <stdio.h>
diff --git a/tools/testing/selftests/vm/virtual_address_range.c b/tools/testing/selftests/vm/virtual_address_range.c
index 1830d66a6f0e..c0592646ed93 100644
--- a/tools/testing/selftests/vm/virtual_address_range.c
+++ b/tools/testing/selftests/vm/virtual_address_range.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2017, Anshuman Khandual, IBM Corp.
- * Licensed under GPLv2.
*
* Works on architectures which support 128TB virtual
* address range and beyond.
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 186520198de7..5d49bfec1e9a 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -11,12 +11,13 @@ CAN_BUILD_X86_64 := $(shell ./check_cc.sh $(CC) trivial_64bit_program.c)
CAN_BUILD_WITH_NOPIE := $(shell ./check_cc.sh $(CC) trivial_program.c -no-pie)
TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt test_mremap_vdso \
- check_initial_reg_state sigreturn iopl mpx-mini-test ioperm \
- protection_keys test_vdso test_vsyscall mov_ss_trap
-TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \
+ check_initial_reg_state sigreturn iopl ioperm \
+ protection_keys test_vdso test_vsyscall mov_ss_trap \
+ syscall_arg_fault
+TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso unwind_vdso \
test_FCMOV test_FCOMI test_FISTTP \
vdso_restorer
-TARGETS_C_64BIT_ONLY := fsgsbase sysret_rip
+TARGETS_C_64BIT_ONLY := fsgsbase sysret_rip syscall_numbering
# Some selftests require 32bit support enabled also on 64bit systems
TARGETS_C_32BIT_NEEDED := ldt_gdt ptrace_syscall
diff --git a/tools/testing/selftests/x86/check_cc.sh b/tools/testing/selftests/x86/check_cc.sh
index 172d3293fb7b..3e2089c8cf54 100755
--- a/tools/testing/selftests/x86/check_cc.sh
+++ b/tools/testing/selftests/x86/check_cc.sh
@@ -1,7 +1,7 @@
#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
# check_cc.sh - Helper to test userspace compilation support
# Copyright (c) 2015 Andrew Lutomirski
-# GPL v2
CC="$1"
TESTPROG="$2"
diff --git a/tools/testing/selftests/x86/check_initial_reg_state.c b/tools/testing/selftests/x86/check_initial_reg_state.c
index 6aaed9b85baf..3bc95f3ed585 100644
--- a/tools/testing/selftests/x86/check_initial_reg_state.c
+++ b/tools/testing/selftests/x86/check_initial_reg_state.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* check_initial_reg_state.c - check that execve sets the correct state
* Copyright (c) 2014-2016 Andrew Lutomirski
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#define _GNU_SOURCE
diff --git a/tools/testing/selftests/x86/entry_from_vm86.c b/tools/testing/selftests/x86/entry_from_vm86.c
index ade443a88421..d1e919b0c1dc 100644
--- a/tools/testing/selftests/x86/entry_from_vm86.c
+++ b/tools/testing/selftests/x86/entry_from_vm86.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* entry_from_vm86.c - tests kernel entries from vm86 mode
* Copyright (c) 2014-2015 Andrew Lutomirski
*
* This exercises a few paths that need to special-case vm86 mode.
- *
- * GPL v2.
*/
#define _GNU_SOURCE
diff --git a/tools/testing/selftests/x86/fsgsbase.c b/tools/testing/selftests/x86/fsgsbase.c
index f249e042b3b5..15a329da59fa 100644
--- a/tools/testing/selftests/x86/fsgsbase.c
+++ b/tools/testing/selftests/x86/fsgsbase.c
@@ -1,7 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* fsgsbase.c, an fsgsbase test
* Copyright (c) 2014-2016 Andy Lutomirski
- * GPL v2
*/
#define _GNU_SOURCE
@@ -23,6 +23,10 @@
#include <pthread.h>
#include <asm/ldt.h>
#include <sys/mman.h>
+#include <stddef.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <setjmp.h>
#ifndef __x86_64__
# error This test is 64-bit only
@@ -31,6 +35,8 @@
static volatile sig_atomic_t want_segv;
static volatile unsigned long segv_addr;
+static unsigned short *shared_scratch;
+
static int nerrs;
static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
@@ -71,6 +77,43 @@ static void sigsegv(int sig, siginfo_t *si, void *ctx_void)
}
+static jmp_buf jmpbuf;
+
+static void sigill(int sig, siginfo_t *si, void *ctx_void)
+{
+ siglongjmp(jmpbuf, 1);
+}
+
+static bool have_fsgsbase;
+
+static inline unsigned long rdgsbase(void)
+{
+ unsigned long gsbase;
+
+ asm volatile("rdgsbase %0" : "=r" (gsbase) :: "memory");
+
+ return gsbase;
+}
+
+static inline unsigned long rdfsbase(void)
+{
+ unsigned long fsbase;
+
+ asm volatile("rdfsbase %0" : "=r" (fsbase) :: "memory");
+
+ return fsbase;
+}
+
+static inline void wrgsbase(unsigned long gsbase)
+{
+ asm volatile("wrgsbase %0" :: "r" (gsbase) : "memory");
+}
+
+static inline void wrfsbase(unsigned long fsbase)
+{
+ asm volatile("wrfsbase %0" :: "r" (fsbase) : "memory");
+}
+
enum which_base { FS, GS };
static unsigned long read_base(enum which_base which)
@@ -199,16 +242,13 @@ static void do_remote_base()
to_set, hard_zero ? " and clear gs" : "", sel);
}
-void do_unexpected_base(void)
+static __thread int set_thread_area_entry_number = -1;
+
+static unsigned short load_gs(void)
{
/*
- * The goal here is to try to arrange for GS == 0, GSBASE !=
- * 0, and for the the kernel the think that GSBASE == 0.
- *
- * To make the test as reliable as possible, this uses
- * explicit descriptorss. (This is not the only way. This
- * could use ARCH_SET_GS with a low, nonzero base, but the
- * relevant side effect of ARCH_SET_GS could change.)
+ * Sets GS != 0 and GSBASE != 0 but arranges for the kernel to think
+ * that GSBASE == 0 (i.e. thread.gsbase == 0).
*/
/* Step 1: tell the kernel that we have GSBASE == 0. */
@@ -228,8 +268,9 @@ void do_unexpected_base(void)
.useable = 0
};
if (syscall(SYS_modify_ldt, 1, &desc, sizeof(desc)) == 0) {
- printf("\tother thread: using LDT slot 0\n");
+ printf("\tusing LDT slot 0\n");
asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0x7));
+ return 0x7;
} else {
/* No modify_ldt for us (configured out, perhaps) */
@@ -239,7 +280,7 @@ void do_unexpected_base(void)
MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
memcpy(low_desc, &desc, sizeof(desc));
- low_desc->entry_number = -1;
+ low_desc->entry_number = set_thread_area_entry_number;
/* 32-bit set_thread_area */
long ret;
@@ -251,18 +292,43 @@ void do_unexpected_base(void)
if (ret != 0) {
printf("[NOTE]\tcould not create a segment -- test won't do anything\n");
- return;
+ return 0;
}
- printf("\tother thread: using GDT slot %d\n", desc.entry_number);
- asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)((desc.entry_number << 3) | 0x3)));
+ printf("\tusing GDT slot %d\n", desc.entry_number);
+ set_thread_area_entry_number = desc.entry_number;
+
+ unsigned short gs = (unsigned short)((desc.entry_number << 3) | 0x3);
+ asm volatile ("mov %0, %%gs" : : "rm" (gs));
+ return gs;
}
+}
- /*
- * Step 3: set the selector back to zero. On AMD chips, this will
- * preserve GSBASE.
- */
+void test_wrbase(unsigned short index, unsigned long base)
+{
+ unsigned short newindex;
+ unsigned long newbase;
- asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0));
+ printf("[RUN]\tGS = 0x%hx, GSBASE = 0x%lx\n", index, base);
+
+ asm volatile ("mov %0, %%gs" : : "rm" (index));
+ wrgsbase(base);
+
+ remote_base = 0;
+ ftx = 1;
+ syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0);
+ while (ftx != 0)
+ syscall(SYS_futex, &ftx, FUTEX_WAIT, 1, NULL, NULL, 0);
+
+ asm volatile ("mov %%gs, %0" : "=rm" (newindex));
+ newbase = rdgsbase();
+
+ if (newindex == index && newbase == base) {
+ printf("[OK]\tIndex and base were preserved\n");
+ } else {
+ printf("[FAIL]\tAfter switch, GS = 0x%hx and GSBASE = 0x%lx\n",
+ newindex, newbase);
+ nerrs++;
+ }
}
static void *threadproc(void *ctx)
@@ -273,12 +339,19 @@ static void *threadproc(void *ctx)
if (ftx == 3)
return NULL;
- if (ftx == 1)
+ if (ftx == 1) {
do_remote_base();
- else if (ftx == 2)
- do_unexpected_base();
- else
+ } else if (ftx == 2) {
+ /*
+ * On AMD chips, this causes GSBASE != 0, GS == 0, and
+ * thread.gsbase == 0.
+ */
+
+ load_gs();
+ asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0));
+ } else {
errx(1, "helper thread got bad command");
+ }
ftx = 0;
syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0);
@@ -367,10 +440,85 @@ static void test_unexpected_base(void)
}
}
+#define USER_REGS_OFFSET(r) offsetof(struct user_regs_struct, r)
+
+static void test_ptrace_write_gsbase(void)
+{
+ int status;
+ pid_t child = fork();
+
+ if (child < 0)
+ err(1, "fork");
+
+ if (child == 0) {
+ printf("[RUN]\tPTRACE_POKE(), write GSBASE from ptracer\n");
+
+ *shared_scratch = load_gs();
+
+ if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0)
+ err(1, "PTRACE_TRACEME");
+
+ raise(SIGTRAP);
+ _exit(0);
+ }
+
+ wait(&status);
+
+ if (WSTOPSIG(status) == SIGTRAP) {
+ unsigned long gs, base;
+ unsigned long gs_offset = USER_REGS_OFFSET(gs);
+ unsigned long base_offset = USER_REGS_OFFSET(gs_base);
+
+ gs = ptrace(PTRACE_PEEKUSER, child, gs_offset, NULL);
+
+ if (gs != *shared_scratch) {
+ nerrs++;
+ printf("[FAIL]\tGS is not prepared with nonzero\n");
+ goto END;
+ }
+
+ if (ptrace(PTRACE_POKEUSER, child, base_offset, 0xFF) != 0)
+ err(1, "PTRACE_POKEUSER");
+
+ gs = ptrace(PTRACE_PEEKUSER, child, gs_offset, NULL);
+ base = ptrace(PTRACE_PEEKUSER, child, base_offset, NULL);
+
+ /*
+ * In a non-FSGSBASE system, the nonzero selector will load
+ * GSBASE (again). But what is tested here is whether the
+ * selector value is changed or not by the GSBASE write in
+ * a ptracer.
+ */
+ if (gs == 0 && base == 0xFF) {
+ printf("[OK]\tGS was reset as expected\n");
+ } else {
+ nerrs++;
+ printf("[FAIL]\tGS=0x%lx, GSBASE=0x%lx (should be 0, 0xFF)\n", gs, base);
+ }
+ }
+
+END:
+ ptrace(PTRACE_CONT, child, NULL, NULL);
+}
+
int main()
{
pthread_t thread;
+ shared_scratch = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+
+ /* Probe FSGSBASE */
+ sethandler(SIGILL, sigill, 0);
+ if (sigsetjmp(jmpbuf, 1) == 0) {
+ rdfsbase();
+ have_fsgsbase = true;
+ printf("\tFSGSBASE instructions are enabled\n");
+ } else {
+ printf("\tFSGSBASE instructions are disabled\n");
+ }
+ clearhandler(SIGILL);
+
sethandler(SIGSEGV, sigsegv, 0);
check_gs_value(0);
@@ -417,11 +565,28 @@ int main()
test_unexpected_base();
+ if (have_fsgsbase) {
+ unsigned short ss;
+
+ asm volatile ("mov %%ss, %0" : "=rm" (ss));
+
+ test_wrbase(0, 0);
+ test_wrbase(0, 1);
+ test_wrbase(0, 0x200000000);
+ test_wrbase(0, 0xffffffffffffffff);
+ test_wrbase(ss, 0);
+ test_wrbase(ss, 1);
+ test_wrbase(ss, 0x200000000);
+ test_wrbase(ss, 0xffffffffffffffff);
+ }
+
ftx = 3; /* Kill the thread. */
syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0);
if (pthread_join(thread, NULL) != 0)
err(1, "pthread_join");
+ test_ptrace_write_gsbase();
+
return nerrs == 0 ? 0 : 1;
}
diff --git a/tools/testing/selftests/x86/mpx-debug.h b/tools/testing/selftests/x86/mpx-debug.h
deleted file mode 100644
index 7546eba7f17a..000000000000
--- a/tools/testing/selftests/x86/mpx-debug.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _MPX_DEBUG_H
-#define _MPX_DEBUG_H
-
-#ifndef DEBUG_LEVEL
-#define DEBUG_LEVEL 0
-#endif
-#define dprintf_level(level, args...) do { if(level <= DEBUG_LEVEL) printf(args); } while(0)
-#define dprintf1(args...) dprintf_level(1, args)
-#define dprintf2(args...) dprintf_level(2, args)
-#define dprintf3(args...) dprintf_level(3, args)
-#define dprintf4(args...) dprintf_level(4, args)
-#define dprintf5(args...) dprintf_level(5, args)
-
-#endif /* _MPX_DEBUG_H */
diff --git a/tools/testing/selftests/x86/mpx-dig.c b/tools/testing/selftests/x86/mpx-dig.c
deleted file mode 100644
index 880fbf676968..000000000000
--- a/tools/testing/selftests/x86/mpx-dig.c
+++ /dev/null
@@ -1,497 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Written by Dave Hansen <dave.hansen@intel.com>
- */
-
-#include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <string.h>
-#include <fcntl.h>
-#include "mpx-debug.h"
-#include "mpx-mm.h"
-#include "mpx-hw.h"
-
-unsigned long bounds_dir_global;
-
-#define mpx_dig_abort() __mpx_dig_abort(__FILE__, __func__, __LINE__)
-static void inline __mpx_dig_abort(const char *file, const char *func, int line)
-{
- fprintf(stderr, "MPX dig abort @ %s::%d in %s()\n", file, line, func);
- printf("MPX dig abort @ %s::%d in %s()\n", file, line, func);
- abort();
-}
-
-/*
- * run like this (BDIR finds the probably bounds directory):
- *
- * BDIR="$(cat /proc/$pid/smaps | grep -B1 2097152 \
- * | head -1 | awk -F- '{print $1}')";
- * ./mpx-dig $pid 0x$BDIR
- *
- * NOTE:
- * assumes that the only 2097152-kb VMA is the bounds dir
- */
-
-long nr_incore(void *ptr, unsigned long size_bytes)
-{
- int i;
- long ret = 0;
- long vec_len = size_bytes / PAGE_SIZE;
- unsigned char *vec = malloc(vec_len);
- int incore_ret;
-
- if (!vec)
- mpx_dig_abort();
-
- incore_ret = mincore(ptr, size_bytes, vec);
- if (incore_ret) {
- printf("mincore ret: %d\n", incore_ret);
- perror("mincore");
- mpx_dig_abort();
- }
- for (i = 0; i < vec_len; i++)
- ret += vec[i];
- free(vec);
- return ret;
-}
-
-int open_proc(int pid, char *file)
-{
- static char buf[100];
- int fd;
-
- snprintf(&buf[0], sizeof(buf), "/proc/%d/%s", pid, file);
- fd = open(&buf[0], O_RDONLY);
- if (fd < 0)
- perror(buf);
-
- return fd;
-}
-
-struct vaddr_range {
- unsigned long start;
- unsigned long end;
-};
-struct vaddr_range *ranges;
-int nr_ranges_allocated;
-int nr_ranges_populated;
-int last_range = -1;
-
-int __pid_load_vaddrs(int pid)
-{
- int ret = 0;
- int proc_maps_fd = open_proc(pid, "maps");
- char linebuf[10000];
- unsigned long start;
- unsigned long end;
- char rest[1000];
- FILE *f = fdopen(proc_maps_fd, "r");
-
- if (!f)
- mpx_dig_abort();
- nr_ranges_populated = 0;
- while (!feof(f)) {
- char *readret = fgets(linebuf, sizeof(linebuf), f);
- int parsed;
-
- if (readret == NULL) {
- if (feof(f))
- break;
- mpx_dig_abort();
- }
-
- parsed = sscanf(linebuf, "%lx-%lx%s", &start, &end, rest);
- if (parsed != 3)
- mpx_dig_abort();
-
- dprintf4("result[%d]: %lx-%lx<->%s\n", parsed, start, end, rest);
- if (nr_ranges_populated >= nr_ranges_allocated) {
- ret = -E2BIG;
- break;
- }
- ranges[nr_ranges_populated].start = start;
- ranges[nr_ranges_populated].end = end;
- nr_ranges_populated++;
- }
- last_range = -1;
- fclose(f);
- close(proc_maps_fd);
- return ret;
-}
-
-int pid_load_vaddrs(int pid)
-{
- int ret;
-
- dprintf2("%s(%d)\n", __func__, pid);
- if (!ranges) {
- nr_ranges_allocated = 4;
- ranges = malloc(nr_ranges_allocated * sizeof(ranges[0]));
- dprintf2("%s(%d) allocated %d ranges @ %p\n", __func__, pid,
- nr_ranges_allocated, ranges);
- assert(ranges != NULL);
- }
- do {
- ret = __pid_load_vaddrs(pid);
- if (!ret)
- break;
- if (ret == -E2BIG) {
- dprintf2("%s(%d) need to realloc\n", __func__, pid);
- nr_ranges_allocated *= 2;
- ranges = realloc(ranges,
- nr_ranges_allocated * sizeof(ranges[0]));
- dprintf2("%s(%d) allocated %d ranges @ %p\n", __func__,
- pid, nr_ranges_allocated, ranges);
- assert(ranges != NULL);
- dprintf1("reallocating to hold %d ranges\n", nr_ranges_allocated);
- }
- } while (1);
-
- dprintf2("%s(%d) done\n", __func__, pid);
-
- return ret;
-}
-
-static inline int vaddr_in_range(unsigned long vaddr, struct vaddr_range *r)
-{
- if (vaddr < r->start)
- return 0;
- if (vaddr >= r->end)
- return 0;
- return 1;
-}
-
-static inline int vaddr_mapped_by_range(unsigned long vaddr)
-{
- int i;
-
- if (last_range > 0 && vaddr_in_range(vaddr, &ranges[last_range]))
- return 1;
-
- for (i = 0; i < nr_ranges_populated; i++) {
- struct vaddr_range *r = &ranges[i];
-
- if (vaddr_in_range(vaddr, r))
- continue;
- last_range = i;
- return 1;
- }
- return 0;
-}
-
-const int bt_entry_size_bytes = sizeof(unsigned long) * 4;
-
-void *read_bounds_table_into_buf(unsigned long table_vaddr)
-{
-#ifdef MPX_DIG_STANDALONE
- static char bt_buf[MPX_BOUNDS_TABLE_SIZE_BYTES];
- off_t seek_ret = lseek(fd, table_vaddr, SEEK_SET);
- if (seek_ret != table_vaddr)
- mpx_dig_abort();
-
- int read_ret = read(fd, &bt_buf, sizeof(bt_buf));
- if (read_ret != sizeof(bt_buf))
- mpx_dig_abort();
- return &bt_buf;
-#else
- return (void *)table_vaddr;
-#endif
-}
-
-int dump_table(unsigned long table_vaddr, unsigned long base_controlled_vaddr,
- unsigned long bde_vaddr)
-{
- unsigned long offset_inside_bt;
- int nr_entries = 0;
- int do_abort = 0;
- char *bt_buf;
-
- dprintf3("%s() base_controlled_vaddr: 0x%012lx bde_vaddr: 0x%012lx\n",
- __func__, base_controlled_vaddr, bde_vaddr);
-
- bt_buf = read_bounds_table_into_buf(table_vaddr);
-
- dprintf4("%s() read done\n", __func__);
-
- for (offset_inside_bt = 0;
- offset_inside_bt < MPX_BOUNDS_TABLE_SIZE_BYTES;
- offset_inside_bt += bt_entry_size_bytes) {
- unsigned long bt_entry_index;
- unsigned long bt_entry_controls;
- unsigned long this_bt_entry_for_vaddr;
- unsigned long *bt_entry_buf;
- int i;
-
- dprintf4("%s() offset_inside_bt: 0x%lx of 0x%llx\n", __func__,
- offset_inside_bt, MPX_BOUNDS_TABLE_SIZE_BYTES);
- bt_entry_buf = (void *)&bt_buf[offset_inside_bt];
- if (!bt_buf) {
- printf("null bt_buf\n");
- mpx_dig_abort();
- }
- if (!bt_entry_buf) {
- printf("null bt_entry_buf\n");
- mpx_dig_abort();
- }
- dprintf4("%s() reading *bt_entry_buf @ %p\n", __func__,
- bt_entry_buf);
- if (!bt_entry_buf[0] &&
- !bt_entry_buf[1] &&
- !bt_entry_buf[2] &&
- !bt_entry_buf[3])
- continue;
-
- nr_entries++;
-
- bt_entry_index = offset_inside_bt/bt_entry_size_bytes;
- bt_entry_controls = sizeof(void *);
- this_bt_entry_for_vaddr =
- base_controlled_vaddr + bt_entry_index*bt_entry_controls;
- /*
- * We sign extend vaddr bits 48->63 which effectively
- * creates a hole in the virtual address space.
- * This calculation corrects for the hole.
- */
- if (this_bt_entry_for_vaddr > 0x00007fffffffffffUL)
- this_bt_entry_for_vaddr |= 0xffff800000000000;
-
- if (!vaddr_mapped_by_range(this_bt_entry_for_vaddr)) {
- printf("bt_entry_buf: %p\n", bt_entry_buf);
- printf("there is a bte for %lx but no mapping\n",
- this_bt_entry_for_vaddr);
- printf(" bde vaddr: %016lx\n", bde_vaddr);
- printf("base_controlled_vaddr: %016lx\n", base_controlled_vaddr);
- printf(" table_vaddr: %016lx\n", table_vaddr);
- printf(" entry vaddr: %016lx @ offset %lx\n",
- table_vaddr + offset_inside_bt, offset_inside_bt);
- do_abort = 1;
- mpx_dig_abort();
- }
- if (DEBUG_LEVEL < 4)
- continue;
-
- printf("table entry[%lx]: ", offset_inside_bt);
- for (i = 0; i < bt_entry_size_bytes; i += sizeof(unsigned long))
- printf("0x%016lx ", bt_entry_buf[i]);
- printf("\n");
- }
- if (do_abort)
- mpx_dig_abort();
- dprintf4("%s() done\n", __func__);
- return nr_entries;
-}
-
-int search_bd_buf(char *buf, int len_bytes, unsigned long bd_offset_bytes,
- int *nr_populated_bdes)
-{
- unsigned long i;
- int total_entries = 0;
-
- dprintf3("%s(%p, %x, %lx, ...) buf end: %p\n", __func__, buf,
- len_bytes, bd_offset_bytes, buf + len_bytes);
-
- for (i = 0; i < len_bytes; i += sizeof(unsigned long)) {
- unsigned long bd_index = (bd_offset_bytes + i) / sizeof(unsigned long);
- unsigned long *bounds_dir_entry_ptr = (unsigned long *)&buf[i];
- unsigned long bounds_dir_entry;
- unsigned long bd_for_vaddr;
- unsigned long bt_start;
- unsigned long bt_tail;
- int nr_entries;
-
- dprintf4("%s() loop i: %ld bounds_dir_entry_ptr: %p\n", __func__, i,
- bounds_dir_entry_ptr);
-
- bounds_dir_entry = *bounds_dir_entry_ptr;
- if (!bounds_dir_entry) {
- dprintf4("no bounds dir at index 0x%lx / 0x%lx "
- "start at offset:%lx %lx\n", bd_index, bd_index,
- bd_offset_bytes, i);
- continue;
- }
- dprintf3("found bounds_dir_entry: 0x%lx @ "
- "index 0x%lx buf ptr: %p\n", bounds_dir_entry, i,
- &buf[i]);
- /* mask off the enable bit: */
- bounds_dir_entry &= ~0x1;
- (*nr_populated_bdes)++;
- dprintf4("nr_populated_bdes: %p\n", nr_populated_bdes);
- dprintf4("*nr_populated_bdes: %d\n", *nr_populated_bdes);
-
- bt_start = bounds_dir_entry;
- bt_tail = bounds_dir_entry + MPX_BOUNDS_TABLE_SIZE_BYTES - 1;
- if (!vaddr_mapped_by_range(bt_start)) {
- printf("bounds directory 0x%lx points to nowhere\n",
- bounds_dir_entry);
- mpx_dig_abort();
- }
- if (!vaddr_mapped_by_range(bt_tail)) {
- printf("bounds directory end 0x%lx points to nowhere\n",
- bt_tail);
- mpx_dig_abort();
- }
- /*
- * Each bounds directory entry controls 1MB of virtual address
- * space. This variable is the virtual address in the process
- * of the beginning of the area controlled by this bounds_dir.
- */
- bd_for_vaddr = bd_index * (1UL<<20);
-
- nr_entries = dump_table(bounds_dir_entry, bd_for_vaddr,
- bounds_dir_global+bd_offset_bytes+i);
- total_entries += nr_entries;
- dprintf5("dir entry[%4ld @ %p]: 0x%lx %6d entries "
- "total this buf: %7d bd_for_vaddrs: 0x%lx -> 0x%lx\n",
- bd_index, buf+i,
- bounds_dir_entry, nr_entries, total_entries,
- bd_for_vaddr, bd_for_vaddr + (1UL<<20));
- }
- dprintf3("%s(%p, %x, %lx, ...) done\n", __func__, buf, len_bytes,
- bd_offset_bytes);
- return total_entries;
-}
-
-int proc_pid_mem_fd = -1;
-
-void *fill_bounds_dir_buf_other(long byte_offset_inside_bounds_dir,
- long buffer_size_bytes, void *buffer)
-{
- unsigned long seekto = bounds_dir_global + byte_offset_inside_bounds_dir;
- int read_ret;
- off_t seek_ret = lseek(proc_pid_mem_fd, seekto, SEEK_SET);
-
- if (seek_ret != seekto)
- mpx_dig_abort();
-
- read_ret = read(proc_pid_mem_fd, buffer, buffer_size_bytes);
- /* there shouldn't practically be short reads of /proc/$pid/mem */
- if (read_ret != buffer_size_bytes)
- mpx_dig_abort();
-
- return buffer;
-}
-void *fill_bounds_dir_buf_self(long byte_offset_inside_bounds_dir,
- long buffer_size_bytes, void *buffer)
-
-{
- unsigned char vec[buffer_size_bytes / PAGE_SIZE];
- char *dig_bounds_dir_ptr =
- (void *)(bounds_dir_global + byte_offset_inside_bounds_dir);
- /*
- * use mincore() to quickly find the areas of the bounds directory
- * that have memory and thus will be worth scanning.
- */
- int incore_ret;
-
- int incore = 0;
- int i;
-
- dprintf4("%s() dig_bounds_dir_ptr: %p\n", __func__, dig_bounds_dir_ptr);
-
- incore_ret = mincore(dig_bounds_dir_ptr, buffer_size_bytes, &vec[0]);
- if (incore_ret) {
- printf("mincore ret: %d\n", incore_ret);
- perror("mincore");
- mpx_dig_abort();
- }
- for (i = 0; i < sizeof(vec); i++)
- incore += vec[i];
- dprintf4("%s() total incore: %d\n", __func__, incore);
- if (!incore)
- return NULL;
- dprintf3("%s() total incore: %d\n", __func__, incore);
- return dig_bounds_dir_ptr;
-}
-
-int inspect_pid(int pid)
-{
- static int dig_nr;
- long offset_inside_bounds_dir;
- char bounds_dir_buf[sizeof(unsigned long) * (1UL << 15)];
- char *dig_bounds_dir_ptr;
- int total_entries = 0;
- int nr_populated_bdes = 0;
- int inspect_self;
-
- if (getpid() == pid) {
- dprintf4("inspecting self\n");
- inspect_self = 1;
- } else {
- dprintf4("inspecting pid %d\n", pid);
- mpx_dig_abort();
- }
-
- for (offset_inside_bounds_dir = 0;
- offset_inside_bounds_dir < MPX_BOUNDS_TABLE_SIZE_BYTES;
- offset_inside_bounds_dir += sizeof(bounds_dir_buf)) {
- static int bufs_skipped;
- int this_entries;
-
- if (inspect_self) {
- dig_bounds_dir_ptr =
- fill_bounds_dir_buf_self(offset_inside_bounds_dir,
- sizeof(bounds_dir_buf),
- &bounds_dir_buf[0]);
- } else {
- dig_bounds_dir_ptr =
- fill_bounds_dir_buf_other(offset_inside_bounds_dir,
- sizeof(bounds_dir_buf),
- &bounds_dir_buf[0]);
- }
- if (!dig_bounds_dir_ptr) {
- bufs_skipped++;
- continue;
- }
- this_entries = search_bd_buf(dig_bounds_dir_ptr,
- sizeof(bounds_dir_buf),
- offset_inside_bounds_dir,
- &nr_populated_bdes);
- total_entries += this_entries;
- }
- printf("mpx dig (%3d) complete, SUCCESS (%8d / %4d)\n", ++dig_nr,
- total_entries, nr_populated_bdes);
- return total_entries + nr_populated_bdes;
-}
-
-#ifdef MPX_DIG_REMOTE
-int main(int argc, char **argv)
-{
- int err;
- char *c;
- unsigned long bounds_dir_entry;
- int pid;
-
- printf("mpx-dig starting...\n");
- err = sscanf(argv[1], "%d", &pid);
- printf("parsing: '%s', err: %d\n", argv[1], err);
- if (err != 1)
- mpx_dig_abort();
-
- err = sscanf(argv[2], "%lx", &bounds_dir_global);
- printf("parsing: '%s': %d\n", argv[2], err);
- if (err != 1)
- mpx_dig_abort();
-
- proc_pid_mem_fd = open_proc(pid, "mem");
- if (proc_pid_mem_fd < 0)
- mpx_dig_abort();
-
- inspect_pid(pid);
- return 0;
-}
-#endif
-
-long inspect_me(struct mpx_bounds_dir *bounds_dir)
-{
- int pid = getpid();
-
- pid_load_vaddrs(pid);
- bounds_dir_global = (unsigned long)bounds_dir;
- dprintf4("enter %s() bounds dir: %p\n", __func__, bounds_dir);
- return inspect_pid(pid);
-}
diff --git a/tools/testing/selftests/x86/mpx-hw.h b/tools/testing/selftests/x86/mpx-hw.h
deleted file mode 100644
index d1b61ab870f8..000000000000
--- a/tools/testing/selftests/x86/mpx-hw.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _MPX_HW_H
-#define _MPX_HW_H
-
-#include <assert.h>
-
-/* Describe the MPX Hardware Layout in here */
-
-#define NR_MPX_BOUNDS_REGISTERS 4
-
-#ifdef __i386__
-
-#define MPX_BOUNDS_TABLE_ENTRY_SIZE_BYTES 16 /* 4 * 32-bits */
-#define MPX_BOUNDS_TABLE_SIZE_BYTES (1ULL << 14) /* 16k */
-#define MPX_BOUNDS_DIR_ENTRY_SIZE_BYTES 4
-#define MPX_BOUNDS_DIR_SIZE_BYTES (1ULL << 22) /* 4MB */
-
-#define MPX_BOUNDS_TABLE_BOTTOM_BIT 2
-#define MPX_BOUNDS_TABLE_TOP_BIT 11
-#define MPX_BOUNDS_DIR_BOTTOM_BIT 12
-#define MPX_BOUNDS_DIR_TOP_BIT 31
-
-#else
-
-/*
- * Linear Address of "pointer" (LAp)
- * 0 -> 2: ignored
- * 3 -> 19: index in to bounds table
- * 20 -> 47: index in to bounds directory
- * 48 -> 63: ignored
- */
-
-#define MPX_BOUNDS_TABLE_ENTRY_SIZE_BYTES 32
-#define MPX_BOUNDS_TABLE_SIZE_BYTES (1ULL << 22) /* 4MB */
-#define MPX_BOUNDS_DIR_ENTRY_SIZE_BYTES 8
-#define MPX_BOUNDS_DIR_SIZE_BYTES (1ULL << 31) /* 2GB */
-
-#define MPX_BOUNDS_TABLE_BOTTOM_BIT 3
-#define MPX_BOUNDS_TABLE_TOP_BIT 19
-#define MPX_BOUNDS_DIR_BOTTOM_BIT 20
-#define MPX_BOUNDS_DIR_TOP_BIT 47
-
-#endif
-
-#define MPX_BOUNDS_DIR_NR_ENTRIES \
- (MPX_BOUNDS_DIR_SIZE_BYTES/MPX_BOUNDS_DIR_ENTRY_SIZE_BYTES)
-#define MPX_BOUNDS_TABLE_NR_ENTRIES \
- (MPX_BOUNDS_TABLE_SIZE_BYTES/MPX_BOUNDS_TABLE_ENTRY_SIZE_BYTES)
-
-#define MPX_BOUNDS_TABLE_ENTRY_VALID_BIT 0x1
-
-struct mpx_bd_entry {
- union {
- char x[MPX_BOUNDS_DIR_ENTRY_SIZE_BYTES];
- void *contents[0];
- };
-} __attribute__((packed));
-
-struct mpx_bt_entry {
- union {
- char x[MPX_BOUNDS_TABLE_ENTRY_SIZE_BYTES];
- unsigned long contents[0];
- };
-} __attribute__((packed));
-
-struct mpx_bounds_dir {
- struct mpx_bd_entry entries[MPX_BOUNDS_DIR_NR_ENTRIES];
-} __attribute__((packed));
-
-struct mpx_bounds_table {
- struct mpx_bt_entry entries[MPX_BOUNDS_TABLE_NR_ENTRIES];
-} __attribute__((packed));
-
-static inline unsigned long GET_BITS(unsigned long val, int bottombit, int topbit)
-{
- int total_nr_bits = topbit - bottombit;
- unsigned long mask = (1UL << total_nr_bits)-1;
- return (val >> bottombit) & mask;
-}
-
-static inline unsigned long __vaddr_bounds_table_index(void *vaddr)
-{
- return GET_BITS((unsigned long)vaddr, MPX_BOUNDS_TABLE_BOTTOM_BIT,
- MPX_BOUNDS_TABLE_TOP_BIT);
-}
-
-static inline unsigned long __vaddr_bounds_directory_index(void *vaddr)
-{
- return GET_BITS((unsigned long)vaddr, MPX_BOUNDS_DIR_BOTTOM_BIT,
- MPX_BOUNDS_DIR_TOP_BIT);
-}
-
-static inline struct mpx_bd_entry *mpx_vaddr_to_bd_entry(void *vaddr,
- struct mpx_bounds_dir *bounds_dir)
-{
- unsigned long index = __vaddr_bounds_directory_index(vaddr);
- return &bounds_dir->entries[index];
-}
-
-static inline int bd_entry_valid(struct mpx_bd_entry *bounds_dir_entry)
-{
- unsigned long __bd_entry = (unsigned long)bounds_dir_entry->contents;
- return (__bd_entry & MPX_BOUNDS_TABLE_ENTRY_VALID_BIT);
-}
-
-static inline struct mpx_bounds_table *
-__bd_entry_to_bounds_table(struct mpx_bd_entry *bounds_dir_entry)
-{
- unsigned long __bd_entry = (unsigned long)bounds_dir_entry->contents;
- assert(__bd_entry & MPX_BOUNDS_TABLE_ENTRY_VALID_BIT);
- __bd_entry &= ~MPX_BOUNDS_TABLE_ENTRY_VALID_BIT;
- return (struct mpx_bounds_table *)__bd_entry;
-}
-
-static inline struct mpx_bt_entry *
-mpx_vaddr_to_bt_entry(void *vaddr, struct mpx_bounds_dir *bounds_dir)
-{
- struct mpx_bd_entry *bde = mpx_vaddr_to_bd_entry(vaddr, bounds_dir);
- struct mpx_bounds_table *bt = __bd_entry_to_bounds_table(bde);
- unsigned long index = __vaddr_bounds_table_index(vaddr);
- return &bt->entries[index];
-}
-
-#endif /* _MPX_HW_H */
diff --git a/tools/testing/selftests/x86/mpx-mini-test.c b/tools/testing/selftests/x86/mpx-mini-test.c
deleted file mode 100644
index bf1bb15b6fbe..000000000000
--- a/tools/testing/selftests/x86/mpx-mini-test.c
+++ /dev/null
@@ -1,1616 +0,0 @@
-/*
- * mpx-mini-test.c: routines to test Intel MPX (Memory Protection eXtentions)
- *
- * Written by:
- * "Ren, Qiaowei" <qiaowei.ren@intel.com>
- * "Wei, Gang" <gang.wei@intel.com>
- * "Hansen, Dave" <dave.hansen@intel.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2.
- */
-
-/*
- * 2014-12-05: Dave Hansen: fixed all of the compiler warnings, and made sure
- * it works on 32-bit.
- */
-
-int inspect_every_this_many_mallocs = 100;
-int zap_all_every_this_many_mallocs = 1000;
-
-#define _GNU_SOURCE
-#define _LARGEFILE64_SOURCE
-
-#include <string.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <signal.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <ucontext.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "mpx-hw.h"
-#include "mpx-debug.h"
-#include "mpx-mm.h"
-
-#ifndef __always_inline
-#define __always_inline inline __attribute__((always_inline)
-#endif
-
-#ifndef TEST_DURATION_SECS
-#define TEST_DURATION_SECS 3
-#endif
-
-void write_int_to(char *prefix, char *file, int int_to_write)
-{
- char buf[100];
- int fd = open(file, O_RDWR);
- int len;
- int ret;
-
- assert(fd >= 0);
- len = snprintf(buf, sizeof(buf), "%s%d", prefix, int_to_write);
- assert(len >= 0);
- assert(len < sizeof(buf));
- ret = write(fd, buf, len);
- assert(ret == len);
- ret = close(fd);
- assert(!ret);
-}
-
-void write_pid_to(char *prefix, char *file)
-{
- write_int_to(prefix, file, getpid());
-}
-
-void trace_me(void)
-{
-/* tracing events dir */
-#define TED "/sys/kernel/debug/tracing/events/"
-/*
- write_pid_to("common_pid=", TED "signal/filter");
- write_pid_to("common_pid=", TED "exceptions/filter");
- write_int_to("", TED "signal/enable", 1);
- write_int_to("", TED "exceptions/enable", 1);
-*/
- write_pid_to("", "/sys/kernel/debug/tracing/set_ftrace_pid");
- write_int_to("", "/sys/kernel/debug/tracing/trace", 0);
-}
-
-#define test_failed() __test_failed(__FILE__, __LINE__)
-static void __test_failed(char *f, int l)
-{
- fprintf(stderr, "abort @ %s::%d\n", f, l);
- abort();
-}
-
-/* Error Printf */
-#define eprintf(args...) fprintf(stderr, args)
-
-#ifdef __i386__
-
-/* i386 directory size is 4MB */
-#define REG_IP_IDX REG_EIP
-#define REX_PREFIX
-
-#define XSAVE_OFFSET_IN_FPMEM sizeof(struct _libc_fpstate)
-
-/*
- * __cpuid() is from the Linux Kernel:
- */
-static inline void __cpuid(unsigned int *eax, unsigned int *ebx,
- unsigned int *ecx, unsigned int *edx)
-{
- /* ecx is often an input as well as an output. */
- asm volatile(
- "push %%ebx;"
- "cpuid;"
- "mov %%ebx, %1;"
- "pop %%ebx"
- : "=a" (*eax),
- "=g" (*ebx),
- "=c" (*ecx),
- "=d" (*edx)
- : "0" (*eax), "2" (*ecx));
-}
-
-#else /* __i386__ */
-
-#define REG_IP_IDX REG_RIP
-#define REX_PREFIX "0x48, "
-
-#define XSAVE_OFFSET_IN_FPMEM 0
-
-/*
- * __cpuid() is from the Linux Kernel:
- */
-static inline void __cpuid(unsigned int *eax, unsigned int *ebx,
- unsigned int *ecx, unsigned int *edx)
-{
- /* ecx is often an input as well as an output. */
- asm volatile(
- "cpuid;"
- : "=a" (*eax),
- "=b" (*ebx),
- "=c" (*ecx),
- "=d" (*edx)
- : "0" (*eax), "2" (*ecx));
-}
-
-#endif /* !__i386__ */
-
-struct xsave_hdr_struct {
- uint64_t xstate_bv;
- uint64_t reserved1[2];
- uint64_t reserved2[5];
-} __attribute__((packed));
-
-struct bndregs_struct {
- uint64_t bndregs[8];
-} __attribute__((packed));
-
-struct bndcsr_struct {
- uint64_t cfg_reg_u;
- uint64_t status_reg;
-} __attribute__((packed));
-
-struct xsave_struct {
- uint8_t fpu_sse[512];
- struct xsave_hdr_struct xsave_hdr;
- uint8_t ymm[256];
- uint8_t lwp[128];
- struct bndregs_struct bndregs;
- struct bndcsr_struct bndcsr;
-} __attribute__((packed));
-
-uint8_t __attribute__((__aligned__(64))) buffer[4096];
-struct xsave_struct *xsave_buf = (struct xsave_struct *)buffer;
-
-uint8_t __attribute__((__aligned__(64))) test_buffer[4096];
-struct xsave_struct *xsave_test_buf = (struct xsave_struct *)test_buffer;
-
-uint64_t num_bnd_chk;
-
-static __always_inline void xrstor_state(struct xsave_struct *fx, uint64_t mask)
-{
- uint32_t lmask = mask;
- uint32_t hmask = mask >> 32;
-
- asm volatile(".byte " REX_PREFIX "0x0f,0xae,0x2f\n\t"
- : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
- : "memory");
-}
-
-static __always_inline void xsave_state_1(void *_fx, uint64_t mask)
-{
- uint32_t lmask = mask;
- uint32_t hmask = mask >> 32;
- unsigned char *fx = _fx;
-
- asm volatile(".byte " REX_PREFIX "0x0f,0xae,0x27\n\t"
- : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
- : "memory");
-}
-
-static inline uint64_t xgetbv(uint32_t index)
-{
- uint32_t eax, edx;
-
- asm volatile(".byte 0x0f,0x01,0xd0" /* xgetbv */
- : "=a" (eax), "=d" (edx)
- : "c" (index));
- return eax + ((uint64_t)edx << 32);
-}
-
-static uint64_t read_mpx_status_sig(ucontext_t *uctxt)
-{
- memset(buffer, 0, sizeof(buffer));
- memcpy(buffer,
- (uint8_t *)uctxt->uc_mcontext.fpregs + XSAVE_OFFSET_IN_FPMEM,
- sizeof(struct xsave_struct));
-
- return xsave_buf->bndcsr.status_reg;
-}
-
-#include <pthread.h>
-
-static uint8_t *get_next_inst_ip(uint8_t *addr)
-{
- uint8_t *ip = addr;
- uint8_t sib;
- uint8_t rm;
- uint8_t mod;
- uint8_t base;
- uint8_t modrm;
-
- /* determine the prefix. */
- switch(*ip) {
- case 0xf2:
- case 0xf3:
- case 0x66:
- ip++;
- break;
- }
-
- /* look for rex prefix */
- if ((*ip & 0x40) == 0x40)
- ip++;
-
- /* Make sure we have a MPX instruction. */
- if (*ip++ != 0x0f)
- return addr;
-
- /* Skip the op code byte. */
- ip++;
-
- /* Get the modrm byte. */
- modrm = *ip++;
-
- /* Break it down into parts. */
- rm = modrm & 7;
- mod = (modrm >> 6);
-
- /* Init the parts of the address mode. */
- base = 8;
-
- /* Is it a mem mode? */
- if (mod != 3) {
- /* look for scaled indexed addressing */
- if (rm == 4) {
- /* SIB addressing */
- sib = *ip++;
- base = sib & 7;
- switch (mod) {
- case 0:
- if (base == 5)
- ip += 4;
- break;
-
- case 1:
- ip++;
- break;
-
- case 2:
- ip += 4;
- break;
- }
-
- } else {
- /* MODRM addressing */
- switch (mod) {
- case 0:
- /* DISP32 addressing, no base */
- if (rm == 5)
- ip += 4;
- break;
-
- case 1:
- ip++;
- break;
-
- case 2:
- ip += 4;
- break;
- }
- }
- }
- return ip;
-}
-
-#ifdef si_lower
-static inline void *__si_bounds_lower(siginfo_t *si)
-{
- return si->si_lower;
-}
-
-static inline void *__si_bounds_upper(siginfo_t *si)
-{
- return si->si_upper;
-}
-#else
-
-/*
- * This deals with old version of _sigfault in some distros:
- *
-
-old _sigfault:
- struct {
- void *si_addr;
- } _sigfault;
-
-new _sigfault:
- struct {
- void __user *_addr;
- int _trapno;
- short _addr_lsb;
- union {
- struct {
- void __user *_lower;
- void __user *_upper;
- } _addr_bnd;
- __u32 _pkey;
- };
- } _sigfault;
- *
- */
-
-static inline void **__si_bounds_hack(siginfo_t *si)
-{
- void *sigfault = &si->_sifields._sigfault;
- void *end_sigfault = sigfault + sizeof(si->_sifields._sigfault);
- int *trapno = (int*)end_sigfault;
- /* skip _trapno and _addr_lsb */
- void **__si_lower = (void**)(trapno + 2);
-
- return __si_lower;
-}
-
-static inline void *__si_bounds_lower(siginfo_t *si)
-{
- return *__si_bounds_hack(si);
-}
-
-static inline void *__si_bounds_upper(siginfo_t *si)
-{
- return *(__si_bounds_hack(si) + 1);
-}
-#endif
-
-static int br_count;
-static int expected_bnd_index = -1;
-uint64_t shadow_plb[NR_MPX_BOUNDS_REGISTERS][2]; /* shadow MPX bound registers */
-unsigned long shadow_map[NR_MPX_BOUNDS_REGISTERS];
-
-/* Failed address bound checks: */
-#ifndef SEGV_BNDERR
-# define SEGV_BNDERR 3
-#endif
-
-/*
- * The kernel is supposed to provide some information about the bounds
- * exception in the siginfo. It should match what we have in the bounds
- * registers that we are checking against. Just check against the shadow copy
- * since it is easily available, and we also check that *it* matches the real
- * registers.
- */
-void check_siginfo_vs_shadow(siginfo_t* si)
-{
- int siginfo_ok = 1;
- void *shadow_lower = (void *)(unsigned long)shadow_plb[expected_bnd_index][0];
- void *shadow_upper = (void *)(unsigned long)shadow_plb[expected_bnd_index][1];
-
- if ((expected_bnd_index < 0) ||
- (expected_bnd_index >= NR_MPX_BOUNDS_REGISTERS)) {
- fprintf(stderr, "ERROR: invalid expected_bnd_index: %d\n",
- expected_bnd_index);
- exit(6);
- }
- if (__si_bounds_lower(si) != shadow_lower)
- siginfo_ok = 0;
- if (__si_bounds_upper(si) != shadow_upper)
- siginfo_ok = 0;
-
- if (!siginfo_ok) {
- fprintf(stderr, "ERROR: siginfo bounds do not match "
- "shadow bounds for register %d\n", expected_bnd_index);
- exit(7);
- }
-}
-
-void handler(int signum, siginfo_t *si, void *vucontext)
-{
- int i;
- ucontext_t *uctxt = vucontext;
- int trapno;
- unsigned long ip;
-
- dprintf1("entered signal handler\n");
-
- trapno = uctxt->uc_mcontext.gregs[REG_TRAPNO];
- ip = uctxt->uc_mcontext.gregs[REG_IP_IDX];
-
- if (trapno == 5) {
- typeof(si->si_addr) *si_addr_ptr = &si->si_addr;
- uint64_t status = read_mpx_status_sig(uctxt);
- uint64_t br_reason = status & 0x3;
-
- br_count++;
- dprintf1("#BR 0x%jx (total seen: %d)\n", status, br_count);
-
- dprintf2("Saw a #BR! status 0x%jx at %016lx br_reason: %jx\n",
- status, ip, br_reason);
- dprintf2("si_signo: %d\n", si->si_signo);
- dprintf2(" signum: %d\n", signum);
- dprintf2("info->si_code == SEGV_BNDERR: %d\n",
- (si->si_code == SEGV_BNDERR));
- dprintf2("info->si_code: %d\n", si->si_code);
- dprintf2("info->si_lower: %p\n", __si_bounds_lower(si));
- dprintf2("info->si_upper: %p\n", __si_bounds_upper(si));
-
- for (i = 0; i < 8; i++)
- dprintf3("[%d]: %p\n", i, si_addr_ptr[i]);
- switch (br_reason) {
- case 0: /* traditional BR */
- fprintf(stderr,
- "Undefined status with bound exception:%jx\n",
- status);
- exit(5);
- case 1: /* #BR MPX bounds exception */
- /* these are normal and we expect to see them */
-
- check_siginfo_vs_shadow(si);
-
- dprintf1("bounds exception (normal): status 0x%jx at %p si_addr: %p\n",
- status, (void *)ip, si->si_addr);
- num_bnd_chk++;
- uctxt->uc_mcontext.gregs[REG_IP_IDX] =
- (greg_t)get_next_inst_ip((uint8_t *)ip);
- break;
- case 2:
- fprintf(stderr, "#BR status == 2, missing bounds table,"
- "kernel should have handled!!\n");
- exit(4);
- break;
- default:
- fprintf(stderr, "bound check error: status 0x%jx at %p\n",
- status, (void *)ip);
- num_bnd_chk++;
- uctxt->uc_mcontext.gregs[REG_IP_IDX] =
- (greg_t)get_next_inst_ip((uint8_t *)ip);
- fprintf(stderr, "bound check error: si_addr %p\n", si->si_addr);
- exit(3);
- }
- } else if (trapno == 14) {
- eprintf("ERROR: In signal handler, page fault, trapno = %d, ip = %016lx\n",
- trapno, ip);
- eprintf("si_addr %p\n", si->si_addr);
- eprintf("REG_ERR: %lx\n", (unsigned long)uctxt->uc_mcontext.gregs[REG_ERR]);
- test_failed();
- } else {
- eprintf("unexpected trap %d! at 0x%lx\n", trapno, ip);
- eprintf("si_addr %p\n", si->si_addr);
- eprintf("REG_ERR: %lx\n", (unsigned long)uctxt->uc_mcontext.gregs[REG_ERR]);
- test_failed();
- }
-}
-
-static inline void cpuid_count(unsigned int op, int count,
- unsigned int *eax, unsigned int *ebx,
- unsigned int *ecx, unsigned int *edx)
-{
- *eax = op;
- *ecx = count;
- __cpuid(eax, ebx, ecx, edx);
-}
-
-#define XSTATE_CPUID 0x0000000d
-
-/*
- * List of XSAVE features Linux knows about:
- */
-enum xfeature_bit {
- XSTATE_BIT_FP,
- XSTATE_BIT_SSE,
- XSTATE_BIT_YMM,
- XSTATE_BIT_BNDREGS,
- XSTATE_BIT_BNDCSR,
- XSTATE_BIT_OPMASK,
- XSTATE_BIT_ZMM_Hi256,
- XSTATE_BIT_Hi16_ZMM,
-
- XFEATURES_NR_MAX,
-};
-
-#define XSTATE_FP (1 << XSTATE_BIT_FP)
-#define XSTATE_SSE (1 << XSTATE_BIT_SSE)
-#define XSTATE_YMM (1 << XSTATE_BIT_YMM)
-#define XSTATE_BNDREGS (1 << XSTATE_BIT_BNDREGS)
-#define XSTATE_BNDCSR (1 << XSTATE_BIT_BNDCSR)
-#define XSTATE_OPMASK (1 << XSTATE_BIT_OPMASK)
-#define XSTATE_ZMM_Hi256 (1 << XSTATE_BIT_ZMM_Hi256)
-#define XSTATE_Hi16_ZMM (1 << XSTATE_BIT_Hi16_ZMM)
-
-#define MPX_XSTATES (XSTATE_BNDREGS | XSTATE_BNDCSR) /* 0x18 */
-
-bool one_bit(unsigned int x, int bit)
-{
- return !!(x & (1<<bit));
-}
-
-void print_state_component(int state_bit_nr, char *name)
-{
- unsigned int eax, ebx, ecx, edx;
- unsigned int state_component_size;
- unsigned int state_component_supervisor;
- unsigned int state_component_user;
- unsigned int state_component_aligned;
-
- /* See SDM Section 13.2 */
- cpuid_count(XSTATE_CPUID, state_bit_nr, &eax, &ebx, &ecx, &edx);
- assert(eax || ebx || ecx);
- state_component_size = eax;
- state_component_supervisor = ((!ebx) && one_bit(ecx, 0));
- state_component_user = !one_bit(ecx, 0);
- state_component_aligned = one_bit(ecx, 1);
- printf("%8s: size: %d user: %d supervisor: %d aligned: %d\n",
- name,
- state_component_size, state_component_user,
- state_component_supervisor, state_component_aligned);
-
-}
-
-/* Intel-defined CPU features, CPUID level 0x00000001 (ecx) */
-#define XSAVE_FEATURE_BIT (26) /* XSAVE/XRSTOR/XSETBV/XGETBV */
-#define OSXSAVE_FEATURE_BIT (27) /* XSAVE enabled in the OS */
-
-bool check_mpx_support(void)
-{
- unsigned int eax, ebx, ecx, edx;
-
- cpuid_count(1, 0, &eax, &ebx, &ecx, &edx);
-
- /* We can't do much without XSAVE, so just make these assert()'s */
- if (!one_bit(ecx, XSAVE_FEATURE_BIT)) {
- fprintf(stderr, "processor lacks XSAVE, can not run MPX tests\n");
- exit(0);
- }
-
- if (!one_bit(ecx, OSXSAVE_FEATURE_BIT)) {
- fprintf(stderr, "processor lacks OSXSAVE, can not run MPX tests\n");
- exit(0);
- }
-
- /* CPUs not supporting the XSTATE CPUID leaf do not support MPX */
- /* Is this redundant with the feature bit checks? */
- cpuid_count(0, 0, &eax, &ebx, &ecx, &edx);
- if (eax < XSTATE_CPUID) {
- fprintf(stderr, "processor lacks XSTATE CPUID leaf,"
- " can not run MPX tests\n");
- exit(0);
- }
-
- printf("XSAVE is supported by HW & OS\n");
-
- cpuid_count(XSTATE_CPUID, 0, &eax, &ebx, &ecx, &edx);
-
- printf("XSAVE processor supported state mask: 0x%x\n", eax);
- printf("XSAVE OS supported state mask: 0x%jx\n", xgetbv(0));
-
- /* Make sure that the MPX states are enabled in in XCR0 */
- if ((eax & MPX_XSTATES) != MPX_XSTATES) {
- fprintf(stderr, "processor lacks MPX XSTATE(s), can not run MPX tests\n");
- exit(0);
- }
-
- /* Make sure the MPX states are supported by XSAVE* */
- if ((xgetbv(0) & MPX_XSTATES) != MPX_XSTATES) {
- fprintf(stderr, "MPX XSTATE(s) no enabled in XCR0, "
- "can not run MPX tests\n");
- exit(0);
- }
-
- print_state_component(XSTATE_BIT_BNDREGS, "BNDREGS");
- print_state_component(XSTATE_BIT_BNDCSR, "BNDCSR");
-
- return true;
-}
-
-void enable_mpx(void *l1base)
-{
- /* enable point lookup */
- memset(buffer, 0, sizeof(buffer));
- xrstor_state(xsave_buf, 0x18);
-
- xsave_buf->xsave_hdr.xstate_bv = 0x10;
- xsave_buf->bndcsr.cfg_reg_u = (unsigned long)l1base | 1;
- xsave_buf->bndcsr.status_reg = 0;
-
- dprintf2("bf xrstor\n");
- dprintf2("xsave cndcsr: status %jx, configu %jx\n",
- xsave_buf->bndcsr.status_reg, xsave_buf->bndcsr.cfg_reg_u);
- xrstor_state(xsave_buf, 0x18);
- dprintf2("after xrstor\n");
-
- xsave_state_1(xsave_buf, 0x18);
-
- dprintf1("xsave bndcsr: status %jx, configu %jx\n",
- xsave_buf->bndcsr.status_reg, xsave_buf->bndcsr.cfg_reg_u);
-}
-
-#include <sys/prctl.h>
-
-struct mpx_bounds_dir *bounds_dir_ptr;
-
-unsigned long __bd_incore(const char *func, int line)
-{
- unsigned long ret = nr_incore(bounds_dir_ptr, MPX_BOUNDS_DIR_SIZE_BYTES);
- return ret;
-}
-#define bd_incore() __bd_incore(__func__, __LINE__)
-
-void check_clear(void *ptr, unsigned long sz)
-{
- unsigned long *i;
-
- for (i = ptr; (void *)i < ptr + sz; i++) {
- if (*i) {
- dprintf1("%p is NOT clear at %p\n", ptr, i);
- assert(0);
- }
- }
- dprintf1("%p is clear for %lx\n", ptr, sz);
-}
-
-void check_clear_bd(void)
-{
- check_clear(bounds_dir_ptr, 2UL << 30);
-}
-
-#define USE_MALLOC_FOR_BOUNDS_DIR 1
-bool process_specific_init(void)
-{
- unsigned long size;
- unsigned long *dir;
- /* Guarantee we have the space to align it, add padding: */
- unsigned long pad = getpagesize();
-
- size = 2UL << 30; /* 2GB */
- if (sizeof(unsigned long) == 4)
- size = 4UL << 20; /* 4MB */
- dprintf1("trying to allocate %ld MB bounds directory\n", (size >> 20));
-
- if (USE_MALLOC_FOR_BOUNDS_DIR) {
- unsigned long _dir;
-
- dir = malloc(size + pad);
- assert(dir);
- _dir = (unsigned long)dir;
- _dir += 0xfffUL;
- _dir &= ~0xfffUL;
- dir = (void *)_dir;
- } else {
- /*
- * This makes debugging easier because the address
- * calculations are simpler:
- */
- dir = mmap((void *)0x200000000000, size + pad,
- PROT_READ|PROT_WRITE,
- MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
- if (dir == (void *)-1) {
- perror("unable to allocate bounds directory");
- abort();
- }
- check_clear(dir, size);
- }
- bounds_dir_ptr = (void *)dir;
- madvise(bounds_dir_ptr, size, MADV_NOHUGEPAGE);
- bd_incore();
- dprintf1("bounds directory: 0x%p -> 0x%p\n", bounds_dir_ptr,
- (char *)bounds_dir_ptr + size);
- check_clear(dir, size);
- enable_mpx(dir);
- check_clear(dir, size);
- if (prctl(43, 0, 0, 0, 0)) {
- printf("no MPX support\n");
- abort();
- return false;
- }
- return true;
-}
-
-bool process_specific_finish(void)
-{
- if (prctl(44)) {
- printf("no MPX support\n");
- return false;
- }
- return true;
-}
-
-void setup_handler()
-{
- int r, rs;
- struct sigaction newact;
- struct sigaction oldact;
-
- /* #BR is mapped to sigsegv */
- int signum = SIGSEGV;
-
- newact.sa_handler = 0; /* void(*)(int)*/
- newact.sa_sigaction = handler; /* void (*)(int, siginfo_t*, void *) */
-
- /*sigset_t - signals to block while in the handler */
- /* get the old signal mask. */
- rs = sigprocmask(SIG_SETMASK, 0, &newact.sa_mask);
- assert(rs == 0);
-
- /* call sa_sigaction, not sa_handler*/
- newact.sa_flags = SA_SIGINFO;
-
- newact.sa_restorer = 0; /* void(*)(), obsolete */
- r = sigaction(signum, &newact, &oldact);
- assert(r == 0);
-}
-
-void mpx_prepare(void)
-{
- dprintf2("%s()\n", __func__);
- setup_handler();
- process_specific_init();
-}
-
-void mpx_cleanup(void)
-{
- printf("%s(): %jd BRs. bye...\n", __func__, num_bnd_chk);
- process_specific_finish();
-}
-
-/*-------------- the following is test case ---------------*/
-#include <stdint.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <time.h>
-
-uint64_t num_lower_brs;
-uint64_t num_upper_brs;
-
-#define MPX_CONFIG_OFFSET 1024
-#define MPX_BOUNDS_OFFSET 960
-#define MPX_HEADER_OFFSET 512
-#define MAX_ADDR_TESTED (1<<28)
-#define TEST_ROUNDS 100
-
-/*
- 0F 1A /r BNDLDX-Load
- 0F 1B /r BNDSTX-Store Extended Bounds Using Address Translation
- 66 0F 1A /r BNDMOV bnd1, bnd2/m128
- 66 0F 1B /r BNDMOV bnd1/m128, bnd2
- F2 0F 1A /r BNDCU bnd, r/m64
- F2 0F 1B /r BNDCN bnd, r/m64
- F3 0F 1A /r BNDCL bnd, r/m64
- F3 0F 1B /r BNDMK bnd, m64
-*/
-
-static __always_inline void xsave_state(void *_fx, uint64_t mask)
-{
- uint32_t lmask = mask;
- uint32_t hmask = mask >> 32;
- unsigned char *fx = _fx;
-
- asm volatile(".byte " REX_PREFIX "0x0f,0xae,0x27\n\t"
- : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
- : "memory");
-}
-
-static __always_inline void mpx_clear_bnd0(void)
-{
- long size = 0;
- void *ptr = NULL;
- /* F3 0F 1B /r BNDMK bnd, m64 */
- /* f3 0f 1b 04 11 bndmk (%rcx,%rdx,1),%bnd0 */
- asm volatile(".byte 0xf3,0x0f,0x1b,0x04,0x11\n\t"
- : : "c" (ptr), "d" (size-1)
- : "memory");
-}
-
-static __always_inline void mpx_make_bound_helper(unsigned long ptr,
- unsigned long size)
-{
- /* F3 0F 1B /r BNDMK bnd, m64 */
- /* f3 0f 1b 04 11 bndmk (%rcx,%rdx,1),%bnd0 */
- asm volatile(".byte 0xf3,0x0f,0x1b,0x04,0x11\n\t"
- : : "c" (ptr), "d" (size-1)
- : "memory");
-}
-
-static __always_inline void mpx_check_lowerbound_helper(unsigned long ptr)
-{
- /* F3 0F 1A /r NDCL bnd, r/m64 */
- /* f3 0f 1a 01 bndcl (%rcx),%bnd0 */
- asm volatile(".byte 0xf3,0x0f,0x1a,0x01\n\t"
- : : "c" (ptr)
- : "memory");
-}
-
-static __always_inline void mpx_check_upperbound_helper(unsigned long ptr)
-{
- /* F2 0F 1A /r BNDCU bnd, r/m64 */
- /* f2 0f 1a 01 bndcu (%rcx),%bnd0 */
- asm volatile(".byte 0xf2,0x0f,0x1a,0x01\n\t"
- : : "c" (ptr)
- : "memory");
-}
-
-static __always_inline void mpx_movbndreg_helper()
-{
- /* 66 0F 1B /r BNDMOV bnd1/m128, bnd2 */
- /* 66 0f 1b c2 bndmov %bnd0,%bnd2 */
-
- asm volatile(".byte 0x66,0x0f,0x1b,0xc2\n\t");
-}
-
-static __always_inline void mpx_movbnd2mem_helper(uint8_t *mem)
-{
- /* 66 0F 1B /r BNDMOV bnd1/m128, bnd2 */
- /* 66 0f 1b 01 bndmov %bnd0,(%rcx) */
- asm volatile(".byte 0x66,0x0f,0x1b,0x01\n\t"
- : : "c" (mem)
- : "memory");
-}
-
-static __always_inline void mpx_movbnd_from_mem_helper(uint8_t *mem)
-{
- /* 66 0F 1A /r BNDMOV bnd1, bnd2/m128 */
- /* 66 0f 1a 01 bndmov (%rcx),%bnd0 */
- asm volatile(".byte 0x66,0x0f,0x1a,0x01\n\t"
- : : "c" (mem)
- : "memory");
-}
-
-static __always_inline void mpx_store_dsc_helper(unsigned long ptr_addr,
- unsigned long ptr_val)
-{
- /* 0F 1B /r BNDSTX-Store Extended Bounds Using Address Translation */
- /* 0f 1b 04 11 bndstx %bnd0,(%rcx,%rdx,1) */
- asm volatile(".byte 0x0f,0x1b,0x04,0x11\n\t"
- : : "c" (ptr_addr), "d" (ptr_val)
- : "memory");
-}
-
-static __always_inline void mpx_load_dsc_helper(unsigned long ptr_addr,
- unsigned long ptr_val)
-{
- /* 0F 1A /r BNDLDX-Load */
- /*/ 0f 1a 04 11 bndldx (%rcx,%rdx,1),%bnd0 */
- asm volatile(".byte 0x0f,0x1a,0x04,0x11\n\t"
- : : "c" (ptr_addr), "d" (ptr_val)
- : "memory");
-}
-
-void __print_context(void *__print_xsave_buffer, int line)
-{
- uint64_t *bounds = (uint64_t *)(__print_xsave_buffer + MPX_BOUNDS_OFFSET);
- uint64_t *cfg = (uint64_t *)(__print_xsave_buffer + MPX_CONFIG_OFFSET);
-
- int i;
- eprintf("%s()::%d\n", "print_context", line);
- for (i = 0; i < 4; i++) {
- eprintf("bound[%d]: 0x%016lx 0x%016lx(0x%016lx)\n", i,
- (unsigned long)bounds[i*2],
- ~(unsigned long)bounds[i*2+1],
- (unsigned long)bounds[i*2+1]);
- }
-
- eprintf("cpcfg: %jx cpstatus: %jx\n", cfg[0], cfg[1]);
-}
-#define print_context(x) __print_context(x, __LINE__)
-#ifdef DEBUG
-#define dprint_context(x) print_context(x)
-#else
-#define dprint_context(x) do{}while(0)
-#endif
-
-void init()
-{
- int i;
-
- srand((unsigned int)time(NULL));
-
- for (i = 0; i < 4; i++) {
- shadow_plb[i][0] = 0;
- shadow_plb[i][1] = ~(unsigned long)0;
- }
-}
-
-long int __mpx_random(int line)
-{
-#ifdef NOT_SO_RANDOM
- static long fake = 722122311;
- fake += 563792075;
- return fakse;
-#else
- return random();
-#endif
-}
-#define mpx_random() __mpx_random(__LINE__)
-
-uint8_t *get_random_addr()
-{
- uint8_t*addr = (uint8_t *)(unsigned long)(rand() % MAX_ADDR_TESTED);
- return (addr - (unsigned long)addr % sizeof(uint8_t *));
-}
-
-static inline bool compare_context(void *__xsave_buffer)
-{
- uint64_t *bounds = (uint64_t *)(__xsave_buffer + MPX_BOUNDS_OFFSET);
-
- int i;
- for (i = 0; i < 4; i++) {
- dprintf3("shadow[%d]{%016lx/%016lx}\nbounds[%d]{%016lx/%016lx}\n",
- i, (unsigned long)shadow_plb[i][0], (unsigned long)shadow_plb[i][1],
- i, (unsigned long)bounds[i*2], ~(unsigned long)bounds[i*2+1]);
- if ((shadow_plb[i][0] != bounds[i*2]) ||
- (shadow_plb[i][1] != ~(unsigned long)bounds[i*2+1])) {
- eprintf("ERROR comparing shadow to real bound register %d\n", i);
- eprintf("shadow{0x%016lx/0x%016lx}\nbounds{0x%016lx/0x%016lx}\n",
- (unsigned long)shadow_plb[i][0], (unsigned long)shadow_plb[i][1],
- (unsigned long)bounds[i*2], (unsigned long)bounds[i*2+1]);
- return false;
- }
- }
-
- return true;
-}
-
-void mkbnd_shadow(uint8_t *ptr, int index, long offset)
-{
- uint64_t *lower = (uint64_t *)&(shadow_plb[index][0]);
- uint64_t *upper = (uint64_t *)&(shadow_plb[index][1]);
- *lower = (unsigned long)ptr;
- *upper = (unsigned long)ptr + offset - 1;
-}
-
-void check_lowerbound_shadow(uint8_t *ptr, int index)
-{
- uint64_t *lower = (uint64_t *)&(shadow_plb[index][0]);
- if (*lower > (uint64_t)(unsigned long)ptr)
- num_lower_brs++;
- else
- dprintf1("LowerBoundChk passed:%p\n", ptr);
-}
-
-void check_upperbound_shadow(uint8_t *ptr, int index)
-{
- uint64_t upper = *(uint64_t *)&(shadow_plb[index][1]);
- if (upper < (uint64_t)(unsigned long)ptr)
- num_upper_brs++;
- else
- dprintf1("UpperBoundChk passed:%p\n", ptr);
-}
-
-__always_inline void movbndreg_shadow(int src, int dest)
-{
- shadow_plb[dest][0] = shadow_plb[src][0];
- shadow_plb[dest][1] = shadow_plb[src][1];
-}
-
-__always_inline void movbnd2mem_shadow(int src, unsigned long *dest)
-{
- unsigned long *lower = (unsigned long *)&(shadow_plb[src][0]);
- unsigned long *upper = (unsigned long *)&(shadow_plb[src][1]);
- *dest = *lower;
- *(dest+1) = *upper;
-}
-
-__always_inline void movbnd_from_mem_shadow(unsigned long *src, int dest)
-{
- unsigned long *lower = (unsigned long *)&(shadow_plb[dest][0]);
- unsigned long *upper = (unsigned long *)&(shadow_plb[dest][1]);
- *lower = *src;
- *upper = *(src+1);
-}
-
-__always_inline void stdsc_shadow(int index, uint8_t *ptr, uint8_t *ptr_val)
-{
- shadow_map[0] = (unsigned long)shadow_plb[index][0];
- shadow_map[1] = (unsigned long)shadow_plb[index][1];
- shadow_map[2] = (unsigned long)ptr_val;
- dprintf3("%s(%d, %p, %p) set shadow map[2]: %p\n", __func__,
- index, ptr, ptr_val, ptr_val);
- /*ptr ignored */
-}
-
-void lddsc_shadow(int index, uint8_t *ptr, uint8_t *ptr_val)
-{
- uint64_t lower = shadow_map[0];
- uint64_t upper = shadow_map[1];
- uint8_t *value = (uint8_t *)shadow_map[2];
-
- if (value != ptr_val) {
- dprintf2("%s(%d, %p, %p) init shadow bounds[%d] "
- "because %p != %p\n", __func__, index, ptr,
- ptr_val, index, value, ptr_val);
- shadow_plb[index][0] = 0;
- shadow_plb[index][1] = ~(unsigned long)0;
- } else {
- shadow_plb[index][0] = lower;
- shadow_plb[index][1] = upper;
- }
- /* ptr ignored */
-}
-
-static __always_inline void mpx_test_helper0(uint8_t *buf, uint8_t *ptr)
-{
- mpx_make_bound_helper((unsigned long)ptr, 0x1800);
-}
-
-static __always_inline void mpx_test_helper0_shadow(uint8_t *buf, uint8_t *ptr)
-{
- mkbnd_shadow(ptr, 0, 0x1800);
-}
-
-static __always_inline void mpx_test_helper1(uint8_t *buf, uint8_t *ptr)
-{
- /* these are hard-coded to check bnd0 */
- expected_bnd_index = 0;
- mpx_check_lowerbound_helper((unsigned long)(ptr-1));
- mpx_check_upperbound_helper((unsigned long)(ptr+0x1800));
- /* reset this since we do not expect any more bounds exceptions */
- expected_bnd_index = -1;
-}
-
-static __always_inline void mpx_test_helper1_shadow(uint8_t *buf, uint8_t *ptr)
-{
- check_lowerbound_shadow(ptr-1, 0);
- check_upperbound_shadow(ptr+0x1800, 0);
-}
-
-static __always_inline void mpx_test_helper2(uint8_t *buf, uint8_t *ptr)
-{
- mpx_make_bound_helper((unsigned long)ptr, 0x1800);
- mpx_movbndreg_helper();
- mpx_movbnd2mem_helper(buf);
- mpx_make_bound_helper((unsigned long)(ptr+0x12), 0x1800);
-}
-
-static __always_inline void mpx_test_helper2_shadow(uint8_t *buf, uint8_t *ptr)
-{
- mkbnd_shadow(ptr, 0, 0x1800);
- movbndreg_shadow(0, 2);
- movbnd2mem_shadow(0, (unsigned long *)buf);
- mkbnd_shadow(ptr+0x12, 0, 0x1800);
-}
-
-static __always_inline void mpx_test_helper3(uint8_t *buf, uint8_t *ptr)
-{
- mpx_movbnd_from_mem_helper(buf);
-}
-
-static __always_inline void mpx_test_helper3_shadow(uint8_t *buf, uint8_t *ptr)
-{
- movbnd_from_mem_shadow((unsigned long *)buf, 0);
-}
-
-static __always_inline void mpx_test_helper4(uint8_t *buf, uint8_t *ptr)
-{
- mpx_store_dsc_helper((unsigned long)buf, (unsigned long)ptr);
- mpx_make_bound_helper((unsigned long)(ptr+0x12), 0x1800);
-}
-
-static __always_inline void mpx_test_helper4_shadow(uint8_t *buf, uint8_t *ptr)
-{
- stdsc_shadow(0, buf, ptr);
- mkbnd_shadow(ptr+0x12, 0, 0x1800);
-}
-
-static __always_inline void mpx_test_helper5(uint8_t *buf, uint8_t *ptr)
-{
- mpx_load_dsc_helper((unsigned long)buf, (unsigned long)ptr);
-}
-
-static __always_inline void mpx_test_helper5_shadow(uint8_t *buf, uint8_t *ptr)
-{
- lddsc_shadow(0, buf, ptr);
-}
-
-#define NR_MPX_TEST_FUNCTIONS 6
-
-/*
- * For compatibility reasons, MPX will clear the bounds registers
- * when you make function calls (among other things). We have to
- * preserve the registers in between calls to the "helpers" since
- * they build on each other.
- *
- * Be very careful not to make any function calls inside the
- * helpers, or anywhere else beween the xrstor and xsave.
- */
-#define run_helper(helper_nr, buf, buf_shadow, ptr) do { \
- xrstor_state(xsave_test_buf, flags); \
- mpx_test_helper##helper_nr(buf, ptr); \
- xsave_state(xsave_test_buf, flags); \
- mpx_test_helper##helper_nr##_shadow(buf_shadow, ptr); \
-} while (0)
-
-static void run_helpers(int nr, uint8_t *buf, uint8_t *buf_shadow, uint8_t *ptr)
-{
- uint64_t flags = 0x18;
-
- dprint_context(xsave_test_buf);
- switch (nr) {
- case 0:
- run_helper(0, buf, buf_shadow, ptr);
- break;
- case 1:
- run_helper(1, buf, buf_shadow, ptr);
- break;
- case 2:
- run_helper(2, buf, buf_shadow, ptr);
- break;
- case 3:
- run_helper(3, buf, buf_shadow, ptr);
- break;
- case 4:
- run_helper(4, buf, buf_shadow, ptr);
- break;
- case 5:
- run_helper(5, buf, buf_shadow, ptr);
- break;
- default:
- test_failed();
- break;
- }
- dprint_context(xsave_test_buf);
-}
-
-unsigned long buf_shadow[1024]; /* used to check load / store descriptors */
-extern long inspect_me(struct mpx_bounds_dir *bounds_dir);
-
-long cover_buf_with_bt_entries(void *buf, long buf_len)
-{
- int i;
- long nr_to_fill;
- int ratio = 1000;
- unsigned long buf_len_in_ptrs;
-
- /* Fill about 1/100 of the space with bt entries */
- nr_to_fill = buf_len / (sizeof(unsigned long) * ratio);
-
- if (!nr_to_fill)
- dprintf3("%s() nr_to_fill: %ld\n", __func__, nr_to_fill);
-
- /* Align the buffer to pointer size */
- while (((unsigned long)buf) % sizeof(void *)) {
- buf++;
- buf_len--;
- }
- /* We are storing pointers, so make */
- buf_len_in_ptrs = buf_len / sizeof(void *);
-
- for (i = 0; i < nr_to_fill; i++) {
- long index = (mpx_random() % buf_len_in_ptrs);
- void *ptr = buf + index * sizeof(unsigned long);
- unsigned long ptr_addr = (unsigned long)ptr;
-
- /* ptr and size can be anything */
- mpx_make_bound_helper((unsigned long)ptr, 8);
-
- /*
- * take bnd0 and put it in to bounds tables "buf + index" is an
- * address inside the buffer where we are pretending that we
- * are going to put a pointer We do not, though because we will
- * never load entries from the table, so it doesn't matter.
- */
- mpx_store_dsc_helper(ptr_addr, (unsigned long)ptr);
- dprintf4("storing bound table entry for %lx (buf start @ %p)\n",
- ptr_addr, buf);
- }
- return nr_to_fill;
-}
-
-unsigned long align_down(unsigned long alignme, unsigned long align_to)
-{
- return alignme & ~(align_to-1);
-}
-
-unsigned long align_up(unsigned long alignme, unsigned long align_to)
-{
- return (alignme + align_to - 1) & ~(align_to-1);
-}
-
-/*
- * Using 1MB alignment guarantees that each no allocation
- * will overlap with another's bounds tables.
- *
- * We have to cook our own allocator here. malloc() can
- * mix other allocation with ours which means that even
- * if we free all of our allocations, there might still
- * be bounds tables for the *areas* since there is other
- * valid memory there.
- *
- * We also can't use malloc() because a free() of an area
- * might not free it back to the kernel. We want it
- * completely unmapped an malloc() does not guarantee
- * that.
- */
-#ifdef __i386__
-long alignment = 4096;
-long sz_alignment = 4096;
-#else
-long alignment = 1 * MB;
-long sz_alignment = 1 * MB;
-#endif
-void *mpx_mini_alloc(unsigned long sz)
-{
- unsigned long long tries = 0;
- static void *last;
- void *ptr;
- void *try_at;
-
- sz = align_up(sz, sz_alignment);
-
- try_at = last + alignment;
- while (1) {
- ptr = mmap(try_at, sz, PROT_READ|PROT_WRITE,
- MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
- if (ptr == (void *)-1)
- return NULL;
- if (ptr == try_at)
- break;
-
- munmap(ptr, sz);
- try_at += alignment;
-#ifdef __i386__
- /*
- * This isn't quite correct for 32-bit binaries
- * on 64-bit kernels since they can use the
- * entire 32-bit address space, but it's close
- * enough.
- */
- if (try_at > (void *)0xC0000000)
-#else
- if (try_at > (void *)0x0000800000000000)
-#endif
- try_at = (void *)0x0;
- if (!(++tries % 10000))
- dprintf1("stuck in %s(), tries: %lld\n", __func__, tries);
- continue;
- }
- last = ptr;
- dprintf3("mpx_mini_alloc(0x%lx) returning: %p\n", sz, ptr);
- return ptr;
-}
-void mpx_mini_free(void *ptr, long sz)
-{
- dprintf2("%s() ptr: %p\n", __func__, ptr);
- if ((unsigned long)ptr > 0x100000000000) {
- dprintf1("uh oh !!!!!!!!!!!!!!! pointer too high: %p\n", ptr);
- test_failed();
- }
- sz = align_up(sz, sz_alignment);
- dprintf3("%s() ptr: %p before munmap\n", __func__, ptr);
- munmap(ptr, sz);
- dprintf3("%s() ptr: %p DONE\n", __func__, ptr);
-}
-
-#define NR_MALLOCS 100
-struct one_malloc {
- char *ptr;
- int nr_filled_btes;
- unsigned long size;
-};
-struct one_malloc mallocs[NR_MALLOCS];
-
-void free_one_malloc(int index)
-{
- unsigned long free_ptr;
- unsigned long mask;
-
- if (!mallocs[index].ptr)
- return;
-
- mpx_mini_free(mallocs[index].ptr, mallocs[index].size);
- dprintf4("freed[%d]: %p\n", index, mallocs[index].ptr);
-
- free_ptr = (unsigned long)mallocs[index].ptr;
- mask = alignment-1;
- dprintf4("lowerbits: %lx / %lx mask: %lx\n", free_ptr,
- (free_ptr & mask), mask);
- assert((free_ptr & mask) == 0);
-
- mallocs[index].ptr = NULL;
-}
-
-#ifdef __i386__
-#define MPX_BOUNDS_TABLE_COVERS 4096
-#else
-#define MPX_BOUNDS_TABLE_COVERS (1 * MB)
-#endif
-void zap_everything(void)
-{
- long after_zap;
- long before_zap;
- int i;
-
- before_zap = inspect_me(bounds_dir_ptr);
- dprintf1("zapping everything start: %ld\n", before_zap);
- for (i = 0; i < NR_MALLOCS; i++)
- free_one_malloc(i);
-
- after_zap = inspect_me(bounds_dir_ptr);
- dprintf1("zapping everything done: %ld\n", after_zap);
- /*
- * We only guarantee to empty the thing out if our allocations are
- * exactly aligned on the boundaries of a boudns table.
- */
- if ((alignment >= MPX_BOUNDS_TABLE_COVERS) &&
- (sz_alignment >= MPX_BOUNDS_TABLE_COVERS)) {
- if (after_zap != 0)
- test_failed();
-
- assert(after_zap == 0);
- }
-}
-
-void do_one_malloc(void)
-{
- static int malloc_counter;
- long sz;
- int rand_index = (mpx_random() % NR_MALLOCS);
- void *ptr = mallocs[rand_index].ptr;
-
- dprintf3("%s() enter\n", __func__);
-
- if (ptr) {
- dprintf3("freeing one malloc at index: %d\n", rand_index);
- free_one_malloc(rand_index);
- if (mpx_random() % (NR_MALLOCS*3) == 3) {
- int i;
- dprintf3("zapping some more\n");
- for (i = rand_index; i < NR_MALLOCS; i++)
- free_one_malloc(i);
- }
- if ((mpx_random() % zap_all_every_this_many_mallocs) == 4)
- zap_everything();
- }
-
- /* 1->~1M */
- sz = (1 + mpx_random() % 1000) * 1000;
- ptr = mpx_mini_alloc(sz);
- if (!ptr) {
- /*
- * If we are failing allocations, just assume we
- * are out of memory and zap everything.
- */
- dprintf3("zapping everything because out of memory\n");
- zap_everything();
- goto out;
- }
-
- dprintf3("malloc: %p size: 0x%lx\n", ptr, sz);
- mallocs[rand_index].nr_filled_btes = cover_buf_with_bt_entries(ptr, sz);
- mallocs[rand_index].ptr = ptr;
- mallocs[rand_index].size = sz;
-out:
- if ((++malloc_counter) % inspect_every_this_many_mallocs == 0)
- inspect_me(bounds_dir_ptr);
-}
-
-void run_timed_test(void (*test_func)(void))
-{
- int done = 0;
- long iteration = 0;
- static time_t last_print;
- time_t now;
- time_t start;
-
- time(&start);
- while (!done) {
- time(&now);
- if ((now - start) > TEST_DURATION_SECS)
- done = 1;
-
- test_func();
- iteration++;
-
- if ((now - last_print > 1) || done) {
- printf("iteration %ld complete, OK so far\n", iteration);
- last_print = now;
- }
- }
-}
-
-void check_bounds_table_frees(void)
-{
- printf("executing unmaptest\n");
- inspect_me(bounds_dir_ptr);
- run_timed_test(&do_one_malloc);
- printf("done with malloc() fun\n");
-}
-
-void insn_test_failed(int test_nr, int test_round, void *buf,
- void *buf_shadow, void *ptr)
-{
- print_context(xsave_test_buf);
- eprintf("ERROR: test %d round %d failed\n", test_nr, test_round);
- while (test_nr == 5) {
- struct mpx_bt_entry *bte;
- struct mpx_bounds_dir *bd = (void *)bounds_dir_ptr;
- struct mpx_bd_entry *bde = mpx_vaddr_to_bd_entry(buf, bd);
-
- printf(" bd: %p\n", bd);
- printf("&bde: %p\n", bde);
- printf("*bde: %lx\n", *(unsigned long *)bde);
- if (!bd_entry_valid(bde))
- break;
-
- bte = mpx_vaddr_to_bt_entry(buf, bd);
- printf(" te: %p\n", bte);
- printf("bte[0]: %lx\n", bte->contents[0]);
- printf("bte[1]: %lx\n", bte->contents[1]);
- printf("bte[2]: %lx\n", bte->contents[2]);
- printf("bte[3]: %lx\n", bte->contents[3]);
- break;
- }
- test_failed();
-}
-
-void check_mpx_insns_and_tables(void)
-{
- int successes = 0;
- int failures = 0;
- int buf_size = (1024*1024);
- unsigned long *buf = malloc(buf_size);
- const int total_nr_tests = NR_MPX_TEST_FUNCTIONS * TEST_ROUNDS;
- int i, j;
-
- memset(buf, 0, buf_size);
- memset(buf_shadow, 0, sizeof(buf_shadow));
-
- for (i = 0; i < TEST_ROUNDS; i++) {
- uint8_t *ptr = get_random_addr() + 8;
-
- for (j = 0; j < NR_MPX_TEST_FUNCTIONS; j++) {
- if (0 && j != 5) {
- successes++;
- continue;
- }
- dprintf2("starting test %d round %d\n", j, i);
- dprint_context(xsave_test_buf);
- /*
- * test5 loads an address from the bounds tables.
- * The load will only complete if 'ptr' matches
- * the load and the store, so with random addrs,
- * the odds of this are very small. Make it
- * higher by only moving 'ptr' 1/10 times.
- */
- if (random() % 10 <= 0)
- ptr = get_random_addr() + 8;
- dprintf3("random ptr{%p}\n", ptr);
- dprint_context(xsave_test_buf);
- run_helpers(j, (void *)buf, (void *)buf_shadow, ptr);
- dprint_context(xsave_test_buf);
- if (!compare_context(xsave_test_buf)) {
- insn_test_failed(j, i, buf, buf_shadow, ptr);
- failures++;
- goto exit;
- }
- successes++;
- dprint_context(xsave_test_buf);
- dprintf2("finished test %d round %d\n", j, i);
- dprintf3("\n");
- dprint_context(xsave_test_buf);
- }
- }
-
-exit:
- dprintf2("\nabout to free:\n");
- free(buf);
- dprintf1("successes: %d\n", successes);
- dprintf1(" failures: %d\n", failures);
- dprintf1(" tests: %d\n", total_nr_tests);
- dprintf1(" expected: %jd #BRs\n", num_upper_brs + num_lower_brs);
- dprintf1(" saw: %d #BRs\n", br_count);
- if (failures) {
- eprintf("ERROR: non-zero number of failures\n");
- exit(20);
- }
- if (successes != total_nr_tests) {
- eprintf("ERROR: succeeded fewer than number of tries (%d != %d)\n",
- successes, total_nr_tests);
- exit(21);
- }
- if (num_upper_brs + num_lower_brs != br_count) {
- eprintf("ERROR: unexpected number of #BRs: %jd %jd %d\n",
- num_upper_brs, num_lower_brs, br_count);
- eprintf("successes: %d\n", successes);
- eprintf(" failures: %d\n", failures);
- eprintf(" tests: %d\n", total_nr_tests);
- eprintf(" expected: %jd #BRs\n", num_upper_brs + num_lower_brs);
- eprintf(" saw: %d #BRs\n", br_count);
- exit(22);
- }
-}
-
-/*
- * This is supposed to SIGSEGV nicely once the kernel
- * can no longer allocate vaddr space.
- */
-void exhaust_vaddr_space(void)
-{
- unsigned long ptr;
- /* Try to make sure there is no room for a bounds table anywhere */
- unsigned long skip = MPX_BOUNDS_TABLE_SIZE_BYTES - PAGE_SIZE;
-#ifdef __i386__
- unsigned long max_vaddr = 0xf7788000UL;
-#else
- unsigned long max_vaddr = 0x800000000000UL;
-#endif
-
- dprintf1("%s() start\n", __func__);
- /* do not start at 0, we aren't allowed to map there */
- for (ptr = PAGE_SIZE; ptr < max_vaddr; ptr += skip) {
- void *ptr_ret;
- int ret = madvise((void *)ptr, PAGE_SIZE, MADV_NORMAL);
-
- if (!ret) {
- dprintf1("madvise() %lx ret: %d\n", ptr, ret);
- continue;
- }
- ptr_ret = mmap((void *)ptr, PAGE_SIZE, PROT_READ|PROT_WRITE,
- MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
- if (ptr_ret != (void *)ptr) {
- perror("mmap");
- dprintf1("mmap(%lx) ret: %p\n", ptr, ptr_ret);
- break;
- }
- if (!(ptr & 0xffffff))
- dprintf1("mmap(%lx) ret: %p\n", ptr, ptr_ret);
- }
- for (ptr = PAGE_SIZE; ptr < max_vaddr; ptr += skip) {
- dprintf2("covering 0x%lx with bounds table entries\n", ptr);
- cover_buf_with_bt_entries((void *)ptr, PAGE_SIZE);
- }
- dprintf1("%s() end\n", __func__);
- printf("done with vaddr space fun\n");
-}
-
-void mpx_table_test(void)
-{
- printf("starting mpx bounds table test\n");
- run_timed_test(check_mpx_insns_and_tables);
- printf("done with mpx bounds table test\n");
-}
-
-int main(int argc, char **argv)
-{
- int unmaptest = 0;
- int vaddrexhaust = 0;
- int tabletest = 0;
- int i;
-
- check_mpx_support();
- mpx_prepare();
- srandom(11179);
-
- bd_incore();
- init();
- bd_incore();
-
- trace_me();
-
- xsave_state((void *)xsave_test_buf, 0x1f);
- if (!compare_context(xsave_test_buf))
- printf("Init failed\n");
-
- for (i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "unmaptest"))
- unmaptest = 1;
- if (!strcmp(argv[i], "vaddrexhaust"))
- vaddrexhaust = 1;
- if (!strcmp(argv[i], "tabletest"))
- tabletest = 1;
- }
- if (!(unmaptest || vaddrexhaust || tabletest)) {
- unmaptest = 1;
- /* vaddrexhaust = 1; */
- tabletest = 1;
- }
- if (unmaptest)
- check_bounds_table_frees();
- if (tabletest)
- mpx_table_test();
- if (vaddrexhaust)
- exhaust_vaddr_space();
- printf("%s completed successfully\n", argv[0]);
- exit(0);
-}
-
-#include "mpx-dig.c"
diff --git a/tools/testing/selftests/x86/mpx-mm.h b/tools/testing/selftests/x86/mpx-mm.h
deleted file mode 100644
index 6dbdd66b8242..000000000000
--- a/tools/testing/selftests/x86/mpx-mm.h
+++ /dev/null
@@ -1,10 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _MPX_MM_H
-#define _MPX_MM_H
-
-#define PAGE_SIZE 4096
-#define MB (1UL<<20)
-
-extern long nr_incore(void *ptr, unsigned long size_bytes);
-
-#endif /* _MPX_MM_H */
diff --git a/tools/testing/selftests/x86/protection_keys.c b/tools/testing/selftests/x86/protection_keys.c
index 5d546dcdbc80..480995bceefa 100644
--- a/tools/testing/selftests/x86/protection_keys.c
+++ b/tools/testing/selftests/x86/protection_keys.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Tests x86 Memory Protection Keys (see Documentation/x86/protection-keys.txt)
+ * Tests x86 Memory Protection Keys (see Documentation/core-api/protection-keys.rst)
*
* There are examples in here of:
* * how to set protection keys on memory
diff --git a/tools/testing/selftests/x86/sigreturn.c b/tools/testing/selftests/x86/sigreturn.c
index 4d9dc3f2fd70..3e49a7873f3e 100644
--- a/tools/testing/selftests/x86/sigreturn.c
+++ b/tools/testing/selftests/x86/sigreturn.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* sigreturn.c - tests for x86 sigreturn(2) and exit-to-userspace
* Copyright (c) 2014-2015 Andrew Lutomirski
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
* This is a series of tests that exercises the sigreturn(2) syscall and
* the IRET / SYSRET paths in the kernel.
*
diff --git a/tools/testing/selftests/x86/single_step_syscall.c b/tools/testing/selftests/x86/single_step_syscall.c
index ddfdd635de16..50ce6c3dd904 100644
--- a/tools/testing/selftests/x86/single_step_syscall.c
+++ b/tools/testing/selftests/x86/single_step_syscall.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* single_step_syscall.c - single-steps various x86 syscalls
* Copyright (c) 2014-2015 Andrew Lutomirski
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
* This is a very simple series of tests that makes system calls with
* the TF flag set. This exercises some nasty kernel code in the
* SYSENTER case: SYSENTER does not clear TF, so SYSENTER with TF set
diff --git a/tools/testing/selftests/x86/syscall_arg_fault.c b/tools/testing/selftests/x86/syscall_arg_fault.c
index 7db4fc9fa09f..bc0ecc2e862e 100644
--- a/tools/testing/selftests/x86/syscall_arg_fault.c
+++ b/tools/testing/selftests/x86/syscall_arg_fault.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* syscall_arg_fault.c - tests faults 32-bit fast syscall stack args
* Copyright (c) 2015 Andrew Lutomirski
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#define _GNU_SOURCE
@@ -23,9 +15,30 @@
#include <setjmp.h>
#include <errno.h>
+#ifdef __x86_64__
+# define WIDTH "q"
+#else
+# define WIDTH "l"
+#endif
+
/* Our sigaltstack scratch space. */
static unsigned char altstack_data[SIGSTKSZ];
+static unsigned long get_eflags(void)
+{
+ unsigned long eflags;
+ asm volatile ("pushf" WIDTH "\n\tpop" WIDTH " %0" : "=rm" (eflags));
+ return eflags;
+}
+
+static void set_eflags(unsigned long eflags)
+{
+ asm volatile ("push" WIDTH " %0\n\tpopf" WIDTH
+ : : "rm" (eflags) : "flags");
+}
+
+#define X86_EFLAGS_TF (1UL << 8)
+
static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
int flags)
{
@@ -43,13 +56,22 @@ static sigjmp_buf jmpbuf;
static volatile sig_atomic_t n_errs;
-static void sigsegv(int sig, siginfo_t *info, void *ctx_void)
+#ifdef __x86_64__
+#define REG_AX REG_RAX
+#define REG_IP REG_RIP
+#else
+#define REG_AX REG_EAX
+#define REG_IP REG_EIP
+#endif
+
+static void sigsegv_or_sigbus(int sig, siginfo_t *info, void *ctx_void)
{
ucontext_t *ctx = (ucontext_t*)ctx_void;
+ long ax = (long)ctx->uc_mcontext.gregs[REG_AX];
- if (ctx->uc_mcontext.gregs[REG_EAX] != -EFAULT) {
- printf("[FAIL]\tAX had the wrong value: 0x%x\n",
- ctx->uc_mcontext.gregs[REG_EAX]);
+ if (ax != -EFAULT && ax != -ENOSYS) {
+ printf("[FAIL]\tAX had the wrong value: 0x%lx\n",
+ (unsigned long)ax);
n_errs++;
} else {
printf("[OK]\tSeems okay\n");
@@ -58,9 +80,42 @@ static void sigsegv(int sig, siginfo_t *info, void *ctx_void)
siglongjmp(jmpbuf, 1);
}
+static volatile sig_atomic_t sigtrap_consecutive_syscalls;
+
+static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
+{
+ /*
+ * KVM has some bugs that can cause us to stop making progress.
+ * detect them and complain, but don't infinite loop or fail the
+ * test.
+ */
+
+ ucontext_t *ctx = (ucontext_t*)ctx_void;
+ unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP];
+
+ if (*ip == 0x340f || *ip == 0x050f) {
+ /* The trap was on SYSCALL or SYSENTER */
+ sigtrap_consecutive_syscalls++;
+ if (sigtrap_consecutive_syscalls > 3) {
+ printf("[WARN]\tGot stuck single-stepping -- you probably have a KVM bug\n");
+ siglongjmp(jmpbuf, 1);
+ }
+ } else {
+ sigtrap_consecutive_syscalls = 0;
+ }
+}
+
static void sigill(int sig, siginfo_t *info, void *ctx_void)
{
- printf("[SKIP]\tIllegal instruction\n");
+ ucontext_t *ctx = (ucontext_t*)ctx_void;
+ unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP];
+
+ if (*ip == 0x0b0f) {
+ /* one of the ud2 instructions faulted */
+ printf("[OK]\tSYSCALL returned normally\n");
+ } else {
+ printf("[SKIP]\tIllegal instruction\n");
+ }
siglongjmp(jmpbuf, 1);
}
@@ -73,7 +128,13 @@ int main()
if (sigaltstack(&stack, NULL) != 0)
err(1, "sigaltstack");
- sethandler(SIGSEGV, sigsegv, SA_ONSTACK);
+ sethandler(SIGSEGV, sigsegv_or_sigbus, SA_ONSTACK);
+ /*
+ * The actual exception can vary. On Atom CPUs, we get #SS
+ * instead of #PF when the vDSO fails to access the stack when
+ * ESP is too close to 2^32, and #SS causes SIGBUS.
+ */
+ sethandler(SIGBUS, sigsegv_or_sigbus, SA_ONSTACK);
sethandler(SIGILL, sigill, SA_ONSTACK);
/*
@@ -122,9 +183,48 @@ int main()
"movl $-1, %%ebp\n\t"
"movl $-1, %%esp\n\t"
"syscall\n\t"
- "pushl $0" /* make sure we segfault cleanly */
+ "ud2" /* make sure we recover cleanly */
+ : : : "memory", "flags");
+ }
+
+ printf("[RUN]\tSYSENTER with TF and invalid state\n");
+ sethandler(SIGTRAP, sigtrap, SA_ONSTACK);
+
+ if (sigsetjmp(jmpbuf, 1) == 0) {
+ sigtrap_consecutive_syscalls = 0;
+ set_eflags(get_eflags() | X86_EFLAGS_TF);
+ asm volatile (
+ "movl $-1, %%eax\n\t"
+ "movl $-1, %%ebx\n\t"
+ "movl $-1, %%ecx\n\t"
+ "movl $-1, %%edx\n\t"
+ "movl $-1, %%esi\n\t"
+ "movl $-1, %%edi\n\t"
+ "movl $-1, %%ebp\n\t"
+ "movl $-1, %%esp\n\t"
+ "sysenter"
+ : : : "memory", "flags");
+ }
+ set_eflags(get_eflags() & ~X86_EFLAGS_TF);
+
+ printf("[RUN]\tSYSCALL with TF and invalid state\n");
+ if (sigsetjmp(jmpbuf, 1) == 0) {
+ sigtrap_consecutive_syscalls = 0;
+ set_eflags(get_eflags() | X86_EFLAGS_TF);
+ asm volatile (
+ "movl $-1, %%eax\n\t"
+ "movl $-1, %%ebx\n\t"
+ "movl $-1, %%ecx\n\t"
+ "movl $-1, %%edx\n\t"
+ "movl $-1, %%esi\n\t"
+ "movl $-1, %%edi\n\t"
+ "movl $-1, %%ebp\n\t"
+ "movl $-1, %%esp\n\t"
+ "syscall\n\t"
+ "ud2" /* make sure we recover cleanly */
: : : "memory", "flags");
}
+ set_eflags(get_eflags() & ~X86_EFLAGS_TF);
return 0;
}
diff --git a/tools/testing/selftests/x86/syscall_nt.c b/tools/testing/selftests/x86/syscall_nt.c
index 43fcab367fb0..02309a195041 100644
--- a/tools/testing/selftests/x86/syscall_nt.c
+++ b/tools/testing/selftests/x86/syscall_nt.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* syscall_nt.c - checks syscalls with NT set
* Copyright (c) 2014-2015 Andrew Lutomirski
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
* Some obscure user-space code requires the ability to make system calls
* with FLAGS.NT set. Make sure it works.
*/
diff --git a/tools/testing/selftests/x86/syscall_numbering.c b/tools/testing/selftests/x86/syscall_numbering.c
new file mode 100644
index 000000000000..d6b09cb1aa2c
--- /dev/null
+++ b/tools/testing/selftests/x86/syscall_numbering.c
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * syscall_arg_fault.c - tests faults 32-bit fast syscall stack args
+ * Copyright (c) 2018 Andrew Lutomirski
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <syscall.h>
+
+static int nerrs;
+
+#define X32_BIT 0x40000000UL
+
+static void check_enosys(unsigned long nr, bool *ok)
+{
+ /* If this fails, a segfault is reasonably likely. */
+ fflush(stdout);
+
+ long ret = syscall(nr, 0, 0, 0, 0, 0, 0);
+ if (ret == 0) {
+ printf("[FAIL]\tsyscall %lu succeeded, but it should have failed\n", nr);
+ *ok = false;
+ } else if (errno != ENOSYS) {
+ printf("[FAIL]\tsyscall %lu had error code %d, but it should have reported ENOSYS\n", nr, errno);
+ *ok = false;
+ }
+}
+
+static void test_x32_without_x32_bit(void)
+{
+ bool ok = true;
+
+ /*
+ * Syscalls 512-547 are "x32" syscalls. They are intended to be
+ * called with the x32 (0x40000000) bit set. Calling them without
+ * the x32 bit set is nonsense and should not work.
+ */
+ printf("[RUN]\tChecking syscalls 512-547\n");
+ for (int i = 512; i <= 547; i++)
+ check_enosys(i, &ok);
+
+ /*
+ * Check that a handful of 64-bit-only syscalls are rejected if the x32
+ * bit is set.
+ */
+ printf("[RUN]\tChecking some 64-bit syscalls in x32 range\n");
+ check_enosys(16 | X32_BIT, &ok); /* ioctl */
+ check_enosys(19 | X32_BIT, &ok); /* readv */
+ check_enosys(20 | X32_BIT, &ok); /* writev */
+
+ /*
+ * Check some syscalls with high bits set.
+ */
+ printf("[RUN]\tChecking numbers above 2^32-1\n");
+ check_enosys((1UL << 32), &ok);
+ check_enosys(X32_BIT | (1UL << 32), &ok);
+
+ if (!ok)
+ nerrs++;
+ else
+ printf("[OK]\tThey all returned -ENOSYS\n");
+}
+
+int main()
+{
+ /*
+ * Anyone diagnosing a failure will want to know whether the kernel
+ * supports x32. Tell them.
+ */
+ printf("\tChecking for x32...");
+ fflush(stdout);
+ if (syscall(39 | X32_BIT, 0, 0, 0, 0, 0, 0) >= 0) {
+ printf(" supported\n");
+ } else if (errno == ENOSYS) {
+ printf(" not supported\n");
+ } else {
+ printf(" confused\n");
+ }
+
+ test_x32_without_x32_bit();
+
+ return nerrs ? 1 : 0;
+}
diff --git a/tools/testing/selftests/x86/sysret_rip.c b/tools/testing/selftests/x86/sysret_rip.c
index d85ec5b3671c..84d74be1d902 100644
--- a/tools/testing/selftests/x86/sysret_rip.c
+++ b/tools/testing/selftests/x86/sysret_rip.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* sigreturn.c - tests that x86 avoids Intel SYSRET pitfalls
* Copyright (c) 2014-2016 Andrew Lutomirski
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#define _GNU_SOURCE
diff --git a/tools/testing/selftests/x86/sysret_ss_attrs.c b/tools/testing/selftests/x86/sysret_ss_attrs.c
index ce42d5a64009..5f3d4fca440f 100644
--- a/tools/testing/selftests/x86/sysret_ss_attrs.c
+++ b/tools/testing/selftests/x86/sysret_ss_attrs.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* sysret_ss_attrs.c - test that syscalls return valid hidden SS attributes
* Copyright (c) 2015 Andrew Lutomirski
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
* On AMD CPUs, SYSRET can return with a valid SS descriptor with with
* the hidden attributes set to an unusable state. Make sure the kernel
* doesn't let this happen.
diff --git a/tools/testing/selftests/x86/test_mremap_vdso.c b/tools/testing/selftests/x86/test_mremap_vdso.c
index 64f11c8d9b76..f0d876d48277 100644
--- a/tools/testing/selftests/x86/test_mremap_vdso.c
+++ b/tools/testing/selftests/x86/test_mremap_vdso.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 32-bit test to check vDSO mremap.
*
* Copyright (c) 2016 Dmitry Safonov
* Suggested-by: Andrew Lutomirski
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
/*
* Can be built statically:
diff --git a/tools/testing/selftests/x86/test_syscall_vdso.c b/tools/testing/selftests/x86/test_syscall_vdso.c
index c9c3281077bc..8965c311bd65 100644
--- a/tools/testing/selftests/x86/test_syscall_vdso.c
+++ b/tools/testing/selftests/x86/test_syscall_vdso.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 32-bit syscall ABI conformance test.
*
* Copyright (c) 2015 Denys Vlasenko
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
/*
* Can be built statically:
diff --git a/tools/testing/selftests/x86/test_vsyscall.c b/tools/testing/selftests/x86/test_vsyscall.c
index 0b4f1cc2291c..a4f4d4cf22c3 100644
--- a/tools/testing/selftests/x86/test_vsyscall.c
+++ b/tools/testing/selftests/x86/test_vsyscall.c
@@ -18,6 +18,7 @@
#include <sched.h>
#include <stdbool.h>
#include <setjmp.h>
+#include <sys/uio.h>
#ifdef __x86_64__
# define VSYS(x) (x)
@@ -49,21 +50,21 @@ static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
}
/* vsyscalls and vDSO */
-bool should_read_vsyscall = false;
+bool vsyscall_map_r = false, vsyscall_map_x = false;
typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz);
-gtod_t vgtod = (gtod_t)VSYS(0xffffffffff600000);
+const gtod_t vgtod = (gtod_t)VSYS(0xffffffffff600000);
gtod_t vdso_gtod;
typedef int (*vgettime_t)(clockid_t, struct timespec *);
vgettime_t vdso_gettime;
typedef long (*time_func_t)(time_t *t);
-time_func_t vtime = (time_func_t)VSYS(0xffffffffff600400);
+const time_func_t vtime = (time_func_t)VSYS(0xffffffffff600400);
time_func_t vdso_time;
typedef long (*getcpu_t)(unsigned *, unsigned *, void *);
-getcpu_t vgetcpu = (getcpu_t)VSYS(0xffffffffff600800);
+const getcpu_t vgetcpu = (getcpu_t)VSYS(0xffffffffff600800);
getcpu_t vdso_getcpu;
static void init_vdso(void)
@@ -107,7 +108,7 @@ static int init_vsys(void)
maps = fopen("/proc/self/maps", "r");
if (!maps) {
printf("[WARN]\tCould not open /proc/self/maps -- assuming vsyscall is r-x\n");
- should_read_vsyscall = true;
+ vsyscall_map_r = true;
return 0;
}
@@ -133,12 +134,8 @@ static int init_vsys(void)
}
printf("\tvsyscall permissions are %c-%c\n", r, x);
- should_read_vsyscall = (r == 'r');
- if (x != 'x') {
- vgtod = NULL;
- vtime = NULL;
- vgetcpu = NULL;
- }
+ vsyscall_map_r = (r == 'r');
+ vsyscall_map_x = (x == 'x');
found = true;
break;
@@ -148,10 +145,8 @@ static int init_vsys(void)
if (!found) {
printf("\tno vsyscall map in /proc/self/maps\n");
- should_read_vsyscall = false;
- vgtod = NULL;
- vtime = NULL;
- vgetcpu = NULL;
+ vsyscall_map_r = false;
+ vsyscall_map_x = false;
}
return nerrs;
@@ -183,9 +178,13 @@ static inline long sys_getcpu(unsigned * cpu, unsigned * node,
}
static jmp_buf jmpbuf;
+static volatile unsigned long segv_err;
static void sigsegv(int sig, siginfo_t *info, void *ctx_void)
{
+ ucontext_t *ctx = (ucontext_t *)ctx_void;
+
+ segv_err = ctx->uc_mcontext.gregs[REG_ERR];
siglongjmp(jmpbuf, 1);
}
@@ -238,7 +237,7 @@ static int test_gtod(void)
err(1, "syscall gettimeofday");
if (vdso_gtod)
ret_vdso = vdso_gtod(&tv_vdso, &tz_vdso);
- if (vgtod)
+ if (vsyscall_map_x)
ret_vsys = vgtod(&tv_vsys, &tz_vsys);
if (sys_gtod(&tv_sys2, &tz_sys) != 0)
err(1, "syscall gettimeofday");
@@ -252,7 +251,7 @@ static int test_gtod(void)
}
}
- if (vgtod) {
+ if (vsyscall_map_x) {
if (ret_vsys == 0) {
nerrs += check_gtod(&tv_sys1, &tv_sys2, &tz_sys, "vsyscall", &tv_vsys, &tz_vsys);
} else {
@@ -273,7 +272,7 @@ static int test_time(void) {
t_sys1 = sys_time(&t2_sys1);
if (vdso_time)
t_vdso = vdso_time(&t2_vdso);
- if (vtime)
+ if (vsyscall_map_x)
t_vsys = vtime(&t2_vsys);
t_sys2 = sys_time(&t2_sys2);
if (t_sys1 < 0 || t_sys1 != t2_sys1 || t_sys2 < 0 || t_sys2 != t2_sys2) {
@@ -294,7 +293,7 @@ static int test_time(void) {
}
}
- if (vtime) {
+ if (vsyscall_map_x) {
if (t_vsys < 0 || t_vsys != t2_vsys) {
printf("[FAIL]\tvsyscall failed (ret:%ld output:%ld)\n", t_vsys, t2_vsys);
nerrs++;
@@ -330,7 +329,7 @@ static int test_getcpu(int cpu)
ret_sys = sys_getcpu(&cpu_sys, &node_sys, 0);
if (vdso_getcpu)
ret_vdso = vdso_getcpu(&cpu_vdso, &node_vdso, 0);
- if (vgetcpu)
+ if (vsyscall_map_x)
ret_vsys = vgetcpu(&cpu_vsys, &node_vsys, 0);
if (ret_sys == 0) {
@@ -369,7 +368,7 @@ static int test_getcpu(int cpu)
}
}
- if (vgetcpu) {
+ if (vsyscall_map_x) {
if (ret_vsys) {
printf("[FAIL]\tvsyscall getcpu() failed\n");
nerrs++;
@@ -410,20 +409,88 @@ static int test_vsys_r(void)
can_read = false;
}
- if (can_read && !should_read_vsyscall) {
+ if (can_read && !vsyscall_map_r) {
printf("[FAIL]\tWe have read access, but we shouldn't\n");
return 1;
- } else if (!can_read && should_read_vsyscall) {
+ } else if (!can_read && vsyscall_map_r) {
printf("[FAIL]\tWe don't have read access, but we should\n");
return 1;
+ } else if (can_read) {
+ printf("[OK]\tWe have read access\n");
} else {
- printf("[OK]\tgot expected result\n");
+ printf("[OK]\tWe do not have read access: #PF(0x%lx)\n",
+ segv_err);
}
#endif
return 0;
}
+static int test_vsys_x(void)
+{
+#ifdef __x86_64__
+ if (vsyscall_map_x) {
+ /* We already tested this adequately. */
+ return 0;
+ }
+
+ printf("[RUN]\tMake sure that vsyscalls really page fault\n");
+
+ bool can_exec;
+ if (sigsetjmp(jmpbuf, 1) == 0) {
+ vgtod(NULL, NULL);
+ can_exec = true;
+ } else {
+ can_exec = false;
+ }
+
+ if (can_exec) {
+ printf("[FAIL]\tExecuting the vsyscall did not page fault\n");
+ return 1;
+ } else if (segv_err & (1 << 4)) { /* INSTR */
+ printf("[OK]\tExecuting the vsyscall page failed: #PF(0x%lx)\n",
+ segv_err);
+ } else {
+ printf("[FAIL]\tExecution failed with the wrong error: #PF(0x%lx)\n",
+ segv_err);
+ return 1;
+ }
+#endif
+
+ return 0;
+}
+
+static int test_process_vm_readv(void)
+{
+#ifdef __x86_64__
+ char buf[4096];
+ struct iovec local, remote;
+ int ret;
+
+ printf("[RUN]\tprocess_vm_readv() from vsyscall page\n");
+
+ local.iov_base = buf;
+ local.iov_len = 4096;
+ remote.iov_base = (void *)0xffffffffff600000;
+ remote.iov_len = 4096;
+ ret = process_vm_readv(getpid(), &local, 1, &remote, 1, 0);
+ if (ret != 4096) {
+ printf("[OK]\tprocess_vm_readv() failed (ret = %d, errno = %d)\n", ret, errno);
+ return 0;
+ }
+
+ if (vsyscall_map_r) {
+ if (!memcmp(buf, (const void *)0xffffffffff600000, 4096)) {
+ printf("[OK]\tIt worked and read correct data\n");
+ } else {
+ printf("[FAIL]\tIt worked but returned incorrect data\n");
+ return 1;
+ }
+ }
+#endif
+
+ return 0;
+}
#ifdef __x86_64__
#define X86_EFLAGS_TF (1UL << 8)
@@ -455,7 +522,7 @@ static int test_emulation(void)
time_t tmp;
bool is_native;
- if (!vtime)
+ if (!vsyscall_map_x)
return 0;
printf("[RUN]\tchecking that vsyscalls are emulated\n");
@@ -497,6 +564,9 @@ int main(int argc, char **argv)
sethandler(SIGSEGV, sigsegv, 0);
nerrs += test_vsys_r();
+ nerrs += test_vsys_x();
+
+ nerrs += test_process_vm_readv();
#ifdef __x86_64__
nerrs += test_emulation();
diff --git a/tools/testing/selftests/x86/thunks.S b/tools/testing/selftests/x86/thunks.S
index ce8a995bbb17..1bb5d62c16a4 100644
--- a/tools/testing/selftests/x86/thunks.S
+++ b/tools/testing/selftests/x86/thunks.S
@@ -1,16 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* thunks.S - assembly helpers for mixed-bitness code
* Copyright (c) 2015 Andrew Lutomirski
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
* These are little helpers that make it easier to switch bitness on
* the fly.
*/
diff --git a/tools/testing/selftests/x86/thunks_32.S b/tools/testing/selftests/x86/thunks_32.S
index 29b644bb9f2f..a71d92da8f46 100644
--- a/tools/testing/selftests/x86/thunks_32.S
+++ b/tools/testing/selftests/x86/thunks_32.S
@@ -1,16 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* thunks_32.S - assembly helpers for mixed-bitness code
* Copyright (c) 2015 Denys Vlasenko
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
* These are little helpers that make it easier to switch bitness on
* the fly.
*/
diff --git a/tools/testing/selftests/x86/trivial_32bit_program.c b/tools/testing/selftests/x86/trivial_32bit_program.c
index fabdf0f51621..aa1f58c2f71c 100644
--- a/tools/testing/selftests/x86/trivial_32bit_program.c
+++ b/tools/testing/selftests/x86/trivial_32bit_program.c
@@ -1,7 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Trivial program to check that we have a valid 32-bit build environment.
* Copyright (c) 2015 Andy Lutomirski
- * GPL v2
*/
#ifndef __i386__
diff --git a/tools/testing/selftests/x86/trivial_64bit_program.c b/tools/testing/selftests/x86/trivial_64bit_program.c
index 05c6a41b3671..39f4b84fbf15 100644
--- a/tools/testing/selftests/x86/trivial_64bit_program.c
+++ b/tools/testing/selftests/x86/trivial_64bit_program.c
@@ -1,7 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Trivial program to check that we have a valid 64-bit build environment.
* Copyright (c) 2015 Andy Lutomirski
- * GPL v2
*/
#ifndef __x86_64__
diff --git a/tools/testing/selftests/x86/unwind_vdso.c b/tools/testing/selftests/x86/unwind_vdso.c
index 97311333700e..0075ccd65407 100644
--- a/tools/testing/selftests/x86/unwind_vdso.c
+++ b/tools/testing/selftests/x86/unwind_vdso.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* unwind_vdso.c - tests unwind info for AT_SYSINFO in the vDSO
* Copyright (c) 2014-2015 Andrew Lutomirski
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
* This tests __kernel_vsyscall's unwind info.
*/
diff --git a/tools/testing/selftests/x86/vdso_restorer.c b/tools/testing/selftests/x86/vdso_restorer.c
index cb038424a403..29a5c94c4b50 100644
--- a/tools/testing/selftests/x86/vdso_restorer.c
+++ b/tools/testing/selftests/x86/vdso_restorer.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vdso_restorer.c - tests vDSO-based signal restore
* Copyright (c) 2015 Andrew Lutomirski
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
* This makes sure that sa_restorer == NULL keeps working on 32-bit
* configurations. Modern glibc doesn't use it under any circumstances,
* so it's easy to overlook breakage.
diff --git a/tools/testing/selftests/zram/README b/tools/testing/selftests/zram/README
index 7972cc512408..110b34834a6f 100644
--- a/tools/testing/selftests/zram/README
+++ b/tools/testing/selftests/zram/README
@@ -37,4 +37,4 @@ Commands required for testing:
- mkfs/ mkfs.ext4
For more information please refer:
-kernel-source-tree/Documentation/blockdev/zram.txt
+kernel-source-tree/Documentation/admin-guide/blockdev/zram.rst
diff --git a/tools/testing/selftests/zram/zram01.sh b/tools/testing/selftests/zram/zram01.sh
index b9566a6478a9..114863d9fb87 100755
--- a/tools/testing/selftests/zram/zram01.sh
+++ b/tools/testing/selftests/zram/zram01.sh
@@ -1,16 +1,7 @@
#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (c) 2015 Oracle and/or its affiliates. All Rights Reserved.
#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as
-# published by the Free Software Foundation; either version 2 of
-# the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it would be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
# Test creates several zram devices with different filesystems on them.
# It fills each device with zeros and checks that compression works.
#
diff --git a/tools/testing/selftests/zram/zram02.sh b/tools/testing/selftests/zram/zram02.sh
index 74569b883737..e83b404807c0 100755
--- a/tools/testing/selftests/zram/zram02.sh
+++ b/tools/testing/selftests/zram/zram02.sh
@@ -1,16 +1,7 @@
#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (c) 2015 Oracle and/or its affiliates. All Rights Reserved.
#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as
-# published by the Free Software Foundation; either version 2 of
-# the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it would be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
# Test checks that we can create swap zram device.
#
# Author: Alexey Kodanev <alexey.kodanev@oracle.com>
diff --git a/tools/testing/selftests/zram/zram_lib.sh b/tools/testing/selftests/zram/zram_lib.sh
index 9e73a4fb9b0a..6f872f266fd1 100755
--- a/tools/testing/selftests/zram/zram_lib.sh
+++ b/tools/testing/selftests/zram/zram_lib.sh
@@ -1,16 +1,7 @@
#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (c) 2015 Oracle and/or its affiliates. All Rights Reserved.
#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as
-# published by the Free Software Foundation; either version 2 of
-# the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it would be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
# Author: Alexey Kodanev <alexey.kodanev@oracle.com>
# Modified: Naresh Kamboju <naresh.kamboju@linaro.org>
diff --git a/tools/testing/vsock/Makefile b/tools/testing/vsock/Makefile
index 66ba0924194d..5be687b1e16c 100644
--- a/tools/testing/vsock/Makefile
+++ b/tools/testing/vsock/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
all: test
test: vsock_diag_test
vsock_diag_test: vsock_diag_test.o timeout.o control.o
diff --git a/tools/testing/vsock/control.c b/tools/testing/vsock/control.c
index 90fd47f0e422..45f328c6ff23 100644
--- a/tools/testing/vsock/control.c
+++ b/tools/testing/vsock/control.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Control socket for client/server test execution
*
* Copyright (C) 2017 Red Hat, Inc.
*
* Author: Stefan Hajnoczi <stefanha@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; version 2
- * of the License.
*/
/* The client and server may need to coordinate to avoid race conditions like
diff --git a/tools/testing/vsock/timeout.c b/tools/testing/vsock/timeout.c
index c49b3003b2db..44aee49b6cee 100644
--- a/tools/testing/vsock/timeout.c
+++ b/tools/testing/vsock/timeout.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Timeout API for single-threaded programs that use blocking
* syscalls (read/write/send/recv/connect/accept).
*
* Copyright (C) 2017 Red Hat, Inc.
*
* Author: Stefan Hajnoczi <stefanha@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; version 2
- * of the License.
*/
/* Use the following pattern:
diff --git a/tools/testing/vsock/vsock_diag_test.c b/tools/testing/vsock/vsock_diag_test.c
index e896a4af52f4..c481101364a4 100644
--- a/tools/testing/vsock/vsock_diag_test.c
+++ b/tools/testing/vsock/vsock_diag_test.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vsock_diag_test - vsock_diag.ko test suite
*
* Copyright (C) 2017 Red Hat, Inc.
*
* Author: Stefan Hajnoczi <stefanha@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; version 2
- * of the License.
*/
#include <getopt.h>
diff --git a/tools/thermal/tmon/pid.c b/tools/thermal/tmon/pid.c
index fd7e9e9d6f4a..c54edb4f630c 100644
--- a/tools/thermal/tmon/pid.c
+++ b/tools/thermal/tmon/pid.c
@@ -1,21 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* pid.c PID controller for testing cooling devices
*
- *
- *
* Copyright (C) 2012 Intel Corporation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 or later as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* Author Name Jacob Pan <jacob.jun.pan@linux.intel.com>
- *
*/
#include <unistd.h>
diff --git a/tools/thermal/tmon/sysfs.c b/tools/thermal/tmon/sysfs.c
index 18f523557983..b00b1bfd9d8e 100644
--- a/tools/thermal/tmon/sysfs.c
+++ b/tools/thermal/tmon/sysfs.c
@@ -1,19 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* sysfs.c sysfs ABI access functions for TMON program
*
* Copyright (C) 2013 Intel Corporation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 or later as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* Author: Jacob Pan <jacob.jun.pan@linux.intel.com>
- *
*/
#include <unistd.h>
#include <stdio.h>
diff --git a/tools/thermal/tmon/tmon.c b/tools/thermal/tmon/tmon.c
index b43138f8b862..83ec6e482f12 100644
--- a/tools/thermal/tmon/tmon.c
+++ b/tools/thermal/tmon/tmon.c
@@ -1,19 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* tmon.c Thermal Monitor (TMON) main function and entry point
*
* Copyright (C) 2012 Intel Corporation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 or later as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* Author: Jacob Pan <jacob.jun.pan@linux.intel.com>
- *
*/
#include <getopt.h>
diff --git a/tools/thermal/tmon/tmon.h b/tools/thermal/tmon/tmon.h
index 9e3c49c547ac..c9066ec104dd 100644
--- a/tools/thermal/tmon/tmon.h
+++ b/tools/thermal/tmon/tmon.h
@@ -1,19 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* tmon.h contains data structures and constants used by TMON
*
* Copyright (C) 2012 Intel Corporation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 or later as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* Author Name Jacob Pan <jacob.jun.pan@linux.intel.com>
- *
*/
#ifndef TMON_H
diff --git a/tools/thermal/tmon/tui.c b/tools/thermal/tmon/tui.c
index b5d1c6b22dd3..031b258667d8 100644
--- a/tools/thermal/tmon/tui.c
+++ b/tools/thermal/tmon/tui.c
@@ -1,19 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* tui.c ncurses text user interface for TMON program
*
* Copyright (C) 2013 Intel Corporation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 or later as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* Author: Jacob Pan <jacob.jun.pan@linux.intel.com>
- *
*/
#include <unistd.h>
diff --git a/tools/time/udelay_test.sh b/tools/time/udelay_test.sh
index 12d46b926917..6779d7e55d85 100755
--- a/tools/time/udelay_test.sh
+++ b/tools/time/udelay_test.sh
@@ -1,4 +1,5 @@
#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-only
# udelay() test script
#
@@ -8,14 +9,6 @@
#
# Copyright (C) 2014 Google, Inc.
#
-# This software is licensed under the terms of the GNU General Public
-# License version 2, as published by the Free Software Foundation, and
-# may be copied, distributed, and modified under those terms.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
MODULE_NAME=udelay_test
UDELAY_PATH=/sys/kernel/debug/udelay_test
diff --git a/tools/usb/ffs-aio-example/simple/host_app/Makefile b/tools/usb/ffs-aio-example/simple/host_app/Makefile
index 8c4a6f0aa82d..c3523837c936 100644
--- a/tools/usb/ffs-aio-example/simple/host_app/Makefile
+++ b/tools/usb/ffs-aio-example/simple/host_app/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
CC = gcc
LIBUSB_CFLAGS = $(shell pkg-config --cflags libusb-1.0)
LIBUSB_LIBS = $(shell pkg-config --libs libusb-1.0)
diff --git a/tools/usb/ffs-test.c b/tools/usb/ffs-test.c
index 0f395dfb7774..22b938fbdfb7 100644
--- a/tools/usb/ffs-test.c
+++ b/tools/usb/ffs-test.c
@@ -1,22 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ffs-test.c -- user mode filesystem api for usb composite function
*
* Copyright (C) 2010 Samsung Electronics
* Author: Michal Nazarewicz <mina86@mina86.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* $(CROSS_COMPILE)cc -Wall -Wextra -g -o ffs-test ffs-test.c -lpthread */
diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c
index 2d89b5f686b1..ee8208b2f946 100644
--- a/tools/usb/testusb.c
+++ b/tools/usb/testusb.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* $(CROSS_COMPILE)cc -Wall -Wextra -g -lpthread -o testusb testusb.c */
/*
* Copyright (c) 2002 by David Brownell
* Copyright (c) 2010 by Samsung Electronics
* Author: Michal Nazarewicz <mina86@mina86.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
diff --git a/tools/usb/usbip/libsrc/names.c b/tools/usb/usbip/libsrc/names.c
index 81ff8522405c..aba7f4188044 100644
--- a/tools/usb/usbip/libsrc/names.c
+++ b/tools/usb/usbip/libsrc/names.c
@@ -1,29 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* names.c -- USB name database manipulation routines
*
* Copyright (C) 1999, 2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *
- *
- *
- *
* Copyright (C) 2005 Takahiro Hirofuchi
* - names_deinit() is added.
- *
*/
#include <sys/types.h>
diff --git a/tools/usb/usbip/libsrc/names.h b/tools/usb/usbip/libsrc/names.h
index 680926512de2..b39958230e70 100644
--- a/tools/usb/usbip/libsrc/names.h
+++ b/tools/usb/usbip/libsrc/names.h
@@ -1,24 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* names.h -- USB name database manipulation routines
*
* Copyright (C) 1999, 2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *
- *
* Copyright (C) 2005 Takahiro Hirofuchi
* - names_free() is added.
*/
diff --git a/tools/usb/usbip/libsrc/usbip_common.c b/tools/usb/usbip/libsrc/usbip_common.c
index bb424638d75b..b8d7d480595a 100644
--- a/tools/usb/usbip/libsrc/usbip_common.c
+++ b/tools/usb/usbip/libsrc/usbip_common.c
@@ -226,8 +226,10 @@ int read_usb_device(struct udev_device *sdev, struct usbip_usb_device *udev)
path = udev_device_get_syspath(sdev);
name = udev_device_get_sysname(sdev);
- strncpy(udev->path, path, SYSFS_PATH_MAX);
- strncpy(udev->busid, name, SYSFS_BUS_ID_SIZE);
+ strncpy(udev->path, path, SYSFS_PATH_MAX - 1);
+ udev->path[SYSFS_PATH_MAX - 1] = '\0';
+ strncpy(udev->busid, name, SYSFS_BUS_ID_SIZE - 1);
+ udev->busid[SYSFS_BUS_ID_SIZE - 1] = '\0';
sscanf(name, "%u-%u", &busnum, &devnum);
udev->busnum = busnum;
diff --git a/tools/usb/usbip/libsrc/usbip_device_driver.c b/tools/usb/usbip/libsrc/usbip_device_driver.c
index ec3a0b794f15..051d7d3f443b 100644
--- a/tools/usb/usbip/libsrc/usbip_device_driver.c
+++ b/tools/usb/usbip/libsrc/usbip_device_driver.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2015 Karol Kosik <karo9@interia.eu>
* 2015 Samsung Electronics
@@ -6,19 +7,6 @@
* Based on tools/usb/usbip/libsrc/usbip_host_driver.c, which is:
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fcntl.h>
@@ -103,7 +91,8 @@ int read_usb_vudc_device(struct udev_device *sdev, struct usbip_usb_device *dev)
copy_descr_attr16(dev, &descr, idProduct);
copy_descr_attr16(dev, &descr, bcdDevice);
- strncpy(dev->path, path, SYSFS_PATH_MAX);
+ strncpy(dev->path, path, SYSFS_PATH_MAX - 1);
+ dev->path[SYSFS_PATH_MAX - 1] = '\0';
dev->speed = USB_SPEED_UNKNOWN;
speed = udev_device_get_sysattr_value(sdev, "current_speed");
@@ -122,7 +111,8 @@ int read_usb_vudc_device(struct udev_device *sdev, struct usbip_usb_device *dev)
dev->busnum = 0;
name = udev_device_get_sysname(plat);
- strncpy(dev->busid, name, SYSFS_BUS_ID_SIZE);
+ strncpy(dev->busid, name, SYSFS_BUS_ID_SIZE - 1);
+ dev->busid[SYSFS_BUS_ID_SIZE - 1] = '\0';
return 0;
err:
fclose(fd);
diff --git a/tools/usb/usbip/libsrc/usbip_device_driver.h b/tools/usb/usbip/libsrc/usbip_device_driver.h
index 54cb658b37a3..1ce0bbd75f34 100644
--- a/tools/usb/usbip/libsrc/usbip_device_driver.h
+++ b/tools/usb/usbip/libsrc/usbip_device_driver.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2015 Karol Kosik <karo9@interia.eu>
* 2015 Samsung Electronics
@@ -6,19 +7,6 @@
* Based on tools/usb/usbip/libsrc/usbip_host_driver.c, which is:
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __USBIP_DEVICE_DRIVER_H
diff --git a/tools/usb/usbip/libsrc/usbip_host_common.c b/tools/usb/usbip/libsrc/usbip_host_common.c
index d79c7581b175..2813aa821c82 100644
--- a/tools/usb/usbip/libsrc/usbip_host_common.c
+++ b/tools/usb/usbip/libsrc/usbip_host_common.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2015-2016 Samsung Electronics
* Igor Kotrasinski <i.kotrasinsk@samsung.com>
@@ -6,19 +7,6 @@
* Refactored from usbip_host_driver.c, which is:
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
diff --git a/tools/usb/usbip/libsrc/usbip_host_common.h b/tools/usb/usbip/libsrc/usbip_host_common.h
index a64b8033fe64..f46967c0aa18 100644
--- a/tools/usb/usbip/libsrc/usbip_host_common.h
+++ b/tools/usb/usbip/libsrc/usbip_host_common.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2015-2016 Samsung Electronics
* Igor Kotrasinski <i.kotrasinsk@samsung.com>
@@ -6,19 +7,6 @@
* Refactored from usbip_host_driver.c, which is:
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __USBIP_HOST_COMMON_H
diff --git a/tools/usb/usbip/libsrc/usbip_host_driver.c b/tools/usb/usbip/libsrc/usbip_host_driver.c
index 4de6edc54d35..573e73ec36bd 100644
--- a/tools/usb/usbip/libsrc/usbip_host_driver.c
+++ b/tools/usb/usbip/libsrc/usbip_host_driver.c
@@ -1,22 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
* Copyright (C) 2015-2016 Samsung Electronics
* Igor Kotrasinski <i.kotrasinsk@samsung.com>
* Krzysztof Opasiak <k.opasiak@samsung.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <unistd.h>
diff --git a/tools/usb/usbip/libsrc/usbip_host_driver.h b/tools/usb/usbip/libsrc/usbip_host_driver.h
index 77f07e72a7fe..6ba996c5a709 100644
--- a/tools/usb/usbip/libsrc/usbip_host_driver.h
+++ b/tools/usb/usbip/libsrc/usbip_host_driver.h
@@ -1,22 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
* Copyright (C) 2015-2016 Samsung Electronics
* Igor Kotrasinski <i.kotrasinsk@samsung.com>
* Krzysztof Opasiak <k.opasiak@samsung.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __USBIP_HOST_DRIVER_H
diff --git a/tools/usb/usbip/src/usbip.c b/tools/usb/usbip/src/usbip.c
index 73d8eee8130b..f7c7220d9766 100644
--- a/tools/usb/usbip/src/usbip.c
+++ b/tools/usb/usbip/src/usbip.c
@@ -1,22 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* command structure borrowed from udev
* (git://git.kernel.org/pub/scm/linux/hotplug/udev.git)
*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/tools/usb/usbip/src/usbip.h b/tools/usb/usbip/src/usbip.h
index 84fe66a9d8ad..e31779290601 100644
--- a/tools/usb/usbip/src/usbip.h
+++ b/tools/usb/usbip/src/usbip.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __USBIP_H
diff --git a/tools/usb/usbip/src/usbip_attach.c b/tools/usb/usbip/src/usbip_attach.c
index ba88728483ff..b4aeb9f1f493 100644
--- a/tools/usb/usbip/src/usbip_attach.c
+++ b/tools/usb/usbip/src/usbip_attach.c
@@ -1,22 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
* Copyright (C) 2015-2016 Samsung Electronics
* Igor Kotrasinski <i.kotrasinsk@samsung.com>
* Krzysztof Opasiak <k.opasiak@samsung.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/stat.h>
diff --git a/tools/usb/usbip/src/usbip_bind.c b/tools/usb/usbip/src/usbip_bind.c
index e121cfb1746a..f1cf9225a69c 100644
--- a/tools/usb/usbip/src/usbip_bind.c
+++ b/tools/usb/usbip/src/usbip_bind.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libudev.h>
diff --git a/tools/usb/usbip/src/usbip_detach.c b/tools/usb/usbip/src/usbip_detach.c
index 777f7286a0c5..aec993159036 100644
--- a/tools/usb/usbip/src/usbip_detach.c
+++ b/tools/usb/usbip/src/usbip_detach.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
diff --git a/tools/usb/usbip/src/usbip_list.c b/tools/usb/usbip/src/usbip_list.c
index 8d4ccf4b9480..8625b0f514ee 100644
--- a/tools/usb/usbip/src/usbip_list.c
+++ b/tools/usb/usbip/src/usbip_list.c
@@ -1,22 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
* Copyright (C) 2015-2016 Samsung Electronics
* Igor Kotrasinski <i.kotrasinsk@samsung.com>
* Krzysztof Opasiak <k.opasiak@samsung.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
diff --git a/tools/usb/usbip/src/usbip_network.c b/tools/usb/usbip/src/usbip_network.c
index 8ffcd47d9638..d595d72693fb 100644
--- a/tools/usb/usbip/src/usbip_network.c
+++ b/tools/usb/usbip/src/usbip_network.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/socket.h>
diff --git a/tools/usb/usbip/src/usbip_port.c b/tools/usb/usbip/src/usbip_port.c
index 7bd74fb3a9cd..4d14387df13d 100644
--- a/tools/usb/usbip/src/usbip_port.c
+++ b/tools/usb/usbip/src/usbip_port.c
@@ -1,16 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include "vhci_driver.h"
diff --git a/tools/usb/usbip/src/usbip_unbind.c b/tools/usb/usbip/src/usbip_unbind.c
index a4a496c9cbaf..66a44d4a0d56 100644
--- a/tools/usb/usbip/src/usbip_unbind.c
+++ b/tools/usb/usbip/src/usbip_unbind.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libudev.h>
diff --git a/tools/usb/usbip/src/usbipd.c b/tools/usb/usbip/src/usbipd.c
index 32864c52942d..48398a78e88a 100644
--- a/tools/usb/usbip/src/usbipd.c
+++ b/tools/usb/usbip/src/usbipd.c
@@ -1,22 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
* Copyright (C) 2015-2016 Samsung Electronics
* Igor Kotrasinski <i.kotrasinsk@samsung.com>
* Krzysztof Opasiak <k.opasiak@samsung.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
diff --git a/tools/usb/usbip/src/utils.c b/tools/usb/usbip/src/utils.c
index 3d7b42e77299..76a2e1247f33 100644
--- a/tools/usb/usbip/src/utils.c
+++ b/tools/usb/usbip/src/utils.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
diff --git a/tools/usb/usbip/src/utils.h b/tools/usb/usbip/src/utils.h
index 5916fd3e02a6..4fc13854f7b9 100644
--- a/tools/usb/usbip/src/utils.h
+++ b/tools/usb/usbip/src/utils.h
@@ -1,19 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __UTILS_H
diff --git a/tools/virtio/linux/kernel.h b/tools/virtio/linux/kernel.h
index 7ef45a4a3cba..6683b4a70b05 100644
--- a/tools/virtio/linux/kernel.h
+++ b/tools/virtio/linux/kernel.h
@@ -127,7 +127,7 @@ static inline void free_page(unsigned long addr)
#define dev_err(dev, format, ...) fprintf (stderr, format, ## __VA_ARGS__)
#define dev_warn(dev, format, ...) fprintf (stderr, format, ## __VA_ARGS__)
-#define WARN_ON_ONCE(cond) ((cond) ? fprintf (stderr, "WARNING\n") : 0)
+#define WARN_ON_ONCE(cond) (unlikely(cond) ? fprintf (stderr, "WARNING\n") : 0)
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
diff --git a/tools/virtio/ringtest/main.c b/tools/virtio/ringtest/main.c
index 453ca3c21193..5a18b2301a63 100644
--- a/tools/virtio/ringtest/main.c
+++ b/tools/virtio/ringtest/main.c
@@ -1,7 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2016 Red Hat, Inc.
* Author: Michael S. Tsirkin <mst@redhat.com>
- * This work is licensed under the terms of the GNU GPL, version 2.
*
* Command line processing and common functions for ring benchmarking.
*/
diff --git a/tools/virtio/ringtest/main.h b/tools/virtio/ringtest/main.h
index 301d59bfcd0a..6d1fccd3d86c 100644
--- a/tools/virtio/ringtest/main.h
+++ b/tools/virtio/ringtest/main.h
@@ -1,7 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2016 Red Hat, Inc.
* Author: Michael S. Tsirkin <mst@redhat.com>
- * This work is licensed under the terms of the GNU GPL, version 2.
*
* Common macros and functions for ring benchmarking.
*/
diff --git a/tools/virtio/ringtest/ring.c b/tools/virtio/ringtest/ring.c
index 5a41404aaef5..58e7d33bddfc 100644
--- a/tools/virtio/ringtest/ring.c
+++ b/tools/virtio/ringtest/ring.c
@@ -1,7 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2016 Red Hat, Inc.
* Author: Michael S. Tsirkin <mst@redhat.com>
- * This work is licensed under the terms of the GNU GPL, version 2.
*
* Simple descriptor-based ring. virtio 0.9 compatible event index is used for
* signalling, unconditionally.
diff --git a/tools/virtio/ringtest/virtio_ring_0_9.c b/tools/virtio/ringtest/virtio_ring_0_9.c
index 5fd3fbcb9e57..13a035a390e9 100644
--- a/tools/virtio/ringtest/virtio_ring_0_9.c
+++ b/tools/virtio/ringtest/virtio_ring_0_9.c
@@ -1,7 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2016 Red Hat, Inc.
* Author: Michael S. Tsirkin <mst@redhat.com>
- * This work is licensed under the terms of the GNU GPL, version 2.
*
* Partial implementation of virtio 0.9. event index is used for signalling,
* unconditionally. Design roughly follows linux kernel implementation in order
diff --git a/tools/virtio/vhost_test/Makefile b/tools/virtio/vhost_test/Makefile
index a1d35b81b314..94d3aff987dc 100644
--- a/tools/virtio/vhost_test/Makefile
+++ b/tools/virtio/vhost_test/Makefile
@@ -1,2 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-m += vhost_test.o
EXTRA_CFLAGS += -Idrivers/vhost
diff --git a/tools/virtio/virtio-trace/trace-agent-ctl.c b/tools/virtio/virtio-trace/trace-agent-ctl.c
index a2d0403c4f94..73d253d4b559 100644
--- a/tools/virtio/virtio-trace/trace-agent-ctl.c
+++ b/tools/virtio/virtio-trace/trace-agent-ctl.c
@@ -1,12 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Controller of read/write threads for virtio-trace
*
* Copyright (C) 2012 Hitachi, Ltd.
* Created by Yoshihiro Yunomae <yoshihiro.yunomae.ez@hitachi.com>
* Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
- *
- * Licensed under GPL version 2 only.
- *
*/
#define _GNU_SOURCE
diff --git a/tools/virtio/virtio-trace/trace-agent-rw.c b/tools/virtio/virtio-trace/trace-agent-rw.c
index 3aace5ea4842..ddfe7875eb16 100644
--- a/tools/virtio/virtio-trace/trace-agent-rw.c
+++ b/tools/virtio/virtio-trace/trace-agent-rw.c
@@ -1,12 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Read/write thread of a guest agent for virtio-trace
*
* Copyright (C) 2012 Hitachi, Ltd.
* Created by Yoshihiro Yunomae <yoshihiro.yunomae.ez@hitachi.com>
* Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
- *
- * Licensed under GPL version 2 only.
- *
*/
#define _GNU_SOURCE
diff --git a/tools/virtio/virtio-trace/trace-agent.c b/tools/virtio/virtio-trace/trace-agent.c
index 0a0a7dd4eff7..cdfe77c2b4c8 100644
--- a/tools/virtio/virtio-trace/trace-agent.c
+++ b/tools/virtio/virtio-trace/trace-agent.c
@@ -1,12 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Guest agent for virtio-trace
*
* Copyright (C) 2012 Hitachi, Ltd.
* Created by Yoshihiro Yunomae <yoshihiro.yunomae.ez@hitachi.com>
* Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
- *
- * Licensed under GPL version 2 only.
- *
*/
#define _GNU_SOURCE
diff --git a/tools/vm/page-types.c b/tools/vm/page-types.c
index 6f64b2b93234..58c0eab71bca 100644
--- a/tools/vm/page-types.c
+++ b/tools/vm/page-types.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* page-types: Tool for querying page flags
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should find a copy of v2 of the GNU General Public License somewhere on
- * your Linux system; if not, write to the Free Software Foundation, Inc., 59
- * Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
* Copyright (C) 2009 Intel corporation
*
* Authors: Wu Fengguang <fengguang.wu@intel.com>
diff --git a/tools/vm/slabinfo-gnuplot.sh b/tools/vm/slabinfo-gnuplot.sh
index 0cf28aa6f21c..26e193ffd2a2 100644
--- a/tools/vm/slabinfo-gnuplot.sh
+++ b/tools/vm/slabinfo-gnuplot.sh
@@ -1,16 +1,9 @@
#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-only
# Sergey Senozhatsky, 2015
# sergey.senozhatsky.work@gmail.com
#
-# This software is licensed under the terms of the GNU General Public
-# License version 2, as published by the Free Software Foundation, and
-# may be copied, distributed, and modified under those terms.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
# This program is intended to plot a `slabinfo -X' stats, collected,
diff --git a/tools/vm/slabinfo.c b/tools/vm/slabinfo.c
index 73818f1b2ef8..68092d15e12b 100644
--- a/tools/vm/slabinfo.c
+++ b/tools/vm/slabinfo.c
@@ -79,6 +79,7 @@ int sort_size;
int sort_active;
int set_debug;
int show_ops;
+int sort_partial;
int show_activity;
int output_lines = -1;
int sort_loss;
@@ -110,7 +111,7 @@ static void fatal(const char *x, ...)
static void usage(void)
{
printf("slabinfo 4/15/2011. (c) 2007 sgi/(c) 2011 Linux Foundation.\n\n"
- "slabinfo [-aADefhilnosrStTvz1LXBU] [N=K] [-dafzput] [slab-regexp]\n"
+ "slabinfo [-aABDefhilLnoPrsStTUvXz1] [N=K] [-dafzput] [slab-regexp]\n"
"-a|--aliases Show aliases\n"
"-A|--activity Most active slabs first\n"
"-B|--Bytes Show size in bytes\n"
@@ -124,6 +125,7 @@ static void usage(void)
"-n|--numa Show NUMA information\n"
"-N|--lines=K Show the first K slabs\n"
"-o|--ops Show kmem_cache_ops\n"
+ "-P|--partial Sort by number of partial slabs\n"
"-r|--report Detailed report on single slabs\n"
"-s|--shrink Shrink slabs\n"
"-S|--Size Sort by size\n"
@@ -131,9 +133,9 @@ static void usage(void)
"-T|--Totals Show summary information\n"
"-U|--Unreclaim Show unreclaimable slabs only\n"
"-v|--validate Validate slabs\n"
+ "-X|--Xtotals Show extended summary information\n"
"-z|--zero Include empty slabs\n"
"-1|--1ref Single reference\n"
- "-X|--Xtotals Show extended summary information\n"
"\n"
"-d | --debug Switch off all debug options\n"
@@ -146,6 +148,8 @@ static void usage(void)
" p | P Poisoning\n"
" u | U Tracking\n"
" t | T Tracing\n"
+
+ "\nSorting options (--Loss, --Size, --Partial) are mutually exclusive\n"
);
}
@@ -1047,6 +1051,8 @@ static void sort_slabs(void)
result = slab_activity(s1) < slab_activity(s2);
else if (sort_loss)
result = slab_waste(s1) < slab_waste(s2);
+ else if (sort_partial)
+ result = s1->partial < s2->partial;
else
result = strcasecmp(s1->name, s2->name);
@@ -1307,33 +1313,46 @@ static void output_slabs(void)
}
}
+static void _xtotals(char *heading, char *underline,
+ int loss, int size, int partial)
+{
+ printf("%s%s", heading, underline);
+ line = 0;
+ sort_loss = loss;
+ sort_size = size;
+ sort_partial = partial;
+ sort_slabs();
+ output_slabs();
+}
+
static void xtotals(void)
{
+ char *heading, *underline;
+
totals();
link_slabs();
rename_slabs();
- printf("\nSlabs sorted by size\n");
- printf("--------------------\n");
- sort_loss = 0;
- sort_size = 1;
- sort_slabs();
- output_slabs();
+ heading = "\nSlabs sorted by size\n";
+ underline = "--------------------\n";
+ _xtotals(heading, underline, 0, 1, 0);
+
+ heading = "\nSlabs sorted by loss\n";
+ underline = "--------------------\n";
+ _xtotals(heading, underline, 1, 0, 0);
+
+ heading = "\nSlabs sorted by number of partial slabs\n";
+ underline = "---------------------------------------\n";
+ _xtotals(heading, underline, 0, 0, 1);
- printf("\nSlabs sorted by loss\n");
- printf("--------------------\n");
- line = 0;
- sort_loss = 1;
- sort_size = 0;
- sort_slabs();
- output_slabs();
printf("\n");
}
struct option opts[] = {
{ "aliases", no_argument, NULL, 'a' },
{ "activity", no_argument, NULL, 'A' },
+ { "Bytes", no_argument, NULL, 'B'},
{ "debug", optional_argument, NULL, 'd' },
{ "display-activity", no_argument, NULL, 'D' },
{ "empty", no_argument, NULL, 'e' },
@@ -1341,21 +1360,21 @@ struct option opts[] = {
{ "help", no_argument, NULL, 'h' },
{ "inverted", no_argument, NULL, 'i'},
{ "slabs", no_argument, NULL, 'l' },
+ { "Loss", no_argument, NULL, 'L'},
{ "numa", no_argument, NULL, 'n' },
+ { "lines", required_argument, NULL, 'N'},
{ "ops", no_argument, NULL, 'o' },
- { "shrink", no_argument, NULL, 's' },
+ { "partial", no_argument, NULL, 'p'},
{ "report", no_argument, NULL, 'r' },
+ { "shrink", no_argument, NULL, 's' },
{ "Size", no_argument, NULL, 'S'},
{ "tracking", no_argument, NULL, 't'},
{ "Totals", no_argument, NULL, 'T'},
+ { "Unreclaim", no_argument, NULL, 'U'},
{ "validate", no_argument, NULL, 'v' },
+ { "Xtotals", no_argument, NULL, 'X'},
{ "zero", no_argument, NULL, 'z' },
{ "1ref", no_argument, NULL, '1'},
- { "lines", required_argument, NULL, 'N'},
- { "Loss", no_argument, NULL, 'L'},
- { "Xtotals", no_argument, NULL, 'X'},
- { "Bytes", no_argument, NULL, 'B'},
- { "Unreclaim", no_argument, NULL, 'U'},
{ NULL, 0, NULL, 0 }
};
@@ -1367,18 +1386,18 @@ int main(int argc, char *argv[])
page_size = getpagesize();
- while ((c = getopt_long(argc, argv, "aAd::Defhil1noprstvzTSN:LXBU",
+ while ((c = getopt_long(argc, argv, "aABd::DefhilLnN:oPrsStTUvXz1",
opts, NULL)) != -1)
switch (c) {
- case '1':
- show_single_ref = 1;
- break;
case 'a':
show_alias = 1;
break;
case 'A':
sort_active = 1;
break;
+ case 'B':
+ show_bytes = 1;
+ break;
case 'd':
set_debug = 1;
if (!debug_opt_scan(optarg))
@@ -1399,45 +1418,48 @@ int main(int argc, char *argv[])
case 'i':
show_inverted = 1;
break;
+ case 'l':
+ show_slab = 1;
+ break;
+ case 'L':
+ sort_loss = 1;
+ break;
case 'n':
show_numa = 1;
break;
+ case 'N':
+ if (optarg) {
+ output_lines = atoi(optarg);
+ if (output_lines < 1)
+ output_lines = 1;
+ }
+ break;
case 'o':
show_ops = 1;
break;
case 'r':
show_report = 1;
break;
+ case 'P':
+ sort_partial = 1;
+ break;
case 's':
shrink = 1;
break;
- case 'l':
- show_slab = 1;
+ case 'S':
+ sort_size = 1;
break;
case 't':
show_track = 1;
break;
- case 'v':
- validate = 1;
- break;
- case 'z':
- skip_zero = 0;
- break;
case 'T':
show_totals = 1;
break;
- case 'S':
- sort_size = 1;
- break;
- case 'N':
- if (optarg) {
- output_lines = atoi(optarg);
- if (output_lines < 1)
- output_lines = 1;
- }
+ case 'U':
+ unreclaim_only = 1;
break;
- case 'L':
- sort_loss = 1;
+ case 'v':
+ validate = 1;
break;
case 'X':
if (output_lines == -1)
@@ -1445,11 +1467,11 @@ int main(int argc, char *argv[])
extended_totals = 1;
show_bytes = 1;
break;
- case 'B':
- show_bytes = 1;
+ case 'z':
+ skip_zero = 0;
break;
- case 'U':
- unreclaim_only = 1;
+ case '1':
+ show_single_ref = 1;
break;
default:
fatal("%s: Invalid option '%c'\n", argv[0], optopt);
diff --git a/tools/wmi/Makefile b/tools/wmi/Makefile
index e0e87239126b..e161ff59ec46 100644
--- a/tools/wmi/Makefile
+++ b/tools/wmi/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
PREFIX ?= /usr
SBINDIR ?= sbin
INSTALL ?= install
diff --git a/tools/wmi/dell-smbios-example.c b/tools/wmi/dell-smbios-example.c
index 9d3bde081249..1f3e7ab14b68 100644
--- a/tools/wmi/dell-smbios-example.c
+++ b/tools/wmi/dell-smbios-example.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Sample application for SMBIOS communication over WMI interface
* Performs the following:
@@ -6,10 +7,6 @@
* - Simple activation of a token
*
* Copyright (C) 2017 Dell, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <errno.h>