summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2023-04-26 23:52:34 +0300
committerJiri Kosina <jkosina@suse.cz>2023-04-26 23:52:34 +0300
commitcdc780f044a803aff8845b949f800f0f3d095d5f (patch)
tree6695a80568f6b4aef414070f17859f434e229957 /tools
parent38518593ec55e897abda4b4be77b2ec8ec4447d1 (diff)
parent37386669887d3f2ccf021322c5558353d20f2387 (diff)
downloadlinux-cdc780f044a803aff8845b949f800f0f3d095d5f.tar.xz
Merge branch 'for-6.4/amd-sfh' into for-linus
- assorted functional fixes for amd-sfh driver (Basavaraj Natikar)
Diffstat (limited to 'tools')
-rw-r--r--tools/arch/arm64/include/uapi/asm/kvm.h1
-rw-r--r--tools/arch/loongarch/include/uapi/asm/bitsperlong.h9
-rw-r--r--tools/arch/x86/include/asm/cpufeatures.h2
-rw-r--r--tools/arch/x86/include/asm/disabled-features.h3
-rw-r--r--tools/arch/x86/include/asm/msr-index.h31
-rw-r--r--tools/arch/x86/include/asm/orc_types.h4
-rw-r--r--tools/arch/x86/include/asm/required-features.h3
-rw-r--r--tools/arch/x86/include/uapi/asm/kvm.h34
-rw-r--r--tools/arch/x86/include/uapi/asm/svm.h6
-rw-r--r--tools/arch/x86/include/uapi/asm/unistd_32.h23
-rw-r--r--tools/arch/x86/include/uapi/asm/unistd_64.h23
-rw-r--r--tools/arch/x86/lib/memcpy_64.S5
-rw-r--r--tools/arch/x86/lib/memset_64.S4
-rwxr-xr-xtools/bootconfig/scripts/ftrace2bconf.sh2
-rwxr-xr-xtools/bootconfig/test-bootconfig.sh12
-rw-r--r--tools/build/Makefile.build1
-rw-r--r--tools/cgroup/memcg_shrinker.py3
-rw-r--r--tools/iio/iio_utils.c23
-rw-r--r--tools/include/linux/bits.h1
-rw-r--r--tools/include/linux/objtool.h11
-rw-r--r--tools/include/nolibc/arch-aarch64.h52
-rw-r--r--tools/include/nolibc/arch-arm.h138
-rw-r--r--tools/include/nolibc/arch-i386.h60
-rw-r--r--tools/include/nolibc/arch-mips.h79
-rw-r--r--tools/include/nolibc/arch-riscv.h62
-rw-r--r--tools/include/nolibc/arch-s390.h226
-rw-r--r--tools/include/nolibc/arch-x86_64.h52
-rw-r--r--tools/include/nolibc/arch.h2
-rw-r--r--tools/include/nolibc/errno.h4
-rw-r--r--tools/include/nolibc/stdlib.h27
-rw-r--r--tools/include/nolibc/sys.h24
-rw-r--r--tools/include/uapi/linux/fcntl.h1
-rw-r--r--tools/include/uapi/linux/kvm.h9
-rw-r--r--tools/include/uapi/linux/netdev.h6
-rw-r--r--tools/include/uapi/linux/perf_event.h3
-rw-r--r--tools/include/uapi/linux/prctl.h6
-rw-r--r--tools/include/uapi/linux/vhost.h8
-rw-r--r--tools/include/vdso/bits.h1
-rw-r--r--tools/lib/api/fs/tracing_path.c4
-rw-r--r--tools/lib/bpf/libbpf_internal.h4
-rw-r--r--tools/memory-model/Documentation/explanation.txt39
-rw-r--r--tools/memory-model/linux-kernel.bell6
-rw-r--r--tools/memory-model/linux-kernel.cat5
-rw-r--r--tools/memory-model/litmus-tests/dep+plain.litmus31
-rw-r--r--tools/mm/.gitignore (renamed from tools/vm/.gitignore)0
-rw-r--r--tools/mm/Makefile (renamed from tools/vm/Makefile)4
-rw-r--r--tools/mm/page-types.c (renamed from tools/vm/page-types.c)0
-rw-r--r--tools/mm/page_owner_sort.c (renamed from tools/vm/page_owner_sort.c)65
-rw-r--r--tools/mm/slabinfo-gnuplot.sh (renamed from tools/vm/slabinfo-gnuplot.sh)0
-rw-r--r--tools/mm/slabinfo.c (renamed from tools/vm/slabinfo.c)0
-rwxr-xr-xtools/net/ynl/cli.py2
-rw-r--r--tools/net/ynl/lib/.gitignore1
-rw-r--r--tools/net/ynl/lib/__init__.py9
-rw-r--r--tools/net/ynl/lib/nlspec.py139
-rw-r--r--tools/net/ynl/lib/ynl.py17
-rwxr-xr-xtools/net/ynl/ynl-gen-c.py141
-rwxr-xr-xtools/net/ynl/ynl-regen.sh2
-rw-r--r--tools/objtool/.gitignore1
-rw-r--r--tools/objtool/Build2
-rw-r--r--tools/objtool/Documentation/objtool.txt8
-rw-r--r--tools/objtool/Makefile66
-rw-r--r--tools/objtool/arch/powerpc/decode.c22
-rw-r--r--tools/objtool/arch/x86/decode.c107
-rw-r--r--tools/objtool/builtin-check.c2
-rw-r--r--tools/objtool/check.c377
-rw-r--r--tools/objtool/elf.c42
-rw-r--r--tools/objtool/include/objtool/arch.h6
-rw-r--r--tools/objtool/include/objtool/builtin.h2
-rw-r--r--tools/objtool/include/objtool/cfi.h1
-rw-r--r--tools/objtool/include/objtool/check.h61
-rw-r--r--tools/objtool/include/objtool/elf.h9
-rw-r--r--tools/objtool/include/objtool/objtool.h1
-rw-r--r--tools/objtool/include/objtool/special.h2
-rw-r--r--tools/objtool/objtool.c1
-rw-r--r--tools/objtool/orc_dump.c4
-rw-r--r--tools/objtool/orc_gen.c1
-rw-r--r--tools/objtool/special.c6
-rw-r--r--tools/perf/.gitignore1
-rw-r--r--tools/perf/Documentation/itrace.txt3
-rw-r--r--tools/perf/Documentation/perf-bench.txt2
-rw-r--r--tools/perf/Documentation/perf-c2c.txt16
-rw-r--r--tools/perf/Documentation/perf-intel-pt.txt66
-rw-r--r--tools/perf/Documentation/perf-list.txt2
-rw-r--r--tools/perf/Documentation/perf-lock.txt11
-rw-r--r--tools/perf/Documentation/perf-mem.txt7
-rw-r--r--tools/perf/Documentation/perf-probe.txt2
-rw-r--r--tools/perf/Documentation/perf-report.txt4
-rw-r--r--tools/perf/Documentation/perf-script-perl.txt2
-rw-r--r--tools/perf/Documentation/perf-script-python.txt4
-rw-r--r--tools/perf/Documentation/perf-script.txt7
-rw-r--r--tools/perf/Documentation/perf-test.txt3
-rw-r--r--tools/perf/Documentation/perf-top.txt2
-rw-r--r--tools/perf/Makefile.config4
-rw-r--r--tools/perf/Makefile.perf1
-rw-r--r--tools/perf/arch/arm/util/auxtrace.c5
-rw-r--r--tools/perf/arch/arm/util/cs-etm.c103
-rw-r--r--tools/perf/arch/arm64/util/pmu.c44
-rw-r--r--tools/perf/arch/powerpc/util/header.c4
-rw-r--r--tools/perf/arch/x86/tests/sample-parsing.c5
-rw-r--r--tools/perf/arch/x86/util/event.c21
-rw-r--r--tools/perf/arch/x86/util/pmu.c12
-rw-r--r--tools/perf/bench/bench.h2
-rw-r--r--tools/perf/bench/syscall.c76
-rw-r--r--tools/perf/builtin-bench.c4
-rw-r--r--tools/perf/builtin-c2c.c21
-rw-r--r--tools/perf/builtin-inject.c7
-rw-r--r--tools/perf/builtin-kmem.c1
-rw-r--r--tools/perf/builtin-list.c20
-rw-r--r--tools/perf/builtin-lock.c147
-rw-r--r--tools/perf/builtin-probe.c9
-rw-r--r--tools/perf/builtin-record.c16
-rw-r--r--tools/perf/builtin-script.c40
-rw-r--r--tools/perf/builtin-stat.c16
-rw-r--r--tools/perf/builtin-trace.c4
-rw-r--r--tools/perf/perf-completion.sh11
-rw-r--r--tools/perf/pmu-events/Build16
-rw-r--r--tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/metrics.json273
-rw-r--r--tools/perf/pmu-events/arch/arm64/sbsa.json30
-rw-r--r--tools/perf/pmu-events/arch/powerpc/power10/metrics.json8
-rw-r--r--tools/perf/pmu-events/arch/powerpc/power10/others.json2
-rw-r--r--tools/perf/pmu-events/arch/x86/mapfile.csv2
-rw-r--r--tools/perf/pmu-events/empty-pmu-events.c108
-rwxr-xr-xtools/perf/pmu-events/jevents.py359
-rw-r--r--tools/perf/pmu-events/metric.py79
-rwxr-xr-x[-rw-r--r--]tools/perf/pmu-events/metric_test.py15
-rw-r--r--tools/perf/pmu-events/pmu-events.h26
-rwxr-xr-xtools/perf/scripts/python/flamegraph.py107
-rw-r--r--tools/perf/tests/Build1
-rw-r--r--tools/perf/tests/bpf-script-example.c2
-rw-r--r--tools/perf/tests/bpf.c28
-rw-r--r--tools/perf/tests/builtin-test.c3
-rw-r--r--tools/perf/tests/dwarf-unwind.c5
-rw-r--r--tools/perf/tests/expand-cgroup.c4
-rw-r--r--tools/perf/tests/parse-metric.c4
-rw-r--r--tools/perf/tests/pmu-events.c69
-rw-r--r--tools/perf/tests/sample-parsing.c2
-rwxr-xr-xtools/perf/tests/shell/buildid.sh18
-rw-r--r--tools/perf/tests/shell/lib/coresight.sh18
-rw-r--r--tools/perf/tests/shell/lib/perf_json_output_lint.py29
-rw-r--r--tools/perf/tests/shell/lib/probe_vfs_getname.sh8
-rwxr-xr-xtools/perf/tests/shell/lock_contention.sh66
-rwxr-xr-xtools/perf/tests/shell/record+probe_libc_inet_pton.sh11
-rwxr-xr-xtools/perf/tests/shell/record+script_probe_vfs_getname.sh3
-rwxr-xr-xtools/perf/tests/shell/stat+csv_output.sh23
-rwxr-xr-xtools/perf/tests/shell/stat_all_metrics.sh2
-rwxr-xr-xtools/perf/tests/shell/test_brstack.sh18
-rwxr-xr-xtools/perf/tests/shell/test_intel_pt.sh17
-rw-r--r--tools/perf/tests/symbols.c150
-rw-r--r--tools/perf/tests/tests.h3
-rw-r--r--tools/perf/tests/workloads/thloop.c2
-rw-r--r--tools/perf/util/Build5
-rw-r--r--tools/perf/util/arm-spe-decoder/arm-spe-decoder.c6
-rw-r--r--tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c4
-rw-r--r--tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.h1
-rw-r--r--tools/perf/util/auxtrace.c12
-rw-r--r--tools/perf/util/auxtrace.h7
-rw-r--r--tools/perf/util/bpf_lock_contention.c183
-rw-r--r--tools/perf/util/bpf_skel/lock_contention.bpf.c104
-rw-r--r--tools/perf/util/bpf_skel/lock_data.h4
-rw-r--r--tools/perf/util/bpf_skel/off_cpu.bpf.c2
-rw-r--r--tools/perf/util/branch.c15
-rw-r--r--tools/perf/util/branch.h2
-rw-r--r--tools/perf/util/cacheline.h25
-rw-r--r--tools/perf/util/cgroup.c1
-rw-r--r--tools/perf/util/cputopo.c9
-rw-r--r--tools/perf/util/cs-etm-base.c34
-rw-r--r--tools/perf/util/cs-etm-decoder/cs-etm-decoder.c71
-rw-r--r--tools/perf/util/cs-etm.c95
-rw-r--r--tools/perf/util/cs-etm.h16
-rw-r--r--tools/perf/util/data-convert-bt.c4
-rw-r--r--tools/perf/util/debug.c15
-rw-r--r--tools/perf/util/evsel.c21
-rw-r--r--tools/perf/util/evsel.h2
-rw-r--r--tools/perf/util/evswitch.h4
-rw-r--r--tools/perf/util/expr.c5
-rw-r--r--tools/perf/util/expr.h1
-rw-r--r--tools/perf/util/expr.l8
-rw-r--r--tools/perf/util/intel-pt.c73
-rw-r--r--tools/perf/util/llvm-utils.c25
-rw-r--r--tools/perf/util/lock-contention.h7
-rw-r--r--tools/perf/util/metricgroup.c207
-rw-r--r--tools/perf/util/metricgroup.h4
-rw-r--r--tools/perf/util/parse-events.c2
-rw-r--r--tools/perf/util/pfm.c6
-rw-r--r--tools/perf/util/pmu-hybrid.c27
-rw-r--r--tools/perf/util/pmu.c227
-rw-r--r--tools/perf/util/pmu.h21
-rw-r--r--tools/perf/util/print-events.c32
-rw-r--r--tools/perf/util/print-events.h3
-rw-r--r--tools/perf/util/probe-event.c4
-rw-r--r--tools/perf/util/python.c19
-rw-r--r--tools/perf/util/sample.h5
-rw-r--r--tools/perf/util/scripting-engines/trace-event-perl.c4
-rw-r--r--tools/perf/util/scripting-engines/trace-event-python.c4
-rw-r--r--tools/perf/util/session.c14
-rw-r--r--tools/perf/util/sort.c20
-rw-r--r--tools/perf/util/sort.h3
-rw-r--r--tools/perf/util/stat-display.c51
-rw-r--r--tools/perf/util/stat-shadow.c114
-rw-r--r--tools/perf/util/stat.c6
-rw-r--r--tools/perf/util/stat.h2
-rw-r--r--tools/perf/util/symbol-elf.c527
-rw-r--r--tools/perf/util/symbol.c26
-rw-r--r--tools/perf/util/symbol.h3
-rw-r--r--tools/perf/util/symsrc.h1
-rw-r--r--tools/perf/util/synthetic-events.c4
-rw-r--r--tools/perf/util/target.h12
-rw-r--r--tools/perf/util/trace-event.h19
-rw-r--r--tools/power/acpi/tools/pfrut/pfrut.c18
-rwxr-xr-xtools/power/pm-graph/sleepgraph.py5
-rw-r--r--tools/power/x86/turbostat/turbostat.84
-rw-r--r--tools/power/x86/turbostat/turbostat.c24
-rw-r--r--tools/scripts/Makefile.arch11
-rw-r--r--tools/testing/cxl/Kbuild9
-rw-r--r--tools/testing/cxl/config_check.c1
-rw-r--r--tools/testing/cxl/cxl_acpi_test.c6
-rw-r--r--tools/testing/cxl/cxl_core_test.c6
-rw-r--r--tools/testing/cxl/cxl_mem_test.c6
-rw-r--r--tools/testing/cxl/cxl_pmem_test.c6
-rw-r--r--tools/testing/cxl/cxl_port_test.c6
-rw-r--r--tools/testing/cxl/test/Kbuild2
-rw-r--r--tools/testing/cxl/test/cxl.c165
-rw-r--r--tools/testing/cxl/test/mem.c352
-rw-r--r--tools/testing/cxl/test/mock.c36
-rw-r--r--tools/testing/cxl/test/mock.h6
-rw-r--r--tools/testing/cxl/watermark.h25
-rwxr-xr-xtools/testing/ktest/ktest.pl36
-rw-r--r--tools/testing/ktest/sample.conf5
-rwxr-xr-xtools/testing/kunit/kunit.py186
-rw-r--r--tools/testing/radix-tree/maple.c66
-rw-r--r--tools/testing/selftests/Makefile6
-rw-r--r--tools/testing/selftests/amd-pstate/Makefile13
-rw-r--r--tools/testing/selftests/arm64/fp/Makefile2
-rw-r--r--tools/testing/selftests/arm64/tags/Makefile2
-rw-r--r--tools/testing/selftests/bpf/prog_tests/btf.c28
-rw-r--r--tools/testing/selftests/bpf/prog_tests/uninit_stack.c9
-rw-r--r--tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c7
-rw-r--r--tools/testing/selftests/bpf/progs/find_vma_fail1.c1
-rw-r--r--tools/testing/selftests/bpf/progs/test_deny_namespace.c13
-rw-r--r--tools/testing/selftests/bpf/progs/test_global_func10.c8
-rw-r--r--tools/testing/selftests/bpf/progs/uninit_stack.c87
-rw-r--r--tools/testing/selftests/bpf/verifier/calls.c13
-rw-r--r--tools/testing/selftests/bpf/verifier/helper_access_var_len.c104
-rw-r--r--tools/testing/selftests/bpf/verifier/int_ptr.c9
-rw-r--r--tools/testing/selftests/bpf/verifier/search_pruning.c13
-rw-r--r--tools/testing/selftests/bpf/verifier/sock.c27
-rw-r--r--tools/testing/selftests/bpf/verifier/spill_fill.c7
-rw-r--r--tools/testing/selftests/bpf/verifier/var_off.c52
-rw-r--r--tools/testing/selftests/clone3/Makefile2
-rw-r--r--tools/testing/selftests/clone3/clone3.c3
-rw-r--r--tools/testing/selftests/core/Makefile2
-rw-r--r--tools/testing/selftests/damon/debugfs_rm_non_contexts.sh2
-rw-r--r--tools/testing/selftests/damon/sysfs.sh31
-rw-r--r--tools/testing/selftests/dmabuf-heaps/Makefile2
-rw-r--r--tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c3
-rw-r--r--tools/testing/selftests/drivers/dma-buf/Makefile2
-rw-r--r--tools/testing/selftests/drivers/net/bonding/Makefile3
-rwxr-xr-xtools/testing/selftests/drivers/net/bonding/bond-eth-type-change.sh85
-rw-r--r--tools/testing/selftests/drivers/s390x/uvdevice/Makefile3
-rw-r--r--tools/testing/selftests/filesystems/Makefile2
-rw-r--r--tools/testing/selftests/filesystems/binderfs/Makefile2
-rw-r--r--tools/testing/selftests/filesystems/epoll/Makefile2
-rw-r--r--tools/testing/selftests/ftrace/test.d/dynevent/eprobes_syntax_errors.tc4
-rw-r--r--tools/testing/selftests/ftrace/test.d/filter/event-filter-function.tc58
-rw-r--r--tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc2
-rw-r--r--tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_char.tc47
-rw-r--r--tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc3
-rw-r--r--tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc4
-rw-r--r--tools/testing/selftests/ftrace/test.d/kprobe/probepoint.tc2
-rw-r--r--tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic-event-stack.tc24
-rw-r--r--tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic-event-syntax.tc6
-rw-r--r--tools/testing/selftests/futex/functional/Makefile2
-rw-r--r--tools/testing/selftests/gpio/Makefile2
-rw-r--r--tools/testing/selftests/iommu/Makefile3
-rw-r--r--tools/testing/selftests/iommu/iommufd.c2
-rw-r--r--tools/testing/selftests/ipc/Makefile2
-rw-r--r--tools/testing/selftests/kcmp/Makefile2
-rwxr-xr-xtools/testing/selftests/kselftest_deps.sh6
-rw-r--r--tools/testing/selftests/kselftest_harness.h142
-rw-r--r--tools/testing/selftests/kvm/Makefile4
-rw-r--r--tools/testing/selftests/kvm/aarch64/page_fault_test.c2
-rw-r--r--tools/testing/selftests/kvm/aarch64/psci_test.c4
-rw-r--r--tools/testing/selftests/kvm/config1
-rw-r--r--tools/testing/selftests/kvm/include/test_util.h9
-rw-r--r--tools/testing/selftests/kvm/include/x86_64/hyperv.h149
-rw-r--r--tools/testing/selftests/kvm/include/x86_64/processor.h33
-rw-r--r--tools/testing/selftests/kvm/kvm_binary_stats_test.c21
-rw-r--r--tools/testing/selftests/kvm/lib/elf.c2
-rw-r--r--tools/testing/selftests/kvm/lib/guest_modes.c2
-rw-r--r--tools/testing/selftests/kvm/lib/kvm_util.c69
-rw-r--r--tools/testing/selftests/kvm/lib/s390x/diag318_test_handler.c3
-rw-r--r--tools/testing/selftests/kvm/lib/test_util.c25
-rw-r--r--tools/testing/selftests/kvm/lib/x86_64/processor.c67
-rw-r--r--tools/testing/selftests/kvm/memslot_perf_test.c5
-rw-r--r--tools/testing/selftests/kvm/s390x/memop.c672
-rw-r--r--tools/testing/selftests/kvm/s390x/sync_regs_test.c15
-rw-r--r--tools/testing/selftests/kvm/set_memory_region_test.c6
-rw-r--r--tools/testing/selftests/kvm/x86_64/amx_test.c8
-rw-r--r--tools/testing/selftests/kvm/x86_64/cr4_cpuid_sync_test.c8
-rw-r--r--tools/testing/selftests/kvm/x86_64/debug_regs.c2
-rw-r--r--tools/testing/selftests/kvm/x86_64/exit_on_emulation_failure_test.c3
-rw-r--r--tools/testing/selftests/kvm/x86_64/fix_hypercall_test.c4
-rw-r--r--tools/testing/selftests/kvm/x86_64/flds_emulation.h5
-rw-r--r--tools/testing/selftests/kvm/x86_64/hyperv_clock.c9
-rw-r--r--tools/testing/selftests/kvm/x86_64/hyperv_evmcs.c8
-rw-r--r--tools/testing/selftests/kvm/x86_64/hyperv_extended_hypercalls.c97
-rw-r--r--tools/testing/selftests/kvm/x86_64/hyperv_features.c353
-rw-r--r--tools/testing/selftests/kvm/x86_64/hyperv_ipi.c6
-rw-r--r--tools/testing/selftests/kvm/x86_64/hyperv_svm_test.c7
-rw-r--r--tools/testing/selftests/kvm/x86_64/hyperv_tlb_flush.c14
-rw-r--r--tools/testing/selftests/kvm/x86_64/kvm_clock_test.c5
-rw-r--r--tools/testing/selftests/kvm/x86_64/kvm_pv_test.c5
-rw-r--r--tools/testing/selftests/kvm/x86_64/mmio_warning_test.c2
-rw-r--r--tools/testing/selftests/kvm/x86_64/monitor_mwait_test.c9
-rw-r--r--tools/testing/selftests/kvm/x86_64/nested_exceptions_test.c5
-rw-r--r--tools/testing/selftests/kvm/x86_64/platform_info_test.c14
-rw-r--r--tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c391
-rw-r--r--tools/testing/selftests/kvm/x86_64/smm_test.c9
-rw-r--r--tools/testing/selftests/kvm/x86_64/state_test.c8
-rw-r--r--tools/testing/selftests/kvm/x86_64/svm_int_ctl_test.c8
-rw-r--r--tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c7
-rw-r--r--tools/testing/selftests/kvm/x86_64/svm_nested_soft_inject_test.c6
-rw-r--r--tools/testing/selftests/kvm/x86_64/svm_vmcall_test.c6
-rw-r--r--tools/testing/selftests/kvm/x86_64/sync_regs_test.c25
-rw-r--r--tools/testing/selftests/kvm/x86_64/triple_fault_event_test.c9
-rw-r--r--tools/testing/selftests/kvm/x86_64/tsc_msrs_test.c16
-rw-r--r--tools/testing/selftests/kvm/x86_64/tsc_scaling_sync.c6
-rw-r--r--tools/testing/selftests/kvm/x86_64/ucna_injection_test.c22
-rw-r--r--tools/testing/selftests/kvm/x86_64/userspace_io_test.c6
-rw-r--r--tools/testing/selftests/kvm/x86_64/userspace_msr_exit_test.c22
-rw-r--r--tools/testing/selftests/kvm/x86_64/vmx_apic_access_test.c11
-rw-r--r--tools/testing/selftests/kvm/x86_64/vmx_close_while_nested_test.c5
-rw-r--r--tools/testing/selftests/kvm/x86_64/vmx_dirty_log_test.c7
-rw-r--r--tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c6
-rw-r--r--tools/testing/selftests/kvm/x86_64/vmx_invalid_nested_guest_state.c4
-rw-r--r--tools/testing/selftests/kvm/x86_64/vmx_nested_tsc_scaling_test.c6
-rw-r--r--tools/testing/selftests/kvm/x86_64/vmx_preemption_timer_test.c8
-rw-r--r--tools/testing/selftests/kvm/x86_64/vmx_tsc_adjust_test.c6
-rw-r--r--tools/testing/selftests/kvm/x86_64/xapic_ipi_test.c6
-rw-r--r--tools/testing/selftests/kvm/x86_64/xapic_state_test.c55
-rw-r--r--tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c243
-rw-r--r--tools/testing/selftests/kvm/x86_64/xen_vmcall_test.c5
-rw-r--r--tools/testing/selftests/landlock/fs_test.c47
-rw-r--r--tools/testing/selftests/landlock/ptrace_test.c113
-rw-r--r--tools/testing/selftests/lib.mk2
-rw-r--r--tools/testing/selftests/media_tests/Makefile2
-rw-r--r--tools/testing/selftests/membarrier/Makefile2
-rw-r--r--tools/testing/selftests/memfd/Makefile4
-rw-r--r--tools/testing/selftests/memfd/fuse_test.c1
-rw-r--r--tools/testing/selftests/memfd/memfd_test.c341
-rw-r--r--tools/testing/selftests/mm/.gitignore (renamed from tools/testing/selftests/vm/.gitignore)0
-rw-r--r--tools/testing/selftests/mm/Makefile (renamed from tools/testing/selftests/vm/Makefile)11
-rw-r--r--tools/testing/selftests/mm/charge_reserved_hugetlb.sh (renamed from tools/testing/selftests/vm/charge_reserved_hugetlb.sh)0
-rw-r--r--tools/testing/selftests/mm/check_config.sh (renamed from tools/testing/selftests/vm/check_config.sh)0
-rw-r--r--tools/testing/selftests/mm/compaction_test.c (renamed from tools/testing/selftests/vm/compaction_test.c)0
-rw-r--r--tools/testing/selftests/mm/config (renamed from tools/testing/selftests/vm/config)0
-rw-r--r--tools/testing/selftests/mm/cow.c (renamed from tools/testing/selftests/vm/cow.c)231
-rw-r--r--tools/testing/selftests/mm/gup_test.c (renamed from tools/testing/selftests/vm/gup_test.c)0
-rw-r--r--tools/testing/selftests/mm/hmm-tests.c (renamed from tools/testing/selftests/vm/hmm-tests.c)0
-rw-r--r--tools/testing/selftests/mm/hugepage-mmap.c (renamed from tools/testing/selftests/vm/hugepage-mmap.c)0
-rw-r--r--tools/testing/selftests/mm/hugepage-mremap.c (renamed from tools/testing/selftests/vm/hugepage-mremap.c)0
-rw-r--r--tools/testing/selftests/mm/hugepage-shm.c (renamed from tools/testing/selftests/vm/hugepage-shm.c)0
-rw-r--r--tools/testing/selftests/mm/hugepage-vmemmap.c (renamed from tools/testing/selftests/vm/hugepage-vmemmap.c)0
-rw-r--r--tools/testing/selftests/mm/hugetlb-madvise.c (renamed from tools/testing/selftests/vm/hugetlb-madvise.c)0
-rw-r--r--tools/testing/selftests/mm/hugetlb_reparenting_test.sh (renamed from tools/testing/selftests/vm/hugetlb_reparenting_test.sh)0
-rw-r--r--tools/testing/selftests/mm/khugepaged.c (renamed from tools/testing/selftests/vm/khugepaged.c)0
-rw-r--r--tools/testing/selftests/mm/ksm_functional_tests.c (renamed from tools/testing/selftests/vm/ksm_functional_tests.c)2
-rw-r--r--tools/testing/selftests/mm/ksm_tests.c (renamed from tools/testing/selftests/vm/ksm_tests.c)0
-rw-r--r--tools/testing/selftests/mm/madv_populate.c (renamed from tools/testing/selftests/vm/madv_populate.c)0
-rw-r--r--tools/testing/selftests/mm/map_fixed_noreplace.c (renamed from tools/testing/selftests/vm/map_fixed_noreplace.c)0
-rw-r--r--tools/testing/selftests/mm/map_hugetlb.c (renamed from tools/testing/selftests/vm/map_hugetlb.c)0
-rw-r--r--tools/testing/selftests/mm/map_populate.c (renamed from tools/testing/selftests/vm/map_populate.c)0
-rw-r--r--tools/testing/selftests/mm/mdwe_test.c196
-rw-r--r--tools/testing/selftests/mm/memfd_secret.c (renamed from tools/testing/selftests/vm/memfd_secret.c)0
-rw-r--r--tools/testing/selftests/mm/migration.c (renamed from tools/testing/selftests/vm/migration.c)0
-rw-r--r--tools/testing/selftests/mm/mlock-random-test.c (renamed from tools/testing/selftests/vm/mlock-random-test.c)0
-rw-r--r--tools/testing/selftests/mm/mlock2-tests.c (renamed from tools/testing/selftests/vm/mlock2-tests.c)0
-rw-r--r--tools/testing/selftests/mm/mlock2.h (renamed from tools/testing/selftests/vm/mlock2.h)0
-rw-r--r--tools/testing/selftests/mm/mrelease_test.c (renamed from tools/testing/selftests/vm/mrelease_test.c)0
-rw-r--r--tools/testing/selftests/mm/mremap_dontunmap.c (renamed from tools/testing/selftests/vm/mremap_dontunmap.c)0
-rw-r--r--tools/testing/selftests/mm/mremap_test.c (renamed from tools/testing/selftests/vm/mremap_test.c)119
-rw-r--r--tools/testing/selftests/mm/on-fault-limit.c (renamed from tools/testing/selftests/vm/on-fault-limit.c)0
-rw-r--r--tools/testing/selftests/mm/pkey-helpers.h (renamed from tools/testing/selftests/vm/pkey-helpers.h)0
-rw-r--r--tools/testing/selftests/mm/pkey-powerpc.h (renamed from tools/testing/selftests/vm/pkey-powerpc.h)0
-rw-r--r--tools/testing/selftests/mm/pkey-x86.h (renamed from tools/testing/selftests/vm/pkey-x86.h)0
-rw-r--r--tools/testing/selftests/mm/protection_keys.c (renamed from tools/testing/selftests/vm/protection_keys.c)0
-rw-r--r--[-rwxr-xr-x]tools/testing/selftests/mm/run_vmtests.sh (renamed from tools/testing/selftests/vm/run_vmtests.sh)0
-rw-r--r--tools/testing/selftests/mm/settings (renamed from tools/testing/selftests/vm/settings)0
-rw-r--r--tools/testing/selftests/mm/soft-dirty.c (renamed from tools/testing/selftests/vm/soft-dirty.c)0
-rw-r--r--tools/testing/selftests/mm/split_huge_page_test.c (renamed from tools/testing/selftests/vm/split_huge_page_test.c)0
-rw-r--r--[-rwxr-xr-x]tools/testing/selftests/mm/test_hmm.sh (renamed from tools/testing/selftests/vm/test_hmm.sh)0
-rw-r--r--[-rwxr-xr-x]tools/testing/selftests/mm/test_vmalloc.sh (renamed from tools/testing/selftests/vm/test_vmalloc.sh)0
-rw-r--r--tools/testing/selftests/mm/thuge-gen.c (renamed from tools/testing/selftests/vm/thuge-gen.c)0
-rw-r--r--tools/testing/selftests/mm/transhuge-stress.c (renamed from tools/testing/selftests/vm/transhuge-stress.c)0
-rw-r--r--tools/testing/selftests/mm/userfaultfd.c (renamed from tools/testing/selftests/vm/userfaultfd.c)0
-rw-r--r--tools/testing/selftests/mm/util.h (renamed from tools/testing/selftests/vm/util.h)0
-rw-r--r--tools/testing/selftests/mm/va_128TBswitch.c (renamed from tools/testing/selftests/vm/va_128TBswitch.c)0
-rw-r--r--[-rwxr-xr-x]tools/testing/selftests/mm/va_128TBswitch.sh (renamed from tools/testing/selftests/vm/va_128TBswitch.sh)0
-rw-r--r--tools/testing/selftests/mm/virtual_address_range.c (renamed from tools/testing/selftests/vm/virtual_address_range.c)0
-rw-r--r--tools/testing/selftests/mm/vm_util.c (renamed from tools/testing/selftests/vm/vm_util.c)0
-rw-r--r--tools/testing/selftests/mm/vm_util.h (renamed from tools/testing/selftests/vm/vm_util.h)0
-rw-r--r--tools/testing/selftests/mm/write_hugetlb_memory.sh (renamed from tools/testing/selftests/vm/write_hugetlb_memory.sh)0
-rw-r--r--tools/testing/selftests/mm/write_to_hugetlbfs.c (renamed from tools/testing/selftests/vm/write_to_hugetlbfs.c)0
-rw-r--r--tools/testing/selftests/mount_setattr/Makefile4
-rw-r--r--tools/testing/selftests/mount_setattr/mount_setattr_test.c8
-rw-r--r--tools/testing/selftests/move_mount_set_group/Makefile2
-rw-r--r--tools/testing/selftests/net/.gitignore1
-rw-r--r--tools/testing/selftests/net/Makefile1
-rw-r--r--tools/testing/selftests/net/bind_wildcard.c114
-rwxr-xr-xtools/testing/selftests/net/devlink_port_split.py36
-rwxr-xr-xtools/testing/selftests/net/fib_tests.sh96
-rwxr-xr-xtools/testing/selftests/net/mptcp/userspace_pm.sh2
-rwxr-xr-xtools/testing/selftests/net/rps_default_mask.sh1
-rwxr-xr-xtools/testing/selftests/netfilter/nft_nat.sh2
-rwxr-xr-xtools/testing/selftests/netfilter/rpath.sh32
-rw-r--r--tools/testing/selftests/nolibc/Makefile17
-rw-r--r--tools/testing/selftests/nolibc/nolibc-test.c30
-rw-r--r--tools/testing/selftests/perf_events/Makefile2
-rw-r--r--tools/testing/selftests/pid_namespace/Makefile2
-rw-r--r--tools/testing/selftests/pidfd/Makefile2
-rw-r--r--tools/testing/selftests/powerpc/dscr/dscr.h34
-rw-r--r--tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c25
-rw-r--r--tools/testing/selftests/powerpc/include/utils.h20
-rw-r--r--tools/testing/selftests/powerpc/nx-gzip/gzfht_test.c52
-rw-r--r--tools/testing/selftests/powerpc/pmu/lib.c34
-rw-r--r--tools/testing/selftests/powerpc/ptrace/Makefile2
-rw-r--r--tools/testing/selftests/powerpc/ptrace/core-pkey.c28
-rw-r--r--tools/testing/selftests/powerpc/security/Makefile2
-rw-r--r--tools/testing/selftests/powerpc/security/entry_flush.c12
-rw-r--r--tools/testing/selftests/powerpc/security/rfi_flush.c12
-rw-r--r--tools/testing/selftests/powerpc/security/uaccess_flush.c18
-rw-r--r--tools/testing/selftests/powerpc/syscalls/Makefile4
-rw-r--r--tools/testing/selftests/powerpc/syscalls/rtas_filter.c81
-rw-r--r--tools/testing/selftests/powerpc/tm/Makefile2
-rw-r--r--tools/testing/selftests/powerpc/utils.c412
-rw-r--r--tools/testing/selftests/prctl/disable-tsc-ctxt-sw-stress-test.c2
-rw-r--r--tools/testing/selftests/prctl/disable-tsc-on-off-stress-test.c2
-rw-r--r--tools/testing/selftests/ptp/Makefile9
-rw-r--r--tools/testing/selftests/ptrace/Makefile2
-rw-r--r--tools/testing/selftests/rcutorture/bin/functions.sh6
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/mkinitrd.sh2
-rw-r--r--tools/testing/selftests/rseq/Makefile2
-rw-r--r--tools/testing/selftests/sched/Makefile2
-rw-r--r--tools/testing/selftests/sched/cs_prctl_test.c15
-rw-r--r--tools/testing/selftests/seccomp/Makefile2
-rw-r--r--tools/testing/selftests/seccomp/seccomp_bpf.c6
-rw-r--r--tools/testing/selftests/sigaltstack/current_stack_pointer.h23
-rw-r--r--tools/testing/selftests/sigaltstack/sas.c7
-rw-r--r--tools/testing/selftests/sync/Makefile2
-rw-r--r--tools/testing/selftests/tdx/Makefile2
-rw-r--r--tools/testing/selftests/tdx/tdx_guest_test.c2
-rw-r--r--tools/testing/selftests/tpm2/tpm2.py2
-rw-r--r--tools/testing/selftests/user_events/Makefile10
-rw-r--r--tools/testing/selftests/x86/Makefile2
-rw-r--r--tools/testing/selftests/x86/amx.c108
-rw-r--r--tools/testing/vsock/vsock_test.c208
-rw-r--r--tools/tracing/latency/latency-collector.c2
-rw-r--r--tools/tracing/rtla/Makefile2
-rw-r--r--tools/tracing/rtla/src/osnoise.c117
-rw-r--r--tools/tracing/rtla/src/osnoise.h7
-rw-r--r--tools/tracing/rtla/src/osnoise_hist.c9
-rw-r--r--tools/tracing/rtla/src/osnoise_top.c84
-rw-r--r--tools/tracing/rtla/src/rtla.c4
-rw-r--r--tools/tracing/rtla/src/timerlat_aa.c990
-rw-r--r--tools/tracing/rtla/src/timerlat_aa.h12
-rw-r--r--tools/tracing/rtla/src/timerlat_top.c46
-rw-r--r--tools/tracing/rtla/src/utils.h3
-rw-r--r--tools/verification/rv/src/in_kernel.c2
-rw-r--r--tools/virtio/.gitignore1
-rw-r--r--tools/virtio/Makefile2
-rw-r--r--tools/virtio/virtio-trace/README2
471 files changed, 11283 insertions, 3465 deletions
diff --git a/tools/arch/arm64/include/uapi/asm/kvm.h b/tools/arch/arm64/include/uapi/asm/kvm.h
index a7a857f1784d..f8129c624b07 100644
--- a/tools/arch/arm64/include/uapi/asm/kvm.h
+++ b/tools/arch/arm64/include/uapi/asm/kvm.h
@@ -109,6 +109,7 @@ struct kvm_regs {
#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 */
+#define KVM_ARM_VCPU_HAS_EL2 7 /* Support nested virtualization */
struct kvm_vcpu_init {
__u32 target;
diff --git a/tools/arch/loongarch/include/uapi/asm/bitsperlong.h b/tools/arch/loongarch/include/uapi/asm/bitsperlong.h
new file mode 100644
index 000000000000..d4e32b3d4843
--- /dev/null
+++ b/tools/arch/loongarch/include/uapi/asm/bitsperlong.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __ASM_LOONGARCH_BITSPERLONG_H
+#define __ASM_LOONGARCH_BITSPERLONG_H
+
+#define __BITS_PER_LONG (__SIZEOF_POINTER__ * 8)
+
+#include <asm-generic/bitsperlong.h>
+
+#endif /* __ASM_LOONGARCH_BITSPERLONG_H */
diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h
index b70111a75688..b89005819cd5 100644
--- a/tools/arch/x86/include/asm/cpufeatures.h
+++ b/tools/arch/x86/include/asm/cpufeatures.h
@@ -13,7 +13,7 @@
/*
* Defines x86 CPU feature bits
*/
-#define NCAPINTS 20 /* N 32-bit words worth of info */
+#define NCAPINTS 21 /* N 32-bit words worth of info */
#define NBUGINTS 1 /* N 32-bit bug flags */
/*
diff --git a/tools/arch/x86/include/asm/disabled-features.h b/tools/arch/x86/include/asm/disabled-features.h
index c44b56f7ffba..5dfa4fb76f4b 100644
--- a/tools/arch/x86/include/asm/disabled-features.h
+++ b/tools/arch/x86/include/asm/disabled-features.h
@@ -124,6 +124,7 @@
#define DISABLED_MASK17 0
#define DISABLED_MASK18 0
#define DISABLED_MASK19 0
-#define DISABLED_MASK_CHECK BUILD_BUG_ON_ZERO(NCAPINTS != 20)
+#define DISABLED_MASK20 0
+#define DISABLED_MASK_CHECK BUILD_BUG_ON_ZERO(NCAPINTS != 21)
#endif /* _ASM_X86_DISABLED_FEATURES_H */
diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h
index 37ff47552bcb..ad35355ee43e 100644
--- a/tools/arch/x86/include/asm/msr-index.h
+++ b/tools/arch/x86/include/asm/msr-index.h
@@ -25,6 +25,7 @@
#define _EFER_SVME 12 /* Enable virtualization */
#define _EFER_LMSLE 13 /* Long Mode Segment Limit Enable */
#define _EFER_FFXSR 14 /* Enable Fast FXSAVE/FXRSTOR */
+#define _EFER_AUTOIBRS 21 /* Enable Automatic IBRS */
#define EFER_SCE (1<<_EFER_SCE)
#define EFER_LME (1<<_EFER_LME)
@@ -33,6 +34,7 @@
#define EFER_SVME (1<<_EFER_SVME)
#define EFER_LMSLE (1<<_EFER_LMSLE)
#define EFER_FFXSR (1<<_EFER_FFXSR)
+#define EFER_AUTOIBRS (1<<_EFER_AUTOIBRS)
/* Intel MSRs. Some also available on other CPUs */
@@ -49,6 +51,10 @@
#define SPEC_CTRL_RRSBA_DIS_S_SHIFT 6 /* Disable RRSBA behavior */
#define SPEC_CTRL_RRSBA_DIS_S BIT(SPEC_CTRL_RRSBA_DIS_S_SHIFT)
+/* A mask for bits which the kernel toggles when controlling mitigations */
+#define SPEC_CTRL_MITIGATIONS_MASK (SPEC_CTRL_IBRS | SPEC_CTRL_STIBP | SPEC_CTRL_SSBD \
+ | SPEC_CTRL_RRSBA_DIS_S)
+
#define MSR_IA32_PRED_CMD 0x00000049 /* Prediction Command */
#define PRED_CMD_IBPB BIT(0) /* Indirect Branch Prediction Barrier */
@@ -189,6 +195,9 @@
#define MSR_TURBO_RATIO_LIMIT1 0x000001ae
#define MSR_TURBO_RATIO_LIMIT2 0x000001af
+#define MSR_SNOOP_RSP_0 0x00001328
+#define MSR_SNOOP_RSP_1 0x00001329
+
#define MSR_LBR_SELECT 0x000001c8
#define MSR_LBR_TOS 0x000001c9
@@ -566,6 +575,26 @@
#define MSR_AMD64_SEV_ES_ENABLED BIT_ULL(MSR_AMD64_SEV_ES_ENABLED_BIT)
#define MSR_AMD64_SEV_SNP_ENABLED BIT_ULL(MSR_AMD64_SEV_SNP_ENABLED_BIT)
+/* SNP feature bits enabled by the hypervisor */
+#define MSR_AMD64_SNP_VTOM BIT_ULL(3)
+#define MSR_AMD64_SNP_REFLECT_VC BIT_ULL(4)
+#define MSR_AMD64_SNP_RESTRICTED_INJ BIT_ULL(5)
+#define MSR_AMD64_SNP_ALT_INJ BIT_ULL(6)
+#define MSR_AMD64_SNP_DEBUG_SWAP BIT_ULL(7)
+#define MSR_AMD64_SNP_PREVENT_HOST_IBS BIT_ULL(8)
+#define MSR_AMD64_SNP_BTB_ISOLATION BIT_ULL(9)
+#define MSR_AMD64_SNP_VMPL_SSS BIT_ULL(10)
+#define MSR_AMD64_SNP_SECURE_TSC BIT_ULL(11)
+#define MSR_AMD64_SNP_VMGEXIT_PARAM BIT_ULL(12)
+#define MSR_AMD64_SNP_IBS_VIRT BIT_ULL(14)
+#define MSR_AMD64_SNP_VMSA_REG_PROTECTION BIT_ULL(16)
+#define MSR_AMD64_SNP_SMT_PROTECTION BIT_ULL(17)
+
+/* SNP feature bits reserved for future use. */
+#define MSR_AMD64_SNP_RESERVED_BIT13 BIT_ULL(13)
+#define MSR_AMD64_SNP_RESERVED_BIT15 BIT_ULL(15)
+#define MSR_AMD64_SNP_RESERVED_MASK GENMASK_ULL(63, 18)
+
#define MSR_AMD64_VIRT_SPEC_CTRL 0xc001011f
/* AMD Collaborative Processor Performance Control MSRs */
@@ -1061,6 +1090,8 @@
/* - AMD: */
#define MSR_IA32_MBA_BW_BASE 0xc0000200
+#define MSR_IA32_SMBA_BW_BASE 0xc0000280
+#define MSR_IA32_EVT_CFG_BASE 0xc0000400
/* MSR_IA32_VMX_MISC bits */
#define MSR_IA32_VMX_MISC_INTEL_PT (1ULL << 14)
diff --git a/tools/arch/x86/include/asm/orc_types.h b/tools/arch/x86/include/asm/orc_types.h
index 5a2baf28a1dc..1343a62106de 100644
--- a/tools/arch/x86/include/asm/orc_types.h
+++ b/tools/arch/x86/include/asm/orc_types.h
@@ -57,12 +57,14 @@ struct orc_entry {
unsigned sp_reg:4;
unsigned bp_reg:4;
unsigned type:2;
+ unsigned signal:1;
unsigned end:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
unsigned bp_reg:4;
unsigned sp_reg:4;
- unsigned unused:5;
+ unsigned unused:4;
unsigned end:1;
+ unsigned signal:1;
unsigned type:2;
#endif
} __packed;
diff --git a/tools/arch/x86/include/asm/required-features.h b/tools/arch/x86/include/asm/required-features.h
index aff774775c67..7ba1726b71c7 100644
--- a/tools/arch/x86/include/asm/required-features.h
+++ b/tools/arch/x86/include/asm/required-features.h
@@ -98,6 +98,7 @@
#define REQUIRED_MASK17 0
#define REQUIRED_MASK18 0
#define REQUIRED_MASK19 0
-#define REQUIRED_MASK_CHECK BUILD_BUG_ON_ZERO(NCAPINTS != 20)
+#define REQUIRED_MASK20 0
+#define REQUIRED_MASK_CHECK BUILD_BUG_ON_ZERO(NCAPINTS != 21)
#endif /* _ASM_X86_REQUIRED_FEATURES_H */
diff --git a/tools/arch/x86/include/uapi/asm/kvm.h b/tools/arch/x86/include/uapi/asm/kvm.h
index e48deab8901d..7f467fe05d42 100644
--- a/tools/arch/x86/include/uapi/asm/kvm.h
+++ b/tools/arch/x86/include/uapi/asm/kvm.h
@@ -9,6 +9,7 @@
#include <linux/types.h>
#include <linux/ioctl.h>
+#include <linux/stddef.h>
#define KVM_PIO_PAGE_OFFSET 1
#define KVM_COALESCED_MMIO_PAGE_OFFSET 2
@@ -507,8 +508,8 @@ struct kvm_nested_state {
* KVM_{GET,PUT}_NESTED_STATE ioctl values.
*/
union {
- struct kvm_vmx_nested_state_data vmx[0];
- struct kvm_svm_nested_state_data svm[0];
+ __DECLARE_FLEX_ARRAY(struct kvm_vmx_nested_state_data, vmx);
+ __DECLARE_FLEX_ARRAY(struct kvm_svm_nested_state_data, svm);
} data;
};
@@ -525,6 +526,35 @@ struct kvm_pmu_event_filter {
#define KVM_PMU_EVENT_ALLOW 0
#define KVM_PMU_EVENT_DENY 1
+#define KVM_PMU_EVENT_FLAG_MASKED_EVENTS BIT(0)
+#define KVM_PMU_EVENT_FLAGS_VALID_MASK (KVM_PMU_EVENT_FLAG_MASKED_EVENTS)
+
+/*
+ * Masked event layout.
+ * Bits Description
+ * ---- -----------
+ * 7:0 event select (low bits)
+ * 15:8 umask match
+ * 31:16 unused
+ * 35:32 event select (high bits)
+ * 36:54 unused
+ * 55 exclude bit
+ * 63:56 umask mask
+ */
+
+#define KVM_PMU_ENCODE_MASKED_ENTRY(event_select, mask, match, exclude) \
+ (((event_select) & 0xFFULL) | (((event_select) & 0XF00ULL) << 24) | \
+ (((mask) & 0xFFULL) << 56) | \
+ (((match) & 0xFFULL) << 8) | \
+ ((__u64)(!!(exclude)) << 55))
+
+#define KVM_PMU_MASKED_ENTRY_EVENT_SELECT \
+ (GENMASK_ULL(7, 0) | GENMASK_ULL(35, 32))
+#define KVM_PMU_MASKED_ENTRY_UMASK_MASK (GENMASK_ULL(63, 56))
+#define KVM_PMU_MASKED_ENTRY_UMASK_MATCH (GENMASK_ULL(15, 8))
+#define KVM_PMU_MASKED_ENTRY_EXCLUDE (BIT_ULL(55))
+#define KVM_PMU_MASKED_ENTRY_UMASK_MASK_SHIFT (56)
+
/* for KVM_{GET,SET,HAS}_DEVICE_ATTR */
#define KVM_VCPU_TSC_CTRL 0 /* control group for the timestamp counter (TSC) */
#define KVM_VCPU_TSC_OFFSET 0 /* attribute for the TSC offset */
diff --git a/tools/arch/x86/include/uapi/asm/svm.h b/tools/arch/x86/include/uapi/asm/svm.h
index f69c168391aa..80e1df482337 100644
--- a/tools/arch/x86/include/uapi/asm/svm.h
+++ b/tools/arch/x86/include/uapi/asm/svm.h
@@ -116,6 +116,12 @@
#define SVM_VMGEXIT_AP_CREATE 1
#define SVM_VMGEXIT_AP_DESTROY 2
#define SVM_VMGEXIT_HV_FEATURES 0x8000fffd
+#define SVM_VMGEXIT_TERM_REQUEST 0x8000fffe
+#define SVM_VMGEXIT_TERM_REASON(reason_set, reason_code) \
+ /* SW_EXITINFO1[3:0] */ \
+ (((((u64)reason_set) & 0xf)) | \
+ /* SW_EXITINFO1[11:4] */ \
+ ((((u64)reason_code) & 0xff) << 4))
#define SVM_VMGEXIT_UNSUPPORTED_EVENT 0x8000ffff
/* Exit code reserved for hypervisor/software use */
diff --git a/tools/arch/x86/include/uapi/asm/unistd_32.h b/tools/arch/x86/include/uapi/asm/unistd_32.h
index 60a89dba01b6..2712d5e03e2e 100644
--- a/tools/arch/x86/include/uapi/asm/unistd_32.h
+++ b/tools/arch/x86/include/uapi/asm/unistd_32.h
@@ -1,16 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __NR_perf_event_open
-# define __NR_perf_event_open 336
+#ifndef __NR_execve
+#define __NR_execve 11
#endif
-#ifndef __NR_futex
-# define __NR_futex 240
+#ifndef __NR_getppid
+#define __NR_getppid 64
+#endif
+#ifndef __NR_getpgid
+#define __NR_getpgid 132
#endif
#ifndef __NR_gettid
-# define __NR_gettid 224
+#define __NR_gettid 224
+#endif
+#ifndef __NR_futex
+#define __NR_futex 240
#endif
#ifndef __NR_getcpu
-# define __NR_getcpu 318
+#define __NR_getcpu 318
+#endif
+#ifndef __NR_perf_event_open
+#define __NR_perf_event_open 336
#endif
#ifndef __NR_setns
-# define __NR_setns 346
+#define __NR_setns 346
#endif
diff --git a/tools/arch/x86/include/uapi/asm/unistd_64.h b/tools/arch/x86/include/uapi/asm/unistd_64.h
index cb52a3a8b8fc..a6f7fe84d4df 100644
--- a/tools/arch/x86/include/uapi/asm/unistd_64.h
+++ b/tools/arch/x86/include/uapi/asm/unistd_64.h
@@ -1,16 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __NR_perf_event_open
-# define __NR_perf_event_open 298
+#ifndef __NR_execve
+#define __NR_execve 59
#endif
-#ifndef __NR_futex
-# define __NR_futex 202
+#ifndef __NR_getppid
+#define __NR_getppid 110
+#endif
+#ifndef __NR_getpgid
+#define __NR_getpgid 121
#endif
#ifndef __NR_gettid
-# define __NR_gettid 186
+#define __NR_gettid 186
#endif
-#ifndef __NR_getcpu
-# define __NR_getcpu 309
+#ifndef __NR_futex
+#define __NR_futex 202
+#endif
+#ifndef __NR_perf_event_open
+#define __NR_perf_event_open 298
#endif
#ifndef __NR_setns
#define __NR_setns 308
#endif
+#ifndef __NR_getcpu
+#define __NR_getcpu 309
+#endif
diff --git a/tools/arch/x86/lib/memcpy_64.S b/tools/arch/x86/lib/memcpy_64.S
index 5418e2f99834..a91ac666f758 100644
--- a/tools/arch/x86/lib/memcpy_64.S
+++ b/tools/arch/x86/lib/memcpy_64.S
@@ -7,7 +7,7 @@
#include <asm/alternative.h>
#include <asm/export.h>
-.pushsection .noinstr.text, "ax"
+.section .noinstr.text, "ax"
/*
* We build a jump to memcpy_orig by default which gets NOPped out on
@@ -42,7 +42,7 @@ SYM_TYPED_FUNC_START(__memcpy)
SYM_FUNC_END(__memcpy)
EXPORT_SYMBOL(__memcpy)
-SYM_FUNC_ALIAS_WEAK(memcpy, __memcpy)
+SYM_FUNC_ALIAS(memcpy, __memcpy)
EXPORT_SYMBOL(memcpy)
/*
@@ -183,4 +183,3 @@ SYM_FUNC_START_LOCAL(memcpy_orig)
RET
SYM_FUNC_END(memcpy_orig)
-.popsection
diff --git a/tools/arch/x86/lib/memset_64.S b/tools/arch/x86/lib/memset_64.S
index fc9ffd3ff3b2..6143b1a6fa2c 100644
--- a/tools/arch/x86/lib/memset_64.S
+++ b/tools/arch/x86/lib/memset_64.S
@@ -6,6 +6,8 @@
#include <asm/alternative.h>
#include <asm/export.h>
+.section .noinstr.text, "ax"
+
/*
* ISO C memset - set a memory block to a byte value. This function uses fast
* string to get better performance than the original function. The code is
@@ -43,7 +45,7 @@ SYM_FUNC_START(__memset)
SYM_FUNC_END(__memset)
EXPORT_SYMBOL(__memset)
-SYM_FUNC_ALIAS_WEAK(memset, __memset)
+SYM_FUNC_ALIAS(memset, __memset)
EXPORT_SYMBOL(memset)
/*
diff --git a/tools/bootconfig/scripts/ftrace2bconf.sh b/tools/bootconfig/scripts/ftrace2bconf.sh
index 6183b36c6846..1603801cf126 100755
--- a/tools/bootconfig/scripts/ftrace2bconf.sh
+++ b/tools/bootconfig/scripts/ftrace2bconf.sh
@@ -93,7 +93,7 @@ referred_vars() {
}
event_is_enabled() { # enable-file
- test -f $1 & grep -q "1" $1
+ test -f $1 && grep -q "1" $1
}
per_event_options() { # event-dir
diff --git a/tools/bootconfig/test-bootconfig.sh b/tools/bootconfig/test-bootconfig.sh
index f68e2e9eef8b..a2c484c243f5 100755
--- a/tools/bootconfig/test-bootconfig.sh
+++ b/tools/bootconfig/test-bootconfig.sh
@@ -87,10 +87,14 @@ xfail grep -i "error" $OUTFILE
echo "Max node number check"
-echo -n > $TEMPCONF
-for i in `seq 1 1024` ; do
- echo "node$i" >> $TEMPCONF
-done
+awk '
+BEGIN {
+ for (i = 0; i < 26; i += 1)
+ printf("%c\n", 65 + i % 26)
+ for (i = 26; i < 8192; i += 1)
+ printf("%c%c%c\n", 65 + i % 26, 65 + (i / 26) % 26, 65 + (i / 26 / 26))
+}
+' > $TEMPCONF
xpass $BOOTCONF -a $TEMPCONF $INITRD
echo "badnode" >> $TEMPCONF
diff --git a/tools/build/Makefile.build b/tools/build/Makefile.build
index 715092fc6a23..89430338a3d9 100644
--- a/tools/build/Makefile.build
+++ b/tools/build/Makefile.build
@@ -53,6 +53,7 @@ build-file := $(dir)/Build
quiet_cmd_flex = FLEX $@
quiet_cmd_bison = BISON $@
+quiet_cmd_test = TEST $@
# Create directory unless it exists
quiet_cmd_mkdir = MKDIR $(dir $@)
diff --git a/tools/cgroup/memcg_shrinker.py b/tools/cgroup/memcg_shrinker.py
index 706ab27666a4..e81c3017ada9 100644
--- a/tools/cgroup/memcg_shrinker.py
+++ b/tools/cgroup/memcg_shrinker.py
@@ -5,7 +5,6 @@
import os
import argparse
-import sys
def scan_cgroups(cgroup_root):
@@ -44,7 +43,7 @@ def main():
cgroups = scan_cgroups("/sys/fs/cgroup/")
shrinkers = scan_shrinkers("/sys/kernel/debug/shrinker/")
- shrinkers = sorted(shrinkers, reverse = True, key = lambda x: x[0])
+ shrinkers.sort(reverse = True, key = lambda x: x[0])
n = 0
for s in shrinkers:
diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c
index 8d35893b2fa8..6a00a6eecaef 100644
--- a/tools/iio/iio_utils.c
+++ b/tools/iio/iio_utils.c
@@ -264,6 +264,7 @@ int iioutils_get_param_float(float *output, const char *param_name,
if (fscanf(sysfsfp, "%f", output) != 1)
ret = errno ? -errno : -ENODATA;
+ fclose(sysfsfp);
break;
}
error_free_filename:
@@ -345,9 +346,9 @@ int build_channel_array(const char *device_dir, int buffer_idx,
}
sysfsfp = fopen(filename, "r");
+ free(filename);
if (!sysfsfp) {
ret = -errno;
- free(filename);
goto error_close_dir;
}
@@ -357,7 +358,6 @@ int build_channel_array(const char *device_dir, int buffer_idx,
if (fclose(sysfsfp))
perror("build_channel_array(): Failed to close file");
- free(filename);
goto error_close_dir;
}
if (ret == 1)
@@ -365,11 +365,9 @@ int build_channel_array(const char *device_dir, int buffer_idx,
if (fclose(sysfsfp)) {
ret = -errno;
- free(filename);
goto error_close_dir;
}
- free(filename);
}
*ci_array = malloc(sizeof(**ci_array) * (*counter));
@@ -395,9 +393,9 @@ int build_channel_array(const char *device_dir, int buffer_idx,
}
sysfsfp = fopen(filename, "r");
+ free(filename);
if (!sysfsfp) {
ret = -errno;
- free(filename);
count--;
goto error_cleanup_array;
}
@@ -405,20 +403,17 @@ int build_channel_array(const char *device_dir, int buffer_idx,
errno = 0;
if (fscanf(sysfsfp, "%i", &current_enabled) != 1) {
ret = errno ? -errno : -ENODATA;
- free(filename);
count--;
goto error_cleanup_array;
}
if (fclose(sysfsfp)) {
ret = -errno;
- free(filename);
count--;
goto error_cleanup_array;
}
if (!current_enabled) {
- free(filename);
count--;
continue;
}
@@ -429,7 +424,6 @@ int build_channel_array(const char *device_dir, int buffer_idx,
strlen(ent->d_name) -
strlen("_en"));
if (!current->name) {
- free(filename);
ret = -ENOMEM;
count--;
goto error_cleanup_array;
@@ -439,7 +433,6 @@ int build_channel_array(const char *device_dir, int buffer_idx,
ret = iioutils_break_up_name(current->name,
&current->generic_name);
if (ret) {
- free(filename);
free(current->name);
count--;
goto error_cleanup_array;
@@ -450,17 +443,16 @@ int build_channel_array(const char *device_dir, int buffer_idx,
scan_el_dir,
current->name);
if (ret < 0) {
- free(filename);
ret = -ENOMEM;
goto error_cleanup_array;
}
sysfsfp = fopen(filename, "r");
+ free(filename);
if (!sysfsfp) {
ret = -errno;
- fprintf(stderr, "failed to open %s\n",
- filename);
- free(filename);
+ fprintf(stderr, "failed to open %s/%s_index\n",
+ scan_el_dir, current->name);
goto error_cleanup_array;
}
@@ -470,17 +462,14 @@ int build_channel_array(const char *device_dir, int buffer_idx,
if (fclose(sysfsfp))
perror("build_channel_array(): Failed to close file");
- free(filename);
goto error_cleanup_array;
}
if (fclose(sysfsfp)) {
ret = -errno;
- free(filename);
goto error_cleanup_array;
}
- free(filename);
/* Find the scale */
ret = iioutils_get_param_float(&current->scale,
"scale",
diff --git a/tools/include/linux/bits.h b/tools/include/linux/bits.h
index 87d112650dfb..7c0cf5031abe 100644
--- a/tools/include/linux/bits.h
+++ b/tools/include/linux/bits.h
@@ -6,7 +6,6 @@
#include <vdso/bits.h>
#include <asm/bitsperlong.h>
-#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) (ULL(1) << ((nr) % BITS_PER_LONG_LONG))
diff --git a/tools/include/linux/objtool.h b/tools/include/linux/objtool.h
index 62c54ffbeeaa..9ac3df3fccf0 100644
--- a/tools/include/linux/objtool.h
+++ b/tools/include/linux/objtool.h
@@ -15,6 +15,7 @@ struct unwind_hint {
s16 sp_offset;
u8 sp_reg;
u8 type;
+ u8 signal;
u8 end;
};
#endif
@@ -49,7 +50,7 @@ struct unwind_hint {
#ifndef __ASSEMBLY__
-#define UNWIND_HINT(sp_reg, sp_offset, type, end) \
+#define UNWIND_HINT(sp_reg, sp_offset, type, signal, end) \
"987: \n\t" \
".pushsection .discard.unwind_hints\n\t" \
/* struct unwind_hint */ \
@@ -57,6 +58,7 @@ struct unwind_hint {
".short " __stringify(sp_offset) "\n\t" \
".byte " __stringify(sp_reg) "\n\t" \
".byte " __stringify(type) "\n\t" \
+ ".byte " __stringify(signal) "\n\t" \
".byte " __stringify(end) "\n\t" \
".balign 4 \n\t" \
".popsection\n\t"
@@ -129,7 +131,7 @@ struct unwind_hint {
* the debuginfo as necessary. It will also warn if it sees any
* inconsistencies.
*/
-.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 end=0
+.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 end=0
.Lunwind_hint_ip_\@:
.pushsection .discard.unwind_hints
/* struct unwind_hint */
@@ -137,6 +139,7 @@ struct unwind_hint {
.short \sp_offset
.byte \sp_reg
.byte \type
+ .byte \signal
.byte \end
.balign 4
.popsection
@@ -174,7 +177,7 @@ struct unwind_hint {
#ifndef __ASSEMBLY__
-#define UNWIND_HINT(sp_reg, sp_offset, type, end) \
+#define UNWIND_HINT(sp_reg, sp_offset, type, signal, end) \
"\n\t"
#define STACK_FRAME_NON_STANDARD(func)
#define STACK_FRAME_NON_STANDARD_FP(func)
@@ -182,7 +185,7 @@ struct unwind_hint {
#define ASM_REACHABLE
#else
#define ANNOTATE_INTRA_FUNCTION_CALL
-.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 end=0
+.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 end=0
.endm
.macro STACK_FRAME_NON_STANDARD func:req
.endm
diff --git a/tools/include/nolibc/arch-aarch64.h b/tools/include/nolibc/arch-aarch64.h
index f68baf8f395f..383baddef701 100644
--- a/tools/include/nolibc/arch-aarch64.h
+++ b/tools/include/nolibc/arch-aarch64.h
@@ -7,18 +7,6 @@
#ifndef _NOLIBC_ARCH_AARCH64_H
#define _NOLIBC_ARCH_AARCH64_H
-/* O_* macros for fcntl/open are architecture-specific */
-#define O_RDONLY 0
-#define O_WRONLY 1
-#define O_RDWR 2
-#define O_CREAT 0x40
-#define O_EXCL 0x80
-#define O_NOCTTY 0x100
-#define O_TRUNC 0x200
-#define O_APPEND 0x400
-#define O_NONBLOCK 0x800
-#define O_DIRECTORY 0x4000
-
/* The struct returned by the newfstatat() syscall. Differs slightly from the
* x86_64's stat one by field ordering, so be careful.
*/
@@ -181,19 +169,31 @@ struct sys_stat_struct {
_arg1; \
})
-/* startup code */
-__asm__ (".section .text\n"
- ".weak _start\n"
- "_start:\n"
- "ldr x0, [sp]\n" // argc (x0) was in the stack
- "add x1, sp, 8\n" // argv (x1) = sp
- "lsl x2, x0, 3\n" // envp (x2) = 8*argc ...
- "add x2, x2, 8\n" // + 8 (skip null)
- "add x2, x2, x1\n" // + argv
- "and sp, x1, -16\n" // sp must be 16-byte aligned in the callee
- "bl main\n" // main() returns the status code, we'll exit with it.
- "mov x8, 93\n" // NR_exit == 93
- "svc #0\n"
- "");
+char **environ __attribute__((weak));
+const unsigned long *_auxv __attribute__((weak));
+/* startup code */
+void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _start(void)
+{
+ __asm__ volatile (
+ "ldr x0, [sp]\n" // argc (x0) was in the stack
+ "add x1, sp, 8\n" // argv (x1) = sp
+ "lsl x2, x0, 3\n" // envp (x2) = 8*argc ...
+ "add x2, x2, 8\n" // + 8 (skip null)
+ "add x2, x2, x1\n" // + argv
+ "adrp x3, environ\n" // x3 = &environ (high bits)
+ "str x2, [x3, #:lo12:environ]\n" // store envp into environ
+ "mov x4, x2\n" // search for auxv (follows NULL after last env)
+ "0:\n"
+ "ldr x5, [x4], 8\n" // x5 = *x4; x4 += 8
+ "cbnz x5, 0b\n" // and stop at NULL after last env
+ "adrp x3, _auxv\n" // x3 = &_auxv (high bits)
+ "str x4, [x3, #:lo12:_auxv]\n" // store x4 into _auxv
+ "and sp, x1, -16\n" // sp must be 16-byte aligned in the callee
+ "bl main\n" // main() returns the status code, we'll exit with it.
+ "mov x8, 93\n" // NR_exit == 93
+ "svc #0\n"
+ );
+ __builtin_unreachable();
+}
#endif // _NOLIBC_ARCH_AARCH64_H
diff --git a/tools/include/nolibc/arch-arm.h b/tools/include/nolibc/arch-arm.h
index f31be8e967d6..42499f23e73c 100644
--- a/tools/include/nolibc/arch-arm.h
+++ b/tools/include/nolibc/arch-arm.h
@@ -7,18 +7,6 @@
#ifndef _NOLIBC_ARCH_ARM_H
#define _NOLIBC_ARCH_ARM_H
-/* O_* macros for fcntl/open are architecture-specific */
-#define O_RDONLY 0
-#define O_WRONLY 1
-#define O_RDWR 2
-#define O_CREAT 0x40
-#define O_EXCL 0x80
-#define O_NOCTTY 0x100
-#define O_TRUNC 0x200
-#define O_APPEND 0x400
-#define O_NONBLOCK 0x800
-#define O_DIRECTORY 0x4000
-
/* The struct returned by the stat() syscall, 32-bit only, the syscall returns
* exactly 56 bytes (stops before the unused array). In big endian, the format
* differs as devices are returned as short only.
@@ -70,20 +58,44 @@ struct sys_stat_struct {
* don't have to experience issues with register constraints.
* - the syscall number is always specified last in order to allow to force
* some registers before (gcc refuses a %-register at the last position).
+ * - in thumb mode without -fomit-frame-pointer, r7 is also used to store the
+ * frame pointer, and we cannot directly assign it as a register variable,
+ * nor can we clobber it. Instead we assign the r6 register and swap it
+ * with r7 before calling svc, and r6 is marked as clobbered.
+ * We're just using any regular register which we assign to r7 after saving
+ * it.
*
* Also, ARM supports the old_select syscall if newselect is not available
*/
#define __ARCH_WANT_SYS_OLD_SELECT
+#if (defined(__THUMBEB__) || defined(__THUMBEL__)) && \
+ !defined(NOLIBC_OMIT_FRAME_POINTER)
+/* swap r6,r7 needed in Thumb mode since we can't use nor clobber r7 */
+#define _NOLIBC_SYSCALL_REG "r6"
+#define _NOLIBC_THUMB_SET_R7 "eor r7, r6\neor r6, r7\neor r7, r6\n"
+#define _NOLIBC_THUMB_RESTORE_R7 "mov r7, r6\n"
+
+#else /* we're in ARM mode */
+/* in Arm mode we can directly use r7 */
+#define _NOLIBC_SYSCALL_REG "r7"
+#define _NOLIBC_THUMB_SET_R7 ""
+#define _NOLIBC_THUMB_RESTORE_R7 ""
+
+#endif /* end THUMB */
+
#define my_syscall0(num) \
({ \
- register long _num __asm__ ("r7") = (num); \
+ register long _num __asm__(_NOLIBC_SYSCALL_REG) = (num); \
register long _arg1 __asm__ ("r0"); \
\
__asm__ volatile ( \
+ _NOLIBC_THUMB_SET_R7 \
"svc #0\n" \
- : "=r"(_arg1) \
- : "r"(_num) \
+ _NOLIBC_THUMB_RESTORE_R7 \
+ : "=r"(_arg1), "=r"(_num) \
+ : "r"(_arg1), \
+ "r"(_num) \
: "memory", "cc", "lr" \
); \
_arg1; \
@@ -91,12 +103,14 @@ struct sys_stat_struct {
#define my_syscall1(num, arg1) \
({ \
- register long _num __asm__ ("r7") = (num); \
+ register long _num __asm__(_NOLIBC_SYSCALL_REG) = (num); \
register long _arg1 __asm__ ("r0") = (long)(arg1); \
\
__asm__ volatile ( \
+ _NOLIBC_THUMB_SET_R7 \
"svc #0\n" \
- : "=r"(_arg1) \
+ _NOLIBC_THUMB_RESTORE_R7 \
+ : "=r"(_arg1), "=r" (_num) \
: "r"(_arg1), \
"r"(_num) \
: "memory", "cc", "lr" \
@@ -106,13 +120,15 @@ struct sys_stat_struct {
#define my_syscall2(num, arg1, arg2) \
({ \
- register long _num __asm__ ("r7") = (num); \
+ register long _num __asm__(_NOLIBC_SYSCALL_REG) = (num); \
register long _arg1 __asm__ ("r0") = (long)(arg1); \
register long _arg2 __asm__ ("r1") = (long)(arg2); \
\
__asm__ volatile ( \
+ _NOLIBC_THUMB_SET_R7 \
"svc #0\n" \
- : "=r"(_arg1) \
+ _NOLIBC_THUMB_RESTORE_R7 \
+ : "=r"(_arg1), "=r" (_num) \
: "r"(_arg1), "r"(_arg2), \
"r"(_num) \
: "memory", "cc", "lr" \
@@ -122,14 +138,16 @@ struct sys_stat_struct {
#define my_syscall3(num, arg1, arg2, arg3) \
({ \
- register long _num __asm__ ("r7") = (num); \
+ register long _num __asm__(_NOLIBC_SYSCALL_REG) = (num); \
register long _arg1 __asm__ ("r0") = (long)(arg1); \
register long _arg2 __asm__ ("r1") = (long)(arg2); \
register long _arg3 __asm__ ("r2") = (long)(arg3); \
\
__asm__ volatile ( \
+ _NOLIBC_THUMB_SET_R7 \
"svc #0\n" \
- : "=r"(_arg1) \
+ _NOLIBC_THUMB_RESTORE_R7 \
+ : "=r"(_arg1), "=r" (_num) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), \
"r"(_num) \
: "memory", "cc", "lr" \
@@ -139,15 +157,17 @@ struct sys_stat_struct {
#define my_syscall4(num, arg1, arg2, arg3, arg4) \
({ \
- register long _num __asm__ ("r7") = (num); \
+ register long _num __asm__(_NOLIBC_SYSCALL_REG) = (num); \
register long _arg1 __asm__ ("r0") = (long)(arg1); \
register long _arg2 __asm__ ("r1") = (long)(arg2); \
register long _arg3 __asm__ ("r2") = (long)(arg3); \
register long _arg4 __asm__ ("r3") = (long)(arg4); \
\
__asm__ volatile ( \
+ _NOLIBC_THUMB_SET_R7 \
"svc #0\n" \
- : "=r"(_arg1) \
+ _NOLIBC_THUMB_RESTORE_R7 \
+ : "=r"(_arg1), "=r" (_num) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \
"r"(_num) \
: "memory", "cc", "lr" \
@@ -157,7 +177,7 @@ struct sys_stat_struct {
#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \
({ \
- register long _num __asm__ ("r7") = (num); \
+ register long _num __asm__(_NOLIBC_SYSCALL_REG) = (num); \
register long _arg1 __asm__ ("r0") = (long)(arg1); \
register long _arg2 __asm__ ("r1") = (long)(arg2); \
register long _arg3 __asm__ ("r2") = (long)(arg3); \
@@ -165,8 +185,10 @@ struct sys_stat_struct {
register long _arg5 __asm__ ("r4") = (long)(arg5); \
\
__asm__ volatile ( \
+ _NOLIBC_THUMB_SET_R7 \
"svc #0\n" \
- : "=r" (_arg1) \
+ _NOLIBC_THUMB_RESTORE_R7 \
+ : "=r"(_arg1), "=r" (_num) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
"r"(_num) \
: "memory", "cc", "lr" \
@@ -174,31 +196,47 @@ struct sys_stat_struct {
_arg1; \
})
+char **environ __attribute__((weak));
+const unsigned long *_auxv __attribute__((weak));
+
/* startup code */
-__asm__ (".section .text\n"
- ".weak _start\n"
- "_start:\n"
-#if defined(__THUMBEB__) || defined(__THUMBEL__)
- /* We enter here in 32-bit mode but if some previous functions were in
- * 16-bit mode, the assembler cannot know, so we need to tell it we're in
- * 32-bit now, then switch to 16-bit (is there a better way to do it than
- * adding 1 by hand ?) and tell the asm we're now in 16-bit mode so that
- * it generates correct instructions. Note that we do not support thumb1.
- */
- ".code 32\n"
- "add r0, pc, #1\n"
- "bx r0\n"
- ".code 16\n"
-#endif
- "pop {%r0}\n" // argc was in the stack
- "mov %r1, %sp\n" // argv = sp
- "add %r2, %r1, %r0, lsl #2\n" // envp = argv + 4*argc ...
- "add %r2, %r2, $4\n" // ... + 4
- "and %r3, %r1, $-8\n" // AAPCS : sp must be 8-byte aligned in the
- "mov %sp, %r3\n" // callee, an bl doesn't push (lr=pc)
- "bl main\n" // main() returns the status code, we'll exit with it.
- "movs r7, $1\n" // NR_exit == 1
- "svc $0x00\n"
- "");
+void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _start(void)
+{
+ __asm__ volatile (
+ "pop {%r0}\n" // argc was in the stack
+ "mov %r1, %sp\n" // argv = sp
+
+ "add %r2, %r0, $1\n" // envp = (argc + 1) ...
+ "lsl %r2, %r2, $2\n" // * 4 ...
+ "add %r2, %r2, %r1\n" // + argv
+ "ldr %r3, 1f\n" // r3 = &environ (see below)
+ "str %r2, [r3]\n" // store envp into environ
+
+ "mov r4, r2\n" // search for auxv (follows NULL after last env)
+ "0:\n"
+ "mov r5, r4\n" // r5 = r4
+ "add r4, r4, #4\n" // r4 += 4
+ "ldr r5,[r5]\n" // r5 = *r5 = *(r4-4)
+ "cmp r5, #0\n" // and stop at NULL after last env
+ "bne 0b\n"
+ "ldr %r3, 2f\n" // r3 = &_auxv (low bits)
+ "str r4, [r3]\n" // store r4 into _auxv
+
+ "mov %r3, $8\n" // AAPCS : sp must be 8-byte aligned in the
+ "neg %r3, %r3\n" // callee, and bl doesn't push (lr=pc)
+ "and %r3, %r3, %r1\n" // so we do sp = r1(=sp) & r3(=-8);
+ "mov %sp, %r3\n" //
+
+ "bl main\n" // main() returns the status code, we'll exit with it.
+ "movs r7, $1\n" // NR_exit == 1
+ "svc $0x00\n"
+ ".align 2\n" // below are the pointers to a few variables
+ "1:\n"
+ ".word environ\n"
+ "2:\n"
+ ".word _auxv\n"
+ );
+ __builtin_unreachable();
+}
#endif // _NOLIBC_ARCH_ARM_H
diff --git a/tools/include/nolibc/arch-i386.h b/tools/include/nolibc/arch-i386.h
index d7e7212346e2..e8d0cf545bf1 100644
--- a/tools/include/nolibc/arch-i386.h
+++ b/tools/include/nolibc/arch-i386.h
@@ -7,18 +7,6 @@
#ifndef _NOLIBC_ARCH_I386_H
#define _NOLIBC_ARCH_I386_H
-/* O_* macros for fcntl/open are architecture-specific */
-#define O_RDONLY 0
-#define O_WRONLY 1
-#define O_RDWR 2
-#define O_CREAT 0x40
-#define O_EXCL 0x80
-#define O_NOCTTY 0x100
-#define O_TRUNC 0x200
-#define O_APPEND 0x400
-#define O_NONBLOCK 0x800
-#define O_DIRECTORY 0x10000
-
/* The struct returned by the stat() syscall, 32-bit only, the syscall returns
* exactly 56 bytes (stops before the unused array).
*/
@@ -190,6 +178,9 @@ struct sys_stat_struct {
_eax; \
})
+char **environ __attribute__((weak));
+const unsigned long *_auxv __attribute__((weak));
+
/* startup code */
/*
* i386 System V ABI mandates:
@@ -197,23 +188,32 @@ struct sys_stat_struct {
* 2) The deepest stack frame should be set to zero
*
*/
-__asm__ (".section .text\n"
- ".weak _start\n"
- "_start:\n"
- "pop %eax\n" // argc (first arg, %eax)
- "mov %esp, %ebx\n" // argv[] (second arg, %ebx)
- "lea 4(%ebx,%eax,4),%ecx\n" // then a NULL then envp (third arg, %ecx)
- "xor %ebp, %ebp\n" // zero the stack frame
- "and $-16, %esp\n" // x86 ABI : esp must be 16-byte aligned before
- "sub $4, %esp\n" // the call instruction (args are aligned)
- "push %ecx\n" // push all registers on the stack so that we
- "push %ebx\n" // support both regparm and plain stack modes
- "push %eax\n"
- "call main\n" // main() returns the status code in %eax
- "mov %eax, %ebx\n" // retrieve exit code (32-bit int)
- "movl $1, %eax\n" // NR_exit == 1
- "int $0x80\n" // exit now
- "hlt\n" // ensure it does not
- "");
+void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _start(void)
+{
+ __asm__ volatile (
+ "pop %eax\n" // argc (first arg, %eax)
+ "mov %esp, %ebx\n" // argv[] (second arg, %ebx)
+ "lea 4(%ebx,%eax,4),%ecx\n" // then a NULL then envp (third arg, %ecx)
+ "mov %ecx, environ\n" // save environ
+ "xor %ebp, %ebp\n" // zero the stack frame
+ "mov %ecx, %edx\n" // search for auxv (follows NULL after last env)
+ "0:\n"
+ "add $4, %edx\n" // search for auxv using edx, it follows the
+ "cmp -4(%edx), %ebp\n" // ... NULL after last env (ebp is zero here)
+ "jnz 0b\n"
+ "mov %edx, _auxv\n" // save it into _auxv
+ "and $-16, %esp\n" // x86 ABI : esp must be 16-byte aligned before
+ "sub $4, %esp\n" // the call instruction (args are aligned)
+ "push %ecx\n" // push all registers on the stack so that we
+ "push %ebx\n" // support both regparm and plain stack modes
+ "push %eax\n"
+ "call main\n" // main() returns the status code in %eax
+ "mov %eax, %ebx\n" // retrieve exit code (32-bit int)
+ "movl $1, %eax\n" // NR_exit == 1
+ "int $0x80\n" // exit now
+ "hlt\n" // ensure it does not
+ );
+ __builtin_unreachable();
+}
#endif // _NOLIBC_ARCH_I386_H
diff --git a/tools/include/nolibc/arch-mips.h b/tools/include/nolibc/arch-mips.h
index 7380093ba9e7..bf83432d23ed 100644
--- a/tools/include/nolibc/arch-mips.h
+++ b/tools/include/nolibc/arch-mips.h
@@ -7,18 +7,6 @@
#ifndef _NOLIBC_ARCH_MIPS_H
#define _NOLIBC_ARCH_MIPS_H
-/* O_* macros for fcntl/open are architecture-specific */
-#define O_RDONLY 0
-#define O_WRONLY 1
-#define O_RDWR 2
-#define O_APPEND 0x0008
-#define O_NONBLOCK 0x0080
-#define O_CREAT 0x0100
-#define O_TRUNC 0x0200
-#define O_EXCL 0x0400
-#define O_NOCTTY 0x0800
-#define O_DIRECTORY 0x10000
-
/* The struct returned by the stat() syscall. 88 bytes are returned by the
* syscall.
*/
@@ -188,30 +176,49 @@ struct sys_stat_struct {
_arg4 ? -_num : _num; \
})
+char **environ __attribute__((weak));
+const unsigned long *_auxv __attribute__((weak));
+
/* startup code, note that it's called __start on MIPS */
-__asm__ (".section .text\n"
- ".weak __start\n"
- ".set nomips16\n"
- ".set push\n"
- ".set noreorder\n"
- ".option pic0\n"
- ".ent __start\n"
- "__start:\n"
- "lw $a0,($sp)\n" // argc was in the stack
- "addiu $a1, $sp, 4\n" // argv = sp + 4
- "sll $a2, $a0, 2\n" // a2 = argc * 4
- "add $a2, $a2, $a1\n" // envp = argv + 4*argc ...
- "addiu $a2, $a2, 4\n" // ... + 4
- "li $t0, -8\n"
- "and $sp, $sp, $t0\n" // sp must be 8-byte aligned
- "addiu $sp,$sp,-16\n" // the callee expects to save a0..a3 there!
- "jal main\n" // main() returns the status code, we'll exit with it.
- "nop\n" // delayed slot
- "move $a0, $v0\n" // retrieve 32-bit exit code from v0
- "li $v0, 4001\n" // NR_exit == 4001
- "syscall\n"
- ".end __start\n"
- ".set pop\n"
- "");
+void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) __start(void)
+{
+ __asm__ volatile (
+ //".set nomips16\n"
+ ".set push\n"
+ ".set noreorder\n"
+ ".option pic0\n"
+ //".ent __start\n"
+ //"__start:\n"
+ "lw $a0,($sp)\n" // argc was in the stack
+ "addiu $a1, $sp, 4\n" // argv = sp + 4
+ "sll $a2, $a0, 2\n" // a2 = argc * 4
+ "add $a2, $a2, $a1\n" // envp = argv + 4*argc ...
+ "addiu $a2, $a2, 4\n" // ... + 4
+ "lui $a3, %hi(environ)\n" // load environ into a3 (hi)
+ "addiu $a3, %lo(environ)\n" // load environ into a3 (lo)
+ "sw $a2,($a3)\n" // store envp(a2) into environ
+
+ "move $t0, $a2\n" // iterate t0 over envp, look for NULL
+ "0:" // do {
+ "lw $a3, ($t0)\n" // a3=*(t0);
+ "bne $a3, $0, 0b\n" // } while (a3);
+ "addiu $t0, $t0, 4\n" // delayed slot: t0+=4;
+ "lui $a3, %hi(_auxv)\n" // load _auxv into a3 (hi)
+ "addiu $a3, %lo(_auxv)\n" // load _auxv into a3 (lo)
+ "sw $t0, ($a3)\n" // store t0 into _auxv
+
+ "li $t0, -8\n"
+ "and $sp, $sp, $t0\n" // sp must be 8-byte aligned
+ "addiu $sp,$sp,-16\n" // the callee expects to save a0..a3 there!
+ "jal main\n" // main() returns the status code, we'll exit with it.
+ "nop\n" // delayed slot
+ "move $a0, $v0\n" // retrieve 32-bit exit code from v0
+ "li $v0, 4001\n" // NR_exit == 4001
+ "syscall\n"
+ //".end __start\n"
+ ".set pop\n"
+ );
+ __builtin_unreachable();
+}
#endif // _NOLIBC_ARCH_MIPS_H
diff --git a/tools/include/nolibc/arch-riscv.h b/tools/include/nolibc/arch-riscv.h
index a3bdd9803f8c..e197fcb10ac0 100644
--- a/tools/include/nolibc/arch-riscv.h
+++ b/tools/include/nolibc/arch-riscv.h
@@ -7,18 +7,6 @@
#ifndef _NOLIBC_ARCH_RISCV_H
#define _NOLIBC_ARCH_RISCV_H
-/* O_* macros for fcntl/open are architecture-specific */
-#define O_RDONLY 0
-#define O_WRONLY 1
-#define O_RDWR 2
-#define O_CREAT 0x40
-#define O_EXCL 0x80
-#define O_NOCTTY 0x100
-#define O_TRUNC 0x200
-#define O_APPEND 0x400
-#define O_NONBLOCK 0x800
-#define O_DIRECTORY 0x10000
-
struct sys_stat_struct {
unsigned long st_dev; /* Device. */
unsigned long st_ino; /* File serial number. */
@@ -182,23 +170,39 @@ struct sys_stat_struct {
_arg1; \
})
+char **environ __attribute__((weak));
+const unsigned long *_auxv __attribute__((weak));
+
/* startup code */
-__asm__ (".section .text\n"
- ".weak _start\n"
- "_start:\n"
- ".option push\n"
- ".option norelax\n"
- "lla gp, __global_pointer$\n"
- ".option pop\n"
- "lw a0, 0(sp)\n" // argc (a0) was in the stack
- "add a1, sp, "SZREG"\n" // argv (a1) = sp
- "slli a2, a0, "PTRLOG"\n" // envp (a2) = SZREG*argc ...
- "add a2, a2, "SZREG"\n" // + SZREG (skip null)
- "add a2,a2,a1\n" // + argv
- "andi sp,a1,-16\n" // sp must be 16-byte aligned
- "call main\n" // main() returns the status code, we'll exit with it.
- "li a7, 93\n" // NR_exit == 93
- "ecall\n"
- "");
+void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _start(void)
+{
+ __asm__ volatile (
+ ".option push\n"
+ ".option norelax\n"
+ "lla gp, __global_pointer$\n"
+ ".option pop\n"
+ "lw a0, 0(sp)\n" // argc (a0) was in the stack
+ "add a1, sp, "SZREG"\n" // argv (a1) = sp
+ "slli a2, a0, "PTRLOG"\n" // envp (a2) = SZREG*argc ...
+ "add a2, a2, "SZREG"\n" // + SZREG (skip null)
+ "add a2,a2,a1\n" // + argv
+
+ "add a3, a2, zero\n" // iterate a3 over envp to find auxv (after NULL)
+ "0:\n" // do {
+ "ld a4, 0(a3)\n" // a4 = *a3;
+ "add a3, a3, "SZREG"\n" // a3 += sizeof(void*);
+ "bne a4, zero, 0b\n" // } while (a4);
+ "lui a4, %hi(_auxv)\n" // a4 = &_auxv (high bits)
+ "sd a3, %lo(_auxv)(a4)\n" // store a3 into _auxv
+
+ "lui a3, %hi(environ)\n" // a3 = &environ (high bits)
+ "sd a2,%lo(environ)(a3)\n" // store envp(a2) into environ
+ "andi sp,a1,-16\n" // sp must be 16-byte aligned
+ "call main\n" // main() returns the status code, we'll exit with it.
+ "li a7, 93\n" // NR_exit == 93
+ "ecall\n"
+ );
+ __builtin_unreachable();
+}
#endif // _NOLIBC_ARCH_RISCV_H
diff --git a/tools/include/nolibc/arch-s390.h b/tools/include/nolibc/arch-s390.h
new file mode 100644
index 000000000000..6b0e54ed543d
--- /dev/null
+++ b/tools/include/nolibc/arch-s390.h
@@ -0,0 +1,226 @@
+/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
+/*
+ * s390 specific definitions for NOLIBC
+ */
+
+#ifndef _NOLIBC_ARCH_S390_H
+#define _NOLIBC_ARCH_S390_H
+#include <asm/unistd.h>
+
+/* The struct returned by the stat() syscall, equivalent to stat64(). The
+ * syscall returns 116 bytes and stops in the middle of __unused.
+ */
+
+struct sys_stat_struct {
+ unsigned long st_dev;
+ unsigned long st_ino;
+ unsigned long st_nlink;
+ unsigned int st_mode;
+ unsigned int st_uid;
+ unsigned int st_gid;
+ unsigned int __pad1;
+ unsigned long st_rdev;
+ unsigned long st_size;
+ unsigned long st_atime;
+ unsigned long st_atime_nsec;
+ unsigned long st_mtime;
+ unsigned long st_mtime_nsec;
+ unsigned long st_ctime;
+ unsigned long st_ctime_nsec;
+ unsigned long st_blksize;
+ long st_blocks;
+ unsigned long __unused[3];
+};
+
+/* Syscalls for s390:
+ * - registers are 64-bit
+ * - syscall number is passed in r1
+ * - arguments are in r2-r7
+ * - the system call is performed by calling the svc instruction
+ * - syscall return value is in r2
+ * - r1 and r2 are clobbered, others are preserved.
+ *
+ * Link s390 ABI: https://github.com/IBM/s390x-abi
+ *
+ */
+
+#define my_syscall0(num) \
+({ \
+ register long _num __asm__ ("1") = (num); \
+ register long _rc __asm__ ("2"); \
+ \
+ __asm__ volatile ( \
+ "svc 0\n" \
+ : "=d"(_rc) \
+ : "d"(_num) \
+ : "memory", "cc" \
+ ); \
+ _rc; \
+})
+
+#define my_syscall1(num, arg1) \
+({ \
+ register long _num __asm__ ("1") = (num); \
+ register long _arg1 __asm__ ("2") = (long)(arg1); \
+ \
+ __asm__ volatile ( \
+ "svc 0\n" \
+ : "+d"(_arg1) \
+ : "d"(_num) \
+ : "memory", "cc" \
+ ); \
+ _arg1; \
+})
+
+#define my_syscall2(num, arg1, arg2) \
+({ \
+ register long _num __asm__ ("1") = (num); \
+ register long _arg1 __asm__ ("2") = (long)(arg1); \
+ register long _arg2 __asm__ ("3") = (long)(arg2); \
+ \
+ __asm__ volatile ( \
+ "svc 0\n" \
+ : "+d"(_arg1) \
+ : "d"(_arg2), "d"(_num) \
+ : "memory", "cc" \
+ ); \
+ _arg1; \
+})
+
+#define my_syscall3(num, arg1, arg2, arg3) \
+({ \
+ register long _num __asm__ ("1") = (num); \
+ register long _arg1 __asm__ ("2") = (long)(arg1); \
+ register long _arg2 __asm__ ("3") = (long)(arg2); \
+ register long _arg3 __asm__ ("4") = (long)(arg3); \
+ \
+ __asm__ volatile ( \
+ "svc 0\n" \
+ : "+d"(_arg1) \
+ : "d"(_arg2), "d"(_arg3), "d"(_num) \
+ : "memory", "cc" \
+ ); \
+ _arg1; \
+})
+
+#define my_syscall4(num, arg1, arg2, arg3, arg4) \
+({ \
+ register long _num __asm__ ("1") = (num); \
+ register long _arg1 __asm__ ("2") = (long)(arg1); \
+ register long _arg2 __asm__ ("3") = (long)(arg2); \
+ register long _arg3 __asm__ ("4") = (long)(arg3); \
+ register long _arg4 __asm__ ("5") = (long)(arg4); \
+ \
+ __asm__ volatile ( \
+ "svc 0\n" \
+ : "+d"(_arg1) \
+ : "d"(_arg2), "d"(_arg3), "d"(_arg4), "d"(_num) \
+ : "memory", "cc" \
+ ); \
+ _arg1; \
+})
+
+#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \
+({ \
+ register long _num __asm__ ("1") = (num); \
+ register long _arg1 __asm__ ("2") = (long)(arg1); \
+ register long _arg2 __asm__ ("3") = (long)(arg2); \
+ register long _arg3 __asm__ ("4") = (long)(arg3); \
+ register long _arg4 __asm__ ("5") = (long)(arg4); \
+ register long _arg5 __asm__ ("6") = (long)(arg5); \
+ \
+ __asm__ volatile ( \
+ "svc 0\n" \
+ : "+d"(_arg1) \
+ : "d"(_arg2), "d"(_arg3), "d"(_arg4), "d"(_arg5), \
+ "d"(_num) \
+ : "memory", "cc" \
+ ); \
+ _arg1; \
+})
+
+#define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \
+({ \
+ register long _num __asm__ ("1") = (num); \
+ register long _arg1 __asm__ ("2") = (long)(arg1); \
+ register long _arg2 __asm__ ("3") = (long)(arg2); \
+ register long _arg3 __asm__ ("4") = (long)(arg3); \
+ register long _arg4 __asm__ ("5") = (long)(arg4); \
+ register long _arg5 __asm__ ("6") = (long)(arg5); \
+ register long _arg6 __asm__ ("7") = (long)(arg6); \
+ \
+ __asm__ volatile ( \
+ "svc 0\n" \
+ : "+d"(_arg1) \
+ : "d"(_arg2), "d"(_arg3), "d"(_arg4), "d"(_arg5), \
+ "d"(_arg6), "d"(_num) \
+ : "memory", "cc" \
+ ); \
+ _arg1; \
+})
+
+char **environ __attribute__((weak));
+const unsigned long *_auxv __attribute__((weak));
+
+/* startup code */
+void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _start(void)
+{
+ __asm__ volatile (
+ "lg %r2,0(%r15)\n" /* argument count */
+ "la %r3,8(%r15)\n" /* argument pointers */
+
+ "xgr %r0,%r0\n" /* r0 will be our NULL value */
+ /* search for envp */
+ "lgr %r4,%r3\n" /* start at argv */
+ "0:\n"
+ "clg %r0,0(%r4)\n" /* entry zero? */
+ "la %r4,8(%r4)\n" /* advance pointer */
+ "jnz 0b\n" /* no -> test next pointer */
+ /* yes -> r4 now contains start of envp */
+ "larl %r1,environ\n"
+ "stg %r4,0(%r1)\n"
+
+ /* search for auxv */
+ "lgr %r5,%r4\n" /* start at envp */
+ "1:\n"
+ "clg %r0,0(%r5)\n" /* entry zero? */
+ "la %r5,8(%r5)\n" /* advance pointer */
+ "jnz 1b\n" /* no -> test next pointer */
+ "larl %r1,_auxv\n" /* yes -> store value in _auxv */
+ "stg %r5,0(%r1)\n"
+
+ "aghi %r15,-160\n" /* allocate new stackframe */
+ "xc 0(8,%r15),0(%r15)\n" /* clear backchain */
+ "brasl %r14,main\n" /* ret value of main is arg to exit */
+ "lghi %r1,1\n" /* __NR_exit */
+ "svc 0\n"
+ );
+ __builtin_unreachable();
+}
+
+struct s390_mmap_arg_struct {
+ unsigned long addr;
+ unsigned long len;
+ unsigned long prot;
+ unsigned long flags;
+ unsigned long fd;
+ unsigned long offset;
+};
+
+static __attribute__((unused))
+void *sys_mmap(void *addr, size_t length, int prot, int flags, int fd,
+ off_t offset)
+{
+ struct s390_mmap_arg_struct args = {
+ .addr = (unsigned long)addr,
+ .len = (unsigned long)length,
+ .prot = prot,
+ .flags = flags,
+ .fd = fd,
+ .offset = (unsigned long)offset
+ };
+
+ return (void *)my_syscall1(__NR_mmap, &args);
+}
+#define sys_mmap sys_mmap
+#endif // _NOLIBC_ARCH_S390_H
diff --git a/tools/include/nolibc/arch-x86_64.h b/tools/include/nolibc/arch-x86_64.h
index 0e1e9eb8545d..17f6751208e7 100644
--- a/tools/include/nolibc/arch-x86_64.h
+++ b/tools/include/nolibc/arch-x86_64.h
@@ -7,18 +7,6 @@
#ifndef _NOLIBC_ARCH_X86_64_H
#define _NOLIBC_ARCH_X86_64_H
-/* O_* macros for fcntl/open are architecture-specific */
-#define O_RDONLY 0
-#define O_WRONLY 1
-#define O_RDWR 2
-#define O_CREAT 0x40
-#define O_EXCL 0x80
-#define O_NOCTTY 0x100
-#define O_TRUNC 0x200
-#define O_APPEND 0x400
-#define O_NONBLOCK 0x800
-#define O_DIRECTORY 0x10000
-
/* The struct returned by the stat() syscall, equivalent to stat64(). The
* syscall returns 116 bytes and stops in the middle of __unused.
*/
@@ -190,6 +178,9 @@ struct sys_stat_struct {
_ret; \
})
+char **environ __attribute__((weak));
+const unsigned long *_auxv __attribute__((weak));
+
/* startup code */
/*
* x86-64 System V ABI mandates:
@@ -197,19 +188,28 @@ struct sys_stat_struct {
* 2) The deepest stack frame should be zero (the %rbp).
*
*/
-__asm__ (".section .text\n"
- ".weak _start\n"
- "_start:\n"
- "pop %rdi\n" // argc (first arg, %rdi)
- "mov %rsp, %rsi\n" // argv[] (second arg, %rsi)
- "lea 8(%rsi,%rdi,8),%rdx\n" // then a NULL then envp (third arg, %rdx)
- "xor %ebp, %ebp\n" // zero the stack frame
- "and $-16, %rsp\n" // x86 ABI : esp must be 16-byte aligned before call
- "call main\n" // main() returns the status code, we'll exit with it.
- "mov %eax, %edi\n" // retrieve exit code (32 bit)
- "mov $60, %eax\n" // NR_exit == 60
- "syscall\n" // really exit
- "hlt\n" // ensure it does not return
- "");
+void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _start(void)
+{
+ __asm__ volatile (
+ "pop %rdi\n" // argc (first arg, %rdi)
+ "mov %rsp, %rsi\n" // argv[] (second arg, %rsi)
+ "lea 8(%rsi,%rdi,8),%rdx\n" // then a NULL then envp (third arg, %rdx)
+ "mov %rdx, environ\n" // save environ
+ "xor %ebp, %ebp\n" // zero the stack frame
+ "mov %rdx, %rax\n" // search for auxv (follows NULL after last env)
+ "0:\n"
+ "add $8, %rax\n" // search for auxv using rax, it follows the
+ "cmp -8(%rax), %rbp\n" // ... NULL after last env (rbp is zero here)
+ "jnz 0b\n"
+ "mov %rax, _auxv\n" // save it into _auxv
+ "and $-16, %rsp\n" // x86 ABI : esp must be 16-byte aligned before call
+ "call main\n" // main() returns the status code, we'll exit with it.
+ "mov %eax, %edi\n" // retrieve exit code (32 bit)
+ "mov $60, %eax\n" // NR_exit == 60
+ "syscall\n" // really exit
+ "hlt\n" // ensure it does not return
+ );
+ __builtin_unreachable();
+}
#endif // _NOLIBC_ARCH_X86_64_H
diff --git a/tools/include/nolibc/arch.h b/tools/include/nolibc/arch.h
index 4c6992321b0d..78b067a4fa47 100644
--- a/tools/include/nolibc/arch.h
+++ b/tools/include/nolibc/arch.h
@@ -27,6 +27,8 @@
#include "arch-mips.h"
#elif defined(__riscv)
#include "arch-riscv.h"
+#elif defined(__s390x__)
+#include "arch-s390.h"
#endif
#endif /* _NOLIBC_ARCH_H */
diff --git a/tools/include/nolibc/errno.h b/tools/include/nolibc/errno.h
index 9dc4919c769b..a44486ff0477 100644
--- a/tools/include/nolibc/errno.h
+++ b/tools/include/nolibc/errno.h
@@ -9,11 +9,9 @@
#include <asm/errno.h>
-/* this way it will be removed if unused */
-static int errno;
-
#ifndef NOLIBC_IGNORE_ERRNO
#define SET_ERRNO(v) do { errno = (v); } while (0)
+int errno __attribute__((weak));
#else
#define SET_ERRNO(v) do { } while (0)
#endif
diff --git a/tools/include/nolibc/stdlib.h b/tools/include/nolibc/stdlib.h
index a24000d1e822..894c955d027e 100644
--- a/tools/include/nolibc/stdlib.h
+++ b/tools/include/nolibc/stdlib.h
@@ -12,6 +12,7 @@
#include "types.h"
#include "sys.h"
#include "string.h"
+#include <linux/auxvec.h>
struct nolibc_heap {
size_t len;
@@ -109,6 +110,32 @@ char *getenv(const char *name)
}
static __attribute__((unused))
+unsigned long getauxval(unsigned long type)
+{
+ const unsigned long *auxv = _auxv;
+ unsigned long ret;
+
+ if (!auxv)
+ return 0;
+
+ while (1) {
+ if (!auxv[0] && !auxv[1]) {
+ ret = 0;
+ break;
+ }
+
+ if (auxv[0] == type) {
+ ret = auxv[1];
+ break;
+ }
+
+ auxv += 2;
+ }
+
+ return ret;
+}
+
+static __attribute__((unused))
void *malloc(size_t len)
{
struct nolibc_heap *heap;
diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h
index 78473d34e27c..b5f8cd35c03b 100644
--- a/tools/include/nolibc/sys.h
+++ b/tools/include/nolibc/sys.h
@@ -11,6 +11,7 @@
#include "std.h"
/* system includes */
+#include <asm/fcntl.h> // for O_*
#include <asm/unistd.h>
#include <asm/signal.h> // for SIGCHLD
#include <asm/ioctls.h>
@@ -18,6 +19,7 @@
#include <linux/fs.h>
#include <linux/loop.h>
#include <linux/time.h>
+#include <linux/auxvec.h>
#include "arch.h"
#include "errno.h"
@@ -498,6 +500,26 @@ pid_t gettid(void)
return sys_gettid();
}
+static unsigned long getauxval(unsigned long key);
+
+/*
+ * long getpagesize(void);
+ */
+
+static __attribute__((unused))
+long getpagesize(void)
+{
+ long ret;
+
+ ret = getauxval(AT_PAGESZ);
+ if (!ret) {
+ SET_ERRNO(ENOENT);
+ return -1;
+ }
+
+ return ret;
+}
+
/*
* int gettimeofday(struct timeval *tv, struct timezone *tz);
@@ -686,6 +708,7 @@ int mknod(const char *path, mode_t mode, dev_t dev)
#define MAP_FAILED ((void *)-1)
#endif
+#ifndef sys_mmap
static __attribute__((unused))
void *sys_mmap(void *addr, size_t length, int prot, int flags, int fd,
off_t offset)
@@ -707,6 +730,7 @@ void *sys_mmap(void *addr, size_t length, int prot, int flags, int fd,
return (void *)my_syscall6(n, addr, length, prot, flags, fd, offset);
#endif
}
+#endif
static __attribute__((unused))
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
diff --git a/tools/include/uapi/linux/fcntl.h b/tools/include/uapi/linux/fcntl.h
index 2f86b2ad6d7e..e8c07da58c9f 100644
--- a/tools/include/uapi/linux/fcntl.h
+++ b/tools/include/uapi/linux/fcntl.h
@@ -43,6 +43,7 @@
#define F_SEAL_GROW 0x0004 /* prevent file from growing */
#define F_SEAL_WRITE 0x0008 /* prevent writes */
#define F_SEAL_FUTURE_WRITE 0x0010 /* prevent future writes while mapped */
+#define F_SEAL_EXEC 0x0020 /* prevent chmod modifying exec bits */
/* (1U << 31) is reserved for signed error codes */
/*
diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h
index 55155e262646..d77aef872a0a 100644
--- a/tools/include/uapi/linux/kvm.h
+++ b/tools/include/uapi/linux/kvm.h
@@ -583,6 +583,8 @@ struct kvm_s390_mem_op {
struct {
__u8 ar; /* the access register number */
__u8 key; /* access key, ignored if flag unset */
+ __u8 pad1[6]; /* ignored */
+ __u64 old_addr; /* ignored if cmpxchg flag unset */
};
__u32 sida_offset; /* offset into the sida */
__u8 reserved[32]; /* ignored */
@@ -595,11 +597,17 @@ struct kvm_s390_mem_op {
#define KVM_S390_MEMOP_SIDA_WRITE 3
#define KVM_S390_MEMOP_ABSOLUTE_READ 4
#define KVM_S390_MEMOP_ABSOLUTE_WRITE 5
+#define KVM_S390_MEMOP_ABSOLUTE_CMPXCHG 6
+
/* flags for kvm_s390_mem_op->flags */
#define KVM_S390_MEMOP_F_CHECK_ONLY (1ULL << 0)
#define KVM_S390_MEMOP_F_INJECT_EXCEPTION (1ULL << 1)
#define KVM_S390_MEMOP_F_SKEY_PROTECTION (1ULL << 2)
+/* flags specifying extension support via KVM_CAP_S390_MEM_OP_EXTENSION */
+#define KVM_S390_MEMOP_EXTENSION_CAP_BASE (1 << 0)
+#define KVM_S390_MEMOP_EXTENSION_CAP_CMPXCHG (1 << 1)
+
/* for KVM_INTERRUPT */
struct kvm_interrupt {
/* in */
@@ -1175,6 +1183,7 @@ struct kvm_ppc_resize_hpt {
#define KVM_CAP_DIRTY_LOG_RING_ACQ_REL 223
#define KVM_CAP_S390_PROTECTED_ASYNC_DISABLE 224
#define KVM_CAP_DIRTY_LOG_RING_WITH_BITMAP 225
+#define KVM_CAP_PMU_EVENT_MASKED_EVENTS 226
#ifdef KVM_CAP_IRQ_ROUTING
diff --git a/tools/include/uapi/linux/netdev.h b/tools/include/uapi/linux/netdev.h
index 9ee459872600..639524b59930 100644
--- a/tools/include/uapi/linux/netdev.h
+++ b/tools/include/uapi/linux/netdev.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/netdev.yaml */
/* YNL-GEN uapi header */
@@ -19,7 +19,7 @@
* @NETDEV_XDP_ACT_XSK_ZEROCOPY: This feature informs if netdev supports AF_XDP
* in zero copy mode.
* @NETDEV_XDP_ACT_HW_OFFLOAD: This feature informs if netdev supports XDP hw
- * oflloading.
+ * offloading.
* @NETDEV_XDP_ACT_RX_SG: This feature informs if netdev implements non-linear
* XDP buffer support in the driver napi callback.
* @NETDEV_XDP_ACT_NDO_XMIT_SG: This feature informs if netdev implements
@@ -33,6 +33,8 @@ enum netdev_xdp_act {
NETDEV_XDP_ACT_HW_OFFLOAD = 16,
NETDEV_XDP_ACT_RX_SG = 32,
NETDEV_XDP_ACT_NDO_XMIT_SG = 64,
+
+ NETDEV_XDP_ACT_MASK = 127,
};
enum {
diff --git a/tools/include/uapi/linux/perf_event.h b/tools/include/uapi/linux/perf_event.h
index ccb7f5dad59b..37675437b768 100644
--- a/tools/include/uapi/linux/perf_event.h
+++ b/tools/include/uapi/linux/perf_event.h
@@ -374,6 +374,7 @@ enum perf_event_read_format {
#define PERF_ATTR_SIZE_VER5 112 /* add: aux_watermark */
#define PERF_ATTR_SIZE_VER6 120 /* add: aux_sample_size */
#define PERF_ATTR_SIZE_VER7 128 /* add: sig_data */
+#define PERF_ATTR_SIZE_VER8 136 /* add: config3 */
/*
* Hardware event_id to monitor via a performance monitoring event:
@@ -515,6 +516,8 @@ struct perf_event_attr {
* truncated accordingly on 32 bit architectures.
*/
__u64 sig_data;
+
+ __u64 config3; /* extension of config2 */
};
/*
diff --git a/tools/include/uapi/linux/prctl.h b/tools/include/uapi/linux/prctl.h
index a5e06dcbba13..1312a137f7fb 100644
--- a/tools/include/uapi/linux/prctl.h
+++ b/tools/include/uapi/linux/prctl.h
@@ -281,6 +281,12 @@ struct prctl_mm_map {
# define PR_SME_VL_LEN_MASK 0xffff
# define PR_SME_VL_INHERIT (1 << 17) /* inherit across exec */
+/* Memory deny write / execute */
+#define PR_SET_MDWE 65
+# define PR_MDWE_REFUSE_EXEC_GAIN 1
+
+#define PR_GET_MDWE 66
+
#define PR_SET_VMA 0x53564d41
# define PR_SET_VMA_ANON_NAME 0
diff --git a/tools/include/uapi/linux/vhost.h b/tools/include/uapi/linux/vhost.h
index f9f115a7c75b..92e1b700b51c 100644
--- a/tools/include/uapi/linux/vhost.h
+++ b/tools/include/uapi/linux/vhost.h
@@ -180,4 +180,12 @@
*/
#define VHOST_VDPA_SUSPEND _IO(VHOST_VIRTIO, 0x7D)
+/* Resume a device so it can resume processing virtqueue requests
+ *
+ * After the return of this ioctl the device will have restored all the
+ * necessary states and it is fully operational to continue processing the
+ * virtqueue descriptors.
+ */
+#define VHOST_VDPA_RESUME _IO(VHOST_VIRTIO, 0x7E)
+
#endif
diff --git a/tools/include/vdso/bits.h b/tools/include/vdso/bits.h
index 6d005a1f5d94..388b212088ea 100644
--- a/tools/include/vdso/bits.h
+++ b/tools/include/vdso/bits.h
@@ -5,5 +5,6 @@
#include <vdso/const.h>
#define BIT(nr) (UL(1) << (nr))
+#define BIT_ULL(nr) (ULL(1) << (nr))
#endif /* __VDSO_BITS_H */
diff --git a/tools/lib/api/fs/tracing_path.c b/tools/lib/api/fs/tracing_path.c
index b8e457c841ab..7ba3e81274e8 100644
--- a/tools/lib/api/fs/tracing_path.c
+++ b/tools/lib/api/fs/tracing_path.c
@@ -14,8 +14,8 @@
#include "tracing_path.h"
static char tracing_mnt[PATH_MAX] = "/sys/kernel/debug";
-static char tracing_path[PATH_MAX] = "/sys/kernel/debug/tracing";
-static char tracing_events_path[PATH_MAX] = "/sys/kernel/debug/tracing/events";
+static char tracing_path[PATH_MAX] = "/sys/kernel/tracing";
+static char tracing_events_path[PATH_MAX] = "/sys/kernel/tracing/events";
static void __tracing_path_set(const char *tracing, const char *mountpoint)
{
diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
index fbaf68335394..e4d05662a96c 100644
--- a/tools/lib/bpf/libbpf_internal.h
+++ b/tools/lib/bpf/libbpf_internal.h
@@ -20,8 +20,8 @@
/* make sure libbpf doesn't use kernel-only integer typedefs */
#pragma GCC poison u8 u16 u32 u64 s8 s16 s32 s64
-/* prevent accidental re-addition of reallocarray()/strlcpy() */
-#pragma GCC poison reallocarray strlcpy
+/* prevent accidental re-addition of reallocarray() */
+#pragma GCC poison reallocarray
#include "libbpf.h"
#include "btf.h"
diff --git a/tools/memory-model/Documentation/explanation.txt b/tools/memory-model/Documentation/explanation.txt
index 11a1d2d4f681..8e7085238470 100644
--- a/tools/memory-model/Documentation/explanation.txt
+++ b/tools/memory-model/Documentation/explanation.txt
@@ -1007,6 +1007,36 @@ order. Equivalently,
where the rmw relation links the read and write events making up each
atomic update. This is what the LKMM's "atomic" axiom says.
+Atomic rmw updates play one more role in the LKMM: They can form "rmw
+sequences". An rmw sequence is simply a bunch of atomic updates where
+each update reads from the previous one. Written using events, it
+looks like this:
+
+ Z0 ->rf Y1 ->rmw Z1 ->rf ... ->rf Yn ->rmw Zn,
+
+where Z0 is some store event and n can be any number (even 0, in the
+degenerate case). We write this relation as: Z0 ->rmw-sequence Zn.
+Note that this implies Z0 and Zn are stores to the same variable.
+
+Rmw sequences have a special property in the LKMM: They can extend the
+cumul-fence relation. That is, if we have:
+
+ U ->cumul-fence X -> rmw-sequence Y
+
+then also U ->cumul-fence Y. Thinking about this in terms of the
+operational model, U ->cumul-fence X says that the store U propagates
+to each CPU before the store X does. Then the fact that X and Y are
+linked by an rmw sequence means that U also propagates to each CPU
+before Y does. In an analogous way, rmw sequences can also extend
+the w-post-bounded relation defined below in the PLAIN ACCESSES AND
+DATA RACES section.
+
+(The notion of rmw sequences in the LKMM is similar to, but not quite
+the same as, that of release sequences in the C11 memory model. They
+were added to the LKMM to fix an obscure bug; without them, atomic
+updates with full-barrier semantics did not always guarantee ordering
+at least as strong as atomic updates with release-barrier semantics.)
+
THE PRESERVED PROGRAM ORDER RELATION: ppo
-----------------------------------------
@@ -2545,7 +2575,7 @@ smp_store_release() -- which is basically how the Linux kernel treats
them.
Although we said that plain accesses are not linked by the ppo
-relation, they do contribute to it indirectly. Namely, when there is
+relation, they do contribute to it indirectly. Firstly, when there is
an address dependency from a marked load R to a plain store W,
followed by smp_wmb() and then a marked store W', the LKMM creates a
ppo link from R to W'. The reasoning behind this is perhaps a little
@@ -2554,6 +2584,13 @@ for this source code in which W' could execute before R. Just as with
pre-bounding by address dependencies, it is possible for the compiler
to undermine this relation if sufficient care is not taken.
+Secondly, plain accesses can carry dependencies: If a data dependency
+links a marked load R to a store W, and the store is read by a load R'
+from the same thread, then the data loaded by R' depends on the data
+loaded originally by R. Thus, if R' is linked to any access X by a
+dependency, R is also linked to access X by the same dependency, even
+if W' or R' (or both!) are plain.
+
There are a few oddball fences which need special treatment:
smp_mb__before_atomic(), smp_mb__after_atomic(), and
smp_mb__after_spinlock(). The LKMM uses fence events with special
diff --git a/tools/memory-model/linux-kernel.bell b/tools/memory-model/linux-kernel.bell
index 5be86b1025e8..70a9073dec3e 100644
--- a/tools/memory-model/linux-kernel.bell
+++ b/tools/memory-model/linux-kernel.bell
@@ -82,3 +82,9 @@ flag ~empty different-values(srcu-rscs) as srcu-bad-nesting
let Marked = (~M) | IW | Once | Release | Acquire | domain(rmw) | range(rmw) |
LKR | LKW | UL | LF | RL | RU
let Plain = M \ Marked
+
+(* Redefine dependencies to include those carried through plain accesses *)
+let carry-dep = (data ; rfi)*
+let addr = carry-dep ; addr
+let ctrl = carry-dep ; ctrl
+let data = carry-dep ; data
diff --git a/tools/memory-model/linux-kernel.cat b/tools/memory-model/linux-kernel.cat
index d70315fddef6..07f884f9b2bf 100644
--- a/tools/memory-model/linux-kernel.cat
+++ b/tools/memory-model/linux-kernel.cat
@@ -74,8 +74,9 @@ let ppo = to-r | to-w | fence | (po-unlock-lock-po & int)
(* Propagation: Ordering from release operations and strong fences. *)
let A-cumul(r) = (rfe ; [Marked])? ; r
+let rmw-sequence = (rf ; rmw)*
let cumul-fence = [Marked] ; (A-cumul(strong-fence | po-rel) | wmb |
- po-unlock-lock-po) ; [Marked]
+ po-unlock-lock-po) ; [Marked] ; rmw-sequence
let prop = [Marked] ; (overwrite & ext)? ; cumul-fence* ;
[Marked] ; rfe? ; [Marked]
@@ -174,7 +175,7 @@ let vis = cumul-fence* ; rfe? ; [Marked] ;
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 w-post-bounded = fence? ; [Marked] ; rmw-sequence
let r-post-bounded = (nonrw-fence | ([~Noreturn] ; fencerel(Rmb) ; [R4rmb]))? ;
[Marked]
diff --git a/tools/memory-model/litmus-tests/dep+plain.litmus b/tools/memory-model/litmus-tests/dep+plain.litmus
new file mode 100644
index 000000000000..ebf84daa9a59
--- /dev/null
+++ b/tools/memory-model/litmus-tests/dep+plain.litmus
@@ -0,0 +1,31 @@
+C dep+plain
+
+(*
+ * Result: Never
+ *
+ * This litmus test demonstrates that in LKMM, plain accesses
+ * carry dependencies much like accesses to registers:
+ * The data stored to *z1 and *z2 by P0() originates from P0()'s
+ * READ_ONCE(), and therefore using that data to compute the
+ * conditional of P0()'s if-statement creates a control dependency
+ * from that READ_ONCE() to P0()'s WRITE_ONCE().
+ *)
+
+{}
+
+P0(int *x, int *y, int *z1, int *z2)
+{
+ int a = READ_ONCE(*x);
+ *z1 = a;
+ *z2 = *z1;
+ if (*z2 == 1)
+ WRITE_ONCE(*y, 1);
+}
+
+P1(int *x, int *y)
+{
+ int r = smp_load_acquire(y);
+ smp_store_release(x, r);
+}
+
+exists (x=1 /\ y=1)
diff --git a/tools/vm/.gitignore b/tools/mm/.gitignore
index 922879f93fc8..922879f93fc8 100644
--- a/tools/vm/.gitignore
+++ b/tools/mm/.gitignore
diff --git a/tools/vm/Makefile b/tools/mm/Makefile
index 9860622cbb15..6c1da51f4177 100644
--- a/tools/vm/Makefile
+++ b/tools/mm/Makefile
@@ -8,8 +8,8 @@ TARGETS=page-types slabinfo page_owner_sort
LIB_DIR = ../lib/api
LIBS = $(LIB_DIR)/libapi.a
-CFLAGS = -Wall -Wextra -I../lib/
-LDFLAGS = $(LIBS)
+CFLAGS += -Wall -Wextra -I../lib/
+LDFLAGS += $(LIBS)
all: $(TARGETS)
diff --git a/tools/vm/page-types.c b/tools/mm/page-types.c
index 381dcc00cb62..381dcc00cb62 100644
--- a/tools/vm/page-types.c
+++ b/tools/mm/page-types.c
diff --git a/tools/vm/page_owner_sort.c b/tools/mm/page_owner_sort.c
index ce860ab94162..7c2ac124cdc8 100644
--- a/tools/vm/page_owner_sort.c
+++ b/tools/mm/page_owner_sort.c
@@ -246,15 +246,16 @@ static int search_pattern(regex_t *pattern, char *pattern_str, char *buf)
return 0;
}
-static void check_regcomp(regex_t *pattern, const char *regex)
+static bool check_regcomp(regex_t *pattern, const char *regex)
{
int err;
err = regcomp(pattern, regex, REG_EXTENDED | REG_NEWLINE);
if (err != 0 || pattern->re_nsub != 1) {
fprintf(stderr, "Invalid pattern %s code %d\n", regex, err);
- exit(1);
+ return false;
}
+ return true;
}
static char **explode(char sep, const char *str, int *size)
@@ -494,28 +495,28 @@ static bool is_need(char *buf)
return true;
}
-static void add_list(char *buf, int len, char *ext_buf)
+static bool add_list(char *buf, int len, char *ext_buf)
{
if (list_size != 0 &&
len == list[list_size-1].len &&
memcmp(buf, list[list_size-1].txt, len) == 0) {
list[list_size-1].num++;
list[list_size-1].page_num += get_page_num(buf);
- return;
+ return true;
}
if (list_size == max_size) {
fprintf(stderr, "max_size too small??\n");
- exit(1);
+ return false;
}
if (!is_need(buf))
- return;
+ return true;
list[list_size].pid = get_pid(buf);
list[list_size].tgid = get_tgid(buf);
list[list_size].comm = get_comm(buf);
list[list_size].txt = malloc(len+1);
if (!list[list_size].txt) {
fprintf(stderr, "Out of memory\n");
- exit(1);
+ return false;
}
memcpy(list[list_size].txt, buf, len);
list[list_size].txt[len] = 0;
@@ -534,6 +535,7 @@ static void add_list(char *buf, int len, char *ext_buf)
printf("loaded %d\r", list_size);
fflush(stdout);
}
+ return true;
}
static bool parse_cull_args(const char *arg_str)
@@ -790,12 +792,19 @@ int main(int argc, char **argv)
exit(1);
}
- check_regcomp(&order_pattern, "order\\s*([0-9]*),");
- check_regcomp(&pid_pattern, "pid\\s*([0-9]*),");
- check_regcomp(&tgid_pattern, "tgid\\s*([0-9]*) ");
- check_regcomp(&comm_pattern, "tgid\\s*[0-9]*\\s*\\((.*)\\),\\s*ts");
- check_regcomp(&ts_nsec_pattern, "ts\\s*([0-9]*)\\s*ns,");
- check_regcomp(&free_ts_nsec_pattern, "free_ts\\s*([0-9]*)\\s*ns");
+ if (!check_regcomp(&order_pattern, "order\\s*([0-9]*),"))
+ goto out_order;
+ if (!check_regcomp(&pid_pattern, "pid\\s*([0-9]*),"))
+ goto out_pid;
+ if (!check_regcomp(&tgid_pattern, "tgid\\s*([0-9]*) "))
+ goto out_tgid;
+ if (!check_regcomp(&comm_pattern, "tgid\\s*[0-9]*\\s*\\((.*)\\),\\s*ts"))
+ goto out_comm;
+ if (!check_regcomp(&ts_nsec_pattern, "ts\\s*([0-9]*)\\s*ns,"))
+ goto out_ts;
+ if (!check_regcomp(&free_ts_nsec_pattern, "free_ts\\s*([0-9]*)\\s*ns"))
+ goto out_free_ts;
+
fstat(fileno(fin), &st);
max_size = st.st_size / 100; /* hack ... */
@@ -804,7 +813,7 @@ int main(int argc, char **argv)
ext_buf = malloc(BUF_SIZE);
if (!list || !buf || !ext_buf) {
fprintf(stderr, "Out of memory\n");
- exit(1);
+ goto out_free;
}
for ( ; ; ) {
@@ -812,7 +821,8 @@ int main(int argc, char **argv)
if (buf_len < 0)
break;
- add_list(buf, buf_len, ext_buf);
+ if (!add_list(buf, buf_len, ext_buf))
+ goto out_free;
}
printf("loaded %d\n", list_size);
@@ -862,11 +872,26 @@ int main(int argc, char **argv)
fprintf(fout, "\n");
}
}
- regfree(&order_pattern);
- regfree(&pid_pattern);
- regfree(&tgid_pattern);
- regfree(&comm_pattern);
- regfree(&ts_nsec_pattern);
+
+out_free:
+ if (ext_buf)
+ free(ext_buf);
+ if (buf)
+ free(buf);
+ if (list)
+ free(list);
+out_free_ts:
regfree(&free_ts_nsec_pattern);
+out_ts:
+ regfree(&ts_nsec_pattern);
+out_comm:
+ regfree(&comm_pattern);
+out_tgid:
+ regfree(&tgid_pattern);
+out_pid:
+ regfree(&pid_pattern);
+out_order:
+ regfree(&order_pattern);
+
return 0;
}
diff --git a/tools/vm/slabinfo-gnuplot.sh b/tools/mm/slabinfo-gnuplot.sh
index 873a892147e5..873a892147e5 100644
--- a/tools/vm/slabinfo-gnuplot.sh
+++ b/tools/mm/slabinfo-gnuplot.sh
diff --git a/tools/vm/slabinfo.c b/tools/mm/slabinfo.c
index cfaeaea71042..cfaeaea71042 100644
--- a/tools/vm/slabinfo.c
+++ b/tools/mm/slabinfo.c
diff --git a/tools/net/ynl/cli.py b/tools/net/ynl/cli.py
index db410b74d539..ffaa8038aa8c 100755
--- a/tools/net/ynl/cli.py
+++ b/tools/net/ynl/cli.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
import argparse
import json
diff --git a/tools/net/ynl/lib/.gitignore b/tools/net/ynl/lib/.gitignore
new file mode 100644
index 000000000000..c18dd8d83cee
--- /dev/null
+++ b/tools/net/ynl/lib/.gitignore
@@ -0,0 +1 @@
+__pycache__/
diff --git a/tools/net/ynl/lib/__init__.py b/tools/net/ynl/lib/__init__.py
index 3c73f59eabab..4b3797fe784b 100644
--- a/tools/net/ynl/lib/__init__.py
+++ b/tools/net/ynl/lib/__init__.py
@@ -1,7 +1,8 @@
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
-from .nlspec import SpecAttr, SpecAttrSet, SpecFamily, SpecOperation
+from .nlspec import SpecAttr, SpecAttrSet, SpecEnumEntry, SpecEnumSet, \
+ SpecFamily, SpecOperation
from .ynl import YnlFamily
-__all__ = ["SpecAttr", "SpecAttrSet", "SpecFamily", "SpecOperation",
- "YnlFamily"]
+__all__ = ["SpecAttr", "SpecAttrSet", "SpecEnumEntry", "SpecEnumSet",
+ "SpecFamily", "SpecOperation", "YnlFamily"]
diff --git a/tools/net/ynl/lib/nlspec.py b/tools/net/ynl/lib/nlspec.py
index e204679ad8b7..d04450c2a44a 100644
--- a/tools/net/ynl/lib/nlspec.py
+++ b/tools/net/ynl/lib/nlspec.py
@@ -1,9 +1,8 @@
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
import collections
import importlib
import os
-import traceback
import yaml
@@ -58,6 +57,92 @@ class SpecElement:
pass
+class SpecEnumEntry(SpecElement):
+ """ Entry within an enum declared in the Netlink spec.
+
+ Attributes:
+ doc documentation string
+ enum_set back reference to the enum
+ value numerical value of this enum (use accessors in most situations!)
+
+ Methods:
+ raw_value raw value, i.e. the id in the enum, unlike user value which is a mask for flags
+ user_value user value, same as raw value for enums, for flags it's the mask
+ """
+ def __init__(self, enum_set, yaml, prev, value_start):
+ if isinstance(yaml, str):
+ yaml = {'name': yaml}
+ super().__init__(enum_set.family, yaml)
+
+ self.doc = yaml.get('doc', '')
+ self.enum_set = enum_set
+
+ if 'value' in yaml:
+ self.value = yaml['value']
+ elif prev:
+ self.value = prev.value + 1
+ else:
+ self.value = value_start
+
+ def has_doc(self):
+ return bool(self.doc)
+
+ def raw_value(self):
+ return self.value
+
+ def user_value(self):
+ if self.enum_set['type'] == 'flags':
+ return 1 << self.value
+ else:
+ return self.value
+
+
+class SpecEnumSet(SpecElement):
+ """ Enum type
+
+ Represents an enumeration (list of numerical constants)
+ as declared in the "definitions" section of the spec.
+
+ Attributes:
+ type enum or flags
+ entries entries by name
+ entries_by_val entries by value
+ Methods:
+ get_mask for flags compute the mask of all defined values
+ """
+ def __init__(self, family, yaml):
+ super().__init__(family, yaml)
+
+ self.type = yaml['type']
+
+ prev_entry = None
+ value_start = self.yaml.get('value-start', 0)
+ self.entries = dict()
+ self.entries_by_val = dict()
+ for entry in self.yaml['entries']:
+ e = self.new_entry(entry, prev_entry, value_start)
+ self.entries[e.name] = e
+ self.entries_by_val[e.raw_value()] = e
+ prev_entry = e
+
+ def new_entry(self, entry, prev_entry, value_start):
+ return SpecEnumEntry(self, entry, prev_entry, value_start)
+
+ def has_doc(self):
+ if 'doc' in self.yaml:
+ return True
+ for entry in self.entries.values():
+ if entry.has_doc():
+ return True
+ return False
+
+ def get_mask(self):
+ mask = 0
+ for e in self.entries.values():
+ mask += e.user_value()
+ return mask
+
+
class SpecAttr(SpecElement):
""" Single Netlink atttribute type
@@ -96,15 +181,22 @@ class SpecAttrSet(SpecElement):
self.attrs = collections.OrderedDict()
self.attrs_by_val = collections.OrderedDict()
- val = 0
- for elem in self.yaml['attributes']:
- if 'value' in elem:
- val = elem['value']
+ if self.subset_of is None:
+ val = 1
+ for elem in self.yaml['attributes']:
+ if 'value' in elem:
+ val = elem['value']
- attr = self.new_attr(elem, val)
- self.attrs[attr.name] = attr
- self.attrs_by_val[attr.value] = attr
- val += 1
+ attr = self.new_attr(elem, val)
+ self.attrs[attr.name] = attr
+ self.attrs_by_val[attr.value] = attr
+ val += 1
+ else:
+ real_set = family.attr_sets[self.subset_of]
+ for elem in self.yaml['attributes']:
+ attr = real_set[elem['name']]
+ self.attrs[attr.name] = attr
+ self.attrs_by_val[attr.value] = attr
def new_attr(self, elem, value):
return SpecAttr(self.family, self, elem, value)
@@ -182,14 +274,23 @@ class SpecFamily(SpecElement):
Attributes:
proto protocol type (e.g. genetlink)
+ license spec license (loaded from an SPDX tag on the spec)
attr_sets dict of attribute sets
msgs dict of all messages (index by name)
msgs_by_value dict of all messages (indexed by name)
ops dict of all valid requests / responses
+ consts dict of all constants/enums
"""
def __init__(self, spec_path, schema_path=None):
with open(spec_path, "r") as stream:
+ prefix = '# SPDX-License-Identifier: '
+ first = stream.readline().strip()
+ if not first.startswith(prefix):
+ raise Exception('SPDX license tag required in the spec')
+ self.license = first[len(prefix):]
+
+ stream.seek(0)
spec = yaml.safe_load(stream)
self._resolution_list = []
@@ -216,6 +317,7 @@ class SpecFamily(SpecElement):
self.req_by_value = collections.OrderedDict()
self.rsp_by_value = collections.OrderedDict()
self.ops = collections.OrderedDict()
+ self.consts = collections.OrderedDict()
last_exception = None
while len(self._resolution_list) > 0:
@@ -234,8 +336,10 @@ class SpecFamily(SpecElement):
resolved.append(elem)
if len(resolved) == 0:
- traceback.print_exception(last_exception)
- raise Exception("Could not resolve any spec element, infinite loop?")
+ raise last_exception
+
+ def new_enum(self, elem):
+ return SpecEnumSet(self, elem)
def new_attr_set(self, elem):
return SpecAttrSet(self, elem)
@@ -247,7 +351,7 @@ class SpecFamily(SpecElement):
self._resolution_list.append(elem)
def _dictify_ops_unified(self):
- val = 0
+ val = 1
for elem in self.yaml['operations']['list']:
if 'value' in elem:
val = elem['value']
@@ -258,7 +362,7 @@ class SpecFamily(SpecElement):
self.msgs[op.name] = op
def _dictify_ops_directional(self):
- req_val = rsp_val = 0
+ req_val = rsp_val = 1
for elem in self.yaml['operations']['list']:
if 'notify' in elem:
if 'value' in elem:
@@ -291,6 +395,13 @@ class SpecFamily(SpecElement):
def resolve(self):
self.resolve_up(super())
+ definitions = self.yaml.get('definitions', [])
+ for elem in definitions:
+ if elem['type'] == 'enum' or elem['type'] == 'flags':
+ self.consts[elem['name']] = self.new_enum(elem)
+ else:
+ self.consts[elem['name']] = elem
+
for elem in self.yaml['attribute-sets']:
attr_set = self.new_attr_set(elem)
self.attr_sets[elem['name']] = attr_set
diff --git a/tools/net/ynl/lib/ynl.py b/tools/net/ynl/lib/ynl.py
index 1c7411ee04dc..32536e1f9064 100644
--- a/tools/net/ynl/lib/ynl.py
+++ b/tools/net/ynl/lib/ynl.py
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
import functools
import os
@@ -200,7 +200,7 @@ def _genl_msg(nl_type, nl_flags, genl_cmd, genl_version, seq=None):
if seq is None:
seq = random.randint(1, 1024)
nlmsg = struct.pack("HHII", nl_type, nl_flags, seq, 0)
- genlmsg = struct.pack("bbH", genl_cmd, genl_version, 0)
+ genlmsg = struct.pack("BBH", genl_cmd, genl_version, 0)
return nlmsg + genlmsg
@@ -264,7 +264,7 @@ class GenlMsg:
self.hdr = nl_msg.raw[0:4]
self.raw = nl_msg.raw[4:]
- self.genl_cmd, self.genl_version, _ = struct.unpack("bbH", self.hdr)
+ self.genl_cmd, self.genl_version, _ = struct.unpack("BBH", self.hdr)
self.raw_attrs = NlAttrs(self.raw)
@@ -303,11 +303,6 @@ class YnlFamily(SpecFamily):
self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_CAP_ACK, 1)
self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_EXT_ACK, 1)
- self._types = dict()
-
- for elem in self.yaml.get('definitions', []):
- self._types[elem['name']] = elem
-
self.async_msg_ids = set()
self.async_msg_queue = []
@@ -353,17 +348,17 @@ class YnlFamily(SpecFamily):
def _decode_enum(self, rsp, attr_spec):
raw = rsp[attr_spec['name']]
- enum = self._types[attr_spec['enum']]
+ enum = self.consts[attr_spec['enum']]
i = attr_spec.get('value-start', 0)
if 'enum-as-flags' in attr_spec and attr_spec['enum-as-flags']:
value = set()
while raw:
if raw & 1:
- value.add(enum['entries'][i])
+ value.add(enum.entries_by_val[i].name)
raw >>= 1
i += 1
else:
- value = enum['entries'][raw - i]
+ value = enum.entries_by_val[raw - i].name
rsp[attr_spec['name']] = value
def _decode(self, attrs, space):
diff --git a/tools/net/ynl/ynl-gen-c.py b/tools/net/ynl/ynl-gen-c.py
index 3942f24b9163..c16671a02621 100755
--- a/tools/net/ynl/ynl-gen-c.py
+++ b/tools/net/ynl/ynl-gen-c.py
@@ -1,11 +1,12 @@
#!/usr/bin/env python3
+# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
import argparse
import collections
import os
import yaml
-from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation
+from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, SpecEnumEntry
def c_upper(name):
@@ -546,7 +547,7 @@ class Struct:
max_val = 0
self.attr_max_val = None
for name, attr in self.attr_list:
- if attr.value > max_val:
+ if attr.value >= max_val:
max_val = attr.value
self.attr_max_val = attr
self.attrs[name] = attr
@@ -566,97 +567,37 @@ class Struct:
self.inherited = [c_lower(x) for x in sorted(self._inherited)]
-class EnumEntry:
+class EnumEntry(SpecEnumEntry):
def __init__(self, enum_set, yaml, prev, value_start):
- if isinstance(yaml, str):
- self.name = yaml
- yaml = {}
- self.doc = ''
- else:
- self.name = yaml['name']
- self.doc = yaml.get('doc', '')
-
- self.yaml = yaml
- self.enum_set = enum_set
- self.c_name = c_upper(enum_set.value_pfx + self.name)
-
- if 'value' in yaml:
- self.value = yaml['value']
- if prev:
- self.value_change = (self.value != prev.value + 1)
- elif prev:
- self.value_change = False
- self.value = prev.value + 1
+ super().__init__(enum_set, yaml, prev, value_start)
+
+ if prev:
+ self.value_change = (self.value != prev.value + 1)
else:
- self.value = value_start
self.value_change = (self.value != 0)
-
self.value_change = self.value_change or self.enum_set['type'] == 'flags'
- def __getitem__(self, key):
- return self.yaml[key]
-
- def __contains__(self, key):
- return key in self.yaml
-
- def has_doc(self):
- return bool(self.doc)
+ # Added by resolve:
+ self.c_name = None
+ delattr(self, "c_name")
- # raw value, i.e. the id in the enum, unlike user value which is a mask for flags
- def raw_value(self):
- return self.value
+ def resolve(self):
+ self.resolve_up(super())
- # user value, same as raw value for enums, for flags it's the mask
- def user_value(self):
- if self.enum_set['type'] == 'flags':
- return 1 << self.value
- else:
- return self.value
+ self.c_name = c_upper(self.enum_set.value_pfx + self.name)
-class EnumSet:
+class EnumSet(SpecEnumSet):
def __init__(self, family, yaml):
- self.yaml = yaml
- self.family = family
-
self.render_name = c_lower(family.name + '-' + yaml['name'])
self.enum_name = 'enum ' + self.render_name
self.value_pfx = yaml.get('name-prefix', f"{family.name}-{yaml['name']}-")
- self.type = yaml['type']
-
- prev_entry = None
- value_start = self.yaml.get('value-start', 0)
- self.entries = {}
- self.entry_list = []
- for entry in self.yaml['entries']:
- e = EnumEntry(self, entry, prev_entry, value_start)
- self.entries[e.name] = e
- self.entry_list.append(e)
- prev_entry = e
-
- def __getitem__(self, key):
- return self.yaml[key]
-
- def __contains__(self, key):
- return key in self.yaml
-
- def has_doc(self):
- if 'doc' in self.yaml:
- return True
- for entry in self.entry_list:
- if entry.has_doc():
- return True
- return False
+ super().__init__(family, yaml)
- def get_mask(self):
- mask = 0
- idx = self.yaml.get('value-start', 0)
- for _ in self.entry_list:
- mask |= 1 << idx
- idx += 1
- return mask
+ def new_entry(self, entry, prev_entry, value_start):
+ return EnumEntry(self, entry, prev_entry, value_start)
class AttrSet(SpecAttrSet):
@@ -791,8 +732,6 @@ class Family(SpecFamily):
self.mcgrps = self.yaml.get('mcast-groups', {'list': []})
- self.consts = dict()
-
self.hooks = dict()
for when in ['pre', 'post']:
self.hooks[when] = dict()
@@ -819,6 +758,9 @@ class Family(SpecFamily):
if self.kernel_policy == 'global':
self._load_global_policy()
+ def new_enum(self, elem):
+ return EnumSet(self, elem)
+
def new_attr_set(self, elem):
return AttrSet(self, elem)
@@ -836,12 +778,6 @@ class Family(SpecFamily):
}
def _dictify(self):
- for elem in self.yaml['definitions']:
- if elem['type'] == 'enum' or elem['type'] == 'flags':
- self.consts[elem['name']] = EnumSet(self, elem)
- else:
- self.consts[elem['name']] = elem
-
ntf = []
for msg in self.msgs.values():
if 'notify' in msg:
@@ -1979,7 +1915,7 @@ def render_uapi(family, cw):
if 'doc' in enum:
doc = ' - ' + enum['doc']
cw.write_doc_line(enum.enum_name + doc)
- for entry in enum.entry_list:
+ for entry in enum.entries.values():
if entry.has_doc():
doc = '@' + entry.c_name + ': ' + entry['doc']
cw.write_doc_line(doc)
@@ -1987,7 +1923,7 @@ def render_uapi(family, cw):
uapi_enum_start(family, cw, const, 'name')
name_pfx = const.get('name-prefix', f"{family.name}-{const['name']}-")
- for entry in enum.entry_list:
+ for entry in enum.entries.values():
suffix = ','
if entry.value_change:
suffix = f" = {entry.user_value()}" + suffix
@@ -1995,9 +1931,14 @@ def render_uapi(family, cw):
if const.get('render-max', False):
cw.nl()
- max_name = c_upper(name_pfx + 'max')
- cw.p('__' + max_name + ',')
- cw.p(max_name + ' = (__' + max_name + ' - 1)')
+ if const['type'] == 'flags':
+ max_name = c_upper(name_pfx + 'mask')
+ max_val = f' = {enum.get_mask()},'
+ cw.p(max_name + max_val)
+ else:
+ max_name = c_upper(name_pfx + 'max')
+ cw.p('__' + max_name + ',')
+ cw.p(max_name + ' = (__' + max_name + ' - 1)')
cw.block_end(line=';')
cw.nl()
elif const['type'] == 'const':
@@ -2044,14 +1985,17 @@ def render_uapi(family, cw):
max_value = f"({cnt_name} - 1)"
uapi_enum_start(family, cw, family['operations'], 'enum-name')
+ val = 0
for op in family.msgs.values():
if separate_ntf and ('notify' in op or 'event' in op):
continue
suffix = ','
- if 'value' in op:
- suffix = f" = {op['value']},"
+ if op.value != val:
+ suffix = f" = {op.value},"
+ val = op.value
cw.p(op.enum_name + suffix)
+ val += 1
cw.nl()
cw.p(cnt_name + ('' if max_by_define else ','))
if not max_by_define:
@@ -2115,6 +2059,10 @@ def main():
try:
parsed = Family(args.spec)
+ if parsed.license != '((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)':
+ print('Spec license:', parsed.license)
+ print('License must be: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)')
+ os.sys.exit(1)
except yaml.YAMLError as exc:
print(exc)
os.sys.exit(1)
@@ -2123,13 +2071,10 @@ def main():
cw = CodeWriter(BaseNlLib(), out_file)
_, spec_kernel = find_kernel_root(args.spec)
- if args.mode == 'uapi':
- cw.p('/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */')
+ if args.mode == 'uapi' or args.header:
+ cw.p(f'/* SPDX-License-Identifier: {parsed.license} */')
else:
- if args.header:
- cw.p('/* SPDX-License-Identifier: BSD-3-Clause */')
- else:
- cw.p('// SPDX-License-Identifier: BSD-3-Clause')
+ cw.p(f'// SPDX-License-Identifier: {parsed.license}')
cw.p("/* Do not edit directly, auto-generated from: */")
cw.p(f"/*\t{spec_kernel} */")
cw.p(f"/* YNL-GEN {args.mode} {'header' if args.header else 'source'} */")
diff --git a/tools/net/ynl/ynl-regen.sh b/tools/net/ynl/ynl-regen.sh
index 43989ae48ed0..74f5de1c2399 100755
--- a/tools/net/ynl/ynl-regen.sh
+++ b/tools/net/ynl/ynl-regen.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
TOOL=$(dirname $(realpath $0))/ynl-gen-c.py
diff --git a/tools/objtool/.gitignore b/tools/objtool/.gitignore
index 14236db3677f..4faa4dd72f35 100644
--- a/tools/objtool/.gitignore
+++ b/tools/objtool/.gitignore
@@ -2,3 +2,4 @@
arch/x86/lib/inat-tables.c
/objtool
fixdep
+libsubcmd/
diff --git a/tools/objtool/Build b/tools/objtool/Build
index 33f2ee5a46d3..a3cdf8af6635 100644
--- a/tools/objtool/Build
+++ b/tools/objtool/Build
@@ -16,8 +16,6 @@ objtool-y += libctype.o
objtool-y += str_error_r.o
objtool-y += librbtree.o
-CFLAGS += -I$(srctree)/tools/lib
-
$(OUTPUT)libstring.o: ../lib/string.c FORCE
$(call rule_mkdir)
$(call if_changed_dep,cc_o_c)
diff --git a/tools/objtool/Documentation/objtool.txt b/tools/objtool/Documentation/objtool.txt
index 8a671902a187..8e53fc6735ef 100644
--- a/tools/objtool/Documentation/objtool.txt
+++ b/tools/objtool/Documentation/objtool.txt
@@ -410,6 +410,14 @@ the objtool maintainers.
can remove this warning by putting the ANNOTATE_INTRA_FUNCTION_CALL
directive right before the call.
+12. file.o: warning: func(): not an indirect call target
+
+ This means that objtool is running with --ibt and a function expected
+ to be an indirect call target is not. In particular, this happens for
+ init_module() or cleanup_module() if a module relies on these special
+ names and does not use module_init() / module_exit() macros to create
+ them.
+
If the error doesn't seem to make sense, it could be a bug in objtool.
Feel free to ask the objtool maintainer for help.
diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index a3a9cc24e0e3..83b100c1e7f6 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -2,19 +2,18 @@
include ../scripts/Makefile.include
include ../scripts/Makefile.arch
-# always use the host compiler
-AR = $(HOSTAR)
-CC = $(HOSTCC)
-LD = $(HOSTLD)
-
ifeq ($(srctree),)
srctree := $(patsubst %/,%,$(dir $(CURDIR)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
endif
-SUBCMD_SRCDIR = $(srctree)/tools/lib/subcmd/
-LIBSUBCMD_OUTPUT = $(or $(OUTPUT),$(CURDIR)/)
-LIBSUBCMD = $(LIBSUBCMD_OUTPUT)libsubcmd.a
+LIBSUBCMD_DIR = $(srctree)/tools/lib/subcmd/
+ifneq ($(OUTPUT),)
+ LIBSUBCMD_OUTPUT = $(abspath $(OUTPUT))/libsubcmd
+else
+ LIBSUBCMD_OUTPUT = $(CURDIR)/libsubcmd
+endif
+LIBSUBCMD = $(LIBSUBCMD_OUTPUT)/libsubcmd.a
OBJTOOL := $(OUTPUT)objtool
OBJTOOL_IN := $(OBJTOOL)-in.o
@@ -28,16 +27,29 @@ INCLUDES := -I$(srctree)/tools/include \
-I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi \
-I$(srctree)/tools/arch/$(SRCARCH)/include \
-I$(srctree)/tools/objtool/include \
- -I$(srctree)/tools/objtool/arch/$(SRCARCH)/include
+ -I$(srctree)/tools/objtool/arch/$(SRCARCH)/include \
+ -I$(LIBSUBCMD_OUTPUT)/include
+# Note, EXTRA_WARNINGS here was determined for CC and not HOSTCC, it
+# is passed here to match a legacy behavior.
WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed -Wno-nested-externs
-CFLAGS := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS)
-LDFLAGS += $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS)
+OBJTOOL_CFLAGS := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS)
+OBJTOOL_LDFLAGS := $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS)
# Allow old libelf to be used:
-elfshdr := $(shell echo '$(pound)include <libelf.h>' | $(CC) $(CFLAGS) -x c -E - | grep elf_getshdr)
-CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED)
+elfshdr := $(shell echo '$(pound)include <libelf.h>' | $(HOSTCC) $(OBJTOOL_CFLAGS) -x c -E - | grep elf_getshdr)
+OBJTOOL_CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED)
+
+# Always want host compilation.
+HOST_OVERRIDES := CC="$(HOSTCC)" LD="$(HOSTLD)" AR="$(HOSTAR)"
AWK = awk
+MKDIR = mkdir
+
+ifeq ($(V),1)
+ Q =
+else
+ Q = @
+endif
BUILD_ORC := n
@@ -49,21 +61,33 @@ export BUILD_ORC
export srctree OUTPUT CFLAGS SRCARCH AWK
include $(srctree)/tools/build/Makefile.include
-$(OBJTOOL_IN): fixdep FORCE
- @$(CONFIG_SHELL) ./sync-check.sh
- @$(MAKE) $(build)=objtool
+$(OBJTOOL_IN): fixdep $(LIBSUBCMD) FORCE
+ $(Q)$(CONFIG_SHELL) ./sync-check.sh
+ $(Q)$(MAKE) $(build)=objtool $(HOST_OVERRIDES) CFLAGS="$(OBJTOOL_CFLAGS)" \
+ LDFLAGS="$(OBJTOOL_LDFLAGS)"
+
$(OBJTOOL): $(LIBSUBCMD) $(OBJTOOL_IN)
- $(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@
+ $(QUIET_LINK)$(HOSTCC) $(OBJTOOL_IN) $(OBJTOOL_LDFLAGS) -o $@
+
+
+$(LIBSUBCMD_OUTPUT):
+ $(Q)$(MKDIR) -p $@
+$(LIBSUBCMD): fixdep $(LIBSUBCMD_OUTPUT) FORCE
+ $(Q)$(MAKE) -C $(LIBSUBCMD_DIR) O=$(LIBSUBCMD_OUTPUT) \
+ DESTDIR=$(LIBSUBCMD_OUTPUT) prefix= subdir= \
+ $(HOST_OVERRIDES) EXTRA_CFLAGS="$(OBJTOOL_CFLAGS)" \
+ $@ install_headers
-$(LIBSUBCMD): fixdep FORCE
- $(Q)$(MAKE) -C $(SUBCMD_SRCDIR) OUTPUT=$(LIBSUBCMD_OUTPUT)
+$(LIBSUBCMD)-clean:
+ $(call QUIET_CLEAN, libsubcmd)
+ $(Q)$(RM) -r -- $(LIBSUBCMD_OUTPUT)
-clean:
+clean: $(LIBSUBCMD)-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 $(LIBSUBCMD)
+ $(Q)$(RM) $(OUTPUT)arch/x86/lib/inat-tables.c $(OUTPUT)fixdep
FORCE:
diff --git a/tools/objtool/arch/powerpc/decode.c b/tools/objtool/arch/powerpc/decode.c
index 9c653805a08a..53b55690f320 100644
--- a/tools/objtool/arch/powerpc/decode.c
+++ b/tools/objtool/arch/powerpc/decode.c
@@ -41,38 +41,36 @@ const char *arch_ret_insn(int len)
int arch_decode_instruction(struct objtool_file *file, const struct section *sec,
unsigned long offset, unsigned int maxlen,
- unsigned int *len, enum insn_type *type,
- unsigned long *immediate,
- struct list_head *ops_list)
+ struct instruction *insn)
{
unsigned int opcode;
enum insn_type typ;
unsigned long imm;
- u32 insn;
+ u32 ins;
- insn = bswap_if_needed(file->elf, *(u32 *)(sec->data->d_buf + offset));
- opcode = insn >> 26;
+ ins = bswap_if_needed(file->elf, *(u32 *)(sec->data->d_buf + offset));
+ opcode = ins >> 26;
typ = INSN_OTHER;
imm = 0;
switch (opcode) {
case 18: /* b[l][a] */
- if ((insn & 3) == 1) /* bl */
+ if ((ins & 3) == 1) /* bl */
typ = INSN_CALL;
- imm = insn & 0x3fffffc;
+ imm = ins & 0x3fffffc;
if (imm & 0x2000000)
imm -= 0x4000000;
break;
}
if (opcode == 1)
- *len = 8;
+ insn->len = 8;
else
- *len = 4;
+ insn->len = 4;
- *type = typ;
- *immediate = imm;
+ insn->type = typ;
+ insn->immediate = imm;
return 0;
}
diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c
index e7b030f7e2a5..9ef024fd648c 100644
--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -105,7 +105,7 @@ bool arch_pc_relative_reloc(struct reloc *reloc)
#define ADD_OP(op) \
if (!(op = calloc(1, sizeof(*op)))) \
return -1; \
- else for (list_add_tail(&op->list, ops_list); op; op = NULL)
+ else for (*ops_list = op, ops_list = &op->next; op; op = NULL)
/*
* Helpers to decode ModRM/SIB:
@@ -146,12 +146,11 @@ static bool has_notrack_prefix(struct insn *insn)
int arch_decode_instruction(struct objtool_file *file, const struct section *sec,
unsigned long offset, unsigned int maxlen,
- unsigned int *len, enum insn_type *type,
- unsigned long *immediate,
- struct list_head *ops_list)
+ struct instruction *insn)
{
+ struct stack_op **ops_list = &insn->stack_ops;
const struct elf *elf = file->elf;
- struct insn insn;
+ struct insn ins;
int x86_64, ret;
unsigned char op1, op2, op3, prefix,
rex = 0, rex_b = 0, rex_r = 0, rex_w = 0, rex_x = 0,
@@ -165,42 +164,42 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
if (x86_64 == -1)
return -1;
- ret = insn_decode(&insn, sec->data->d_buf + offset, maxlen,
+ ret = insn_decode(&ins, sec->data->d_buf + offset, maxlen,
x86_64 ? INSN_MODE_64 : INSN_MODE_32);
if (ret < 0) {
WARN("can't decode instruction at %s:0x%lx", sec->name, offset);
return -1;
}
- *len = insn.length;
- *type = INSN_OTHER;
+ insn->len = ins.length;
+ insn->type = INSN_OTHER;
- if (insn.vex_prefix.nbytes)
+ if (ins.vex_prefix.nbytes)
return 0;
- prefix = insn.prefixes.bytes[0];
+ prefix = ins.prefixes.bytes[0];
- op1 = insn.opcode.bytes[0];
- op2 = insn.opcode.bytes[1];
- op3 = insn.opcode.bytes[2];
+ op1 = ins.opcode.bytes[0];
+ op2 = ins.opcode.bytes[1];
+ op3 = ins.opcode.bytes[2];
- if (insn.rex_prefix.nbytes) {
- rex = insn.rex_prefix.bytes[0];
+ if (ins.rex_prefix.nbytes) {
+ rex = ins.rex_prefix.bytes[0];
rex_w = X86_REX_W(rex) >> 3;
rex_r = X86_REX_R(rex) >> 2;
rex_x = X86_REX_X(rex) >> 1;
rex_b = X86_REX_B(rex);
}
- if (insn.modrm.nbytes) {
- modrm = insn.modrm.bytes[0];
+ if (ins.modrm.nbytes) {
+ modrm = ins.modrm.bytes[0];
modrm_mod = X86_MODRM_MOD(modrm);
modrm_reg = X86_MODRM_REG(modrm) + 8*rex_r;
modrm_rm = X86_MODRM_RM(modrm) + 8*rex_b;
}
- if (insn.sib.nbytes) {
- sib = insn.sib.bytes[0];
+ if (ins.sib.nbytes) {
+ sib = ins.sib.bytes[0];
/* sib_scale = X86_SIB_SCALE(sib); */
sib_index = X86_SIB_INDEX(sib) + 8*rex_x;
sib_base = X86_SIB_BASE(sib) + 8*rex_b;
@@ -254,7 +253,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
break;
case 0x70 ... 0x7f:
- *type = INSN_JUMP_CONDITIONAL;
+ insn->type = INSN_JUMP_CONDITIONAL;
break;
case 0x80 ... 0x83:
@@ -278,7 +277,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
if (!rm_is_reg(CFI_SP))
break;
- imm = insn.immediate.value;
+ imm = ins.immediate.value;
if (op1 & 2) { /* sign extend */
if (op1 & 1) { /* imm32 */
imm <<= 32;
@@ -309,7 +308,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
ADD_OP(op) {
op->src.type = OP_SRC_AND;
op->src.reg = CFI_SP;
- op->src.offset = insn.immediate.value;
+ op->src.offset = ins.immediate.value;
op->dest.type = OP_DEST_REG;
op->dest.reg = CFI_SP;
}
@@ -356,7 +355,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
op->src.reg = CFI_SP;
op->dest.type = OP_DEST_REG_INDIRECT;
op->dest.reg = modrm_rm;
- op->dest.offset = insn.displacement.value;
+ op->dest.offset = ins.displacement.value;
}
break;
}
@@ -389,7 +388,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
op->src.reg = modrm_reg;
op->dest.type = OP_DEST_REG_INDIRECT;
op->dest.reg = CFI_BP;
- op->dest.offset = insn.displacement.value;
+ op->dest.offset = ins.displacement.value;
}
break;
}
@@ -402,7 +401,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
op->src.reg = modrm_reg;
op->dest.type = OP_DEST_REG_INDIRECT;
op->dest.reg = CFI_SP;
- op->dest.offset = insn.displacement.value;
+ op->dest.offset = ins.displacement.value;
}
break;
}
@@ -419,7 +418,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
ADD_OP(op) {
op->src.type = OP_SRC_REG_INDIRECT;
op->src.reg = CFI_BP;
- op->src.offset = insn.displacement.value;
+ op->src.offset = ins.displacement.value;
op->dest.type = OP_DEST_REG;
op->dest.reg = modrm_reg;
}
@@ -432,7 +431,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
ADD_OP(op) {
op->src.type = OP_SRC_REG_INDIRECT;
op->src.reg = CFI_SP;
- op->src.offset = insn.displacement.value;
+ op->src.offset = ins.displacement.value;
op->dest.type = OP_DEST_REG;
op->dest.reg = modrm_reg;
}
@@ -464,7 +463,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
/* lea disp(%src), %dst */
ADD_OP(op) {
- op->src.offset = insn.displacement.value;
+ op->src.offset = ins.displacement.value;
if (!op->src.offset) {
/* lea (%src), %dst */
op->src.type = OP_SRC_REG;
@@ -487,7 +486,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
break;
case 0x90:
- *type = INSN_NOP;
+ insn->type = INSN_NOP;
break;
case 0x9c:
@@ -511,39 +510,39 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
if (op2 == 0x01) {
if (modrm == 0xca)
- *type = INSN_CLAC;
+ insn->type = INSN_CLAC;
else if (modrm == 0xcb)
- *type = INSN_STAC;
+ insn->type = INSN_STAC;
} else if (op2 >= 0x80 && op2 <= 0x8f) {
- *type = INSN_JUMP_CONDITIONAL;
+ insn->type = INSN_JUMP_CONDITIONAL;
} else if (op2 == 0x05 || op2 == 0x07 || op2 == 0x34 ||
op2 == 0x35) {
/* sysenter, sysret */
- *type = INSN_CONTEXT_SWITCH;
+ insn->type = INSN_CONTEXT_SWITCH;
} else if (op2 == 0x0b || op2 == 0xb9) {
/* ud2 */
- *type = INSN_BUG;
+ insn->type = INSN_BUG;
} else if (op2 == 0x0d || op2 == 0x1f) {
/* nopl/nopw */
- *type = INSN_NOP;
+ insn->type = INSN_NOP;
} else if (op2 == 0x1e) {
if (prefix == 0xf3 && (modrm == 0xfa || modrm == 0xfb))
- *type = INSN_ENDBR;
+ insn->type = INSN_ENDBR;
} else if (op2 == 0x38 && op3 == 0xf8) {
- if (insn.prefixes.nbytes == 1 &&
- insn.prefixes.bytes[0] == 0xf2) {
+ if (ins.prefixes.nbytes == 1 &&
+ ins.prefixes.bytes[0] == 0xf2) {
/* ENQCMD cannot be used in the kernel. */
WARN("ENQCMD instruction at %s:%lx", sec->name,
offset);
@@ -591,29 +590,29 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
case 0xcc:
/* int3 */
- *type = INSN_TRAP;
+ insn->type = INSN_TRAP;
break;
case 0xe3:
/* jecxz/jrcxz */
- *type = INSN_JUMP_CONDITIONAL;
+ insn->type = INSN_JUMP_CONDITIONAL;
break;
case 0xe9:
case 0xeb:
- *type = INSN_JUMP_UNCONDITIONAL;
+ insn->type = INSN_JUMP_UNCONDITIONAL;
break;
case 0xc2:
case 0xc3:
- *type = INSN_RETURN;
+ insn->type = INSN_RETURN;
break;
case 0xc7: /* mov imm, r/m */
if (!opts.noinstr)
break;
- if (insn.length == 3+4+4 && !strncmp(sec->name, ".init.text", 10)) {
+ if (ins.length == 3+4+4 && !strncmp(sec->name, ".init.text", 10)) {
struct reloc *immr, *disp;
struct symbol *func;
int idx;
@@ -661,17 +660,17 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
case 0xca: /* retf */
case 0xcb: /* retf */
- *type = INSN_CONTEXT_SWITCH;
+ insn->type = INSN_CONTEXT_SWITCH;
break;
case 0xe0: /* loopne */
case 0xe1: /* loope */
case 0xe2: /* loop */
- *type = INSN_JUMP_CONDITIONAL;
+ insn->type = INSN_JUMP_CONDITIONAL;
break;
case 0xe8:
- *type = INSN_CALL;
+ insn->type = INSN_CALL;
/*
* For the impact on the stack, a CALL behaves like
* a PUSH of an immediate value (the return address).
@@ -683,30 +682,30 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
break;
case 0xfc:
- *type = INSN_CLD;
+ insn->type = INSN_CLD;
break;
case 0xfd:
- *type = INSN_STD;
+ insn->type = INSN_STD;
break;
case 0xff:
if (modrm_reg == 2 || modrm_reg == 3) {
- *type = INSN_CALL_DYNAMIC;
- if (has_notrack_prefix(&insn))
+ insn->type = INSN_CALL_DYNAMIC;
+ if (has_notrack_prefix(&ins))
WARN("notrack prefix found at %s:0x%lx", sec->name, offset);
} else if (modrm_reg == 4) {
- *type = INSN_JUMP_DYNAMIC;
- if (has_notrack_prefix(&insn))
+ insn->type = INSN_JUMP_DYNAMIC;
+ if (has_notrack_prefix(&ins))
WARN("notrack prefix found at %s:0x%lx", sec->name, offset);
} else if (modrm_reg == 5) {
/* jmpf */
- *type = INSN_CONTEXT_SWITCH;
+ insn->type = INSN_CONTEXT_SWITCH;
} else if (modrm_reg == 6) {
@@ -723,7 +722,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
break;
}
- *immediate = insn.immediate.nbytes ? insn.immediate.value : 0;
+ insn->immediate = ins.immediate.nbytes ? ins.immediate.value : 0;
return 0;
}
diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index a4f39407bf59..7c175198d09f 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -65,7 +65,7 @@ static int parse_hacks(const struct option *opt, const char *str, int unset)
return found ? 0 : -1;
}
-const struct option check_options[] = {
+static const struct option check_options[] = {
OPT_GROUP("Actions:"),
OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake", "patch toolchain bugs/limitations", parse_hacks),
OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"),
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 94b518c12f9a..f937be1afe65 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -23,7 +23,7 @@
#include <linux/static_call_types.h>
struct alternative {
- struct list_head list;
+ struct alternative *next;
struct instruction *insn;
bool skip_orig;
};
@@ -47,27 +47,29 @@ struct instruction *find_insn(struct objtool_file *file,
return NULL;
}
-static struct instruction *next_insn_same_sec(struct objtool_file *file,
- struct instruction *insn)
+struct instruction *next_insn_same_sec(struct objtool_file *file,
+ struct instruction *insn)
{
- struct instruction *next = list_next_entry(insn, list);
+ if (insn->idx == INSN_CHUNK_MAX)
+ return find_insn(file, insn->sec, insn->offset + insn->len);
- if (!next || &next->list == &file->insn_list || next->sec != insn->sec)
+ insn++;
+ if (!insn->len)
return NULL;
- return next;
+ return insn;
}
static struct instruction *next_insn_same_func(struct objtool_file *file,
struct instruction *insn)
{
- struct instruction *next = list_next_entry(insn, list);
+ struct instruction *next = next_insn_same_sec(file, insn);
struct symbol *func = insn_func(insn);
if (!func)
return NULL;
- if (&next->list != &file->insn_list && insn_func(next) == func)
+ if (next && insn_func(next) == func)
return next;
/* Check if we're already in the subfunction: */
@@ -78,17 +80,35 @@ static struct instruction *next_insn_same_func(struct objtool_file *file,
return find_insn(file, func->cfunc->sec, func->cfunc->offset);
}
+static struct instruction *prev_insn_same_sec(struct objtool_file *file,
+ struct instruction *insn)
+{
+ if (insn->idx == 0) {
+ if (insn->prev_len)
+ return find_insn(file, insn->sec, insn->offset - insn->prev_len);
+ return NULL;
+ }
+
+ return insn - 1;
+}
+
static struct instruction *prev_insn_same_sym(struct objtool_file *file,
- struct instruction *insn)
+ struct instruction *insn)
{
- struct instruction *prev = list_prev_entry(insn, list);
+ struct instruction *prev = prev_insn_same_sec(file, insn);
- if (&prev->list != &file->insn_list && insn_func(prev) == insn_func(insn))
+ if (prev && insn_func(prev) == insn_func(insn))
return prev;
return NULL;
}
+#define for_each_insn(file, insn) \
+ for (struct section *__sec, *__fake = (struct section *)1; \
+ __fake; __fake = NULL) \
+ for_each_sec(file, __sec) \
+ sec_for_each_insn(file, __sec, insn)
+
#define func_for_each_insn(file, func, insn) \
for (insn = find_insn(file, func->sec, func->offset); \
insn; \
@@ -96,16 +116,13 @@ static struct instruction *prev_insn_same_sym(struct objtool_file *file,
#define sym_for_each_insn(file, sym, insn) \
for (insn = find_insn(file, sym->sec, sym->offset); \
- insn && &insn->list != &file->insn_list && \
- insn->sec == sym->sec && \
- insn->offset < sym->offset + sym->len; \
- insn = list_next_entry(insn, list))
+ insn && insn->offset < sym->offset + sym->len; \
+ insn = next_insn_same_sec(file, insn))
#define sym_for_each_insn_continue_reverse(file, sym, insn) \
- for (insn = list_prev_entry(insn, list); \
- &insn->list != &file->insn_list && \
- insn->sec == sym->sec && insn->offset >= sym->offset; \
- insn = list_prev_entry(insn, list))
+ for (insn = prev_insn_same_sec(file, insn); \
+ insn && insn->offset >= sym->offset; \
+ insn = prev_insn_same_sec(file, insn))
#define sec_for_each_insn_from(file, insn) \
for (; insn; insn = next_insn_same_sec(file, insn))
@@ -114,16 +131,34 @@ static struct instruction *prev_insn_same_sym(struct objtool_file *file,
for (insn = next_insn_same_sec(file, insn); insn; \
insn = next_insn_same_sec(file, insn))
+static inline struct symbol *insn_call_dest(struct instruction *insn)
+{
+ if (insn->type == INSN_JUMP_DYNAMIC ||
+ insn->type == INSN_CALL_DYNAMIC)
+ return NULL;
+
+ return insn->_call_dest;
+}
+
+static inline struct reloc *insn_jump_table(struct instruction *insn)
+{
+ if (insn->type == INSN_JUMP_DYNAMIC ||
+ insn->type == INSN_CALL_DYNAMIC)
+ return insn->_jump_table;
+
+ return NULL;
+}
+
static bool is_jump_table_jump(struct instruction *insn)
{
struct alt_group *alt_group = insn->alt_group;
- if (insn->jump_table)
+ if (insn_jump_table(insn))
return true;
/* Retpoline alternative for a jump table? */
return alt_group && alt_group->orig_group &&
- alt_group->orig_group->first_insn->jump_table;
+ insn_jump_table(alt_group->orig_group->first_insn);
}
static bool is_sibling_call(struct instruction *insn)
@@ -137,8 +172,8 @@ static bool is_sibling_call(struct instruction *insn)
return !is_jump_table_jump(insn);
}
- /* add_jump_destinations() sets insn->call_dest for sibling calls. */
- return (is_static_jump(insn) && insn->call_dest);
+ /* add_jump_destinations() sets insn_call_dest(insn) for sibling calls. */
+ return (is_static_jump(insn) && insn_call_dest(insn));
}
/*
@@ -274,8 +309,8 @@ static void init_insn_state(struct objtool_file *file, struct insn_state *state,
/*
* We need the full vmlinux for noinstr validation, otherwise we can
- * not correctly determine insn->call_dest->sec (external symbols do
- * not have a section).
+ * not correctly determine insn_call_dest(insn)->sec (external symbols
+ * do not have a section).
*/
if (opts.link && opts.noinstr && sec)
state->noinstr = sec->noinstr;
@@ -366,6 +401,9 @@ static int decode_instructions(struct objtool_file *file)
int ret;
for_each_sec(file, sec) {
+ struct instruction *insns = NULL;
+ u8 prev_len = 0;
+ u8 idx = 0;
if (!(sec->sh.sh_flags & SHF_EXECINSTR))
continue;
@@ -391,26 +429,31 @@ static int decode_instructions(struct objtool_file *file)
sec->init = true;
for (offset = 0; offset < sec->sh.sh_size; offset += insn->len) {
- insn = malloc(sizeof(*insn));
- if (!insn) {
- WARN("malloc failed");
- return -1;
+ if (!insns || idx == INSN_CHUNK_MAX) {
+ insns = calloc(sizeof(*insn), INSN_CHUNK_SIZE);
+ if (!insns) {
+ WARN("malloc failed");
+ return -1;
+ }
+ idx = 0;
+ } else {
+ idx++;
}
- memset(insn, 0, sizeof(*insn));
- INIT_LIST_HEAD(&insn->alts);
- INIT_LIST_HEAD(&insn->stack_ops);
- INIT_LIST_HEAD(&insn->call_node);
+ insn = &insns[idx];
+ insn->idx = idx;
+ INIT_LIST_HEAD(&insn->call_node);
insn->sec = sec;
insn->offset = offset;
+ insn->prev_len = prev_len;
ret = arch_decode_instruction(file, sec, offset,
sec->sh.sh_size - offset,
- &insn->len, &insn->type,
- &insn->immediate,
- &insn->stack_ops);
+ insn);
if (ret)
- goto err;
+ return ret;
+
+ prev_len = insn->len;
/*
* By default, "ud2" is a dead end unless otherwise
@@ -421,10 +464,11 @@ static int decode_instructions(struct objtool_file *file)
insn->dead_end = true;
hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset));
- list_add_tail(&insn->list, &file->insn_list);
nr_insns++;
}
+// printf("%s: last chunk used: %d\n", sec->name, (int)idx);
+
list_for_each_entry(func, &sec->symbol_list, list) {
if (func->type != STT_NOTYPE && func->type != STT_FUNC)
continue;
@@ -467,10 +511,6 @@ static int decode_instructions(struct objtool_file *file)
printf("nr_insns: %lu\n", nr_insns);
return 0;
-
-err:
- free(insn);
- return ret;
}
/*
@@ -585,7 +625,7 @@ static int add_dead_ends(struct objtool_file *file)
}
insn = find_insn(file, reloc->sym->sec, reloc->addend);
if (insn)
- insn = list_prev_entry(insn, list);
+ insn = prev_insn_same_sec(file, insn);
else if (reloc->addend == reloc->sym->sec->sh.sh_size) {
insn = find_last_insn(file, reloc->sym->sec);
if (!insn) {
@@ -620,7 +660,7 @@ reachable:
}
insn = find_insn(file, reloc->sym->sec, reloc->addend);
if (insn)
- insn = list_prev_entry(insn, list);
+ insn = prev_insn_same_sec(file, insn);
else if (reloc->addend == reloc->sym->sec->sh.sh_size) {
insn = find_last_insn(file, reloc->sym->sec);
if (!insn) {
@@ -682,7 +722,7 @@ static int create_static_call_sections(struct objtool_file *file)
return -1;
/* find key symbol */
- key_name = strdup(insn->call_dest->name);
+ key_name = strdup(insn_call_dest(insn)->name);
if (!key_name) {
perror("strdup");
return -1;
@@ -690,6 +730,7 @@ static int create_static_call_sections(struct objtool_file *file)
if (strncmp(key_name, STATIC_CALL_TRAMP_PREFIX_STR,
STATIC_CALL_TRAMP_PREFIX_LEN)) {
WARN("static_call: trampoline name malformed: %s", key_name);
+ free(key_name);
return -1;
}
tmp = key_name + STATIC_CALL_TRAMP_PREFIX_LEN - STATIC_CALL_KEY_PREFIX_LEN;
@@ -699,6 +740,7 @@ static int create_static_call_sections(struct objtool_file *file)
if (!key_sym) {
if (!opts.module) {
WARN("static_call: can't find static_call_key symbol: %s", tmp);
+ free(key_name);
return -1;
}
@@ -711,7 +753,7 @@ static int create_static_call_sections(struct objtool_file *file)
* trampoline address. This is fixed up in
* static_call_add_module().
*/
- key_sym = insn->call_dest;
+ key_sym = insn_call_dest(insn);
}
free(key_name);
@@ -856,8 +898,15 @@ static int create_ibt_endbr_seal_sections(struct objtool_file *file)
list_for_each_entry(insn, &file->endbr_list, call_node) {
int *site = (int *)sec->data->d_buf + idx;
+ struct symbol *sym = insn->sym;
*site = 0;
+ if (opts.module && sym && sym->type == STT_FUNC &&
+ insn->offset == sym->offset &&
+ (!strcmp(sym->name, "init_module") ||
+ !strcmp(sym->name, "cleanup_module")))
+ WARN("%s(): not an indirect call target", sym->name);
+
if (elf_add_reloc_to_insn(file->elf, sec,
idx * sizeof(int),
R_X86_64_PC32,
@@ -1188,6 +1237,8 @@ static const char *uaccess_safe_builtin[] = {
"__tsan_atomic64_compare_exchange_val",
"__tsan_atomic_thread_fence",
"__tsan_atomic_signal_fence",
+ "__tsan_unaligned_read16",
+ "__tsan_unaligned_write16",
/* KCOV */
"write_comp_data",
"check_kcov_mode",
@@ -1300,43 +1351,42 @@ __weak bool arch_is_rethunk(struct symbol *sym)
return false;
}
-#define NEGATIVE_RELOC ((void *)-1L)
-
static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *insn)
{
- if (insn->reloc == NEGATIVE_RELOC)
+ struct reloc *reloc;
+
+ if (insn->no_reloc)
return NULL;
- if (!insn->reloc) {
- if (!file)
- return NULL;
+ if (!file)
+ return NULL;
- insn->reloc = find_reloc_by_dest_range(file->elf, insn->sec,
- insn->offset, insn->len);
- if (!insn->reloc) {
- insn->reloc = NEGATIVE_RELOC;
- return NULL;
- }
+ reloc = find_reloc_by_dest_range(file->elf, insn->sec,
+ insn->offset, insn->len);
+ if (!reloc) {
+ insn->no_reloc = 1;
+ return NULL;
}
- return insn->reloc;
+ return reloc;
}
static void remove_insn_ops(struct instruction *insn)
{
- struct stack_op *op, *tmp;
+ struct stack_op *op, *next;
- list_for_each_entry_safe(op, tmp, &insn->stack_ops, list) {
- list_del(&op->list);
+ for (op = insn->stack_ops; op; op = next) {
+ next = op->next;
free(op);
}
+ insn->stack_ops = NULL;
}
static void annotate_call_site(struct objtool_file *file,
struct instruction *insn, bool sibling)
{
struct reloc *reloc = insn_reloc(file, insn);
- struct symbol *sym = insn->call_dest;
+ struct symbol *sym = insn_call_dest(insn);
if (!sym)
sym = reloc->sym;
@@ -1421,7 +1471,7 @@ static void annotate_call_site(struct objtool_file *file,
static void add_call_dest(struct objtool_file *file, struct instruction *insn,
struct symbol *dest, bool sibling)
{
- insn->call_dest = dest;
+ insn->_call_dest = dest;
if (!dest)
return;
@@ -1679,12 +1729,12 @@ static int add_call_destinations(struct objtool_file *file)
if (insn->ignore)
continue;
- if (!insn->call_dest) {
+ if (!insn_call_dest(insn)) {
WARN_FUNC("unannotated intra-function call", insn->sec, insn->offset);
return -1;
}
- if (insn_func(insn) && insn->call_dest->type != STT_FUNC) {
+ if (insn_func(insn) && insn_call_dest(insn)->type != STT_FUNC) {
WARN_FUNC("unsupported call to non-function",
insn->sec, insn->offset);
return -1;
@@ -1722,36 +1772,50 @@ static int handle_group_alt(struct objtool_file *file,
struct instruction *orig_insn,
struct instruction **new_insn)
{
- struct instruction *last_orig_insn, *last_new_insn = NULL, *insn, *nop = NULL;
+ struct instruction *last_new_insn = NULL, *insn, *nop = NULL;
struct alt_group *orig_alt_group, *new_alt_group;
unsigned long dest_off;
-
- orig_alt_group = malloc(sizeof(*orig_alt_group));
+ orig_alt_group = orig_insn->alt_group;
if (!orig_alt_group) {
- WARN("malloc failed");
- return -1;
- }
- orig_alt_group->cfi = calloc(special_alt->orig_len,
- sizeof(struct cfi_state *));
- if (!orig_alt_group->cfi) {
- WARN("calloc failed");
- return -1;
- }
+ struct instruction *last_orig_insn = NULL;
- last_orig_insn = NULL;
- insn = orig_insn;
- sec_for_each_insn_from(file, insn) {
- if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
- break;
+ orig_alt_group = malloc(sizeof(*orig_alt_group));
+ if (!orig_alt_group) {
+ WARN("malloc failed");
+ return -1;
+ }
+ orig_alt_group->cfi = calloc(special_alt->orig_len,
+ sizeof(struct cfi_state *));
+ if (!orig_alt_group->cfi) {
+ WARN("calloc failed");
+ return -1;
+ }
- insn->alt_group = orig_alt_group;
- last_orig_insn = insn;
- }
- orig_alt_group->orig_group = NULL;
- orig_alt_group->first_insn = orig_insn;
- orig_alt_group->last_insn = last_orig_insn;
+ insn = orig_insn;
+ sec_for_each_insn_from(file, insn) {
+ if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
+ break;
+ insn->alt_group = orig_alt_group;
+ last_orig_insn = insn;
+ }
+ orig_alt_group->orig_group = NULL;
+ orig_alt_group->first_insn = orig_insn;
+ orig_alt_group->last_insn = last_orig_insn;
+ orig_alt_group->nop = NULL;
+ } else {
+ if (orig_alt_group->last_insn->offset + orig_alt_group->last_insn->len -
+ orig_alt_group->first_insn->offset != special_alt->orig_len) {
+ WARN_FUNC("weirdly overlapping alternative! %ld != %d",
+ orig_insn->sec, orig_insn->offset,
+ orig_alt_group->last_insn->offset +
+ orig_alt_group->last_insn->len -
+ orig_alt_group->first_insn->offset,
+ special_alt->orig_len);
+ return -1;
+ }
+ }
new_alt_group = malloc(sizeof(*new_alt_group));
if (!new_alt_group) {
@@ -1773,8 +1837,6 @@ static int handle_group_alt(struct objtool_file *file,
return -1;
}
memset(nop, 0, sizeof(*nop));
- INIT_LIST_HEAD(&nop->alts);
- INIT_LIST_HEAD(&nop->stack_ops);
nop->sec = special_alt->new_sec;
nop->offset = special_alt->new_off + special_alt->new_len;
@@ -1828,7 +1890,7 @@ static int handle_group_alt(struct objtool_file *file,
dest_off = arch_jump_destination(insn);
if (dest_off == special_alt->new_off + special_alt->new_len) {
- insn->jump_dest = next_insn_same_sec(file, last_orig_insn);
+ insn->jump_dest = next_insn_same_sec(file, orig_alt_group->last_insn);
if (!insn->jump_dest) {
WARN_FUNC("can't find alternative jump destination",
insn->sec, insn->offset);
@@ -1843,12 +1905,11 @@ static int handle_group_alt(struct objtool_file *file,
return -1;
}
- if (nop)
- list_add(&nop->list, &last_new_insn->list);
end:
new_alt_group->orig_group = orig_alt_group;
new_alt_group->first_insn = *new_insn;
- new_alt_group->last_insn = nop ? : last_new_insn;
+ new_alt_group->last_insn = last_new_insn;
+ new_alt_group->nop = nop;
new_alt_group->cfi = orig_alt_group->cfi;
return 0;
}
@@ -1898,7 +1959,7 @@ static int handle_jump_alt(struct objtool_file *file,
else
file->jl_long++;
- *new_insn = list_next_entry(orig_insn, list);
+ *new_insn = next_insn_same_sec(file, orig_insn);
return 0;
}
@@ -1972,7 +2033,8 @@ static int add_special_section_alts(struct objtool_file *file)
alt->insn = new_insn;
alt->skip_orig = special_alt->skip_orig;
orig_insn->ignore_alts |= special_alt->skip_alt;
- list_add_tail(&alt->list, &orig_insn->alts);
+ alt->next = orig_insn->alts;
+ orig_insn->alts = alt;
list_del(&special_alt->list);
free(special_alt);
@@ -2031,7 +2093,8 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn,
}
alt->insn = dest_insn;
- list_add_tail(&alt->list, &insn->alts);
+ alt->next = insn->alts;
+ insn->alts = alt;
prev_offset = reloc->offset;
}
@@ -2121,7 +2184,7 @@ static void mark_func_jump_tables(struct objtool_file *file,
reloc = find_jump_table(file, func, insn);
if (reloc) {
reloc->jump_table_start = true;
- insn->jump_table = reloc;
+ insn->_jump_table = reloc;
}
}
}
@@ -2133,10 +2196,10 @@ static int add_func_jump_tables(struct objtool_file *file,
int ret;
func_for_each_insn(file, func, insn) {
- if (!insn->jump_table)
+ if (!insn_jump_table(insn))
continue;
- ret = add_jump_table(file, insn, insn->jump_table);
+ ret = add_jump_table(file, insn, insn_jump_table(insn));
if (ret)
return ret;
}
@@ -2269,6 +2332,7 @@ static int read_unwind_hints(struct objtool_file *file)
cfi.cfa.offset = bswap_if_needed(file->elf, hint->sp_offset);
cfi.type = hint->type;
+ cfi.signal = hint->signal;
cfi.end = hint->end;
insn->cfi = cfi_hash_find_or_add(&cfi);
@@ -2608,8 +2672,8 @@ static int decode_sections(struct objtool_file *file)
static bool is_fentry_call(struct instruction *insn)
{
if (insn->type == INSN_CALL &&
- insn->call_dest &&
- insn->call_dest->fentry)
+ insn_call_dest(insn) &&
+ insn_call_dest(insn)->fentry)
return true;
return false;
@@ -3204,8 +3268,12 @@ static int propagate_alt_cfi(struct objtool_file *file, struct instruction *insn
alt_cfi[group_off] = insn->cfi;
} else {
if (cficmp(alt_cfi[group_off], insn->cfi)) {
- WARN_FUNC("stack layout conflict in alternatives",
- insn->sec, insn->offset);
+ struct alt_group *orig_group = insn->alt_group->orig_group ?: insn->alt_group;
+ struct instruction *orig = orig_group->first_insn;
+ char *where = offstr(insn->sec, insn->offset);
+ WARN_FUNC("stack layout conflict in alternatives: %s",
+ orig->sec, orig->offset, where);
+ free(where);
return -1;
}
}
@@ -3219,7 +3287,7 @@ static int handle_insn_ops(struct instruction *insn,
{
struct stack_op *op;
- list_for_each_entry(op, &insn->stack_ops, list) {
+ for (op = insn->stack_ops; op; op = op->next) {
if (update_cfi_state(insn, next_insn, &state->cfi, op))
return 1;
@@ -3316,8 +3384,8 @@ static inline const char *call_dest_name(struct instruction *insn)
struct reloc *rel;
int idx;
- if (insn->call_dest)
- return insn->call_dest->name;
+ if (insn_call_dest(insn))
+ return insn_call_dest(insn)->name;
rel = insn_reloc(NULL, insn);
if (rel && !strcmp(rel->sym->name, "pv_ops")) {
@@ -3399,13 +3467,13 @@ static int validate_call(struct objtool_file *file,
struct insn_state *state)
{
if (state->noinstr && state->instr <= 0 &&
- !noinstr_call_dest(file, insn, insn->call_dest)) {
+ !noinstr_call_dest(file, insn, insn_call_dest(insn))) {
WARN_FUNC("call to %s() leaves .noinstr.text section",
insn->sec, insn->offset, call_dest_name(insn));
return 1;
}
- if (state->uaccess && !func_uaccess_safe(insn->call_dest)) {
+ if (state->uaccess && !func_uaccess_safe(insn_call_dest(insn))) {
WARN_FUNC("call to %s() with UACCESS enabled",
insn->sec, insn->offset, call_dest_name(insn));
return 1;
@@ -3483,11 +3551,28 @@ static struct instruction *next_insn_to_validate(struct objtool_file *file,
* Simulate the fact that alternatives are patched in-place. When the
* end of a replacement alt_group is reached, redirect objtool flow to
* the end of the original alt_group.
+ *
+ * insn->alts->insn -> alt_group->first_insn
+ * ...
+ * alt_group->last_insn
+ * [alt_group->nop] -> next(orig_group->last_insn)
*/
- if (alt_group && insn == alt_group->last_insn && alt_group->orig_group)
- return next_insn_same_sec(file, alt_group->orig_group->last_insn);
+ if (alt_group) {
+ if (alt_group->nop) {
+ /* ->nop implies ->orig_group */
+ if (insn == alt_group->last_insn)
+ return alt_group->nop;
+ if (insn == alt_group->nop)
+ goto next_orig;
+ }
+ if (insn == alt_group->last_insn && alt_group->orig_group)
+ goto next_orig;
+ }
return next_insn_same_sec(file, insn);
+
+next_orig:
+ return next_insn_same_sec(file, alt_group->orig_group->last_insn);
}
/*
@@ -3588,10 +3673,10 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
if (propagate_alt_cfi(file, insn))
return 1;
- if (!insn->ignore_alts && !list_empty(&insn->alts)) {
+ if (!insn->ignore_alts && insn->alts) {
bool skip_orig = false;
- list_for_each_entry(alt, &insn->alts, list) {
+ for (alt = insn->alts; alt; alt = alt->next) {
if (alt->skip_orig)
skip_orig = true;
@@ -3738,11 +3823,25 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
return 0;
}
+static int validate_unwind_hint(struct objtool_file *file,
+ struct instruction *insn,
+ struct insn_state *state)
+{
+ if (insn->hint && !insn->visited && !insn->ignore) {
+ int ret = validate_branch(file, insn_func(insn), insn, *state);
+ if (ret && opts.backtrace)
+ BT_FUNC("<=== (hint)", insn);
+ return ret;
+ }
+
+ return 0;
+}
+
static int validate_unwind_hints(struct objtool_file *file, struct section *sec)
{
struct instruction *insn;
struct insn_state state;
- int ret, warnings = 0;
+ int warnings = 0;
if (!file->hints)
return 0;
@@ -3750,22 +3849,11 @@ static int validate_unwind_hints(struct objtool_file *file, struct section *sec)
init_insn_state(file, &state, sec);
if (sec) {
- insn = find_insn(file, sec, 0);
- if (!insn)
- return 0;
+ sec_for_each_insn(file, sec, insn)
+ warnings += validate_unwind_hint(file, insn, &state);
} else {
- insn = list_first_entry(&file->insn_list, typeof(*insn), list);
- }
-
- while (&insn->list != &file->insn_list && (!sec || insn->sec == sec)) {
- if (insn->hint && !insn->visited && !insn->ignore) {
- ret = validate_branch(file, insn_func(insn), insn, state);
- if (ret && opts.backtrace)
- BT_FUNC("<=== (hint)", insn);
- warnings += ret;
- }
-
- insn = list_next_entry(insn, list);
+ for_each_insn(file, insn)
+ warnings += validate_unwind_hint(file, insn, &state);
}
return warnings;
@@ -3790,11 +3878,11 @@ static int validate_entry(struct objtool_file *file, struct instruction *insn)
insn->visited |= VISITED_ENTRY;
- if (!insn->ignore_alts && !list_empty(&insn->alts)) {
+ if (!insn->ignore_alts && insn->alts) {
struct alternative *alt;
bool skip_orig = false;
- list_for_each_entry(alt, &insn->alts, list) {
+ for (alt = insn->alts; alt; alt = alt->next) {
if (alt->skip_orig)
skip_orig = true;
@@ -3843,11 +3931,11 @@ static int validate_entry(struct objtool_file *file, struct instruction *insn)
/* fallthrough */
case INSN_CALL:
- dest = find_insn(file, insn->call_dest->sec,
- insn->call_dest->offset);
+ dest = find_insn(file, insn_call_dest(insn)->sec,
+ insn_call_dest(insn)->offset);
if (!dest) {
WARN("Unresolved function after linking!?: %s",
- insn->call_dest->name);
+ insn_call_dest(insn)->name);
return -1;
}
@@ -3948,13 +4036,13 @@ static int validate_retpoline(struct objtool_file *file)
static bool is_kasan_insn(struct instruction *insn)
{
return (insn->type == INSN_CALL &&
- !strcmp(insn->call_dest->name, "__asan_handle_no_return"));
+ !strcmp(insn_call_dest(insn)->name, "__asan_handle_no_return"));
}
static bool is_ubsan_insn(struct instruction *insn)
{
return (insn->type == INSN_CALL &&
- !strcmp(insn->call_dest->name,
+ !strcmp(insn_call_dest(insn)->name,
"__ubsan_handle_builtin_unreachable"));
}
@@ -4031,8 +4119,9 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio
*
* It may also insert a UD2 after calling a __noreturn function.
*/
- prev_insn = list_prev_entry(insn, list);
- if ((prev_insn->dead_end || dead_end_function(file, prev_insn->call_dest)) &&
+ prev_insn = prev_insn_same_sec(file, insn);
+ if ((prev_insn->dead_end ||
+ dead_end_function(file, insn_call_dest(prev_insn))) &&
(insn->type == INSN_BUG ||
(insn->type == INSN_JUMP_UNCONDITIONAL &&
insn->jump_dest && insn->jump_dest->type == INSN_BUG)))
@@ -4062,7 +4151,7 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio
if (insn->offset + insn->len >= insn_func(insn)->offset + insn_func(insn)->len)
break;
- insn = list_next_entry(insn, list);
+ insn = next_insn_same_sec(file, insn);
}
return false;
@@ -4075,10 +4164,10 @@ static int add_prefix_symbol(struct objtool_file *file, struct symbol *func,
return 0;
for (;;) {
- struct instruction *prev = list_prev_entry(insn, list);
+ struct instruction *prev = prev_insn_same_sec(file, insn);
u64 offset;
- if (&prev->list == &file->insn_list)
+ if (!prev)
break;
if (prev->type != INSN_NOP)
@@ -4477,7 +4566,7 @@ int check(struct objtool_file *file)
warnings += ret;
- if (list_empty(&file->insn_list))
+ if (!nr_insns)
goto out;
if (opts.retpoline) {
@@ -4586,7 +4675,7 @@ int check(struct objtool_file *file)
warnings += ret;
}
- if (opts.orc && !list_empty(&file->insn_list)) {
+ if (opts.orc && nr_insns) {
ret = orc_create(file);
if (ret < 0)
goto out;
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 64443a7f4bbf..6806ce01d933 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -284,13 +284,13 @@ static int read_sections(struct elf *elf)
!elf_alloc_hash(section_name, sections_nr))
return -1;
+ elf->section_data = calloc(sections_nr, sizeof(*sec));
+ if (!elf->section_data) {
+ perror("calloc");
+ return -1;
+ }
for (i = 0; i < sections_nr; i++) {
- sec = malloc(sizeof(*sec));
- if (!sec) {
- perror("malloc");
- return -1;
- }
- memset(sec, 0, sizeof(*sec));
+ sec = &elf->section_data[i];
INIT_LIST_HEAD(&sec->symbol_list);
INIT_LIST_HEAD(&sec->reloc_list);
@@ -422,13 +422,13 @@ static int read_symbols(struct elf *elf)
!elf_alloc_hash(symbol_name, symbols_nr))
return -1;
+ elf->symbol_data = calloc(symbols_nr, sizeof(*sym));
+ if (!elf->symbol_data) {
+ perror("calloc");
+ return -1;
+ }
for (i = 0; i < symbols_nr; i++) {
- sym = malloc(sizeof(*sym));
- if (!sym) {
- perror("malloc");
- return -1;
- }
- memset(sym, 0, sizeof(*sym));
+ sym = &elf->symbol_data[i];
sym->idx = i;
@@ -918,13 +918,13 @@ static int read_relocs(struct elf *elf)
sec->base->reloc = sec;
nr_reloc = 0;
+ sec->reloc_data = calloc(sec->sh.sh_size / sec->sh.sh_entsize, sizeof(*reloc));
+ if (!sec->reloc_data) {
+ perror("calloc");
+ return -1;
+ }
for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) {
- reloc = malloc(sizeof(*reloc));
- if (!reloc) {
- perror("malloc");
- return -1;
- }
- memset(reloc, 0, sizeof(*reloc));
+ reloc = &sec->reloc_data[i];
switch (sec->sh.sh_type) {
case SHT_REL:
if (read_rel_reloc(sec, i, reloc, &symndx))
@@ -1453,16 +1453,16 @@ void elf_close(struct elf *elf)
list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) {
list_del(&sym->list);
hash_del(&sym->hash);
- free(sym);
}
list_for_each_entry_safe(reloc, tmpreloc, &sec->reloc_list, list) {
list_del(&reloc->list);
hash_del(&reloc->hash);
- free(reloc);
}
list_del(&sec->list);
- free(sec);
+ free(sec->reloc_data);
}
+ free(elf->symbol_data);
+ free(elf->section_data);
free(elf);
}
diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h
index 4ecb480131c7..2b6d2ce4f9a5 100644
--- a/tools/objtool/include/objtool/arch.h
+++ b/tools/objtool/include/objtool/arch.h
@@ -62,9 +62,9 @@ struct op_src {
};
struct stack_op {
+ struct stack_op *next;
struct op_dest dest;
struct op_src src;
- struct list_head list;
};
struct instruction;
@@ -75,9 +75,7 @@ void arch_initial_func_cfi_state(struct cfi_init_state *state);
int arch_decode_instruction(struct objtool_file *file, const struct section *sec,
unsigned long offset, unsigned int maxlen,
- unsigned int *len, enum insn_type *type,
- unsigned long *immediate,
- struct list_head *ops_list);
+ struct instruction *insn);
bool arch_callee_saved_reg(unsigned char reg);
diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h
index fa45044e3863..2a108e648b7a 100644
--- a/tools/objtool/include/objtool/builtin.h
+++ b/tools/objtool/include/objtool/builtin.h
@@ -7,8 +7,6 @@
#include <subcmd/parse-options.h>
-extern const struct option check_options[];
-
struct opts {
/* actions: */
bool dump_orc;
diff --git a/tools/objtool/include/objtool/cfi.h b/tools/objtool/include/objtool/cfi.h
index f11d1ac1dadf..b1258e79a1b7 100644
--- a/tools/objtool/include/objtool/cfi.h
+++ b/tools/objtool/include/objtool/cfi.h
@@ -34,6 +34,7 @@ struct cfi_state {
unsigned char type;
bool bp_scratch;
bool drap;
+ bool signal;
bool end;
};
diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h
index acd7fae59348..3e7c7004f7df 100644
--- a/tools/objtool/include/objtool/check.h
+++ b/tools/objtool/include/objtool/check.h
@@ -27,7 +27,7 @@ struct alt_group {
struct alt_group *orig_group;
/* First and last instructions in the group */
- struct instruction *first_insn, *last_insn;
+ struct instruction *first_insn, *last_insn, *nop;
/*
* Byte-offset-addressed len-sized array of pointers to CFI structs.
@@ -36,39 +36,46 @@ struct alt_group {
struct cfi_state **cfi;
};
+#define INSN_CHUNK_BITS 8
+#define INSN_CHUNK_SIZE (1 << INSN_CHUNK_BITS)
+#define INSN_CHUNK_MAX (INSN_CHUNK_SIZE - 1)
+
struct instruction {
- struct list_head list;
struct hlist_node hash;
struct list_head call_node;
struct section *sec;
unsigned long offset;
- unsigned int len;
- enum insn_type type;
unsigned long immediate;
- u16 dead_end : 1,
- ignore : 1,
- ignore_alts : 1,
- hint : 1,
- save : 1,
- restore : 1,
- retpoline_safe : 1,
- noendbr : 1,
- entry : 1;
- /* 7 bit hole */
-
+ u8 len;
+ u8 prev_len;
+ u8 type;
s8 instr;
- u8 visited;
+
+ u32 idx : INSN_CHUNK_BITS,
+ dead_end : 1,
+ ignore : 1,
+ ignore_alts : 1,
+ hint : 1,
+ save : 1,
+ restore : 1,
+ retpoline_safe : 1,
+ noendbr : 1,
+ entry : 1,
+ visited : 4,
+ no_reloc : 1;
+ /* 10 bit hole */
struct alt_group *alt_group;
- struct symbol *call_dest;
struct instruction *jump_dest;
struct instruction *first_jump_src;
- struct reloc *jump_table;
- struct reloc *reloc;
- struct list_head alts;
+ union {
+ struct symbol *_call_dest;
+ struct reloc *_jump_table;
+ };
+ struct alternative *alts;
struct symbol *sym;
- struct list_head stack_ops;
+ struct stack_op *stack_ops;
struct cfi_state *cfi;
};
@@ -107,13 +114,11 @@ static inline bool is_jump(struct instruction *insn)
struct instruction *find_insn(struct objtool_file *file,
struct section *sec, unsigned long offset);
-#define for_each_insn(file, insn) \
- list_for_each_entry(insn, &file->insn_list, list)
+struct instruction *next_insn_same_sec(struct objtool_file *file, struct instruction *insn);
-#define sec_for_each_insn(file, sec, insn) \
- for (insn = find_insn(file, sec, 0); \
- insn && &insn->list != &file->insn_list && \
- insn->sec == sec; \
- insn = list_next_entry(insn, list))
+#define sec_for_each_insn(file, _sec, insn) \
+ for (insn = find_insn(file, _sec, 0); \
+ insn && insn->sec == _sec; \
+ insn = next_insn_same_sec(file, insn))
#endif /* _CHECK_H */
diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h
index bb60fd42b46f..ad0024da262b 100644
--- a/tools/objtool/include/objtool/elf.h
+++ b/tools/objtool/include/objtool/elf.h
@@ -39,6 +39,7 @@ struct section {
char *name;
int idx;
bool changed, text, rodata, noinstr, init, truncate;
+ struct reloc *reloc_data;
};
struct symbol {
@@ -49,12 +50,11 @@ struct symbol {
GElf_Sym sym;
struct section *sec;
char *name;
- unsigned int idx;
- unsigned char bind, type;
+ unsigned int idx, len;
unsigned long offset;
- unsigned int len;
unsigned long __subtree_last;
struct symbol *pfunc, *cfunc, *alias;
+ unsigned char bind, type;
u8 uaccess_safe : 1;
u8 static_call_tramp : 1;
u8 retpoline_thunk : 1;
@@ -104,6 +104,9 @@ struct elf {
struct hlist_head *section_hash;
struct hlist_head *section_name_hash;
struct hlist_head *reloc_hash;
+
+ struct section *section_data;
+ struct symbol *symbol_data;
};
#define OFFSET_STRIDE_BITS 4
diff --git a/tools/objtool/include/objtool/objtool.h b/tools/objtool/include/objtool/objtool.h
index 6b40977bcdb1..94a33ee7b363 100644
--- a/tools/objtool/include/objtool/objtool.h
+++ b/tools/objtool/include/objtool/objtool.h
@@ -21,7 +21,6 @@ struct pv_state {
struct objtool_file {
struct elf *elf;
- struct list_head insn_list;
DECLARE_HASHTABLE(insn_hash, 20);
struct list_head retpoline_call_list;
struct list_head return_thunk_list;
diff --git a/tools/objtool/include/objtool/special.h b/tools/objtool/include/objtool/special.h
index dc4721e19002..86d4af9c5aa9 100644
--- a/tools/objtool/include/objtool/special.h
+++ b/tools/objtool/include/objtool/special.h
@@ -19,6 +19,7 @@ struct special_alt {
bool skip_orig;
bool skip_alt;
bool jump_or_nop;
+ u8 key_addend;
struct section *orig_sec;
unsigned long orig_off;
@@ -27,7 +28,6 @@ struct special_alt {
unsigned long new_off;
unsigned int orig_len, new_len; /* group only */
- u8 key_addend;
};
int special_get_alts(struct elf *elf, struct list_head *alts);
diff --git a/tools/objtool/objtool.c b/tools/objtool/objtool.c
index 6affd8067f83..c54f7235c5d9 100644
--- a/tools/objtool/objtool.c
+++ b/tools/objtool/objtool.c
@@ -99,7 +99,6 @@ struct objtool_file *objtool_open_read(const char *_objname)
return NULL;
}
- INIT_LIST_HEAD(&file.insn_list);
hash_init(file.insn_hash);
INIT_LIST_HEAD(&file.retpoline_call_list);
INIT_LIST_HEAD(&file.return_thunk_list);
diff --git a/tools/objtool/orc_dump.c b/tools/objtool/orc_dump.c
index 4f1211fec82c..2d8ebdcd1db3 100644
--- a/tools/objtool/orc_dump.c
+++ b/tools/objtool/orc_dump.c
@@ -211,8 +211,8 @@ int orc_dump(const char *_objname)
print_reg(orc[i].bp_reg, bswap_if_needed(&dummy_elf, orc[i].bp_offset));
- printf(" type:%s end:%d\n",
- orc_type_name(orc[i].type), orc[i].end);
+ printf(" type:%s signal:%d end:%d\n",
+ orc_type_name(orc[i].type), orc[i].signal, orc[i].end);
}
elf_end(elf);
diff --git a/tools/objtool/orc_gen.c b/tools/objtool/orc_gen.c
index 1f22b7ebae58..57a4527d5988 100644
--- a/tools/objtool/orc_gen.c
+++ b/tools/objtool/orc_gen.c
@@ -27,6 +27,7 @@ static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi,
}
orc->end = cfi->end;
+ orc->signal = cfi->signal;
if (cfi->cfa.base == CFI_UNDEFINED) {
orc->sp_reg = ORC_REG_UNDEFINED;
diff --git a/tools/objtool/special.c b/tools/objtool/special.c
index 9c8d827f69af..baa85c31526b 100644
--- a/tools/objtool/special.c
+++ b/tools/objtool/special.c
@@ -26,7 +26,7 @@ struct special_entry {
unsigned char key; /* jump_label key */
};
-struct special_entry entries[] = {
+static const struct special_entry entries[] = {
{
.sec = ".altinstructions",
.group = true,
@@ -65,7 +65,7 @@ static void reloc_to_sec_off(struct reloc *reloc, struct section **sec,
*off = reloc->sym->offset + reloc->addend;
}
-static int get_alt_entry(struct elf *elf, struct special_entry *entry,
+static int get_alt_entry(struct elf *elf, const struct special_entry *entry,
struct section *sec, int idx,
struct special_alt *alt)
{
@@ -139,7 +139,7 @@ static int get_alt_entry(struct elf *elf, struct special_entry *entry,
*/
int special_get_alts(struct elf *elf, struct list_head *alts)
{
- struct special_entry *entry;
+ const struct special_entry *entry;
struct section *sec;
unsigned int nr_entries;
struct special_alt *alt;
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore
index 05806ecfc33c..f533e76fb480 100644
--- a/tools/perf/.gitignore
+++ b/tools/perf/.gitignore
@@ -38,6 +38,7 @@ arch/*/include/generated/
trace/beauty/generated/
pmu-events/pmu-events.c
pmu-events/jevents
+pmu-events/metric_test.log
feature/
libapi/
libbpf/
diff --git a/tools/perf/Documentation/itrace.txt b/tools/perf/Documentation/itrace.txt
index 0916bbfe64cb..a97f95825b14 100644
--- a/tools/perf/Documentation/itrace.txt
+++ b/tools/perf/Documentation/itrace.txt
@@ -1,4 +1,5 @@
i synthesize instructions events
+ y synthesize cycles events
b synthesize branches events (branch misses for Arm SPE)
c synthesize branches events (calls only)
r synthesize branches events (returns only)
@@ -25,7 +26,7 @@
A approximate IPC
Z prefer to ignore timestamps (so-called "timeless" decoding)
- The default is all events i.e. the same as --itrace=ibxwpe,
+ The default is all events i.e. the same as --itrace=iybxwpe,
except for perf script where it is --itrace=ce
In addition, the period (default 100000, except for perf script where it is 1)
diff --git a/tools/perf/Documentation/perf-bench.txt b/tools/perf/Documentation/perf-bench.txt
index a0529c7fa5ef..f04f0eaded98 100644
--- a/tools/perf/Documentation/perf-bench.txt
+++ b/tools/perf/Documentation/perf-bench.txt
@@ -18,7 +18,7 @@ COMMON OPTIONS
--------------
-r::
--repeat=::
-Specify amount of times to repeat the run (default 10).
+Specify number of times to repeat the run (default 10).
-f::
--format=::
diff --git a/tools/perf/Documentation/perf-c2c.txt b/tools/perf/Documentation/perf-c2c.txt
index 5c5eb2def83e..856f0dfb8e5a 100644
--- a/tools/perf/Documentation/perf-c2c.txt
+++ b/tools/perf/Documentation/perf-c2c.txt
@@ -22,7 +22,11 @@ you to track down the cacheline contentions.
On Intel, the tool is based on load latency and precise store facility events
provided by Intel CPUs. On PowerPC, the tool uses random instruction sampling
with thresholding feature. On AMD, the tool uses IBS op pmu (due to hardware
-limitations, perf c2c is not supported on Zen3 cpus).
+limitations, perf c2c is not supported on Zen3 cpus). On Arm64 it uses SPE to
+sample load and store operations, therefore hardware and kernel support is
+required. See linkperf:perf-arm-spe[1] for a setup guide. Due to the
+statistical nature of Arm SPE sampling, not every memory operation will be
+sampled.
These events provide:
- memory address of the access
@@ -121,11 +125,17 @@ REPORT OPTIONS
perf c2c record --call-graph lbr.
Disabled by default. In common cases with call stack overflows,
it can recreate better call stacks than the default lbr call stack
- output. But this approach is not full proof. There can be cases
+ output. But this approach is not foolproof. There can be cases
where it creates incorrect call stacks from incorrect matches.
The known limitations include exception handing such as
setjmp/longjmp will have calls/returns not match.
+--double-cl::
+ Group the detection of shared cacheline events into double cacheline
+ granularity. Some architectures have an Adjacent Cacheline Prefetch
+ feature, which causes cacheline sharing to behave like the cacheline
+ size is doubled.
+
C2C RECORD
----------
The perf c2c record command setup options related to HITM cacheline analysis
@@ -333,4 +343,4 @@ Check Joe's blog on c2c tool for detailed use case explanation:
SEE ALSO
--------
-linkperf:perf-record[1], linkperf:perf-mem[1]
+linkperf:perf-record[1], linkperf:perf-mem[1], linkperf:perf-arm-spe[1]
diff --git a/tools/perf/Documentation/perf-intel-pt.txt b/tools/perf/Documentation/perf-intel-pt.txt
index 7b6ccd2fa3bf..4c90cc176f81 100644
--- a/tools/perf/Documentation/perf-intel-pt.txt
+++ b/tools/perf/Documentation/perf-intel-pt.txt
@@ -101,12 +101,12 @@ 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 --itrace=ibxwpe
+ perf script --itrace=iybxwpe
An interesting field that is not printed by default is 'flags' which can be
displayed as follows:
- perf script --itrace=ibxwpe -F+flags
+ perf script --itrace=iybxwpe -F+flags
The flags are "bcrosyiABExghDt" which stand for branch, call, return, conditional,
system, asynchronous, interrupt, transaction abort, trace begin, trace end,
@@ -147,16 +147,17 @@ displayed as follows:
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.
+If the 'cyc' config term (see config terms section below) was used, then IPC
+and cycle events are 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.
+the average IPC cycle count since the last IPC for that event type.
+Note IPC for "branches" events is calculated separately from IPC for "instructions"
+events.
Even with the 'cyc' config term, it is possible to produce IPC information for
every change of timestamp, but at the expense of accuracy. That is selected by
@@ -900,11 +901,12 @@ Having no option is the same as
which, in turn, is the same as
- --itrace=cepwx
+ --itrace=cepwxy
The letters are:
i synthesize "instructions" events
+ y synthesize "cycles" events
b synthesize "branches" events
x synthesize "transactions" events
w synthesize "ptwrite" events
@@ -927,6 +929,16 @@ The letters are:
"Instructions" events look like they were recorded by "perf record -e
instructions".
+"Cycles" events look like they were recorded by "perf record -e cycles"
+(ie., the default). Note that even with CYC packets enabled and no sampling,
+these are not fully accurate, since CYC packets are not emitted for each
+instruction, only when some other event (like an indirect branch, or a
+TNT packet representing multiple branches) happens causes a packet to
+be emitted. Thus, it is more effective for attributing cycles to functions
+(and possibly basic blocks) than to individual instructions, although it
+is not even perfect for functions (although it becomes better if the noretcomp
+option is active).
+
"Branches" events look like they were recorded by "perf record -e branches". "c"
and "r" can be combined to get calls and returns.
@@ -934,9 +946,9 @@ and "r" can be combined to get calls and returns.
'flags' field can be used in perf script to determine whether the event is a
transaction start, commit or abort.
-Note that "instructions", "branches" and "transactions" events depend on code
-flow packets which can be disabled by using the config term "branch=0". Refer
-to the config terms section above.
+Note that "instructions", "cycles", "branches" and "transactions" events
+depend on code flow packets which can be disabled by using the config term
+"branch=0". Refer to the config terms section above.
"ptwrite" events record the payload of the ptwrite instruction and whether
"fup_on_ptw" was used. "ptwrite" events depend on PTWRITE packets which are
@@ -1821,6 +1833,36 @@ Can be compiled and traced:
$
+Pipe mode
+---------
+Pipe mode is a problem for Intel PT and possibly other auxtrace users.
+It's not recommended to use a pipe as data output with Intel PT because
+of the following reason.
+
+Essentially the auxtrace buffers do not behave like the regular perf
+event buffers. That is because the head and tail are updated by
+software, but in the auxtrace case the data is written by hardware.
+So the head and tail do not get updated as data is written.
+
+In the Intel PT case, the head and tail are updated only when the trace
+is disabled by software, for example:
+ - full-trace, system wide : when buffer passes watermark
+ - full-trace, not system-wide : when buffer passes watermark or
+ context switches
+ - snapshot mode : as above but also when a snapshot is made
+ - sample mode : as above but also when a sample is made
+
+That means finished-round ordering doesn't work. An auxtrace buffer
+can turn up that has data that extends back in time, possibly to the
+very beginning of tracing.
+
+For a perf.data file, that problem is solved by going through the trace
+and queuing up the auxtrace buffers in advance.
+
+For pipe mode, the order of events and timestamps can presumably
+be messed up.
+
+
EXAMPLE
-------
diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt
index c5a3cb0f57c7..d5f78e125efe 100644
--- a/tools/perf/Documentation/perf-list.txt
+++ b/tools/perf/Documentation/perf-list.txt
@@ -232,7 +232,7 @@ This can be overridden by setting the kernel.perf_event_paranoid
sysctl to -1, which allows non root to use these events.
For accessing trace point events perf needs to have read access to
-/sys/kernel/debug/tracing, even when perf_event_paranoid is in a relaxed
+/sys/kernel/tracing, even when perf_event_paranoid is in a relaxed
setting.
TRACING
diff --git a/tools/perf/Documentation/perf-lock.txt b/tools/perf/Documentation/perf-lock.txt
index 0f9f720e599d..37aae194a2a1 100644
--- a/tools/perf/Documentation/perf-lock.txt
+++ b/tools/perf/Documentation/perf-lock.txt
@@ -172,6 +172,11 @@ CONTENTION OPTIONS
--lock-addr::
Show lock contention stat by address
+-o::
+--lock-owner::
+ Show lock contention stat by owners. Implies --threads and
+ requires --use-bpf.
+
-Y::
--type-filter=<value>::
Show lock contention only for given lock types (comma separated list).
@@ -187,6 +192,12 @@ CONTENTION OPTIONS
--lock-filter=<value>::
Show lock contention only for given lock addresses or names (comma separated list).
+-S::
+--callstack-filter=<value>::
+ Show lock contention only if the callstack contains the given string.
+ Note that it matches the substring so 'rq' would match both 'raw_spin_rq_lock'
+ and 'irq_enter_rcu'.
+
SEE ALSO
--------
diff --git a/tools/perf/Documentation/perf-mem.txt b/tools/perf/Documentation/perf-mem.txt
index 005c95580b1e..19862572e3f2 100644
--- a/tools/perf/Documentation/perf-mem.txt
+++ b/tools/perf/Documentation/perf-mem.txt
@@ -23,6 +23,11 @@ Note that on Intel systems the memory latency reported is the use-latency,
not the pure load (or store latency). Use latency includes any pipeline
queueing delays in addition to the memory subsystem latency.
+On Arm64 this uses SPE to sample load and store operations, therefore hardware
+and kernel support is required. See linkperf:perf-arm-spe[1] for a setup guide.
+Due to the statistical nature of SPE sampling, not every memory operation will
+be sampled.
+
OPTIONS
-------
<command>...::
@@ -93,4 +98,4 @@ all perf record options.
SEE ALSO
--------
-linkperf:perf-record[1], linkperf:perf-report[1]
+linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-arm-spe[1]
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 7f8e8ba3a787..5c43a6edc0e5 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -222,7 +222,7 @@ probe syntax, 'SRC' means the source file path, 'ALN' is start line number,
and 'ALN2' is end line number in the file. It is also possible to specify how
many lines to show by using 'NUM'. Moreover, 'FUNC@SRC' combination is good
for searching a specific function when several functions share same name.
-So, "source.c:100-120" shows lines between 100th to l20th in source.c file. And "func:10+20" shows 20 lines from 10th line of func function.
+So, "source.c:100-120" shows lines between 100th to 120th in source.c file. And "func:10+20" shows 20 lines from 10th line of func function.
LAZY MATCHING
-------------
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 4fa509b15948..c242e8da6b1a 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -115,6 +115,8 @@ OPTIONS
- p_stage_cyc: On powerpc, this presents the number of cycles spent in a
pipeline stage. And currently supported only on powerpc.
- addr: (Full) virtual address of the sampled instruction
+ - retire_lat: On X86, this reports pipeline stall of this instruction compared
+ to the previous instruction in cycles. And currently supported only on X86
By default, comm, dso and symbol keys are used.
(i.e. --sort comm,dso,symbol)
@@ -507,7 +509,7 @@ include::itrace.txt[]
perf record --call-graph lbr.
Disabled by default. In common cases with call stack overflows,
it can recreate better call stacks than the default lbr call stack
- output. But this approach is not full proof. There can be cases
+ output. But this approach is not foolproof. There can be cases
where it creates incorrect call stacks from incorrect matches.
The known limitations include exception handing such as
setjmp/longjmp will have calls/returns not match.
diff --git a/tools/perf/Documentation/perf-script-perl.txt b/tools/perf/Documentation/perf-script-perl.txt
index fa4f39d305a7..5b479f5e62ff 100644
--- a/tools/perf/Documentation/perf-script-perl.txt
+++ b/tools/perf/Documentation/perf-script-perl.txt
@@ -55,7 +55,7 @@ Traces meant to be processed using a script should be recorded with
the above option: -a to enable system-wide collection.
The format file for the sched_wakeup event defines the following fields
-(see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format):
+(see /sys/kernel/tracing/events/sched/sched_wakeup/format):
----
format:
diff --git a/tools/perf/Documentation/perf-script-python.txt b/tools/perf/Documentation/perf-script-python.txt
index cf4b7f4b625a..6a8581012e16 100644
--- a/tools/perf/Documentation/perf-script-python.txt
+++ b/tools/perf/Documentation/perf-script-python.txt
@@ -319,7 +319,7 @@ So those are the essential steps in writing and running a script. The
process can be generalized to any tracepoint or set of tracepoints
you're interested in - basically find the tracepoint(s) you're
interested in by looking at the list of available events shown by
-'perf list' and/or look in /sys/kernel/debug/tracing/events/ for
+'perf list' and/or look in /sys/kernel/tracing/events/ for
detailed event and field info, record the corresponding trace data
using 'perf record', passing it the list of interesting events,
generate a skeleton script using 'perf script -g python' and modify the
@@ -449,7 +449,7 @@ Traces meant to be processed using a script should be recorded with
the above option: -a to enable system-wide collection.
The format file for the sched_wakeup event defines the following fields
-(see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format):
+(see /sys/kernel/tracing/events/sched/sched_wakeup/format):
----
format:
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt
index 68e37de5fae4..777a0d8ba7d1 100644
--- a/tools/perf/Documentation/perf-script.txt
+++ b/tools/perf/Documentation/perf-script.txt
@@ -134,7 +134,7 @@ OPTIONS
srcline, period, iregs, uregs, brstack, brstacksym, flags, bpf-output,
brstackinsn, brstackinsnlen, brstackoff, callindent, insn, insnlen, synth,
phys_addr, metric, misc, srccode, ipc, data_page_size, code_page_size, ins_lat,
- machine_pid, vcpu.
+ machine_pid, vcpu, cgroup, retire_lat.
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
@@ -231,6 +231,9 @@ OPTIONS
perf inject to insert a perf.data file recorded inside a virtual machine into
a perf.data file recorded on the host at the same time.
+ The cgroup fields requires sample having the cgroup id which is saved
+ when "--all-cgroups" option is passed to 'perf record'.
+
Finally, a user may not set fields to none for all event types.
i.e., -F "" is not allowed.
@@ -502,7 +505,7 @@ include::itrace.txt[]
perf record --call-graph lbr.
Disabled by default. In common cases with call stack overflows,
it can recreate better call stacks than the default lbr call stack
- output. But this approach is not full proof. There can be cases
+ output. But this approach is not foolproof. There can be cases
where it creates incorrect call stacks from incorrect matches.
The known limitations include exception handing such as
setjmp/longjmp will have calls/returns not match.
diff --git a/tools/perf/Documentation/perf-test.txt b/tools/perf/Documentation/perf-test.txt
index b329c65d7f40..951a2f262872 100644
--- a/tools/perf/Documentation/perf-test.txt
+++ b/tools/perf/Documentation/perf-test.txt
@@ -34,3 +34,6 @@ OPTIONS
-F::
--dont-fork::
Do not fork child for each test, run all tests within single process.
+
+--dso::
+ Specify a DSO for the "Symbols" test.
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
index e534d709cc5a..c60e615b7183 100644
--- a/tools/perf/Documentation/perf-top.txt
+++ b/tools/perf/Documentation/perf-top.txt
@@ -334,7 +334,7 @@ use '-e e1 -e e2 -G foo,foo' or just use '-e e1 -e e2 -G foo'.
callgraph. The option must be used with --call-graph lbr recording.
Disabled by default. In common cases with call stack overflows,
it can recreate better call stacks than the default lbr call stack
- output. But this approach is not full proof. There can be cases
+ output. But this approach is not foolproof. There can be cases
where it creates incorrect call stacks from incorrect matches.
The known limitations include exception handing such as
setjmp/longjmp will have calls/returns not match.
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index 5b8784675903..3519a0139026 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -1208,10 +1208,6 @@ ifneq ($(NO_LIBTRACEEVENT),1)
LIBTRACEEVENT_VERSION_CPP := $(shell expr $(LIBTRACEEVENT_VERSION_1) \* 255 \* 255 + $(LIBTRACEEVENT_VERSION_2) \* 255 + $(LIBTRACEEVENT_VERSION_3))
CFLAGS += -DLIBTRACEEVENT_VERSION=$(LIBTRACEEVENT_VERSION_CPP)
$(call detected,CONFIG_LIBTRACEEVENT)
- LIBTRACEEVENT_VERSION_WITH_TEP_FIELD_IS_RELATIVE := $(shell expr 1 \* 255 \* 255 + 5 \* 255 + 0) # 1.5.0
- ifeq ($(shell test $(LIBTRACEEVENT_VERSION_CPP) -gt $(LIBTRACEEVENT_VERSION_WITH_TEP_FIELD_IS_RELATIVE); echo $$?),0)
- CFLAGS += -DHAVE_LIBTRACEEVENT_TEP_FIELD_IS_RELATIVE
- endif
else
dummy := $(warning Warning: libtraceevent is missing limiting functionality, please install libtraceevent-dev/libtraceevent-devel)
endif
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index b7d9c4206230..bac9272682b7 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -1103,6 +1103,7 @@ clean:: $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean $(LIBSYMBOL)-clean $(
$(OUTPUT)util/intel-pt-decoder/inat-tables.c \
$(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c \
$(OUTPUT)pmu-events/pmu-events.c \
+ $(OUTPUT)pmu-events/metric_test.log \
$(OUTPUT)$(fadvise_advice_array) \
$(OUTPUT)$(fsconfig_arrays) \
$(OUTPUT)$(fsmount_arrays) \
diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c
index deeb163999ce..adec6c9ee11d 100644
--- a/tools/perf/arch/arm/util/auxtrace.c
+++ b/tools/perf/arch/arm/util/auxtrace.c
@@ -55,17 +55,16 @@ static struct perf_pmu **find_all_arm_spe_pmus(int *nr_spes, int *err)
static struct perf_pmu **find_all_hisi_ptt_pmus(int *nr_ptts, int *err)
{
- const char *sysfs = sysfs__mountpoint();
struct perf_pmu **hisi_ptt_pmus = NULL;
struct dirent *dent;
char path[PATH_MAX];
DIR *dir = NULL;
int idx = 0;
- snprintf(path, PATH_MAX, "%s" EVENT_SOURCE_DEVICE_PATH, sysfs);
+ perf_pmu__event_source_devices_scnprintf(path, sizeof(path));
dir = opendir(path);
if (!dir) {
- pr_err("can't read directory '%s'\n", EVENT_SOURCE_DEVICE_PATH);
+ pr_err("can't read directory '%s'\n", path);
*err = -EINVAL;
return NULL;
}
diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/cs-etm.c
index a346d5f3dafa..7f71c8a237ff 100644
--- a/tools/perf/arch/arm/util/cs-etm.c
+++ b/tools/perf/arch/arm/util/cs-etm.c
@@ -53,7 +53,17 @@ static const char * const metadata_etmv4_ro[] = {
[CS_ETMV4_TRCIDR2] = "trcidr/trcidr2",
[CS_ETMV4_TRCIDR8] = "trcidr/trcidr8",
[CS_ETMV4_TRCAUTHSTATUS] = "mgmt/trcauthstatus",
- [CS_ETE_TRCDEVARCH] = "mgmt/trcdevarch"
+ [CS_ETMV4_TS_SOURCE] = "ts_source",
+};
+
+static const char * const metadata_ete_ro[] = {
+ [CS_ETE_TRCIDR0] = "trcidr/trcidr0",
+ [CS_ETE_TRCIDR1] = "trcidr/trcidr1",
+ [CS_ETE_TRCIDR2] = "trcidr/trcidr2",
+ [CS_ETE_TRCIDR8] = "trcidr/trcidr8",
+ [CS_ETE_TRCAUTHSTATUS] = "mgmt/trcauthstatus",
+ [CS_ETE_TRCDEVARCH] = "mgmt/trcdevarch",
+ [CS_ETE_TS_SOURCE] = "ts_source",
};
static bool cs_etm_is_etmv4(struct auxtrace_record *itr, int cpu);
@@ -273,9 +283,15 @@ static int cs_etm_set_sink_attr(struct perf_pmu *pmu,
ret = perf_pmu__scan_file(pmu, path, "%x", &hash);
if (ret != 1) {
- pr_err("failed to set sink \"%s\" on event %s with %d (%s)\n",
- sink, evsel__name(evsel), errno,
- str_error_r(errno, msg, sizeof(msg)));
+ if (errno == ENOENT)
+ pr_err("Couldn't find sink \"%s\" on event %s\n"
+ "Missing kernel or device support?\n\n"
+ "Hint: An appropriate sink will be picked automatically if one isn't specified.\n",
+ sink, evsel__name(evsel));
+ else
+ pr_err("Failed to set sink \"%s\" on event %s with %d (%s)\n",
+ sink, evsel__name(evsel), errno,
+ str_error_r(errno, msg, sizeof(msg)));
return ret;
}
@@ -605,6 +621,32 @@ static int cs_etm_get_ro(struct perf_pmu *pmu, int cpu, const char *path)
return val;
}
+static int cs_etm_get_ro_signed(struct perf_pmu *pmu, int cpu, const char *path)
+{
+ char pmu_path[PATH_MAX];
+ int scan;
+ int val = 0;
+
+ /* Get RO metadata from sysfs */
+ snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu, path);
+
+ scan = perf_pmu__scan_file(pmu, pmu_path, "%d", &val);
+ if (scan != 1)
+ pr_err("%s: error reading: %s\n", __func__, pmu_path);
+
+ return val;
+}
+
+static bool cs_etm_pmu_path_exists(struct perf_pmu *pmu, int cpu, const char *path)
+{
+ char pmu_path[PATH_MAX];
+
+ /* Get RO metadata from sysfs */
+ snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu, path);
+
+ return perf_pmu__file_exists(pmu, pmu_path);
+}
+
#define TRCDEVARCH_ARCHPART_SHIFT 0
#define TRCDEVARCH_ARCHPART_MASK GENMASK(11, 0)
#define TRCDEVARCH_ARCHPART(x) (((x) & TRCDEVARCH_ARCHPART_MASK) >> TRCDEVARCH_ARCHPART_SHIFT)
@@ -617,7 +659,7 @@ static bool cs_etm_is_ete(struct auxtrace_record *itr, int cpu)
{
struct cs_etm_recording *ptr = container_of(itr, struct cs_etm_recording, itr);
struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
- int trcdevarch = cs_etm_get_ro(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETE_TRCDEVARCH]);
+ int trcdevarch = cs_etm_get_ro(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TRCDEVARCH]);
/*
* ETE if ARCHVER is 5 (ARCHVER is 4 for ETM) and ARCHPART is 0xA13.
@@ -646,6 +688,51 @@ static void cs_etm_save_etmv4_header(__u64 data[], struct auxtrace_record *itr,
metadata_etmv4_ro[CS_ETMV4_TRCIDR8]);
data[CS_ETMV4_TRCAUTHSTATUS] = cs_etm_get_ro(cs_etm_pmu, cpu,
metadata_etmv4_ro[CS_ETMV4_TRCAUTHSTATUS]);
+
+ /* Kernels older than 5.19 may not expose ts_source */
+ if (cs_etm_pmu_path_exists(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETMV4_TS_SOURCE]))
+ data[CS_ETMV4_TS_SOURCE] = (__u64) cs_etm_get_ro_signed(cs_etm_pmu, cpu,
+ metadata_etmv4_ro[CS_ETMV4_TS_SOURCE]);
+ else {
+ pr_warning("[%03d] pmu file 'ts_source' not found. Fallback to safe value (-1)\n",
+ cpu);
+ data[CS_ETMV4_TS_SOURCE] = (__u64) -1;
+ }
+}
+
+static void cs_etm_save_ete_header(__u64 data[], struct auxtrace_record *itr, int cpu)
+{
+ struct cs_etm_recording *ptr = container_of(itr, struct cs_etm_recording, itr);
+ struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
+
+ /* Get trace configuration register */
+ data[CS_ETE_TRCCONFIGR] = cs_etmv4_get_config(itr);
+ /* Get traceID from the framework */
+ data[CS_ETE_TRCTRACEIDR] = coresight_get_trace_id(cpu);
+ /* Get read-only information from sysFS */
+ data[CS_ETE_TRCIDR0] = cs_etm_get_ro(cs_etm_pmu, cpu,
+ metadata_ete_ro[CS_ETE_TRCIDR0]);
+ data[CS_ETE_TRCIDR1] = cs_etm_get_ro(cs_etm_pmu, cpu,
+ metadata_ete_ro[CS_ETE_TRCIDR1]);
+ data[CS_ETE_TRCIDR2] = cs_etm_get_ro(cs_etm_pmu, cpu,
+ metadata_ete_ro[CS_ETE_TRCIDR2]);
+ data[CS_ETE_TRCIDR8] = cs_etm_get_ro(cs_etm_pmu, cpu,
+ metadata_ete_ro[CS_ETE_TRCIDR8]);
+ data[CS_ETE_TRCAUTHSTATUS] = cs_etm_get_ro(cs_etm_pmu, cpu,
+ metadata_ete_ro[CS_ETE_TRCAUTHSTATUS]);
+ /* ETE uses the same registers as ETMv4 plus TRCDEVARCH */
+ data[CS_ETE_TRCDEVARCH] = cs_etm_get_ro(cs_etm_pmu, cpu,
+ metadata_ete_ro[CS_ETE_TRCDEVARCH]);
+
+ /* Kernels older than 5.19 may not expose ts_source */
+ if (cs_etm_pmu_path_exists(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TS_SOURCE]))
+ data[CS_ETE_TS_SOURCE] = (__u64) cs_etm_get_ro_signed(cs_etm_pmu, cpu,
+ metadata_ete_ro[CS_ETE_TS_SOURCE]);
+ else {
+ pr_warning("[%03d] pmu file 'ts_source' not found. Fallback to safe value (-1)\n",
+ cpu);
+ data[CS_ETE_TS_SOURCE] = (__u64) -1;
+ }
}
static void cs_etm_get_metadata(int cpu, u32 *offset,
@@ -661,11 +748,7 @@ static void cs_etm_get_metadata(int cpu, u32 *offset,
/* first see what kind of tracer this cpu is affined to */
if (cs_etm_is_ete(itr, cpu)) {
magic = __perf_cs_ete_magic;
- /* ETE uses the same registers as ETMv4 plus TRCDEVARCH */
- cs_etm_save_etmv4_header(&info->priv[*offset], itr, cpu);
- info->priv[*offset + CS_ETE_TRCDEVARCH] =
- cs_etm_get_ro(cs_etm_pmu, cpu,
- metadata_etmv4_ro[CS_ETE_TRCDEVARCH]);
+ cs_etm_save_ete_header(&info->priv[*offset], itr, cpu);
/* How much space was used */
increment = CS_ETE_PRIV_MAX;
diff --git a/tools/perf/arch/arm64/util/pmu.c b/tools/perf/arch/arm64/util/pmu.c
index 477e513972a4..fa143acb4c8d 100644
--- a/tools/perf/arch/arm64/util/pmu.c
+++ b/tools/perf/arch/arm64/util/pmu.c
@@ -3,8 +3,10 @@
#include <internal/cpumap.h>
#include "../../../util/cpumap.h"
#include "../../../util/pmu.h"
+#include <api/fs/fs.h>
+#include <math.h>
-const struct pmu_events_table *pmu_events_table__find(void)
+static struct perf_pmu *pmu__find_core_pmu(void)
{
struct perf_pmu *pmu = NULL;
@@ -19,8 +21,46 @@ const struct pmu_events_table *pmu_events_table__find(void)
if (pmu->cpus->nr != cpu__max_cpu().cpu)
return NULL;
- return perf_pmu__find_table(pmu);
+ return pmu;
}
+ return NULL;
+}
+
+const struct pmu_metrics_table *pmu_metrics_table__find(void)
+{
+ struct perf_pmu *pmu = pmu__find_core_pmu();
+
+ if (pmu)
+ return perf_pmu__find_metrics_table(pmu);
return NULL;
}
+
+const struct pmu_events_table *pmu_events_table__find(void)
+{
+ struct perf_pmu *pmu = pmu__find_core_pmu();
+
+ if (pmu)
+ return perf_pmu__find_events_table(pmu);
+
+ return NULL;
+}
+
+double perf_pmu__cpu_slots_per_cycle(void)
+{
+ char path[PATH_MAX];
+ unsigned long long slots = 0;
+ struct perf_pmu *pmu = pmu__find_core_pmu();
+
+ if (pmu) {
+ perf_pmu__pathname_scnprintf(path, sizeof(path),
+ pmu->name, "caps/slots");
+ /*
+ * The value of slots is not greater than 32 bits, but sysfs__read_int
+ * can't read value with 0x prefix, so use sysfs__read_ull instead.
+ */
+ sysfs__read_ull(path, &slots);
+ }
+
+ return slots ? (double)slots : NAN;
+}
diff --git a/tools/perf/arch/powerpc/util/header.c b/tools/perf/arch/powerpc/util/header.c
index e8fe36b10d20..78eef77d8a8d 100644
--- a/tools/perf/arch/powerpc/util/header.c
+++ b/tools/perf/arch/powerpc/util/header.c
@@ -40,11 +40,11 @@ get_cpuid_str(struct perf_pmu *pmu __maybe_unused)
return bufp;
}
-int arch_get_runtimeparam(const struct pmu_event *pe)
+int arch_get_runtimeparam(const struct pmu_metric *pm)
{
int count;
char path[PATH_MAX] = "/devices/hv_24x7/interface/";
- atoi(pe->aggr_mode) == PerChip ? strcat(path, "sockets") : strcat(path, "coresperchip");
+ atoi(pm->aggr_mode) == PerChip ? strcat(path, "sockets") : strcat(path, "coresperchip");
return sysfs__read_int(path, &count) < 0 ? 1 : count;
}
diff --git a/tools/perf/arch/x86/tests/sample-parsing.c b/tools/perf/arch/x86/tests/sample-parsing.c
index 690c7c07e90d..a061e8619267 100644
--- a/tools/perf/arch/x86/tests/sample-parsing.c
+++ b/tools/perf/arch/x86/tests/sample-parsing.c
@@ -27,8 +27,10 @@ static bool samples_same(const struct perf_sample *s1,
const struct perf_sample *s2,
u64 type)
{
- if (type & PERF_SAMPLE_WEIGHT_STRUCT)
+ if (type & PERF_SAMPLE_WEIGHT_STRUCT) {
COMP(ins_lat);
+ COMP(retire_lat);
+ }
return true;
}
@@ -48,6 +50,7 @@ static int do_test(u64 sample_type)
struct perf_sample sample = {
.weight = 101,
.ins_lat = 102,
+ .retire_lat = 103,
};
struct perf_sample sample_out;
size_t i, sz, bufsz;
diff --git a/tools/perf/arch/x86/util/event.c b/tools/perf/arch/x86/util/event.c
index a3acefe6d0c6..e4288d09f3a0 100644
--- a/tools/perf/arch/x86/util/event.c
+++ b/tools/perf/arch/x86/util/event.c
@@ -89,6 +89,7 @@ void arch_perf_parse_sample_weight(struct perf_sample *data,
else {
data->weight = weight.var1_dw;
data->ins_lat = weight.var2_w;
+ data->retire_lat = weight.var3_w;
}
}
@@ -100,5 +101,25 @@ void arch_perf_synthesize_sample_weight(const struct perf_sample *data,
if (type & PERF_SAMPLE_WEIGHT_STRUCT) {
*array &= 0xffffffff;
*array |= ((u64)data->ins_lat << 32);
+ *array |= ((u64)data->retire_lat << 48);
}
}
+
+const char *arch_perf_header_entry(const char *se_header)
+{
+ if (!strcmp(se_header, "Local Pipeline Stage Cycle"))
+ return "Local Retire Latency";
+ else if (!strcmp(se_header, "Pipeline Stage Cycle"))
+ return "Retire Latency";
+
+ return se_header;
+}
+
+int arch_support_sort_key(const char *sort_key)
+{
+ if (!strcmp(sort_key, "p_stage_cyc"))
+ return 1;
+ if (!strcmp(sort_key, "local_p_stage_cyc"))
+ return 1;
+ return 0;
+}
diff --git a/tools/perf/arch/x86/util/pmu.c b/tools/perf/arch/x86/util/pmu.c
index 74d69db1ea99..358340b34243 100644
--- a/tools/perf/arch/x86/util/pmu.c
+++ b/tools/perf/arch/x86/util/pmu.c
@@ -15,8 +15,6 @@
#include "../../../util/pmu.h"
#include "../../../util/fncache.h"
-#define TEMPLATE_ALIAS "%s/bus/event_source/devices/%s/alias"
-
struct pmu_alias {
char *name;
char *alias;
@@ -72,18 +70,14 @@ static int setup_pmu_alias_list(void)
char path[PATH_MAX];
DIR *dir;
struct dirent *dent;
- const char *sysfs = sysfs__mountpoint();
struct pmu_alias *pmu_alias;
char buf[MAX_PMU_NAME_LEN];
FILE *file;
int ret = -ENOMEM;
- if (!sysfs)
+ if (!perf_pmu__event_source_devices_scnprintf(path, sizeof(path)))
return -1;
- snprintf(path, PATH_MAX,
- "%s" EVENT_SOURCE_DEVICE_PATH, sysfs);
-
dir = opendir(path);
if (!dir)
return -errno;
@@ -93,9 +87,7 @@ static int setup_pmu_alias_list(void)
!strcmp(dent->d_name, ".."))
continue;
- snprintf(path, PATH_MAX,
- TEMPLATE_ALIAS, sysfs, dent->d_name);
-
+ perf_pmu__pathname_scnprintf(path, sizeof(path), dent->d_name, "alias");
if (!file_available(path))
continue;
diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h
index a5d49b3b6a09..e43893151a3e 100644
--- a/tools/perf/bench/bench.h
+++ b/tools/perf/bench/bench.h
@@ -22,6 +22,8 @@ int bench_numa(int argc, const char **argv);
int bench_sched_messaging(int argc, const char **argv);
int bench_sched_pipe(int argc, const char **argv);
int bench_syscall_basic(int argc, const char **argv);
+int bench_syscall_getpgid(int argc, const char **argv);
+int bench_syscall_execve(int argc, const char **argv);
int bench_mem_memcpy(int argc, const char **argv);
int bench_mem_memset(int argc, const char **argv);
int bench_mem_find_bit(int argc, const char **argv);
diff --git a/tools/perf/bench/syscall.c b/tools/perf/bench/syscall.c
index 9b751016f4b6..fe79f7f3091e 100644
--- a/tools/perf/bench/syscall.c
+++ b/tools/perf/bench/syscall.c
@@ -14,6 +14,7 @@
#include <sys/time.h>
#include <sys/syscall.h>
#include <sys/types.h>
+#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
@@ -30,25 +31,77 @@ static const char * const bench_syscall_usage[] = {
NULL
};
-int bench_syscall_basic(int argc, const char **argv)
+static void test_execve(void)
+{
+ const char *pathname = "/bin/true";
+ char *const argv[] = { (char *)pathname, NULL };
+ pid_t pid = fork();
+
+ if (pid < 0) {
+ fprintf(stderr, "fork failed\n");
+ exit(1);
+ } else if (pid == 0) {
+ execve(pathname, argv, NULL);
+ fprintf(stderr, "execve /bin/true failed\n");
+ exit(1);
+ } else {
+ if (waitpid(pid, NULL, 0) < 0) {
+ fprintf(stderr, "waitpid failed\n");
+ exit(1);
+ }
+ }
+}
+
+static int bench_syscall_common(int argc, const char **argv, int syscall)
{
struct timeval start, stop, diff;
unsigned long long result_usec = 0;
+ const char *name = NULL;
int i;
argc = parse_options(argc, argv, options, bench_syscall_usage, 0);
gettimeofday(&start, NULL);
- for (i = 0; i < loops; i++)
- getppid();
+ for (i = 0; i < loops; i++) {
+ switch (syscall) {
+ case __NR_getppid:
+ getppid();
+ break;
+ case __NR_getpgid:
+ getpgid(0);
+ break;
+ case __NR_execve:
+ test_execve();
+ /* Only loop 10000 times to save time */
+ if (i == 10000)
+ loops = 10000;
+ break;
+ default:
+ break;
+ }
+ }
gettimeofday(&stop, NULL);
timersub(&stop, &start, &diff);
+ switch (syscall) {
+ case __NR_getppid:
+ name = "getppid()";
+ break;
+ case __NR_getpgid:
+ name = "getpgid()";
+ break;
+ case __NR_execve:
+ name = "execve()";
+ break;
+ default:
+ break;
+ }
+
switch (bench_format) {
case BENCH_FORMAT_DEFAULT:
- printf("# Executed %'d getppid() calls\n", loops);
+ printf("# Executed %'d %s calls\n", loops, name);
result_usec = diff.tv_sec * 1000000;
result_usec += diff.tv_usec;
@@ -79,3 +132,18 @@ int bench_syscall_basic(int argc, const char **argv)
return 0;
}
+
+int bench_syscall_basic(int argc, const char **argv)
+{
+ return bench_syscall_common(argc, argv, __NR_getppid);
+}
+
+int bench_syscall_getpgid(int argc, const char **argv)
+{
+ return bench_syscall_common(argc, argv, __NR_getpgid);
+}
+
+int bench_syscall_execve(int argc, const char **argv)
+{
+ return bench_syscall_common(argc, argv, __NR_execve);
+}
diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c
index 334ab897aae3..814e9afc86f6 100644
--- a/tools/perf/builtin-bench.c
+++ b/tools/perf/builtin-bench.c
@@ -52,6 +52,8 @@ static struct bench sched_benchmarks[] = {
static struct bench syscall_benchmarks[] = {
{ "basic", "Benchmark for basic getppid(2) calls", bench_syscall_basic },
+ { "getpgid", "Benchmark for getpgid(2) calls", bench_syscall_getpgid },
+ { "execve", "Benchmark for execve(2) calls", bench_syscall_execve },
{ "all", "Run all syscall benchmarks", NULL },
{ NULL, NULL, NULL },
};
@@ -150,7 +152,7 @@ unsigned int bench_repeat = 10; /* default number of times to repeat the run */
static const struct option bench_options[] = {
OPT_STRING('f', "format", &bench_format_str, "default|simple", "Specify the output formatting style"),
- OPT_UINTEGER('r', "repeat", &bench_repeat, "Specify amount of times to repeat the run"),
+ OPT_UINTEGER('r', "repeat", &bench_repeat, "Specify number of times to repeat the run"),
OPT_END()
};
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index 52d94c7dd836..56974eae0638 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -524,7 +524,7 @@ static int dcacheline_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
char buf[20];
if (he->mem_info)
- addr = cl_address(he->mem_info->daddr.addr);
+ addr = cl_address(he->mem_info->daddr.addr, chk_double_cl);
return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
}
@@ -562,7 +562,7 @@ static int offset_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
char buf[20];
if (he->mem_info)
- addr = cl_offset(he->mem_info->daddr.al_addr);
+ addr = cl_offset(he->mem_info->daddr.al_addr, chk_double_cl);
return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
}
@@ -574,9 +574,10 @@ offset_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
uint64_t l = 0, r = 0;
if (left->mem_info)
- l = cl_offset(left->mem_info->daddr.addr);
+ l = cl_offset(left->mem_info->daddr.addr, chk_double_cl);
+
if (right->mem_info)
- r = cl_offset(right->mem_info->daddr.addr);
+ r = cl_offset(right->mem_info->daddr.addr, chk_double_cl);
return (int64_t)(r - l);
}
@@ -2590,7 +2591,7 @@ perf_c2c_cacheline_browser__title(struct hist_browser *browser,
he = cl_browser->he;
if (he->mem_info)
- addr = cl_address(he->mem_info->daddr.addr);
+ addr = cl_address(he->mem_info->daddr.addr, chk_double_cl);
scnprintf(bf, size, "Cacheline 0x%lx", addr);
return 0;
@@ -2788,15 +2789,16 @@ static int ui_quirks(void)
if (!c2c.use_stdio) {
dim_offset.width = 5;
dim_offset.header = header_offset_tui;
- nodestr = "CL";
+ nodestr = chk_double_cl ? "Double-CL" : "CL";
}
dim_percent_costly_snoop.header = percent_costly_snoop_header[c2c.display];
/* Fix the zero line for dcacheline column. */
- buf = fill_line("Cacheline", dim_dcacheline.width +
- dim_dcacheline_node.width +
- dim_dcacheline_count.width + 4);
+ buf = fill_line(chk_double_cl ? "Double-Cacheline" : "Cacheline",
+ dim_dcacheline.width +
+ dim_dcacheline_node.width +
+ dim_dcacheline_count.width + 4);
if (!buf)
return -ENOMEM;
@@ -3037,6 +3039,7 @@ static int perf_c2c__report(int argc, const char **argv)
OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr,
"Enable LBR callgraph stitching approach"),
+ OPT_BOOLEAN(0, "double-cl", &chk_double_cl, "Detect adjacent cacheline false sharing"),
OPT_PARENT(c2c_options),
OPT_END()
};
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index 3f4e4dd5abf3..10bb1d494258 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -215,14 +215,14 @@ static int perf_event__repipe_event_update(struct perf_tool *tool,
#ifdef HAVE_AUXTRACE_SUPPORT
-static int copy_bytes(struct perf_inject *inject, int fd, off_t size)
+static int copy_bytes(struct perf_inject *inject, struct perf_data *data, off_t size)
{
char buf[4096];
ssize_t ssz;
int ret;
while (size > 0) {
- ssz = read(fd, buf, min(size, (off_t)sizeof(buf)));
+ ssz = perf_data__read(data, buf, min(size, (off_t)sizeof(buf)));
if (ssz < 0)
return -errno;
ret = output_bytes(inject, buf, ssz);
@@ -260,7 +260,7 @@ static s64 perf_event__repipe_auxtrace(struct perf_session *session,
ret = output_bytes(inject, event, event->header.size);
if (ret < 0)
return ret;
- ret = copy_bytes(inject, perf_data__fd(session->data),
+ ret = copy_bytes(inject, session->data,
event->auxtrace.size);
} else {
ret = output_bytes(inject, event,
@@ -538,6 +538,7 @@ static int perf_event__repipe_buildid_mmap2(struct perf_tool *tool,
dso->hit = 1;
}
dso__put(dso);
+ perf_event__repipe(tool, event, sample, machine);
return 0;
}
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 8ae0a1535293..f3029742b800 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -653,7 +653,6 @@ static const struct {
{ "__GFP_HIGHMEM", "HM" },
{ "GFP_DMA32", "D32" },
{ "__GFP_HIGH", "H" },
- { "__GFP_ATOMIC", "_A" },
{ "__GFP_IO", "I" },
{ "__GFP_FS", "F" },
{ "__GFP_NOWARN", "NWR" },
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index 137d73edb541..791f513ae5b4 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -99,8 +99,7 @@ static void default_print_event(void *ps, const char *pmu_name, const char *topi
const char *scale_unit __maybe_unused,
bool deprecated, const char *event_type_desc,
const char *desc, const char *long_desc,
- const char *encoding_desc,
- const char *metric_name, const char *metric_expr)
+ const char *encoding_desc)
{
struct print_state *print_state = ps;
int pos;
@@ -159,10 +158,6 @@ static void default_print_event(void *ps, const char *pmu_name, const char *topi
if (print_state->detailed && encoding_desc) {
printf("%*s", 8, "");
wordwrap(encoding_desc, 8, pager_get_columns(), 0);
- if (metric_name)
- printf(" MetricName: %s", metric_name);
- if (metric_expr)
- printf(" MetricExpr: %s", metric_expr);
putchar('\n');
}
}
@@ -308,8 +303,7 @@ static void json_print_event(void *ps, const char *pmu_name, const char *topic,
const char *scale_unit,
bool deprecated, const char *event_type_desc,
const char *desc, const char *long_desc,
- const char *encoding_desc,
- const char *metric_name, const char *metric_expr)
+ const char *encoding_desc)
{
struct json_print_state *print_state = ps;
bool need_sep = false;
@@ -366,16 +360,6 @@ static void json_print_event(void *ps, const char *pmu_name, const char *topic,
encoding_desc);
need_sep = true;
}
- if (metric_name) {
- fix_escape_printf(&buf, "%s\t\"MetricName\": \"%S\"", need_sep ? ",\n" : "",
- metric_name);
- need_sep = true;
- }
- if (metric_expr) {
- fix_escape_printf(&buf, "%s\t\"MetricExpr\": \"%S\"", need_sep ? ",\n" : "",
- metric_expr);
- need_sep = true;
- }
printf("%s}", need_sep ? "\n" : "");
strbuf_release(&buf);
}
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index 506c2fe42d52..054997edd98b 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -58,16 +58,28 @@ static struct rb_root thread_stats;
static bool combine_locks;
static bool show_thread_stats;
static bool show_lock_addrs;
+static bool show_lock_owner;
static bool use_bpf;
static unsigned long bpf_map_entries = 10240;
static int max_stack_depth = CONTENTION_STACK_DEPTH;
static int stack_skip = CONTENTION_STACK_SKIP;
static int print_nr_entries = INT_MAX / 2;
+static LIST_HEAD(callstack_filters);
+
+struct callstack_filter {
+ struct list_head list;
+ char name[];
+};
static struct lock_filter filters;
static enum lock_aggr_mode aggr_mode = LOCK_AGGR_ADDR;
+static bool needs_callstack(void)
+{
+ return verbose > 0 || !list_empty(&callstack_filters);
+}
+
static struct thread_stat *thread_stat_find(u32 tid)
{
struct rb_node *node;
@@ -454,7 +466,7 @@ static struct lock_stat *pop_from_result(void)
return container_of(node, struct lock_stat, rb);
}
-static struct lock_stat *lock_stat_find(u64 addr)
+struct lock_stat *lock_stat_find(u64 addr)
{
struct hlist_head *entry = lockhashentry(addr);
struct lock_stat *ret;
@@ -466,7 +478,7 @@ static struct lock_stat *lock_stat_find(u64 addr)
return NULL;
}
-static struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags)
+struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags)
{
struct hlist_head *entry = lockhashentry(addr);
struct lock_stat *ret, *new;
@@ -498,6 +510,34 @@ alloc_failed:
return NULL;
}
+bool match_callstack_filter(struct machine *machine, u64 *callstack)
+{
+ struct map *kmap;
+ struct symbol *sym;
+ u64 ip;
+
+ if (list_empty(&callstack_filters))
+ return true;
+
+ for (int i = 0; i < max_stack_depth; i++) {
+ struct callstack_filter *filter;
+
+ if (!callstack || !callstack[i])
+ break;
+
+ ip = callstack[i];
+ sym = machine__find_kernel_symbol(machine, ip, &kmap);
+ if (sym == NULL)
+ continue;
+
+ list_for_each_entry(filter, &callstack_filters, list) {
+ if (strstr(sym->name, filter->name))
+ return true;
+ }
+ }
+ return false;
+}
+
struct trace_lock_handler {
/* it's used on CONFIG_LOCKDEP */
int (*acquire_event)(struct evsel *evsel,
@@ -1059,12 +1099,6 @@ static int report_lock_contention_begin_event(struct evsel *evsel,
ls = lock_stat_findnew(key, name, flags);
if (!ls)
return -ENOMEM;
-
- if (aggr_mode == LOCK_AGGR_CALLER && verbose > 0) {
- ls->callstack = get_callstack(sample, max_stack_depth);
- if (ls->callstack == NULL)
- return -ENOMEM;
- }
}
if (filters.nr_types) {
@@ -1095,6 +1129,22 @@ static int report_lock_contention_begin_event(struct evsel *evsel,
return 0;
}
+ if (needs_callstack()) {
+ u64 *callstack = get_callstack(sample, max_stack_depth);
+ if (callstack == NULL)
+ return -ENOMEM;
+
+ if (!match_callstack_filter(machine, callstack)) {
+ free(callstack);
+ return 0;
+ }
+
+ if (ls->callstack == NULL)
+ ls->callstack = callstack;
+ else
+ free(callstack);
+ }
+
ts = thread_stat_findnew(sample->tid);
if (!ts)
return -ENOMEM;
@@ -1567,7 +1617,8 @@ static void print_contention_result(struct lock_contention *con)
switch (aggr_mode) {
case LOCK_AGGR_TASK:
- pr_info(" %10s %s\n\n", "pid", "comm");
+ pr_info(" %10s %s\n\n", "pid",
+ show_lock_owner ? "owner" : "comm");
break;
case LOCK_AGGR_CALLER:
pr_info(" %10s %s\n\n", "type", "caller");
@@ -1607,7 +1658,8 @@ static void print_contention_result(struct lock_contention *con)
case LOCK_AGGR_TASK:
pid = st->addr;
t = perf_session__findnew(session, pid);
- pr_info(" %10d %s\n", pid, thread__comm_str(t));
+ pr_info(" %10d %s\n",
+ pid, pid == -1 ? "Unknown" : thread__comm_str(t));
break;
case LOCK_AGGR_ADDR:
pr_info(" %016llx %s\n", (unsigned long long)st->addr,
@@ -1719,6 +1771,37 @@ static void sighandler(int sig __maybe_unused)
{
}
+static int check_lock_contention_options(const struct option *options,
+ const char * const *usage)
+
+{
+ if (show_thread_stats && show_lock_addrs) {
+ pr_err("Cannot use thread and addr mode together\n");
+ parse_options_usage(usage, options, "threads", 0);
+ parse_options_usage(NULL, options, "lock-addr", 0);
+ return -1;
+ }
+
+ if (show_lock_owner && !use_bpf) {
+ pr_err("Lock owners are available only with BPF\n");
+ parse_options_usage(usage, options, "lock-owner", 0);
+ parse_options_usage(NULL, options, "use-bpf", 0);
+ return -1;
+ }
+
+ if (show_lock_owner && show_lock_addrs) {
+ pr_err("Cannot use owner and addr mode together\n");
+ parse_options_usage(usage, options, "lock-owner", 0);
+ parse_options_usage(NULL, options, "lock-addr", 0);
+ return -1;
+ }
+
+ if (show_lock_owner)
+ show_thread_stats = true;
+
+ return 0;
+}
+
static int __cmd_contention(int argc, const char **argv)
{
int err = -EINVAL;
@@ -1743,6 +1826,8 @@ static int __cmd_contention(int argc, const char **argv)
.max_stack = max_stack_depth,
.stack_skip = stack_skip,
.filters = &filters,
+ .save_callstack = needs_callstack(),
+ .owner = show_lock_owner,
};
session = perf_session__new(use_bpf ? NULL : &data, &eops);
@@ -1756,6 +1841,9 @@ static int __cmd_contention(int argc, const char **argv)
con.aggr_mode = aggr_mode = show_thread_stats ? LOCK_AGGR_TASK :
show_lock_addrs ? LOCK_AGGR_ADDR : LOCK_AGGR_CALLER;
+ if (con.aggr_mode == LOCK_AGGR_CALLER)
+ con.save_callstack = true;
+
/* for lock function check */
symbol_conf.sort_by_name = true;
symbol_conf.allow_aliases = true;
@@ -2123,6 +2211,33 @@ static int parse_lock_addr(const struct option *opt __maybe_unused, const char *
return ret;
}
+static int parse_call_stack(const struct option *opt __maybe_unused, const char *str,
+ int unset __maybe_unused)
+{
+ char *s, *tmp, *tok;
+ int ret = 0;
+
+ s = strdup(str);
+ if (s == NULL)
+ return -1;
+
+ for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) {
+ struct callstack_filter *entry;
+
+ entry = malloc(sizeof(*entry) + strlen(tok) + 1);
+ if (entry == NULL) {
+ pr_err("Memory allocation failure\n");
+ return -1;
+ }
+
+ strcpy(entry->name, tok);
+ list_add_tail(&entry->list, &callstack_filters);
+ }
+
+ free(s);
+ return ret;
+}
+
int cmd_lock(int argc, const char **argv)
{
const struct option lock_options[] = {
@@ -2190,6 +2305,9 @@ int cmd_lock(int argc, const char **argv)
"Filter specific type of locks", parse_lock_type),
OPT_CALLBACK('L', "lock-filter", NULL, "ADDRS/NAMES",
"Filter specific address/symbol of locks", parse_lock_addr),
+ OPT_CALLBACK('S', "callstack-filter", NULL, "NAMES",
+ "Filter specific function in the callstack", parse_call_stack),
+ OPT_BOOLEAN('o', "lock-owner", &show_lock_owner, "show lock owners instead of waiters"),
OPT_PARENT(lock_options)
};
@@ -2260,14 +2378,9 @@ int cmd_lock(int argc, const char **argv)
contention_usage, 0);
}
- if (show_thread_stats && show_lock_addrs) {
- pr_err("Cannot use thread and addr mode together\n");
- parse_options_usage(contention_usage, contention_options,
- "threads", 0);
- parse_options_usage(NULL, contention_options,
- "lock-addr", 0);
+ if (check_lock_contention_options(contention_options,
+ contention_usage) < 0)
return -1;
- }
rc = __cmd_contention(argc, argv);
} else {
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index ed73d0b89ca2..e72f6cea76f7 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -383,9 +383,18 @@ static int perf_add_probe_events(struct perf_probe_event *pevs, int npevs)
/* Note that it is possible to skip all events because of blacklist */
if (event) {
+#ifndef HAVE_LIBTRACEEVENT
+ pr_info("\nperf is not linked with libtraceevent, to use the new probe you can use tracefs:\n\n");
+ pr_info("\tcd /sys/kernel/tracing/\n");
+ pr_info("\techo 1 > events/%s/%s/enable\n", group, event);
+ pr_info("\techo 1 > tracing_on\n");
+ pr_info("\tcat trace_pipe\n");
+ pr_info("\tBefore removing the probe, echo 0 > events/%s/%s/enable\n", group, event);
+#else
/* Show how to use the event. */
pr_info("\nYou can now use it in all perf tools, such as:\n\n");
pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", group, event);
+#endif
}
out_cleanup:
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 29dcd454b8e2..8374117e66f6 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -154,6 +154,7 @@ struct record {
struct perf_tool tool;
struct record_opts opts;
u64 bytes_written;
+ u64 thread_bytes_written;
struct perf_data data;
struct auxtrace_record *itr;
struct evlist *evlist;
@@ -226,14 +227,7 @@ static bool switch_output_time(struct record *rec)
static u64 record__bytes_written(struct record *rec)
{
- int t;
- u64 bytes_written = rec->bytes_written;
- struct record_thread *thread_data = rec->thread_data;
-
- for (t = 0; t < rec->nr_threads; t++)
- bytes_written += thread_data[t].bytes_written;
-
- return bytes_written;
+ return rec->bytes_written + rec->thread_bytes_written;
}
static bool record__output_max_size_exceeded(struct record *rec)
@@ -255,10 +249,12 @@ static int record__write(struct record *rec, struct mmap *map __maybe_unused,
return -1;
}
- if (map && map->file)
+ if (map && map->file) {
thread->bytes_written += size;
- else
+ rec->thread_bytes_written += size;
+ } else {
rec->bytes_written += size;
+ }
if (record__output_max_size_exceeded(rec) && !done) {
fprintf(stderr, "[ perf record: perf size limit reached (%" PRIu64 " KB),"
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 69394ac0a20d..a792214d1af8 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -59,6 +59,7 @@
#include "util/dlfilter.h"
#include "util/record.h"
#include "util/util.h"
+#include "util/cgroup.h"
#include "perf.h"
#include <linux/ctype.h>
@@ -130,6 +131,8 @@ enum perf_output_field {
PERF_OUTPUT_BRSTACKINSNLEN = 1ULL << 36,
PERF_OUTPUT_MACHINE_PID = 1ULL << 37,
PERF_OUTPUT_VCPU = 1ULL << 38,
+ PERF_OUTPUT_CGROUP = 1ULL << 39,
+ PERF_OUTPUT_RETIRE_LAT = 1ULL << 40,
};
struct perf_script {
@@ -200,6 +203,8 @@ struct output_option {
{.str = "brstackinsnlen", .field = PERF_OUTPUT_BRSTACKINSNLEN},
{.str = "machine_pid", .field = PERF_OUTPUT_MACHINE_PID},
{.str = "vcpu", .field = PERF_OUTPUT_VCPU},
+ {.str = "cgroup", .field = PERF_OUTPUT_CGROUP},
+ {.str = "retire_lat", .field = PERF_OUTPUT_RETIRE_LAT},
};
enum {
@@ -275,7 +280,7 @@ static struct {
PERF_OUTPUT_ADDR | PERF_OUTPUT_DATA_SRC |
PERF_OUTPUT_WEIGHT | PERF_OUTPUT_PHYS_ADDR |
PERF_OUTPUT_DATA_PAGE_SIZE | PERF_OUTPUT_CODE_PAGE_SIZE |
- PERF_OUTPUT_INS_LAT,
+ PERF_OUTPUT_INS_LAT | PERF_OUTPUT_RETIRE_LAT,
.invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT,
},
@@ -542,6 +547,16 @@ static int evsel__check_attr(struct evsel *evsel, struct perf_session *session)
evsel__check_stype(evsel, PERF_SAMPLE_WEIGHT_STRUCT, "WEIGHT_STRUCT", PERF_OUTPUT_INS_LAT))
return -EINVAL;
+ if (PRINT_FIELD(CGROUP) &&
+ evsel__check_stype(evsel, PERF_SAMPLE_CGROUP, "CGROUP", PERF_OUTPUT_CGROUP)) {
+ pr_err("Hint: run 'perf record --all-cgroups ...'\n");
+ return -EINVAL;
+ }
+
+ if (PRINT_FIELD(RETIRE_LAT) &&
+ evsel__check_stype(evsel, PERF_SAMPLE_WEIGHT_STRUCT, "WEIGHT_STRUCT", PERF_OUTPUT_RETIRE_LAT))
+ return -EINVAL;
+
return 0;
}
@@ -880,12 +895,13 @@ mispred_str(struct branch_entry *br)
static int print_bstack_flags(FILE *fp, struct branch_entry *br)
{
- return fprintf(fp, "/%c/%c/%c/%d/%s ",
+ return fprintf(fp, "/%c/%c/%c/%d/%s/%s ",
mispred_str(br),
br->flags.in_tx ? 'X' : '-',
br->flags.abort ? 'A' : '-',
br->flags.cycles,
- get_branch_type(br));
+ get_branch_type(br),
+ br->flags.spec ? branch_spec_desc(br->flags.spec) : "-");
}
static int perf_sample__fprintf_brstack(struct perf_sample *sample,
@@ -1301,7 +1317,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
goto out;
/*
- * Print final block upto sample
+ * Print final block up to sample
*
* Due to pipeline delays the LBRs might be missing a branch
* or two, which can result in very large or negative blocks
@@ -2178,6 +2194,9 @@ static void process_event(struct perf_script *script,
if (PRINT_FIELD(INS_LAT))
fprintf(fp, "%16" PRIu16, sample->ins_lat);
+ if (PRINT_FIELD(RETIRE_LAT))
+ fprintf(fp, "%16" PRIu16, sample->retire_lat);
+
if (PRINT_FIELD(IP)) {
struct callchain_cursor *cursor = NULL;
@@ -2220,6 +2239,17 @@ static void process_event(struct perf_script *script,
if (PRINT_FIELD(CODE_PAGE_SIZE))
fprintf(fp, " %s", get_page_size_name(sample->code_page_size, str));
+ if (PRINT_FIELD(CGROUP)) {
+ const char *cgrp_name;
+ struct cgroup *cgrp = cgroup__find(machine->env,
+ sample->cgroup);
+ if (cgrp != NULL)
+ cgrp_name = cgrp->name;
+ else
+ cgrp_name = "unknown";
+ fprintf(fp, " %s", cgrp_name);
+ }
+
perf_sample__fprintf_ipc(sample, attr, fp);
fprintf(fp, "\n");
@@ -3856,7 +3886,7 @@ int cmd_script(int argc, const char **argv)
"brstacksym,flags,data_src,weight,bpf-output,brstackinsn,"
"brstackinsnlen,brstackoff,callindent,insn,insnlen,synth,"
"phys_addr,metric,misc,srccode,ipc,tod,data_page_size,"
- "code_page_size,ins_lat",
+ "code_page_size,ins_lat,machine_pid,vcpu,cgroup,retire_lat",
parse_output_fields),
OPT_BOOLEAN('a', "all-cpus", &system_wide,
"system-wide collection from all CPUs"),
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 9f3e4b257516..fa7c40956d0f 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -539,12 +539,7 @@ static int enable_counters(void)
return err;
}
- /*
- * We need to enable counters only if:
- * - we don't have tracee (attaching to task or cpu)
- * - we have initial delay configured
- */
- if (!target__none(&target)) {
+ if (!target__enable_on_exec(&target)) {
if (!all_counters_use_bpf)
evlist__enable(evsel_list);
}
@@ -914,7 +909,7 @@ try_again_reset:
return err;
}
- if (stat_config.initial_delay) {
+ if (target.initial_delay) {
pr_info(EVLIST_DISABLED_MSG);
} else {
err = enable_counters();
@@ -926,8 +921,8 @@ try_again_reset:
if (forks)
evlist__start_workload(evsel_list);
- if (stat_config.initial_delay > 0) {
- usleep(stat_config.initial_delay * USEC_PER_MSEC);
+ if (target.initial_delay > 0) {
+ usleep(target.initial_delay * USEC_PER_MSEC);
err = enable_counters();
if (err)
return -1;
@@ -1248,7 +1243,7 @@ static struct option stat_options[] = {
"aggregate counts per thread", AGGR_THREAD),
OPT_SET_UINT(0, "per-node", &stat_config.aggr_mode,
"aggregate counts per numa node", AGGR_NODE),
- OPT_INTEGER('D', "delay", &stat_config.initial_delay,
+ OPT_INTEGER('D', "delay", &target.initial_delay,
"ms to wait before starting measurement after program start (-1: start with events disabled)"),
OPT_CALLBACK_NOOPT(0, "metric-only", &stat_config.metric_only, NULL,
"Only print computed metrics. No raw values", enable_metric_only),
@@ -2524,7 +2519,6 @@ int cmd_stat(int argc, const char **argv)
&stat_config.metric_events);
zfree(&metrics);
}
- perf_stat__collect_metric_expr(evsel_list);
perf_stat__init_shadow_stats();
if (add_default_attributes())
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index d21fe0f32a6d..610fb60b1c0d 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -2731,10 +2731,8 @@ static size_t trace__fprintf_tp_fields(struct trace *trace, struct evsel *evsel,
offset = format_field__intval(field, sample, evsel->needs_swap);
syscall_arg.len = offset >> 16;
offset &= 0xffff;
-#ifdef HAVE_LIBTRACEEVENT_TEP_FIELD_IS_RELATIVE
- if (field->flags & TEP_FIELD_IS_RELATIVE)
+ if (tep_field_is_relative(field->flags))
offset += field->offset + field->size;
-#endif
}
val = (uintptr_t)(sample->raw_data + offset);
diff --git a/tools/perf/perf-completion.sh b/tools/perf/perf-completion.sh
index fdf75d45efff..978249d7868c 100644
--- a/tools/perf/perf-completion.sh
+++ b/tools/perf/perf-completion.sh
@@ -165,7 +165,12 @@ __perf_main ()
local cur1=${COMP_WORDS[COMP_CWORD]}
local raw_evts=$($cmd list --raw-dump)
- local arr s tmp result
+ local arr s tmp result cpu_evts
+
+ # aarch64 doesn't have /sys/bus/event_source/devices/cpu/events
+ if [[ `uname -m` != aarch64 ]]; then
+ cpu_evts=$(ls /sys/bus/event_source/devices/cpu/events)
+ fi
if [[ "$cur1" == */* && ${cur1#*/} =~ ^[A-Z] ]]; then
OLD_IFS="$IFS"
@@ -183,9 +188,9 @@ __perf_main ()
fi
done
- evts=${result}" "$(ls /sys/bus/event_source/devices/cpu/events)
+ evts=${result}" "${cpu_evts}
else
- evts=${raw_evts}" "$(ls /sys/bus/event_source/devices/cpu/events)
+ evts=${raw_evts}" "${cpu_evts}
fi
if [[ "$cur1" == , ]]; then
diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build
index 15b9e8fdbffa..150765f2baee 100644
--- a/tools/perf/pmu-events/Build
+++ b/tools/perf/pmu-events/Build
@@ -6,10 +6,16 @@ JDIR_TEST = pmu-events/arch/test
JSON_TEST = $(shell [ -d $(JDIR_TEST) ] && \
find $(JDIR_TEST) -name '*.json')
JEVENTS_PY = pmu-events/jevents.py
+METRIC_PY = pmu-events/metric.py
+METRIC_TEST_PY = pmu-events/metric_test.py
+EMPTY_PMU_EVENTS_C = pmu-events/empty-pmu-events.c
+PMU_EVENTS_C = $(OUTPUT)pmu-events/pmu-events.c
+METRIC_TEST_LOG = $(OUTPUT)pmu-events/metric_test.log
ifeq ($(JEVENTS_ARCH),)
JEVENTS_ARCH=$(SRCARCH)
endif
+JEVENTS_MODEL ?= all
#
# Locate/process JSON files in pmu-events/arch/
@@ -17,11 +23,15 @@ endif
#
ifeq ($(NO_JEVENTS),1)
-$(OUTPUT)pmu-events/pmu-events.c: pmu-events/empty-pmu-events.c
+$(PMU_EVENTS_C): $(EMPTY_PMU_EVENTS_C)
$(call rule_mkdir)
$(Q)$(call echo-cmd,gen)cp $< $@
else
-$(OUTPUT)pmu-events/pmu-events.c: $(JSON) $(JSON_TEST) $(JEVENTS_PY) pmu-events/metric.py
+$(METRIC_TEST_LOG): $(METRIC_TEST_PY) $(METRIC_PY)
$(call rule_mkdir)
- $(Q)$(call echo-cmd,gen)$(PYTHON) $(JEVENTS_PY) $(JEVENTS_ARCH) pmu-events/arch $@
+ $(Q)$(call echo-cmd,test)$(PYTHON) $< 2> $@ || (cat $@ && false)
+
+$(PMU_EVENTS_C): $(JSON) $(JSON_TEST) $(JEVENTS_PY) $(METRIC_PY) $(METRIC_TEST_LOG)
+ $(call rule_mkdir)
+ $(Q)$(call echo-cmd,gen)$(PYTHON) $(JEVENTS_PY) $(JEVENTS_ARCH) $(JEVENTS_MODEL) pmu-events/arch $@
endif
diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/metrics.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/metrics.json
new file mode 100644
index 000000000000..8ad15b726dca
--- /dev/null
+++ b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/metrics.json
@@ -0,0 +1,273 @@
+[
+ {
+ "ArchStdEvent": "FRONTEND_BOUND",
+ "MetricExpr": "((stall_slot_frontend) if (#slots - 5) else (stall_slot_frontend - cpu_cycles)) / (#slots * cpu_cycles)"
+ },
+ {
+ "ArchStdEvent": "BAD_SPECULATION",
+ "MetricExpr": "(1 - op_retired / op_spec) * (1 - (stall_slot if (#slots - 5) else (stall_slot - cpu_cycles)) / (#slots * cpu_cycles))"
+ },
+ {
+ "ArchStdEvent": "RETIRING",
+ "MetricExpr": "(op_retired / op_spec) * (1 - (stall_slot if (#slots - 5) else (stall_slot - cpu_cycles)) / (#slots * cpu_cycles))"
+ },
+ {
+ "ArchStdEvent": "BACKEND_BOUND"
+ },
+ {
+ "MetricExpr": "L1D_TLB_REFILL / L1D_TLB",
+ "BriefDescription": "The rate of L1D TLB refill to the overall L1D TLB lookups",
+ "MetricGroup": "TLB",
+ "MetricName": "l1d_tlb_miss_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "L1I_TLB_REFILL / L1I_TLB",
+ "BriefDescription": "The rate of L1I TLB refill to the overall L1I TLB lookups",
+ "MetricGroup": "TLB",
+ "MetricName": "l1i_tlb_miss_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "L2D_TLB_REFILL / L2D_TLB",
+ "BriefDescription": "The rate of L2D TLB refill to the overall L2D TLB lookups",
+ "MetricGroup": "TLB",
+ "MetricName": "l2_tlb_miss_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "DTLB_WALK / INST_RETIRED * 1000",
+ "BriefDescription": "The rate of TLB Walks per kilo instructions for data accesses",
+ "MetricGroup": "TLB",
+ "MetricName": "dtlb_mpki",
+ "ScaleUnit": "1MPKI"
+ },
+ {
+ "MetricExpr": "DTLB_WALK / L1D_TLB",
+ "BriefDescription": "The rate of DTLB Walks to the overall L1D TLB lookups",
+ "MetricGroup": "TLB",
+ "MetricName": "dtlb_walk_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "ITLB_WALK / INST_RETIRED * 1000",
+ "BriefDescription": "The rate of TLB Walks per kilo instructions for instruction accesses",
+ "MetricGroup": "TLB",
+ "MetricName": "itlb_mpki",
+ "ScaleUnit": "1MPKI"
+ },
+ {
+ "MetricExpr": "ITLB_WALK / L1I_TLB",
+ "BriefDescription": "The rate of ITLB Walks to the overall L1I TLB lookups",
+ "MetricGroup": "TLB",
+ "MetricName": "itlb_walk_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "L1I_CACHE_REFILL / INST_RETIRED * 1000",
+ "BriefDescription": "The rate of L1 I-Cache misses per kilo instructions",
+ "MetricGroup": "Cache",
+ "MetricName": "l1i_cache_mpki",
+ "ScaleUnit": "1MPKI"
+ },
+ {
+ "MetricExpr": "L1I_CACHE_REFILL / L1I_CACHE",
+ "BriefDescription": "The rate of L1 I-Cache misses to the overall L1 I-Cache",
+ "MetricGroup": "Cache",
+ "MetricName": "l1i_cache_miss_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "L1D_CACHE_REFILL / INST_RETIRED * 1000",
+ "BriefDescription": "The rate of L1 D-Cache misses per kilo instructions",
+ "MetricGroup": "Cache",
+ "MetricName": "l1d_cache_mpki",
+ "ScaleUnit": "1MPKI"
+ },
+ {
+ "MetricExpr": "L1D_CACHE_REFILL / L1D_CACHE",
+ "BriefDescription": "The rate of L1 D-Cache misses to the overall L1 D-Cache",
+ "MetricGroup": "Cache",
+ "MetricName": "l1d_cache_miss_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "L2D_CACHE_REFILL / INST_RETIRED * 1000",
+ "BriefDescription": "The rate of L2 D-Cache misses per kilo instructions",
+ "MetricGroup": "Cache",
+ "MetricName": "l2d_cache_mpki",
+ "ScaleUnit": "1MPKI"
+ },
+ {
+ "MetricExpr": "L2D_CACHE_REFILL / L2D_CACHE",
+ "BriefDescription": "The rate of L2 D-Cache misses to the overall L2 D-Cache",
+ "MetricGroup": "Cache",
+ "MetricName": "l2d_cache_miss_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "L3D_CACHE_REFILL / INST_RETIRED * 1000",
+ "BriefDescription": "The rate of L3 D-Cache misses per kilo instructions",
+ "MetricGroup": "Cache",
+ "MetricName": "l3d_cache_mpki",
+ "ScaleUnit": "1MPKI"
+ },
+ {
+ "MetricExpr": "L3D_CACHE_REFILL / L3D_CACHE",
+ "BriefDescription": "The rate of L3 D-Cache misses to the overall L3 D-Cache",
+ "MetricGroup": "Cache",
+ "MetricName": "l3d_cache_miss_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "LL_CACHE_MISS_RD / INST_RETIRED * 1000",
+ "BriefDescription": "The rate of LL Cache read misses per kilo instructions",
+ "MetricGroup": "Cache",
+ "MetricName": "ll_cache_read_mpki",
+ "ScaleUnit": "1MPKI"
+ },
+ {
+ "MetricExpr": "LL_CACHE_MISS_RD / LL_CACHE_RD",
+ "BriefDescription": "The rate of LL Cache read misses to the overall LL Cache read",
+ "MetricGroup": "Cache",
+ "MetricName": "ll_cache_read_miss_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "(LL_CACHE_RD - LL_CACHE_MISS_RD) / LL_CACHE_RD",
+ "BriefDescription": "The rate of LL Cache read hit to the overall LL Cache read",
+ "MetricGroup": "Cache",
+ "MetricName": "ll_cache_read_hit_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "BR_MIS_PRED_RETIRED / INST_RETIRED * 1000",
+ "BriefDescription": "The rate of branches mis-predicted per kilo instructions",
+ "MetricGroup": "Branch",
+ "MetricName": "branch_mpki",
+ "ScaleUnit": "1MPKI"
+ },
+ {
+ "MetricExpr": "BR_RETIRED / INST_RETIRED * 1000",
+ "BriefDescription": "The rate of branches retired per kilo instructions",
+ "MetricGroup": "Branch",
+ "MetricName": "branch_pki",
+ "ScaleUnit": "1PKI"
+ },
+ {
+ "MetricExpr": "BR_MIS_PRED_RETIRED / BR_RETIRED",
+ "BriefDescription": "The rate of branches mis-predited to the overall branches",
+ "MetricGroup": "Branch",
+ "MetricName": "branch_miss_pred_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "instructions / CPU_CYCLES",
+ "BriefDescription": "The average number of instructions executed for each cycle.",
+ "MetricGroup": "PEutilization",
+ "MetricName": "ipc"
+ },
+ {
+ "MetricExpr": "ipc / 5",
+ "BriefDescription": "IPC percentage of peak. The peak of IPC is 5.",
+ "MetricGroup": "PEutilization",
+ "MetricName": "ipc_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "INST_RETIRED / CPU_CYCLES",
+ "BriefDescription": "Architecturally executed Instructions Per Cycle (IPC)",
+ "MetricGroup": "PEutilization",
+ "MetricName": "retired_ipc"
+ },
+ {
+ "MetricExpr": "INST_SPEC / CPU_CYCLES",
+ "BriefDescription": "Speculatively executed Instructions Per Cycle (IPC)",
+ "MetricGroup": "PEutilization",
+ "MetricName": "spec_ipc"
+ },
+ {
+ "MetricExpr": "OP_RETIRED / OP_SPEC",
+ "BriefDescription": "Of all the micro-operations issued, what percentage are retired(committed)",
+ "MetricGroup": "PEutilization",
+ "MetricName": "retired_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "1 - OP_RETIRED / OP_SPEC",
+ "BriefDescription": "Of all the micro-operations issued, what percentage are not retired(committed)",
+ "MetricGroup": "PEutilization",
+ "MetricName": "wasted_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "OP_RETIRED / OP_SPEC * (1 - (STALL_SLOT if (#slots - 5) else (STALL_SLOT - CPU_CYCLES)) / (#slots * CPU_CYCLES))",
+ "BriefDescription": "The truly effective ratio of micro-operations executed by the CPU, which means that misprediction and stall are not included",
+ "MetricGroup": "PEutilization",
+ "MetricName": "cpu_utilization",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "LD_SPEC / INST_SPEC",
+ "BriefDescription": "The rate of load instructions speculatively executed to overall instructions speclatively executed",
+ "MetricGroup": "InstructionMix",
+ "MetricName": "load_spec_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "ST_SPEC / INST_SPEC",
+ "BriefDescription": "The rate of store instructions speculatively executed to overall instructions speclatively executed",
+ "MetricGroup": "InstructionMix",
+ "MetricName": "store_spec_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "DP_SPEC / INST_SPEC",
+ "BriefDescription": "The rate of integer data-processing instructions speculatively executed to overall instructions speclatively executed",
+ "MetricGroup": "InstructionMix",
+ "MetricName": "data_process_spec_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "ASE_SPEC / INST_SPEC",
+ "BriefDescription": "The rate of advanced SIMD instructions speculatively executed to overall instructions speclatively executed",
+ "MetricGroup": "InstructionMix",
+ "MetricName": "advanced_simd_spec_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "VFP_SPEC / INST_SPEC",
+ "BriefDescription": "The rate of floating point instructions speculatively executed to overall instructions speclatively executed",
+ "MetricGroup": "InstructionMix",
+ "MetricName": "float_point_spec_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "CRYPTO_SPEC / INST_SPEC",
+ "BriefDescription": "The rate of crypto instructions speculatively executed to overall instructions speclatively executed",
+ "MetricGroup": "InstructionMix",
+ "MetricName": "crypto_spec_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "BR_IMMED_SPEC / INST_SPEC",
+ "BriefDescription": "The rate of branch immediate instructions speculatively executed to overall instructions speclatively executed",
+ "MetricGroup": "InstructionMix",
+ "MetricName": "branch_immed_spec_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "BR_RETURN_SPEC / INST_SPEC",
+ "BriefDescription": "The rate of procedure return instructions speculatively executed to overall instructions speclatively executed",
+ "MetricGroup": "InstructionMix",
+ "MetricName": "branch_return_spec_rate",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "BR_INDIRECT_SPEC / INST_SPEC",
+ "BriefDescription": "The rate of indirect branch instructions speculatively executed to overall instructions speclatively executed",
+ "MetricGroup": "InstructionMix",
+ "MetricName": "branch_indirect_spec_rate",
+ "ScaleUnit": "100%"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/arm64/sbsa.json b/tools/perf/pmu-events/arch/arm64/sbsa.json
new file mode 100644
index 000000000000..f678c37ea9c3
--- /dev/null
+++ b/tools/perf/pmu-events/arch/arm64/sbsa.json
@@ -0,0 +1,30 @@
+[
+ {
+ "MetricExpr": "stall_slot_frontend / (#slots * cpu_cycles)",
+ "BriefDescription": "Frontend bound L1 topdown metric",
+ "MetricGroup": "TopdownL1",
+ "MetricName": "frontend_bound",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "(1 - op_retired / op_spec) * (1 - stall_slot / (#slots * cpu_cycles))",
+ "BriefDescription": "Bad speculation L1 topdown metric",
+ "MetricGroup": "TopdownL1",
+ "MetricName": "bad_speculation",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "(op_retired / op_spec) * (1 - stall_slot / (#slots * cpu_cycles))",
+ "BriefDescription": "Retiring L1 topdown metric",
+ "MetricGroup": "TopdownL1",
+ "MetricName": "retiring",
+ "ScaleUnit": "100%"
+ },
+ {
+ "MetricExpr": "stall_slot_backend / (#slots * cpu_cycles)",
+ "BriefDescription": "Backend Bound L1 topdown metric",
+ "MetricGroup": "TopdownL1",
+ "MetricName": "backend_bound",
+ "ScaleUnit": "100%"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/powerpc/power10/metrics.json b/tools/perf/pmu-events/arch/powerpc/power10/metrics.json
index b57526fa44f2..6f53583a0c62 100644
--- a/tools/perf/pmu-events/arch/powerpc/power10/metrics.json
+++ b/tools/perf/pmu-events/arch/powerpc/power10/metrics.json
@@ -15,7 +15,7 @@
{
"BriefDescription": "Average cycles per completed instruction when dispatch was stalled for any reason",
"MetricExpr": "PM_DISP_STALL_CYC / PM_RUN_INST_CMPL",
- "MetricGroup": "CPI",
+ "MetricGroup": "CPI;CPI_STALL_RATIO",
"MetricName": "DISPATCHED_CPI"
},
{
@@ -147,13 +147,13 @@
{
"BriefDescription": "Average cycles per completed instruction when the NTC instruction has been dispatched but not issued for any reason",
"MetricExpr": "PM_ISSUE_STALL / PM_RUN_INST_CMPL",
- "MetricGroup": "CPI",
+ "MetricGroup": "CPI;CPI_STALL_RATIO",
"MetricName": "ISSUE_STALL_CPI"
},
{
"BriefDescription": "Average cycles per completed instruction when the NTC instruction is waiting to be finished in one of the execution units",
"MetricExpr": "PM_EXEC_STALL / PM_RUN_INST_CMPL",
- "MetricGroup": "CPI",
+ "MetricGroup": "CPI;CPI_STALL_RATIO",
"MetricName": "EXECUTION_STALL_CPI"
},
{
@@ -309,7 +309,7 @@
{
"BriefDescription": "Average cycles per completed instruction when the NTC instruction cannot complete because the thread was blocked",
"MetricExpr": "PM_CMPL_STALL / PM_RUN_INST_CMPL",
- "MetricGroup": "CPI",
+ "MetricGroup": "CPI;CPI_STALL_RATIO",
"MetricName": "COMPLETION_STALL_CPI"
},
{
diff --git a/tools/perf/pmu-events/arch/powerpc/power10/others.json b/tools/perf/pmu-events/arch/powerpc/power10/others.json
index 7d0de1a2860b..a771e4b6bec5 100644
--- a/tools/perf/pmu-events/arch/powerpc/power10/others.json
+++ b/tools/perf/pmu-events/arch/powerpc/power10/others.json
@@ -265,7 +265,7 @@
"BriefDescription": "Load Missed L1, counted at finish time."
},
{
- "EventCode": "0x400FA",
+ "EventCode": "0x500FA",
"EventName": "PM_RUN_INST_CMPL",
"BriefDescription": "Completed PowerPC instructions gated by the run latch."
}
diff --git a/tools/perf/pmu-events/arch/x86/mapfile.csv b/tools/perf/pmu-events/arch/x86/mapfile.csv
index 711a4ef05fdf..5facdac6fe8e 100644
--- a/tools/perf/pmu-events/arch/x86/mapfile.csv
+++ b/tools/perf/pmu-events/arch/x86/mapfile.csv
@@ -21,7 +21,7 @@ GenuineIntel-6-A[AC],v1.00,meteorlake,core
GenuineIntel-6-1[AEF],v3,nehalemep,core
GenuineIntel-6-2E,v3,nehalemex,core
GenuineIntel-6-2A,v17,sandybridge,core
-GenuineIntel-6-8F,v1.09,sapphirerapids,core
+GenuineIntel-6-(8F|CF),v1.09,sapphirerapids,core
GenuineIntel-6-(37|4A|4C|4D|5A),v14,silvermont,core
GenuineIntel-6-(4E|5E|8E|9E|A5|A6),v53,skylake,core
GenuineIntel-6-55-[01234],v1.28,skylakex,core
diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c
index 480e8f0d30c8..a938b74cf487 100644
--- a/tools/perf/pmu-events/empty-pmu-events.c
+++ b/tools/perf/pmu-events/empty-pmu-events.c
@@ -11,7 +11,7 @@
#include <string.h>
#include <stddef.h>
-static const struct pmu_event pme_test_soc_cpu[] = {
+static const struct pmu_event pmu_events__test_soc_cpu[] = {
{
.name = "l3_cache_rd",
.event = "event=0x40",
@@ -106,6 +106,14 @@ static const struct pmu_event pme_test_soc_cpu[] = {
.topic = "branch",
},
{
+ .name = 0,
+ .event = 0,
+ .desc = 0,
+ },
+};
+
+static const struct pmu_metric pmu_metrics__test_soc_cpu[] = {
+ {
.metric_expr = "1 / IPC",
.metric_name = "CPI",
},
@@ -170,9 +178,8 @@ static const struct pmu_event pme_test_soc_cpu[] = {
.metric_name = "L1D_Cache_Fill_BW",
},
{
- .name = 0,
- .event = 0,
- .desc = 0,
+ .metric_expr = 0,
+ .metric_name = 0,
},
};
@@ -181,6 +188,11 @@ struct pmu_events_table {
const struct pmu_event *entries;
};
+/* Struct used to make the PMU metric table implementation opaque to callers. */
+struct pmu_metrics_table {
+ const struct pmu_metric *entries;
+};
+
/*
* Map a CPU to its table of PMU events. The CPU is identified by the
* cpuid field, which is an arch-specific identifier for the CPU.
@@ -192,7 +204,8 @@ struct pmu_events_table {
struct pmu_events_map {
const char *arch;
const char *cpuid;
- const struct pmu_events_table table;
+ const struct pmu_events_table event_table;
+ const struct pmu_metrics_table metric_table;
};
/*
@@ -203,12 +216,14 @@ static const struct pmu_events_map pmu_events_map[] = {
{
.arch = "testarch",
.cpuid = "testcpu",
- .table = { pme_test_soc_cpu },
+ .event_table = { pmu_events__test_soc_cpu },
+ .metric_table = { pmu_metrics__test_soc_cpu },
},
{
.arch = 0,
.cpuid = 0,
- .table = { 0 },
+ .event_table = { 0 },
+ .metric_table = { 0 },
},
};
@@ -254,9 +269,7 @@ static const struct pmu_sys_events pmu_sys_event_tables[] = {
int pmu_events_table_for_each_event(const struct pmu_events_table *table, pmu_event_iter_fn fn,
void *data)
{
- for (const struct pmu_event *pe = &table->entries[0];
- pe->name || pe->metric_group || pe->metric_name;
- pe++) {
+ for (const struct pmu_event *pe = &table->entries[0]; pe->name; pe++) {
int ret = fn(pe, table, data);
if (ret)
@@ -265,7 +278,19 @@ int pmu_events_table_for_each_event(const struct pmu_events_table *table, pmu_ev
return 0;
}
-const struct pmu_events_table *perf_pmu__find_table(struct perf_pmu *pmu)
+int pmu_metrics_table_for_each_metric(const struct pmu_metrics_table *table, pmu_metric_iter_fn fn,
+ void *data)
+{
+ for (const struct pmu_metric *pm = &table->entries[0]; pm->metric_expr; pm++) {
+ int ret = fn(pm, table, data);
+
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+const struct pmu_events_table *perf_pmu__find_events_table(struct perf_pmu *pmu)
{
const struct pmu_events_table *table = NULL;
char *cpuid = perf_pmu__getcpuid(pmu);
@@ -285,7 +310,35 @@ const struct pmu_events_table *perf_pmu__find_table(struct perf_pmu *pmu)
break;
if (!strcmp_cpuid_str(map->cpuid, cpuid)) {
- table = &map->table;
+ table = &map->event_table;
+ break;
+ }
+ }
+ free(cpuid);
+ return table;
+}
+
+const struct pmu_metrics_table *perf_pmu__find_metrics_table(struct perf_pmu *pmu)
+{
+ const struct pmu_metrics_table *table = NULL;
+ char *cpuid = perf_pmu__getcpuid(pmu);
+ int i;
+
+ /* on some platforms which uses cpus map, cpuid can be NULL for
+ * PMUs other than CORE PMUs.
+ */
+ if (!cpuid)
+ return NULL;
+
+ i = 0;
+ for (;;) {
+ const struct pmu_events_map *map = &pmu_events_map[i++];
+
+ if (!map->cpuid)
+ break;
+
+ if (!strcmp_cpuid_str(map->cpuid, cpuid)) {
+ table = &map->metric_table;
break;
}
}
@@ -299,17 +352,39 @@ const struct pmu_events_table *find_core_events_table(const char *arch, const ch
tables->arch;
tables++) {
if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid))
- return &tables->table;
+ return &tables->event_table;
+ }
+ return NULL;
+}
+
+const struct pmu_metrics_table *find_core_metrics_table(const char *arch, const char *cpuid)
+{
+ for (const struct pmu_events_map *tables = &pmu_events_map[0];
+ tables->arch;
+ tables++) {
+ if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid))
+ return &tables->metric_table;
}
return NULL;
}
int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data)
{
+ for (const struct pmu_events_map *tables = &pmu_events_map[0]; tables->arch; tables++) {
+ int ret = pmu_events_table_for_each_event(&tables->event_table, fn, data);
+
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+int pmu_for_each_core_metric(pmu_metric_iter_fn fn, void *data)
+{
for (const struct pmu_events_map *tables = &pmu_events_map[0];
tables->arch;
tables++) {
- int ret = pmu_events_table_for_each_event(&tables->table, fn, data);
+ int ret = pmu_metrics_table_for_each_metric(&tables->metric_table, fn, data);
if (ret)
return ret;
@@ -340,3 +415,8 @@ int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data)
}
return 0;
}
+
+int pmu_for_each_sys_metric(pmu_metric_iter_fn fn __maybe_unused, void *data __maybe_unused)
+{
+ return 0;
+}
diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index 4c398e0eeb2f..2bcd07ce609f 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -3,6 +3,7 @@
"""Convert directories of JSON events to C code."""
import argparse
import csv
+from functools import lru_cache
import json
import metric
import os
@@ -12,30 +13,47 @@ import collections
# Global command line arguments.
_args = None
+# List of regular event tables.
+_event_tables = []
# List of event tables generated from "/sys" directories.
_sys_event_tables = []
+# List of regular metric tables.
+_metric_tables = []
+# List of metric tables generated from "/sys" directories.
+_sys_metric_tables = []
+# Mapping between sys event table names and sys metric table names.
+_sys_event_table_to_metric_table_mapping = {}
# Map from an event name to an architecture standard
# JsonEvent. Architecture standard events are in json files in the top
# f'{_args.starting_dir}/{_args.arch}' directory.
_arch_std_events = {}
-# Track whether an events table is currently being defined and needs closing.
-_close_table = False
# Events to write out when the table is closed
_pending_events = []
+# Name of events table to be written out
+_pending_events_tblname = None
+# Metrics to write out when the table is closed
+_pending_metrics = []
+# Name of metrics table to be written out
+_pending_metrics_tblname = None
# Global BigCString shared by all structures.
_bcs = None
# Order specific JsonEvent attributes will be visited.
_json_event_attributes = [
# cmp_sevent related attributes.
- 'name', 'pmu', 'topic', 'desc', 'metric_name', 'metric_group',
+ 'name', 'pmu', 'topic', 'desc',
# Seems useful, put it early.
'event',
# Short things in alphabetical order.
'aggr_mode', 'compat', 'deprecated', 'perpkg', 'unit',
# Longer things (the last won't be iterated over during decompress).
- 'metric_constraint', 'metric_expr', 'long_desc'
+ 'long_desc'
]
+# Attributes that are in pmu_metric rather than pmu_event.
+_json_metric_attributes = [
+ 'metric_name', 'metric_group', 'metric_constraint', 'metric_expr', 'desc',
+ 'long_desc', 'unit', 'compat', 'aggr_mode'
+]
def removesuffix(s: str, suffix: str) -> str:
"""Remove the suffix from a string
@@ -46,14 +64,16 @@ def removesuffix(s: str, suffix: str) -> str:
return s[0:-len(suffix)] if s.endswith(suffix) else s
-def file_name_to_table_name(parents: Sequence[str], dirname: str) -> str:
+def file_name_to_table_name(prefix: str, parents: Sequence[str],
+ dirname: str) -> str:
"""Generate a C table name from directory names."""
- tblname = 'pme'
+ tblname = prefix
for p in parents:
tblname += '_' + p
tblname += '_' + dirname
return tblname.replace('-', '_')
+
def c_len(s: str) -> int:
"""Return the length of s a C string
@@ -271,7 +291,7 @@ class JsonEvent:
self.metric_constraint = jd.get('MetricConstraint')
self.metric_expr = None
if 'MetricExpr' in jd:
- self.metric_expr = metric.ParsePerfJson(jd['MetricExpr']).Simplify()
+ self.metric_expr = metric.ParsePerfJson(jd['MetricExpr']).Simplify()
arch_std = jd.get('ArchStdEvent')
if precise and self.desc and '(Precise Event)' not in self.desc:
@@ -320,35 +340,46 @@ class JsonEvent:
s += f'\t{attr} = {value},\n'
return s + '}'
- def build_c_string(self) -> str:
+ def build_c_string(self, metric: bool) -> str:
s = ''
- for attr in _json_event_attributes:
+ for attr in _json_metric_attributes if metric else _json_event_attributes:
x = getattr(self, attr)
- if x and attr == 'metric_expr':
+ if metric and x and attr == 'metric_expr':
# Convert parsed metric expressions into a string. Slashes
# must be doubled in the file.
x = x.ToPerfJson().replace('\\', '\\\\')
s += f'{x}\\000' if x else '\\000'
return s
- def to_c_string(self) -> str:
+ def to_c_string(self, metric: bool) -> str:
"""Representation of the event as a C struct initializer."""
- s = self.build_c_string()
+ s = self.build_c_string(metric)
return f'{{ { _bcs.offsets[s] } }}, /* {s} */\n'
+@lru_cache(maxsize=None)
def read_json_events(path: str, topic: str) -> Sequence[JsonEvent]:
"""Read json events from the specified file."""
-
try:
- result = json.load(open(path), object_hook=JsonEvent)
+ events = json.load(open(path), object_hook=JsonEvent)
except BaseException as err:
print(f"Exception processing {path}")
raise
- for event in result:
+ metrics: list[Tuple[str, metric.Expression]] = []
+ for event in events:
event.topic = topic
- return result
+ if event.metric_name and '-' not in event.metric_name:
+ metrics.append((event.metric_name, event.metric_expr))
+ updates = metric.RewriteMetricsInTermsOfOthers(metrics)
+ if updates:
+ for event in events:
+ if event.metric_name in updates:
+ # print(f'Updated {event.metric_name} from\n"{event.metric_expr}"\n'
+ # f'to\n"{updates[event.metric_name]}"')
+ event.metric_expr = updates[event.metric_name]
+
+ return events
def preprocess_arch_std_files(archpath: str) -> None:
"""Read in all architecture standard events."""
@@ -358,26 +389,20 @@ def preprocess_arch_std_files(archpath: str) -> None:
for event in read_json_events(item.path, topic=''):
if event.name:
_arch_std_events[event.name.lower()] = event
-
-
-def print_events_table_prefix(tblname: str) -> None:
- """Called when a new events table is started."""
- global _close_table
- if _close_table:
- raise IOError('Printing table prefix but last table has no suffix')
- _args.output_file.write(f'static const struct compact_pmu_event {tblname}[] = {{\n')
- _close_table = True
+ if event.metric_name:
+ _arch_std_events[event.metric_name.lower()] = event
def add_events_table_entries(item: os.DirEntry, topic: str) -> None:
"""Add contents of file to _pending_events table."""
- if not _close_table:
- raise IOError('Table entries missing prefix')
for e in read_json_events(item.path, topic):
- _pending_events.append(e)
+ if e.name:
+ _pending_events.append(e)
+ if e.metric_name:
+ _pending_metrics.append(e)
-def print_events_table_suffix() -> None:
+def print_pending_events() -> None:
"""Optionally close events table."""
def event_cmp_key(j: JsonEvent) -> Tuple[bool, str, str, str, str]:
@@ -389,17 +414,58 @@ def print_events_table_suffix() -> None:
return (j.desc is not None, fix_none(j.topic), fix_none(j.name), fix_none(j.pmu),
fix_none(j.metric_name))
- global _close_table
- if not _close_table:
+ global _pending_events
+ if not _pending_events:
return
- global _pending_events
+ global _pending_events_tblname
+ if _pending_events_tblname.endswith('_sys'):
+ global _sys_event_tables
+ _sys_event_tables.append(_pending_events_tblname)
+ else:
+ global event_tables
+ _event_tables.append(_pending_events_tblname)
+
+ _args.output_file.write(
+ f'static const struct compact_pmu_event {_pending_events_tblname}[] = {{\n')
+
for event in sorted(_pending_events, key=event_cmp_key):
- _args.output_file.write(event.to_c_string())
- _pending_events = []
+ _args.output_file.write(event.to_c_string(metric=False))
+ _pending_events = []
+
+ _args.output_file.write('};\n\n')
+
+def print_pending_metrics() -> None:
+ """Optionally close metrics table."""
+
+ def metric_cmp_key(j: JsonEvent) -> Tuple[bool, str, str]:
+ def fix_none(s: Optional[str]) -> str:
+ if s is None:
+ return ''
+ return s
+
+ return (j.desc is not None, fix_none(j.pmu), fix_none(j.metric_name))
+
+ global _pending_metrics
+ if not _pending_metrics:
+ return
+
+ global _pending_metrics_tblname
+ if _pending_metrics_tblname.endswith('_sys'):
+ global _sys_metric_tables
+ _sys_metric_tables.append(_pending_metrics_tblname)
+ else:
+ global metric_tables
+ _metric_tables.append(_pending_metrics_tblname)
+
+ _args.output_file.write(
+ f'static const struct compact_pmu_event {_pending_metrics_tblname}[] = {{\n')
+
+ for metric in sorted(_pending_metrics, key=metric_cmp_key):
+ _args.output_file.write(metric.to_c_string(metric=True))
+ _pending_metrics = []
_args.output_file.write('};\n\n')
- _close_table = False
def get_topic(topic: str) -> str:
if topic.endswith('metrics.json'):
@@ -423,12 +489,13 @@ def preprocess_one_file(parents: Sequence[str], item: os.DirEntry) -> None:
topic = get_topic(item.name)
for event in read_json_events(item.path, topic):
- _bcs.add(event.build_c_string())
+ if event.name:
+ _bcs.add(event.build_c_string(metric=False))
+ if event.metric_name:
+ _bcs.add(event.build_c_string(metric=True))
def process_one_file(parents: Sequence[str], item: os.DirEntry) -> None:
"""Process a JSON file during the main walk."""
- global _sys_event_tables
-
def is_leaf_dir(path: str) -> bool:
for item in os.scandir(path):
if item.is_dir():
@@ -437,12 +504,16 @@ def process_one_file(parents: Sequence[str], item: os.DirEntry) -> None:
# model directory, reset topic
if item.is_dir() and is_leaf_dir(item.path):
- print_events_table_suffix()
+ print_pending_events()
+ print_pending_metrics()
+
+ global _pending_events_tblname
+ _pending_events_tblname = file_name_to_table_name('pmu_events_', parents, item.name)
+ global _pending_metrics_tblname
+ _pending_metrics_tblname = file_name_to_table_name('pmu_metrics_', parents, item.name)
- tblname = file_name_to_table_name(parents, item.name)
if item.name == 'sys':
- _sys_event_tables.append(tblname)
- print_events_table_prefix(tblname)
+ _sys_event_table_to_metric_table_mapping[_pending_events_tblname] = _pending_metrics_tblname
return
# base dir or too deep
@@ -467,6 +538,12 @@ struct pmu_events_table {
size_t length;
};
+/* Struct used to make the PMU metric table implementation opaque to callers. */
+struct pmu_metrics_table {
+ const struct compact_pmu_event *entries;
+ size_t length;
+};
+
/*
* Map a CPU to its table of PMU events. The CPU is identified by the
* cpuid field, which is an arch-specific identifier for the CPU.
@@ -478,7 +555,8 @@ struct pmu_events_table {
struct pmu_events_map {
const char *arch;
const char *cpuid;
- struct pmu_events_table table;
+ struct pmu_events_table event_table;
+ struct pmu_metrics_table metric_table;
};
/*
@@ -492,9 +570,13 @@ const struct pmu_events_map pmu_events_map[] = {
_args.output_file.write("""{
\t.arch = "testarch",
\t.cpuid = "testcpu",
-\t.table = {
-\t.entries = pme_test_soc_cpu,
-\t.length = ARRAY_SIZE(pme_test_soc_cpu),
+\t.event_table = {
+\t\t.entries = pmu_events__test_soc_cpu,
+\t\t.length = ARRAY_SIZE(pmu_events__test_soc_cpu),
+\t},
+\t.metric_table = {
+\t\t.entries = pmu_metrics__test_soc_cpu,
+\t\t.length = ARRAY_SIZE(pmu_metrics__test_soc_cpu),
\t}
},
""")
@@ -505,14 +587,31 @@ const struct pmu_events_map pmu_events_map[] = {
for row in table:
# Skip the first row or any row beginning with #.
if not first and len(row) > 0 and not row[0].startswith('#'):
- tblname = file_name_to_table_name([], row[2].replace('/', '_'))
+ event_tblname = file_name_to_table_name('pmu_events_', [], row[2].replace('/', '_'))
+ if event_tblname in _event_tables:
+ event_size = f'ARRAY_SIZE({event_tblname})'
+ else:
+ event_tblname = 'NULL'
+ event_size = '0'
+ metric_tblname = file_name_to_table_name('pmu_metrics_', [], row[2].replace('/', '_'))
+ if metric_tblname in _metric_tables:
+ metric_size = f'ARRAY_SIZE({metric_tblname})'
+ else:
+ metric_tblname = 'NULL'
+ metric_size = '0'
+ if event_size == '0' and metric_size == '0':
+ continue
cpuid = row[0].replace('\\', '\\\\')
_args.output_file.write(f"""{{
\t.arch = "{arch}",
\t.cpuid = "{cpuid}",
-\t.table = {{
-\t\t.entries = {tblname},
-\t\t.length = ARRAY_SIZE({tblname})
+\t.event_table = {{
+\t\t.entries = {event_tblname},
+\t\t.length = {event_size}
+\t}},
+\t.metric_table = {{
+\t\t.entries = {metric_tblname},
+\t\t.length = {metric_size}
\t}}
}},
""")
@@ -521,7 +620,8 @@ const struct pmu_events_map pmu_events_map[] = {
_args.output_file.write("""{
\t.arch = 0,
\t.cpuid = 0,
-\t.table = { 0, 0 },
+\t.event_table = { 0, 0 },
+\t.metric_table = { 0, 0 },
}
};
""")
@@ -532,14 +632,36 @@ def print_system_mapping_table() -> None:
_args.output_file.write("""
struct pmu_sys_events {
\tconst char *name;
-\tstruct pmu_events_table table;
+\tstruct pmu_events_table event_table;
+\tstruct pmu_metrics_table metric_table;
};
static const struct pmu_sys_events pmu_sys_event_tables[] = {
""")
+ printed_metric_tables = []
for tblname in _sys_event_tables:
_args.output_file.write(f"""\t{{
-\t\t.table = {{
+\t\t.event_table = {{
+\t\t\t.entries = {tblname},
+\t\t\t.length = ARRAY_SIZE({tblname})
+\t\t}},""")
+ metric_tblname = _sys_event_table_to_metric_table_mapping[tblname]
+ if metric_tblname in _sys_metric_tables:
+ _args.output_file.write(f"""
+\t\t.metric_table = {{
+\t\t\t.entries = {metric_tblname},
+\t\t\t.length = ARRAY_SIZE({metric_tblname})
+\t\t}},""")
+ printed_metric_tables.append(metric_tblname)
+ _args.output_file.write(f"""
+\t\t.name = \"{tblname}\",
+\t}},
+""")
+ for tblname in _sys_metric_tables:
+ if tblname in printed_metric_tables:
+ continue
+ _args.output_file.write(f"""\t{{
+\t\t.metric_table = {{
\t\t\t.entries = {tblname},
\t\t\t.length = ARRAY_SIZE({tblname})
\t\t}},
@@ -547,11 +669,12 @@ static const struct pmu_sys_events pmu_sys_event_tables[] = {
\t}},
""")
_args.output_file.write("""\t{
-\t\t.table = { 0, 0 }
+\t\t.event_table = { 0, 0 },
+\t\t.metric_table = { 0, 0 },
\t},
};
-static void decompress(int offset, struct pmu_event *pe)
+static void decompress_event(int offset, struct pmu_event *pe)
{
\tconst char *p = &big_c_string[offset];
""")
@@ -564,6 +687,19 @@ static void decompress(int offset, struct pmu_event *pe)
_args.output_file.write('\twhile (*p++);')
_args.output_file.write("""}
+static void decompress_metric(int offset, struct pmu_metric *pm)
+{
+\tconst char *p = &big_c_string[offset];
+""")
+ for attr in _json_metric_attributes:
+ _args.output_file.write(f"""
+\tpm->{attr} = (*p == '\\0' ? NULL : p);
+""")
+ if attr == _json_metric_attributes[-1]:
+ continue
+ _args.output_file.write('\twhile (*p++);')
+ _args.output_file.write("""}
+
int pmu_events_table_for_each_event(const struct pmu_events_table *table,
pmu_event_iter_fn fn,
void *data)
@@ -572,7 +708,9 @@ int pmu_events_table_for_each_event(const struct pmu_events_table *table,
struct pmu_event pe;
int ret;
- decompress(table->entries[i].offset, &pe);
+ decompress_event(table->entries[i].offset, &pe);
+ if (!pe.name)
+ continue;
ret = fn(&pe, table, data);
if (ret)
return ret;
@@ -580,7 +718,25 @@ int pmu_events_table_for_each_event(const struct pmu_events_table *table,
return 0;
}
-const struct pmu_events_table *perf_pmu__find_table(struct perf_pmu *pmu)
+int pmu_metrics_table_for_each_metric(const struct pmu_metrics_table *table,
+ pmu_metric_iter_fn fn,
+ void *data)
+{
+ for (size_t i = 0; i < table->length; i++) {
+ struct pmu_metric pm;
+ int ret;
+
+ decompress_metric(table->entries[i].offset, &pm);
+ if (!pm.metric_expr)
+ continue;
+ ret = fn(&pm, table, data);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+const struct pmu_events_table *perf_pmu__find_events_table(struct perf_pmu *pmu)
{
const struct pmu_events_table *table = NULL;
char *cpuid = perf_pmu__getcpuid(pmu);
@@ -599,7 +755,34 @@ const struct pmu_events_table *perf_pmu__find_table(struct perf_pmu *pmu)
break;
if (!strcmp_cpuid_str(map->cpuid, cpuid)) {
- table = &map->table;
+ table = &map->event_table;
+ break;
+ }
+ }
+ free(cpuid);
+ return table;
+}
+
+const struct pmu_metrics_table *perf_pmu__find_metrics_table(struct perf_pmu *pmu)
+{
+ const struct pmu_metrics_table *table = NULL;
+ char *cpuid = perf_pmu__getcpuid(pmu);
+ int i;
+
+ /* on some platforms which uses cpus map, cpuid can be NULL for
+ * PMUs other than CORE PMUs.
+ */
+ if (!cpuid)
+ return NULL;
+
+ i = 0;
+ for (;;) {
+ const struct pmu_events_map *map = &pmu_events_map[i++];
+ if (!map->arch)
+ break;
+
+ if (!strcmp_cpuid_str(map->cpuid, cpuid)) {
+ table = &map->metric_table;
break;
}
}
@@ -613,7 +796,18 @@ const struct pmu_events_table *find_core_events_table(const char *arch, const ch
tables->arch;
tables++) {
if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid))
- return &tables->table;
+ return &tables->event_table;
+ }
+ return NULL;
+}
+
+const struct pmu_metrics_table *find_core_metrics_table(const char *arch, const char *cpuid)
+{
+ for (const struct pmu_events_map *tables = &pmu_events_map[0];
+ tables->arch;
+ tables++) {
+ if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid))
+ return &tables->metric_table;
}
return NULL;
}
@@ -623,7 +817,20 @@ int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data)
for (const struct pmu_events_map *tables = &pmu_events_map[0];
tables->arch;
tables++) {
- int ret = pmu_events_table_for_each_event(&tables->table, fn, data);
+ int ret = pmu_events_table_for_each_event(&tables->event_table, fn, data);
+
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+int pmu_for_each_core_metric(pmu_metric_iter_fn fn, void *data)
+{
+ for (const struct pmu_events_map *tables = &pmu_events_map[0];
+ tables->arch;
+ tables++) {
+ int ret = pmu_metrics_table_for_each_metric(&tables->metric_table, fn, data);
if (ret)
return ret;
@@ -637,7 +844,7 @@ const struct pmu_events_table *find_sys_events_table(const char *name)
tables->name;
tables++) {
if (!strcmp(tables->name, name))
- return &tables->table;
+ return &tables->event_table;
}
return NULL;
}
@@ -647,7 +854,20 @@ int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data)
for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0];
tables->name;
tables++) {
- int ret = pmu_events_table_for_each_event(&tables->table, fn, data);
+ int ret = pmu_events_table_for_each_event(&tables->event_table, fn, data);
+
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+int pmu_for_each_sys_metric(pmu_metric_iter_fn fn, void *data)
+{
+ for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0];
+ tables->name;
+ tables++) {
+ int ret = pmu_metrics_table_for_each_metric(&tables->metric_table, fn, data);
if (ret)
return ret;
@@ -670,12 +890,24 @@ def main() -> None:
action: Callable[[Sequence[str], os.DirEntry], None]) -> None:
"""Replicate the directory/file walking behavior of C's file tree walk."""
for item in os.scandir(path):
+ if _args.model != 'all' and item.is_dir():
+ # Check if the model matches one in _args.model.
+ if len(parents) == _args.model.split(',')[0].count('/'):
+ # We're testing the correct directory.
+ item_path = '/'.join(parents) + ('/' if len(parents) > 0 else '') + item.name
+ if 'test' not in item_path and item_path not in _args.model.split(','):
+ continue
action(parents, item)
if item.is_dir():
ftw(item.path, parents + [item.name], action)
ap = argparse.ArgumentParser()
ap.add_argument('arch', help='Architecture name like x86')
+ ap.add_argument('model', help='''Select a model such as skylake to
+reduce the code size. Normally set to "all". For architectures like
+ARM64 with an implementor/model, the model must include the implementor
+such as "arm/cortex-a34".''',
+ default='all')
ap.add_argument(
'starting_dir',
type=dir_path,
@@ -721,7 +953,8 @@ struct compact_pmu_event {
for arch in archs:
arch_path = f'{_args.starting_dir}/{arch}'
ftw(arch_path, [], process_one_file)
- print_events_table_suffix()
+ print_pending_events()
+ print_pending_metrics()
print_mapping_table(archs)
print_system_mapping_table()
diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py
index 4797ed4fd817..77ea6ff98538 100644
--- a/tools/perf/pmu-events/metric.py
+++ b/tools/perf/pmu-events/metric.py
@@ -4,7 +4,7 @@ import ast
import decimal
import json
import re
-from typing import Dict, List, Optional, Set, Union
+from typing import Dict, List, Optional, Set, Tuple, Union
class Expression:
@@ -26,6 +26,9 @@ class Expression:
"""Returns true when two expressions are the same."""
raise NotImplementedError()
+ def Substitute(self, name: str, expression: 'Expression') -> 'Expression':
+ raise NotImplementedError()
+
def __str__(self) -> str:
return self.ToPerfJson()
@@ -186,6 +189,15 @@ class Operator(Expression):
other.lhs) and self.rhs.Equals(other.rhs)
return False
+ def Substitute(self, name: str, expression: Expression) -> Expression:
+ if self.Equals(expression):
+ return Event(name)
+ lhs = self.lhs.Substitute(name, expression)
+ rhs = None
+ if self.rhs:
+ rhs = self.rhs.Substitute(name, expression)
+ return Operator(self.operator, lhs, rhs)
+
class Select(Expression):
"""Represents a select ternary in the parse tree."""
@@ -225,6 +237,14 @@ class Select(Expression):
other.false_val) and self.true_val.Equals(other.true_val)
return False
+ def Substitute(self, name: str, expression: Expression) -> Expression:
+ if self.Equals(expression):
+ return Event(name)
+ true_val = self.true_val.Substitute(name, expression)
+ cond = self.cond.Substitute(name, expression)
+ false_val = self.false_val.Substitute(name, expression)
+ return Select(true_val, cond, false_val)
+
class Function(Expression):
"""A function in an expression like min, max, d_ratio."""
@@ -261,10 +281,21 @@ class Function(Expression):
def Equals(self, other: Expression) -> bool:
if isinstance(other, Function):
- return self.fn == other.fn and self.lhs.Equals(
- other.lhs) and self.rhs.Equals(other.rhs)
+ result = self.fn == other.fn and self.lhs.Equals(other.lhs)
+ if self.rhs:
+ result = result and self.rhs.Equals(other.rhs)
+ return result
return False
+ def Substitute(self, name: str, expression: Expression) -> Expression:
+ if self.Equals(expression):
+ return Event(name)
+ lhs = self.lhs.Substitute(name, expression)
+ rhs = None
+ if self.rhs:
+ rhs = self.rhs.Substitute(name, expression)
+ return Function(self.fn, lhs, rhs)
+
def _FixEscapes(s: str) -> str:
s = re.sub(r'([^\\]),', r'\1\\,', s)
@@ -291,6 +322,9 @@ class Event(Expression):
def Equals(self, other: Expression) -> bool:
return isinstance(other, Event) and self.name == other.name
+ def Substitute(self, name: str, expression: Expression) -> Expression:
+ return self
+
class Constant(Expression):
"""A constant within the expression tree."""
@@ -315,6 +349,9 @@ class Constant(Expression):
def Equals(self, other: Expression) -> bool:
return isinstance(other, Constant) and self.value == other.value
+ def Substitute(self, name: str, expression: Expression) -> Expression:
+ return self
+
class Literal(Expression):
"""A runtime literal within the expression tree."""
@@ -334,6 +371,9 @@ class Literal(Expression):
def Equals(self, other: Expression) -> bool:
return isinstance(other, Literal) and self.value == other.value
+ def Substitute(self, name: str, expression: Expression) -> Expression:
+ return self
+
def min(lhs: Union[int, float, Expression], rhs: Union[int, float,
Expression]) -> Function:
@@ -459,6 +499,7 @@ class MetricGroup:
class _RewriteIfExpToSelect(ast.NodeTransformer):
+ """Transformer to convert if-else nodes to Select expressions."""
def visit_IfExp(self, node):
# pylint: disable=invalid-name
@@ -496,7 +537,37 @@ def ParsePerfJson(orig: str) -> Expression:
for kw in keywords:
py = re.sub(rf'Event\(r"{kw}"\)', kw, py)
- parsed = ast.parse(py, mode='eval')
+ try:
+ parsed = ast.parse(py, mode='eval')
+ except SyntaxError as e:
+ raise SyntaxError(f'Parsing expression:\n{orig}') from e
_RewriteIfExpToSelect().visit(parsed)
parsed = ast.fix_missing_locations(parsed)
return _Constify(eval(compile(parsed, orig, 'eval')))
+
+
+def RewriteMetricsInTermsOfOthers(metrics: List[Tuple[str, Expression]]
+ )-> Dict[str, Expression]:
+ """Shorten metrics by rewriting in terms of others.
+
+ Args:
+ metrics (list): pairs of metric names and their expressions.
+ Returns:
+ Dict: mapping from a metric name to a shortened expression.
+ """
+ updates: Dict[str, Expression] = dict()
+ for outer_name, outer_expression in metrics:
+ updated = outer_expression
+ while True:
+ for inner_name, inner_expression in metrics:
+ if inner_name.lower() == outer_name.lower():
+ continue
+ if inner_name in updates:
+ inner_expression = updates[inner_name]
+ updated = updated.Substitute(inner_name, inner_expression)
+ if updated.Equals(outer_expression):
+ break
+ if outer_name in updates and updated.Equals(updates[outer_name]):
+ break
+ updates[outer_name] = updated
+ return updates
diff --git a/tools/perf/pmu-events/metric_test.py b/tools/perf/pmu-events/metric_test.py
index 15315d0f716c..40a3c7d8b2bc 100644..100755
--- a/tools/perf/pmu-events/metric_test.py
+++ b/tools/perf/pmu-events/metric_test.py
@@ -1,8 +1,11 @@
+#!/usr/bin/env python3
# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
import unittest
from metric import Constant
from metric import Event
+from metric import Expression
from metric import ParsePerfJson
+from metric import RewriteMetricsInTermsOfOthers
class TestMetricExpressions(unittest.TestCase):
@@ -87,8 +90,8 @@ class TestMetricExpressions(unittest.TestCase):
after = r'min((a + b if c > 1 else c + d), e + f)'
self.assertEqual(ParsePerfJson(before).ToPerfJson(), after)
- before =3D r'a if b else c if d else e'
- after =3D r'(a if b else (c if d else e))'
+ before = r'a if b else c if d else e'
+ after = r'(a if b else (c if d else e))'
self.assertEqual(ParsePerfJson(before).ToPerfJson(), after)
def test_ToPython(self):
@@ -153,5 +156,13 @@ class TestMetricExpressions(unittest.TestCase):
after = '0 * SLOTS'
self.assertEqual(ParsePerfJson(before).Simplify().ToPerfJson(), after)
+ def test_RewriteMetricsInTermsOfOthers(self):
+ Expression.__eq__ = lambda e1, e2: e1.Equals(e2)
+ before = [('m1', ParsePerfJson('a + b + c + d')),
+ ('m2', ParsePerfJson('a + b + c'))]
+ after = {'m1': ParsePerfJson('m2 + d')}
+ self.assertEqual(RewriteMetricsInTermsOfOthers(before), after)
+ Expression.__eq__ = None
+
if __name__ == '__main__':
unittest.main()
diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h
index fe343c4d8016..b7d4a66b8ad2 100644
--- a/tools/perf/pmu-events/pmu-events.h
+++ b/tools/perf/pmu-events/pmu-events.h
@@ -23,27 +23,47 @@ struct pmu_event {
const char *unit;
const char *perpkg;
const char *aggr_mode;
- const char *metric_expr;
+ const char *deprecated;
+};
+
+struct pmu_metric {
const char *metric_name;
const char *metric_group;
- const char *deprecated;
+ const char *metric_expr;
+ const char *unit;
+ const char *compat;
+ const char *aggr_mode;
const char *metric_constraint;
+ const char *desc;
+ const char *long_desc;
};
struct pmu_events_table;
+struct pmu_metrics_table;
typedef int (*pmu_event_iter_fn)(const struct pmu_event *pe,
const struct pmu_events_table *table,
void *data);
+typedef int (*pmu_metric_iter_fn)(const struct pmu_metric *pm,
+ const struct pmu_metrics_table *table,
+ void *data);
+
int pmu_events_table_for_each_event(const struct pmu_events_table *table, pmu_event_iter_fn fn,
void *data);
+int pmu_metrics_table_for_each_metric(const struct pmu_metrics_table *table, pmu_metric_iter_fn fn,
+ void *data);
-const struct pmu_events_table *perf_pmu__find_table(struct perf_pmu *pmu);
+const struct pmu_events_table *perf_pmu__find_events_table(struct perf_pmu *pmu);
+const struct pmu_metrics_table *perf_pmu__find_metrics_table(struct perf_pmu *pmu);
const struct pmu_events_table *find_core_events_table(const char *arch, const char *cpuid);
+const struct pmu_metrics_table *find_core_metrics_table(const char *arch, const char *cpuid);
int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data);
+int pmu_for_each_core_metric(pmu_metric_iter_fn fn, void *data);
const struct pmu_events_table *find_sys_events_table(const char *name);
+const struct pmu_metrics_table *find_sys_metrics_table(const char *name);
int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data);
+int pmu_for_each_sys_metric(pmu_metric_iter_fn fn, void *data);
#endif
diff --git a/tools/perf/scripts/python/flamegraph.py b/tools/perf/scripts/python/flamegraph.py
index b6af1dd5f816..cf7ce8229a6c 100755
--- a/tools/perf/scripts/python/flamegraph.py
+++ b/tools/perf/scripts/python/flamegraph.py
@@ -19,12 +19,34 @@
# pylint: disable=missing-function-docstring
from __future__ import print_function
-import sys
-import os
-import io
import argparse
+import hashlib
+import io
import json
+import os
import subprocess
+import sys
+import urllib.request
+
+minimal_html = """<head>
+ <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/d3-flamegraph.css">
+</head>
+<body>
+ <div id="chart"></div>
+ <script type="text/javascript" src="https://d3js.org/d3.v7.js"></script>
+ <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/d3-flamegraph.min.js"></script>
+ <script type="text/javascript">
+ const stacks = [/** @flamegraph_json **/];
+ // Note, options is unused.
+ const options = [/** @options_json **/];
+
+ var chart = flamegraph();
+ d3.select("#chart")
+ .datum(stacks[0])
+ .call(chart);
+ </script>
+</body>
+"""
# pylint: disable=too-few-public-methods
class Node:
@@ -50,16 +72,6 @@ class FlameGraphCLI:
self.args = args
self.stack = Node("all", "root")
- if self.args.format == "html" and \
- not os.path.isfile(self.args.template):
- print("Flame Graph template {} does not exist. Please install "
- "the js-d3-flame-graph (RPM) or libjs-d3-flame-graph (deb) "
- "package, specify an existing flame graph template "
- "(--template PATH) or another output format "
- "(--format FORMAT).".format(self.args.template),
- file=sys.stderr)
- sys.exit(1)
-
@staticmethod
def get_libtype_from_dso(dso):
"""
@@ -128,16 +140,63 @@ class FlameGraphCLI:
}
options_json = json.dumps(options)
+ template_md5sum = None
+ if self.args.format == "html":
+ if os.path.isfile(self.args.template):
+ template = f"file://{self.args.template}"
+ else:
+ if not self.args.allow_download:
+ print(f"""Warning: Flame Graph template '{self.args.template}'
+does not exist. To avoid this please install a package such as the
+js-d3-flame-graph or libjs-d3-flame-graph, specify an existing flame
+graph template (--template PATH) or use another output format (--format
+FORMAT).""",
+ file=sys.stderr)
+ if self.args.input == "-":
+ print("""Not attempting to download Flame Graph template as script command line
+input is disabled due to using live mode. If you want to download the
+template retry without live mode. For example, use 'perf record -a -g
+-F 99 sleep 60' and 'perf script report flamegraph'. Alternatively,
+download the template from:
+https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/templates/d3-flamegraph-base.html
+and place it at:
+/usr/share/d3-flame-graph/d3-flamegraph-base.html""",
+ file=sys.stderr)
+ quit()
+ s = None
+ while s != "y" and s != "n":
+ s = input("Do you wish to download a template from cdn.jsdelivr.net? (this warning can be suppressed with --allow-download) [yn] ").lower()
+ if s == "n":
+ quit()
+ template = "https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/templates/d3-flamegraph-base.html"
+ template_md5sum = "143e0d06ba69b8370b9848dcd6ae3f36"
+
try:
- with io.open(self.args.template, encoding="utf-8") as template:
- output_str = (
- template.read()
- .replace("/** @options_json **/", options_json)
- .replace("/** @flamegraph_json **/", stacks_json)
- )
- except IOError as err:
- print("Error reading template file: {}".format(err), file=sys.stderr)
- sys.exit(1)
+ with urllib.request.urlopen(template) as template:
+ output_str = "".join([
+ l.decode("utf-8") for l in template.readlines()
+ ])
+ except Exception as err:
+ print(f"Error reading template {template}: {err}\n"
+ "a minimal flame graph will be generated", file=sys.stderr)
+ output_str = minimal_html
+ template_md5sum = None
+
+ if template_md5sum:
+ download_md5sum = hashlib.md5(output_str.encode("utf-8")).hexdigest()
+ if download_md5sum != template_md5sum:
+ s = None
+ while s != "y" and s != "n":
+ s = input(f"""Unexpected template md5sum.
+{download_md5sum} != {template_md5sum}, for:
+{output_str}
+continue?[yn] """).lower()
+ if s == "n":
+ quit()
+
+ output_str = output_str.replace("/** @options_json **/", options_json)
+ output_str = output_str.replace("/** @flamegraph_json **/", stacks_json)
+
output_fn = self.args.output or "flamegraph.html"
else:
output_str = stacks_json
@@ -172,6 +231,10 @@ if __name__ == "__main__":
choices=["blue-green", "orange"])
parser.add_argument("-i", "--input",
help=argparse.SUPPRESS)
+ parser.add_argument("--allow-download",
+ default=False,
+ action="store_true",
+ help="allow unprompted downloading of HTML template")
cli_args = parser.parse_args()
cli = FlameGraphCLI(cli_args)
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index 90fd1eb317bb..fb9ac5dc4079 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -68,6 +68,7 @@ perf-y += perf-time-to-tsc.o
perf-y += dlfilter-test.o
perf-y += sigtrap.o
perf-y += event_groups.o
+perf-y += symbols.o
$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build
$(call rule_mkdir)
diff --git a/tools/perf/tests/bpf-script-example.c b/tools/perf/tests/bpf-script-example.c
index 7981c69ed1b4..b638cc99d5ae 100644
--- a/tools/perf/tests/bpf-script-example.c
+++ b/tools/perf/tests/bpf-script-example.c
@@ -43,7 +43,7 @@ struct {
__type(value, int);
} flip_table SEC(".maps");
-SEC("func=do_epoll_wait")
+SEC("syscalls:sys_enter_epoll_pwait")
int bpf_func__SyS_epoll_pwait(void *ctx)
{
int ind =0;
diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
index 17c023823713..ae9223f27cfb 100644
--- a/tools/perf/tests/bpf.c
+++ b/tools/perf/tests/bpf.c
@@ -23,7 +23,7 @@
#define NR_ITERS 111
#define PERF_TEST_BPF_PATH "/sys/fs/bpf/perf_test"
-#ifdef HAVE_LIBBPF_SUPPORT
+#if defined(HAVE_LIBBPF_SUPPORT) && defined(HAVE_LIBTRACEEVENT)
#include <linux/bpf.h>
#include <bpf/bpf.h>
@@ -126,6 +126,10 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
err = parse_events_load_bpf_obj(&parse_state, &parse_state.list, obj, NULL);
parse_events_error__exit(&parse_error);
+ if (err == -ENODATA) {
+ pr_debug("Failed to add events selected by BPF, debuginfo package not installed\n");
+ return TEST_SKIP;
+ }
if (err || list_empty(&parse_state.list)) {
pr_debug("Failed to add events selected by BPF\n");
return TEST_FAIL;
@@ -330,10 +334,10 @@ static int test__bpf(int i)
static int test__basic_bpf_test(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
-#ifdef HAVE_LIBBPF_SUPPORT
+#if defined(HAVE_LIBBPF_SUPPORT) && defined(HAVE_LIBTRACEEVENT)
return test__bpf(0);
#else
- pr_debug("Skip BPF test because BPF support is not compiled\n");
+ pr_debug("Skip BPF test because BPF or libtraceevent support is not compiled\n");
return TEST_SKIP;
#endif
}
@@ -341,10 +345,10 @@ static int test__basic_bpf_test(struct test_suite *test __maybe_unused,
static int test__bpf_pinning(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
-#ifdef HAVE_LIBBPF_SUPPORT
+#if defined(HAVE_LIBBPF_SUPPORT) && defined(HAVE_LIBTRACEEVENT)
return test__bpf(1);
#else
- pr_debug("Skip BPF test because BPF support is not compiled\n");
+ pr_debug("Skip BPF test because BPF or libtraceevent support is not compiled\n");
return TEST_SKIP;
#endif
}
@@ -352,30 +356,30 @@ static int test__bpf_pinning(struct test_suite *test __maybe_unused,
static int test__bpf_prologue_test(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
-#if defined(HAVE_LIBBPF_SUPPORT) && defined(HAVE_BPF_PROLOGUE)
+#if defined(HAVE_LIBBPF_SUPPORT) && defined(HAVE_BPF_PROLOGUE) && defined(HAVE_LIBTRACEEVENT)
return test__bpf(2);
#else
- pr_debug("Skip BPF test because BPF support is not compiled\n");
+ pr_debug("Skip BPF test because BPF or libtraceevent support is not compiled\n");
return TEST_SKIP;
#endif
}
static struct test_case bpf_tests[] = {
-#ifdef HAVE_LIBBPF_SUPPORT
+#if defined(HAVE_LIBBPF_SUPPORT) && defined(HAVE_LIBTRACEEVENT)
TEST_CASE("Basic BPF filtering", basic_bpf_test),
TEST_CASE_REASON("BPF pinning", bpf_pinning,
"clang isn't installed or environment missing BPF support"),
#ifdef HAVE_BPF_PROLOGUE
TEST_CASE_REASON("BPF prologue generation", bpf_prologue_test,
- "clang isn't installed or environment missing BPF support"),
+ "clang/debuginfo isn't installed or environment missing BPF support"),
#else
TEST_CASE_REASON("BPF prologue generation", bpf_prologue_test, "not compiled in"),
#endif
#else
- TEST_CASE_REASON("Basic BPF filtering", basic_bpf_test, "not compiled in"),
- TEST_CASE_REASON("BPF pinning", bpf_pinning, "not compiled in"),
- TEST_CASE_REASON("BPF prologue generation", bpf_prologue_test, "not compiled in"),
+ TEST_CASE_REASON("Basic BPF filtering", basic_bpf_test, "not compiled in or missing libtraceevent support"),
+ TEST_CASE_REASON("BPF pinning", bpf_pinning, "not compiled in or missing libtraceevent support"),
+ TEST_CASE_REASON("BPF prologue generation", bpf_prologue_test, "not compiled in or missing libtraceevent support"),
#endif
{ .name = NULL, }
};
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index cfa61493c750..35cc3807cc9e 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -31,6 +31,7 @@
#include "builtin-test-list.h"
static bool dont_fork;
+const char *dso_to_test;
struct test_suite *__weak arch_tests[] = {
NULL,
@@ -117,6 +118,7 @@ static struct test_suite *generic_tests[] = {
&suite__dlfilter,
&suite__sigtrap,
&suite__event_groups,
+ &suite__symbols,
NULL,
};
@@ -521,6 +523,7 @@ int cmd_test(int argc, const char **argv)
OPT_BOOLEAN('F', "dont-fork", &dont_fork,
"Do not fork for testcase"),
OPT_STRING('w', "workload", &workload, "work", "workload to run for testing"),
+ OPT_STRING(0, "dso", &dso_to_test, "dso", "dso to test"),
OPT_END()
};
const char * const test_subcommands[] = { "list", NULL };
diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c
index afdca7f2959f..ee983b677a6a 100644
--- a/tools/perf/tests/dwarf-unwind.c
+++ b/tools/perf/tests/dwarf-unwind.c
@@ -67,6 +67,7 @@ int test_dwarf_unwind__compare(void *p1, void *p2);
int test_dwarf_unwind__krava_3(struct thread *thread);
int test_dwarf_unwind__krava_2(struct thread *thread);
int test_dwarf_unwind__krava_1(struct thread *thread);
+int test__dwarf_unwind(struct test_suite *test, int subtest);
#define MAX_STACK 8
@@ -195,8 +196,8 @@ NO_TAIL_CALL_ATTRIBUTE noinline int test_dwarf_unwind__krava_1(struct thread *th
return ret;
}
-static int test__dwarf_unwind(struct test_suite *test __maybe_unused,
- int subtest __maybe_unused)
+noinline int test__dwarf_unwind(struct test_suite *test __maybe_unused,
+ int subtest __maybe_unused)
{
struct machine *machine;
struct thread *thread;
diff --git a/tools/perf/tests/expand-cgroup.c b/tools/perf/tests/expand-cgroup.c
index 51fb5f34c1dd..672a27f37060 100644
--- a/tools/perf/tests/expand-cgroup.c
+++ b/tools/perf/tests/expand-cgroup.c
@@ -180,13 +180,13 @@ static int expand_metric_events(void)
struct evlist *evlist;
struct rblist metric_events;
const char metric_str[] = "CPI";
- const struct pmu_events_table *pme_test;
+ const struct pmu_metrics_table *pme_test;
evlist = evlist__new();
TEST_ASSERT_VAL("failed to get evlist", evlist);
rblist__init(&metric_events);
- pme_test = find_core_events_table("testarch", "testcpu");
+ pme_test = find_core_metrics_table("testarch", "testcpu");
ret = metricgroup__parse_groups_test(evlist, pme_test, metric_str,
false, false, &metric_events);
if (ret < 0) {
diff --git a/tools/perf/tests/parse-metric.c b/tools/perf/tests/parse-metric.c
index 21b7ac00d798..9fec6040950c 100644
--- a/tools/perf/tests/parse-metric.c
+++ b/tools/perf/tests/parse-metric.c
@@ -72,7 +72,7 @@ static int __compute_metric(const char *name, struct value *vals,
struct rblist metric_events = {
.nr_entries = 0,
};
- const struct pmu_events_table *pme_test;
+ const struct pmu_metrics_table *pme_test;
struct perf_cpu_map *cpus;
struct runtime_stat st;
struct evlist *evlist;
@@ -96,7 +96,7 @@ static int __compute_metric(const char *name, struct value *vals,
runtime_stat__init(&st);
/* Parse the metric into metric_events list. */
- pme_test = find_core_events_table("testarch", "testcpu");
+ pme_test = find_core_metrics_table("testarch", "testcpu");
err = metricgroup__parse_groups_test(evlist, pme_test, name,
false, false,
&metric_events);
diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index a9f2330f6257..accf44b3d968 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -337,36 +337,12 @@ static int compare_pmu_events(const struct pmu_event *e1, const struct pmu_event
return -1;
}
- if (!is_same(e1->metric_expr, e2->metric_expr)) {
- pr_debug2("testing event e1 %s: mismatched metric_expr, %s vs %s\n",
- e1->name, e1->metric_expr, e2->metric_expr);
- return -1;
- }
-
- if (!is_same(e1->metric_name, e2->metric_name)) {
- pr_debug2("testing event e1 %s: mismatched metric_name, %s vs %s\n",
- e1->name, e1->metric_name, e2->metric_name);
- return -1;
- }
-
- if (!is_same(e1->metric_group, e2->metric_group)) {
- pr_debug2("testing event e1 %s: mismatched metric_group, %s vs %s\n",
- e1->name, e1->metric_group, e2->metric_group);
- return -1;
- }
-
if (!is_same(e1->deprecated, e2->deprecated)) {
pr_debug2("testing event e1 %s: mismatched deprecated, %s vs %s\n",
e1->name, e1->deprecated, e2->deprecated);
return -1;
}
- if (!is_same(e1->metric_constraint, e2->metric_constraint)) {
- pr_debug2("testing event e1 %s: mismatched metric_constant, %s vs %s\n",
- e1->name, e1->metric_constraint, e2->metric_constraint);
- return -1;
- }
-
return 0;
}
@@ -432,9 +408,6 @@ static int test__pmu_event_table_core_callback(const struct pmu_event *pe,
struct perf_pmu_test_event const **test_event_table;
bool found = false;
- if (!pe->name)
- return 0;
-
if (pe->pmu)
test_event_table = &uncore_events[0];
else
@@ -496,7 +469,8 @@ static int test__pmu_event_table_sys_callback(const struct pmu_event *pe,
static int test__pmu_event_table(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
- const struct pmu_events_table *sys_event_table = find_sys_events_table("pme_test_soc_sys");
+ const struct pmu_events_table *sys_event_table =
+ find_sys_events_table("pmu_events__test_soc_sys");
const struct pmu_events_table *table = find_core_events_table("testarch", "testcpu");
int map_events = 0, expected_events, err;
@@ -840,7 +814,8 @@ struct metric {
struct metric_ref metric_ref;
};
-static int test__parsing_callback(const struct pmu_event *pe, const struct pmu_events_table *table,
+static int test__parsing_callback(const struct pmu_metric *pm,
+ const struct pmu_metrics_table *table,
void *data)
{
int *failures = data;
@@ -854,10 +829,10 @@ static int test__parsing_callback(const struct pmu_event *pe, const struct pmu_e
};
int err = 0;
- if (!pe->metric_expr)
+ if (!pm->metric_expr)
return 0;
- pr_debug("Found metric '%s'\n", pe->metric_name);
+ pr_debug("Found metric '%s'\n", pm->metric_name);
(*failures)++;
/*
@@ -877,14 +852,14 @@ static int test__parsing_callback(const struct pmu_event *pe, const struct pmu_e
perf_evlist__set_maps(&evlist->core, cpus, NULL);
runtime_stat__init(&st);
- err = metricgroup__parse_groups_test(evlist, table, pe->metric_name,
+ err = metricgroup__parse_groups_test(evlist, table, pm->metric_name,
false, false,
&metric_events);
if (err) {
- if (!strcmp(pe->metric_name, "M1") || !strcmp(pe->metric_name, "M2") ||
- !strcmp(pe->metric_name, "M3")) {
+ if (!strcmp(pm->metric_name, "M1") || !strcmp(pm->metric_name, "M2") ||
+ !strcmp(pm->metric_name, "M3")) {
(*failures)--;
- pr_debug("Expected broken metric %s skipping\n", pe->metric_name);
+ pr_debug("Expected broken metric %s skipping\n", pm->metric_name);
err = 0;
}
goto out_err;
@@ -912,7 +887,7 @@ static int test__parsing_callback(const struct pmu_event *pe, const struct pmu_e
struct metric_expr *mexp;
list_for_each_entry (mexp, &me->head, nd) {
- if (strcmp(mexp->metric_name, pe->metric_name))
+ if (strcmp(mexp->metric_name, pm->metric_name))
continue;
pr_debug("Result %f\n", test_generic_metric(mexp, 0, &st));
err = 0;
@@ -921,11 +896,11 @@ static int test__parsing_callback(const struct pmu_event *pe, const struct pmu_e
}
}
}
- pr_debug("Didn't find parsed metric %s", pe->metric_name);
+ pr_debug("Didn't find parsed metric %s", pm->metric_name);
err = 1;
out_err:
if (err)
- pr_debug("Broken metric %s\n", pe->metric_name);
+ pr_debug("Broken metric %s\n", pm->metric_name);
/* ... cleanup. */
metricgroup__rblist_exit(&metric_events);
@@ -941,8 +916,8 @@ static int test__parsing(struct test_suite *test __maybe_unused,
{
int failures = 0;
- pmu_for_each_core_event(test__parsing_callback, &failures);
- pmu_for_each_sys_event(test__parsing_callback, &failures);
+ pmu_for_each_core_metric(test__parsing_callback, &failures);
+ pmu_for_each_sys_metric(test__parsing_callback, &failures);
return failures == 0 ? TEST_OK : TEST_FAIL;
}
@@ -975,6 +950,7 @@ static int metric_parse_fake(const char *metric_name, const char *str)
pr_debug("expr__ctx_new failed");
return TEST_FAIL;
}
+ ctx->sctx.is_test = true;
if (expr__find_ids(str, NULL, ctx) < 0) {
pr_err("expr__find_ids failed\n");
return -1;
@@ -1021,14 +997,11 @@ out:
return ret;
}
-static int test__parsing_fake_callback(const struct pmu_event *pe,
- const struct pmu_events_table *table __maybe_unused,
+static int test__parsing_fake_callback(const struct pmu_metric *pm,
+ const struct pmu_metrics_table *table __maybe_unused,
void *data __maybe_unused)
{
- if (!pe->metric_expr)
- return 0;
-
- return metric_parse_fake(pe->metric_name, pe->metric_expr);
+ return metric_parse_fake(pm->metric_name, pm->metric_expr);
}
/*
@@ -1047,11 +1020,11 @@ static int test__parsing_fake(struct test_suite *test __maybe_unused,
return err;
}
- err = pmu_for_each_core_event(test__parsing_fake_callback, NULL);
+ err = pmu_for_each_core_metric(test__parsing_fake_callback, NULL);
if (err)
return err;
- return pmu_for_each_sys_event(test__parsing_fake_callback, NULL);
+ return pmu_for_each_sys_metric(test__parsing_fake_callback, NULL);
}
static struct test_case pmu_events_tests[] = {
diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c
index 927c7f0cc4cc..25a3f6cece50 100644
--- a/tools/perf/tests/sample-parsing.c
+++ b/tools/perf/tests/sample-parsing.c
@@ -37,7 +37,7 @@
* in branch_stack variable.
*/
#define BS_EXPECTED_BE 0xa000d00000000000
-#define BS_EXPECTED_LE 0xd5000000
+#define BS_EXPECTED_LE 0x1aa00000000
#define FLAG(s) s->branch_stack->entries[i].flags
static bool samples_same(const struct perf_sample *s1,
diff --git a/tools/perf/tests/shell/buildid.sh b/tools/perf/tests/shell/buildid.sh
index aaf851108ca3..0ce22ea0a7f1 100755
--- a/tools/perf/tests/shell/buildid.sh
+++ b/tools/perf/tests/shell/buildid.sh
@@ -66,7 +66,9 @@ check()
esac
echo "build id: ${id}"
- link=${build_id_dir}/.build-id/${id:0:2}/${id:2}
+ id_file=${id#??}
+ id_dir=${id%$id_file}
+ link=$build_id_dir/.build-id/$id_dir/$id_file
echo "link: ${link}"
if [ ! -h $link ]; then
@@ -74,7 +76,7 @@ check()
exit 1
fi
- file=${build_id_dir}/.build-id/${id:0:2}/`readlink ${link}`/elf
+ file=${build_id_dir}/.build-id/$id_dir/`readlink ${link}`/elf
echo "file: ${file}"
# Check for file permission of original file
@@ -130,20 +132,22 @@ test_record()
{
data=$(mktemp /tmp/perf.data.XXX)
build_id_dir=$(mktemp -d /tmp/perf.debug.XXX)
- log=$(mktemp /tmp/perf.log.XXX)
+ log_out=$(mktemp /tmp/perf.log.out.XXX)
+ log_err=$(mktemp /tmp/perf.log.err.XXX)
perf="perf --buildid-dir ${build_id_dir}"
echo "running: perf record $@"
- ${perf} record --buildid-all -o ${data} $@ &> ${log}
+ ${perf} record --buildid-all -o ${data} $@ 1>${log_out} 2>${log_err}
if [ $? -ne 0 ]; then
echo "failed: record $@"
- echo "see log: ${log}"
+ echo "see log: ${log_err}"
exit 1
fi
- check ${@: -1}
+ args="$*"
+ check ${args##* }
- rm -f ${log}
+ rm -f ${log_out} ${log_err}
rm -rf ${build_id_dir}
rm -rf ${data}
}
diff --git a/tools/perf/tests/shell/lib/coresight.sh b/tools/perf/tests/shell/lib/coresight.sh
index 45a1477256b6..6c3d34ec64d8 100644
--- a/tools/perf/tests/shell/lib/coresight.sh
+++ b/tools/perf/tests/shell/lib/coresight.sh
@@ -58,9 +58,9 @@ perf_dump_aux_verify() {
# compiler may produce different code depending on the compiler and
# optimization options, so this is rough just to see if we're
# either missing almost all the data or all of it
- ATOM_FX_NUM=`grep I_ATOM_F "$DUMP" | wc -l`
- ASYNC_NUM=`grep I_ASYNC "$DUMP" | wc -l`
- TRACE_INFO_NUM=`grep I_TRACE_INFO "$DUMP" | wc -l`
+ ATOM_FX_NUM=$(grep -c I_ATOM_F "$DUMP")
+ ASYNC_NUM=$(grep -c I_ASYNC "$DUMP")
+ TRACE_INFO_NUM=$(grep -c I_TRACE_INFO "$DUMP")
rm -f "$DUMP"
# Arguments provide minimums for a pass
@@ -96,18 +96,18 @@ perf_dump_aux_tid_verify() {
# The TID test tools will print a TID per stdout line that are being
# tested
- TIDS=`cat "$2"`
+ TIDS=$(cat "$2")
# Scan the perf report to find the TIDs that are actually CID in hex
# and build a list of the ones found
- FOUND_TIDS=`perf report --stdio --dump -i "$1" | \
+ FOUND_TIDS=$(perf report --stdio --dump -i "$1" | \
grep -o "CID=0x[0-9a-z]\+" | sed 's/CID=//g' | \
- uniq | sort | uniq`
+ uniq | sort | uniq)
# No CID=xxx found - maybe your kernel is reporting these as
# VMID=xxx so look there
if test -z "$FOUND_TIDS"; then
- FOUND_TIDS=`perf report --stdio --dump -i "$1" | \
+ FOUND_TIDS=$(perf report --stdio --dump -i "$1" | \
grep -o "VMID=0x[0-9a-z]\+" | sed 's/VMID=//g' | \
- uniq | sort | uniq`
+ uniq | sort | uniq)
fi
# Iterate over the list of TIDs that the test says it has and find
@@ -116,7 +116,7 @@ perf_dump_aux_tid_verify() {
for TID2 in $TIDS; do
FOUND=""
for TIDHEX in $FOUND_TIDS; do
- TID=`printf "%i" $TIDHEX`
+ TID=$(printf "%i" $TIDHEX)
if test "$TID" -eq "$TID2"; then
FOUND="y"
break
diff --git a/tools/perf/tests/shell/lib/perf_json_output_lint.py b/tools/perf/tests/shell/lib/perf_json_output_lint.py
index d90f8d102eb9..97598d14e532 100644
--- a/tools/perf/tests/shell/lib/perf_json_output_lint.py
+++ b/tools/perf/tests/shell/lib/perf_json_output_lint.py
@@ -40,19 +40,6 @@ def is_counter_value(num):
return isfloat(num) or num == '<not counted>' or num == '<not supported>'
def check_json_output(expected_items):
- if expected_items != -1:
- for line in Lines:
- if 'failed' not in line:
- count = 0
- count = line.count(',')
- if count != expected_items and count >= 1 and count <= 3 and 'metric-value' in line:
- # Events that generate >1 metric may have isolated metric
- # values and possibly other prefixes like interval, core and
- # aggregate-number.
- continue
- if count != expected_items:
- raise RuntimeError(f'wrong number of fields. counted {count} expected {expected_items}'
- f' in \'{line}\'')
checks = {
'aggregate-number': lambda x: isfloat(x),
'core': lambda x: True,
@@ -73,6 +60,16 @@ def check_json_output(expected_items):
}
input = '[\n' + ','.join(Lines) + '\n]'
for item in json.loads(input):
+ if expected_items != -1:
+ count = len(item)
+ if count != expected_items and count >= 1 and count <= 4 and 'metric-value' in item:
+ # Events that generate >1 metric may have isolated metric
+ # values and possibly other prefixes like interval, core and
+ # aggregate-number.
+ pass
+ elif count != expected_items:
+ raise RuntimeError(f'wrong number of fields. counted {count} expected {expected_items}'
+ f' in \'{item}\'')
for key, value in item.items():
if key not in checks:
raise RuntimeError(f'Unexpected key: key={key} value={value}')
@@ -82,11 +79,11 @@ def check_json_output(expected_items):
try:
if args.no_args or args.system_wide or args.event:
- expected_items = 6
- elif args.interval or args.per_thread or args.system_wide_no_aggr:
expected_items = 7
- elif args.per_core or args.per_socket or args.per_node or args.per_die:
+ elif args.interval or args.per_thread or args.system_wide_no_aggr:
expected_items = 8
+ elif args.per_core or args.per_socket or args.per_node or args.per_die:
+ expected_items = 9
else:
# If no option is specified, don't check the number of items.
expected_items = -1
diff --git a/tools/perf/tests/shell/lib/probe_vfs_getname.sh b/tools/perf/tests/shell/lib/probe_vfs_getname.sh
index ed0a3972c4c8..60c5e34f90c4 100644
--- a/tools/perf/tests/shell/lib/probe_vfs_getname.sh
+++ b/tools/perf/tests/shell/lib/probe_vfs_getname.sh
@@ -22,3 +22,11 @@ skip_if_no_debuginfo() {
add_probe_vfs_getname -v 2>&1 | grep -E -q "^(Failed to find the path for the kernel|Debuginfo-analysis is not supported)|(file has no debug information)" && return 2
return 1
}
+
+# check if perf is compiled with libtraceevent support
+skip_no_probe_record_support() {
+ if [ $had_vfs_getname -eq 1 ] ; then
+ perf record --dry-run -e $1 2>&1 | grep "libtraceevent is necessary for tracepoint support" && return 2
+ return 1
+ fi
+}
diff --git a/tools/perf/tests/shell/lock_contention.sh b/tools/perf/tests/shell/lock_contention.sh
index b05f1b1ca6c8..be5fcafb26aa 100755
--- a/tools/perf/tests/shell/lock_contention.sh
+++ b/tools/perf/tests/shell/lock_contention.sh
@@ -128,7 +128,7 @@ test_type_filter()
echo "Testing perf lock contention --type-filter (w/ spinlock)"
perf lock contention -i ${perfdata} -Y spinlock -q 2> ${result}
if [ $(grep -c -v spinlock "${result}") != "0" ]; then
- echo "[Fail] Recorded should not have non-spinlocks:" $(cat "${result}")
+ echo "[Fail] Recorded result should not have non-spinlocks:" $(cat "${result}")
err=1
exit
fi
@@ -139,7 +139,7 @@ test_type_filter()
perf lock con -a -b -Y spinlock -q -- perf bench sched messaging > /dev/null 2> ${result}
if [ $(grep -c -v spinlock "${result}") != "0" ]; then
- echo "[Fail] Recorded should not have non-spinlocks:" $(cat "${result}")
+ echo "[Fail] BPF result should not have non-spinlocks:" $(cat "${result}")
err=1
exit
fi
@@ -160,7 +160,7 @@ test_lock_filter()
local type=$(head -1 "${result}" | awk '{ print $8 }' | sed -e 's/:.*//')
if [ $(grep -c -v "${type}" "${result}") != "0" ]; then
- echo "[Fail] Recorded should not have non-${type} locks:" $(cat "${result}")
+ echo "[Fail] Recorded result should not have non-${type} locks:" $(cat "${result}")
err=1
exit
fi
@@ -171,7 +171,63 @@ test_lock_filter()
perf lock con -a -b -L tasklist_lock -q -- perf bench sched messaging > /dev/null 2> ${result}
if [ $(grep -c -v "${type}" "${result}") != "0" ]; then
- echo "[Fail] Recorded should not have non-${type} locks:" $(cat "${result}")
+ echo "[Fail] BPF result should not have non-${type} locks:" $(cat "${result}")
+ err=1
+ exit
+ fi
+}
+
+test_stack_filter()
+{
+ echo "Testing perf lock contention --callstack-filter (w/ unix_stream)"
+ perf lock contention -i ${perfdata} -v -q 2> ${result}
+ if [ $(grep -c unix_stream "${result}") == "0" ]; then
+ echo "[Skip] Could not find 'unix_stream'"
+ return
+ fi
+
+ perf lock contention -i ${perfdata} -E 1 -S unix_stream -q 2> ${result}
+ if [ $(cat "${result}" | wc -l) != "1" ]; then
+ echo "[Fail] Recorded result should have a lock from unix_stream:" $(cat "${result}")
+ err=1
+ exit
+ fi
+
+ if ! perf lock con -b true > /dev/null 2>&1 ; then
+ return
+ fi
+
+ perf lock con -a -b -S unix_stream -E 1 -q -- perf bench sched messaging > /dev/null 2> ${result}
+ if [ $(cat "${result}" | wc -l) != "1" ]; then
+ echo "[Fail] BPF result should have a lock from unix_stream:" $(cat "${result}")
+ err=1
+ exit
+ fi
+}
+
+test_aggr_task_stack_filter()
+{
+ echo "Testing perf lock contention --callstack-filter with task aggregation"
+ perf lock contention -i ${perfdata} -v -q 2> ${result}
+ if [ $(grep -c unix_stream "${result}") == "0" ]; then
+ echo "[Skip] Could not find 'unix_stream'"
+ return
+ fi
+
+ perf lock contention -i ${perfdata} -t -E 1 -S unix_stream -q 2> ${result}
+ if [ $(cat "${result}" | wc -l) != "1" ]; then
+ echo "[Fail] Recorded result should have a task from unix_stream:" $(cat "${result}")
+ err=1
+ exit
+ fi
+
+ if ! perf lock con -b true > /dev/null 2>&1 ; then
+ return
+ fi
+
+ perf lock con -a -b -t -S unix_stream -E 1 -q -- perf bench sched messaging > /dev/null 2> ${result}
+ if [ $(cat "${result}" | wc -l) != "1" ]; then
+ echo "[Fail] BPF result should have a task from unix_stream:" $(cat "${result}")
err=1
exit
fi
@@ -186,5 +242,7 @@ test_aggr_task
test_aggr_addr
test_type_filter
test_lock_filter
+test_stack_filter
+test_aggr_task_stack_filter
exit ${err}
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 57e7a6a470c9..b4149b2db4c6 100755
--- a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh
+++ b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh
@@ -11,6 +11,7 @@
# Arnaldo Carvalho de Melo <acme@kernel.org>, 2017
. $(dirname $0)/lib/probe.sh
+. $(dirname $0)/lib/probe_vfs_getname.sh
libc=$(grep -w libc /proc/self/maps | head -1 | sed -r 's/.*[[:space:]](\/.*)/\1/g')
nm -Dg $libc 2>/dev/null | fgrep -q inet_pton || exit 254
@@ -57,7 +58,17 @@ trace_libc_inet_pton_backtrace() {
perf_data=`mktemp -u /tmp/perf.data.XXX`
perf_script=`mktemp -u /tmp/perf.script.XXX`
+
+ # Check presence of libtraceevent support to run perf record
+ skip_no_probe_record_support "$event_name/$eventattr/"
+ [ $? -eq 2 ] && return 2
+
perf record -e $event_name/$eventattr/ -o $perf_data ping -6 -c 1 ::1 > /dev/null 2>&1
+ # check if perf data file got created in above step.
+ if [ ! -e $perf_data ]; then
+ printf "FAIL: perf record failed to create \"%s\" \n" "$perf_data"
+ return 1
+ fi
perf script -i $perf_data | tac | grep -m1 ^ping -B9 | tac > $perf_script
exec 3<$perf_script
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 7f83b2715b9a..1341437e1bd9 100755
--- a/tools/perf/tests/shell/record+script_probe_vfs_getname.sh
+++ b/tools/perf/tests/shell/record+script_probe_vfs_getname.sh
@@ -17,6 +17,9 @@ skip_if_no_perf_probe || exit 2
record_open_file() {
echo "Recording open file:"
+ # Check presence of libtraceevent support to run perf record
+ skip_no_probe_record_support "probe:vfs_getname*"
+ [ $? -eq 2 ] && return 2
perf record -o ${perfdata} -e probe:vfs_getname\* touch $file
}
diff --git a/tools/perf/tests/shell/stat+csv_output.sh b/tools/perf/tests/shell/stat+csv_output.sh
index b7f050aa6210..324fc9e6edd7 100755
--- a/tools/perf/tests/shell/stat+csv_output.sh
+++ b/tools/perf/tests/shell/stat+csv_output.sh
@@ -7,6 +7,7 @@
set -e
skip_test=0
+csv_sep=@
function commachecker()
{
@@ -34,7 +35,7 @@ function commachecker()
[ "$x" = "Failed" ] && continue
# Count the number of commas
- x=$(echo $line | tr -d -c ',')
+ x=$(echo $line | tr -d -c $csv_sep)
cnt="${#x}"
# echo $line $cnt
[[ ! "$cnt" =~ $exp ]] && {
@@ -54,7 +55,7 @@ function ParanoidAndNotRoot()
check_no_args()
{
echo -n "Checking CSV output: no args "
- perf stat -x, true 2>&1 | commachecker --no-args
+ perf stat -x$csv_sep true 2>&1 | commachecker --no-args
echo "[Success]"
}
@@ -66,7 +67,7 @@ check_system_wide()
echo "[Skip] paranoid and not root"
return
fi
- perf stat -x, -a true 2>&1 | commachecker --system-wide
+ perf stat -x$csv_sep -a true 2>&1 | commachecker --system-wide
echo "[Success]"
}
@@ -79,14 +80,14 @@ check_system_wide_no_aggr()
return
fi
echo -n "Checking CSV output: system wide no aggregation "
- perf stat -x, -A -a --no-merge true 2>&1 | commachecker --system-wide-no-aggr
+ perf stat -x$csv_sep -A -a --no-merge true 2>&1 | commachecker --system-wide-no-aggr
echo "[Success]"
}
check_interval()
{
echo -n "Checking CSV output: interval "
- perf stat -x, -I 1000 true 2>&1 | commachecker --interval
+ perf stat -x$csv_sep -I 1000 true 2>&1 | commachecker --interval
echo "[Success]"
}
@@ -94,7 +95,7 @@ check_interval()
check_event()
{
echo -n "Checking CSV output: event "
- perf stat -x, -e cpu-clock true 2>&1 | commachecker --event
+ perf stat -x$csv_sep -e cpu-clock true 2>&1 | commachecker --event
echo "[Success]"
}
@@ -106,7 +107,7 @@ check_per_core()
echo "[Skip] paranoid and not root"
return
fi
- perf stat -x, --per-core -a true 2>&1 | commachecker --per-core
+ perf stat -x$csv_sep --per-core -a true 2>&1 | commachecker --per-core
echo "[Success]"
}
@@ -118,7 +119,7 @@ check_per_thread()
echo "[Skip] paranoid and not root"
return
fi
- perf stat -x, --per-thread -a true 2>&1 | commachecker --per-thread
+ perf stat -x$csv_sep --per-thread -a true 2>&1 | commachecker --per-thread
echo "[Success]"
}
@@ -130,7 +131,7 @@ check_per_die()
echo "[Skip] paranoid and not root"
return
fi
- perf stat -x, --per-die -a true 2>&1 | commachecker --per-die
+ perf stat -x$csv_sep --per-die -a true 2>&1 | commachecker --per-die
echo "[Success]"
}
@@ -142,7 +143,7 @@ check_per_node()
echo "[Skip] paranoid and not root"
return
fi
- perf stat -x, --per-node -a true 2>&1 | commachecker --per-node
+ perf stat -x$csv_sep --per-node -a true 2>&1 | commachecker --per-node
echo "[Success]"
}
@@ -154,7 +155,7 @@ check_per_socket()
echo "[Skip] paranoid and not root"
return
fi
- perf stat -x, --per-socket -a true 2>&1 | commachecker --per-socket
+ perf stat -x$csv_sep --per-socket -a true 2>&1 | commachecker --per-socket
echo "[Success]"
}
diff --git a/tools/perf/tests/shell/stat_all_metrics.sh b/tools/perf/tests/shell/stat_all_metrics.sh
index 6e79349e42be..22e9cb294b40 100755
--- a/tools/perf/tests/shell/stat_all_metrics.sh
+++ b/tools/perf/tests/shell/stat_all_metrics.sh
@@ -11,7 +11,7 @@ for m in $(perf list --raw-dump metrics); do
continue
fi
# Failed so try system wide.
- result=$(perf stat -M "$m" -a true 2>&1)
+ result=$(perf stat -M "$m" -a sleep 0.01 2>&1)
if [[ "$result" =~ "${m:0:50}" ]]
then
continue
diff --git a/tools/perf/tests/shell/test_brstack.sh b/tools/perf/tests/shell/test_brstack.sh
index 59195eb80052..1c49d8293003 100755
--- a/tools/perf/tests/shell/test_brstack.sh
+++ b/tools/perf/tests/shell/test_brstack.sh
@@ -30,14 +30,14 @@ test_user_branches() {
# brstack_foo+0x14/brstack_bar+0x40/P/-/-/0/CALL
set -x
- grep -E -m1 "^brstack_bench\+[^ ]*/brstack_foo\+[^ ]*/IND_CALL$" $TMPDIR/perf.script
- grep -E -m1 "^brstack_foo\+[^ ]*/brstack_bar\+[^ ]*/CALL$" $TMPDIR/perf.script
- grep -E -m1 "^brstack_bench\+[^ ]*/brstack_foo\+[^ ]*/CALL$" $TMPDIR/perf.script
- grep -E -m1 "^brstack_bench\+[^ ]*/brstack_bar\+[^ ]*/CALL$" $TMPDIR/perf.script
- grep -E -m1 "^brstack_bar\+[^ ]*/brstack_foo\+[^ ]*/RET$" $TMPDIR/perf.script
- grep -E -m1 "^brstack_foo\+[^ ]*/brstack_bench\+[^ ]*/RET$" $TMPDIR/perf.script
- grep -E -m1 "^brstack_bench\+[^ ]*/brstack_bench\+[^ ]*/COND$" $TMPDIR/perf.script
- grep -E -m1 "^brstack\+[^ ]*/brstack\+[^ ]*/UNCOND$" $TMPDIR/perf.script
+ grep -E -m1 "^brstack_bench\+[^ ]*/brstack_foo\+[^ ]*/IND_CALL/.*$" $TMPDIR/perf.script
+ grep -E -m1 "^brstack_foo\+[^ ]*/brstack_bar\+[^ ]*/CALL/.*$" $TMPDIR/perf.script
+ grep -E -m1 "^brstack_bench\+[^ ]*/brstack_foo\+[^ ]*/CALL/.*$" $TMPDIR/perf.script
+ grep -E -m1 "^brstack_bench\+[^ ]*/brstack_bar\+[^ ]*/CALL/.*$" $TMPDIR/perf.script
+ grep -E -m1 "^brstack_bar\+[^ ]*/brstack_foo\+[^ ]*/RET/.*$" $TMPDIR/perf.script
+ grep -E -m1 "^brstack_foo\+[^ ]*/brstack_bench\+[^ ]*/RET/.*$" $TMPDIR/perf.script
+ grep -E -m1 "^brstack_bench\+[^ ]*/brstack_bench\+[^ ]*/COND/.*$" $TMPDIR/perf.script
+ grep -E -m1 "^brstack\+[^ ]*/brstack\+[^ ]*/UNCOND/.*$" $TMPDIR/perf.script
set +x
# some branch types are still not being tested:
@@ -57,7 +57,7 @@ test_filter() {
# fail if we find any branch type that doesn't match any of the expected ones
# also consider UNKNOWN branch types (-)
- if grep -E -vm1 "^[^ ]*/($expect|-|( *))$" $TMPDIR/perf.script; then
+ if grep -E -vm1 "^[^ ]*/($expect|-|( *))/.*$" $TMPDIR/perf.script; then
return 1
fi
}
diff --git a/tools/perf/tests/shell/test_intel_pt.sh b/tools/perf/tests/shell/test_intel_pt.sh
index f5ed7b1af419..4ddb17cb83c5 100755
--- a/tools/perf/tests/shell/test_intel_pt.sh
+++ b/tools/perf/tests/shell/test_intel_pt.sh
@@ -620,6 +620,22 @@ test_event_trace()
return 0
}
+test_pipe()
+{
+ echo "--- Test with pipe mode ---"
+ # Check if it works with pipe
+ if ! perf_record_no_bpf -o- -e intel_pt//u uname | perf report -q -i- --itrace=i10000 ; then
+ echo "perf record + report failed with pipe mode"
+ return 1
+ fi
+ if ! perf_record_no_bpf -o- -e intel_pt//u uname | perf inject -b > /dev/null ; then
+ echo "perf record + inject failed with pipe mode"
+ return 1
+ fi
+ echo OK
+ return 0
+}
+
count_result()
{
if [ "$1" -eq 2 ] ; then
@@ -647,6 +663,7 @@ test_virtual_lbr || ret=$? ; count_result $ret ; ret=0
test_power_event || ret=$? ; count_result $ret ; ret=0
test_no_tnt || ret=$? ; count_result $ret ; ret=0
test_event_trace || ret=$? ; count_result $ret ; ret=0
+test_pipe || ret=$? ; count_result $ret ; ret=0
cleanup
diff --git a/tools/perf/tests/symbols.c b/tools/perf/tests/symbols.c
new file mode 100644
index 000000000000..057b16df6416
--- /dev/null
+++ b/tools/perf/tests/symbols.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/compiler.h>
+#include <linux/string.h>
+#include <sys/mman.h>
+#include <limits.h>
+#include "debug.h"
+#include "dso.h"
+#include "machine.h"
+#include "thread.h"
+#include "symbol.h"
+#include "map.h"
+#include "util.h"
+#include "tests.h"
+
+struct test_info {
+ struct machine *machine;
+ struct thread *thread;
+};
+
+static int init_test_info(struct test_info *ti)
+{
+ ti->machine = machine__new_host();
+ if (!ti->machine) {
+ pr_debug("machine__new_host() failed!\n");
+ return TEST_FAIL;
+ }
+
+ /* Create a dummy thread */
+ ti->thread = machine__findnew_thread(ti->machine, 100, 100);
+ if (!ti->thread) {
+ pr_debug("machine__findnew_thread() failed!\n");
+ return TEST_FAIL;
+ }
+
+ return TEST_OK;
+}
+
+static void exit_test_info(struct test_info *ti)
+{
+ thread__put(ti->thread);
+ machine__delete(ti->machine);
+}
+
+static void get_test_dso_filename(char *filename, size_t max_sz)
+{
+ if (dso_to_test)
+ strlcpy(filename, dso_to_test, max_sz);
+ else
+ perf_exe(filename, max_sz);
+}
+
+static int create_map(struct test_info *ti, char *filename, struct map **map_p)
+{
+ /* Create a dummy map at 0x100000 */
+ *map_p = map__new(ti->machine, 0x100000, 0xffffffff, 0, NULL,
+ PROT_EXEC, 0, NULL, filename, ti->thread);
+ if (!*map_p) {
+ pr_debug("Failed to create map!");
+ return TEST_FAIL;
+ }
+
+ return TEST_OK;
+}
+
+static int test_dso(struct dso *dso)
+{
+ struct symbol *last_sym = NULL;
+ struct rb_node *nd;
+ int ret = TEST_OK;
+
+ /* dso__fprintf() prints all the symbols */
+ if (verbose > 1)
+ dso__fprintf(dso, stderr);
+
+ for (nd = rb_first_cached(&dso->symbols); nd; nd = rb_next(nd)) {
+ struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
+
+ if (sym->type != STT_FUNC && sym->type != STT_GNU_IFUNC)
+ continue;
+
+ /* Check for overlapping function symbols */
+ if (last_sym && sym->start < last_sym->end) {
+ pr_debug("Overlapping symbols:\n");
+ symbol__fprintf(last_sym, stderr);
+ symbol__fprintf(sym, stderr);
+ ret = TEST_FAIL;
+ }
+ /* Check for zero-length function symbol */
+ if (sym->start == sym->end) {
+ pr_debug("Zero-length symbol:\n");
+ symbol__fprintf(sym, stderr);
+ ret = TEST_FAIL;
+ }
+ last_sym = sym;
+ }
+
+ return ret;
+}
+
+static int test_file(struct test_info *ti, char *filename)
+{
+ struct map *map = NULL;
+ int ret, nr;
+
+ pr_debug("Testing %s\n", filename);
+
+ ret = create_map(ti, filename, &map);
+ if (ret != TEST_OK)
+ return ret;
+
+ nr = dso__load(map->dso, map);
+ if (nr < 0) {
+ pr_debug("dso__load() failed!\n");
+ ret = TEST_FAIL;
+ goto out_put;
+ }
+
+ if (nr == 0) {
+ pr_debug("DSO has no symbols!\n");
+ ret = TEST_SKIP;
+ goto out_put;
+ }
+
+ ret = test_dso(map->dso);
+out_put:
+ map__put(map);
+
+ return ret;
+}
+
+static int test__symbols(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
+{
+ char filename[PATH_MAX];
+ struct test_info ti;
+ int ret;
+
+ ret = init_test_info(&ti);
+ if (ret != TEST_OK)
+ return ret;
+
+ get_test_dso_filename(filename, sizeof(filename));
+
+ ret = test_file(&ti, filename);
+
+ exit_test_info(&ti);
+
+ return ret;
+}
+
+DEFINE_SUITE("Symbols", symbols);
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index fb4b5ad4dd0f..9a0f3904e53d 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -148,6 +148,7 @@ DECLARE_SUITE(perf_time_to_tsc);
DECLARE_SUITE(dlfilter);
DECLARE_SUITE(sigtrap);
DECLARE_SUITE(event_groups);
+DECLARE_SUITE(symbols);
/*
* PowerPC and S390 do not support creation of instruction breakpoints using the
@@ -208,4 +209,6 @@ DECLARE_WORKLOAD(sqrtloop);
DECLARE_WORKLOAD(brstack);
DECLARE_WORKLOAD(datasym);
+extern const char *dso_to_test;
+
#endif /* TESTS_H */
diff --git a/tools/perf/tests/workloads/thloop.c b/tools/perf/tests/workloads/thloop.c
index 29193b75717e..af05269c2eb8 100644
--- a/tools/perf/tests/workloads/thloop.c
+++ b/tools/perf/tests/workloads/thloop.c
@@ -20,7 +20,7 @@ static void sighandler(int sig __maybe_unused)
noinline void test_loop(void)
{
while (!done)
- count++;
+ __atomic_fetch_add(&count, 1, __ATOMIC_RELAXED);
}
static void *thfunc(void *arg)
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 79b9498886a2..918b501f9bd8 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -154,7 +154,10 @@ perf-$(CONFIG_PERF_BPF_SKEL) += bpf_counter.o
perf-$(CONFIG_PERF_BPF_SKEL) += bpf_counter_cgroup.o
perf-$(CONFIG_PERF_BPF_SKEL) += bpf_ftrace.o
perf-$(CONFIG_PERF_BPF_SKEL) += bpf_off_cpu.o
-perf-$(CONFIG_PERF_BPF_SKEL) += bpf_lock_contention.o
+
+ifeq ($(CONFIG_LIBTRACEEVENT),y)
+ perf-$(CONFIG_PERF_BPF_SKEL) += bpf_lock_contention.o
+endif
ifeq ($(CONFIG_LIBTRACEEVENT),y)
perf-$(CONFIG_PERF_BPF_SKEL) += bpf_kwork.o
diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-decoder.c b/tools/perf/util/arm-spe-decoder/arm-spe-decoder.c
index 091987dd3966..40dcedfd75cd 100644
--- a/tools/perf/util/arm-spe-decoder/arm-spe-decoder.c
+++ b/tools/perf/util/arm-spe-decoder/arm-spe-decoder.c
@@ -68,7 +68,11 @@ static u64 arm_spe_calc_ip(int index, u64 payload)
/* Clean highest byte */
payload = SPE_ADDR_PKT_ADDR_GET_BYTES_0_6(payload);
} else {
- pr_err("unsupported address packet index: 0x%x\n", index);
+ static u32 seen_idx = 0;
+ if (!(seen_idx & BIT(index))) {
+ seen_idx |= BIT(index);
+ pr_warning("ignoring unsupported address packet index: 0x%x\n", index);
+ }
}
return payload;
diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c
index 2f311189c6e8..fed4741f372e 100644
--- a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c
+++ b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c
@@ -422,16 +422,18 @@ static int arm_spe_pkt_desc_addr(const struct arm_spe_pkt *packet,
int ch, pat;
u64 payload = packet->payload;
int err = 0;
+ static const char *idx_name[] = {"PC", "TGT", "VA", "PA", "PBT"};
switch (idx) {
case SPE_ADDR_PKT_HDR_INDEX_INS:
case SPE_ADDR_PKT_HDR_INDEX_BRANCH:
+ case SPE_ADDR_PKT_HDR_INDEX_PREV_BRANCH:
ns = !!SPE_ADDR_PKT_GET_NS(payload);
el = SPE_ADDR_PKT_GET_EL(payload);
payload = SPE_ADDR_PKT_ADDR_GET_BYTES_0_6(payload);
arm_spe_pkt_out_string(&err, &buf, &buf_len,
"%s 0x%llx el%d ns=%d",
- (idx == 1) ? "TGT" : "PC", payload, el, ns);
+ idx_name[idx], payload, el, ns);
break;
case SPE_ADDR_PKT_HDR_INDEX_DATA_VIRT:
arm_spe_pkt_out_string(&err, &buf, &buf_len,
diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.h b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.h
index 9b970e7bf1e2..f75ed3a8a050 100644
--- a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.h
+++ b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.h
@@ -65,6 +65,7 @@ struct arm_spe_pkt {
#define SPE_ADDR_PKT_HDR_INDEX_BRANCH 0x1
#define SPE_ADDR_PKT_HDR_INDEX_DATA_VIRT 0x2
#define SPE_ADDR_PKT_HDR_INDEX_DATA_PHYS 0x3
+#define SPE_ADDR_PKT_HDR_INDEX_PREV_BRANCH 0x4
/* Address packet payload */
#define SPE_ADDR_PKT_ADDR_BYTE7_SHIFT 56
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index c2e323cd7d49..498ff7f24463 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -1133,6 +1133,9 @@ int auxtrace_queue_data(struct perf_session *session, bool samples, bool events)
if (auxtrace__dont_decode(session))
return 0;
+ if (perf_data__is_pipe(session->data))
+ return 0;
+
if (!session->auxtrace || !session->auxtrace->queue_data)
return -EINVAL;
@@ -1391,6 +1394,7 @@ void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts,
synth_opts->calls = true;
} else {
synth_opts->instructions = true;
+ synth_opts->cycles = true;
synth_opts->period_type = PERF_ITRACE_DEFAULT_PERIOD_TYPE;
synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD;
}
@@ -1479,7 +1483,11 @@ int itrace_do_parse_synth_opts(struct itrace_synth_opts *synth_opts,
for (p = str; *p;) {
switch (*p++) {
case 'i':
- synth_opts->instructions = true;
+ case 'y':
+ if (p[-1] == 'y')
+ synth_opts->cycles = true;
+ else
+ synth_opts->instructions = true;
while (*p == ' ' || *p == ',')
p += 1;
if (isdigit(*p)) {
@@ -1638,7 +1646,7 @@ int itrace_do_parse_synth_opts(struct itrace_synth_opts *synth_opts,
}
}
out:
- if (synth_opts->instructions) {
+ if (synth_opts->instructions || synth_opts->cycles) {
if (!period_type_set)
synth_opts->period_type =
PERF_ITRACE_DEFAULT_PERIOD_TYPE;
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
index 2cf63d377831..29eb82dff574 100644
--- a/tools/perf/util/auxtrace.h
+++ b/tools/perf/util/auxtrace.h
@@ -71,6 +71,9 @@ enum itrace_period_type {
* @inject: indicates the event (not just the sample) must be fully synthesized
* because 'perf inject' will write it out
* @instructions: whether to synthesize 'instructions' events
+ * @cycles: whether to synthesize 'cycles' events
+ * (not fully accurate, since CYC packets are only emitted
+ * together with other events, such as branches)
* @branches: whether to synthesize 'branches' events
* (branch misses only for Arm SPE)
* @transactions: whether to synthesize events for transactions
@@ -119,6 +122,7 @@ struct itrace_synth_opts {
bool default_no_sample;
bool inject;
bool instructions;
+ bool cycles;
bool branches;
bool transactions;
bool ptwrites;
@@ -643,6 +647,7 @@ bool auxtrace__evsel_is_auxtrace(struct perf_session *session,
#define ITRACE_HELP \
" i[period]: synthesize instructions events\n" \
+" y[period]: synthesize cycles events (same period as i)\n" \
" b: synthesize branches events (branch misses for Arm SPE)\n" \
" c: synthesize branches events (calls only)\n" \
" r: synthesize branches events (returns only)\n" \
@@ -674,7 +679,7 @@ bool auxtrace__evsel_is_auxtrace(struct perf_session *session,
" A: approximate IPC\n" \
" Z: prefer to ignore timestamps (so-called \"timeless\" decoding)\n" \
" PERIOD[ns|us|ms|i|t]: specify period to sample stream\n" \
-" concatenate multiple options. Default is ibxwpe or cewp\n"
+" concatenate multiple options. Default is iybxwpe or cewp\n"
static inline
void itrace_synth_opts__set_time_range(struct itrace_synth_opts *opts,
diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lock_contention.c
index 0236334fd69b..fadcacb9d501 100644
--- a/tools/perf/util/bpf_lock_contention.c
+++ b/tools/perf/util/bpf_lock_contention.c
@@ -34,13 +34,15 @@ int lock_contention_prepare(struct lock_contention *con)
bpf_map__set_max_entries(skel->maps.lock_stat, con->map_nr_entries);
bpf_map__set_max_entries(skel->maps.tstamp, con->map_nr_entries);
- if (con->aggr_mode == LOCK_AGGR_TASK) {
+ if (con->aggr_mode == LOCK_AGGR_TASK)
bpf_map__set_max_entries(skel->maps.task_data, con->map_nr_entries);
- bpf_map__set_max_entries(skel->maps.stacks, 1);
- } else {
+ else
bpf_map__set_max_entries(skel->maps.task_data, 1);
+
+ if (con->save_callstack)
bpf_map__set_max_entries(skel->maps.stacks, con->map_nr_entries);
- }
+ else
+ bpf_map__set_max_entries(skel->maps.stacks, 1);
if (target__has_cpu(target))
ncpus = perf_cpu_map__nr(evlist->core.user_requested_cpus);
@@ -146,6 +148,8 @@ int lock_contention_prepare(struct lock_contention *con)
/* these don't work well if in the rodata section */
skel->bss->stack_skip = con->stack_skip;
skel->bss->aggr_mode = con->aggr_mode;
+ skel->bss->needs_callstack = con->save_callstack;
+ skel->bss->lock_owner = con->owner;
lock_contention_bpf__attach(skel);
return 0;
@@ -163,9 +167,70 @@ int lock_contention_stop(void)
return 0;
}
+static const char *lock_contention_get_name(struct lock_contention *con,
+ struct contention_key *key,
+ u64 *stack_trace)
+{
+ int idx = 0;
+ u64 addr;
+ const char *name = "";
+ static char name_buf[KSYM_NAME_LEN];
+ struct symbol *sym;
+ struct map *kmap;
+ struct machine *machine = con->machine;
+
+ if (con->aggr_mode == LOCK_AGGR_TASK) {
+ struct contention_task_data task;
+ int pid = key->pid;
+ int task_fd = bpf_map__fd(skel->maps.task_data);
+
+ /* do not update idle comm which contains CPU number */
+ if (pid) {
+ struct thread *t = __machine__findnew_thread(machine, /*pid=*/-1, pid);
+
+ if (t == NULL)
+ return name;
+ if (!bpf_map_lookup_elem(task_fd, &pid, &task) &&
+ thread__set_comm(t, task.comm, /*timestamp=*/0))
+ name = task.comm;
+ }
+ return name;
+ }
+
+ if (con->aggr_mode == LOCK_AGGR_ADDR) {
+ sym = machine__find_kernel_symbol(machine, key->lock_addr, &kmap);
+ if (sym)
+ name = sym->name;
+ return name;
+ }
+
+ /* LOCK_AGGR_CALLER: skip lock internal functions */
+ while (machine__is_lock_function(machine, stack_trace[idx]) &&
+ idx < con->max_stack - 1)
+ idx++;
+
+ addr = stack_trace[idx];
+ sym = machine__find_kernel_symbol(machine, addr, &kmap);
+
+ if (sym) {
+ unsigned long offset;
+
+ offset = kmap->map_ip(kmap, addr) - sym->start;
+
+ if (offset == 0)
+ return sym->name;
+
+ snprintf(name_buf, sizeof(name_buf), "%s+%#lx", sym->name, offset);
+ } else {
+ snprintf(name_buf, sizeof(name_buf), "%#lx", (unsigned long)addr);
+ }
+
+ return name_buf;
+}
+
int lock_contention_read(struct lock_contention *con)
{
- int fd, stack, task_fd, err = 0;
+ int fd, stack, err = 0;
struct contention_key *prev_key, key;
struct contention_data data = {};
struct lock_stat *st = NULL;
@@ -175,7 +240,6 @@ int lock_contention_read(struct lock_contention *con)
fd = bpf_map__fd(skel->maps.lock_stat);
stack = bpf_map__fd(skel->maps.stacks);
- task_fd = bpf_map__fd(skel->maps.task_data);
con->lost = skel->bss->lost;
@@ -195,98 +259,75 @@ int lock_contention_read(struct lock_contention *con)
prev_key = NULL;
while (!bpf_map_get_next_key(fd, prev_key, &key)) {
- struct map *kmap;
- struct symbol *sym;
- int idx = 0;
- s32 stack_id;
+ s64 ls_key;
+ const char *name;
/* to handle errors in the loop body */
err = -1;
bpf_map_lookup_elem(fd, &key, &data);
- st = zalloc(sizeof(*st));
- if (st == NULL)
- break;
-
- st->nr_contended = data.count;
- st->wait_time_total = data.total_time;
- st->wait_time_max = data.max_time;
- st->wait_time_min = data.min_time;
-
- if (data.count)
- st->avg_wait_time = data.total_time / data.count;
-
- st->flags = data.flags;
- st->addr = key.aggr_key;
+ if (con->save_callstack) {
+ bpf_map_lookup_elem(stack, &key.stack_id, stack_trace);
- if (con->aggr_mode == LOCK_AGGR_TASK) {
- struct contention_task_data task;
- struct thread *t;
- int pid = key.aggr_key;
+ if (!match_callstack_filter(machine, stack_trace))
+ goto next;
+ }
- /* do not update idle comm which contains CPU number */
- if (st->addr) {
- bpf_map_lookup_elem(task_fd, &pid, &task);
- t = __machine__findnew_thread(machine, /*pid=*/-1, pid);
- thread__set_comm(t, task.comm, /*timestamp=*/0);
- }
+ switch (con->aggr_mode) {
+ case LOCK_AGGR_CALLER:
+ ls_key = key.stack_id;
+ break;
+ case LOCK_AGGR_TASK:
+ ls_key = key.pid;
+ break;
+ case LOCK_AGGR_ADDR:
+ ls_key = key.lock_addr;
+ break;
+ default:
goto next;
}
- if (con->aggr_mode == LOCK_AGGR_ADDR) {
- sym = machine__find_kernel_symbol(machine, st->addr, &kmap);
- if (sym)
- st->name = strdup(sym->name);
+ st = lock_stat_find(ls_key);
+ if (st != NULL) {
+ st->wait_time_total += data.total_time;
+ if (st->wait_time_max < data.max_time)
+ st->wait_time_max = data.max_time;
+ if (st->wait_time_min > data.min_time)
+ st->wait_time_min = data.min_time;
+
+ st->nr_contended += data.count;
+ if (st->nr_contended)
+ st->avg_wait_time = st->wait_time_total / st->nr_contended;
goto next;
}
- stack_id = key.aggr_key;
- bpf_map_lookup_elem(stack, &stack_id, stack_trace);
-
- /* skip lock internal functions */
- while (machine__is_lock_function(machine, stack_trace[idx]) &&
- idx < con->max_stack - 1)
- idx++;
-
- st->addr = stack_trace[idx];
- sym = machine__find_kernel_symbol(machine, st->addr, &kmap);
-
- if (sym) {
- unsigned long offset;
- int ret = 0;
-
- offset = kmap->map_ip(kmap, st->addr) - sym->start;
+ name = lock_contention_get_name(con, &key, stack_trace);
+ st = lock_stat_findnew(ls_key, name, data.flags);
+ if (st == NULL)
+ break;
- if (offset)
- ret = asprintf(&st->name, "%s+%#lx", sym->name, offset);
- else
- st->name = strdup(sym->name);
+ st->nr_contended = data.count;
+ st->wait_time_total = data.total_time;
+ st->wait_time_max = data.max_time;
+ st->wait_time_min = data.min_time;
- if (ret < 0 || st->name == NULL)
- break;
- } else if (asprintf(&st->name, "%#lx", (unsigned long)st->addr) < 0) {
- break;
- }
+ if (data.count)
+ st->avg_wait_time = data.total_time / data.count;
- if (verbose > 0) {
+ if (con->save_callstack) {
st->callstack = memdup(stack_trace, stack_size);
if (st->callstack == NULL)
break;
}
+
next:
- hlist_add_head(&st->hash_entry, con->result);
prev_key = &key;
- /* we're fine now, reset the values */
- st = NULL;
+ /* we're fine now, reset the error */
err = 0;
}
free(stack_trace);
- if (st) {
- free(st->name);
- free(st);
- }
return err;
}
diff --git a/tools/perf/util/bpf_skel/lock_contention.bpf.c b/tools/perf/util/bpf_skel/lock_contention.bpf.c
index ad0ca5d50557..e6007eaeda1a 100644
--- a/tools/perf/util/bpf_skel/lock_contention.bpf.c
+++ b/tools/perf/util/bpf_skel/lock_contention.bpf.c
@@ -10,6 +10,14 @@
/* default buffer size */
#define MAX_ENTRIES 10240
+/* lock contention flags from include/trace/events/lock.h */
+#define LCB_F_SPIN (1U << 0)
+#define LCB_F_READ (1U << 1)
+#define LCB_F_WRITE (1U << 2)
+#define LCB_F_RT (1U << 3)
+#define LCB_F_PERCPU (1U << 4)
+#define LCB_F_MUTEX (1U << 5)
+
struct tstamp_data {
__u64 timestamp;
__u64 lock;
@@ -76,13 +84,23 @@ struct {
__uint(max_entries, 1);
} addr_filter SEC(".maps");
+struct rw_semaphore___old {
+ struct task_struct *owner;
+} __attribute__((preserve_access_index));
+
+struct rw_semaphore___new {
+ atomic_long_t owner;
+} __attribute__((preserve_access_index));
+
/* control flags */
int enabled;
int has_cpu;
int has_task;
int has_type;
int has_addr;
+int needs_callstack;
int stack_skip;
+int lock_owner;
/* determine the key of lock stat */
int aggr_mode;
@@ -131,17 +149,59 @@ static inline int can_record(u64 *ctx)
return 1;
}
-static inline void update_task_data(__u32 pid)
+static inline int update_task_data(struct task_struct *task)
{
struct contention_task_data *p;
+ int pid, err;
+
+ err = bpf_core_read(&pid, sizeof(pid), &task->pid);
+ if (err)
+ return -1;
p = bpf_map_lookup_elem(&task_data, &pid);
if (p == NULL) {
- struct contention_task_data data;
+ struct contention_task_data data = {};
- bpf_get_current_comm(data.comm, sizeof(data.comm));
+ BPF_CORE_READ_STR_INTO(&data.comm, task, comm);
bpf_map_update_elem(&task_data, &pid, &data, BPF_NOEXIST);
}
+
+ return 0;
+}
+
+#ifndef __has_builtin
+# define __has_builtin(x) 0
+#endif
+
+static inline struct task_struct *get_lock_owner(__u64 lock, __u32 flags)
+{
+ struct task_struct *task;
+ __u64 owner = 0;
+
+ if (flags & LCB_F_MUTEX) {
+ struct mutex *mutex = (void *)lock;
+ owner = BPF_CORE_READ(mutex, owner.counter);
+ } else if (flags == LCB_F_READ || flags == LCB_F_WRITE) {
+#if __has_builtin(bpf_core_type_matches)
+ if (bpf_core_type_matches(struct rw_semaphore___old)) {
+ struct rw_semaphore___old *rwsem = (void *)lock;
+ owner = (unsigned long)BPF_CORE_READ(rwsem, owner);
+ } else if (bpf_core_type_matches(struct rw_semaphore___new)) {
+ struct rw_semaphore___new *rwsem = (void *)lock;
+ owner = BPF_CORE_READ(rwsem, owner.counter);
+ }
+#else
+ /* assume new struct */
+ struct rw_semaphore *rwsem = (void *)lock;
+ owner = BPF_CORE_READ(rwsem, owner.counter);
+#endif
+ }
+
+ if (!owner)
+ return NULL;
+
+ task = (void *)(owner & ~7UL);
+ return task;
}
SEC("tp_btf/contention_begin")
@@ -173,11 +233,31 @@ int contention_begin(u64 *ctx)
pelem->lock = (__u64)ctx[0];
pelem->flags = (__u32)ctx[1];
- if (aggr_mode == LOCK_AGGR_CALLER) {
+ if (needs_callstack) {
pelem->stack_id = bpf_get_stackid(ctx, &stacks,
BPF_F_FAST_STACK_CMP | stack_skip);
if (pelem->stack_id < 0)
lost++;
+ } else if (aggr_mode == LOCK_AGGR_TASK) {
+ struct task_struct *task;
+
+ if (lock_owner) {
+ task = get_lock_owner(pelem->lock, pelem->flags);
+
+ /* The flags is not used anymore. Pass the owner pid. */
+ if (task)
+ pelem->flags = BPF_CORE_READ(task, pid);
+ else
+ pelem->flags = -1U;
+
+ } else {
+ task = bpf_get_current_task_btf();
+ }
+
+ if (task) {
+ if (update_task_data(task) < 0 && lock_owner)
+ pelem->flags = -1U;
+ }
}
return 0;
@@ -188,7 +268,7 @@ int contention_end(u64 *ctx)
{
__u32 pid;
struct tstamp_data *pelem;
- struct contention_key key;
+ struct contention_key key = {};
struct contention_data *data;
__u64 duration;
@@ -204,14 +284,20 @@ int contention_end(u64 *ctx)
switch (aggr_mode) {
case LOCK_AGGR_CALLER:
- key.aggr_key = pelem->stack_id;
+ key.stack_id = pelem->stack_id;
break;
case LOCK_AGGR_TASK:
- key.aggr_key = pid;
- update_task_data(pid);
+ if (lock_owner)
+ key.pid = pelem->flags;
+ else
+ key.pid = pid;
+ if (needs_callstack)
+ key.stack_id = pelem->stack_id;
break;
case LOCK_AGGR_ADDR:
- key.aggr_key = pelem->lock;
+ key.lock_addr = pelem->lock;
+ if (needs_callstack)
+ key.stack_id = pelem->stack_id;
break;
default:
/* should not happen */
diff --git a/tools/perf/util/bpf_skel/lock_data.h b/tools/perf/util/bpf_skel/lock_data.h
index ce71cf1a7e1e..3d35fd4407ac 100644
--- a/tools/perf/util/bpf_skel/lock_data.h
+++ b/tools/perf/util/bpf_skel/lock_data.h
@@ -4,7 +4,9 @@
#define UTIL_BPF_SKEL_LOCK_DATA_H
struct contention_key {
- u64 aggr_key; /* can be stack_id, pid or lock addr */
+ u32 stack_id;
+ u32 pid;
+ u64 lock_addr;
};
#define TASK_COMM_LEN 16
diff --git a/tools/perf/util/bpf_skel/off_cpu.bpf.c b/tools/perf/util/bpf_skel/off_cpu.bpf.c
index 38e3b287dbb2..d877a0a9731f 100644
--- a/tools/perf/util/bpf_skel/off_cpu.bpf.c
+++ b/tools/perf/util/bpf_skel/off_cpu.bpf.c
@@ -277,7 +277,7 @@ int on_switch(u64 *ctx)
else
prev_state = get_task_state(prev);
- return off_cpu_stat(ctx, prev, next, prev_state);
+ return off_cpu_stat(ctx, prev, next, prev_state & 0xff);
}
char LICENSE[] SEC("license") = "Dual BSD/GPL";
diff --git a/tools/perf/util/branch.c b/tools/perf/util/branch.c
index 6d38238481d3..378f16a24751 100644
--- a/tools/perf/util/branch.c
+++ b/tools/perf/util/branch.c
@@ -212,3 +212,18 @@ int branch_type_str(struct branch_type_stat *st, char *bf, int size)
return printed;
}
+
+const char *branch_spec_desc(int spec)
+{
+ const char *branch_spec_outcomes[PERF_BR_SPEC_MAX] = {
+ "N/A",
+ "SPEC_WRONG_PATH",
+ "NON_SPEC_CORRECT_PATH",
+ "SPEC_CORRECT_PATH",
+ };
+
+ if (spec >= 0 && spec < PERF_BR_SPEC_MAX)
+ return branch_spec_outcomes[spec];
+
+ return NULL;
+}
diff --git a/tools/perf/util/branch.h b/tools/perf/util/branch.h
index 3ed792db1125..e41bfffe2217 100644
--- a/tools/perf/util/branch.h
+++ b/tools/perf/util/branch.h
@@ -89,4 +89,6 @@ const char *get_branch_type(struct branch_entry *e);
void branch_type_stat_display(FILE *fp, struct branch_type_stat *st);
int branch_type_str(struct branch_type_stat *st, char *bf, int bfsize);
+const char *branch_spec_desc(int spec);
+
#endif /* _PERF_BRANCH_H */
diff --git a/tools/perf/util/cacheline.h b/tools/perf/util/cacheline.h
index dec8c0fb1f4a..fe6d5b60a031 100644
--- a/tools/perf/util/cacheline.h
+++ b/tools/perf/util/cacheline.h
@@ -6,16 +6,31 @@
int __pure cacheline_size(void);
-static inline u64 cl_address(u64 address)
+
+/*
+ * Some architectures have 'Adjacent Cacheline Prefetch' feature,
+ * which performs like the cacheline size being doubled.
+ */
+static inline u64 cl_address(u64 address, bool double_cl)
{
+ u64 size = cacheline_size();
+
+ if (double_cl)
+ size *= 2;
+
/* return the cacheline of the address */
- return (address & ~(cacheline_size() - 1));
+ return (address & ~(size - 1));
}
-static inline u64 cl_offset(u64 address)
+static inline u64 cl_offset(u64 address, bool double_cl)
{
- /* return the cacheline of the address */
- return (address & (cacheline_size() - 1));
+ u64 size = cacheline_size();
+
+ if (double_cl)
+ size *= 2;
+
+ /* return the offset inside cacheline */
+ return (address & (size - 1));
}
#endif // PERF_CACHELINE_H
diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c
index cd978c240e0d..bfb13306d82c 100644
--- a/tools/perf/util/cgroup.c
+++ b/tools/perf/util/cgroup.c
@@ -481,7 +481,6 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str,
nr_cgroups++;
if (metric_events) {
- perf_stat__collect_metric_expr(tmp_list);
if (metricgroup__copy_metric_events(tmp_list, cgrp,
metric_events,
&orig_metric_events) < 0)
diff --git a/tools/perf/util/cputopo.c b/tools/perf/util/cputopo.c
index 1a3ff6449158..e08797c3cdbc 100644
--- a/tools/perf/util/cputopo.c
+++ b/tools/perf/util/cputopo.c
@@ -422,8 +422,6 @@ void numa_topology__delete(struct numa_topology *tp)
static int load_hybrid_node(struct hybrid_topology_node *node,
struct perf_pmu *pmu)
{
- const char *sysfs;
- char path[PATH_MAX];
char *buf = NULL, *p;
FILE *fp;
size_t len = 0;
@@ -432,12 +430,7 @@ static int load_hybrid_node(struct hybrid_topology_node *node,
if (!node->pmu_name)
return -1;
- sysfs = sysfs__mountpoint();
- if (!sysfs)
- goto err;
-
- snprintf(path, PATH_MAX, CPUS_TEMPLATE_CPU, sysfs, pmu->name);
- fp = fopen(path, "r");
+ fp = perf_pmu__open_file(pmu, "cpus");
if (!fp)
goto err;
diff --git a/tools/perf/util/cs-etm-base.c b/tools/perf/util/cs-etm-base.c
index 597542410854..5f48b756c4cf 100644
--- a/tools/perf/util/cs-etm-base.c
+++ b/tools/perf/util/cs-etm-base.c
@@ -36,7 +36,22 @@ static const char * const cs_etmv4_priv_fmts[] = {
[CS_ETMV4_TRCIDR2] = " TRCIDR2 %llx\n",
[CS_ETMV4_TRCIDR8] = " TRCIDR8 %llx\n",
[CS_ETMV4_TRCAUTHSTATUS] = " TRCAUTHSTATUS %llx\n",
- [CS_ETE_TRCDEVARCH] = " TRCDEVARCH %llx\n"
+ [CS_ETMV4_TS_SOURCE] = " TS_SOURCE %lld\n",
+};
+
+static const char * const cs_ete_priv_fmts[] = {
+ [CS_ETM_MAGIC] = " Magic number %llx\n",
+ [CS_ETM_CPU] = " CPU %lld\n",
+ [CS_ETM_NR_TRC_PARAMS] = " NR_TRC_PARAMS %llx\n",
+ [CS_ETE_TRCCONFIGR] = " TRCCONFIGR %llx\n",
+ [CS_ETE_TRCTRACEIDR] = " TRCTRACEIDR %llx\n",
+ [CS_ETE_TRCIDR0] = " TRCIDR0 %llx\n",
+ [CS_ETE_TRCIDR1] = " TRCIDR1 %llx\n",
+ [CS_ETE_TRCIDR2] = " TRCIDR2 %llx\n",
+ [CS_ETE_TRCIDR8] = " TRCIDR8 %llx\n",
+ [CS_ETE_TRCAUTHSTATUS] = " TRCAUTHSTATUS %llx\n",
+ [CS_ETE_TRCDEVARCH] = " TRCDEVARCH %llx\n",
+ [CS_ETE_TS_SOURCE] = " TS_SOURCE %lld\n",
};
static const char * const param_unk_fmt =
@@ -96,19 +111,22 @@ static int cs_etm__print_cpu_metadata_v1(u64 *val, int *offset)
else
fprintf(stdout, cs_etm_priv_fmts[j], val[i]);
}
- } else if (magic == __perf_cs_etmv4_magic || magic == __perf_cs_ete_magic) {
- /*
- * ETE and ETMv4 can be printed in the same block because the number of parameters
- * is saved and they share the list of parameter names. ETE is also only supported
- * in V1 files.
- */
+ } else if (magic == __perf_cs_etmv4_magic) {
for (j = 0; j < total_params; j++, i++) {
/* if newer record - could be excess params */
- if (j >= CS_ETE_PRIV_MAX)
+ if (j >= CS_ETMV4_PRIV_MAX)
fprintf(stdout, param_unk_fmt, j, val[i]);
else
fprintf(stdout, cs_etmv4_priv_fmts[j], val[i]);
}
+ } else if (magic == __perf_cs_ete_magic) {
+ for (j = 0; j < total_params; j++, i++) {
+ /* if newer record - could be excess params */
+ if (j >= CS_ETE_PRIV_MAX)
+ fprintf(stdout, param_unk_fmt, j, val[i]);
+ else
+ fprintf(stdout, cs_ete_priv_fmts[j], val[i]);
+ }
} else {
/* failure - note bad magic value and error out */
fprintf(stdout, magic_unk_fmt, magic);
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 31fa3b45134a..d0e521dfcf35 100644
--- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
+++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
@@ -30,6 +30,15 @@
#endif
#endif
+/*
+ * Assume a maximum of 0.1ns elapsed per instruction. This would be the
+ * case with a theoretical 10GHz core executing 1 instruction per cycle.
+ * Used to estimate the sample time for synthesized instructions because
+ * Coresight only emits a timestamp for a range of instructions rather
+ * than per instruction.
+ */
+const u32 INSTR_PER_NS = 10;
+
struct cs_etm_decoder {
void *data;
void (*packet_printer)(const char *msg);
@@ -112,6 +121,20 @@ int cs_etm_decoder__get_packet(struct cs_etm_packet_queue *packet_queue,
return 1;
}
+/*
+ * Calculate the number of nanoseconds elapsed.
+ *
+ * instr_count is updated in place with the remainder of the instructions
+ * which didn't make up a whole nanosecond.
+ */
+static u32 cs_etm_decoder__dec_instr_count_to_ns(u32 *instr_count)
+{
+ const u32 instr_copy = *instr_count;
+
+ *instr_count %= INSTR_PER_NS;
+ return instr_copy / INSTR_PER_NS;
+}
+
static int cs_etm_decoder__gen_etmv3_config(struct cs_etm_trace_params *params,
ocsd_etmv3_cfg *config)
{
@@ -260,15 +283,17 @@ cs_etm_decoder__do_soft_timestamp(struct cs_etm_queue *etmq,
struct cs_etm_packet_queue *packet_queue,
const uint8_t trace_chan_id)
{
+ u64 estimated_ts;
+
/* No timestamp packet has been received, nothing to do */
- if (!packet_queue->cs_timestamp)
+ if (!packet_queue->next_cs_timestamp)
return OCSD_RESP_CONT;
- packet_queue->cs_timestamp = packet_queue->next_cs_timestamp;
+ estimated_ts = packet_queue->cs_timestamp +
+ cs_etm_decoder__dec_instr_count_to_ns(&packet_queue->instr_count);
- /* Estimate the timestamp for the next range packet */
- packet_queue->next_cs_timestamp += packet_queue->instr_count;
- packet_queue->instr_count = 0;
+ /* Estimated TS can never be higher than the next real one in the trace */
+ packet_queue->cs_timestamp = min(packet_queue->next_cs_timestamp, estimated_ts);
/* Tell the front end which traceid_queue needs attention */
cs_etm__etmq_set_traceid_queue_timestamp(etmq, trace_chan_id);
@@ -283,6 +308,8 @@ cs_etm_decoder__do_hard_timestamp(struct cs_etm_queue *etmq,
const ocsd_trc_index_t indx)
{
struct cs_etm_packet_queue *packet_queue;
+ u64 converted_timestamp;
+ u64 estimated_first_ts;
/* First get the packet queue for this traceID */
packet_queue = cs_etm__etmq_get_packet_queue(etmq, trace_chan_id);
@@ -290,17 +317,28 @@ cs_etm_decoder__do_hard_timestamp(struct cs_etm_queue *etmq,
return OCSD_RESP_FATAL_SYS_ERR;
/*
+ * Coresight timestamps are raw timer values which need to be scaled to ns. Assume
+ * 0 is a bad value so don't try to convert it.
+ */
+ converted_timestamp = elem->timestamp ?
+ cs_etm__convert_sample_time(etmq, elem->timestamp) : 0;
+
+ /*
* 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->cs_timestamp) {
- packet_queue->next_cs_timestamp = elem->timestamp;
+ if (packet_queue->next_cs_timestamp) {
+ /*
+ * What was next is now where new ranges start from, overwriting
+ * any previous estimate in cs_timestamp
+ */
+ packet_queue->cs_timestamp = packet_queue->next_cs_timestamp;
+ packet_queue->next_cs_timestamp = converted_timestamp;
return OCSD_RESP_CONT;
}
-
- if (!elem->timestamp) {
+ if (!converted_timestamp) {
/*
* Zero timestamps can be seen due to misconfiguration or hardware bugs.
* Warn once, and don't try to subtract instr_count as it would result in an
@@ -312,7 +350,7 @@ cs_etm_decoder__do_hard_timestamp(struct cs_etm_queue *etmq,
". Decoding may be improved by prepending 'Z' to your current --itrace arguments.\n",
indx);
- } else if (packet_queue->instr_count > elem->timestamp) {
+ } else if (packet_queue->instr_count / INSTR_PER_NS > converted_timestamp) {
/*
* Sanity check that the elem->timestamp - packet_queue->instr_count would not
* result in an underflow. Warn and clamp at 0 if it would.
@@ -325,11 +363,14 @@ cs_etm_decoder__do_hard_timestamp(struct cs_etm_queue *etmq,
* 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 subtracting the number of instructions
- * executed to the timestamp.
+ * executed to the timestamp. Don't estimate earlier than the last used
+ * timestamp though.
*/
- packet_queue->cs_timestamp = elem->timestamp - packet_queue->instr_count;
+ estimated_first_ts = converted_timestamp -
+ (packet_queue->instr_count / INSTR_PER_NS);
+ packet_queue->cs_timestamp = max(packet_queue->cs_timestamp, estimated_first_ts);
}
- packet_queue->next_cs_timestamp = elem->timestamp;
+ packet_queue->next_cs_timestamp = converted_timestamp;
packet_queue->instr_count = 0;
/* Tell the front end which traceid_queue needs attention */
@@ -342,7 +383,6 @@ cs_etm_decoder__do_hard_timestamp(struct cs_etm_queue *etmq,
static void
cs_etm_decoder__reset_timestamp(struct cs_etm_packet_queue *packet_queue)
{
- packet_queue->cs_timestamp = 0;
packet_queue->next_cs_timestamp = 0;
packet_queue->instr_count = 0;
}
@@ -604,6 +644,9 @@ static ocsd_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer(
case OCSD_GEN_TRC_ELEM_CUSTOM:
case OCSD_GEN_TRC_ELEM_SYNC_MARKER:
case OCSD_GEN_TRC_ELEM_MEMTRANS:
+#if (OCSD_VER_NUM >= 0x010400)
+ case OCSD_GEN_TRC_ELEM_INSTRUMENTATION:
+#endif
default:
break;
}
diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
index 33303d03c2fa..f65bac5ddbdb 100644
--- a/tools/perf/util/cs-etm.c
+++ b/tools/perf/util/cs-etm.c
@@ -35,6 +35,7 @@
#include "tool.h"
#include "thread.h"
#include "thread-stack.h"
+#include "tsc.h"
#include <tools/libc_compat.h>
#include "util/synthetic-events.h"
@@ -46,10 +47,12 @@ struct cs_etm_auxtrace {
struct perf_session *session;
struct machine *machine;
struct thread *unknown_thread;
+ struct perf_tsc_conversion tc;
u8 timeless_decoding;
u8 snapshot_mode;
u8 data_queued;
+ u8 has_virtual_ts; /* Virtual/Kernel timestamps in the trace. */
int num_cpu;
u64 latest_kernel_timestamp;
@@ -464,12 +467,12 @@ static void cs_etm__set_trace_param_ete(struct cs_etm_trace_params *t_params,
u64 **metadata = etm->metadata;
t_params[idx].protocol = CS_ETM_PROTO_ETE;
- t_params[idx].ete.reg_idr0 = metadata[idx][CS_ETMV4_TRCIDR0];
- t_params[idx].ete.reg_idr1 = metadata[idx][CS_ETMV4_TRCIDR1];
- t_params[idx].ete.reg_idr2 = metadata[idx][CS_ETMV4_TRCIDR2];
- t_params[idx].ete.reg_idr8 = metadata[idx][CS_ETMV4_TRCIDR8];
- t_params[idx].ete.reg_configr = metadata[idx][CS_ETMV4_TRCCONFIGR];
- t_params[idx].ete.reg_traceidr = metadata[idx][CS_ETMV4_TRCTRACEIDR];
+ t_params[idx].ete.reg_idr0 = metadata[idx][CS_ETE_TRCIDR0];
+ t_params[idx].ete.reg_idr1 = metadata[idx][CS_ETE_TRCIDR1];
+ t_params[idx].ete.reg_idr2 = metadata[idx][CS_ETE_TRCIDR2];
+ t_params[idx].ete.reg_idr8 = metadata[idx][CS_ETE_TRCIDR8];
+ t_params[idx].ete.reg_configr = metadata[idx][CS_ETE_TRCCONFIGR];
+ t_params[idx].ete.reg_traceidr = metadata[idx][CS_ETE_TRCTRACEIDR];
t_params[idx].ete.reg_devarch = metadata[idx][CS_ETE_TRCDEVARCH];
}
@@ -1161,6 +1164,30 @@ static void cs_etm__copy_insn(struct cs_etm_queue *etmq,
sample->insn_len, (void *)sample->insn);
}
+u64 cs_etm__convert_sample_time(struct cs_etm_queue *etmq, u64 cs_timestamp)
+{
+ struct cs_etm_auxtrace *etm = etmq->etm;
+
+ if (etm->has_virtual_ts)
+ return tsc_to_perf_time(cs_timestamp, &etm->tc);
+ else
+ return cs_timestamp;
+}
+
+static inline u64 cs_etm__resolve_sample_time(struct cs_etm_queue *etmq,
+ struct cs_etm_traceid_queue *tidq)
+{
+ struct cs_etm_auxtrace *etm = etmq->etm;
+ struct cs_etm_packet_queue *packet_queue = &tidq->packet_queue;
+
+ if (etm->timeless_decoding)
+ return 0;
+ else if (etm->has_virtual_ts)
+ return packet_queue->cs_timestamp;
+ else
+ return etm->latest_kernel_timestamp;
+}
+
static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
struct cs_etm_traceid_queue *tidq,
u64 addr, u64 period)
@@ -1174,8 +1201,9 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
event->sample.header.misc = cs_etm__cpu_mode(etmq, addr);
event->sample.header.size = sizeof(struct perf_event_header);
- if (!etm->timeless_decoding)
- sample.time = etm->latest_kernel_timestamp;
+ /* Set time field based on etm auxtrace config. */
+ sample.time = cs_etm__resolve_sample_time(etmq, tidq);
+
sample.ip = addr;
sample.pid = tidq->pid;
sample.tid = tidq->tid;
@@ -1232,8 +1260,9 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq,
event->sample.header.misc = cs_etm__cpu_mode(etmq, ip);
event->sample.header.size = sizeof(struct perf_event_header);
- if (!etm->timeless_decoding)
- sample.time = etm->latest_kernel_timestamp;
+ /* Set time field based on etm auxtrace config. */
+ sample.time = cs_etm__resolve_sample_time(etmq, tidq);
+
sample.ip = ip;
sample.pid = tidq->pid;
sample.tid = tidq->tid;
@@ -2746,12 +2775,42 @@ static int cs_etm__queue_aux_records(struct perf_session *session)
return 0;
}
+#define HAS_PARAM(j, type, param) (metadata[(j)][CS_ETM_NR_TRC_PARAMS] <= \
+ (CS_##type##_##param - CS_ETM_COMMON_BLK_MAX_V1))
+
+/*
+ * Loop through the ETMs and complain if we find at least one where ts_source != 1 (virtual
+ * timestamps).
+ */
+static bool cs_etm__has_virtual_ts(u64 **metadata, int num_cpu)
+{
+ int j;
+
+ for (j = 0; j < num_cpu; j++) {
+ switch (metadata[j][CS_ETM_MAGIC]) {
+ case __perf_cs_etmv4_magic:
+ if (HAS_PARAM(j, ETMV4, TS_SOURCE) || metadata[j][CS_ETMV4_TS_SOURCE] != 1)
+ return false;
+ break;
+ case __perf_cs_ete_magic:
+ if (HAS_PARAM(j, ETE, TS_SOURCE) || metadata[j][CS_ETE_TS_SOURCE] != 1)
+ return false;
+ break;
+ default:
+ /* Unknown / unsupported magic number. */
+ return false;
+ }
+ }
+ return true;
+}
+
int cs_etm__process_auxtrace_info_full(union perf_event *event,
struct perf_session *session)
{
struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
struct cs_etm_auxtrace *etm = NULL;
struct int_node *inode;
+ struct perf_record_time_conv *tc = &session->time_conv;
int event_header_size = sizeof(struct perf_event_header);
int total_size = auxtrace_info->header.size;
int priv_size = 0;
@@ -2886,6 +2945,13 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
etm->auxtrace_type = auxtrace_info->type;
etm->timeless_decoding = cs_etm__is_timeless_decoding(etm);
+ /* Use virtual timestamps if all ETMs report ts_source = 1 */
+ etm->has_virtual_ts = cs_etm__has_virtual_ts(metadata, num_cpu);
+
+ if (!etm->has_virtual_ts)
+ ui__warning("Virtual timestamps are not enabled, or not supported by the traced system.\n"
+ "The time field of the samples will not be set accurately.\n\n");
+
etm->auxtrace.process_event = cs_etm__process_event;
etm->auxtrace.process_auxtrace_event = cs_etm__process_auxtrace_event;
etm->auxtrace.flush_events = cs_etm__flush_events;
@@ -2915,6 +2981,15 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
goto err_delete_thread;
}
+ etm->tc.time_shift = tc->time_shift;
+ etm->tc.time_mult = tc->time_mult;
+ etm->tc.time_zero = tc->time_zero;
+ if (event_contains(*tc, time_cycles)) {
+ etm->tc.time_cycles = tc->time_cycles;
+ etm->tc.time_mask = tc->time_mask;
+ etm->tc.cap_user_time_zero = tc->cap_user_time_zero;
+ etm->tc.cap_user_time_short = tc->cap_user_time_short;
+ }
err = cs_etm__synth_events(etm, session);
if (err)
goto err_delete_thread;
diff --git a/tools/perf/util/cs-etm.h b/tools/perf/util/cs-etm.h
index 5da50d5dae6b..98a4f7113d2f 100644
--- a/tools/perf/util/cs-etm.h
+++ b/tools/perf/util/cs-etm.h
@@ -71,6 +71,7 @@ enum {
CS_ETMV4_TRCIDR2,
CS_ETMV4_TRCIDR8,
CS_ETMV4_TRCAUTHSTATUS,
+ CS_ETMV4_TS_SOURCE,
CS_ETMV4_PRIV_MAX,
};
@@ -82,7 +83,17 @@ enum {
* added in header V1
*/
enum {
- CS_ETE_TRCDEVARCH = CS_ETMV4_PRIV_MAX,
+ /* Dynamic, configurable parameters */
+ CS_ETE_TRCCONFIGR = CS_ETM_COMMON_BLK_MAX_V1,
+ CS_ETE_TRCTRACEIDR,
+ /* RO, taken from sysFS */
+ CS_ETE_TRCIDR0,
+ CS_ETE_TRCIDR1,
+ CS_ETE_TRCIDR2,
+ CS_ETE_TRCIDR8,
+ CS_ETE_TRCAUTHSTATUS,
+ CS_ETE_TRCDEVARCH,
+ CS_ETE_TS_SOURCE,
CS_ETE_PRIV_MAX
};
@@ -181,7 +192,7 @@ struct cs_etm_packet_queue {
u32 head;
u32 tail;
u32 instr_count;
- u64 cs_timestamp;
+ u64 cs_timestamp; /* Timestamp from trace data, converted to ns if possible */
u64 next_cs_timestamp;
struct cs_etm_packet packet_buffer[CS_ETM_PACKET_MAX_BUFFER];
};
@@ -220,6 +231,7 @@ struct cs_etm_packet_queue
*cs_etm__etmq_get_packet_queue(struct cs_etm_queue *etmq, u8 trace_chan_id);
int cs_etm__process_auxtrace_info_full(union perf_event *event __maybe_unused,
struct perf_session *session __maybe_unused);
+u64 cs_etm__convert_sample_time(struct cs_etm_queue *etmq, u64 cs_timestamp);
#else
static inline int
cs_etm__process_auxtrace_info_full(union perf_event *event __maybe_unused,
diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c
index b842273458b8..2b732bccabad 100644
--- a/tools/perf/util/data-convert-bt.c
+++ b/tools/perf/util/data-convert-bt.c
@@ -322,10 +322,8 @@ static int add_tracepoint_field_value(struct ctf_writer *cw,
offset = tmp_val;
len = offset >> 16;
offset &= 0xffff;
-#ifdef HAVE_LIBTRACEEVENT_TEP_FIELD_IS_RELATIVE
- if (flags & TEP_FIELD_IS_RELATIVE)
+ if (tep_field_is_relative(flags))
offset += fmtf->offset + fmtf->size;
-#endif
}
if (flags & TEP_FIELD_IS_ARRAY) {
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index 190e818a0717..88378c4c5dd9 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -19,12 +19,19 @@
#include "debug.h"
#include "print_binary.h"
#include "target.h"
+#include "trace-event.h"
#include "ui/helpline.h"
#include "ui/ui.h"
#include "util/parse-sublevel-options.h"
#include <linux/ctype.h>
+#ifdef HAVE_LIBTRACEEVENT
+#include <traceevent/event-parse.h>
+#else
+#define LIBTRACEEVENT_VERSION 0
+#endif
+
int verbose;
int debug_peo_args;
bool dump_trace = false, quiet = false;
@@ -228,6 +235,14 @@ int perf_debug_option(const char *str)
/* Allow only verbose value in range (0, 10), otherwise set 0. */
verbose = (verbose < 0) || (verbose > 10) ? 0 : verbose;
+#if LIBTRACEEVENT_VERSION >= MAKE_LIBTRACEEVENT_VERSION(1, 3, 0)
+ if (verbose == 1)
+ tep_set_loglevel(TEP_LOG_INFO);
+ else if (verbose == 2)
+ tep_set_loglevel(TEP_LOG_DEBUG);
+ else if (verbose >= 3)
+ tep_set_loglevel(TEP_LOG_ALL);
+#endif
return 0;
}
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 999dd1700502..51e8ce6edddc 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -285,8 +285,6 @@ void evsel__init(struct evsel *evsel,
evsel->sample_size = __evsel__sample_size(attr->sample_type);
evsel__calc_id_pos(evsel);
evsel->cmdline_group_boundary = false;
- evsel->metric_expr = NULL;
- evsel->metric_name = NULL;
evsel->metric_events = NULL;
evsel->per_pkg_mask = NULL;
evsel->collect_stat = false;
@@ -2319,7 +2317,10 @@ u64 evsel__bitfield_swap_branch_flags(u64 value)
* abort:1 //transaction abort
* cycles:16 //cycle count to last branch
* type:4 //branch type
- * reserved:40
+ * spec:2 //branch speculation info
+ * new_type:4 //additional branch type
+ * priv:3 //privilege level
+ * reserved:31
* }
* }
*
@@ -2335,7 +2336,10 @@ u64 evsel__bitfield_swap_branch_flags(u64 value)
new_val |= bitfield_swap(value, 3, 1);
new_val |= bitfield_swap(value, 4, 16);
new_val |= bitfield_swap(value, 20, 4);
- new_val |= bitfield_swap(value, 24, 40);
+ new_val |= bitfield_swap(value, 24, 2);
+ new_val |= bitfield_swap(value, 26, 4);
+ new_val |= bitfield_swap(value, 30, 3);
+ new_val |= bitfield_swap(value, 33, 31);
} else {
new_val = bitfield_swap(value, 63, 1);
new_val |= bitfield_swap(value, 62, 1);
@@ -2343,7 +2347,10 @@ u64 evsel__bitfield_swap_branch_flags(u64 value)
new_val |= bitfield_swap(value, 60, 1);
new_val |= bitfield_swap(value, 44, 16);
new_val |= bitfield_swap(value, 40, 4);
- new_val |= bitfield_swap(value, 0, 40);
+ new_val |= bitfield_swap(value, 38, 2);
+ new_val |= bitfield_swap(value, 34, 4);
+ new_val |= bitfield_swap(value, 31, 3);
+ new_val |= bitfield_swap(value, 0, 31);
}
return new_val;
@@ -2784,10 +2791,8 @@ void *evsel__rawptr(struct evsel *evsel, struct perf_sample *sample, const char
if (field->flags & TEP_FIELD_IS_DYNAMIC) {
offset = *(int *)(sample->raw_data + field->offset);
offset &= 0xffff;
-#ifdef HAVE_LIBTRACEEVENT_TEP_FIELD_IS_RELATIVE
- if (field->flags & TEP_FIELD_IS_RELATIVE)
+ if (tep_field_is_relative(field->flags))
offset += field->offset + field->size;
-#endif
}
return sample->raw_data + offset;
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index d572be41b960..24cb807ef6ce 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -105,8 +105,6 @@ struct evsel {
* metric fields are similar, but needs more care as they can have
* references to other metric (evsel).
*/
- const char * metric_expr;
- const char * metric_name;
struct evsel **metric_events;
struct evsel *metric_leader;
diff --git a/tools/perf/util/evswitch.h b/tools/perf/util/evswitch.h
index fd30460b6218..8ffdbe526d98 100644
--- a/tools/perf/util/evswitch.h
+++ b/tools/perf/util/evswitch.h
@@ -22,9 +22,9 @@ 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"), \
+ "event", "Consider events after the occurrence of this event"), \
OPT_STRING(0, "switch-off", &(evswitch)->off_name, \
- "event", "Stop considering events after the ocurrence of this event"), \
+ "event", "Stop considering events after the occurrence 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")
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index 00dcde35e0d3..c1da20b868db 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -19,6 +19,7 @@
#include <linux/zalloc.h>
#include <ctype.h>
#include <math.h>
+#include "pmu.h"
#ifdef PARSER_DEBUG
extern int expr_debug;
@@ -448,6 +449,10 @@ double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx
result = topology->core_cpus_lists;
goto out;
}
+ if (!strcmp("#slots", literal)) {
+ result = perf_pmu__cpu_slots_per_cycle();
+ goto out;
+ }
pr_err("Unrecognized literal '%s'", literal);
out:
diff --git a/tools/perf/util/expr.h b/tools/perf/util/expr.h
index 029271540fb0..eaa44b24c555 100644
--- a/tools/perf/util/expr.h
+++ b/tools/perf/util/expr.h
@@ -9,6 +9,7 @@ struct expr_scanner_ctx {
char *user_requested_cpu_list;
int runtime;
bool system_wide;
+ bool is_test;
};
struct expr_parse_ctx {
diff --git a/tools/perf/util/expr.l b/tools/perf/util/expr.l
index d47de5f270a8..4fbf353e78e7 100644
--- a/tools/perf/util/expr.l
+++ b/tools/perf/util/expr.l
@@ -87,9 +87,11 @@ static int literal(yyscan_t scanner, const struct expr_scanner_ctx *sctx)
YYSTYPE *yylval = expr_get_lval(scanner);
yylval->num = expr__get_literal(expr_get_text(scanner), sctx);
- if (isnan(yylval->num))
- return EXPR_ERROR;
-
+ if (isnan(yylval->num)) {
+ if (!sctx->is_test)
+ return EXPR_ERROR;
+ yylval->num = 1;
+ }
return LITERAL;
}
%}
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c
index 6d3921627e33..955c1b9dc6a4 100644
--- a/tools/perf/util/intel-pt.c
+++ b/tools/perf/util/intel-pt.c
@@ -5,6 +5,7 @@
*/
#include <inttypes.h>
+#include <linux/perf_event.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
@@ -98,6 +99,10 @@ struct intel_pt {
u64 instructions_sample_type;
u64 instructions_id;
+ bool sample_cycles;
+ u64 cycles_sample_type;
+ u64 cycles_id;
+
bool sample_branches;
u32 branches_filter;
u64 branches_sample_type;
@@ -214,6 +219,8 @@ struct intel_pt_queue {
u64 ipc_cyc_cnt;
u64 last_in_insn_cnt;
u64 last_in_cyc_cnt;
+ u64 last_cy_insn_cnt;
+ u64 last_cy_cyc_cnt;
u64 last_br_insn_cnt;
u64 last_br_cyc_cnt;
unsigned int cbr_seen;
@@ -1319,7 +1326,7 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt,
if (pt->filts.cnt > 0)
params.pgd_ip = intel_pt_pgd_ip;
- if (pt->synth_opts.instructions) {
+ if (pt->synth_opts.instructions || pt->synth_opts.cycles) {
if (pt->synth_opts.period) {
switch (pt->synth_opts.period_type) {
case PERF_ITRACE_PERIOD_INSTRUCTIONS:
@@ -1830,6 +1837,33 @@ static int intel_pt_synth_instruction_sample(struct intel_pt_queue *ptq)
pt->instructions_sample_type);
}
+static int intel_pt_synth_cycle_sample(struct intel_pt_queue *ptq)
+{
+ struct intel_pt *pt = ptq->pt;
+ union perf_event *event = ptq->event_buf;
+ struct perf_sample sample = { .ip = 0, };
+ u64 period = 0;
+
+ if (ptq->sample_ipc)
+ period = ptq->ipc_cyc_cnt - ptq->last_cy_cyc_cnt;
+
+ if (!period || intel_pt_skip_event(pt))
+ return 0;
+
+ intel_pt_prep_sample(pt, ptq, event, &sample);
+
+ sample.id = ptq->pt->cycles_id;
+ sample.stream_id = ptq->pt->cycles_id;
+ sample.period = period;
+
+ sample.cyc_cnt = period;
+ sample.insn_cnt = ptq->ipc_insn_cnt - ptq->last_cy_insn_cnt;
+ ptq->last_cy_insn_cnt = ptq->ipc_insn_cnt;
+ ptq->last_cy_cyc_cnt = ptq->ipc_cyc_cnt;
+
+ return intel_pt_deliver_synth_event(pt, event, &sample, pt->cycles_sample_type);
+}
+
static int intel_pt_synth_transaction_sample(struct intel_pt_queue *ptq)
{
struct intel_pt *pt = ptq->pt;
@@ -2598,10 +2632,17 @@ static int intel_pt_sample(struct intel_pt_queue *ptq)
}
}
- if (pt->sample_instructions && (state->type & INTEL_PT_INSTRUCTION)) {
- err = intel_pt_synth_instruction_sample(ptq);
- if (err)
- return err;
+ if (state->type & INTEL_PT_INSTRUCTION) {
+ if (pt->sample_instructions) {
+ err = intel_pt_synth_instruction_sample(ptq);
+ if (err)
+ return err;
+ }
+ if (pt->sample_cycles) {
+ err = intel_pt_synth_cycle_sample(ptq);
+ if (err)
+ return err;
+ }
}
if (pt->sample_transactions && (state->type & INTEL_PT_TRANSACTION)) {
@@ -3731,6 +3772,22 @@ static int intel_pt_synth_events(struct intel_pt *pt,
id += 1;
}
+ if (pt->synth_opts.cycles) {
+ attr.config = PERF_COUNT_HW_CPU_CYCLES;
+ if (pt->synth_opts.period_type == PERF_ITRACE_PERIOD_NANOSECS)
+ attr.sample_period =
+ intel_pt_ns_to_ticks(pt, pt->synth_opts.period);
+ else
+ attr.sample_period = pt->synth_opts.period;
+ err = intel_pt_synth_event(session, "cycles", &attr, id);
+ if (err)
+ return err;
+ pt->sample_cycles = true;
+ pt->cycles_sample_type = attr.sample_type;
+ pt->cycles_id = id;
+ id += 1;
+ }
+
attr.sample_type &= ~(u64)PERF_SAMPLE_PERIOD;
attr.sample_period = 1;
@@ -4379,6 +4436,12 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
intel_pt_setup_pebs_events(pt);
+ if (perf_data__is_pipe(session->data)) {
+ pr_warning("WARNING: Intel PT with pipe mode is not recommended.\n"
+ " The output cannot relied upon. In particular,\n"
+ " timestamps and the order of events may be incorrect.\n");
+ }
+
if (pt->sampling_mode || list_empty(&session->auxtrace_index))
err = auxtrace_queue_data(session, true, true);
else
diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
index 650ffe336f3a..4e8e243a6e4b 100644
--- a/tools/perf/util/llvm-utils.c
+++ b/tools/perf/util/llvm-utils.c
@@ -531,14 +531,37 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
pr_debug("llvm compiling command template: %s\n", template);
+ /*
+ * Below, substitute control characters for values that can cause the
+ * echo to misbehave, then substitute the values back.
+ */
err = -ENOMEM;
- if (asprintf(&command_echo, "echo -n \"%s\"", template) < 0)
+ if (asprintf(&command_echo, "echo -n \a%s\a", template) < 0)
goto errout;
+#define SWAP_CHAR(a, b) do { if (*p == a) *p = b; } while (0)
+ for (char *p = command_echo; *p; p++) {
+ SWAP_CHAR('<', '\001');
+ SWAP_CHAR('>', '\002');
+ SWAP_CHAR('"', '\003');
+ SWAP_CHAR('\'', '\004');
+ SWAP_CHAR('|', '\005');
+ SWAP_CHAR('&', '\006');
+ SWAP_CHAR('\a', '"');
+ }
err = read_from_pipe(command_echo, (void **) &command_out, NULL);
if (err)
goto errout;
+ for (char *p = command_out; *p; p++) {
+ SWAP_CHAR('\001', '<');
+ SWAP_CHAR('\002', '>');
+ SWAP_CHAR('\003', '"');
+ SWAP_CHAR('\004', '\'');
+ SWAP_CHAR('\005', '|');
+ SWAP_CHAR('\006', '&');
+ }
+#undef SWAP_CHAR
pr_debug("llvm compiling command : %s\n", command_out);
err = read_from_pipe(template, &obj_buf, &obj_buf_sz);
diff --git a/tools/perf/util/lock-contention.h b/tools/perf/util/lock-contention.h
index b99e83fccf5c..040b618b2215 100644
--- a/tools/perf/util/lock-contention.h
+++ b/tools/perf/util/lock-contention.h
@@ -65,6 +65,11 @@ struct lock_stat {
*/
#define MAX_LOCK_DEPTH 48
+struct lock_stat *lock_stat_find(u64 addr);
+struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags);
+
+bool match_callstack_filter(struct machine *machine, u64 *callstack);
+
/*
* struct lock_seq_stat:
* Place to put on state of one lock sequence
@@ -128,6 +133,8 @@ struct lock_contention {
int max_stack;
int stack_skip;
int aggr_mode;
+ int owner;
+ bool save_callstack;
};
#ifdef HAVE_BPF_SKEL
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index b9c273ed080a..f3559be95541 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -167,14 +167,14 @@ static void metricgroup___watchdog_constraint_hint(const char *name, bool foot)
" echo 1 > /proc/sys/kernel/nmi_watchdog\n");
}
-static bool metricgroup__has_constraint(const struct pmu_event *pe)
+static bool metricgroup__has_constraint(const struct pmu_metric *pm)
{
- if (!pe->metric_constraint)
+ if (!pm->metric_constraint)
return false;
- if (!strcmp(pe->metric_constraint, "NO_NMI_WATCHDOG") &&
+ if (!strcmp(pm->metric_constraint, "NO_NMI_WATCHDOG") &&
sysctl__nmi_watchdog_enabled()) {
- metricgroup___watchdog_constraint_hint(pe->metric_name, false);
+ metricgroup___watchdog_constraint_hint(pm->metric_name, false);
return true;
}
@@ -193,7 +193,7 @@ static void metric__free(struct metric *m)
free(m);
}
-static struct metric *metric__new(const struct pmu_event *pe,
+static struct metric *metric__new(const struct pmu_metric *pm,
const char *modifier,
bool metric_no_group,
int runtime,
@@ -210,15 +210,15 @@ static struct metric *metric__new(const struct pmu_event *pe,
if (!m->pctx)
goto out_err;
- m->metric_name = pe->metric_name;
+ m->metric_name = pm->metric_name;
m->modifier = NULL;
if (modifier) {
m->modifier = strdup(modifier);
if (!m->modifier)
goto out_err;
}
- m->metric_expr = pe->metric_expr;
- m->metric_unit = pe->unit;
+ m->metric_expr = pm->metric_expr;
+ m->metric_unit = pm->unit;
m->pctx->sctx.user_requested_cpu_list = NULL;
if (user_requested_cpu_list) {
m->pctx->sctx.user_requested_cpu_list = strdup(user_requested_cpu_list);
@@ -227,7 +227,7 @@ static struct metric *metric__new(const struct pmu_event *pe,
}
m->pctx->sctx.runtime = runtime;
m->pctx->sctx.system_wide = system_wide;
- m->has_constraint = metric_no_group || metricgroup__has_constraint(pe);
+ m->has_constraint = metric_no_group || metricgroup__has_constraint(pm);
m->metric_refs = NULL;
m->evlist = NULL;
@@ -348,10 +348,10 @@ static bool match_metric(const char *n, const char *list)
return false;
}
-static bool match_pe_metric(const struct pmu_event *pe, const char *metric)
+static bool match_pm_metric(const struct pmu_metric *pm, const char *metric)
{
- return match_metric(pe->metric_group, metric) ||
- match_metric(pe->metric_name, metric);
+ return match_metric(pm->metric_group, metric) ||
+ match_metric(pm->metric_name, metric);
}
/** struct mep - RB-tree node for building printing information. */
@@ -420,13 +420,13 @@ static struct mep *mep_lookup(struct rblist *groups, const char *metric_group,
return NULL;
}
-static int metricgroup__add_to_mep_groups(const struct pmu_event *pe,
+static int metricgroup__add_to_mep_groups(const struct pmu_metric *pm,
struct rblist *groups)
{
const char *g;
char *omg, *mg;
- mg = strdup(pe->metric_group ?: "No_group");
+ mg = strdup(pm->metric_group ?: "No_group");
if (!mg)
return -ENOMEM;
omg = mg;
@@ -435,15 +435,15 @@ static int metricgroup__add_to_mep_groups(const struct pmu_event *pe,
g = skip_spaces(g);
if (strlen(g))
- me = mep_lookup(groups, g, pe->metric_name);
+ me = mep_lookup(groups, g, pm->metric_name);
else
- me = mep_lookup(groups, "No_group", pe->metric_name);
+ me = mep_lookup(groups, "No_group", pm->metric_name);
if (me) {
- me->metric_desc = pe->desc;
- me->metric_long_desc = pe->long_desc;
- me->metric_expr = pe->metric_expr;
- me->metric_unit = pe->unit;
+ me->metric_desc = pm->desc;
+ me->metric_long_desc = pm->long_desc;
+ me->metric_expr = pm->metric_expr;
+ me->metric_unit = pm->unit;
}
}
free(omg);
@@ -452,64 +452,61 @@ static int metricgroup__add_to_mep_groups(const struct pmu_event *pe,
}
struct metricgroup_iter_data {
- pmu_event_iter_fn fn;
+ pmu_metric_iter_fn fn;
void *data;
};
-static int metricgroup__sys_event_iter(const struct pmu_event *pe,
- const struct pmu_events_table *table,
+static int metricgroup__sys_event_iter(const struct pmu_metric *pm,
+ const struct pmu_metrics_table *table,
void *data)
{
struct metricgroup_iter_data *d = data;
struct perf_pmu *pmu = NULL;
- if (!pe->metric_expr || !pe->compat)
+ if (!pm->metric_expr || !pm->compat)
return 0;
while ((pmu = perf_pmu__scan(pmu))) {
- if (!pmu->id || strcmp(pmu->id, pe->compat))
+ if (!pmu->id || strcmp(pmu->id, pm->compat))
continue;
- return d->fn(pe, table, d->data);
+ return d->fn(pm, table, d->data);
}
return 0;
}
-static int metricgroup__add_to_mep_groups_callback(const struct pmu_event *pe,
- const struct pmu_events_table *table __maybe_unused,
- void *vdata)
+static int metricgroup__add_to_mep_groups_callback(const struct pmu_metric *pm,
+ const struct pmu_metrics_table *table __maybe_unused,
+ void *vdata)
{
struct rblist *groups = vdata;
- if (!pe->metric_name)
- return 0;
-
- return metricgroup__add_to_mep_groups(pe, groups);
+ return metricgroup__add_to_mep_groups(pm, groups);
}
void metricgroup__print(const struct print_callbacks *print_cb, void *print_state)
{
struct rblist groups;
- const struct pmu_events_table *table;
+ const struct pmu_metrics_table *table;
struct rb_node *node, *next;
rblist__init(&groups);
groups.node_new = mep_new;
groups.node_cmp = mep_cmp;
groups.node_delete = mep_delete;
- table = pmu_events_table__find();
+ table = pmu_metrics_table__find();
if (table) {
- pmu_events_table_for_each_event(table,
- metricgroup__add_to_mep_groups_callback,
- &groups);
+ pmu_metrics_table_for_each_metric(table,
+ metricgroup__add_to_mep_groups_callback,
+ &groups);
}
{
struct metricgroup_iter_data data = {
.fn = metricgroup__add_to_mep_groups_callback,
.data = &groups,
};
- pmu_for_each_sys_event(metricgroup__sys_event_iter, &data);
+ pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data);
}
for (node = rb_first_cached(&groups.entries); node; node = next) {
@@ -743,7 +740,7 @@ static int metricgroup__build_event_string(struct strbuf *events,
#undef RETURN_IF_NON_ZERO
}
-int __weak arch_get_runtimeparam(const struct pmu_event *pe __maybe_unused)
+int __weak arch_get_runtimeparam(const struct pmu_metric *pm __maybe_unused)
{
return 1;
}
@@ -768,22 +765,22 @@ struct metricgroup_add_iter_data {
bool system_wide;
struct metric *root_metric;
const struct visited_metric *visited;
- const struct pmu_events_table *table;
+ const struct pmu_metrics_table *table;
};
static bool metricgroup__find_metric(const char *metric,
- const struct pmu_events_table *table,
- struct pmu_event *pe);
+ const struct pmu_metrics_table *table,
+ struct pmu_metric *pm);
static int add_metric(struct list_head *metric_list,
- const struct pmu_event *pe,
+ const struct pmu_metric *pm,
const char *modifier,
bool metric_no_group,
const char *user_requested_cpu_list,
bool system_wide,
struct metric *root_metric,
const struct visited_metric *visited,
- const struct pmu_events_table *table);
+ const struct pmu_metrics_table *table);
/**
* resolve_metric - Locate metrics within the root metric and recursively add
@@ -810,16 +807,16 @@ static int resolve_metric(struct list_head *metric_list,
bool system_wide,
struct metric *root_metric,
const struct visited_metric *visited,
- const struct pmu_events_table *table)
+ const struct pmu_metrics_table *table)
{
struct hashmap_entry *cur;
size_t bkt;
struct to_resolve {
/* The metric to resolve. */
- struct pmu_event pe;
+ struct pmu_metric pm;
/*
* The key in the IDs map, this may differ from in case,
- * etc. from pe->metric_name.
+ * etc. from pm->metric_name.
*/
const char *key;
} *pending = NULL;
@@ -830,15 +827,15 @@ static int resolve_metric(struct list_head *metric_list,
* the pending array.
*/
hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) {
- struct pmu_event pe;
+ struct pmu_metric pm;
- if (metricgroup__find_metric(cur->pkey, table, &pe)) {
+ if (metricgroup__find_metric(cur->pkey, table, &pm)) {
pending = realloc(pending,
(pending_cnt + 1) * sizeof(struct to_resolve));
if (!pending)
return -ENOMEM;
- memcpy(&pending[pending_cnt].pe, &pe, sizeof(pe));
+ memcpy(&pending[pending_cnt].pm, &pm, sizeof(pm));
pending[pending_cnt].key = cur->pkey;
pending_cnt++;
}
@@ -853,7 +850,7 @@ static int resolve_metric(struct list_head *metric_list,
* context.
*/
for (i = 0; i < pending_cnt; i++) {
- ret = add_metric(metric_list, &pending[i].pe, modifier, metric_no_group,
+ ret = add_metric(metric_list, &pending[i].pm, modifier, metric_no_group,
user_requested_cpu_list, system_wide, root_metric, visited,
table);
if (ret)
@@ -867,7 +864,7 @@ static int resolve_metric(struct list_head *metric_list,
/**
* __add_metric - Add a metric to metric_list.
* @metric_list: The list the metric is added to.
- * @pe: The pmu_event containing the metric to be added.
+ * @pm: The pmu_metric containing the metric to be added.
* @modifier: if non-null event modifiers like "u".
* @metric_no_group: Should events written to events be grouped "{}" or
* global. Grouping is the default but due to multiplexing the
@@ -884,7 +881,7 @@ static int resolve_metric(struct list_head *metric_list,
* architecture perf is running upon.
*/
static int __add_metric(struct list_head *metric_list,
- const struct pmu_event *pe,
+ const struct pmu_metric *pm,
const char *modifier,
bool metric_no_group,
int runtime,
@@ -892,19 +889,19 @@ static int __add_metric(struct list_head *metric_list,
bool system_wide,
struct metric *root_metric,
const struct visited_metric *visited,
- const struct pmu_events_table *table)
+ const struct pmu_metrics_table *table)
{
const struct visited_metric *vm;
int ret;
bool is_root = !root_metric;
struct visited_metric visited_node = {
- .name = pe->metric_name,
+ .name = pm->metric_name,
.parent = visited,
};
for (vm = visited; vm; vm = vm->parent) {
- if (!strcmp(pe->metric_name, vm->name)) {
- pr_err("failed: recursion detected for %s\n", pe->metric_name);
+ if (!strcmp(pm->metric_name, vm->name)) {
+ pr_err("failed: recursion detected for %s\n", pm->metric_name);
return -1;
}
}
@@ -914,7 +911,7 @@ static int __add_metric(struct list_head *metric_list,
* This metric is the root of a tree and may reference other
* metrics that are added recursively.
*/
- root_metric = metric__new(pe, modifier, metric_no_group, runtime,
+ root_metric = metric__new(pm, modifier, metric_no_group, runtime,
user_requested_cpu_list, system_wide);
if (!root_metric)
return -ENOMEM;
@@ -929,7 +926,7 @@ static int __add_metric(struct list_head *metric_list,
*/
if (root_metric->metric_refs) {
for (; root_metric->metric_refs[cnt].metric_name; cnt++) {
- if (!strcmp(pe->metric_name,
+ if (!strcmp(pm->metric_name,
root_metric->metric_refs[cnt].metric_name))
return 0;
}
@@ -947,8 +944,8 @@ static int __add_metric(struct list_head *metric_list,
* need to change them, so there's no need to create
* our own copy.
*/
- root_metric->metric_refs[cnt].metric_name = pe->metric_name;
- root_metric->metric_refs[cnt].metric_expr = pe->metric_expr;
+ root_metric->metric_refs[cnt].metric_name = pm->metric_name;
+ root_metric->metric_refs[cnt].metric_expr = pm->metric_expr;
/* Null terminate array. */
root_metric->metric_refs[cnt+1].metric_name = NULL;
@@ -959,7 +956,7 @@ static int __add_metric(struct list_head *metric_list,
* For both the parent and referenced metrics, we parse
* all the metric's IDs and add it to the root context.
*/
- if (expr__find_ids(pe->metric_expr, NULL, root_metric->pctx) < 0) {
+ if (expr__find_ids(pm->metric_expr, NULL, root_metric->pctx) < 0) {
/* Broken metric. */
ret = -EINVAL;
} else {
@@ -981,57 +978,57 @@ static int __add_metric(struct list_head *metric_list,
struct metricgroup__find_metric_data {
const char *metric;
- struct pmu_event *pe;
+ struct pmu_metric *pm;
};
-static int metricgroup__find_metric_callback(const struct pmu_event *pe,
- const struct pmu_events_table *table __maybe_unused,
+static int metricgroup__find_metric_callback(const struct pmu_metric *pm,
+ const struct pmu_metrics_table *table __maybe_unused,
void *vdata)
{
struct metricgroup__find_metric_data *data = vdata;
- if (!match_metric(pe->metric_name, data->metric))
+ if (!match_metric(pm->metric_name, data->metric))
return 0;
- memcpy(data->pe, pe, sizeof(*pe));
+ memcpy(data->pm, pm, sizeof(*pm));
return 1;
}
static bool metricgroup__find_metric(const char *metric,
- const struct pmu_events_table *table,
- struct pmu_event *pe)
+ const struct pmu_metrics_table *table,
+ struct pmu_metric *pm)
{
struct metricgroup__find_metric_data data = {
.metric = metric,
- .pe = pe,
+ .pm = pm,
};
- return pmu_events_table_for_each_event(table, metricgroup__find_metric_callback, &data)
+ return pmu_metrics_table_for_each_metric(table, metricgroup__find_metric_callback, &data)
? true : false;
}
static int add_metric(struct list_head *metric_list,
- const struct pmu_event *pe,
+ const struct pmu_metric *pm,
const char *modifier,
bool metric_no_group,
const char *user_requested_cpu_list,
bool system_wide,
struct metric *root_metric,
const struct visited_metric *visited,
- const struct pmu_events_table *table)
+ const struct pmu_metrics_table *table)
{
int ret = 0;
- pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name);
+ pr_debug("metric expr %s for %s\n", pm->metric_expr, pm->metric_name);
- if (!strstr(pe->metric_expr, "?")) {
- ret = __add_metric(metric_list, pe, modifier, metric_no_group, 0,
+ if (!strstr(pm->metric_expr, "?")) {
+ ret = __add_metric(metric_list, pm, modifier, metric_no_group, 0,
user_requested_cpu_list, system_wide, root_metric,
visited, table);
} else {
int j, count;
- count = arch_get_runtimeparam(pe);
+ count = arch_get_runtimeparam(pm);
/* This loop is added to create multiple
* events depend on count value and add
@@ -1039,7 +1036,7 @@ static int add_metric(struct list_head *metric_list,
*/
for (j = 0; j < count && !ret; j++)
- ret = __add_metric(metric_list, pe, modifier, metric_no_group, j,
+ ret = __add_metric(metric_list, pm, modifier, metric_no_group, j,
user_requested_cpu_list, system_wide,
root_metric, visited, table);
}
@@ -1047,17 +1044,17 @@ static int add_metric(struct list_head *metric_list,
return ret;
}
-static int metricgroup__add_metric_sys_event_iter(const struct pmu_event *pe,
- const struct pmu_events_table *table __maybe_unused,
- void *data)
+static int metricgroup__add_metric_sys_event_iter(const struct pmu_metric *pm,
+ const struct pmu_metrics_table *table __maybe_unused,
+ void *data)
{
struct metricgroup_add_iter_data *d = data;
int ret;
- if (!match_pe_metric(pe, d->metric_name))
+ if (!match_pm_metric(pm, d->metric_name))
return 0;
- ret = add_metric(d->metric_list, pe, d->modifier, d->metric_no_group,
+ ret = add_metric(d->metric_list, pm, d->modifier, d->metric_no_group,
d->user_requested_cpu_list, d->system_wide,
d->root_metric, d->visited, d->table);
if (ret)
@@ -1107,19 +1104,19 @@ struct metricgroup__add_metric_data {
bool has_match;
};
-static int metricgroup__add_metric_callback(const struct pmu_event *pe,
- const struct pmu_events_table *table,
+static int metricgroup__add_metric_callback(const struct pmu_metric *pm,
+ const struct pmu_metrics_table *table,
void *vdata)
{
struct metricgroup__add_metric_data *data = vdata;
int ret = 0;
- if (pe->metric_expr &&
- (match_metric(pe->metric_group, data->metric_name) ||
- match_metric(pe->metric_name, data->metric_name))) {
+ if (pm->metric_expr &&
+ (match_metric(pm->metric_group, data->metric_name) ||
+ match_metric(pm->metric_name, data->metric_name))) {
data->has_match = true;
- ret = add_metric(data->list, pe, data->modifier, data->metric_no_group,
+ ret = add_metric(data->list, pm, data->modifier, data->metric_no_group,
data->user_requested_cpu_list, data->system_wide,
/*root_metric=*/NULL, /*visited_metrics=*/NULL, table);
}
@@ -1146,7 +1143,7 @@ static int metricgroup__add_metric(const char *metric_name, const char *modifier
const char *user_requested_cpu_list,
bool system_wide,
struct list_head *metric_list,
- const struct pmu_events_table *table)
+ const struct pmu_metrics_table *table)
{
LIST_HEAD(list);
int ret;
@@ -1166,8 +1163,8 @@ static int metricgroup__add_metric(const char *metric_name, const char *modifier
* Iterate over all metrics seeing if metric matches either the
* name or group. When it does add the metric to the list.
*/
- ret = pmu_events_table_for_each_event(table, metricgroup__add_metric_callback,
- &data);
+ ret = pmu_metrics_table_for_each_metric(table, metricgroup__add_metric_callback,
+ &data);
if (ret)
goto out;
@@ -1189,7 +1186,7 @@ static int metricgroup__add_metric(const char *metric_name, const char *modifier
},
};
- pmu_for_each_sys_event(metricgroup__sys_event_iter, &data);
+ pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data);
}
/* End of pmu events. */
if (!has_match)
@@ -1222,7 +1219,7 @@ out:
static int metricgroup__add_metric_list(const char *list, bool metric_no_group,
const char *user_requested_cpu_list,
bool system_wide, struct list_head *metric_list,
- const struct pmu_events_table *table)
+ const struct pmu_metrics_table *table)
{
char *list_itr, *list_copy, *metric_name, *modifier;
int ret, count = 0;
@@ -1432,7 +1429,7 @@ static int parse_groups(struct evlist *perf_evlist, const char *str,
bool system_wide,
struct perf_pmu *fake_pmu,
struct rblist *metric_events_list,
- const struct pmu_events_table *table)
+ const struct pmu_metrics_table *table)
{
struct evlist *combined_evlist = NULL;
LIST_HEAD(metric_list);
@@ -1580,7 +1577,7 @@ int metricgroup__parse_groups(struct evlist *perf_evlist,
bool system_wide,
struct rblist *metric_events)
{
- const struct pmu_events_table *table = pmu_events_table__find();
+ const struct pmu_metrics_table *table = pmu_metrics_table__find();
if (!table)
return -EINVAL;
@@ -1591,7 +1588,7 @@ int metricgroup__parse_groups(struct evlist *perf_evlist,
}
int metricgroup__parse_groups_test(struct evlist *evlist,
- const struct pmu_events_table *table,
+ const struct pmu_metrics_table *table,
const char *str,
bool metric_no_group,
bool metric_no_merge,
@@ -1603,16 +1600,16 @@ int metricgroup__parse_groups_test(struct evlist *evlist,
&perf_pmu__fake, metric_events, table);
}
-static int metricgroup__has_metric_callback(const struct pmu_event *pe,
- const struct pmu_events_table *table __maybe_unused,
+static int metricgroup__has_metric_callback(const struct pmu_metric *pm,
+ const struct pmu_metrics_table *table __maybe_unused,
void *vdata)
{
const char *metric = vdata;
- if (!pe->metric_expr)
+ if (!pm->metric_expr)
return 0;
- if (match_metric(pe->metric_name, metric))
+ if (match_metric(pm->metric_name, metric))
return 1;
return 0;
@@ -1620,13 +1617,13 @@ static int metricgroup__has_metric_callback(const struct pmu_event *pe,
bool metricgroup__has_metric(const char *metric)
{
- const struct pmu_events_table *table = pmu_events_table__find();
+ const struct pmu_metrics_table *table = pmu_metrics_table__find();
if (!table)
return false;
- return pmu_events_table_for_each_event(table, metricgroup__has_metric_callback,
- (void *)metric) ? true : false;
+ return pmu_metrics_table_for_each_metric(table, metricgroup__has_metric_callback,
+ (void *)metric) ? true : false;
}
int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
diff --git a/tools/perf/util/metricgroup.h b/tools/perf/util/metricgroup.h
index 0013cf582173..84030321a057 100644
--- a/tools/perf/util/metricgroup.h
+++ b/tools/perf/util/metricgroup.h
@@ -73,7 +73,7 @@ int metricgroup__parse_groups(struct evlist *perf_evlist,
bool system_wide,
struct rblist *metric_events);
int metricgroup__parse_groups_test(struct evlist *evlist,
- const struct pmu_events_table *table,
+ const struct pmu_metrics_table *table,
const char *str,
bool metric_no_group,
bool metric_no_merge,
@@ -81,7 +81,7 @@ int metricgroup__parse_groups_test(struct evlist *evlist,
void metricgroup__print(const struct print_callbacks *print_cb, void *print_state);
bool metricgroup__has_metric(const char *metric);
-int arch_get_runtimeparam(const struct pmu_event *pe __maybe_unused);
+int arch_get_runtimeparam(const struct pmu_metric *pm);
void metricgroup__rblist_exit(struct rblist *metric_events);
int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 21cce83462b3..0336ff27c15f 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -1570,8 +1570,6 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
evsel->scale = info.scale;
evsel->per_pkg = info.per_pkg;
evsel->snapshot = info.snapshot;
- evsel->metric_expr = info.metric_expr;
- evsel->metric_name = info.metric_name;
return 0;
}
diff --git a/tools/perf/util/pfm.c b/tools/perf/util/pfm.c
index ac3227ba769c..b59ba825ddc9 100644
--- a/tools/perf/util/pfm.c
+++ b/tools/perf/util/pfm.c
@@ -193,8 +193,7 @@ print_libpfm_event(const struct print_callbacks *print_cb, void *print_state,
/*scale_unit=*/NULL,
/*deprecated=*/NULL, "PFM event",
info->desc, /*long_desc=*/NULL,
- /*encoding_desc=*/buf->buf,
- /*metric_name=*/NULL, /*metric_expr=*/NULL);
+ /*encoding_desc=*/buf->buf);
pfm_for_each_event_attr(j, info) {
pfm_event_attr_info_t ainfo;
@@ -224,8 +223,7 @@ print_libpfm_event(const struct print_callbacks *print_cb, void *print_state,
/*scale_unit=*/NULL,
/*deprecated=*/NULL, "PFM event",
ainfo.desc, /*long_desc=*/NULL,
- /*encoding_desc=*/buf->buf,
- /*metric_name=*/NULL, /*metric_expr=*/NULL);
+ /*encoding_desc=*/buf->buf);
}
}
}
diff --git a/tools/perf/util/pmu-hybrid.c b/tools/perf/util/pmu-hybrid.c
index f51ccaac60ee..38628805a952 100644
--- a/tools/perf/util/pmu-hybrid.c
+++ b/tools/perf/util/pmu-hybrid.c
@@ -20,32 +20,15 @@ LIST_HEAD(perf_pmu__hybrid_pmus);
bool perf_pmu__hybrid_mounted(const char *name)
{
- char path[PATH_MAX];
- const char *sysfs;
- FILE *file;
- int n, cpu;
+ int cpu;
+ char pmu_name[PATH_MAX];
+ struct perf_pmu pmu = {.name = pmu_name};
if (strncmp(name, "cpu_", 4))
return false;
- sysfs = sysfs__mountpoint();
- if (!sysfs)
- return false;
-
- snprintf(path, PATH_MAX, CPUS_TEMPLATE_CPU, sysfs, name);
- if (!file_available(path))
- return false;
-
- file = fopen(path, "r");
- if (!file)
- return false;
-
- n = fscanf(file, "%u", &cpu);
- fclose(file);
- if (n <= 0)
- return false;
-
- return true;
+ strlcpy(pmu_name, name, sizeof(pmu_name));
+ return perf_pmu__scan_file(&pmu, "cpus", "%u", &cpu) > 0;
}
struct perf_pmu *perf_pmu__find_hybrid_pmu(const char *name)
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 2bdeb89352e7..c256b29defad 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -19,6 +19,7 @@
#include <regex.h>
#include <perf/cpumap.h>
#include <fnmatch.h>
+#include <math.h>
#include "debug.h"
#include "evsel.h"
#include "pmu.h"
@@ -107,14 +108,10 @@ int perf_pmu__format_parse(char *dir, struct list_head *head)
static int pmu_format(const char *name, struct list_head *format)
{
char path[PATH_MAX];
- const char *sysfs = sysfs__mountpoint();
- if (!sysfs)
+ if (!perf_pmu__pathname_scnprintf(path, sizeof(path), name, "format"))
return -1;
- snprintf(path, PATH_MAX,
- "%s" EVENT_SOURCE_DEVICE_PATH "%s/format", sysfs, name);
-
if (!file_available(path))
return 0;
@@ -283,10 +280,6 @@ static void perf_pmu_update_alias(struct perf_pmu_alias *old,
perf_pmu_assign_str(old->name, "long_desc", &old->long_desc,
&newalias->long_desc);
perf_pmu_assign_str(old->name, "topic", &old->topic, &newalias->topic);
- perf_pmu_assign_str(old->name, "metric_expr", &old->metric_expr,
- &newalias->metric_expr);
- perf_pmu_assign_str(old->name, "metric_name", &old->metric_name,
- &newalias->metric_name);
perf_pmu_assign_str(old->name, "value", &old->str, &newalias->str);
old->scale = newalias->scale;
old->per_pkg = newalias->per_pkg;
@@ -302,8 +295,6 @@ void perf_pmu_free_alias(struct perf_pmu_alias *newalias)
zfree(&newalias->long_desc);
zfree(&newalias->topic);
zfree(&newalias->str);
- zfree(&newalias->metric_expr);
- zfree(&newalias->metric_name);
zfree(&newalias->pmu_name);
parse_events_terms__purge(&newalias->terms);
free(newalias);
@@ -340,16 +331,13 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,
int num;
char newval[256];
char *long_desc = NULL, *topic = NULL, *unit = NULL, *perpkg = NULL,
- *metric_expr = NULL, *metric_name = NULL, *deprecated = NULL,
- *pmu_name = NULL;
+ *deprecated = NULL, *pmu_name = NULL;
if (pe) {
long_desc = (char *)pe->long_desc;
topic = (char *)pe->topic;
unit = (char *)pe->unit;
perpkg = (char *)pe->perpkg;
- metric_expr = (char *)pe->metric_expr;
- metric_name = (char *)pe->metric_name;
deprecated = (char *)pe->deprecated;
pmu_name = (char *)pe->pmu;
}
@@ -404,8 +392,6 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,
perf_pmu__parse_snapshot(alias, dir, name);
}
- alias->metric_expr = metric_expr ? strdup(metric_expr) : NULL;
- alias->metric_name = metric_name ? strdup(metric_name): NULL;
alias->desc = desc ? strdup(desc) : NULL;
alias->long_desc = long_desc ? strdup(long_desc) :
desc ? strdup(desc) : NULL;
@@ -513,14 +499,10 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)
static int pmu_aliases(const char *name, struct list_head *head)
{
char path[PATH_MAX];
- const char *sysfs = sysfs__mountpoint();
- if (!sysfs)
+ if (!perf_pmu__pathname_scnprintf(path, sizeof(path), name, "events"))
return -1;
- snprintf(path, PATH_MAX,
- "%s/bus/event_source/devices/%s/events", sysfs, name);
-
if (!file_available(path))
return 0;
@@ -554,52 +536,16 @@ static int pmu_alias_terms(struct perf_pmu_alias *alias,
return 0;
}
-/*
- * Reading/parsing the default pmu type value, which should be
- * located at:
- * /sys/bus/event_source/devices/<dev>/type as sysfs attribute.
- */
-static int pmu_type(const char *name, __u32 *type)
-{
- char path[PATH_MAX];
- FILE *file;
- int ret = 0;
- const char *sysfs = sysfs__mountpoint();
-
- if (!sysfs)
- return -1;
-
- snprintf(path, PATH_MAX,
- "%s" EVENT_SOURCE_DEVICE_PATH "%s/type", sysfs, name);
-
- if (access(path, R_OK) < 0)
- return -1;
-
- file = fopen(path, "r");
- if (!file)
- return -EINVAL;
-
- if (1 != fscanf(file, "%u", type))
- ret = -1;
-
- fclose(file);
- return ret;
-}
-
/* Add all pmus in sysfs to pmu list: */
static void pmu_read_sysfs(void)
{
char path[PATH_MAX];
DIR *dir;
struct dirent *dent;
- const char *sysfs = sysfs__mountpoint();
- if (!sysfs)
+ if (!perf_pmu__event_source_devices_scnprintf(path, sizeof(path)))
return;
- snprintf(path, PATH_MAX,
- "%s" EVENT_SOURCE_DEVICE_PATH, sysfs);
-
dir = opendir(path);
if (!dir)
return;
@@ -614,45 +560,29 @@ static void pmu_read_sysfs(void)
closedir(dir);
}
-static struct perf_cpu_map *__pmu_cpumask(const char *path)
-{
- FILE *file;
- struct perf_cpu_map *cpus;
-
- file = fopen(path, "r");
- if (!file)
- return NULL;
-
- cpus = perf_cpu_map__read(file);
- fclose(file);
- return cpus;
-}
-
/*
* Uncore PMUs have a "cpumask" file under sysfs. CPU PMUs (e.g. on arm/arm64)
* may have a "cpus" file.
*/
-#define SYS_TEMPLATE_ID "./bus/event_source/devices/%s/identifier"
-#define CPUS_TEMPLATE_UNCORE "%s/bus/event_source/devices/%s/cpumask"
-
static struct perf_cpu_map *pmu_cpumask(const char *name)
{
- char path[PATH_MAX];
struct perf_cpu_map *cpus;
- const char *sysfs = sysfs__mountpoint();
const char *templates[] = {
- CPUS_TEMPLATE_UNCORE,
- CPUS_TEMPLATE_CPU,
+ "cpumask",
+ "cpus",
NULL
};
const char **template;
+ char pmu_name[PATH_MAX];
+ struct perf_pmu pmu = {.name = pmu_name};
+ FILE *file;
- if (!sysfs)
- return NULL;
-
+ strlcpy(pmu_name, name, sizeof(pmu_name));
for (template = templates; *template; template++) {
- snprintf(path, PATH_MAX, *template, sysfs, name);
- cpus = __pmu_cpumask(path);
+ file = perf_pmu__open_file(&pmu, *template);
+ if (!file)
+ continue;
+ cpus = perf_cpu_map__read(file);
if (cpus)
return cpus;
}
@@ -663,13 +593,11 @@ static struct perf_cpu_map *pmu_cpumask(const char *name)
static bool pmu_is_uncore(const char *name)
{
char path[PATH_MAX];
- const char *sysfs;
if (perf_pmu__hybrid_mounted(name))
return false;
- sysfs = sysfs__mountpoint();
- snprintf(path, PATH_MAX, CPUS_TEMPLATE_UNCORE, sysfs, name);
+ perf_pmu__pathname_scnprintf(path, sizeof(path), name, "cpumask");
return file_available(path);
}
@@ -678,9 +606,9 @@ static char *pmu_id(const char *name)
char path[PATH_MAX], *str;
size_t len;
- snprintf(path, PATH_MAX, SYS_TEMPLATE_ID, name);
+ perf_pmu__pathname_scnprintf(path, sizeof(path), name, "identifier");
- if (sysfs__read_str(path, &str, &len) < 0)
+ if (filename__read_str(path, &str, &len) < 0)
return NULL;
str[len - 1] = 0; /* remove line feed */
@@ -696,14 +624,9 @@ static char *pmu_id(const char *name)
static int is_arm_pmu_core(const char *name)
{
char path[PATH_MAX];
- const char *sysfs = sysfs__mountpoint();
- if (!sysfs)
+ if (!perf_pmu__pathname_scnprintf(path, sizeof(path), name, "cpus"))
return 0;
-
- /* Look for cpu sysfs (specific to arm) */
- scnprintf(path, PATH_MAX, "%s/bus/event_source/devices/%s/cpus",
- sysfs, name);
return file_available(path);
}
@@ -729,7 +652,12 @@ char *perf_pmu__getcpuid(struct perf_pmu *pmu)
__weak const struct pmu_events_table *pmu_events_table__find(void)
{
- return perf_pmu__find_table(NULL);
+ return perf_pmu__find_events_table(NULL);
+}
+
+__weak const struct pmu_metrics_table *pmu_metrics_table__find(void)
+{
+ return perf_pmu__find_metrics_table(NULL);
}
/*
@@ -822,9 +750,6 @@ static int pmu_add_cpu_aliases_map_callback(const struct pmu_event *pe,
struct pmu_add_cpu_aliases_map_data *data = vdata;
const char *pname = pe->pmu ? pe->pmu : data->cpu_name;
- if (!pe->name)
- return 0;
-
if (data->pmu->is_uncore && pmu_uncore_alias_match(pname, data->name))
goto new_alias;
@@ -860,7 +785,7 @@ static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu)
{
const struct pmu_events_table *table;
- table = perf_pmu__find_table(pmu);
+ table = perf_pmu__find_events_table(pmu);
if (!table)
return;
@@ -879,12 +804,6 @@ static int pmu_add_sys_aliases_iter_fn(const struct pmu_event *pe,
struct pmu_sys_event_iter_data *idata = data;
struct perf_pmu *pmu = idata->pmu;
- if (!pe->name) {
- if (pe->metric_group || pe->metric_name)
- return 0;
- return -EINVAL;
- }
-
if (!pe->compat || !pe->pmu)
return 0;
@@ -931,16 +850,11 @@ pmu_find_alias_name(const char *name __maybe_unused)
return NULL;
}
-static int pmu_max_precise(const char *name)
+static int pmu_max_precise(struct perf_pmu *pmu)
{
- char path[PATH_MAX];
int max_precise = -1;
- scnprintf(path, PATH_MAX,
- "bus/event_source/devices/%s/caps/max_precise",
- name);
-
- sysfs__read_int(path, &max_precise);
+ perf_pmu__scan_file(pmu, "caps/max_precise", "%d", &max_precise);
return max_precise;
}
@@ -969,11 +883,8 @@ static struct perf_pmu *pmu_lookup(const char *lookup_name)
return NULL;
/*
- * Check the type first to avoid unnecessary work.
+ * Check the aliases first to avoid unnecessary work.
*/
- if (pmu_type(name, &type))
- return NULL;
-
if (pmu_aliases(name, &aliases))
return NULL;
@@ -983,9 +894,14 @@ static struct perf_pmu *pmu_lookup(const char *lookup_name)
pmu->cpus = pmu_cpumask(name);
pmu->name = strdup(name);
+
if (!pmu->name)
goto err;
+ /* Read type, and ensure that type value is successfully assigned (return 1) */
+ if (perf_pmu__scan_file(pmu, "type", "%u", &type) != 1)
+ goto err;
+
alias_name = pmu_find_alias_name(name);
if (alias_name) {
pmu->alias_name = strdup(alias_name);
@@ -997,7 +913,7 @@ static struct perf_pmu *pmu_lookup(const char *lookup_name)
pmu->is_uncore = pmu_is_uncore(name);
if (pmu->is_uncore)
pmu->id = pmu_id(name);
- pmu->max_precise = pmu_max_precise(name);
+ pmu->max_precise = pmu_max_precise(pmu);
pmu_add_cpu_aliases(&aliases, pmu);
pmu_add_sys_aliases(&aliases, pmu);
@@ -1469,8 +1385,6 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
info->unit = NULL;
info->scale = 0.0;
info->snapshot = false;
- info->metric_expr = NULL;
- info->metric_name = NULL;
list_for_each_entry_safe(term, h, head_terms, list) {
alias = pmu_find_alias(pmu, term);
@@ -1486,8 +1400,6 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
if (alias->per_pkg)
info->per_pkg = true;
- info->metric_expr = alias->metric_expr;
- info->metric_name = alias->metric_name;
list_del_init(&term->list);
parse_events_term__delete(term);
@@ -1703,8 +1615,7 @@ void print_pmu_events(const struct print_callbacks *print_cb, void *print_state)
for (j = 0; j < len; j++) {
const char *name, *alias = NULL, *scale_unit = NULL,
*desc = NULL, *long_desc = NULL,
- *encoding_desc = NULL, *topic = NULL,
- *metric_name = NULL, *metric_expr = NULL;
+ *encoding_desc = NULL, *topic = NULL;
bool deprecated = false;
size_t buf_used;
@@ -1742,8 +1653,6 @@ void print_pmu_events(const struct print_callbacks *print_cb, void *print_state)
buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
"%s/%s/", aliases[j].pmu->name,
aliases[j].event->str) + 1;
- metric_name = aliases[j].event->metric_name;
- metric_expr = aliases[j].event->metric_expr;
deprecated = aliases[j].event->deprecated;
}
print_cb->print_event(print_state,
@@ -1756,9 +1665,7 @@ void print_pmu_events(const struct print_callbacks *print_cb, void *print_state)
"Kernel PMU event",
desc,
long_desc,
- encoding_desc,
- metric_name,
- metric_expr);
+ encoding_desc);
}
if (printed && pager_in_use())
printf("\n");
@@ -1783,19 +1690,14 @@ bool pmu_have_event(const char *pname, const char *name)
return false;
}
-static FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name)
+FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name)
{
char path[PATH_MAX];
- const char *sysfs;
- sysfs = sysfs__mountpoint();
- if (!sysfs)
+ if (!perf_pmu__pathname_scnprintf(path, sizeof(path), pmu->name, name) ||
+ !file_available(path))
return NULL;
- snprintf(path, PATH_MAX,
- "%s" EVENT_SOURCE_DEVICE_PATH "%s/%s", sysfs, pmu->name, name);
- if (!file_available(path))
- return NULL;
return fopen(path, "r");
}
@@ -1816,6 +1718,16 @@ int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt,
return ret;
}
+bool perf_pmu__file_exists(struct perf_pmu *pmu, const char *name)
+{
+ char path[PATH_MAX];
+
+ if (!perf_pmu__pathname_scnprintf(path, sizeof(path), pmu->name, name))
+ return false;
+
+ return file_available(path);
+}
+
static int perf_pmu__new_caps(struct list_head *list, char *name, char *value)
{
struct perf_pmu_caps *caps = zalloc(sizeof(*caps));
@@ -1849,7 +1761,6 @@ int perf_pmu__caps_parse(struct perf_pmu *pmu)
{
struct stat st;
char caps_path[PATH_MAX];
- const char *sysfs = sysfs__mountpoint();
DIR *caps_dir;
struct dirent *evt_ent;
@@ -1858,12 +1769,9 @@ int perf_pmu__caps_parse(struct perf_pmu *pmu)
pmu->nr_caps = 0;
- if (!sysfs)
+ if (!perf_pmu__pathname_scnprintf(caps_path, sizeof(caps_path), pmu->name, "caps"))
return -1;
- snprintf(caps_path, PATH_MAX,
- "%s" EVENT_SOURCE_DEVICE_PATH "%s/caps", sysfs, pmu->name);
-
if (stat(caps_path, &st) < 0) {
pmu->caps_initialized = true;
return 0; /* no error if caps does not exist */
@@ -1993,3 +1901,36 @@ int perf_pmu__cpus_match(struct perf_pmu *pmu, struct perf_cpu_map *cpus,
*ucpus_ptr = unmatched_cpus;
return 0;
}
+
+double __weak perf_pmu__cpu_slots_per_cycle(void)
+{
+ return NAN;
+}
+
+int perf_pmu__event_source_devices_scnprintf(char *pathname, size_t size)
+{
+ const char *sysfs = sysfs__mountpoint();
+
+ if (!sysfs)
+ return 0;
+ return scnprintf(pathname, size, "%s/bus/event_source/devices/", sysfs);
+}
+
+/*
+ * Fill 'buf' with the path to a file or folder in 'pmu_name' in
+ * sysfs. For example if pmu_name = "cs_etm" and 'filename' = "format"
+ * then pathname will be filled with
+ * "/sys/bus/event_source/devices/cs_etm/format"
+ *
+ * Return 0 if the sysfs mountpoint couldn't be found or if no
+ * characters were written.
+ */
+int perf_pmu__pathname_scnprintf(char *buf, size_t size,
+ const char *pmu_name, const char *filename)
+{
+ char base_path[PATH_MAX];
+
+ if (!perf_pmu__event_source_devices_scnprintf(base_path, sizeof(base_path)))
+ return 0;
+ return scnprintf(buf, size, "%s%s/%s", base_path, pmu_name, filename);
+}
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 69ca0004f94f..6b770f17eb86 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -7,6 +7,7 @@
#include <linux/perf_event.h>
#include <linux/list.h>
#include <stdbool.h>
+#include <stdio.h>
#include "parse-events.h"
#include "pmu-events/pmu-events.h"
@@ -22,8 +23,6 @@ enum {
};
#define PERF_PMU_FORMAT_BITS 64
-#define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/"
-#define CPUS_TEMPLATE_CPU "%s/bus/event_source/devices/%s/cpus"
#define MAX_PMU_NAME_LEN 128
struct perf_event_attr;
@@ -133,8 +132,6 @@ extern struct perf_pmu perf_pmu__fake;
struct perf_pmu_info {
const char *unit;
- const char *metric_expr;
- const char *metric_name;
double scale;
bool per_pkg;
bool snapshot;
@@ -188,13 +185,6 @@ struct perf_pmu_alias {
* default.
*/
bool deprecated;
- /**
- * @metric_expr: A metric expression associated with an event. Doing
- * this makes little sense due to scale and unit applying to both.
- */
- char *metric_expr;
- /** @metric_name: A name for the metric. unit applying to both. */
- char *metric_name;
/** @pmu_name: The name copied from struct perf_pmu. */
char *pmu_name;
};
@@ -231,6 +221,8 @@ bool pmu_have_event(const char *pname, const char *name);
int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, ...) __scanf(3, 4);
+bool perf_pmu__file_exists(struct perf_pmu *pmu, const char *name);
+
int perf_pmu__test(void);
struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu);
@@ -239,6 +231,7 @@ void pmu_add_cpu_aliases_table(struct list_head *head, struct perf_pmu *pmu,
char *perf_pmu__getcpuid(struct perf_pmu *pmu);
const struct pmu_events_table *pmu_events_table__find(void);
+const struct pmu_metrics_table *pmu_metrics_table__find(void);
bool pmu_uncore_alias_match(const char *pmu_name, const char *name);
void perf_pmu_free_alias(struct perf_pmu_alias *alias);
@@ -259,4 +252,10 @@ int perf_pmu__cpus_match(struct perf_pmu *pmu, struct perf_cpu_map *cpus,
char *pmu_find_real_name(const char *name);
char *pmu_find_alias_name(const char *name);
+double perf_pmu__cpu_slots_per_cycle(void);
+int perf_pmu__event_source_devices_scnprintf(char *pathname, size_t size);
+int perf_pmu__pathname_scnprintf(char *buf, size_t size,
+ const char *pmu_name, const char *filename);
+FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name);
+
#endif /* __PMU_H */
diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c
index 2646ae18d9f9..62e9ea7dcf40 100644
--- a/tools/perf/util/print-events.c
+++ b/tools/perf/util/print-events.c
@@ -101,9 +101,7 @@ void print_tracepoint_events(const struct print_callbacks *print_cb, void *print
"Tracepoint event",
/*desc=*/NULL,
/*long_desc=*/NULL,
- /*encoding_desc=*/NULL,
- /*metric_name=*/NULL,
- /*metric_expr=*/NULL);
+ /*encoding_desc=*/NULL);
}
free(dir_path);
free(evt_namelist);
@@ -195,9 +193,7 @@ void print_sdt_events(const struct print_callbacks *print_cb, void *print_state)
"SDT event",
/*desc=*/NULL,
/*long_desc=*/NULL,
- /*encoding_desc=*/NULL,
- /*metric_name=*/NULL,
- /*metric_expr=*/NULL);
+ /*encoding_desc=*/NULL);
free(evt_name);
}
@@ -255,9 +251,7 @@ int print_hwcache_events(const struct print_callbacks *print_cb, void *print_sta
event_type_descriptors[PERF_TYPE_HW_CACHE],
/*desc=*/NULL,
/*long_desc=*/NULL,
- /*encoding_desc=*/NULL,
- /*metric_name=*/NULL,
- /*metric_expr=*/NULL);
+ /*encoding_desc=*/NULL);
}
strlist__delete(evt_name_list);
return 0;
@@ -277,9 +271,7 @@ void print_tool_events(const struct print_callbacks *print_cb, void *print_state
"Tool event",
/*desc=*/NULL,
/*long_desc=*/NULL,
- /*encoding_desc=*/NULL,
- /*metric_name=*/NULL,
- /*metric_expr=*/NULL);
+ /*encoding_desc=*/NULL);
}
}
@@ -331,9 +323,7 @@ void print_symbol_events(const struct print_callbacks *print_cb, void *print_sta
event_type_descriptors[type],
/*desc=*/NULL,
/*long_desc=*/NULL,
- /*encoding_desc=*/NULL,
- /*metric_name=*/NULL,
- /*metric_expr=*/NULL);
+ /*encoding_desc=*/NULL);
}
strlist__delete(evt_name_list);
}
@@ -364,9 +354,7 @@ void print_events(const struct print_callbacks *print_cb, void *print_state)
event_type_descriptors[PERF_TYPE_RAW],
/*desc=*/NULL,
/*long_desc=*/NULL,
- /*encoding_desc=*/NULL,
- /*metric_name=*/NULL,
- /*metric_expr=*/NULL);
+ /*encoding_desc=*/NULL);
print_cb->print_event(print_state,
/*topic=*/NULL,
@@ -378,9 +366,7 @@ void print_events(const struct print_callbacks *print_cb, void *print_state)
event_type_descriptors[PERF_TYPE_RAW],
"(see 'man perf-list' on how to encode it)",
/*long_desc=*/NULL,
- /*encoding_desc=*/NULL,
- /*metric_name=*/NULL,
- /*metric_expr=*/NULL);
+ /*encoding_desc=*/NULL);
print_cb->print_event(print_state,
/*topic=*/NULL,
@@ -392,9 +378,7 @@ void print_events(const struct print_callbacks *print_cb, void *print_state)
event_type_descriptors[PERF_TYPE_BREAKPOINT],
/*desc=*/NULL,
/*long_desc=*/NULL,
- /*encoding_desc=*/NULL,
- /*metric_name=*/NULL,
- /*metric_expr=*/NULL);
+ /*encoding_desc=*/NULL);
print_tracepoint_events(print_cb, print_state);
diff --git a/tools/perf/util/print-events.h b/tools/perf/util/print-events.h
index c237e53c4487..716dcf4b4859 100644
--- a/tools/perf/util/print-events.h
+++ b/tools/perf/util/print-events.h
@@ -16,8 +16,7 @@ struct print_callbacks {
const char *scale_unit,
bool deprecated, const char *event_type_desc,
const char *desc, const char *long_desc,
- const char *encoding_desc,
- const char *metric_name, const char *metric_expr);
+ const char *encoding_desc);
void (*print_metric)(void *print_state,
const char *group,
const char *name,
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 0c24bc7afbca..881d94f65a6b 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -917,7 +917,7 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
dinfo = open_debuginfo(pev->target, pev->nsi, !need_dwarf);
if (!dinfo) {
if (need_dwarf)
- return -ENOENT;
+ return -ENODATA;
pr_debug("Could not open debuginfo. Try to use symbols.\n");
return 0;
}
@@ -956,7 +956,7 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
if (ntevs == 0) { /* No error but failed to find probe point. */
pr_warning("Probe point '%s' not found.\n",
synthesize_perf_probe_point(&pev->point));
- return -ENOENT;
+ return -ENODEV;
} else if (ntevs < 0) {
/* Error path : ntevs < 0 */
pr_debug("An error occurred in debuginfo analysis (%d).\n", ntevs);
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 212031b97910..42e8b813d010 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -20,6 +20,7 @@
#include "stat.h"
#include "metricgroup.h"
#include "util/env.h"
+#include "util/pmu.h"
#include <internal/lib.h>
#include "util.h"
@@ -76,14 +77,7 @@ const char *perf_env__arch(struct perf_env *env __maybe_unused)
}
/*
- * Add this one here not to drag util/stat-shadow.c
- */
-void perf_stat__collect_metric_expr(struct evlist *evsel_list)
-{
-}
-
-/*
- * This one is needed not to drag the PMU bandwagon, jevents generated
+ * These ones are needed not to drag the PMU bandwagon, jevents generated
* pmu_sys_event_tables, etc and evsel__find_pmu() is used so far just for
* doing per PMU perf_event_attr.exclude_guest handling, not really needed, so
* far, for the perf python binding known usecases, revisit if this become
@@ -94,6 +88,11 @@ struct perf_pmu *evsel__find_pmu(struct evsel *evsel __maybe_unused)
return NULL;
}
+int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, ...)
+{
+ return EOF;
+}
+
/*
* Add this one here not to drag util/metricgroup.c
*/
@@ -442,10 +441,8 @@ tracepoint_field(struct pyrf_event *pe, struct tep_format_field *field)
offset = val;
len = offset >> 16;
offset &= 0xffff;
-#ifdef HAVE_LIBTRACEEVENT_TEP_FIELD_IS_RELATIVE
- if (field->flags & TEP_FIELD_IS_RELATIVE)
+ if (tep_field_is_relative(field->flags))
offset += field->offset + field->size;
-#endif
}
if (field->flags & TEP_FIELD_IS_STRING &&
is_printable_array(data + offset, len)) {
diff --git a/tools/perf/util/sample.h b/tools/perf/util/sample.h
index 60ec79d4eea4..33b08e0ac746 100644
--- a/tools/perf/util/sample.h
+++ b/tools/perf/util/sample.h
@@ -92,7 +92,10 @@ struct perf_sample {
u8 cpumode;
u16 misc;
u16 ins_lat;
- u16 p_stage_cyc;
+ union {
+ u16 p_stage_cyc;
+ u16 retire_lat;
+ };
bool no_hw_idx; /* No hw_idx collected in branch_stack */
char insn[MAX_INSN];
void *raw_data;
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c
index c097b7934fd4..83fd2fd0ba16 100644
--- a/tools/perf/util/scripting-engines/trace-event-perl.c
+++ b/tools/perf/util/scripting-engines/trace-event-perl.c
@@ -393,10 +393,8 @@ static void perl_process_tracepoint(struct perf_sample *sample,
if (field->flags & TEP_FIELD_IS_DYNAMIC) {
offset = *(int *)(data + field->offset);
offset &= 0xffff;
-#ifdef HAVE_LIBTRACEEVENT_TEP_FIELD_IS_RELATIVE
- if (field->flags & TEP_FIELD_IS_RELATIVE)
+ if (tep_field_is_relative(field->flags))
offset += field->offset + field->size;
-#endif
} else
offset = field->offset;
XPUSHs(sv_2mortal(newSVpv((char *)data + offset, 0)));
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index e930f5f1f36d..2c2697c5d025 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -994,10 +994,8 @@ static void python_process_tracepoint(struct perf_sample *sample,
offset = val;
len = offset >> 16;
offset &= 0xffff;
-#ifdef HAVE_LIBTRACEEVENT_TEP_FIELD_IS_RELATIVE
- if (field->flags & TEP_FIELD_IS_RELATIVE)
+ if (tep_field_is_relative(field->flags))
offset += field->offset + field->size;
-#endif
}
if (field->flags & TEP_FIELD_IS_STRING &&
is_printable_array(data + offset, len)) {
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 7c021c6cedb9..749d5b5c135b 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -1180,7 +1180,7 @@ static void branch_stack__printf(struct perf_sample *sample, bool callstack)
struct branch_entry *e = &entries[i];
if (!callstack) {
- printf("..... %2"PRIu64": %016" PRIx64 " -> %016" PRIx64 " %hu cycles %s%s%s%s %x %s\n",
+ printf("..... %2"PRIu64": %016" PRIx64 " -> %016" PRIx64 " %hu cycles %s%s%s%s %x %s %s\n",
i, e->from, e->to,
(unsigned short)e->flags.cycles,
e->flags.mispred ? "M" : " ",
@@ -1188,7 +1188,8 @@ static void branch_stack__printf(struct perf_sample *sample, bool callstack)
e->flags.abort ? "A" : " ",
e->flags.in_tx ? "T" : " ",
(unsigned)e->flags.reserved,
- get_branch_type(e));
+ get_branch_type(e),
+ e->flags.spec ? branch_spec_desc(e->flags.spec) : "");
} else {
if (i == 0) {
printf("..... %2"PRIu64": %016" PRIx64 "\n"
@@ -1699,8 +1700,13 @@ static s64 perf_session__process_user_event(struct perf_session *session,
case PERF_RECORD_AUXTRACE_INFO:
return tool->auxtrace_info(session, event);
case PERF_RECORD_AUXTRACE:
- /* setup for reading amidst mmap */
- lseek(fd, file_offset + event->header.size, SEEK_SET);
+ /*
+ * Setup for reading amidst mmap, but only when we
+ * are in 'file' mode. The 'pipe' fd is in proper
+ * place already.
+ */
+ if (!perf_data__is_pipe(session->data))
+ lseek(fd, file_offset + event->header.size, SEEK_SET);
return tool->auxtrace(session, event);
case PERF_RECORD_AUXTRACE_ERROR:
perf_session__auxtrace_error_inc(session, event);
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 37662cdec5ee..093a0c8b2e3d 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -28,6 +28,7 @@
#include "time-utils.h"
#include "cgroup.h"
#include "machine.h"
+#include "trace-event.h"
#include <linux/kernel.h>
#include <linux/string.h>
@@ -53,6 +54,13 @@ static const char *const dynamic_headers[] = {"local_ins_lat", "ins_lat", "local
static const char *const arch_specific_sort_keys[] = {"local_p_stage_cyc", "p_stage_cyc"};
/*
+ * Some architectures have Adjacent Cacheline Prefetch feature, which
+ * behaves like the cacheline size is doubled. Enable this flag to
+ * check things in double cacheline granularity.
+ */
+bool chk_double_cl;
+
+/*
* Replaces all occurrences of a char used with the:
*
* -t, --field-separator
@@ -1499,8 +1507,8 @@ sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
addr:
/* al_addr does all the right addr - start + offset calculations */
- l = cl_address(left->mem_info->daddr.al_addr);
- r = cl_address(right->mem_info->daddr.al_addr);
+ l = cl_address(left->mem_info->daddr.al_addr, chk_double_cl);
+ r = cl_address(right->mem_info->daddr.al_addr, chk_double_cl);
if (l > r) return -1;
if (l < r) return 1;
@@ -1519,7 +1527,7 @@ static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf,
if (he->mem_info) {
struct map *map = he->mem_info->daddr.ms.map;
- addr = cl_address(he->mem_info->daddr.al_addr);
+ addr = cl_address(he->mem_info->daddr.al_addr, chk_double_cl);
ms = &he->mem_info->daddr.ms;
/* print [s] for shared data mmaps */
@@ -2132,6 +2140,8 @@ static struct sort_dimension common_sort_dimensions[] = {
DIM(SORT_LOCAL_PIPELINE_STAGE_CYC, "local_p_stage_cyc", sort_local_p_stage_cyc),
DIM(SORT_GLOBAL_PIPELINE_STAGE_CYC, "p_stage_cyc", sort_global_p_stage_cyc),
DIM(SORT_ADDR, "addr", sort_addr),
+ DIM(SORT_LOCAL_RETIRE_LAT, "local_retire_lat", sort_local_p_stage_cyc),
+ DIM(SORT_GLOBAL_RETIRE_LAT, "retire_lat", sort_global_p_stage_cyc),
};
#undef DIM
@@ -2667,10 +2677,8 @@ static int64_t __sort__hde_cmp(struct perf_hpp_fmt *fmt,
tep_read_number_field(field, a->raw_data, &dyn);
offset = dyn & 0xffff;
size = (dyn >> 16) & 0xffff;
-#ifdef HAVE_LIBTRACEEVENT_TEP_FIELD_IS_RELATIVE
- if (field->flags & TEP_FIELD_IS_RELATIVE)
+ if (tep_field_is_relative(field->flags))
offset += field->offset + field->size;
-#endif
/* record max width for output */
if (size > hde->dynamic_len)
hde->dynamic_len = size;
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 921715e6aec4..d79a100e5999 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -35,6 +35,7 @@ extern struct sort_entry sort_sym_from;
extern struct sort_entry sort_sym_to;
extern struct sort_entry sort_srcline;
extern const char default_mem_sort_order[];
+extern bool chk_double_cl;
struct res_sample {
u64 time;
@@ -237,6 +238,8 @@ enum sort_type {
SORT_LOCAL_PIPELINE_STAGE_CYC,
SORT_GLOBAL_PIPELINE_STAGE_CYC,
SORT_ADDR,
+ SORT_LOCAL_RETIRE_LAT,
+ SORT_GLOBAL_RETIRE_LAT,
/* branch stack specific sort keys */
__SORT_BRANCH_STACK,
diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c
index 8bd8b0142630..1b5cb20efd23 100644
--- a/tools/perf/util/stat-display.c
+++ b/tools/perf/util/stat-display.c
@@ -787,6 +787,51 @@ static void uniquify_counter(struct perf_stat_config *config, struct evsel *coun
uniquify_event_name(counter);
}
+/**
+ * should_skip_zero_count() - Check if the event should print 0 values.
+ * @config: The perf stat configuration (including aggregation mode).
+ * @counter: The evsel with its associated cpumap.
+ * @id: The aggregation id that is being queried.
+ *
+ * Due to mismatch between the event cpumap or thread-map and the
+ * aggregation mode, sometimes it'd iterate the counter with the map
+ * which does not contain any values.
+ *
+ * For example, uncore events have dedicated CPUs to manage them,
+ * result for other CPUs should be zero and skipped.
+ *
+ * Return: %true if the value should NOT be printed, %false if the value
+ * needs to be printed like "<not counted>" or "<not supported>".
+ */
+static bool should_skip_zero_counter(struct perf_stat_config *config,
+ struct evsel *counter,
+ const struct aggr_cpu_id *id)
+{
+ struct perf_cpu cpu;
+ int idx;
+
+ /*
+ * Skip value 0 when enabling --per-thread globally,
+ * otherwise it will have too many 0 output.
+ */
+ if (config->aggr_mode == AGGR_THREAD && config->system_wide)
+ return true;
+ /*
+ * Skip value 0 when it's an uncore event and the given aggr id
+ * does not belong to the PMU cpumask.
+ */
+ if (!counter->pmu || !counter->pmu->is_uncore)
+ return false;
+
+ perf_cpu_map__for_each_cpu(cpu, idx, counter->pmu->cpus) {
+ struct aggr_cpu_id own_id = config->aggr_get_id(config, cpu);
+
+ if (aggr_cpu_id__equal(id, &own_id))
+ return false;
+ }
+ return true;
+}
+
static void print_counter_aggrdata(struct perf_stat_config *config,
struct evsel *counter, int s,
struct outstate *os)
@@ -814,11 +859,7 @@ static void print_counter_aggrdata(struct perf_stat_config *config,
ena = aggr->counts.ena;
run = aggr->counts.run;
- /*
- * Skip value 0 when enabling --per-thread globally, otherwise it will
- * have too many 0 output.
- */
- if (val == 0 && config->aggr_mode == AGGR_THREAD && config->system_wide)
+ if (val == 0 && should_skip_zero_counter(config, counter, &id))
return;
if (!metric_only) {
diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c
index cadb2df23c87..806b32156459 100644
--- a/tools/perf/util/stat-shadow.c
+++ b/tools/perf/util/stat-shadow.c
@@ -311,7 +311,7 @@ void perf_stat__update_shadow_stats(struct evsel *counter, u64 count,
update_stats(&v->stats, count);
if (counter->metric_leader)
v->metric_total += count;
- } else if (counter->metric_leader) {
+ } else if (counter->metric_leader && !counter->merged_stat) {
v = saved_value_lookup(counter->metric_leader,
map_idx, true, STAT_NONE, 0, st, rsd.cgrp);
v->metric_total += count;
@@ -346,114 +346,6 @@ static const char *get_ratio_color(enum grc_type type, double ratio)
return color;
}
-static struct evsel *perf_stat__find_event(struct evlist *evsel_list,
- const char *name)
-{
- struct evsel *c2;
-
- evlist__for_each_entry (evsel_list, c2) {
- 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 evlist *evsel_list)
-{
- struct evsel *counter, *leader, **metric_events, *oc;
- bool found;
- struct expr_parse_ctx *ctx;
- struct hashmap_entry *cur;
- size_t bkt;
- int i;
-
- ctx = expr__ctx_new();
- if (!ctx) {
- pr_debug("expr__ctx_new failed");
- return;
- }
- evlist__for_each_entry(evsel_list, counter) {
- bool invalid = false;
-
- leader = evsel__leader(counter);
- if (!counter->metric_expr)
- continue;
-
- expr__ctx_clear(ctx);
- metric_events = counter->metric_events;
- if (!metric_events) {
- if (expr__find_ids(counter->metric_expr,
- counter->name,
- ctx) < 0)
- continue;
-
- metric_events = calloc(sizeof(struct evsel *),
- hashmap__size(ctx->ids) + 1);
- if (!metric_events) {
- expr__ctx_free(ctx);
- return;
- }
- counter->metric_events = metric_events;
- }
-
- i = 0;
- hashmap__for_each_entry(ctx->ids, cur, bkt) {
- const char *metric_name = cur->pkey;
-
- found = false;
- if (leader) {
- /* Search in group */
- for_each_group_member (oc, leader) {
- if (!strcasecmp(oc->name,
- metric_name) &&
- !oc->collect_stat) {
- found = true;
- break;
- }
- }
- }
- if (!found) {
- /* Search ignoring groups */
- oc = perf_stat__find_event(evsel_list,
- metric_name);
- }
- if (!oc) {
- /* Deduping one is good enough to handle duplicated PMUs. */
- static char *printed;
-
- /*
- * Adding events automatically would be difficult, because
- * it would risk creating groups that are not schedulable.
- * perf stat doesn't understand all the scheduling constraints
- * of events. So we ask the user instead to add the missing
- * events.
- */
- if (!printed ||
- strcasecmp(printed, metric_name)) {
- fprintf(stderr,
- "Add %s event to groups to get metric expression for %s\n",
- metric_name,
- counter->name);
- free(printed);
- printed = strdup(metric_name);
- }
- invalid = true;
- continue;
- }
- metric_events[i++] = oc;
- oc->collect_stat = true;
- }
- metric_events[i] = NULL;
- if (invalid) {
- free(metric_events);
- counter->metric_events = NULL;
- counter->metric_expr = NULL;
- }
- }
- expr__ctx_free(ctx);
-}
-
static double runtime_stat_avg(struct runtime_stat *st,
enum stat_type type, int map_idx,
struct runtime_stat_data *rsd)
@@ -1299,10 +1191,6 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
color = NULL;
print_metric(config, ctxp, color, "%8.1f%%", "Core Bound",
core_bound * 100.);
- } else if (evsel->metric_expr) {
- generic_metric(config, evsel->metric_expr, evsel->metric_events, NULL,
- evsel->name, evsel->metric_name, NULL, 1,
- map_idx, out, st);
} else if (runtime_stat_n(st, STAT_NSECS, map_idx, &rsd) != 0) {
char unit = ' ';
char unit_buf[10] = "/sec";
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c
index 534d36d26fc3..a07473703c6d 100644
--- a/tools/perf/util/stat.c
+++ b/tools/perf/util/stat.c
@@ -842,11 +842,7 @@ int create_perf_stat_counter(struct evsel *evsel,
if (evsel__is_group_leader(evsel)) {
attr->disabled = 1;
- /*
- * In case of initial_delay we enable tracee
- * events manually.
- */
- if (target__none(target) && !config->initial_delay)
+ if (target__enable_on_exec(target))
attr->enable_on_exec = 1;
}
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h
index 499c3bf81333..bf1794ebc916 100644
--- a/tools/perf/util/stat.h
+++ b/tools/perf/util/stat.h
@@ -166,7 +166,6 @@ struct perf_stat_config {
FILE *output;
unsigned int interval;
unsigned int timeout;
- int initial_delay;
unsigned int unit_width;
unsigned int metric_only_len;
int times;
@@ -257,7 +256,6 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
struct perf_stat_output_ctx *out,
struct rblist *metric_events,
struct runtime_stat *st);
-void perf_stat__collect_metric_expr(struct evlist *);
int evlist__alloc_stats(struct perf_stat_config *config,
struct evlist *evlist, bool alloc_raw);
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 96767d1b3f1c..41882ae8452e 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -323,15 +323,325 @@ static char *demangle_sym(struct dso *dso, int kmodule, const char *elf_name)
return demangled;
}
-#define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \
- for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \
- idx < nr_entries; \
- ++idx, pos = gelf_getrel(reldata, idx, &pos_mem))
+struct rel_info {
+ u32 nr_entries;
+ u32 *sorted;
+ bool is_rela;
+ Elf_Data *reldata;
+ GElf_Rela rela;
+ GElf_Rel rel;
+};
+
+static u32 get_rel_symidx(struct rel_info *ri, u32 idx)
+{
+ idx = ri->sorted ? ri->sorted[idx] : idx;
+ if (ri->is_rela) {
+ gelf_getrela(ri->reldata, idx, &ri->rela);
+ return GELF_R_SYM(ri->rela.r_info);
+ }
+ gelf_getrel(ri->reldata, idx, &ri->rel);
+ return GELF_R_SYM(ri->rel.r_info);
+}
-#define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \
- for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \
- idx < nr_entries; \
- ++idx, pos = gelf_getrela(reldata, idx, &pos_mem))
+static u64 get_rel_offset(struct rel_info *ri, u32 x)
+{
+ if (ri->is_rela) {
+ GElf_Rela rela;
+
+ gelf_getrela(ri->reldata, x, &rela);
+ return rela.r_offset;
+ } else {
+ GElf_Rel rel;
+
+ gelf_getrel(ri->reldata, x, &rel);
+ return rel.r_offset;
+ }
+}
+
+static int rel_cmp(const void *a, const void *b, void *r)
+{
+ struct rel_info *ri = r;
+ u64 a_offset = get_rel_offset(ri, *(const u32 *)a);
+ u64 b_offset = get_rel_offset(ri, *(const u32 *)b);
+
+ return a_offset < b_offset ? -1 : (a_offset > b_offset ? 1 : 0);
+}
+
+static int sort_rel(struct rel_info *ri)
+{
+ size_t sz = sizeof(ri->sorted[0]);
+ u32 i;
+
+ ri->sorted = calloc(ri->nr_entries, sz);
+ if (!ri->sorted)
+ return -1;
+ for (i = 0; i < ri->nr_entries; i++)
+ ri->sorted[i] = i;
+ qsort_r(ri->sorted, ri->nr_entries, sz, rel_cmp, ri);
+ return 0;
+}
+
+/*
+ * For x86_64, the GNU linker is putting IFUNC information in the relocation
+ * addend.
+ */
+static bool addend_may_be_ifunc(GElf_Ehdr *ehdr, struct rel_info *ri)
+{
+ return ehdr->e_machine == EM_X86_64 && ri->is_rela &&
+ GELF_R_TYPE(ri->rela.r_info) == R_X86_64_IRELATIVE;
+}
+
+static bool get_ifunc_name(Elf *elf, struct dso *dso, GElf_Ehdr *ehdr,
+ struct rel_info *ri, char *buf, size_t buf_sz)
+{
+ u64 addr = ri->rela.r_addend;
+ struct symbol *sym;
+ GElf_Phdr phdr;
+
+ if (!addend_may_be_ifunc(ehdr, ri))
+ return false;
+
+ if (elf_read_program_header(elf, addr, &phdr))
+ return false;
+
+ addr -= phdr.p_vaddr - phdr.p_offset;
+
+ sym = dso__find_symbol_nocache(dso, addr);
+
+ /* Expecting the address to be an IFUNC or IFUNC alias */
+ if (!sym || sym->start != addr || (sym->type != STT_GNU_IFUNC && !sym->ifunc_alias))
+ return false;
+
+ snprintf(buf, buf_sz, "%s@plt", sym->name);
+
+ return true;
+}
+
+static void exit_rel(struct rel_info *ri)
+{
+ free(ri->sorted);
+}
+
+static bool get_plt_sizes(struct dso *dso, GElf_Ehdr *ehdr, GElf_Shdr *shdr_plt,
+ u64 *plt_header_size, u64 *plt_entry_size)
+{
+ switch (ehdr->e_machine) {
+ case EM_ARM:
+ *plt_header_size = 20;
+ *plt_entry_size = 12;
+ return true;
+ case EM_AARCH64:
+ *plt_header_size = 32;
+ *plt_entry_size = 16;
+ return true;
+ case EM_SPARC:
+ *plt_header_size = 48;
+ *plt_entry_size = 12;
+ return true;
+ case EM_SPARCV9:
+ *plt_header_size = 128;
+ *plt_entry_size = 32;
+ return true;
+ case EM_386:
+ case EM_X86_64:
+ *plt_entry_size = shdr_plt->sh_entsize;
+ /* Size is 8 or 16, if not, assume alignment indicates size */
+ if (*plt_entry_size != 8 && *plt_entry_size != 16)
+ *plt_entry_size = shdr_plt->sh_addralign == 8 ? 8 : 16;
+ *plt_header_size = *plt_entry_size;
+ break;
+ default: /* FIXME: s390/alpha/mips/parisc/poperpc/sh/xtensa need to be checked */
+ *plt_header_size = shdr_plt->sh_entsize;
+ *plt_entry_size = shdr_plt->sh_entsize;
+ break;
+ }
+ if (*plt_entry_size)
+ return true;
+ pr_debug("Missing PLT entry size for %s\n", dso->long_name);
+ return false;
+}
+
+static bool machine_is_x86(GElf_Half e_machine)
+{
+ return e_machine == EM_386 || e_machine == EM_X86_64;
+}
+
+struct rela_dyn {
+ GElf_Addr offset;
+ u32 sym_idx;
+};
+
+struct rela_dyn_info {
+ struct dso *dso;
+ Elf_Data *plt_got_data;
+ u32 nr_entries;
+ struct rela_dyn *sorted;
+ Elf_Data *dynsym_data;
+ Elf_Data *dynstr_data;
+ Elf_Data *rela_dyn_data;
+};
+
+static void exit_rela_dyn(struct rela_dyn_info *di)
+{
+ free(di->sorted);
+}
+
+static int cmp_offset(const void *a, const void *b)
+{
+ const struct rela_dyn *va = a;
+ const struct rela_dyn *vb = b;
+
+ return va->offset < vb->offset ? -1 : (va->offset > vb->offset ? 1 : 0);
+}
+
+static int sort_rela_dyn(struct rela_dyn_info *di)
+{
+ u32 i, n;
+
+ di->sorted = calloc(di->nr_entries, sizeof(di->sorted[0]));
+ if (!di->sorted)
+ return -1;
+
+ /* Get data for sorting: the offset and symbol index */
+ for (i = 0, n = 0; i < di->nr_entries; i++) {
+ GElf_Rela rela;
+ u32 sym_idx;
+
+ gelf_getrela(di->rela_dyn_data, i, &rela);
+ sym_idx = GELF_R_SYM(rela.r_info);
+ if (sym_idx) {
+ di->sorted[n].sym_idx = sym_idx;
+ di->sorted[n].offset = rela.r_offset;
+ n += 1;
+ }
+ }
+
+ /* Sort by offset */
+ di->nr_entries = n;
+ qsort(di->sorted, n, sizeof(di->sorted[0]), cmp_offset);
+
+ return 0;
+}
+
+static void get_rela_dyn_info(Elf *elf, GElf_Ehdr *ehdr, struct rela_dyn_info *di, Elf_Scn *scn)
+{
+ GElf_Shdr rela_dyn_shdr;
+ GElf_Shdr shdr;
+
+ di->plt_got_data = elf_getdata(scn, NULL);
+
+ scn = elf_section_by_name(elf, ehdr, &rela_dyn_shdr, ".rela.dyn", NULL);
+ if (!scn || !rela_dyn_shdr.sh_link || !rela_dyn_shdr.sh_entsize)
+ return;
+
+ di->nr_entries = rela_dyn_shdr.sh_size / rela_dyn_shdr.sh_entsize;
+ di->rela_dyn_data = elf_getdata(scn, NULL);
+
+ scn = elf_getscn(elf, rela_dyn_shdr.sh_link);
+ if (!scn || !gelf_getshdr(scn, &shdr) || !shdr.sh_link)
+ return;
+
+ di->dynsym_data = elf_getdata(scn, NULL);
+ di->dynstr_data = elf_getdata(elf_getscn(elf, shdr.sh_link), NULL);
+
+ if (!di->plt_got_data || !di->dynstr_data || !di->dynsym_data || !di->rela_dyn_data)
+ return;
+
+ /* Sort into offset order */
+ sort_rela_dyn(di);
+}
+
+/* Get instruction displacement from a plt entry for x86_64 */
+static u32 get_x86_64_plt_disp(const u8 *p)
+{
+ u8 endbr64[] = {0xf3, 0x0f, 0x1e, 0xfa};
+ int n = 0;
+
+ /* Skip endbr64 */
+ if (!memcmp(p, endbr64, sizeof(endbr64)))
+ n += sizeof(endbr64);
+ /* Skip bnd prefix */
+ if (p[n] == 0xf2)
+ n += 1;
+ /* jmp with 4-byte displacement */
+ if (p[n] == 0xff && p[n + 1] == 0x25) {
+ n += 2;
+ /* Also add offset from start of entry to end of instruction */
+ return n + 4 + le32toh(*(const u32 *)(p + n));
+ }
+ return 0;
+}
+
+static bool get_plt_got_name(GElf_Shdr *shdr, size_t i,
+ struct rela_dyn_info *di,
+ char *buf, size_t buf_sz)
+{
+ struct rela_dyn vi, *vr;
+ const char *sym_name;
+ char *demangled;
+ GElf_Sym sym;
+ u32 disp;
+
+ if (!di->sorted)
+ return false;
+
+ disp = get_x86_64_plt_disp(di->plt_got_data->d_buf + i);
+ if (!disp)
+ return false;
+
+ /* Compute target offset of the .plt.got entry */
+ vi.offset = shdr->sh_offset + di->plt_got_data->d_off + i + disp;
+
+ /* Find that offset in .rela.dyn (sorted by offset) */
+ vr = bsearch(&vi, di->sorted, di->nr_entries, sizeof(di->sorted[0]), cmp_offset);
+ if (!vr)
+ return false;
+
+ /* Get the associated symbol */
+ gelf_getsym(di->dynsym_data, vr->sym_idx, &sym);
+ sym_name = elf_sym__name(&sym, di->dynstr_data);
+ demangled = demangle_sym(di->dso, 0, sym_name);
+ if (demangled != NULL)
+ sym_name = demangled;
+
+ snprintf(buf, buf_sz, "%s@plt", sym_name);
+
+ free(demangled);
+
+ return *sym_name;
+}
+
+static int dso__synthesize_plt_got_symbols(struct dso *dso, Elf *elf,
+ GElf_Ehdr *ehdr,
+ char *buf, size_t buf_sz)
+{
+ struct rela_dyn_info di = { .dso = dso };
+ struct symbol *sym;
+ GElf_Shdr shdr;
+ Elf_Scn *scn;
+ int err = -1;
+ size_t i;
+
+ scn = elf_section_by_name(elf, ehdr, &shdr, ".plt.got", NULL);
+ if (!scn || !shdr.sh_entsize)
+ return 0;
+
+ if (ehdr->e_machine == EM_X86_64)
+ get_rela_dyn_info(elf, ehdr, &di, scn);
+
+ for (i = 0; i < shdr.sh_size; i += shdr.sh_entsize) {
+ if (!get_plt_got_name(&shdr, i, &di, buf, buf_sz))
+ snprintf(buf, buf_sz, "offset_%#" PRIx64 "@plt", (u64)shdr.sh_offset + i);
+ sym = symbol__new(shdr.sh_offset + i, shdr.sh_entsize, STB_GLOBAL, STT_FUNC, buf);
+ if (!sym)
+ goto out;
+ symbols__insert(&dso->symbols, sym);
+ }
+ err = 0;
+out:
+ exit_rela_dyn(&di);
+ return err;
+}
/*
* We need to check if we have a .dynsym, so that we can handle the
@@ -342,56 +652,104 @@ static char *demangle_sym(struct dso *dso, int kmodule, const char *elf_name)
*/
int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
{
- uint32_t nr_rel_entries, idx;
+ uint32_t idx;
GElf_Sym sym;
u64 plt_offset, plt_header_size, plt_entry_size;
- GElf_Shdr shdr_plt;
- struct symbol *f;
+ GElf_Shdr shdr_plt, plt_sec_shdr;
+ struct symbol *f, *plt_sym;
GElf_Shdr shdr_rel_plt, shdr_dynsym;
- Elf_Data *reldata, *syms, *symstrs;
+ Elf_Data *syms, *symstrs;
Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym;
- size_t dynsym_idx;
GElf_Ehdr ehdr;
char sympltname[1024];
Elf *elf;
- int nr = 0, symidx, err = 0;
-
- if (!ss->dynsym)
- return 0;
+ int nr = 0, err = -1;
+ struct rel_info ri = { .is_rela = false };
+ bool lazy_plt;
elf = ss->elf;
ehdr = ss->ehdr;
- scn_dynsym = ss->dynsym;
- shdr_dynsym = ss->dynshdr;
- dynsym_idx = ss->dynsym_idx;
+ if (!elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL))
+ return 0;
+
+ /*
+ * A symbol from a previous section (e.g. .init) can have been expanded
+ * by symbols__fixup_end() to overlap .plt. Truncate it before adding
+ * a symbol for .plt header.
+ */
+ f = dso__find_symbol_nocache(dso, shdr_plt.sh_offset);
+ if (f && f->start < shdr_plt.sh_offset && f->end > shdr_plt.sh_offset)
+ f->end = shdr_plt.sh_offset;
+
+ if (!get_plt_sizes(dso, &ehdr, &shdr_plt, &plt_header_size, &plt_entry_size))
+ return 0;
+
+ /* Add a symbol for .plt header */
+ plt_sym = symbol__new(shdr_plt.sh_offset, plt_header_size, STB_GLOBAL, STT_FUNC, ".plt");
+ if (!plt_sym)
+ goto out_elf_end;
+ symbols__insert(&dso->symbols, plt_sym);
- if (scn_dynsym == NULL)
+ /* Only x86 has .plt.got */
+ if (machine_is_x86(ehdr.e_machine) &&
+ dso__synthesize_plt_got_symbols(dso, elf, &ehdr, sympltname, sizeof(sympltname)))
goto out_elf_end;
+ /* Only x86 has .plt.sec */
+ if (machine_is_x86(ehdr.e_machine) &&
+ elf_section_by_name(elf, &ehdr, &plt_sec_shdr, ".plt.sec", NULL)) {
+ if (!get_plt_sizes(dso, &ehdr, &plt_sec_shdr, &plt_header_size, &plt_entry_size))
+ return 0;
+ /* Extend .plt symbol to entire .plt */
+ plt_sym->end = plt_sym->start + shdr_plt.sh_size;
+ /* Use .plt.sec offset */
+ plt_offset = plt_sec_shdr.sh_offset;
+ lazy_plt = false;
+ } else {
+ plt_offset = shdr_plt.sh_offset;
+ lazy_plt = true;
+ }
+
scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
".rela.plt", NULL);
if (scn_plt_rel == NULL) {
scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
".rel.plt", NULL);
if (scn_plt_rel == NULL)
- goto out_elf_end;
+ return 0;
}
- err = -1;
+ if (shdr_rel_plt.sh_type != SHT_RELA &&
+ shdr_rel_plt.sh_type != SHT_REL)
+ return 0;
- if (shdr_rel_plt.sh_link != dynsym_idx)
- goto out_elf_end;
+ if (!shdr_rel_plt.sh_link)
+ return 0;
- if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL)
+ if (shdr_rel_plt.sh_link == ss->dynsym_idx) {
+ scn_dynsym = ss->dynsym;
+ shdr_dynsym = ss->dynshdr;
+ } else if (shdr_rel_plt.sh_link == ss->symtab_idx) {
+ /*
+ * A static executable can have a .plt due to IFUNCs, in which
+ * case .symtab is used not .dynsym.
+ */
+ scn_dynsym = ss->symtab;
+ shdr_dynsym = ss->symshdr;
+ } else {
goto out_elf_end;
+ }
+
+ if (!scn_dynsym)
+ return 0;
/*
* Fetch the relocation section to find the idxes to the GOT
* and the symbols in the .dynsym they refer to.
*/
- reldata = elf_getdata(scn_plt_rel, NULL);
- if (reldata == NULL)
+ ri.reldata = elf_getdata(scn_plt_rel, NULL);
+ if (!ri.reldata)
goto out_elf_end;
syms = elf_getdata(scn_dynsym, NULL);
@@ -409,93 +767,57 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
if (symstrs->d_size == 0)
goto out_elf_end;
- nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize;
- plt_offset = shdr_plt.sh_offset;
- switch (ehdr.e_machine) {
- case EM_ARM:
- plt_header_size = 20;
- plt_entry_size = 12;
- break;
+ ri.nr_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize;
- case EM_AARCH64:
- plt_header_size = 32;
- plt_entry_size = 16;
- break;
+ ri.is_rela = shdr_rel_plt.sh_type == SHT_RELA;
- case EM_SPARC:
- plt_header_size = 48;
- plt_entry_size = 12;
- break;
-
- case EM_SPARCV9:
- plt_header_size = 128;
- plt_entry_size = 32;
- break;
-
- default: /* FIXME: s390/alpha/mips/parisc/poperpc/sh/xtensa need to be checked */
- plt_header_size = shdr_plt.sh_entsize;
- plt_entry_size = shdr_plt.sh_entsize;
- break;
+ if (lazy_plt) {
+ /*
+ * Assume a .plt with the same number of entries as the number
+ * of relocation entries is not lazy and does not have a header.
+ */
+ if (ri.nr_entries * plt_entry_size == shdr_plt.sh_size)
+ dso__delete_symbol(dso, plt_sym);
+ else
+ plt_offset += plt_header_size;
}
- plt_offset += plt_header_size;
-
- if (shdr_rel_plt.sh_type == SHT_RELA) {
- GElf_Rela pos_mem, *pos;
- elf_section__for_each_rela(reldata, pos, pos_mem, idx,
- nr_rel_entries) {
- const char *elf_name = NULL;
- char *demangled = NULL;
- symidx = GELF_R_SYM(pos->r_info);
- gelf_getsym(syms, symidx, &sym);
+ /*
+ * x86 doesn't insert IFUNC relocations in .plt order, so sort to get
+ * back in order.
+ */
+ if (machine_is_x86(ehdr.e_machine) && sort_rel(&ri))
+ goto out_elf_end;
- elf_name = elf_sym__name(&sym, symstrs);
- demangled = demangle_sym(dso, 0, elf_name);
- if (demangled != NULL)
- elf_name = demangled;
- snprintf(sympltname, sizeof(sympltname),
- "%s@plt", elf_name);
- free(demangled);
+ for (idx = 0; idx < ri.nr_entries; idx++) {
+ const char *elf_name = NULL;
+ char *demangled = NULL;
- f = symbol__new(plt_offset, plt_entry_size,
- STB_GLOBAL, STT_FUNC, sympltname);
- if (!f)
- goto out_elf_end;
+ gelf_getsym(syms, get_rel_symidx(&ri, idx), &sym);
- plt_offset += plt_entry_size;
- symbols__insert(&dso->symbols, f);
- ++nr;
- }
- } else if (shdr_rel_plt.sh_type == SHT_REL) {
- GElf_Rel pos_mem, *pos;
- elf_section__for_each_rel(reldata, pos, pos_mem, idx,
- nr_rel_entries) {
- const char *elf_name = NULL;
- char *demangled = NULL;
- symidx = GELF_R_SYM(pos->r_info);
- gelf_getsym(syms, symidx, &sym);
-
- elf_name = elf_sym__name(&sym, symstrs);
- demangled = demangle_sym(dso, 0, elf_name);
- if (demangled != NULL)
- elf_name = demangled;
+ elf_name = elf_sym__name(&sym, symstrs);
+ demangled = demangle_sym(dso, 0, elf_name);
+ if (demangled)
+ elf_name = demangled;
+ if (*elf_name)
+ snprintf(sympltname, sizeof(sympltname), "%s@plt", elf_name);
+ else if (!get_ifunc_name(elf, dso, &ehdr, &ri, sympltname, sizeof(sympltname)))
snprintf(sympltname, sizeof(sympltname),
- "%s@plt", elf_name);
- free(demangled);
+ "offset_%#" PRIx64 "@plt", plt_offset);
+ free(demangled);
- f = symbol__new(plt_offset, plt_entry_size,
- STB_GLOBAL, STT_FUNC, sympltname);
- if (!f)
- goto out_elf_end;
+ f = symbol__new(plt_offset, plt_entry_size, STB_GLOBAL, STT_FUNC, sympltname);
+ if (!f)
+ goto out_elf_end;
- plt_offset += plt_entry_size;
- symbols__insert(&dso->symbols, f);
- ++nr;
- }
+ plt_offset += plt_entry_size;
+ symbols__insert(&dso->symbols, f);
+ ++nr;
}
err = 0;
out_elf_end:
+ exit_rel(&ri);
if (err == 0)
return nr;
pr_debug("%s: problems reading %s PLT info.\n",
@@ -946,8 +1268,9 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
ss->is_64_bit = (gelf_getclass(elf) == ELFCLASS64);
+ ss->symtab_idx = 0;
ss->symtab = elf_section_by_name(elf, &ehdr, &ss->symshdr, ".symtab",
- NULL);
+ &ss->symtab_idx);
if (ss->symshdr.sh_type != SHT_SYMTAB)
ss->symtab = NULL;
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index a3a165ae933a..317c0706852f 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -201,10 +201,14 @@ again:
continue;
if (choose_best_symbol(curr, next) == SYMBOL_A) {
+ if (next->type == STT_GNU_IFUNC)
+ curr->ifunc_alias = true;
rb_erase_cached(&next->rb_node, symbols);
symbol__delete(next);
goto again;
} else {
+ if (curr->type == STT_GNU_IFUNC)
+ next->ifunc_alias = true;
nd = rb_next(&curr->rb_node);
rb_erase_cached(&curr->rb_node, symbols);
symbol__delete(curr);
@@ -556,6 +560,11 @@ struct symbol *dso__find_symbol(struct dso *dso, u64 addr)
return dso->last_find_result.symbol;
}
+struct symbol *dso__find_symbol_nocache(struct dso *dso, u64 addr)
+{
+ return symbols__find(&dso->symbols, addr);
+}
+
struct symbol *dso__first_symbol(struct dso *dso)
{
return symbols__first(&dso->symbols);
@@ -1368,10 +1377,23 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
/* Find the kernel map using the '_stext' symbol */
if (!kallsyms__get_function_start(kallsyms_filename, "_stext", &stext)) {
+ u64 replacement_size = 0;
+
list_for_each_entry(new_map, &md.maps, node) {
- if (stext >= new_map->start && stext < new_map->end) {
+ u64 new_size = new_map->end - new_map->start;
+
+ if (!(stext >= new_map->start && stext < new_map->end))
+ continue;
+
+ /*
+ * On some architectures, ARM64 for example, the kernel
+ * text can get allocated inside of the vmalloc segment.
+ * Select the smallest matching segment, in case stext
+ * falls within more than one in the list.
+ */
+ if (!replacement_map || new_size < replacement_size) {
replacement_map = new_map;
- break;
+ replacement_size = new_size;
}
}
}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index f735108c4d4e..7558735543c2 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -64,6 +64,8 @@ struct symbol {
u8 inlined:1;
/** Has symbol__annotate2 been performed. */
u8 annotate2:1;
+ /** Symbol is an alias of an STT_GNU_IFUNC */
+ u8 ifunc_alias:1;
/** Architecture specific. Unused except on PPC where it holds st_other. */
u8 arch_sym;
/** The name of length namelen associated with the symbol. */
@@ -148,6 +150,7 @@ void dso__delete_symbol(struct dso *dso,
struct symbol *sym);
struct symbol *dso__find_symbol(struct dso *dso, u64 addr);
+struct symbol *dso__find_symbol_nocache(struct dso *dso, u64 addr);
struct symbol *dso__find_symbol_by_name(struct dso *dso, const char *name);
struct symbol *symbol__next_by_name(struct symbol *sym);
diff --git a/tools/perf/util/symsrc.h b/tools/perf/util/symsrc.h
index 2665b4bde751..edf82028c9e6 100644
--- a/tools/perf/util/symsrc.h
+++ b/tools/perf/util/symsrc.h
@@ -26,6 +26,7 @@ struct symsrc {
GElf_Shdr opdshdr;
Elf_Scn *symtab;
+ size_t symtab_idx;
GElf_Shdr symshdr;
Elf_Scn *dynsym;
diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c
index 3ab6a92b1a6d..9ab9308ee80c 100644
--- a/tools/perf/util/synthetic-events.c
+++ b/tools/perf/util/synthetic-events.c
@@ -2219,8 +2219,8 @@ int perf_event__synthesize_build_id(struct perf_tool *tool, struct dso *pos, u16
len = pos->long_name_len + 1;
len = PERF_ALIGN(len, NAME_ALIGN);
- memcpy(&ev.build_id.build_id, pos->bid.data, sizeof(pos->bid.data));
- ev.build_id.size = pos->bid.size;
+ ev.build_id.size = min(pos->bid.size, sizeof(pos->bid.data));
+ memcpy(&ev.build_id.build_id, pos->bid.data, ev.build_id.size);
ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID;
ev.build_id.header.misc = misc | PERF_RECORD_MISC_BUILD_ID_SIZE;
ev.build_id.pid = machine->pid;
diff --git a/tools/perf/util/target.h b/tools/perf/util/target.h
index daec6cba500d..880f1af7f6ad 100644
--- a/tools/perf/util/target.h
+++ b/tools/perf/util/target.h
@@ -18,6 +18,7 @@ struct target {
bool per_thread;
bool use_bpf;
bool hybrid;
+ int initial_delay;
const char *attr_map;
};
@@ -72,6 +73,17 @@ static inline bool target__none(struct target *target)
return !target__has_task(target) && !target__has_cpu(target);
}
+static inline bool target__enable_on_exec(struct target *target)
+{
+ /*
+ * Normally enable_on_exec should be set if:
+ * 1) The tracee process is forked (not attaching to existed task or cpu).
+ * 2) And initial_delay is not configured.
+ * Otherwise, we enable tracee events manually.
+ */
+ return target__none(target) && !target->initial_delay;
+}
+
static inline bool target__has_per_thread(struct target *target)
{
return target->system_wide && target->per_thread;
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index 9b3cd79cca12..a0cff184b1cd 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -21,6 +21,9 @@ struct trace_event {
struct tep_plugin_list *plugin_list;
};
+/* Computes a version number comparable with LIBTRACEEVENT_VERSION from Makefile.config. */
+#define MAKE_LIBTRACEEVENT_VERSION(a, b, c) ((a)*255*255+(b)*255+(c))
+
typedef char *(tep_func_resolver_t)(void *priv,
unsigned long long *addrp, char **modp);
@@ -137,4 +140,20 @@ int common_lock_depth(struct scripting_context *context);
#define SAMPLE_FLAGS_BUF_SIZE 64
int perf_sample__sprintf_flags(u32 flags, char *str, size_t sz);
+#if defined(LIBTRACEEVENT_VERSION) && LIBTRACEEVENT_VERSION >= MAKE_LIBTRACEEVENT_VERSION(1, 5, 0)
+#include <traceevent/event-parse.h>
+
+static inline bool tep_field_is_relative(unsigned long flags)
+{
+ return (flags & TEP_FIELD_IS_RELATIVE) != 0;
+}
+#else
+#include <linux/compiler.h>
+
+static inline bool tep_field_is_relative(unsigned long flags __maybe_unused)
+{
+ return false;
+}
+#endif
+
#endif /* _PERF_UTIL_TRACE_EVENT_H */
diff --git a/tools/power/acpi/tools/pfrut/pfrut.c b/tools/power/acpi/tools/pfrut/pfrut.c
index 52aa0351533c..388c9e3ad040 100644
--- a/tools/power/acpi/tools/pfrut/pfrut.c
+++ b/tools/power/acpi/tools/pfrut/pfrut.c
@@ -97,7 +97,7 @@ static struct option long_options[] = {
static void parse_options(int argc, char **argv)
{
int option_index = 0;
- char *pathname;
+ char *pathname, *endptr;
int opt;
pathname = strdup(argv[0]);
@@ -125,11 +125,23 @@ static void parse_options(int argc, char **argv)
log_getinfo = 1;
break;
case 'T':
- log_type = atoi(optarg);
+ log_type = strtol(optarg, &endptr, 0);
+ if (*endptr || (log_type != 0 && log_type != 1)) {
+ printf("Number expected: type(0:execution, 1:history) - Quit.\n");
+ exit(1);
+ }
+
set_log_type = 1;
break;
case 'L':
- log_level = atoi(optarg);
+ log_level = strtol(optarg, &endptr, 0);
+ if (*endptr ||
+ (log_level != 0 && log_level != 1 &&
+ log_level != 2 && log_level != 4)) {
+ printf("Number expected: level(0, 1, 2, 4) - Quit.\n");
+ exit(1);
+ }
+
set_log_level = 1;
break;
case 'R':
diff --git a/tools/power/pm-graph/sleepgraph.py b/tools/power/pm-graph/sleepgraph.py
index 82c09cd25cc2..bf4ac24a1c7a 100755
--- a/tools/power/pm-graph/sleepgraph.py
+++ b/tools/power/pm-graph/sleepgraph.py
@@ -5556,9 +5556,8 @@ def executeSuspend(quiet=False):
if not quiet:
pprint('CAPTURING TRACE')
op = sv.writeDatafileHeader(sv.ftracefile, testdata)
- fp = open(tp+'trace', 'r')
- for line in fp:
- op.write(line)
+ fp = open(tp+'trace', 'rb')
+ op.write(ascii(fp.read()))
op.close()
sv.fsetVal('', 'trace')
sv.platforminfo(cmdafter)
diff --git a/tools/power/x86/turbostat/turbostat.8 b/tools/power/x86/turbostat/turbostat.8
index c7b26a3603af..8f08c3fd498d 100644
--- a/tools/power/x86/turbostat/turbostat.8
+++ b/tools/power/x86/turbostat/turbostat.8
@@ -340,10 +340,12 @@ starts a new interval.
must be run as root.
Alternatively, non-root users can be enabled to run turbostat this way:
-# setcap cap_sys_admin,cap_sys_rawio,cap_sys_nice=+ep ./turbostat
+# setcap cap_sys_admin,cap_sys_rawio,cap_sys_nice=+ep path/to/turbostat
# chmod +r /dev/cpu/*/msr
+# chmod +r /dev/cpu_dma_latency
+
.B "turbostat "
reads hardware counters, but doesn't write them.
So it will not interfere with the OS or other programs, including
diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c
index aba460410dbd..8a36ba5df9f9 100644
--- a/tools/power/x86/turbostat/turbostat.c
+++ b/tools/power/x86/turbostat/turbostat.c
@@ -3,7 +3,7 @@
* turbostat -- show CPU frequency and C-state residency
* on modern Intel and AMD processors.
*
- * Copyright (c) 2022 Intel Corporation.
+ * Copyright (c) 2023 Intel Corporation.
* Len Brown <len.brown@intel.com>
*/
@@ -670,7 +670,7 @@ static int perf_instr_count_open(int cpu_num)
/* counter for cpu_num, including user + kernel and all processes */
fd = perf_event_open(&pea, -1, cpu_num, -1, 0);
if (fd == -1) {
- warn("cpu%d: perf instruction counter", cpu_num);
+ warnx("capget(CAP_PERFMON) failed, try \"# setcap cap_sys_admin=ep %s\"", progname);
BIC_NOT_PRESENT(BIC_IPC);
}
@@ -2538,7 +2538,7 @@ static void dump_turbo_ratio_limits(int trl_msr_offset, int family, int model)
get_msr(base_cpu, trl_msr_offset, &msr);
fprintf(outf, "cpu%d: MSR_%sTURBO_RATIO_LIMIT: 0x%08llx\n",
- base_cpu, trl_msr_offset == MSR_SECONDARY_TURBO_RATIO_LIMIT ? "SECONDARY" : "", msr);
+ base_cpu, trl_msr_offset == MSR_SECONDARY_TURBO_RATIO_LIMIT ? "SECONDARY_" : "", msr);
if (has_turbo_ratio_group_limits(family, model)) {
get_msr(base_cpu, MSR_TURBO_RATIO_LIMIT1, &core_counts);
@@ -3502,9 +3502,6 @@ release_msr:
/*
* set_my_sched_priority(pri)
* return previous
- *
- * if non-root, do this:
- * # /sbin/setcap cap_sys_rawio,cap_sys_nice=+ep /usr/bin/turbostat
*/
int set_my_sched_priority(int priority)
{
@@ -3518,7 +3515,7 @@ int set_my_sched_priority(int priority)
retval = setpriority(PRIO_PROCESS, 0, priority);
if (retval)
- err(retval, "setpriority(%d)", priority);
+ errx(retval, "capget(CAP_SYS_NICE) failed,try \"# setcap cap_sys_nice=ep %s\"", progname);
errno = 0;
retval = getpriority(PRIO_PROCESS, 0);
@@ -4426,7 +4423,7 @@ int print_hwp(struct thread_data *t, struct core_data *c, struct pkg_data *p)
fprintf(outf, "cpu%d: MSR_HWP_STATUS: 0x%08llx "
"(%sGuaranteed_Perf_Change, %sExcursion_Min)\n",
- cpu, msr, ((msr) & 0x1) ? "" : "No-", ((msr) & 0x2) ? "" : "No-");
+ cpu, msr, ((msr) & 0x1) ? "" : "No-", ((msr) & 0x4) ? "" : "No-");
return 0;
}
@@ -5463,6 +5460,9 @@ unsigned int intel_model_duplicates(unsigned int model)
case INTEL_FAM6_ICELAKE_D:
return INTEL_FAM6_ICELAKE_X;
+
+ case INTEL_FAM6_EMERALDRAPIDS_X:
+ return INTEL_FAM6_SAPPHIRERAPIDS_X;
}
return model;
}
@@ -5476,13 +5476,13 @@ void print_dev_latency(void)
fd = open(path, O_RDONLY);
if (fd < 0) {
- warn("fopen %s\n", path);
+ warnx("capget(CAP_SYS_ADMIN) failed, try \"# setcap cap_sys_admin=ep %s\"", progname);
return;
}
retval = read(fd, (void *)&value, sizeof(int));
if (retval != sizeof(int)) {
- warn("read %s\n", path);
+ warn("read failed %s", path);
close(fd);
return;
}
@@ -5543,7 +5543,7 @@ void process_cpuid()
edx_flags = edx;
if (get_msr(sched_getcpu(), MSR_IA32_UCODE_REV, &ucode_patch))
- warnx("get_msr(UCODE)\n");
+ warnx("get_msr(UCODE)");
/*
* check max extended function levels of CPUID.
@@ -6225,7 +6225,7 @@ int get_and_dump_counters(void)
void print_version()
{
- fprintf(outf, "turbostat version 2022.10.04 - Len Brown <lenb@kernel.org>\n");
+ fprintf(outf, "turbostat version 2023.03.17 - Len Brown <lenb@kernel.org>\n");
}
#define COMMAND_LINE_SIZE 2048
diff --git a/tools/scripts/Makefile.arch b/tools/scripts/Makefile.arch
index 0c6c7f456887..1c72d07cb9fe 100644
--- a/tools/scripts/Makefile.arch
+++ b/tools/scripts/Makefile.arch
@@ -5,7 +5,7 @@ HOSTARCH := $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ \
-e s/s390x/s390/ -e s/parisc64/parisc/ \
-e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
-e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ \
- -e s/riscv.*/riscv/)
+ -e s/riscv.*/riscv/ -e s/loongarch.*/loongarch/)
ifndef ARCH
ARCH := $(HOSTARCH)
@@ -34,6 +34,15 @@ ifeq ($(ARCH),sh64)
SRCARCH := sh
endif
+# Additional ARCH settings for loongarch
+ifeq ($(ARCH),loongarch32)
+ SRCARCH := loongarch
+endif
+
+ifeq ($(ARCH),loongarch64)
+ SRCARCH := loongarch
+endif
+
LP64 := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1)
ifeq ($(LP64), 1)
IS_64_BIT := 1
diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild
index 0805f08af8b3..fba7bec96acd 100644
--- a/tools/testing/cxl/Kbuild
+++ b/tools/testing/cxl/Kbuild
@@ -10,6 +10,7 @@ ldflags-y += --wrap=devm_cxl_add_passthrough_decoder
ldflags-y += --wrap=devm_cxl_enumerate_decoders
ldflags-y += --wrap=cxl_await_media_ready
ldflags-y += --wrap=cxl_hdm_decode_init
+ldflags-y += --wrap=cxl_dvsec_rr_decode
ldflags-y += --wrap=cxl_rcrb_to_component
DRIVERS := ../../../drivers
@@ -17,28 +18,34 @@ CXL_SRC := $(DRIVERS)/cxl
CXL_CORE_SRC := $(DRIVERS)/cxl/core
ccflags-y := -I$(srctree)/drivers/cxl/
ccflags-y += -D__mock=__weak
+ccflags-y += -DTRACE_INCLUDE_PATH=$(CXL_CORE_SRC) -I$(srctree)/drivers/cxl/core/
obj-m += cxl_acpi.o
cxl_acpi-y := $(CXL_SRC)/acpi.o
cxl_acpi-y += mock_acpi.o
cxl_acpi-y += config_check.o
+cxl_acpi-y += cxl_acpi_test.o
obj-m += cxl_pmem.o
cxl_pmem-y := $(CXL_SRC)/pmem.o
cxl_pmem-y += $(CXL_SRC)/security.o
cxl_pmem-y += config_check.o
+cxl_pmem-y += cxl_pmem_test.o
obj-m += cxl_port.o
cxl_port-y := $(CXL_SRC)/port.o
cxl_port-y += config_check.o
+cxl_port-y += cxl_port_test.o
+
obj-m += cxl_mem.o
cxl_mem-y := $(CXL_SRC)/mem.o
cxl_mem-y += config_check.o
+cxl_mem-y += cxl_mem_test.o
obj-m += cxl_core.o
@@ -49,7 +56,9 @@ cxl_core-y += $(CXL_CORE_SRC)/memdev.o
cxl_core-y += $(CXL_CORE_SRC)/mbox.o
cxl_core-y += $(CXL_CORE_SRC)/pci.o
cxl_core-y += $(CXL_CORE_SRC)/hdm.o
+cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o
cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o
cxl_core-y += config_check.o
+cxl_core-y += cxl_core_test.o
obj-m += test/
diff --git a/tools/testing/cxl/config_check.c b/tools/testing/cxl/config_check.c
index c4c457e59841..99b56b5f6edf 100644
--- a/tools/testing/cxl/config_check.c
+++ b/tools/testing/cxl/config_check.c
@@ -7,6 +7,7 @@ void check(void)
* These kconfig symbols must be set to "m" for cxl_test to load
* and operate.
*/
+ BUILD_BUG_ON(!IS_ENABLED(CONFIG_64BIT));
BUILD_BUG_ON(!IS_MODULE(CONFIG_CXL_BUS));
BUILD_BUG_ON(!IS_MODULE(CONFIG_CXL_ACPI));
BUILD_BUG_ON(!IS_MODULE(CONFIG_CXL_PMEM));
diff --git a/tools/testing/cxl/cxl_acpi_test.c b/tools/testing/cxl/cxl_acpi_test.c
new file mode 100644
index 000000000000..8602dc27c81c
--- /dev/null
+++ b/tools/testing/cxl/cxl_acpi_test.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
+
+#include "watermark.h"
+
+cxl_test_watermark(cxl_acpi);
diff --git a/tools/testing/cxl/cxl_core_test.c b/tools/testing/cxl/cxl_core_test.c
new file mode 100644
index 000000000000..464a9255e4d6
--- /dev/null
+++ b/tools/testing/cxl/cxl_core_test.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
+
+#include "watermark.h"
+
+cxl_test_watermark(cxl_core);
diff --git a/tools/testing/cxl/cxl_mem_test.c b/tools/testing/cxl/cxl_mem_test.c
new file mode 100644
index 000000000000..ba7fb8a44288
--- /dev/null
+++ b/tools/testing/cxl/cxl_mem_test.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
+
+#include "watermark.h"
+
+cxl_test_watermark(cxl_mem);
diff --git a/tools/testing/cxl/cxl_pmem_test.c b/tools/testing/cxl/cxl_pmem_test.c
new file mode 100644
index 000000000000..3fd884fae537
--- /dev/null
+++ b/tools/testing/cxl/cxl_pmem_test.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
+
+#include "watermark.h"
+
+cxl_test_watermark(cxl_pmem);
diff --git a/tools/testing/cxl/cxl_port_test.c b/tools/testing/cxl/cxl_port_test.c
new file mode 100644
index 000000000000..be183917a9f6
--- /dev/null
+++ b/tools/testing/cxl/cxl_port_test.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
+
+#include "watermark.h"
+
+cxl_test_watermark(cxl_port);
diff --git a/tools/testing/cxl/test/Kbuild b/tools/testing/cxl/test/Kbuild
index 4e59e2c911f6..61d5f7bcddf9 100644
--- a/tools/testing/cxl/test/Kbuild
+++ b/tools/testing/cxl/test/Kbuild
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-ccflags-y := -I$(srctree)/drivers/cxl/
+ccflags-y := -I$(srctree)/drivers/cxl/ -I$(srctree)/drivers/cxl/core
obj-m += cxl_test.o
obj-m += cxl_mock.o
diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
index 30ee680d38ff..bf00dc52fe96 100644
--- a/tools/testing/cxl/test/cxl.c
+++ b/tools/testing/cxl/test/cxl.c
@@ -9,6 +9,8 @@
#include <linux/pci.h>
#include <linux/mm.h>
#include <cxlmem.h>
+
+#include "../watermark.h"
#include "mock.h"
static int interleave_arithmetic;
@@ -618,7 +620,8 @@ static struct acpi_pci_root *mock_acpi_pci_find_root(acpi_handle handle)
return &mock_pci_root[host_bridge_index(adev)];
}
-static struct cxl_hdm *mock_cxl_setup_hdm(struct cxl_port *port)
+static struct cxl_hdm *mock_cxl_setup_hdm(struct cxl_port *port,
+ struct cxl_endpoint_dvsec_info *info)
{
struct cxl_hdm *cxlhdm = devm_kzalloc(&port->dev, sizeof(*cxlhdm), GFP_KERNEL);
@@ -701,7 +704,144 @@ static int mock_decoder_reset(struct cxl_decoder *cxld)
return 0;
}
-static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm)
+static void default_mock_decoder(struct cxl_decoder *cxld)
+{
+ cxld->hpa_range = (struct range){
+ .start = 0,
+ .end = -1,
+ };
+
+ cxld->interleave_ways = 1;
+ cxld->interleave_granularity = 256;
+ cxld->target_type = CXL_DECODER_EXPANDER;
+ cxld->commit = mock_decoder_commit;
+ cxld->reset = mock_decoder_reset;
+}
+
+static int first_decoder(struct device *dev, void *data)
+{
+ struct cxl_decoder *cxld;
+
+ if (!is_switch_decoder(dev))
+ return 0;
+ cxld = to_cxl_decoder(dev);
+ if (cxld->id == 0)
+ return 1;
+ return 0;
+}
+
+static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
+{
+ struct acpi_cedt_cfmws *window = mock_cfmws[0];
+ struct platform_device *pdev = NULL;
+ struct cxl_endpoint_decoder *cxled;
+ struct cxl_switch_decoder *cxlsd;
+ struct cxl_port *port, *iter;
+ const int size = SZ_512M;
+ struct cxl_memdev *cxlmd;
+ struct cxl_dport *dport;
+ struct device *dev;
+ bool hb0 = false;
+ u64 base;
+ int i;
+
+ if (is_endpoint_decoder(&cxld->dev)) {
+ cxled = to_cxl_endpoint_decoder(&cxld->dev);
+ cxlmd = cxled_to_memdev(cxled);
+ WARN_ON(!dev_is_platform(cxlmd->dev.parent));
+ pdev = to_platform_device(cxlmd->dev.parent);
+
+ /* check is endpoint is attach to host-bridge0 */
+ port = cxled_to_port(cxled);
+ do {
+ if (port->uport == &cxl_host_bridge[0]->dev) {
+ hb0 = true;
+ break;
+ }
+ if (is_cxl_port(port->dev.parent))
+ port = to_cxl_port(port->dev.parent);
+ else
+ port = NULL;
+ } while (port);
+ port = cxled_to_port(cxled);
+ }
+
+ /*
+ * The first decoder on the first 2 devices on the first switch
+ * attached to host-bridge0 mock a fake / static RAM region. All
+ * other decoders are default disabled. Given the round robin
+ * assignment those devices are named cxl_mem.0, and cxl_mem.4.
+ *
+ * See 'cxl list -BMPu -m cxl_mem.0,cxl_mem.4'
+ */
+ if (!hb0 || pdev->id % 4 || pdev->id > 4 || cxld->id > 0) {
+ default_mock_decoder(cxld);
+ return;
+ }
+
+ base = window->base_hpa;
+ cxld->hpa_range = (struct range) {
+ .start = base,
+ .end = base + size - 1,
+ };
+
+ cxld->interleave_ways = 2;
+ eig_to_granularity(window->granularity, &cxld->interleave_granularity);
+ cxld->target_type = CXL_DECODER_EXPANDER;
+ cxld->flags = CXL_DECODER_F_ENABLE;
+ cxled->state = CXL_DECODER_STATE_AUTO;
+ port->commit_end = cxld->id;
+ devm_cxl_dpa_reserve(cxled, 0, size / cxld->interleave_ways, 0);
+ cxld->commit = mock_decoder_commit;
+ cxld->reset = mock_decoder_reset;
+
+ /*
+ * Now that endpoint decoder is set up, walk up the hierarchy
+ * and setup the switch and root port decoders targeting @cxlmd.
+ */
+ iter = port;
+ for (i = 0; i < 2; i++) {
+ dport = iter->parent_dport;
+ iter = dport->port;
+ dev = device_find_child(&iter->dev, NULL, first_decoder);
+ /*
+ * Ancestor ports are guaranteed to be enumerated before
+ * @port, and all ports have at least one decoder.
+ */
+ if (WARN_ON(!dev))
+ continue;
+ cxlsd = to_cxl_switch_decoder(dev);
+ if (i == 0) {
+ /* put cxl_mem.4 second in the decode order */
+ if (pdev->id == 4)
+ cxlsd->target[1] = dport;
+ else
+ cxlsd->target[0] = dport;
+ } else
+ cxlsd->target[0] = dport;
+ cxld = &cxlsd->cxld;
+ cxld->target_type = CXL_DECODER_EXPANDER;
+ cxld->flags = CXL_DECODER_F_ENABLE;
+ iter->commit_end = 0;
+ /*
+ * Switch targets 2 endpoints, while host bridge targets
+ * one root port
+ */
+ if (i == 0)
+ cxld->interleave_ways = 2;
+ else
+ cxld->interleave_ways = 1;
+ cxld->interleave_granularity = 256;
+ cxld->hpa_range = (struct range) {
+ .start = base,
+ .end = base + size - 1,
+ };
+ put_device(dev);
+ }
+}
+
+static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
+ struct cxl_endpoint_dvsec_info *info)
{
struct cxl_port *port = cxlhdm->port;
struct cxl_port *parent_port = to_cxl_port(port->dev.parent);
@@ -746,16 +886,7 @@ static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm)
cxld = &cxled->cxld;
}
- cxld->hpa_range = (struct range) {
- .start = 0,
- .end = -1,
- };
-
- cxld->interleave_ways = min_not_zero(target_count, 1);
- cxld->interleave_granularity = SZ_4K;
- cxld->target_type = CXL_DECODER_EXPANDER;
- cxld->commit = mock_decoder_commit;
- cxld->reset = mock_decoder_reset;
+ mock_init_hdm_decoder(cxld);
if (target_count) {
rc = device_for_each_child(port->uport, &ctx,
@@ -1119,6 +1250,12 @@ static __init int cxl_test_init(void)
{
int rc, i;
+ cxl_acpi_test();
+ cxl_core_test();
+ cxl_mem_test();
+ cxl_pmem_test();
+ cxl_port_test();
+
register_cxl_mock_ops(&cxl_mock_ops);
cxl_mock_pool = gen_pool_create(ilog2(SZ_2M), NUMA_NO_NODE);
@@ -1135,11 +1272,9 @@ static __init int cxl_test_init(void)
if (interleave_arithmetic == 1) {
cfmws_start = CFMWS_XOR_ARRAY_START;
cfmws_end = CFMWS_XOR_ARRAY_END;
- dev_dbg(NULL, "cxl_test loading xor math option\n");
} else {
cfmws_start = CFMWS_MOD_ARRAY_START;
cfmws_end = CFMWS_MOD_ARRAY_END;
- dev_dbg(NULL, "cxl_test loading modulo math option\n");
}
rc = populate_cedt();
@@ -1326,7 +1461,7 @@ static __exit void cxl_test_exit(void)
unregister_cxl_mock_ops(&cxl_mock_ops);
}
-module_param(interleave_arithmetic, int, 0000);
+module_param(interleave_arithmetic, int, 0444);
MODULE_PARM_DESC(interleave_arithmetic, "Modulo:0, XOR:1");
module_init(cxl_test_init);
module_exit(cxl_test_exit);
diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c
index 5e4ecd93f1d2..9263b04d35f7 100644
--- a/tools/testing/cxl/test/mem.c
+++ b/tools/testing/cxl/test/mem.c
@@ -9,6 +9,8 @@
#include <linux/bits.h>
#include <cxlmem.h>
+#include "trace.h"
+
#define LSA_SIZE SZ_128K
#define DEV_SIZE SZ_2G
#define EFFECT(x) (1U << x)
@@ -67,6 +69,26 @@ static struct {
#define PASS_TRY_LIMIT 3
+#define CXL_TEST_EVENT_CNT_MAX 15
+
+/* Set a number of events to return at a time for simulation. */
+#define CXL_TEST_EVENT_CNT 3
+
+struct mock_event_log {
+ u16 clear_idx;
+ u16 cur_idx;
+ u16 nr_events;
+ u16 nr_overflow;
+ u16 overflow_reset;
+ struct cxl_event_record_raw *events[CXL_TEST_EVENT_CNT_MAX];
+};
+
+struct mock_event_store {
+ struct cxl_dev_state *cxlds;
+ struct mock_event_log mock_logs[CXL_EVENT_TYPE_MAX];
+ u32 ev_status;
+};
+
struct cxl_mockmem_data {
void *lsa;
u32 security_state;
@@ -74,9 +96,317 @@ struct cxl_mockmem_data {
u8 master_pass[NVDIMM_PASSPHRASE_LEN];
int user_limit;
int master_limit;
+ struct mock_event_store mes;
+ u8 event_buf[SZ_4K];
+};
+
+static struct mock_event_log *event_find_log(struct device *dev, int log_type)
+{
+ struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);
+
+ if (log_type >= CXL_EVENT_TYPE_MAX)
+ return NULL;
+ return &mdata->mes.mock_logs[log_type];
+}
+static struct cxl_event_record_raw *event_get_current(struct mock_event_log *log)
+{
+ return log->events[log->cur_idx];
+}
+
+static void event_reset_log(struct mock_event_log *log)
+{
+ log->cur_idx = 0;
+ log->clear_idx = 0;
+ log->nr_overflow = log->overflow_reset;
+}
+
+/* Handle can never be 0 use 1 based indexing for handle */
+static u16 event_get_clear_handle(struct mock_event_log *log)
+{
+ return log->clear_idx + 1;
+}
+
+/* Handle can never be 0 use 1 based indexing for handle */
+static __le16 event_get_cur_event_handle(struct mock_event_log *log)
+{
+ u16 cur_handle = log->cur_idx + 1;
+
+ return cpu_to_le16(cur_handle);
+}
+
+static bool event_log_empty(struct mock_event_log *log)
+{
+ return log->cur_idx == log->nr_events;
+}
+
+static void mes_add_event(struct mock_event_store *mes,
+ enum cxl_event_log_type log_type,
+ struct cxl_event_record_raw *event)
+{
+ struct mock_event_log *log;
+
+ if (WARN_ON(log_type >= CXL_EVENT_TYPE_MAX))
+ return;
+
+ log = &mes->mock_logs[log_type];
+
+ if ((log->nr_events + 1) > CXL_TEST_EVENT_CNT_MAX) {
+ log->nr_overflow++;
+ log->overflow_reset = log->nr_overflow;
+ return;
+ }
+
+ log->events[log->nr_events] = event;
+ log->nr_events++;
+}
+
+static int mock_get_event(struct cxl_dev_state *cxlds,
+ struct cxl_mbox_cmd *cmd)
+{
+ struct cxl_get_event_payload *pl;
+ struct mock_event_log *log;
+ u16 nr_overflow;
+ u8 log_type;
+ int i;
+
+ if (cmd->size_in != sizeof(log_type))
+ return -EINVAL;
+
+ if (cmd->size_out < struct_size(pl, records, CXL_TEST_EVENT_CNT))
+ return -EINVAL;
+
+ log_type = *((u8 *)cmd->payload_in);
+ if (log_type >= CXL_EVENT_TYPE_MAX)
+ return -EINVAL;
+
+ memset(cmd->payload_out, 0, cmd->size_out);
+
+ log = event_find_log(cxlds->dev, log_type);
+ if (!log || event_log_empty(log))
+ return 0;
+
+ pl = cmd->payload_out;
+
+ for (i = 0; i < CXL_TEST_EVENT_CNT && !event_log_empty(log); i++) {
+ memcpy(&pl->records[i], event_get_current(log),
+ sizeof(pl->records[i]));
+ pl->records[i].hdr.handle = event_get_cur_event_handle(log);
+ log->cur_idx++;
+ }
+
+ pl->record_count = cpu_to_le16(i);
+ if (!event_log_empty(log))
+ pl->flags |= CXL_GET_EVENT_FLAG_MORE_RECORDS;
+
+ if (log->nr_overflow) {
+ u64 ns;
+
+ pl->flags |= CXL_GET_EVENT_FLAG_OVERFLOW;
+ pl->overflow_err_count = cpu_to_le16(nr_overflow);
+ ns = ktime_get_real_ns();
+ ns -= 5000000000; /* 5s ago */
+ pl->first_overflow_timestamp = cpu_to_le64(ns);
+ ns = ktime_get_real_ns();
+ ns -= 1000000000; /* 1s ago */
+ pl->last_overflow_timestamp = cpu_to_le64(ns);
+ }
+
+ return 0;
+}
+
+static int mock_clear_event(struct cxl_dev_state *cxlds,
+ struct cxl_mbox_cmd *cmd)
+{
+ struct cxl_mbox_clear_event_payload *pl = cmd->payload_in;
+ struct mock_event_log *log;
+ u8 log_type = pl->event_log;
+ u16 handle;
+ int nr;
+
+ if (log_type >= CXL_EVENT_TYPE_MAX)
+ return -EINVAL;
+
+ log = event_find_log(cxlds->dev, log_type);
+ if (!log)
+ return 0; /* No mock data in this log */
+
+ /*
+ * This check is technically not invalid per the specification AFAICS.
+ * (The host could 'guess' handles and clear them in order).
+ * However, this is not good behavior for the host so test it.
+ */
+ if (log->clear_idx + pl->nr_recs > log->cur_idx) {
+ dev_err(cxlds->dev,
+ "Attempting to clear more events than returned!\n");
+ return -EINVAL;
+ }
+
+ /* Check handle order prior to clearing events */
+ for (nr = 0, handle = event_get_clear_handle(log);
+ nr < pl->nr_recs;
+ nr++, handle++) {
+ if (handle != le16_to_cpu(pl->handles[nr])) {
+ dev_err(cxlds->dev, "Clearing events out of order\n");
+ return -EINVAL;
+ }
+ }
+
+ if (log->nr_overflow)
+ log->nr_overflow = 0;
+
+ /* Clear events */
+ log->clear_idx += pl->nr_recs;
+ return 0;
+}
+
+static void cxl_mock_event_trigger(struct device *dev)
+{
+ struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);
+ struct mock_event_store *mes = &mdata->mes;
+ int i;
+
+ for (i = CXL_EVENT_TYPE_INFO; i < CXL_EVENT_TYPE_MAX; i++) {
+ struct mock_event_log *log;
+
+ log = event_find_log(dev, i);
+ if (log)
+ event_reset_log(log);
+ }
+
+ cxl_mem_get_event_records(mes->cxlds, mes->ev_status);
+}
+
+struct cxl_event_record_raw maint_needed = {
+ .hdr = {
+ .id = UUID_INIT(0xBA5EBA11, 0xABCD, 0xEFEB,
+ 0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0xa5, 0x5a, 0xa5),
+ .length = sizeof(struct cxl_event_record_raw),
+ .flags[0] = CXL_EVENT_RECORD_FLAG_MAINT_NEEDED,
+ /* .handle = Set dynamically */
+ .related_handle = cpu_to_le16(0xa5b6),
+ },
+ .data = { 0xDE, 0xAD, 0xBE, 0xEF },
+};
+
+struct cxl_event_record_raw hardware_replace = {
+ .hdr = {
+ .id = UUID_INIT(0xABCDEFEB, 0xBA11, 0xBA5E,
+ 0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0xa5, 0x5a, 0xa5),
+ .length = sizeof(struct cxl_event_record_raw),
+ .flags[0] = CXL_EVENT_RECORD_FLAG_HW_REPLACE,
+ /* .handle = Set dynamically */
+ .related_handle = cpu_to_le16(0xb6a5),
+ },
+ .data = { 0xDE, 0xAD, 0xBE, 0xEF },
+};
+
+struct cxl_event_gen_media gen_media = {
+ .hdr = {
+ .id = UUID_INIT(0xfbcd0a77, 0xc260, 0x417f,
+ 0x85, 0xa9, 0x08, 0x8b, 0x16, 0x21, 0xeb, 0xa6),
+ .length = sizeof(struct cxl_event_gen_media),
+ .flags[0] = CXL_EVENT_RECORD_FLAG_PERMANENT,
+ /* .handle = Set dynamically */
+ .related_handle = cpu_to_le16(0),
+ },
+ .phys_addr = cpu_to_le64(0x2000),
+ .descriptor = CXL_GMER_EVT_DESC_UNCORECTABLE_EVENT,
+ .type = CXL_GMER_MEM_EVT_TYPE_DATA_PATH_ERROR,
+ .transaction_type = CXL_GMER_TRANS_HOST_WRITE,
+ /* .validity_flags = <set below> */
+ .channel = 1,
+ .rank = 30
+};
+
+struct cxl_event_dram dram = {
+ .hdr = {
+ .id = UUID_INIT(0x601dcbb3, 0x9c06, 0x4eab,
+ 0xb8, 0xaf, 0x4e, 0x9b, 0xfb, 0x5c, 0x96, 0x24),
+ .length = sizeof(struct cxl_event_dram),
+ .flags[0] = CXL_EVENT_RECORD_FLAG_PERF_DEGRADED,
+ /* .handle = Set dynamically */
+ .related_handle = cpu_to_le16(0),
+ },
+ .phys_addr = cpu_to_le64(0x8000),
+ .descriptor = CXL_GMER_EVT_DESC_THRESHOLD_EVENT,
+ .type = CXL_GMER_MEM_EVT_TYPE_INV_ADDR,
+ .transaction_type = CXL_GMER_TRANS_INTERNAL_MEDIA_SCRUB,
+ /* .validity_flags = <set below> */
+ .channel = 1,
+ .bank_group = 5,
+ .bank = 2,
+ .column = {0xDE, 0xAD},
};
+struct cxl_event_mem_module mem_module = {
+ .hdr = {
+ .id = UUID_INIT(0xfe927475, 0xdd59, 0x4339,
+ 0xa5, 0x86, 0x79, 0xba, 0xb1, 0x13, 0xb7, 0x74),
+ .length = sizeof(struct cxl_event_mem_module),
+ /* .handle = Set dynamically */
+ .related_handle = cpu_to_le16(0),
+ },
+ .event_type = CXL_MMER_TEMP_CHANGE,
+ .info = {
+ .health_status = CXL_DHI_HS_PERFORMANCE_DEGRADED,
+ .media_status = CXL_DHI_MS_ALL_DATA_LOST,
+ .add_status = (CXL_DHI_AS_CRITICAL << 2) |
+ (CXL_DHI_AS_WARNING << 4) |
+ (CXL_DHI_AS_WARNING << 5),
+ .device_temp = { 0xDE, 0xAD},
+ .dirty_shutdown_cnt = { 0xde, 0xad, 0xbe, 0xef },
+ .cor_vol_err_cnt = { 0xde, 0xad, 0xbe, 0xef },
+ .cor_per_err_cnt = { 0xde, 0xad, 0xbe, 0xef },
+ }
+};
+
+static void cxl_mock_add_event_logs(struct mock_event_store *mes)
+{
+ put_unaligned_le16(CXL_GMER_VALID_CHANNEL | CXL_GMER_VALID_RANK,
+ &gen_media.validity_flags);
+
+ put_unaligned_le16(CXL_DER_VALID_CHANNEL | CXL_DER_VALID_BANK_GROUP |
+ CXL_DER_VALID_BANK | CXL_DER_VALID_COLUMN,
+ &dram.validity_flags);
+
+ mes_add_event(mes, CXL_EVENT_TYPE_INFO, &maint_needed);
+ mes_add_event(mes, CXL_EVENT_TYPE_INFO,
+ (struct cxl_event_record_raw *)&gen_media);
+ mes_add_event(mes, CXL_EVENT_TYPE_INFO,
+ (struct cxl_event_record_raw *)&mem_module);
+ mes->ev_status |= CXLDEV_EVENT_STATUS_INFO;
+
+ mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &maint_needed);
+ mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
+ mes_add_event(mes, CXL_EVENT_TYPE_FAIL,
+ (struct cxl_event_record_raw *)&dram);
+ mes_add_event(mes, CXL_EVENT_TYPE_FAIL,
+ (struct cxl_event_record_raw *)&gen_media);
+ mes_add_event(mes, CXL_EVENT_TYPE_FAIL,
+ (struct cxl_event_record_raw *)&mem_module);
+ mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
+ mes_add_event(mes, CXL_EVENT_TYPE_FAIL,
+ (struct cxl_event_record_raw *)&dram);
+ /* Overflow this log */
+ mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
+ mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
+ mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
+ mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
+ mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
+ mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
+ mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
+ mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
+ mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
+ mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
+ mes->ev_status |= CXLDEV_EVENT_STATUS_FAIL;
+
+ mes_add_event(mes, CXL_EVENT_TYPE_FATAL, &hardware_replace);
+ mes_add_event(mes, CXL_EVENT_TYPE_FATAL,
+ (struct cxl_event_record_raw *)&dram);
+ mes->ev_status |= CXLDEV_EVENT_STATUS_FATAL;
+}
+
static int mock_gsl(struct cxl_mbox_cmd *cmd)
{
if (cmd->size_out < sizeof(mock_gsl_payload))
@@ -582,6 +912,12 @@ static int cxl_mock_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *
case CXL_MBOX_OP_GET_PARTITION_INFO:
rc = mock_partition_info(cxlds, cmd);
break;
+ case CXL_MBOX_OP_GET_EVENT_RECORD:
+ rc = mock_get_event(cxlds, cmd);
+ break;
+ case CXL_MBOX_OP_CLEAR_EVENT_RECORD:
+ rc = mock_clear_event(cxlds, cmd);
+ break;
case CXL_MBOX_OP_SET_LSA:
rc = mock_set_lsa(cxlds, cmd);
break;
@@ -628,6 +964,15 @@ static bool is_rcd(struct platform_device *pdev)
return !!id->driver_data;
}
+static ssize_t event_trigger_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ cxl_mock_event_trigger(dev);
+ return count;
+}
+static DEVICE_ATTR_WO(event_trigger);
+
static int cxl_mock_mem_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -655,6 +1000,7 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
cxlds->serial = pdev->id;
cxlds->mbox_send = cxl_mock_mbox_send;
cxlds->payload_size = SZ_4K;
+ cxlds->event.buf = (struct cxl_get_event_payload *) mdata->event_buf;
if (is_rcd(pdev)) {
cxlds->rcd = true;
cxlds->component_reg_phys = CXL_RESOURCE_NONE;
@@ -672,10 +1018,15 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
if (rc)
return rc;
+ mdata->mes.cxlds = cxlds;
+ cxl_mock_add_event_logs(&mdata->mes);
+
cxlmd = devm_cxl_add_memdev(cxlds);
if (IS_ERR(cxlmd))
return PTR_ERR(cxlmd);
+ cxl_mem_get_event_records(cxlds, CXLDEV_EVENT_STATUS_ALL);
+
return 0;
}
@@ -714,6 +1065,7 @@ static DEVICE_ATTR_RW(security_lock);
static struct attribute *cxl_mock_mem_attrs[] = {
&dev_attr_security_lock.attr,
+ &dev_attr_event_trigger.attr,
NULL
};
ATTRIBUTE_GROUPS(cxl_mock_mem);
diff --git a/tools/testing/cxl/test/mock.c b/tools/testing/cxl/test/mock.c
index 5dface08e0de..c4e53f22e421 100644
--- a/tools/testing/cxl/test/mock.c
+++ b/tools/testing/cxl/test/mock.c
@@ -131,16 +131,18 @@ __wrap_nvdimm_bus_register(struct device *dev,
}
EXPORT_SYMBOL_GPL(__wrap_nvdimm_bus_register);
-struct cxl_hdm *__wrap_devm_cxl_setup_hdm(struct cxl_port *port)
+struct cxl_hdm *__wrap_devm_cxl_setup_hdm(struct cxl_port *port,
+ struct cxl_endpoint_dvsec_info *info)
+
{
int index;
struct cxl_hdm *cxlhdm;
struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
if (ops && ops->is_mock_port(port->uport))
- cxlhdm = ops->devm_cxl_setup_hdm(port);
+ cxlhdm = ops->devm_cxl_setup_hdm(port, info);
else
- cxlhdm = devm_cxl_setup_hdm(port);
+ cxlhdm = devm_cxl_setup_hdm(port, info);
put_cxl_mock_ops(index);
return cxlhdm;
@@ -162,16 +164,17 @@ int __wrap_devm_cxl_add_passthrough_decoder(struct cxl_port *port)
}
EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_add_passthrough_decoder, CXL);
-int __wrap_devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm)
+int __wrap_devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
+ struct cxl_endpoint_dvsec_info *info)
{
int rc, index;
struct cxl_port *port = cxlhdm->port;
struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
if (ops && ops->is_mock_port(port->uport))
- rc = ops->devm_cxl_enumerate_decoders(cxlhdm);
+ rc = ops->devm_cxl_enumerate_decoders(cxlhdm, info);
else
- rc = devm_cxl_enumerate_decoders(cxlhdm);
+ rc = devm_cxl_enumerate_decoders(cxlhdm, info);
put_cxl_mock_ops(index);
return rc;
@@ -209,7 +212,8 @@ int __wrap_cxl_await_media_ready(struct cxl_dev_state *cxlds)
EXPORT_SYMBOL_NS_GPL(__wrap_cxl_await_media_ready, CXL);
int __wrap_cxl_hdm_decode_init(struct cxl_dev_state *cxlds,
- struct cxl_hdm *cxlhdm)
+ struct cxl_hdm *cxlhdm,
+ struct cxl_endpoint_dvsec_info *info)
{
int rc = 0, index;
struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
@@ -217,13 +221,29 @@ int __wrap_cxl_hdm_decode_init(struct cxl_dev_state *cxlds,
if (ops && ops->is_mock_dev(cxlds->dev))
rc = 0;
else
- rc = cxl_hdm_decode_init(cxlds, cxlhdm);
+ rc = cxl_hdm_decode_init(cxlds, cxlhdm, info);
put_cxl_mock_ops(index);
return rc;
}
EXPORT_SYMBOL_NS_GPL(__wrap_cxl_hdm_decode_init, CXL);
+int __wrap_cxl_dvsec_rr_decode(struct device *dev, int dvsec,
+ struct cxl_endpoint_dvsec_info *info)
+{
+ int rc = 0, index;
+ struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
+
+ if (ops && ops->is_mock_dev(dev))
+ rc = 0;
+ else
+ rc = cxl_dvsec_rr_decode(dev, dvsec, info);
+ put_cxl_mock_ops(index);
+
+ return rc;
+}
+EXPORT_SYMBOL_NS_GPL(__wrap_cxl_dvsec_rr_decode, CXL);
+
resource_size_t __wrap_cxl_rcrb_to_component(struct device *dev,
resource_size_t rcrb,
enum cxl_rcrb which)
diff --git a/tools/testing/cxl/test/mock.h b/tools/testing/cxl/test/mock.h
index ef33f159375e..bef8817b01f2 100644
--- a/tools/testing/cxl/test/mock.h
+++ b/tools/testing/cxl/test/mock.h
@@ -23,9 +23,11 @@ struct cxl_mock_ops {
bool (*is_mock_port)(struct device *dev);
bool (*is_mock_dev)(struct device *dev);
int (*devm_cxl_port_enumerate_dports)(struct cxl_port *port);
- struct cxl_hdm *(*devm_cxl_setup_hdm)(struct cxl_port *port);
+ struct cxl_hdm *(*devm_cxl_setup_hdm)(
+ struct cxl_port *port, struct cxl_endpoint_dvsec_info *info);
int (*devm_cxl_add_passthrough_decoder)(struct cxl_port *port);
- int (*devm_cxl_enumerate_decoders)(struct cxl_hdm *hdm);
+ int (*devm_cxl_enumerate_decoders)(
+ struct cxl_hdm *hdm, struct cxl_endpoint_dvsec_info *info);
};
void register_cxl_mock_ops(struct cxl_mock_ops *ops);
diff --git a/tools/testing/cxl/watermark.h b/tools/testing/cxl/watermark.h
new file mode 100644
index 000000000000..9d81d4a5f6be
--- /dev/null
+++ b/tools/testing/cxl/watermark.h
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
+#ifndef _TEST_CXL_WATERMARK_H_
+#define _TEST_CXL_WATERMARK_H_
+#include <linux/module.h>
+#include <linux/printk.h>
+
+int cxl_acpi_test(void);
+int cxl_core_test(void);
+int cxl_mem_test(void);
+int cxl_pmem_test(void);
+int cxl_port_test(void);
+
+/*
+ * dummy routine for cxl_test to validate it is linking to the properly
+ * mocked module and not the standard one from the base tree.
+ */
+#define cxl_test_watermark(x) \
+int x##_test(void) \
+{ \
+ pr_debug("%s for cxl_test\n", KBUILD_MODNAME); \
+ return 0; \
+} \
+EXPORT_SYMBOL(x##_test)
+#endif /* _TEST_CXL_WATERMARK_H_ */
diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl
index ac59999ed3de..829f5bdfd2e4 100755
--- a/tools/testing/ktest/ktest.pl
+++ b/tools/testing/ktest/ktest.pl
@@ -178,6 +178,7 @@ my $store_failures;
my $store_successes;
my $test_name;
my $timeout;
+my $run_timeout;
my $connect_timeout;
my $config_bisect_exec;
my $booted_timeout;
@@ -340,6 +341,7 @@ my %option_map = (
"STORE_SUCCESSES" => \$store_successes,
"TEST_NAME" => \$test_name,
"TIMEOUT" => \$timeout,
+ "RUN_TIMEOUT" => \$run_timeout,
"CONNECT_TIMEOUT" => \$connect_timeout,
"CONFIG_BISECT_EXEC" => \$config_bisect_exec,
"BOOTED_TIMEOUT" => \$booted_timeout,
@@ -1495,7 +1497,8 @@ sub reboot {
# Still need to wait for the reboot to finish
wait_for_monitor($time, $reboot_success_line);
-
+ }
+ if ($powercycle || $time) {
end_monitor;
}
}
@@ -1535,6 +1538,11 @@ sub dodie {
return if ($in_die);
$in_die = 1;
+ if ($monitor_cnt) {
+ # restore terminal settings
+ system("stty $stty_orig");
+ }
+
my $i = $iteration;
doprint "CRITICAL FAILURE... [TEST $i] ", @_, "\n";
@@ -1581,11 +1589,6 @@ sub dodie {
"Your test started at $script_start_time has failed with:\n@_\n", $log_file);
}
- if ($monitor_cnt) {
- # restore terminal settings
- system("stty $stty_orig");
- }
-
if (defined($post_test)) {
run_command $post_test;
}
@@ -1857,6 +1860,14 @@ sub run_command {
$command =~ s/\$SSH_USER/$ssh_user/g;
$command =~ s/\$MACHINE/$machine/g;
+ if (!defined($timeout)) {
+ $timeout = $run_timeout;
+ }
+
+ if (!defined($timeout)) {
+ $timeout = -1; # tell wait_for_input to wait indefinitely
+ }
+
doprint("$command ... ");
$start_time = time;
@@ -1883,13 +1894,10 @@ sub run_command {
while (1) {
my $fp = \*CMD;
- if (defined($timeout)) {
- doprint "timeout = $timeout\n";
- }
my $line = wait_for_input($fp, $timeout);
if (!defined($line)) {
my $now = time;
- if (defined($timeout) && (($now - $start_time) >= $timeout)) {
+ if ($timeout >= 0 && (($now - $start_time) >= $timeout)) {
doprint "Hit timeout of $timeout, killing process\n";
$hit_timeout = 1;
kill 9, $pid;
@@ -2061,6 +2069,11 @@ sub wait_for_input {
$time = $timeout;
}
+ if ($time < 0) {
+ # Negative number means wait indefinitely
+ undef $time;
+ }
+
$rin = '';
vec($rin, fileno($fp), 1) = 1;
vec($rin, fileno(\*STDIN), 1) = 1;
@@ -4200,6 +4213,9 @@ sub send_email {
}
sub cancel_test {
+ if ($monitor_cnt) {
+ end_monitor;
+ }
if ($email_when_canceled) {
my $name = get_test_name;
send_email("KTEST: Your [$name] test was cancelled",
diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf
index 2d0fe15a096d..f43477a9b857 100644
--- a/tools/testing/ktest/sample.conf
+++ b/tools/testing/ktest/sample.conf
@@ -817,6 +817,11 @@
# is issued instead of a reboot.
# CONNECT_TIMEOUT = 25
+# The timeout in seconds for how long to wait for any running command
+# to timeout. If not defined, it will let it go indefinitely.
+# (default undefined)
+#RUN_TIMEOUT = 600
+
# In between tests, a reboot of the box may occur, and this
# is the time to wait for the console after it stops producing
# output. Some machines may not produce a large lag on reboot
diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py
index 43fbe96318fe..741f15420467 100755
--- a/tools/testing/kunit/kunit.py
+++ b/tools/testing/kunit/kunit.py
@@ -77,11 +77,8 @@ def config_tests(linux: kunit_kernel.LinuxSourceTree,
config_start = time.time()
success = linux.build_reconfig(request.build_dir, request.make_options)
config_end = time.time()
- if not success:
- return KunitResult(KunitStatus.CONFIG_FAILURE,
- config_end - config_start)
- return KunitResult(KunitStatus.SUCCESS,
- config_end - config_start)
+ status = KunitStatus.SUCCESS if success else KunitStatus.CONFIG_FAILURE
+ return KunitResult(status, config_end - config_start)
def build_tests(linux: kunit_kernel.LinuxSourceTree,
request: KunitBuildRequest) -> KunitResult:
@@ -92,14 +89,8 @@ def build_tests(linux: kunit_kernel.LinuxSourceTree,
request.build_dir,
request.make_options)
build_end = time.time()
- if not success:
- return KunitResult(KunitStatus.BUILD_FAILURE,
- build_end - build_start)
- if not success:
- return KunitResult(KunitStatus.BUILD_FAILURE,
- build_end - build_start)
- return KunitResult(KunitStatus.SUCCESS,
- build_end - build_start)
+ status = KunitStatus.SUCCESS if success else KunitStatus.BUILD_FAILURE
+ return KunitResult(status, build_end - build_start)
def config_and_build_tests(linux: kunit_kernel.LinuxSourceTree,
request: KunitBuildRequest) -> KunitResult:
@@ -145,7 +136,7 @@ def exec_tests(linux: kunit_kernel.LinuxSourceTree, request: KunitExecRequest) -
tests = _list_tests(linux, request)
if request.run_isolated == 'test':
filter_globs = tests
- if request.run_isolated == 'suite':
+ elif request.run_isolated == 'suite':
filter_globs = _suites_from_test_list(tests)
# Apply the test-part of the user's glob, if present.
if '.' in request.filter_glob:
@@ -395,6 +386,95 @@ def tree_from_args(cli_args: argparse.Namespace) -> kunit_kernel.LinuxSourceTree
extra_qemu_args=qemu_args)
+def run_handler(cli_args):
+ if not os.path.exists(cli_args.build_dir):
+ os.mkdir(cli_args.build_dir)
+
+ linux = tree_from_args(cli_args)
+ request = KunitRequest(build_dir=cli_args.build_dir,
+ make_options=cli_args.make_options,
+ jobs=cli_args.jobs,
+ raw_output=cli_args.raw_output,
+ json=cli_args.json,
+ timeout=cli_args.timeout,
+ filter_glob=cli_args.filter_glob,
+ kernel_args=cli_args.kernel_args,
+ run_isolated=cli_args.run_isolated)
+ result = run_tests(linux, request)
+ if result.status != KunitStatus.SUCCESS:
+ sys.exit(1)
+
+
+def config_handler(cli_args):
+ if cli_args.build_dir and (
+ not os.path.exists(cli_args.build_dir)):
+ os.mkdir(cli_args.build_dir)
+
+ linux = tree_from_args(cli_args)
+ request = KunitConfigRequest(build_dir=cli_args.build_dir,
+ make_options=cli_args.make_options)
+ result = config_tests(linux, request)
+ stdout.print_with_timestamp((
+ 'Elapsed time: %.3fs\n') % (
+ result.elapsed_time))
+ if result.status != KunitStatus.SUCCESS:
+ sys.exit(1)
+
+
+def build_handler(cli_args):
+ linux = tree_from_args(cli_args)
+ request = KunitBuildRequest(build_dir=cli_args.build_dir,
+ make_options=cli_args.make_options,
+ jobs=cli_args.jobs)
+ result = config_and_build_tests(linux, request)
+ stdout.print_with_timestamp((
+ 'Elapsed time: %.3fs\n') % (
+ result.elapsed_time))
+ if result.status != KunitStatus.SUCCESS:
+ sys.exit(1)
+
+
+def exec_handler(cli_args):
+ linux = tree_from_args(cli_args)
+ exec_request = KunitExecRequest(raw_output=cli_args.raw_output,
+ build_dir=cli_args.build_dir,
+ json=cli_args.json,
+ timeout=cli_args.timeout,
+ filter_glob=cli_args.filter_glob,
+ kernel_args=cli_args.kernel_args,
+ run_isolated=cli_args.run_isolated)
+ result = exec_tests(linux, exec_request)
+ stdout.print_with_timestamp((
+ 'Elapsed time: %.3fs\n') % (result.elapsed_time))
+ if result.status != KunitStatus.SUCCESS:
+ sys.exit(1)
+
+
+def parse_handler(cli_args):
+ if cli_args.file is None:
+ sys.stdin.reconfigure(errors='backslashreplace') # pytype: disable=attribute-error
+ kunit_output = sys.stdin
+ else:
+ with open(cli_args.file, 'r', errors='backslashreplace') as f:
+ kunit_output = f.read().splitlines()
+ # We know nothing about how the result was created!
+ metadata = kunit_json.Metadata()
+ request = KunitParseRequest(raw_output=cli_args.raw_output,
+ json=cli_args.json)
+ result, _ = parse_tests(request, metadata, kunit_output)
+ if result.status != KunitStatus.SUCCESS:
+ sys.exit(1)
+
+
+subcommand_handlers_map = {
+ 'run': run_handler,
+ 'config': config_handler,
+ 'build': build_handler,
+ 'exec': exec_handler,
+ 'parse': parse_handler
+}
+
+
def main(argv):
parser = argparse.ArgumentParser(
description='Helps writing and running KUnit tests.')
@@ -438,78 +518,14 @@ def main(argv):
if get_kernel_root_path():
os.chdir(get_kernel_root_path())
- if cli_args.subcommand == 'run':
- if not os.path.exists(cli_args.build_dir):
- os.mkdir(cli_args.build_dir)
-
- linux = tree_from_args(cli_args)
- request = KunitRequest(build_dir=cli_args.build_dir,
- make_options=cli_args.make_options,
- jobs=cli_args.jobs,
- raw_output=cli_args.raw_output,
- json=cli_args.json,
- timeout=cli_args.timeout,
- filter_glob=cli_args.filter_glob,
- kernel_args=cli_args.kernel_args,
- run_isolated=cli_args.run_isolated)
- result = run_tests(linux, request)
- if result.status != KunitStatus.SUCCESS:
- sys.exit(1)
- elif cli_args.subcommand == 'config':
- if cli_args.build_dir and (
- not os.path.exists(cli_args.build_dir)):
- os.mkdir(cli_args.build_dir)
-
- linux = tree_from_args(cli_args)
- request = KunitConfigRequest(build_dir=cli_args.build_dir,
- make_options=cli_args.make_options)
- result = config_tests(linux, request)
- stdout.print_with_timestamp((
- 'Elapsed time: %.3fs\n') % (
- result.elapsed_time))
- if result.status != KunitStatus.SUCCESS:
- sys.exit(1)
- elif cli_args.subcommand == 'build':
- linux = tree_from_args(cli_args)
- request = KunitBuildRequest(build_dir=cli_args.build_dir,
- make_options=cli_args.make_options,
- jobs=cli_args.jobs)
- result = config_and_build_tests(linux, request)
- stdout.print_with_timestamp((
- 'Elapsed time: %.3fs\n') % (
- result.elapsed_time))
- if result.status != KunitStatus.SUCCESS:
- sys.exit(1)
- elif cli_args.subcommand == 'exec':
- linux = tree_from_args(cli_args)
- exec_request = KunitExecRequest(raw_output=cli_args.raw_output,
- build_dir=cli_args.build_dir,
- json=cli_args.json,
- timeout=cli_args.timeout,
- filter_glob=cli_args.filter_glob,
- kernel_args=cli_args.kernel_args,
- run_isolated=cli_args.run_isolated)
- result = exec_tests(linux, exec_request)
- stdout.print_with_timestamp((
- 'Elapsed time: %.3fs\n') % (result.elapsed_time))
- if result.status != KunitStatus.SUCCESS:
- sys.exit(1)
- elif cli_args.subcommand == 'parse':
- if cli_args.file is None:
- sys.stdin.reconfigure(errors='backslashreplace') # pytype: disable=attribute-error
- kunit_output = sys.stdin
- else:
- with open(cli_args.file, 'r', errors='backslashreplace') as f:
- kunit_output = f.read().splitlines()
- # We know nothing about how the result was created!
- metadata = kunit_json.Metadata()
- request = KunitParseRequest(raw_output=cli_args.raw_output,
- json=cli_args.json)
- result, _ = parse_tests(request, metadata, kunit_output)
- if result.status != KunitStatus.SUCCESS:
- sys.exit(1)
- else:
+ subcomand_handler = subcommand_handlers_map.get(cli_args.subcommand, None)
+
+ if subcomand_handler is None:
parser.print_help()
+ return
+
+ subcomand_handler(cli_args)
+
if __name__ == '__main__':
main(sys.argv[1:])
diff --git a/tools/testing/radix-tree/maple.c b/tools/testing/radix-tree/maple.c
index 81fa7ec2e66a..4c89ff333f6f 100644
--- a/tools/testing/radix-tree/maple.c
+++ b/tools/testing/radix-tree/maple.c
@@ -108,6 +108,7 @@ static noinline void check_new_node(struct maple_tree *mt)
MT_BUG_ON(mt, mn->slot[1] != NULL);
MT_BUG_ON(mt, mas_allocated(&mas) != 0);
+ mn->parent = ma_parent_ptr(mn);
ma_free_rcu(mn);
mas.node = MAS_START;
mas_nomem(&mas, GFP_KERNEL);
@@ -160,6 +161,7 @@ static noinline void check_new_node(struct maple_tree *mt)
MT_BUG_ON(mt, mas_allocated(&mas) != i);
MT_BUG_ON(mt, !mn);
MT_BUG_ON(mt, not_empty(mn));
+ mn->parent = ma_parent_ptr(mn);
ma_free_rcu(mn);
}
@@ -173,11 +175,11 @@ static noinline void check_new_node(struct maple_tree *mt)
if (!MAPLE_32BIT) {
if (i >= 35)
- e = i - 35;
+ e = i - 34;
else if (i >= 5)
- e = i - 5;
+ e = i - 4;
else if (i >= 2)
- e = i - 2;
+ e = i - 1;
} else {
if (i >= 4)
e = i - 4;
@@ -192,6 +194,7 @@ static noinline void check_new_node(struct maple_tree *mt)
MT_BUG_ON(mt, not_empty(mn));
MT_BUG_ON(mt, mas_allocated(&mas) != i - 1);
MT_BUG_ON(mt, !mn);
+ mn->parent = ma_parent_ptr(mn);
ma_free_rcu(mn);
}
@@ -210,6 +213,7 @@ static noinline void check_new_node(struct maple_tree *mt)
mn = mas_pop_node(&mas);
MT_BUG_ON(mt, not_empty(mn));
MT_BUG_ON(mt, mas_allocated(&mas) != j - 1);
+ mn->parent = ma_parent_ptr(mn);
ma_free_rcu(mn);
}
MT_BUG_ON(mt, mas_allocated(&mas) != 0);
@@ -233,6 +237,7 @@ static noinline void check_new_node(struct maple_tree *mt)
MT_BUG_ON(mt, mas_allocated(&mas) != i - j);
mn = mas_pop_node(&mas);
MT_BUG_ON(mt, not_empty(mn));
+ mn->parent = ma_parent_ptr(mn);
ma_free_rcu(mn);
MT_BUG_ON(mt, mas_allocated(&mas) != i - j - 1);
}
@@ -269,6 +274,7 @@ static noinline void check_new_node(struct maple_tree *mt)
mn = mas_pop_node(&mas); /* get the next node. */
MT_BUG_ON(mt, mn == NULL);
MT_BUG_ON(mt, not_empty(mn));
+ mn->parent = ma_parent_ptr(mn);
ma_free_rcu(mn);
}
MT_BUG_ON(mt, mas_allocated(&mas) != 0);
@@ -294,6 +300,7 @@ static noinline void check_new_node(struct maple_tree *mt)
mn = mas_pop_node(&mas2); /* get the next node. */
MT_BUG_ON(mt, mn == NULL);
MT_BUG_ON(mt, not_empty(mn));
+ mn->parent = ma_parent_ptr(mn);
ma_free_rcu(mn);
}
MT_BUG_ON(mt, mas_allocated(&mas2) != 0);
@@ -305,17 +312,17 @@ static noinline void check_new_node(struct maple_tree *mt)
MT_BUG_ON(mt, mas.node != MA_ERROR(-ENOMEM));
MT_BUG_ON(mt, !mas_nomem(&mas, GFP_KERNEL));
MT_BUG_ON(mt, mas_allocated(&mas) != MAPLE_ALLOC_SLOTS + 1);
- MT_BUG_ON(mt, mas.alloc->node_count != MAPLE_ALLOC_SLOTS - 1);
+ MT_BUG_ON(mt, mas.alloc->node_count != MAPLE_ALLOC_SLOTS);
mn = mas_pop_node(&mas); /* get the next node. */
MT_BUG_ON(mt, mn == NULL);
MT_BUG_ON(mt, not_empty(mn));
MT_BUG_ON(mt, mas_allocated(&mas) != MAPLE_ALLOC_SLOTS);
- MT_BUG_ON(mt, mas.alloc->node_count != MAPLE_ALLOC_SLOTS - 2);
+ MT_BUG_ON(mt, mas.alloc->node_count != MAPLE_ALLOC_SLOTS - 1);
mas_push_node(&mas, mn);
MT_BUG_ON(mt, mas_allocated(&mas) != MAPLE_ALLOC_SLOTS + 1);
- MT_BUG_ON(mt, mas.alloc->node_count != MAPLE_ALLOC_SLOTS - 1);
+ MT_BUG_ON(mt, mas.alloc->node_count != MAPLE_ALLOC_SLOTS);
/* Check the limit of pop/push/pop */
mas_node_count(&mas, MAPLE_ALLOC_SLOTS + 2); /* Request */
@@ -323,21 +330,23 @@ static noinline void check_new_node(struct maple_tree *mt)
MT_BUG_ON(mt, mas.node != MA_ERROR(-ENOMEM));
MT_BUG_ON(mt, !mas_nomem(&mas, GFP_KERNEL));
MT_BUG_ON(mt, mas_alloc_req(&mas));
- MT_BUG_ON(mt, mas.alloc->node_count);
+ MT_BUG_ON(mt, mas.alloc->node_count != 1);
MT_BUG_ON(mt, mas_allocated(&mas) != MAPLE_ALLOC_SLOTS + 2);
mn = mas_pop_node(&mas);
MT_BUG_ON(mt, not_empty(mn));
MT_BUG_ON(mt, mas_allocated(&mas) != MAPLE_ALLOC_SLOTS + 1);
- MT_BUG_ON(mt, mas.alloc->node_count != MAPLE_ALLOC_SLOTS - 1);
+ MT_BUG_ON(mt, mas.alloc->node_count != MAPLE_ALLOC_SLOTS);
mas_push_node(&mas, mn);
- MT_BUG_ON(mt, mas.alloc->node_count);
+ MT_BUG_ON(mt, mas.alloc->node_count != 1);
MT_BUG_ON(mt, mas_allocated(&mas) != MAPLE_ALLOC_SLOTS + 2);
mn = mas_pop_node(&mas);
MT_BUG_ON(mt, not_empty(mn));
+ mn->parent = ma_parent_ptr(mn);
ma_free_rcu(mn);
for (i = 1; i <= MAPLE_ALLOC_SLOTS + 1; i++) {
mn = mas_pop_node(&mas);
MT_BUG_ON(mt, not_empty(mn));
+ mn->parent = ma_parent_ptr(mn);
ma_free_rcu(mn);
}
MT_BUG_ON(mt, mas_allocated(&mas) != 0);
@@ -375,6 +384,7 @@ static noinline void check_new_node(struct maple_tree *mt)
mas_node_count(&mas, i); /* Request */
mas_nomem(&mas, GFP_KERNEL); /* Fill request */
mn = mas_pop_node(&mas); /* get the next node. */
+ mn->parent = ma_parent_ptr(mn);
ma_free_rcu(mn);
mas_destroy(&mas);
@@ -382,10 +392,13 @@ static noinline void check_new_node(struct maple_tree *mt)
mas_node_count(&mas, i); /* Request */
mas_nomem(&mas, GFP_KERNEL); /* Fill request */
mn = mas_pop_node(&mas); /* get the next node. */
+ mn->parent = ma_parent_ptr(mn);
ma_free_rcu(mn);
mn = mas_pop_node(&mas); /* get the next node. */
+ mn->parent = ma_parent_ptr(mn);
ma_free_rcu(mn);
mn = mas_pop_node(&mas); /* get the next node. */
+ mn->parent = ma_parent_ptr(mn);
ma_free_rcu(mn);
mas_destroy(&mas);
}
@@ -35342,7 +35355,7 @@ static noinline void check_prealloc(struct maple_tree *mt)
for (i = 0; i <= max; i++)
mtree_test_store_range(mt, i * 10, i * 10 + 5, &i);
- MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0);
+ MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0);
allocated = mas_allocated(&mas);
height = mas_mt_height(&mas);
MT_BUG_ON(mt, allocated == 0);
@@ -35351,44 +35364,46 @@ static noinline void check_prealloc(struct maple_tree *mt)
allocated = mas_allocated(&mas);
MT_BUG_ON(mt, allocated != 0);
- MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0);
+ MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0);
allocated = mas_allocated(&mas);
height = mas_mt_height(&mas);
MT_BUG_ON(mt, allocated == 0);
MT_BUG_ON(mt, allocated != 1 + height * 3);
- MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0);
+ MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0);
mas_destroy(&mas);
allocated = mas_allocated(&mas);
MT_BUG_ON(mt, allocated != 0);
- MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0);
+ MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0);
allocated = mas_allocated(&mas);
height = mas_mt_height(&mas);
MT_BUG_ON(mt, allocated == 0);
MT_BUG_ON(mt, allocated != 1 + height * 3);
mn = mas_pop_node(&mas);
MT_BUG_ON(mt, mas_allocated(&mas) != allocated - 1);
+ mn->parent = ma_parent_ptr(mn);
ma_free_rcu(mn);
- MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0);
+ MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0);
mas_destroy(&mas);
allocated = mas_allocated(&mas);
MT_BUG_ON(mt, allocated != 0);
- MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0);
+ MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0);
allocated = mas_allocated(&mas);
height = mas_mt_height(&mas);
MT_BUG_ON(mt, allocated == 0);
MT_BUG_ON(mt, allocated != 1 + height * 3);
mn = mas_pop_node(&mas);
MT_BUG_ON(mt, mas_allocated(&mas) != allocated - 1);
- MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0);
+ MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0);
mas_destroy(&mas);
allocated = mas_allocated(&mas);
MT_BUG_ON(mt, allocated != 0);
+ mn->parent = ma_parent_ptr(mn);
ma_free_rcu(mn);
- MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0);
+ MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0);
allocated = mas_allocated(&mas);
height = mas_mt_height(&mas);
MT_BUG_ON(mt, allocated == 0);
@@ -35397,12 +35412,12 @@ static noinline void check_prealloc(struct maple_tree *mt)
MT_BUG_ON(mt, mas_allocated(&mas) != allocated - 1);
mas_push_node(&mas, mn);
MT_BUG_ON(mt, mas_allocated(&mas) != allocated);
- MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0);
+ MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0);
mas_destroy(&mas);
allocated = mas_allocated(&mas);
MT_BUG_ON(mt, allocated != 0);
- MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0);
+ MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0);
allocated = mas_allocated(&mas);
height = mas_mt_height(&mas);
MT_BUG_ON(mt, allocated == 0);
@@ -35410,21 +35425,21 @@ static noinline void check_prealloc(struct maple_tree *mt)
mas_store_prealloc(&mas, ptr);
MT_BUG_ON(mt, mas_allocated(&mas) != 0);
- MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0);
+ MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0);
allocated = mas_allocated(&mas);
height = mas_mt_height(&mas);
MT_BUG_ON(mt, allocated == 0);
MT_BUG_ON(mt, allocated != 1 + height * 3);
mas_store_prealloc(&mas, ptr);
MT_BUG_ON(mt, mas_allocated(&mas) != 0);
- MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0);
+ MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0);
allocated = mas_allocated(&mas);
height = mas_mt_height(&mas);
MT_BUG_ON(mt, allocated == 0);
MT_BUG_ON(mt, allocated != 1 + height * 3);
mas_store_prealloc(&mas, ptr);
- MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0);
+ MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0);
allocated = mas_allocated(&mas);
height = mas_mt_height(&mas);
MT_BUG_ON(mt, allocated == 0);
@@ -35432,14 +35447,14 @@ static noinline void check_prealloc(struct maple_tree *mt)
mas_store_prealloc(&mas, ptr);
MT_BUG_ON(mt, mas_allocated(&mas) != 0);
mt_set_non_kernel(1);
- MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL & GFP_NOWAIT) == 0);
+ MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL & GFP_NOWAIT) == 0);
allocated = mas_allocated(&mas);
height = mas_mt_height(&mas);
MT_BUG_ON(mt, allocated != 0);
mas_destroy(&mas);
- MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0);
+ MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0);
allocated = mas_allocated(&mas);
height = mas_mt_height(&mas);
MT_BUG_ON(mt, allocated == 0);
@@ -35447,7 +35462,7 @@ static noinline void check_prealloc(struct maple_tree *mt)
mas_store_prealloc(&mas, ptr);
MT_BUG_ON(mt, mas_allocated(&mas) != 0);
mt_set_non_kernel(1);
- MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL & GFP_NOWAIT) == 0);
+ MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL & GFP_NOWAIT) == 0);
allocated = mas_allocated(&mas);
height = mas_mt_height(&mas);
MT_BUG_ON(mt, allocated != 0);
@@ -35756,6 +35771,7 @@ void farmer_tests(void)
tree.ma_root = mt_mk_node(node, maple_leaf_64);
mt_dump(&tree);
+ node->parent = ma_parent_ptr(node);
ma_free_rcu(node);
/* Check things that will make lockdep angry */
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 0bc9592b5e62..13a6837a0c6b 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -86,7 +86,7 @@ TARGETS += tmpfs
TARGETS += tpm2
TARGETS += user
TARGETS += vDSO
-TARGETS += vm
+TARGETS += mm
TARGETS += x86
TARGETS += zram
#Please keep the TARGETS list alphabetically sorted
@@ -237,8 +237,8 @@ ifdef INSTALL_PATH
@# included in the generated runlist.
for TARGET in $(TARGETS); do \
BUILD_TARGET=$$BUILD/$$TARGET; \
- [ ! -d $(INSTALL_PATH)/$$TARGET ] && echo "Skipping non-existent dir: $$TARGET" && continue; \
- echo -ne "Emit Tests for $$TARGET\n"; \
+ [ ! -d $(INSTALL_PATH)/$$TARGET ] && printf "Skipping non-existent dir: $$TARGET\n" && continue; \
+ printf "Emit Tests for $$TARGET\n"; \
$(MAKE) -s --no-print-directory OUTPUT=$$BUILD_TARGET COLLECTION=$$TARGET \
-C $$TARGET emit_tests >> $(TEST_LIST); \
done;
diff --git a/tools/testing/selftests/amd-pstate/Makefile b/tools/testing/selftests/amd-pstate/Makefile
index 5fd1424db37d..c382f579fe94 100644
--- a/tools/testing/selftests/amd-pstate/Makefile
+++ b/tools/testing/selftests/amd-pstate/Makefile
@@ -4,10 +4,15 @@
# No binaries, but make sure arg-less "make" doesn't trigger "run_tests"
all:
-uname_M := $(shell uname -m 2>/dev/null || echo not)
-ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/)
+ARCH ?= $(shell uname -m 2>/dev/null || echo not)
+ARCH := $(shell echo $(ARCH) | sed -e s/i.86/x86/ -e s/x86_64/x86/)
-TEST_PROGS := run.sh
-TEST_FILES := basic.sh tbench.sh gitsource.sh
+ifeq (x86,$(ARCH))
+TEST_FILES += ../../../power/x86/amd_pstate_tracer/amd_pstate_trace.py
+TEST_FILES += ../../../power/x86/intel_pstate_tracer/intel_pstate_tracer.py
+endif
+
+TEST_PROGS += run.sh
+TEST_FILES += basic.sh tbench.sh gitsource.sh
include ../lib.mk
diff --git a/tools/testing/selftests/arm64/fp/Makefile b/tools/testing/selftests/arm64/fp/Makefile
index 50a70220ba6c..48f56c86ad45 100644
--- a/tools/testing/selftests/arm64/fp/Makefile
+++ b/tools/testing/selftests/arm64/fp/Makefile
@@ -3,7 +3,7 @@
# A proper top_srcdir is needed by KSFT(lib.mk)
top_srcdir = $(realpath ../../../../../)
-CFLAGS += -I$(top_srcdir)/usr/include/
+CFLAGS += $(KHDR_INCLUDES)
TEST_GEN_PROGS := fp-stress \
sve-ptrace sve-probe-vls \
diff --git a/tools/testing/selftests/arm64/tags/Makefile b/tools/testing/selftests/arm64/tags/Makefile
index 41cb75070511..6d29cfde43a2 100644
--- a/tools/testing/selftests/arm64/tags/Makefile
+++ b/tools/testing/selftests/arm64/tags/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
-CFLAGS += -I../../../../../usr/include/
+CFLAGS += $(KHDR_INCLUDES)
TEST_GEN_PROGS := tags_test
TEST_PROGS := run_tags_test.sh
diff --git a/tools/testing/selftests/bpf/prog_tests/btf.c b/tools/testing/selftests/bpf/prog_tests/btf.c
index cbb600be943d..210d643fda6c 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf.c
@@ -879,6 +879,34 @@ static struct btf_raw_test raw_tests[] = {
.btf_load_err = true,
.err_str = "Invalid elem",
},
+{
+ .descr = "var after datasec, ptr followed by modifier",
+ .raw_types = {
+ /* .bss section */ /* [1] */
+ BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 2),
+ sizeof(void*)+4),
+ BTF_VAR_SECINFO_ENC(4, 0, sizeof(void*)),
+ BTF_VAR_SECINFO_ENC(6, sizeof(void*), 4),
+ /* int */ /* [2] */
+ BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),
+ /* int* */ /* [3] */
+ BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_PTR, 0, 0), 2),
+ BTF_VAR_ENC(NAME_TBD, 3, 0), /* [4] */
+ /* const int */ /* [5] */
+ BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_CONST, 0, 0), 2),
+ BTF_VAR_ENC(NAME_TBD, 5, 0), /* [6] */
+ BTF_END_RAW,
+ },
+ .str_sec = "\0a\0b\0c\0",
+ .str_sec_size = sizeof("\0a\0b\0c\0"),
+ .map_type = BPF_MAP_TYPE_ARRAY,
+ .map_name = ".bss",
+ .key_size = sizeof(int),
+ .value_size = sizeof(void*)+4,
+ .key_type_id = 0,
+ .value_type_id = 1,
+ .max_entries = 1,
+},
/* Test member exceeds the size of struct.
*
* struct A {
diff --git a/tools/testing/selftests/bpf/prog_tests/uninit_stack.c b/tools/testing/selftests/bpf/prog_tests/uninit_stack.c
new file mode 100644
index 000000000000..e64c71948491
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/uninit_stack.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <test_progs.h>
+#include "uninit_stack.skel.h"
+
+void test_uninit_stack(void)
+{
+ RUN_TESTS(uninit_stack);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c b/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c
index 2666c84dbd01..7271a18ab3e2 100644
--- a/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c
+++ b/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c
@@ -65,12 +65,13 @@ static int attach_tc_prog(struct bpf_tc_hook *hook, int fd)
}
/* The maximum permissible size is: PAGE_SIZE - sizeof(struct xdp_page_head) -
- * sizeof(struct skb_shared_info) - XDP_PACKET_HEADROOM = 3368 bytes
+ * SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) - XDP_PACKET_HEADROOM =
+ * 3408 bytes for 64-byte cacheline and 3216 for 256-byte one.
*/
#if defined(__s390x__)
-#define MAX_PKT_SIZE 3176
+#define MAX_PKT_SIZE 3216
#else
-#define MAX_PKT_SIZE 3368
+#define MAX_PKT_SIZE 3408
#endif
static void test_max_pkt_size(int fd)
{
diff --git a/tools/testing/selftests/bpf/progs/find_vma_fail1.c b/tools/testing/selftests/bpf/progs/find_vma_fail1.c
index b3b326b8e2d1..6dab9cffda13 100644
--- a/tools/testing/selftests/bpf/progs/find_vma_fail1.c
+++ b/tools/testing/selftests/bpf/progs/find_vma_fail1.c
@@ -2,6 +2,7 @@
/* Copyright (c) 2021 Facebook */
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
+#define vm_flags vm_start
char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/test_deny_namespace.c b/tools/testing/selftests/bpf/progs/test_deny_namespace.c
index 09ad5a4ebd1f..e96b901a733c 100644
--- a/tools/testing/selftests/bpf/progs/test_deny_namespace.c
+++ b/tools/testing/selftests/bpf/progs/test_deny_namespace.c
@@ -5,12 +5,10 @@
#include <errno.h>
#include <linux/capability.h>
-struct kernel_cap_struct {
- __u32 cap[_LINUX_CAPABILITY_U32S_3];
-} __attribute__((preserve_access_index));
+typedef struct { unsigned long long val; } kernel_cap_t;
struct cred {
- struct kernel_cap_struct cap_effective;
+ kernel_cap_t cap_effective;
} __attribute__((preserve_access_index));
char _license[] SEC("license") = "GPL";
@@ -18,15 +16,14 @@ char _license[] SEC("license") = "GPL";
SEC("lsm.s/userns_create")
int BPF_PROG(test_userns_create, const struct cred *cred, int ret)
{
- struct kernel_cap_struct caps = cred->cap_effective;
- int cap_index = CAP_TO_INDEX(CAP_SYS_ADMIN);
- __u32 cap_mask = CAP_TO_MASK(CAP_SYS_ADMIN);
+ kernel_cap_t caps = cred->cap_effective;
+ __u64 cap_mask = 1ULL << CAP_SYS_ADMIN;
if (ret)
return 0;
ret = -EPERM;
- if (caps.cap[cap_index] & cap_mask)
+ if (caps.val & cap_mask)
return 0;
return -EPERM;
diff --git a/tools/testing/selftests/bpf/progs/test_global_func10.c b/tools/testing/selftests/bpf/progs/test_global_func10.c
index 98327bdbbfd2..8fba3f3649e2 100644
--- a/tools/testing/selftests/bpf/progs/test_global_func10.c
+++ b/tools/testing/selftests/bpf/progs/test_global_func10.c
@@ -5,12 +5,12 @@
#include "bpf_misc.h"
struct Small {
- int x;
+ long x;
};
struct Big {
- int x;
- int y;
+ long x;
+ long y;
};
__noinline int foo(const struct Big *big)
@@ -22,7 +22,7 @@ __noinline int foo(const struct Big *big)
}
SEC("cgroup_skb/ingress")
-__failure __msg("invalid indirect read from stack")
+__failure __msg("invalid indirect access to stack")
int global_func10(struct __sk_buff *skb)
{
const struct Small small = {.x = skb->len };
diff --git a/tools/testing/selftests/bpf/progs/uninit_stack.c b/tools/testing/selftests/bpf/progs/uninit_stack.c
new file mode 100644
index 000000000000..8a403470e557
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/uninit_stack.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+
+/* Read an uninitialized value from stack at a fixed offset */
+SEC("socket")
+__naked int read_uninit_stack_fixed_off(void *ctx)
+{
+ asm volatile (" \
+ r0 = 0; \
+ /* force stack depth to be 128 */ \
+ *(u64*)(r10 - 128) = r1; \
+ r1 = *(u8 *)(r10 - 8 ); \
+ r0 += r1; \
+ r1 = *(u8 *)(r10 - 11); \
+ r1 = *(u8 *)(r10 - 13); \
+ r1 = *(u8 *)(r10 - 15); \
+ r1 = *(u16*)(r10 - 16); \
+ r1 = *(u32*)(r10 - 32); \
+ r1 = *(u64*)(r10 - 64); \
+ /* read from a spill of a wrong size, it is a separate \
+ * branch in check_stack_read_fixed_off() \
+ */ \
+ *(u32*)(r10 - 72) = r1; \
+ r1 = *(u64*)(r10 - 72); \
+ r0 = 0; \
+ exit; \
+"
+ ::: __clobber_all);
+}
+
+/* Read an uninitialized value from stack at a variable offset */
+SEC("socket")
+__naked int read_uninit_stack_var_off(void *ctx)
+{
+ asm volatile (" \
+ call %[bpf_get_prandom_u32]; \
+ /* force stack depth to be 64 */ \
+ *(u64*)(r10 - 64) = r0; \
+ r0 = -r0; \
+ /* give r0 a range [-31, -1] */ \
+ if r0 s<= -32 goto exit_%=; \
+ if r0 s>= 0 goto exit_%=; \
+ /* access stack using r0 */ \
+ r1 = r10; \
+ r1 += r0; \
+ r2 = *(u8*)(r1 + 0); \
+exit_%=: r0 = 0; \
+ exit; \
+"
+ :
+ : __imm(bpf_get_prandom_u32)
+ : __clobber_all);
+}
+
+static __noinline void dummy(void) {}
+
+/* Pass a pointer to uninitialized stack memory to a helper.
+ * Passed memory block should be marked as STACK_MISC after helper call.
+ */
+SEC("socket")
+__log_level(7) __msg("fp-104=mmmmmmmm")
+__naked int helper_uninit_to_misc(void *ctx)
+{
+ asm volatile (" \
+ /* force stack depth to be 128 */ \
+ *(u64*)(r10 - 128) = r1; \
+ r1 = r10; \
+ r1 += -128; \
+ r2 = 32; \
+ call %[bpf_trace_printk]; \
+ /* Call to dummy() forces print_verifier_state(..., true), \
+ * thus showing the stack state, matched by __msg(). \
+ */ \
+ call %[dummy]; \
+ r0 = 0; \
+ exit; \
+"
+ :
+ : __imm(bpf_trace_printk),
+ __imm(dummy)
+ : __clobber_all);
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/verifier/calls.c b/tools/testing/selftests/bpf/verifier/calls.c
index 9d993926bf0e..289ed202ec66 100644
--- a/tools/testing/selftests/bpf/verifier/calls.c
+++ b/tools/testing/selftests/bpf/verifier/calls.c
@@ -2221,19 +2221,22 @@
* that fp-8 stack slot was unused in the fall-through
* branch and will accept the program incorrectly
*/
- BPF_JMP_IMM(BPF_JGT, BPF_REG_1, 2, 2),
+ BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32),
+ BPF_JMP_IMM(BPF_JGT, BPF_REG_0, 2, 2),
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
BPF_JMP_IMM(BPF_JA, 0, 0, 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_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
},
- .fixup_map_hash_48b = { 6 },
- .errstr = "invalid indirect read from stack R2 off -8+0 size 8",
- .result = REJECT,
- .prog_type = BPF_PROG_TYPE_XDP,
+ .fixup_map_hash_48b = { 7 },
+ .errstr_unpriv = "invalid indirect read from stack R2 off -8+0 size 8",
+ .result_unpriv = REJECT,
+ /* in privileged mode reads from uninitialized stack locations are permitted */
+ .result = ACCEPT,
},
{
"calls: ctx read at start of subprog",
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 a6c869a7319c..9c4885885aba 100644
--- a/tools/testing/selftests/bpf/verifier/helper_access_var_len.c
+++ b/tools/testing/selftests/bpf/verifier/helper_access_var_len.c
@@ -29,19 +29,30 @@
{
"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_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),
- BPF_MOV64_IMM(BPF_REG_3, 0),
- BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel),
+ /* set max stack size */
+ BPF_ST_MEM(BPF_DW, BPF_REG_10, -128, 0),
+ /* set r3 to a random value */
+ BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32),
+ BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
+ /* use bitwise AND to limit r3 range to [0, 64] */
+ BPF_ALU64_IMM(BPF_AND, BPF_REG_3, 64),
+ BPF_LD_MAP_FD(BPF_REG_1, 0),
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -64),
+ BPF_MOV64_IMM(BPF_REG_4, 0),
+ /* Call bpf_ringbuf_output(), it is one of a few helper functions with
+ * ARG_CONST_SIZE_OR_ZERO parameter allowed in unpriv mode.
+ * For unpriv this should signal an error, because memory at &fp[-64] is
+ * not initialized.
+ */
+ BPF_EMIT_CALL(BPF_FUNC_ringbuf_output),
BPF_EXIT_INSN(),
},
- .errstr = "invalid indirect read from stack R1 off -64+0 size 64",
- .result = REJECT,
- .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+ .fixup_map_ringbuf = { 4 },
+ .errstr_unpriv = "invalid indirect read from stack R2 off -64+0 size 64",
+ .result_unpriv = REJECT,
+ /* in privileged mode reads from uninitialized stack locations are permitted */
+ .result = ACCEPT,
},
{
"helper access to variable memory: stack, bitwise AND + JMP, wrong max",
@@ -183,20 +194,31 @@
{
"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_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),
- BPF_MOV64_IMM(BPF_REG_3, 0),
- BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel),
+ /* set max stack size */
+ BPF_ST_MEM(BPF_DW, BPF_REG_10, -128, 0),
+ /* set r3 to a random value */
+ BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32),
+ BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
+ /* use JMP to limit r3 range to [0, 64] */
+ BPF_JMP_IMM(BPF_JGT, BPF_REG_3, 64, 6),
+ BPF_LD_MAP_FD(BPF_REG_1, 0),
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -64),
+ BPF_MOV64_IMM(BPF_REG_4, 0),
+ /* Call bpf_ringbuf_output(), it is one of a few helper functions with
+ * ARG_CONST_SIZE_OR_ZERO parameter allowed in unpriv mode.
+ * For unpriv this should signal an error, because memory at &fp[-64] is
+ * not initialized.
+ */
+ BPF_EMIT_CALL(BPF_FUNC_ringbuf_output),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
},
- .errstr = "invalid indirect read from stack R1 off -64+0 size 64",
- .result = REJECT,
- .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+ .fixup_map_ringbuf = { 4 },
+ .errstr_unpriv = "invalid indirect read from stack R2 off -64+0 size 64",
+ .result_unpriv = REJECT,
+ /* in privileged mode reads from uninitialized stack locations are permitted */
+ .result = ACCEPT,
},
{
"helper access to variable memory: stack, JMP (signed), no min check",
@@ -564,29 +586,41 @@
{
"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),
+ /* set max stack size */
+ BPF_ST_MEM(BPF_DW, BPF_REG_10, -128, 0),
+ /* set r3 to a random value */
+ BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32),
+ BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
+ BPF_LD_MAP_FD(BPF_REG_1, 0),
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -64),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -64),
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -56),
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -48),
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -40),
+ /* Note: fp[-32] left uninitialized */
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_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),
- BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1),
- BPF_MOV64_IMM(BPF_REG_3, 0),
- BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel),
- BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16),
+ /* Limit r3 range to [1, 64] */
+ BPF_ALU64_IMM(BPF_AND, BPF_REG_3, 63),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 1),
+ BPF_MOV64_IMM(BPF_REG_4, 0),
+ /* Call bpf_ringbuf_output(), it is one of a few helper functions with
+ * ARG_CONST_SIZE_OR_ZERO parameter allowed in unpriv mode.
+ * For unpriv this should signal an error, because memory region [1, 64]
+ * at &fp[-64] is not fully initialized.
+ */
+ BPF_EMIT_CALL(BPF_FUNC_ringbuf_output),
+ BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
},
- .errstr = "invalid indirect read from stack R1 off -64+32 size 64",
- .result = REJECT,
- .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+ .fixup_map_ringbuf = { 3 },
+ .errstr_unpriv = "invalid indirect read from stack R2 off -64+32 size 64",
+ .result_unpriv = REJECT,
+ /* in privileged mode reads from uninitialized stack locations are permitted */
+ .result = ACCEPT,
},
{
"helper access to variable memory: 8 bytes no leak (init memory)",
diff --git a/tools/testing/selftests/bpf/verifier/int_ptr.c b/tools/testing/selftests/bpf/verifier/int_ptr.c
index 070893fb2900..02d9e004260b 100644
--- a/tools/testing/selftests/bpf/verifier/int_ptr.c
+++ b/tools/testing/selftests/bpf/verifier/int_ptr.c
@@ -54,12 +54,13 @@
/* bpf_strtoul() */
BPF_EMIT_CALL(BPF_FUNC_strtoul),
- BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
},
- .result = REJECT,
- .prog_type = BPF_PROG_TYPE_CGROUP_SYSCTL,
- .errstr = "invalid indirect read from stack R4 off -16+4 size 8",
+ .result_unpriv = REJECT,
+ .errstr_unpriv = "invalid indirect read from stack R4 off -16+4 size 8",
+ /* in privileged mode reads from uninitialized stack locations are permitted */
+ .result = ACCEPT,
},
{
"ARG_PTR_TO_LONG misaligned",
diff --git a/tools/testing/selftests/bpf/verifier/search_pruning.c b/tools/testing/selftests/bpf/verifier/search_pruning.c
index d63fd8991b03..745d6b5842fd 100644
--- a/tools/testing/selftests/bpf/verifier/search_pruning.c
+++ b/tools/testing/selftests/bpf/verifier/search_pruning.c
@@ -128,9 +128,10 @@
BPF_EXIT_INSN(),
},
.fixup_map_hash_8b = { 3 },
- .errstr = "invalid read from stack off -16+0 size 8",
- .result = REJECT,
- .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+ .errstr_unpriv = "invalid read from stack off -16+0 size 8",
+ .result_unpriv = REJECT,
+ /* in privileged mode reads from uninitialized stack locations are permitted */
+ .result = ACCEPT,
},
{
"precision tracking for u32 spill/fill",
@@ -258,6 +259,8 @@
BPF_EXIT_INSN(),
},
.flags = BPF_F_TEST_STATE_FREQ,
- .errstr = "invalid read from stack off -8+1 size 8",
- .result = REJECT,
+ .errstr_unpriv = "invalid read from stack off -8+1 size 8",
+ .result_unpriv = REJECT,
+ /* in privileged mode reads from uninitialized stack locations are permitted */
+ .result = ACCEPT,
},
diff --git a/tools/testing/selftests/bpf/verifier/sock.c b/tools/testing/selftests/bpf/verifier/sock.c
index d11d0b28be41..108dd3ee1edd 100644
--- a/tools/testing/selftests/bpf/verifier/sock.c
+++ b/tools/testing/selftests/bpf/verifier/sock.c
@@ -531,33 +531,6 @@
.result = ACCEPT,
},
{
- "sk_storage_get(map, skb->sk, &stack_value, 1): partially init stack_value",
- .insns = {
- BPF_MOV64_IMM(BPF_REG_2, 0),
- BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_2, -8),
- BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
- BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
- BPF_MOV64_IMM(BPF_REG_0, 0),
- BPF_EXIT_INSN(),
- BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
- BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
- BPF_MOV64_IMM(BPF_REG_0, 0),
- BPF_EXIT_INSN(),
- BPF_MOV64_IMM(BPF_REG_4, 1),
- BPF_MOV64_REG(BPF_REG_3, BPF_REG_10),
- BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -8),
- BPF_MOV64_REG(BPF_REG_2, BPF_REG_0),
- BPF_LD_MAP_FD(BPF_REG_1, 0),
- BPF_EMIT_CALL(BPF_FUNC_sk_storage_get),
- BPF_MOV64_IMM(BPF_REG_0, 0),
- BPF_EXIT_INSN(),
- },
- .fixup_sk_storage_map = { 14 },
- .prog_type = BPF_PROG_TYPE_SCHED_CLS,
- .result = REJECT,
- .errstr = "invalid indirect read from stack",
-},
-{
"bpf_map_lookup_elem(smap, &key)",
.insns = {
BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
diff --git a/tools/testing/selftests/bpf/verifier/spill_fill.c b/tools/testing/selftests/bpf/verifier/spill_fill.c
index 9bb302dade23..d1463bf4949a 100644
--- a/tools/testing/selftests/bpf/verifier/spill_fill.c
+++ b/tools/testing/selftests/bpf/verifier/spill_fill.c
@@ -171,9 +171,10 @@
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
},
- .result = REJECT,
- .errstr = "invalid read from stack off -4+0 size 4",
- .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+ .result_unpriv = REJECT,
+ .errstr_unpriv = "invalid read from stack off -4+0 size 4",
+ /* in privileged mode reads from uninitialized stack locations are permitted */
+ .result = ACCEPT,
},
{
"Spill a u32 const scalar. Refill as u16. Offset to skb->data",
diff --git a/tools/testing/selftests/bpf/verifier/var_off.c b/tools/testing/selftests/bpf/verifier/var_off.c
index d37f512fad16..b183e26c03f1 100644
--- a/tools/testing/selftests/bpf/verifier/var_off.c
+++ b/tools/testing/selftests/bpf/verifier/var_off.c
@@ -213,31 +213,6 @@
.prog_type = BPF_PROG_TYPE_LWT_IN,
},
{
- "indirect variable-offset stack access, max_off+size > max_initialized",
- .insns = {
- /* Fill only the second from top 8 bytes of the stack. */
- BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, 0),
- /* Get an unknown value. */
- BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 0),
- /* Make it small and 4-byte aligned. */
- BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 4),
- BPF_ALU64_IMM(BPF_SUB, BPF_REG_2, 16),
- /* Add it to fp. We now have either fp-12 or fp-16, but we don't know
- * which. fp-12 size 8 is partially uninitialized stack.
- */
- BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_10),
- /* Dereference it indirectly. */
- BPF_LD_MAP_FD(BPF_REG_1, 0),
- BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
- BPF_MOV64_IMM(BPF_REG_0, 0),
- BPF_EXIT_INSN(),
- },
- .fixup_map_hash_8b = { 5 },
- .errstr = "invalid indirect read from stack R2 var_off",
- .result = REJECT,
- .prog_type = BPF_PROG_TYPE_LWT_IN,
-},
-{
"indirect variable-offset stack access, min_off < min_initialized",
.insns = {
/* Fill only the top 8 bytes of the stack. */
@@ -290,33 +265,6 @@
.prog_type = BPF_PROG_TYPE_CGROUP_SKB,
},
{
- "indirect variable-offset stack access, uninitialized",
- .insns = {
- BPF_MOV64_IMM(BPF_REG_2, 6),
- BPF_MOV64_IMM(BPF_REG_3, 28),
- /* Fill the top 16 bytes of the stack. */
- BPF_ST_MEM(BPF_W, BPF_REG_10, -16, 0),
- BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
- /* Get an unknown value. */
- BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_1, 0),
- /* Make it small and 4-byte aligned. */
- BPF_ALU64_IMM(BPF_AND, BPF_REG_4, 4),
- BPF_ALU64_IMM(BPF_SUB, BPF_REG_4, 16),
- /* Add it to fp. We now have either fp-12 or fp-16, we don't know
- * which, but either way it points to initialized stack.
- */
- BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_10),
- BPF_MOV64_IMM(BPF_REG_5, 8),
- /* Dereference it indirectly. */
- BPF_EMIT_CALL(BPF_FUNC_getsockopt),
- BPF_MOV64_IMM(BPF_REG_0, 0),
- BPF_EXIT_INSN(),
- },
- .errstr = "invalid indirect read from stack R4 var_off",
- .result = REJECT,
- .prog_type = BPF_PROG_TYPE_SOCK_OPS,
-},
-{
"indirect variable-offset stack access, ok",
.insns = {
/* Fill the top 16 bytes of the stack. */
diff --git a/tools/testing/selftests/clone3/Makefile b/tools/testing/selftests/clone3/Makefile
index 79b19a2863a0..84832c369a2e 100644
--- a/tools/testing/selftests/clone3/Makefile
+++ b/tools/testing/selftests/clone3/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-CFLAGS += -g -std=gnu99 -I../../../../usr/include/
+CFLAGS += -g -std=gnu99 $(KHDR_INCLUDES)
LDLIBS += -lcap
TEST_GEN_PROGS := clone3 clone3_clear_sighand clone3_set_tid \
diff --git a/tools/testing/selftests/clone3/clone3.c b/tools/testing/selftests/clone3/clone3.c
index cd4582129c7d..4fce46afe6db 100644
--- a/tools/testing/selftests/clone3/clone3.c
+++ b/tools/testing/selftests/clone3/clone3.c
@@ -195,5 +195,8 @@ int main(int argc, char *argv[])
test_clone3(CLONE_NEWPID, getpagesize() + 8, -E2BIG,
CLONE3_ARGS_NO_TEST);
+ /* Do a clone3() in a new time namespace */
+ test_clone3(CLONE_NEWTIME, 0, 0, CLONE3_ARGS_NO_TEST);
+
return !ksft_get_fail_cnt() ? ksft_exit_pass() : ksft_exit_fail();
}
diff --git a/tools/testing/selftests/core/Makefile b/tools/testing/selftests/core/Makefile
index f6f2d6f473c6..ce262d097269 100644
--- a/tools/testing/selftests/core/Makefile
+++ b/tools/testing/selftests/core/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-CFLAGS += -g -I../../../../usr/include/
+CFLAGS += -g $(KHDR_INCLUDES)
TEST_GEN_PROGS := close_range_test
diff --git a/tools/testing/selftests/damon/debugfs_rm_non_contexts.sh b/tools/testing/selftests/damon/debugfs_rm_non_contexts.sh
index 48b7af6b022c..f3ffeb1343cf 100644
--- a/tools/testing/selftests/damon/debugfs_rm_non_contexts.sh
+++ b/tools/testing/selftests/damon/debugfs_rm_non_contexts.sh
@@ -10,7 +10,7 @@ dmesg -C
for file in "$DBGFS/"*
do
- echo "$(basename "$f")" > "$DBGFS/rm_contexts"
+ (echo "$(basename "$f")" > "$DBGFS/rm_contexts") &> /dev/null
if dmesg | grep -q BUG
then
dmesg
diff --git a/tools/testing/selftests/damon/sysfs.sh b/tools/testing/selftests/damon/sysfs.sh
index db4942383a50..bcd4734ca094 100644
--- a/tools/testing/selftests/damon/sysfs.sh
+++ b/tools/testing/selftests/damon/sysfs.sh
@@ -24,7 +24,7 @@ ensure_write_fail()
content=$2
reason=$3
- if echo "$content" > "$file"
+ if (echo "$content" > "$file") 2> /dev/null
then
echo "writing $content to $file succeed ($fail_reason)"
echo "expected failure because $reason"
@@ -96,6 +96,34 @@ test_stats()
done
}
+test_filter()
+{
+ filter_dir=$1
+ ensure_file "$filter_dir/type" "exist" "600"
+ ensure_write_succ "$filter_dir/type" "anon" "valid input"
+ ensure_write_succ "$filter_dir/type" "memcg" "valid input"
+ ensure_write_fail "$filter_dir/type" "foo" "invalid input"
+ ensure_file "$filter_dir/matching" "exist" "600"
+ ensure_file "$filter_dir/memcg_path" "exist" "600"
+}
+
+test_filters()
+{
+ filters_dir=$1
+ ensure_dir "$filters_dir" "exist"
+ ensure_file "$filters_dir/nr_filters" "exist" "600"
+ ensure_write_succ "$filters_dir/nr_filters" "1" "valid input"
+ test_filter "$filters_dir/0"
+
+ ensure_write_succ "$filters_dir/nr_filters" "2" "valid input"
+ test_filter "$filters_dir/0"
+ test_filter "$filters_dir/1"
+
+ ensure_write_succ "$filters_dir/nr_filters" "0" "valid input"
+ ensure_dir "$filters_dir/0" "not_exist"
+ ensure_dir "$filters_dir/1" "not_exist"
+}
+
test_watermarks()
{
watermarks_dir=$1
@@ -143,6 +171,7 @@ test_scheme()
test_access_pattern "$scheme_dir/access_pattern"
test_quotas "$scheme_dir/quotas"
test_watermarks "$scheme_dir/watermarks"
+ test_filters "$scheme_dir/filters"
test_stats "$scheme_dir/stats"
test_tried_regions "$scheme_dir/tried_regions"
}
diff --git a/tools/testing/selftests/dmabuf-heaps/Makefile b/tools/testing/selftests/dmabuf-heaps/Makefile
index 604b43ece15f..9e7e158d5fa3 100644
--- a/tools/testing/selftests/dmabuf-heaps/Makefile
+++ b/tools/testing/selftests/dmabuf-heaps/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-CFLAGS += -static -O3 -Wl,-no-as-needed -Wall
+CFLAGS += -static -O3 -Wl,-no-as-needed -Wall $(KHDR_INCLUDES)
TEST_GEN_PROGS = dmabuf-heap
diff --git a/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c b/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c
index 29af27acd40e..890a8236a8ba 100644
--- a/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c
+++ b/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c
@@ -13,10 +13,9 @@
#include <sys/types.h>
#include <linux/dma-buf.h>
+#include <linux/dma-heap.h>
#include <drm/drm.h>
-#include "../../../../include/uapi/linux/dma-heap.h"
-
#define DEVPATH "/dev/dma_heap"
static int check_vgem(int fd)
diff --git a/tools/testing/selftests/drivers/dma-buf/Makefile b/tools/testing/selftests/drivers/dma-buf/Makefile
index 79cb16b4e01a..441407bb0e80 100644
--- a/tools/testing/selftests/drivers/dma-buf/Makefile
+++ b/tools/testing/selftests/drivers/dma-buf/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-CFLAGS += -I../../../../../usr/include/
+CFLAGS += $(KHDR_INCLUDES)
TEST_GEN_PROGS := udmabuf
diff --git a/tools/testing/selftests/drivers/net/bonding/Makefile b/tools/testing/selftests/drivers/net/bonding/Makefile
index 8e3b786a748f..a39bb2560d9b 100644
--- a/tools/testing/selftests/drivers/net/bonding/Makefile
+++ b/tools/testing/selftests/drivers/net/bonding/Makefile
@@ -8,7 +8,8 @@ TEST_PROGS := \
dev_addr_lists.sh \
mode-1-recovery-updelay.sh \
mode-2-recovery-updelay.sh \
- option_prio.sh
+ option_prio.sh \
+ bond-eth-type-change.sh
TEST_FILES := \
lag_lib.sh \
diff --git a/tools/testing/selftests/drivers/net/bonding/bond-eth-type-change.sh b/tools/testing/selftests/drivers/net/bonding/bond-eth-type-change.sh
new file mode 100755
index 000000000000..5cdd22048ba7
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/bonding/bond-eth-type-change.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test bond device ether type changing
+#
+
+ALL_TESTS="
+ bond_test_unsuccessful_enslave_type_change
+ bond_test_successful_enslave_type_change
+"
+REQUIRE_MZ=no
+NUM_NETIFS=0
+lib_dir=$(dirname "$0")
+source "$lib_dir"/net_forwarding_lib.sh
+
+bond_check_flags()
+{
+ local bonddev=$1
+
+ ip -d l sh dev "$bonddev" | grep -q "MASTER"
+ check_err $? "MASTER flag is missing from the bond device"
+
+ ip -d l sh dev "$bonddev" | grep -q "SLAVE"
+ check_err $? "SLAVE flag is missing from the bond device"
+}
+
+# test enslaved bond dev type change from ARPHRD_ETHER and back
+# this allows us to test both MASTER and SLAVE flags at once
+bond_test_enslave_type_change()
+{
+ local test_success=$1
+ local devbond0="test-bond0"
+ local devbond1="test-bond1"
+ local devbond2="test-bond2"
+ local nonethdev="test-noneth0"
+
+ # create a non-ARPHRD_ETHER device for testing (e.g. nlmon type)
+ ip link add name "$nonethdev" type nlmon
+ check_err $? "could not create a non-ARPHRD_ETHER device (nlmon)"
+ ip link add name "$devbond0" type bond
+ if [ $test_success -eq 1 ]; then
+ # we need devbond0 in active-backup mode to successfully enslave nonethdev
+ ip link set dev "$devbond0" type bond mode active-backup
+ check_err $? "could not change bond mode to active-backup"
+ fi
+ ip link add name "$devbond1" type bond
+ ip link add name "$devbond2" type bond
+ ip link set dev "$devbond0" master "$devbond1"
+ check_err $? "could not enslave $devbond0 to $devbond1"
+ # change bond type to non-ARPHRD_ETHER
+ ip link set dev "$nonethdev" master "$devbond0" 1>/dev/null 2>/dev/null
+ ip link set dev "$nonethdev" nomaster 1>/dev/null 2>/dev/null
+ # restore ARPHRD_ETHER type by enslaving such device
+ ip link set dev "$devbond2" master "$devbond0"
+ check_err $? "could not enslave $devbond2 to $devbond0"
+ ip link set dev "$devbond1" nomaster
+
+ bond_check_flags "$devbond0"
+
+ # clean up
+ ip link del dev "$devbond0"
+ ip link del dev "$devbond1"
+ ip link del dev "$devbond2"
+ ip link del dev "$nonethdev"
+}
+
+bond_test_unsuccessful_enslave_type_change()
+{
+ RET=0
+
+ bond_test_enslave_type_change 0
+ log_test "Change ether type of an enslaved bond device with unsuccessful enslave"
+}
+
+bond_test_successful_enslave_type_change()
+{
+ RET=0
+
+ bond_test_enslave_type_change 1
+ log_test "Change ether type of an enslaved bond device with successful enslave"
+}
+
+tests_run
+
+exit "$EXIT_STATUS"
diff --git a/tools/testing/selftests/drivers/s390x/uvdevice/Makefile b/tools/testing/selftests/drivers/s390x/uvdevice/Makefile
index 891215a7dc8a..755d164384c4 100644
--- a/tools/testing/selftests/drivers/s390x/uvdevice/Makefile
+++ b/tools/testing/selftests/drivers/s390x/uvdevice/Makefile
@@ -11,10 +11,9 @@ else
TEST_GEN_PROGS := test_uvdevice
top_srcdir ?= ../../../../../..
-khdr_dir = $(top_srcdir)/usr/include
LINUX_TOOL_ARCH_INCLUDE = $(top_srcdir)/tools/arch/$(ARCH)/include
-CFLAGS += -Wall -Werror -static -I$(khdr_dir) -I$(LINUX_TOOL_ARCH_INCLUDE)
+CFLAGS += -Wall -Werror -static $(KHDR_INCLUDES) -I$(LINUX_TOOL_ARCH_INCLUDE)
include ../../../lib.mk
diff --git a/tools/testing/selftests/filesystems/Makefile b/tools/testing/selftests/filesystems/Makefile
index 129880fb42d3..c647fd6a0446 100644
--- a/tools/testing/selftests/filesystems/Makefile
+++ b/tools/testing/selftests/filesystems/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
-CFLAGS += -I../../../../usr/include/
+CFLAGS += $(KHDR_INCLUDES)
TEST_GEN_PROGS := devpts_pts
TEST_GEN_PROGS_EXTENDED := dnotify_test
diff --git a/tools/testing/selftests/filesystems/binderfs/Makefile b/tools/testing/selftests/filesystems/binderfs/Makefile
index 8af25ae96049..c2f7cef919c0 100644
--- a/tools/testing/selftests/filesystems/binderfs/Makefile
+++ b/tools/testing/selftests/filesystems/binderfs/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
-CFLAGS += -I../../../../../usr/include/ -pthread
+CFLAGS += $(KHDR_INCLUDES) -pthread
TEST_GEN_PROGS := binderfs_test
binderfs_test: binderfs_test.c ../../kselftest.h ../../kselftest_harness.h
diff --git a/tools/testing/selftests/filesystems/epoll/Makefile b/tools/testing/selftests/filesystems/epoll/Makefile
index 78ae4aaf7141..0788a7dc8004 100644
--- a/tools/testing/selftests/filesystems/epoll/Makefile
+++ b/tools/testing/selftests/filesystems/epoll/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
-CFLAGS += -I../../../../../usr/include/
+CFLAGS += $(KHDR_INCLUDES)
LDLIBS += -lpthread
TEST_GEN_PROGS := epoll_wakeup_test
diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/eprobes_syntax_errors.tc b/tools/testing/selftests/ftrace/test.d/dynevent/eprobes_syntax_errors.tc
index fc1daac7f066..4f5e8c665156 100644
--- a/tools/testing/selftests/ftrace/test.d/dynevent/eprobes_syntax_errors.tc
+++ b/tools/testing/selftests/ftrace/test.d/dynevent/eprobes_syntax_errors.tc
@@ -22,6 +22,8 @@ check_error 'e:foo/^bar.1 syscalls/sys_enter_openat' # BAD_EVENT_NAME
check_error 'e:foo/bar syscalls/sys_enter_openat arg=^dfd' # BAD_FETCH_ARG
check_error 'e:foo/bar syscalls/sys_enter_openat ^arg=$foo' # BAD_ATTACH_ARG
-check_error 'e:foo/bar syscalls/sys_enter_openat if ^' # NO_EP_FILTER
+if grep -q '<attached-group>\.<attached-event>.*\[if <filter>\]' README; then
+ check_error 'e:foo/bar syscalls/sys_enter_openat if ^' # NO_EP_FILTER
+fi
exit 0
diff --git a/tools/testing/selftests/ftrace/test.d/filter/event-filter-function.tc b/tools/testing/selftests/ftrace/test.d/filter/event-filter-function.tc
new file mode 100644
index 000000000000..e2ff3bf4df80
--- /dev/null
+++ b/tools/testing/selftests/ftrace/test.d/filter/event-filter-function.tc
@@ -0,0 +1,58 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# description: event filter function - test event filtering on functions
+# requires: set_event events/kmem/kmem_cache_free/filter
+# flags: instance
+
+fail() { #msg
+ echo $1
+ exit_fail
+}
+
+echo "Test event filter function name"
+echo 0 > tracing_on
+echo 0 > events/enable
+echo > trace
+echo 'call_site.function == exit_mmap' > events/kmem/kmem_cache_free/filter
+echo 1 > events/kmem/kmem_cache_free/enable
+echo 1 > tracing_on
+ls > /dev/null
+echo 0 > events/kmem/kmem_cache_free/enable
+
+hitcnt=`grep kmem_cache_free trace| grep exit_mmap | wc -l`
+misscnt=`grep kmem_cache_free trace| grep -v exit_mmap | wc -l`
+
+if [ $hitcnt -eq 0 ]; then
+ exit_fail
+fi
+
+if [ $misscnt -gt 0 ]; then
+ exit_fail
+fi
+
+address=`grep ' exit_mmap$' /proc/kallsyms | cut -d' ' -f1`
+
+echo "Test event filter function address"
+echo 0 > tracing_on
+echo 0 > events/enable
+echo > trace
+echo "call_site.function == 0x$address" > events/kmem/kmem_cache_free/filter
+echo 1 > events/kmem/kmem_cache_free/enable
+echo 1 > tracing_on
+sleep 1
+echo 0 > events/kmem/kmem_cache_free/enable
+
+hitcnt=`grep kmem_cache_free trace| grep exit_mmap | wc -l`
+misscnt=`grep kmem_cache_free trace| grep -v exit_mmap | wc -l`
+
+if [ $hitcnt -eq 0 ]; then
+ exit_fail
+fi
+
+if [ $misscnt -gt 0 ]; then
+ exit_fail
+fi
+
+reset_events_filter
+
+exit 0
diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc
index 3eea2abf68f9..2ad7d4b501cc 100644
--- a/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc
+++ b/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc
@@ -42,7 +42,7 @@ test_event_enabled() {
while [ $check_times -ne 0 ]; do
e=`cat $EVENT_ENABLE`
- if [ "$e" == $val ]; then
+ if [ "$e" = $val ]; then
return 0
fi
sleep $SLEEP_TIME
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_char.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_char.tc
new file mode 100644
index 000000000000..285b4770efad
--- /dev/null
+++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_char.tc
@@ -0,0 +1,47 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# description: Kprobe event char type argument
+# requires: kprobe_events
+
+case `uname -m` in
+x86_64)
+ ARG1=%di
+;;
+i[3456]86)
+ ARG1=%ax
+;;
+aarch64)
+ ARG1=%x0
+;;
+arm*)
+ ARG1=%r0
+;;
+ppc64*)
+ ARG1=%r3
+;;
+ppc*)
+ ARG1=%r3
+;;
+s390*)
+ ARG1=%r2
+;;
+mips*)
+ ARG1=%r4
+;;
+*)
+ echo "Please implement other architecture here"
+ exit_untested
+esac
+
+: "Test get argument (1)"
+echo "p:testprobe tracefs_create_dir arg1=+0(${ARG1}):char" > kprobe_events
+echo 1 > events/kprobes/testprobe/enable
+echo "p:test $FUNCTION_FORK" >> kprobe_events
+grep -qe "testprobe.* arg1='t'" trace
+
+echo 0 > events/kprobes/testprobe/enable
+: "Test get argument (2)"
+echo "p:testprobe tracefs_create_dir arg1=+0(${ARG1}):char arg2=+0(${ARG1}):char[4]" > kprobe_events
+echo 1 > events/kprobes/testprobe/enable
+echo "p:test $FUNCTION_FORK" >> kprobe_events
+grep -qe "testprobe.* arg1='t' arg2={'t','e','s','t'}" trace
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc
index 459741565222..a4f8e7c53c1f 100644
--- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc
+++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc
@@ -28,6 +28,9 @@ s390*)
mips*)
ARG1=%r4
;;
+loongarch*)
+ ARG1=%r4
+;;
*)
echo "Please implement other architecture here"
exit_untested
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc
index d4662c8cf407..1df61e13a812 100644
--- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc
+++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc
@@ -40,6 +40,10 @@ mips*)
GOODREG=%r4
BADREG=%r12
;;
+loongarch*)
+ GOODREG=%r4
+ BADREG=%r12
+;;
*)
echo "Please implement other architecture here"
exit_untested
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/probepoint.tc b/tools/testing/selftests/ftrace/test.d/kprobe/probepoint.tc
index 624269c8d534..68425987a5dd 100644
--- a/tools/testing/selftests/ftrace/test.d/kprobe/probepoint.tc
+++ b/tools/testing/selftests/ftrace/test.d/kprobe/probepoint.tc
@@ -21,7 +21,7 @@ set_offs() { # prev target next
# We have to decode symbol addresses to get correct offsets.
# If the offset is not an instruction boundary, it cause -EILSEQ.
-set_offs `grep -A1 -B1 ${TARGET_FUNC} /proc/kallsyms | cut -f 1 -d " " | xargs`
+set_offs `grep -v __pfx_ /proc/kallsyms | grep -A1 -B1 ${TARGET_FUNC} | cut -f 1 -d " " | xargs`
UINT_TEST=no
# printf "%x" -1 returns (unsigned long)-1.
diff --git a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic-event-stack.tc b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic-event-stack.tc
new file mode 100644
index 000000000000..755dbe94ccf4
--- /dev/null
+++ b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic-event-stack.tc
@@ -0,0 +1,24 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# description: event trigger - test inter-event histogram trigger trace action with dynamic string param
+# requires: set_event synthetic_events events/sched/sched_process_exec/hist "long[]' >> synthetic_events":README
+
+fail() { #msg
+ echo $1
+ exit_fail
+}
+
+echo "Test create synthetic event with stack"
+
+
+echo 's:wake_lat pid_t pid; u64 delta; unsigned long[] stack;' > dynamic_events
+echo 'hist:keys=next_pid:ts=common_timestamp.usecs,st=stacktrace if prev_state == 1||prev_state == 2' >> events/sched/sched_switch/trigger
+echo 'hist:keys=prev_pid:delta=common_timestamp.usecs-$ts,s=$st:onmax($delta).trace(wake_lat,prev_pid,$delta,$s)' >> events/sched/sched_switch/trigger
+echo 1 > events/synthetic/wake_lat/enable
+sleep 1
+
+if ! grep -q "=>.*sched" trace; then
+ fail "Failed to create synthetic event with stack"
+fi
+
+exit 0
diff --git a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic-event-syntax.tc b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic-event-syntax.tc
index 2968cdc7df30..366f1f3ad906 100644
--- a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic-event-syntax.tc
+++ b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic-event-syntax.tc
@@ -70,6 +70,12 @@ grep "myevent[[:space:]]unsigned long var" synthetic_events
echo "myevent char var[10]" > synthetic_events
grep "myevent[[:space:]]char\[10\] var" synthetic_events
+if grep -q 'long\[\]' README; then
+ # test stacktrace type
+ echo "myevent unsigned long[] var" > synthetic_events
+ grep "myevent[[:space:]]unsigned long\[\] var" synthetic_events
+fi
+
do_reset
exit 0
diff --git a/tools/testing/selftests/futex/functional/Makefile b/tools/testing/selftests/futex/functional/Makefile
index 5a0e0df8de9b..a392d0917b4e 100644
--- a/tools/testing/selftests/futex/functional/Makefile
+++ b/tools/testing/selftests/futex/functional/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-INCLUDES := -I../include -I../../ -I../../../../../usr/include/
+INCLUDES := -I../include -I../../ $(KHDR_INCLUDES)
CFLAGS := $(CFLAGS) -g -O2 -Wall -D_GNU_SOURCE -pthread $(INCLUDES) $(KHDR_INCLUDES)
LDLIBS := -lpthread -lrt
diff --git a/tools/testing/selftests/gpio/Makefile b/tools/testing/selftests/gpio/Makefile
index 616ed4019655..e0884390447d 100644
--- a/tools/testing/selftests/gpio/Makefile
+++ b/tools/testing/selftests/gpio/Makefile
@@ -3,6 +3,6 @@
TEST_PROGS := gpio-mockup.sh gpio-sim.sh
TEST_FILES := gpio-mockup-sysfs.sh
TEST_GEN_PROGS_EXTENDED := gpio-mockup-cdev gpio-chip-info gpio-line-name
-CFLAGS += -O2 -g -Wall -I../../../../usr/include/ $(KHDR_INCLUDES)
+CFLAGS += -O2 -g -Wall $(KHDR_INCLUDES)
include ../lib.mk
diff --git a/tools/testing/selftests/iommu/Makefile b/tools/testing/selftests/iommu/Makefile
index 7cb74d26f141..32c5fdfd0eef 100644
--- a/tools/testing/selftests/iommu/Makefile
+++ b/tools/testing/selftests/iommu/Makefile
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
CFLAGS += -Wall -O2 -Wno-unused-function
-CFLAGS += -I../../../../include/uapi/
-CFLAGS += -I../../../../include/
+CFLAGS += $(KHDR_INCLUDES)
CFLAGS += -D_GNU_SOURCE
diff --git a/tools/testing/selftests/iommu/iommufd.c b/tools/testing/selftests/iommu/iommufd.c
index 8aa8a346cf22..fa08209268c4 100644
--- a/tools/testing/selftests/iommu/iommufd.c
+++ b/tools/testing/selftests/iommu/iommufd.c
@@ -1259,7 +1259,7 @@ TEST_F(iommufd_mock_domain, user_copy)
test_cmd_destroy_access_pages(
access_cmd.id, access_cmd.access_pages.out_access_pages_id);
- test_cmd_destroy_access(access_cmd.id) test_ioctl_destroy(ioas_id);
+ test_cmd_destroy_access(access_cmd.id);
test_ioctl_destroy(ioas_id);
}
diff --git a/tools/testing/selftests/ipc/Makefile b/tools/testing/selftests/ipc/Makefile
index 1c4448a843a4..50e9c299fc4a 100644
--- a/tools/testing/selftests/ipc/Makefile
+++ b/tools/testing/selftests/ipc/Makefile
@@ -10,7 +10,7 @@ ifeq ($(ARCH),x86_64)
CFLAGS := -DCONFIG_X86_64 -D__x86_64__
endif
-CFLAGS += -I../../../../usr/include/
+CFLAGS += $(KHDR_INCLUDES)
TEST_GEN_PROGS := msgque
diff --git a/tools/testing/selftests/kcmp/Makefile b/tools/testing/selftests/kcmp/Makefile
index b4d39f6b5124..59a1e5379018 100644
--- a/tools/testing/selftests/kcmp/Makefile
+++ b/tools/testing/selftests/kcmp/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-CFLAGS += -I../../../../usr/include/
+CFLAGS += $(KHDR_INCLUDES)
TEST_GEN_PROGS := kcmp_test
diff --git a/tools/testing/selftests/kselftest_deps.sh b/tools/testing/selftests/kselftest_deps.sh
index 7424a1f5babc..4bc14d9e8ff1 100755
--- a/tools/testing/selftests/kselftest_deps.sh
+++ b/tools/testing/selftests/kselftest_deps.sh
@@ -12,9 +12,9 @@ usage()
echo -e "Usage: $0 -[p] <compiler> [test_name]\n"
echo -e "\tkselftest_deps.sh [-p] gcc"
-echo -e "\tkselftest_deps.sh [-p] gcc vm"
+echo -e "\tkselftest_deps.sh [-p] gcc mm"
echo -e "\tkselftest_deps.sh [-p] aarch64-linux-gnu-gcc"
-echo -e "\tkselftest_deps.sh [-p] aarch64-linux-gnu-gcc vm\n"
+echo -e "\tkselftest_deps.sh [-p] aarch64-linux-gnu-gcc mm\n"
echo "- Should be run in selftests directory in the kernel repo."
echo "- Checks if Kselftests can be built/cross-built on a system."
echo "- Parses all test/sub-test Makefile to find library dependencies."
@@ -120,7 +120,7 @@ l1_tests=$(grep -r --include=Makefile "^LDLIBS" | \
# Level 2
# Some tests have multiple valid LDLIBS lines for individual sub-tests
# that need dependency checks. Find them and append them to the tests
-# e.g: vm/Makefile:$(OUTPUT)/userfaultfd: LDLIBS += -lpthread
+# e.g: mm/Makefile:$(OUTPUT)/userfaultfd: LDLIBS += -lpthread
# Filter out VAR_LDLIBS to discard the following:
# memfd/Makefile:$(OUTPUT)/fuse_mnt: LDLIBS += $(VAR_LDLIBS)
# Append space at the end of the list to append more tests.
diff --git a/tools/testing/selftests/kselftest_harness.h b/tools/testing/selftests/kselftest_harness.h
index 25f4d54067c0..d8bff2005dfc 100644
--- a/tools/testing/selftests/kselftest_harness.h
+++ b/tools/testing/selftests/kselftest_harness.h
@@ -54,6 +54,7 @@
#define _GNU_SOURCE
#endif
#include <asm/types.h>
+#include <ctype.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
@@ -985,6 +986,127 @@ void __wait_for_test(struct __test_metadata *t)
}
}
+static void test_harness_list_tests(void)
+{
+ struct __fixture_variant_metadata *v;
+ struct __fixture_metadata *f;
+ struct __test_metadata *t;
+
+ for (f = __fixture_list; f; f = f->next) {
+ v = f->variant;
+ t = f->tests;
+
+ if (f == __fixture_list)
+ fprintf(stderr, "%-20s %-25s %s\n",
+ "# FIXTURE", "VARIANT", "TEST");
+ else
+ fprintf(stderr, "--------------------------------------------------------------------------------\n");
+
+ do {
+ fprintf(stderr, "%-20s %-25s %s\n",
+ t == f->tests ? f->name : "",
+ v ? v->name : "",
+ t ? t->name : "");
+
+ v = v ? v->next : NULL;
+ t = t ? t->next : NULL;
+ } while (v || t);
+ }
+}
+
+static int test_harness_argv_check(int argc, char **argv)
+{
+ int opt;
+
+ while ((opt = getopt(argc, argv, "hlF:f:V:v:t:T:r:")) != -1) {
+ switch (opt) {
+ case 'f':
+ case 'F':
+ case 'v':
+ case 'V':
+ case 't':
+ case 'T':
+ case 'r':
+ break;
+ case 'l':
+ test_harness_list_tests();
+ return KSFT_SKIP;
+ case 'h':
+ default:
+ fprintf(stderr,
+ "Usage: %s [-h|-l] [-t|-T|-v|-V|-f|-F|-r name]\n"
+ "\t-h print help\n"
+ "\t-l list all tests\n"
+ "\n"
+ "\t-t name include test\n"
+ "\t-T name exclude test\n"
+ "\t-v name include variant\n"
+ "\t-V name exclude variant\n"
+ "\t-f name include fixture\n"
+ "\t-F name exclude fixture\n"
+ "\t-r name run specified test\n"
+ "\n"
+ "Test filter options can be specified "
+ "multiple times. The filtering stops\n"
+ "at the first match. For example to "
+ "include all tests from variant 'bla'\n"
+ "but not test 'foo' specify '-T foo -v bla'.\n"
+ "", argv[0]);
+ return opt == 'h' ? KSFT_SKIP : KSFT_FAIL;
+ }
+ }
+
+ return KSFT_PASS;
+}
+
+static bool test_enabled(int argc, char **argv,
+ struct __fixture_metadata *f,
+ struct __fixture_variant_metadata *v,
+ struct __test_metadata *t)
+{
+ unsigned int flen = 0, vlen = 0, tlen = 0;
+ bool has_positive = false;
+ int opt;
+
+ optind = 1;
+ while ((opt = getopt(argc, argv, "F:f:V:v:t:T:r:")) != -1) {
+ has_positive |= islower(opt);
+
+ switch (tolower(opt)) {
+ case 't':
+ if (!strcmp(t->name, optarg))
+ return islower(opt);
+ break;
+ case 'f':
+ if (!strcmp(f->name, optarg))
+ return islower(opt);
+ break;
+ case 'v':
+ if (!strcmp(v->name, optarg))
+ return islower(opt);
+ break;
+ case 'r':
+ if (!tlen) {
+ flen = strlen(f->name);
+ vlen = strlen(v->name);
+ tlen = strlen(t->name);
+ }
+ if (strlen(optarg) == flen + 1 + vlen + !!vlen + tlen &&
+ !strncmp(f->name, &optarg[0], flen) &&
+ !strncmp(v->name, &optarg[flen + 1], vlen) &&
+ !strncmp(t->name, &optarg[flen + 1 + vlen + !!vlen], tlen))
+ return true;
+ break;
+ }
+ }
+
+ /*
+ * If there are no positive tests then we assume user just wants
+ * exclusions and everything else is a pass.
+ */
+ return !has_positive;
+}
+
void __run_test(struct __fixture_metadata *f,
struct __fixture_variant_metadata *variant,
struct __test_metadata *t)
@@ -1032,24 +1154,32 @@ void __run_test(struct __fixture_metadata *f,
f->name, variant->name[0] ? "." : "", variant->name, t->name);
}
-static int test_harness_run(int __attribute__((unused)) argc,
- char __attribute__((unused)) **argv)
+static int test_harness_run(int argc, char **argv)
{
struct __fixture_variant_metadata no_variant = { .name = "", };
struct __fixture_variant_metadata *v;
struct __fixture_metadata *f;
struct __test_results *results;
struct __test_metadata *t;
- int ret = 0;
+ int ret;
unsigned int case_count = 0, test_count = 0;
unsigned int count = 0;
unsigned int pass_count = 0;
+ ret = test_harness_argv_check(argc, argv);
+ if (ret != KSFT_PASS)
+ return ret;
+
for (f = __fixture_list; f; f = f->next) {
for (v = f->variant ?: &no_variant; v; v = v->next) {
- case_count++;
+ unsigned int old_tests = test_count;
+
for (t = f->tests; t; t = t->next)
- test_count++;
+ if (test_enabled(argc, argv, f, v, t))
+ test_count++;
+
+ if (old_tests != test_count)
+ case_count++;
}
}
@@ -1063,6 +1193,8 @@ static int test_harness_run(int __attribute__((unused)) argc,
for (f = __fixture_list; f; f = f->next) {
for (v = f->variant ?: &no_variant; v; v = v->next) {
for (t = f->tests; t; t = t->next) {
+ if (!test_enabled(argc, argv, f, v, t))
+ continue;
count++;
t->results = results;
__run_test(f, v, t);
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 1750f91dd936..84a627c43795 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -67,6 +67,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/fix_hypercall_test
TEST_GEN_PROGS_x86_64 += x86_64/hyperv_clock
TEST_GEN_PROGS_x86_64 += x86_64/hyperv_cpuid
TEST_GEN_PROGS_x86_64 += x86_64/hyperv_evmcs
+TEST_GEN_PROGS_x86_64 += x86_64/hyperv_extended_hypercalls
TEST_GEN_PROGS_x86_64 += x86_64/hyperv_features
TEST_GEN_PROGS_x86_64 += x86_64/hyperv_ipi
TEST_GEN_PROGS_x86_64 += x86_64/hyperv_svm_test
@@ -200,6 +201,9 @@ CFLAGS += -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \
-I$(LINUX_TOOL_ARCH_INCLUDE) -I$(LINUX_HDR_PATH) -Iinclude \
-I$(<D) -Iinclude/$(ARCH_DIR) -I ../rseq -I.. $(EXTRA_CFLAGS) \
$(KHDR_INCLUDES)
+ifeq ($(ARCH),s390)
+ CFLAGS += -march=z10
+endif
no-pie-option := $(call try-run, echo 'int main(void) { return 0; }' | \
$(CC) -Werror $(CFLAGS) -no-pie -x c - -o "$$TMP", -no-pie)
diff --git a/tools/testing/selftests/kvm/aarch64/page_fault_test.c b/tools/testing/selftests/kvm/aarch64/page_fault_test.c
index 54680dc5887f..df10f1ffa20d 100644
--- a/tools/testing/selftests/kvm/aarch64/page_fault_test.c
+++ b/tools/testing/selftests/kvm/aarch64/page_fault_test.c
@@ -1112,8 +1112,6 @@ int main(int argc, char *argv[])
enum vm_mem_backing_src_type src_type;
int opt;
- setbuf(stdout, NULL);
-
src_type = DEFAULT_VM_MEM_SRC;
while ((opt = getopt(argc, argv, "hm:s:")) != -1) {
diff --git a/tools/testing/selftests/kvm/aarch64/psci_test.c b/tools/testing/selftests/kvm/aarch64/psci_test.c
index cfa36f387948..9b004905d1d3 100644
--- a/tools/testing/selftests/kvm/aarch64/psci_test.c
+++ b/tools/testing/selftests/kvm/aarch64/psci_test.c
@@ -180,9 +180,7 @@ static void host_test_system_suspend(void)
enter_guest(source);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_SYSTEM_EVENT,
- "Unhandled exit reason: %u (%s)",
- run->exit_reason, exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(source, KVM_EXIT_SYSTEM_EVENT);
TEST_ASSERT(run->system_event.type == KVM_SYSTEM_EVENT_SUSPEND,
"Unhandled system event: %u (expected: %u)",
run->system_event.type, KVM_SYSTEM_EVENT_SUSPEND);
diff --git a/tools/testing/selftests/kvm/config b/tools/testing/selftests/kvm/config
index 63ed533f73d6..d011b38e259e 100644
--- a/tools/testing/selftests/kvm/config
+++ b/tools/testing/selftests/kvm/config
@@ -1,3 +1,4 @@
CONFIG_KVM=y
CONFIG_KVM_INTEL=y
CONFIG_KVM_AMD=y
+CONFIG_USERFAULTFD=y
diff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h
index 80d6416f3012..a6e9f215ce70 100644
--- a/tools/testing/selftests/kvm/include/test_util.h
+++ b/tools/testing/selftests/kvm/include/test_util.h
@@ -63,6 +63,15 @@ void test_assert(bool exp, const char *exp_str,
#a, #b, #a, (unsigned long) __a, #b, (unsigned long) __b); \
} while (0)
+#define TEST_ASSERT_KVM_EXIT_REASON(vcpu, expected) do { \
+ __u32 exit_reason = (vcpu)->run->exit_reason; \
+ \
+ TEST_ASSERT(exit_reason == (expected), \
+ "Wanted KVM exit reason: %u (%s), got: %u (%s)", \
+ (expected), exit_reason_str((expected)), \
+ exit_reason, exit_reason_str(exit_reason)); \
+} while (0)
+
#define TEST_FAIL(fmt, ...) do { \
TEST_ASSERT(false, fmt, ##__VA_ARGS__); \
__builtin_unreachable(); \
diff --git a/tools/testing/selftests/kvm/include/x86_64/hyperv.h b/tools/testing/selftests/kvm/include/x86_64/hyperv.h
index 9218bb5f44bf..fa65b908b13e 100644
--- a/tools/testing/selftests/kvm/include/x86_64/hyperv.h
+++ b/tools/testing/selftests/kvm/include/x86_64/hyperv.h
@@ -85,61 +85,110 @@
#define HV_X64_MSR_SYNDBG_OPTIONS 0x400000FF
/* HYPERV_CPUID_FEATURES.EAX */
-#define HV_MSR_VP_RUNTIME_AVAILABLE BIT(0)
-#define HV_MSR_TIME_REF_COUNT_AVAILABLE BIT(1)
-#define HV_MSR_SYNIC_AVAILABLE BIT(2)
-#define HV_MSR_SYNTIMER_AVAILABLE BIT(3)
-#define HV_MSR_APIC_ACCESS_AVAILABLE BIT(4)
-#define HV_MSR_HYPERCALL_AVAILABLE BIT(5)
-#define HV_MSR_VP_INDEX_AVAILABLE BIT(6)
-#define HV_MSR_RESET_AVAILABLE BIT(7)
-#define HV_MSR_STAT_PAGES_AVAILABLE BIT(8)
-#define HV_MSR_REFERENCE_TSC_AVAILABLE BIT(9)
-#define HV_MSR_GUEST_IDLE_AVAILABLE BIT(10)
-#define HV_ACCESS_FREQUENCY_MSRS BIT(11)
-#define HV_ACCESS_REENLIGHTENMENT BIT(13)
-#define HV_ACCESS_TSC_INVARIANT BIT(15)
+#define HV_MSR_VP_RUNTIME_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EAX, 0)
+#define HV_MSR_TIME_REF_COUNT_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EAX, 1)
+#define HV_MSR_SYNIC_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EAX, 2)
+#define HV_MSR_SYNTIMER_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EAX, 3)
+#define HV_MSR_APIC_ACCESS_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EAX, 4)
+#define HV_MSR_HYPERCALL_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EAX, 5)
+#define HV_MSR_VP_INDEX_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EAX, 6)
+#define HV_MSR_RESET_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EAX, 7)
+#define HV_MSR_STAT_PAGES_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EAX, 8)
+#define HV_MSR_REFERENCE_TSC_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EAX, 9)
+#define HV_MSR_GUEST_IDLE_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EAX, 10)
+#define HV_ACCESS_FREQUENCY_MSRS \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EAX, 11)
+#define HV_ACCESS_REENLIGHTENMENT \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EAX, 13)
+#define HV_ACCESS_TSC_INVARIANT \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EAX, 15)
/* HYPERV_CPUID_FEATURES.EBX */
-#define HV_CREATE_PARTITIONS BIT(0)
-#define HV_ACCESS_PARTITION_ID BIT(1)
-#define HV_ACCESS_MEMORY_POOL BIT(2)
-#define HV_ADJUST_MESSAGE_BUFFERS BIT(3)
-#define HV_POST_MESSAGES BIT(4)
-#define HV_SIGNAL_EVENTS BIT(5)
-#define HV_CREATE_PORT BIT(6)
-#define HV_CONNECT_PORT BIT(7)
-#define HV_ACCESS_STATS BIT(8)
-#define HV_DEBUGGING BIT(11)
-#define HV_CPU_MANAGEMENT BIT(12)
-#define HV_ISOLATION BIT(22)
+#define HV_CREATE_PARTITIONS \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EBX, 0)
+#define HV_ACCESS_PARTITION_ID \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EBX, 1)
+#define HV_ACCESS_MEMORY_POOL \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EBX, 2)
+#define HV_ADJUST_MESSAGE_BUFFERS \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EBX, 3)
+#define HV_POST_MESSAGES \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EBX, 4)
+#define HV_SIGNAL_EVENTS \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EBX, 5)
+#define HV_CREATE_PORT \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EBX, 6)
+#define HV_CONNECT_PORT \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EBX, 7)
+#define HV_ACCESS_STATS \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EBX, 8)
+#define HV_DEBUGGING \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EBX, 11)
+#define HV_CPU_MANAGEMENT \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EBX, 12)
+#define HV_ENABLE_EXTENDED_HYPERCALLS \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EBX, 20)
+#define HV_ISOLATION \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EBX, 22)
/* HYPERV_CPUID_FEATURES.EDX */
-#define HV_X64_MWAIT_AVAILABLE BIT(0)
-#define HV_X64_GUEST_DEBUGGING_AVAILABLE BIT(1)
-#define HV_X64_PERF_MONITOR_AVAILABLE BIT(2)
-#define HV_X64_CPU_DYNAMIC_PARTITIONING_AVAILABLE BIT(3)
-#define HV_X64_HYPERCALL_XMM_INPUT_AVAILABLE BIT(4)
-#define HV_X64_GUEST_IDLE_STATE_AVAILABLE BIT(5)
-#define HV_FEATURE_FREQUENCY_MSRS_AVAILABLE BIT(8)
-#define HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE BIT(10)
-#define HV_FEATURE_DEBUG_MSRS_AVAILABLE BIT(11)
-#define HV_STIMER_DIRECT_MODE_AVAILABLE BIT(19)
+#define HV_X64_MWAIT_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EDX, 0)
+#define HV_X64_GUEST_DEBUGGING_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EDX, 1)
+#define HV_X64_PERF_MONITOR_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EDX, 2)
+#define HV_X64_CPU_DYNAMIC_PARTITIONING_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EDX, 3)
+#define HV_X64_HYPERCALL_XMM_INPUT_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EDX, 4)
+#define HV_X64_GUEST_IDLE_STATE_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EDX, 5)
+#define HV_FEATURE_FREQUENCY_MSRS_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EDX, 8)
+#define HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EDX, 10)
+#define HV_FEATURE_DEBUG_MSRS_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EDX, 11)
+#define HV_STIMER_DIRECT_MODE_AVAILABLE \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_FEATURES, 0, EDX, 19)
/* HYPERV_CPUID_ENLIGHTMENT_INFO.EAX */
-#define HV_X64_AS_SWITCH_RECOMMENDED BIT(0)
-#define HV_X64_LOCAL_TLB_FLUSH_RECOMMENDED BIT(1)
-#define HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED BIT(2)
-#define HV_X64_APIC_ACCESS_RECOMMENDED BIT(3)
-#define HV_X64_SYSTEM_RESET_RECOMMENDED BIT(4)
-#define HV_X64_RELAXED_TIMING_RECOMMENDED BIT(5)
-#define HV_DEPRECATING_AEOI_RECOMMENDED BIT(9)
-#define HV_X64_CLUSTER_IPI_RECOMMENDED BIT(10)
-#define HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED BIT(11)
-#define HV_X64_ENLIGHTENED_VMCS_RECOMMENDED BIT(14)
+#define HV_X64_AS_SWITCH_RECOMMENDED \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_ENLIGHTMENT_INFO, 0, EAX, 0)
+#define HV_X64_LOCAL_TLB_FLUSH_RECOMMENDED \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_ENLIGHTMENT_INFO, 0, EAX, 1)
+#define HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_ENLIGHTMENT_INFO, 0, EAX, 2)
+#define HV_X64_APIC_ACCESS_RECOMMENDED \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_ENLIGHTMENT_INFO, 0, EAX, 3)
+#define HV_X64_SYSTEM_RESET_RECOMMENDED \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_ENLIGHTMENT_INFO, 0, EAX, 4)
+#define HV_X64_RELAXED_TIMING_RECOMMENDED \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_ENLIGHTMENT_INFO, 0, EAX, 5)
+#define HV_DEPRECATING_AEOI_RECOMMENDED \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_ENLIGHTMENT_INFO, 0, EAX, 9)
+#define HV_X64_CLUSTER_IPI_RECOMMENDED \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_ENLIGHTMENT_INFO, 0, EAX, 10)
+#define HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_ENLIGHTMENT_INFO, 0, EAX, 11)
+#define HV_X64_ENLIGHTENED_VMCS_RECOMMENDED \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_ENLIGHTMENT_INFO, 0, EAX, 14)
/* HYPERV_CPUID_SYNDBG_PLATFORM_CAPABILITIES.EAX */
-#define HV_X64_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING BIT(1)
+#define HV_X64_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_SYNDBG_PLATFORM_CAPABILITIES, 0, EAX, 1)
/* Hypercalls */
#define HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE 0x0002
@@ -166,6 +215,9 @@
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE 0x00af
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0
+/* Extended hypercalls */
+#define HV_EXT_CALL_QUERY_CAPABILITIES 0x8001
+
#define HV_FLUSH_ALL_PROCESSORS BIT(0)
#define HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES BIT(1)
#define HV_FLUSH_NON_GLOBAL_MAPPINGS_ONLY BIT(2)
@@ -288,4 +340,7 @@ struct hyperv_test_pages {
struct hyperv_test_pages *vcpu_alloc_hyperv_test_pages(struct kvm_vm *vm,
vm_vaddr_t *p_hv_pages_gva);
+/* HV_X64_MSR_TSC_INVARIANT_CONTROL bits */
+#define HV_INVARIANT_TSC_EXPOSED BIT_ULL(0)
+
#endif /* !SELFTEST_KVM_HYPERV_H */
diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h
index b1a31de7108a..90387ddcb2a9 100644
--- a/tools/testing/selftests/kvm/include/x86_64/processor.h
+++ b/tools/testing/selftests/kvm/include/x86_64/processor.h
@@ -19,6 +19,9 @@
#include "../kvm_util.h"
+extern bool host_cpu_is_intel;
+extern bool host_cpu_is_amd;
+
#define NMI_VECTOR 0x02
#define X86_EFLAGS_FIXED (1u << 1)
@@ -137,6 +140,7 @@ struct kvm_x86_cpu_feature {
#define X86_FEATURE_GBPAGES KVM_X86_CPU_FEATURE(0x80000001, 0, EDX, 26)
#define X86_FEATURE_RDTSCP KVM_X86_CPU_FEATURE(0x80000001, 0, EDX, 27)
#define X86_FEATURE_LM KVM_X86_CPU_FEATURE(0x80000001, 0, EDX, 29)
+#define X86_FEATURE_INVTSC KVM_X86_CPU_FEATURE(0x80000007, 0, EDX, 8)
#define X86_FEATURE_RDPRU KVM_X86_CPU_FEATURE(0x80000008, 0, EBX, 4)
#define X86_FEATURE_AMD_IBPB KVM_X86_CPU_FEATURE(0x80000008, 0, EBX, 12)
#define X86_FEATURE_NPT KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 0)
@@ -554,6 +558,28 @@ static inline uint32_t this_cpu_model(void)
return x86_model(this_cpu_fms());
}
+static inline bool this_cpu_vendor_string_is(const char *vendor)
+{
+ const uint32_t *chunk = (const uint32_t *)vendor;
+ uint32_t eax, ebx, ecx, edx;
+
+ cpuid(0, &eax, &ebx, &ecx, &edx);
+ return (ebx == chunk[0] && edx == chunk[1] && ecx == chunk[2]);
+}
+
+static inline bool this_cpu_is_intel(void)
+{
+ return this_cpu_vendor_string_is("GenuineIntel");
+}
+
+/*
+ * Exclude early K5 samples with a vendor string of "AMDisbetter!"
+ */
+static inline bool this_cpu_is_amd(void)
+{
+ return this_cpu_vendor_string_is("AuthenticAMD");
+}
+
static inline uint32_t __this_cpu_has(uint32_t function, uint32_t index,
uint8_t reg, uint8_t lo, uint8_t hi)
{
@@ -690,9 +716,6 @@ static inline void cpu_relax(void)
"hlt\n" \
)
-bool is_intel_cpu(void);
-bool is_amd_cpu(void);
-
struct kvm_x86_state *vcpu_save_state(struct kvm_vcpu *vcpu);
void vcpu_load_state(struct kvm_vcpu *vcpu, struct kvm_x86_state *state);
void kvm_x86_state_cleanup(struct kvm_x86_state *state);
@@ -716,7 +739,7 @@ static inline void vcpu_msrs_set(struct kvm_vcpu *vcpu, struct kvm_msrs *msrs)
int r = __vcpu_ioctl(vcpu, KVM_SET_MSRS, msrs);
TEST_ASSERT(r == msrs->nmsrs,
- "KVM_GET_MSRS failed, r: %i (failed on MSR %x)",
+ "KVM_SET_MSRS failed, r: %i (failed on MSR %x)",
r, r < 0 || r >= msrs->nmsrs ? -1 : msrs->entries[r].index);
}
static inline void vcpu_debugregs_get(struct kvm_vcpu *vcpu,
@@ -1040,6 +1063,8 @@ uint64_t *vm_get_page_table_entry(struct kvm_vm *vm, uint64_t vaddr);
uint64_t kvm_hypercall(uint64_t nr, uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3);
+uint64_t __xen_hypercall(uint64_t nr, uint64_t a0, void *a1);
+void xen_hypercall(uint64_t nr, uint64_t a0, void *a1);
void __vm_xsave_require_permission(int bit, const char *name);
diff --git a/tools/testing/selftests/kvm/kvm_binary_stats_test.c b/tools/testing/selftests/kvm/kvm_binary_stats_test.c
index 0b45ac593387..a7001e29dc06 100644
--- a/tools/testing/selftests/kvm/kvm_binary_stats_test.c
+++ b/tools/testing/selftests/kvm/kvm_binary_stats_test.c
@@ -19,6 +19,7 @@
#include "kvm_util.h"
#include "asm/kvm.h"
#include "linux/kvm.h"
+#include "kselftest.h"
static void stats_test(int stats_fd)
{
@@ -51,7 +52,7 @@ static void stats_test(int stats_fd)
/* Sanity check for other fields in header */
if (header.num_desc == 0) {
- printf("No KVM stats defined!");
+ ksft_print_msg("No KVM stats defined!\n");
return;
}
/*
@@ -133,7 +134,7 @@ static void stats_test(int stats_fd)
"Bucket size of stats (%s) is not zero",
pdesc->name);
}
- size_data += pdesc->size * sizeof(*stats_data);
+ size_data = max(size_data, pdesc->offset + pdesc->size * sizeof(*stats_data));
}
/*
@@ -148,14 +149,6 @@ static void stats_test(int stats_fd)
TEST_ASSERT(size_data >= header.num_desc * sizeof(*stats_data),
"Data size is not correct");
- /* Check stats offset */
- for (i = 0; i < header.num_desc; ++i) {
- pdesc = get_stats_descriptor(stats_desc, i, &header);
- TEST_ASSERT(pdesc->offset < size_data,
- "Invalid offset (%u) for stats: %s",
- pdesc->offset, pdesc->name);
- }
-
/* Allocate memory for stats data */
stats_data = malloc(size_data);
TEST_ASSERT(stats_data, "Allocate memory for stats data");
@@ -224,9 +217,13 @@ int main(int argc, char *argv[])
max_vcpu = DEFAULT_NUM_VCPU;
}
+ ksft_print_header();
+
/* Check the extension for binary stats */
TEST_REQUIRE(kvm_has_cap(KVM_CAP_BINARY_STATS_FD));
+ ksft_set_plan(max_vm);
+
/* Create VMs and VCPUs */
vms = malloc(sizeof(vms[0]) * max_vm);
TEST_ASSERT(vms, "Allocate memory for storing VM pointers");
@@ -245,10 +242,12 @@ int main(int argc, char *argv[])
vm_stats_test(vms[i]);
for (j = 0; j < max_vcpu; ++j)
vcpu_stats_test(vcpus[i * max_vcpu + j]);
+ ksft_test_result_pass("vm%i\n", i);
}
for (i = 0; i < max_vm; ++i)
kvm_vm_free(vms[i]);
free(vms);
- return 0;
+
+ ksft_finished(); /* Print results and exit() accordingly */
}
diff --git a/tools/testing/selftests/kvm/lib/elf.c b/tools/testing/selftests/kvm/lib/elf.c
index 820ac2d08c98..266f3876e10a 100644
--- a/tools/testing/selftests/kvm/lib/elf.c
+++ b/tools/testing/selftests/kvm/lib/elf.c
@@ -90,6 +90,7 @@ static void elfhdr_get(const char *filename, Elf64_Ehdr *hdrp)
" hdrp->e_shentsize: %x\n"
" expected: %zx",
hdrp->e_shentsize, sizeof(Elf64_Shdr));
+ close(fd);
}
/* VM ELF Load
@@ -190,4 +191,5 @@ void kvm_vm_elf_load(struct kvm_vm *vm, const char *filename)
phdr.p_filesz);
}
}
+ close(fd);
}
diff --git a/tools/testing/selftests/kvm/lib/guest_modes.c b/tools/testing/selftests/kvm/lib/guest_modes.c
index 99a575bbbc52..1df3ce4b16fd 100644
--- a/tools/testing/selftests/kvm/lib/guest_modes.c
+++ b/tools/testing/selftests/kvm/lib/guest_modes.c
@@ -127,7 +127,7 @@ void guest_modes_cmdline(const char *arg)
mode_selected = true;
}
- mode = strtoul(optarg, NULL, 10);
+ mode = atoi_non_negative("Guest mode ID", arg);
TEST_ASSERT(mode < NUM_VM_MODES, "Guest mode ID %d too big", mode);
guest_modes[mode].enabled = true;
}
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index 56d5ea949cbb..8ec20ac33de0 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -1815,37 +1815,53 @@ void vm_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent)
vcpu_dump(stream, vcpu, indent + 2);
}
+#define KVM_EXIT_STRING(x) {KVM_EXIT_##x, #x}
+
/* Known KVM exit reasons */
static struct exit_reason {
unsigned int reason;
const char *name;
} exit_reasons_known[] = {
- {KVM_EXIT_UNKNOWN, "UNKNOWN"},
- {KVM_EXIT_EXCEPTION, "EXCEPTION"},
- {KVM_EXIT_IO, "IO"},
- {KVM_EXIT_HYPERCALL, "HYPERCALL"},
- {KVM_EXIT_DEBUG, "DEBUG"},
- {KVM_EXIT_HLT, "HLT"},
- {KVM_EXIT_MMIO, "MMIO"},
- {KVM_EXIT_IRQ_WINDOW_OPEN, "IRQ_WINDOW_OPEN"},
- {KVM_EXIT_SHUTDOWN, "SHUTDOWN"},
- {KVM_EXIT_FAIL_ENTRY, "FAIL_ENTRY"},
- {KVM_EXIT_INTR, "INTR"},
- {KVM_EXIT_SET_TPR, "SET_TPR"},
- {KVM_EXIT_TPR_ACCESS, "TPR_ACCESS"},
- {KVM_EXIT_S390_SIEIC, "S390_SIEIC"},
- {KVM_EXIT_S390_RESET, "S390_RESET"},
- {KVM_EXIT_DCR, "DCR"},
- {KVM_EXIT_NMI, "NMI"},
- {KVM_EXIT_INTERNAL_ERROR, "INTERNAL_ERROR"},
- {KVM_EXIT_OSI, "OSI"},
- {KVM_EXIT_PAPR_HCALL, "PAPR_HCALL"},
- {KVM_EXIT_DIRTY_RING_FULL, "DIRTY_RING_FULL"},
- {KVM_EXIT_X86_RDMSR, "RDMSR"},
- {KVM_EXIT_X86_WRMSR, "WRMSR"},
- {KVM_EXIT_XEN, "XEN"},
+ KVM_EXIT_STRING(UNKNOWN),
+ KVM_EXIT_STRING(EXCEPTION),
+ KVM_EXIT_STRING(IO),
+ KVM_EXIT_STRING(HYPERCALL),
+ KVM_EXIT_STRING(DEBUG),
+ KVM_EXIT_STRING(HLT),
+ KVM_EXIT_STRING(MMIO),
+ KVM_EXIT_STRING(IRQ_WINDOW_OPEN),
+ KVM_EXIT_STRING(SHUTDOWN),
+ KVM_EXIT_STRING(FAIL_ENTRY),
+ KVM_EXIT_STRING(INTR),
+ KVM_EXIT_STRING(SET_TPR),
+ KVM_EXIT_STRING(TPR_ACCESS),
+ KVM_EXIT_STRING(S390_SIEIC),
+ KVM_EXIT_STRING(S390_RESET),
+ KVM_EXIT_STRING(DCR),
+ KVM_EXIT_STRING(NMI),
+ KVM_EXIT_STRING(INTERNAL_ERROR),
+ KVM_EXIT_STRING(OSI),
+ KVM_EXIT_STRING(PAPR_HCALL),
+ KVM_EXIT_STRING(S390_UCONTROL),
+ KVM_EXIT_STRING(WATCHDOG),
+ KVM_EXIT_STRING(S390_TSCH),
+ KVM_EXIT_STRING(EPR),
+ KVM_EXIT_STRING(SYSTEM_EVENT),
+ KVM_EXIT_STRING(S390_STSI),
+ KVM_EXIT_STRING(IOAPIC_EOI),
+ KVM_EXIT_STRING(HYPERV),
+ KVM_EXIT_STRING(ARM_NISV),
+ KVM_EXIT_STRING(X86_RDMSR),
+ KVM_EXIT_STRING(X86_WRMSR),
+ KVM_EXIT_STRING(DIRTY_RING_FULL),
+ KVM_EXIT_STRING(AP_RESET_HOLD),
+ KVM_EXIT_STRING(X86_BUS_LOCK),
+ KVM_EXIT_STRING(XEN),
+ KVM_EXIT_STRING(RISCV_SBI),
+ KVM_EXIT_STRING(RISCV_CSR),
+ KVM_EXIT_STRING(NOTIFY),
#ifdef KVM_EXIT_MEMORY_NOT_PRESENT
- {KVM_EXIT_MEMORY_NOT_PRESENT, "MEMORY_NOT_PRESENT"},
+ KVM_EXIT_STRING(MEMORY_NOT_PRESENT),
#endif
};
@@ -1941,9 +1957,6 @@ vm_paddr_t vm_phy_page_alloc(struct kvm_vm *vm, vm_paddr_t paddr_min,
return vm_phy_pages_alloc(vm, 1, paddr_min, memslot);
}
-/* Arbitrary minimum physical address used for virtual translation tables. */
-#define KVM_GUEST_PAGE_TABLE_MIN_PADDR 0x180000
-
vm_paddr_t vm_alloc_page_table(struct kvm_vm *vm)
{
return vm_phy_page_alloc(vm, KVM_GUEST_PAGE_TABLE_MIN_PADDR,
diff --git a/tools/testing/selftests/kvm/lib/s390x/diag318_test_handler.c b/tools/testing/selftests/kvm/lib/s390x/diag318_test_handler.c
index cdb7daeed5fd..2c432fa164f1 100644
--- a/tools/testing/selftests/kvm/lib/s390x/diag318_test_handler.c
+++ b/tools/testing/selftests/kvm/lib/s390x/diag318_test_handler.c
@@ -35,8 +35,7 @@ static uint64_t diag318_handler(void)
vcpu_run(vcpu);
run = vcpu->run;
- TEST_ASSERT(run->exit_reason == KVM_EXIT_S390_SIEIC,
- "DIAGNOSE 0x0318 instruction was not intercepted");
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
TEST_ASSERT(run->s390_sieic.icptcode == ICPT_INSTRUCTION,
"Unexpected intercept code: 0x%x", run->s390_sieic.icptcode);
TEST_ASSERT((run->s390_sieic.ipa & 0xff00) == IPA0_DIAG,
diff --git a/tools/testing/selftests/kvm/lib/test_util.c b/tools/testing/selftests/kvm/lib/test_util.c
index 5c22fa4c2825..b772193f6c18 100644
--- a/tools/testing/selftests/kvm/lib/test_util.c
+++ b/tools/testing/selftests/kvm/lib/test_util.c
@@ -165,26 +165,33 @@ size_t get_trans_hugepagesz(void)
size_t get_def_hugetlb_pagesz(void)
{
char buf[64];
- const char *tag = "Hugepagesize:";
+ const char *hugepagesize = "Hugepagesize:";
+ const char *hugepages_total = "HugePages_Total:";
FILE *f;
f = fopen("/proc/meminfo", "r");
TEST_ASSERT(f != NULL, "Error in opening /proc/meminfo");
while (fgets(buf, sizeof(buf), f) != NULL) {
- if (strstr(buf, tag) == buf) {
+ if (strstr(buf, hugepages_total) == buf) {
+ unsigned long long total = strtoull(buf + strlen(hugepages_total), NULL, 10);
+ if (!total) {
+ fprintf(stderr, "HUGETLB is not enabled in /proc/sys/vm/nr_hugepages\n");
+ exit(KSFT_SKIP);
+ }
+ }
+ if (strstr(buf, hugepagesize) == buf) {
fclose(f);
- return strtoull(buf + strlen(tag), NULL, 10) << 10;
+ return strtoull(buf + strlen(hugepagesize), NULL, 10) << 10;
}
}
- if (feof(f))
- TEST_FAIL("HUGETLB is not configured in host kernel");
- else
- TEST_FAIL("Error in reading /proc/meminfo");
+ if (feof(f)) {
+ fprintf(stderr, "HUGETLB is not configured in host kernel");
+ exit(KSFT_SKIP);
+ }
- fclose(f);
- return 0;
+ TEST_FAIL("Error in reading /proc/meminfo");
}
#define ANON_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS)
diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c
index acfa1d01e7df..c39a4353ba19 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/processor.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c
@@ -19,6 +19,8 @@
#define MAX_NR_CPUID_ENTRIES 100
vm_vaddr_t exception_handlers;
+bool host_cpu_is_amd;
+bool host_cpu_is_intel;
static void regs_dump(FILE *stream, struct kvm_regs *regs, uint8_t indent)
{
@@ -113,7 +115,7 @@ static void sregs_dump(FILE *stream, struct kvm_sregs *sregs, uint8_t indent)
bool kvm_is_tdp_enabled(void)
{
- if (is_intel_cpu())
+ if (host_cpu_is_intel)
return get_kvm_intel_param_bool("ept");
else
return get_kvm_amd_param_bool("npt");
@@ -555,6 +557,8 @@ static void vcpu_setup(struct kvm_vm *vm, struct kvm_vcpu *vcpu)
void kvm_arch_vm_post_create(struct kvm_vm *vm)
{
vm_create_irqchip(vm);
+ sync_global_to_guest(vm, host_cpu_is_intel);
+ sync_global_to_guest(vm, host_cpu_is_amd);
}
struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id,
@@ -1006,28 +1010,6 @@ void kvm_x86_state_cleanup(struct kvm_x86_state *state)
free(state);
}
-static bool cpu_vendor_string_is(const char *vendor)
-{
- const uint32_t *chunk = (const uint32_t *)vendor;
- uint32_t eax, ebx, ecx, edx;
-
- cpuid(0, &eax, &ebx, &ecx, &edx);
- return (ebx == chunk[0] && edx == chunk[1] && ecx == chunk[2]);
-}
-
-bool is_intel_cpu(void)
-{
- return cpu_vendor_string_is("GenuineIntel");
-}
-
-/*
- * Exclude early K5 samples with a vendor string of "AMDisbetter!"
- */
-bool is_amd_cpu(void)
-{
- return cpu_vendor_string_is("AuthenticAMD");
-}
-
void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits)
{
if (!kvm_cpu_has_p(X86_PROPERTY_MAX_PHY_ADDR)) {
@@ -1157,15 +1139,36 @@ const struct kvm_cpuid_entry2 *get_cpuid_entry(const struct kvm_cpuid2 *cpuid,
return NULL;
}
+#define X86_HYPERCALL(inputs...) \
+({ \
+ uint64_t r; \
+ \
+ asm volatile("test %[use_vmmcall], %[use_vmmcall]\n\t" \
+ "jnz 1f\n\t" \
+ "vmcall\n\t" \
+ "jmp 2f\n\t" \
+ "1: vmmcall\n\t" \
+ "2:" \
+ : "=a"(r) \
+ : [use_vmmcall] "r" (host_cpu_is_amd), inputs); \
+ \
+ r; \
+})
+
uint64_t kvm_hypercall(uint64_t nr, uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3)
{
- uint64_t r;
+ return X86_HYPERCALL("a"(nr), "b"(a0), "c"(a1), "d"(a2), "S"(a3));
+}
+
+uint64_t __xen_hypercall(uint64_t nr, uint64_t a0, void *a1)
+{
+ return X86_HYPERCALL("a"(nr), "D"(a0), "S"(a1));
+}
- asm volatile("vmcall"
- : "=a"(r)
- : "a"(nr), "b"(a0), "c"(a1), "d"(a2), "S"(a3));
- return r;
+void xen_hypercall(uint64_t nr, uint64_t a0, void *a1)
+{
+ GUEST_ASSERT(!__xen_hypercall(nr, a0, a1));
}
const struct kvm_cpuid2 *kvm_get_supported_hv_cpuid(void)
@@ -1236,7 +1239,7 @@ unsigned long vm_compute_max_gfn(struct kvm_vm *vm)
max_gfn = (1ULL << (vm->pa_bits - vm->page_shift)) - 1;
/* Avoid reserved HyperTransport region on AMD processors. */
- if (!is_amd_cpu())
+ if (!host_cpu_is_amd)
return max_gfn;
/* On parts with <40 physical address bits, the area is fully hidden */
@@ -1276,3 +1279,9 @@ bool vm_is_unrestricted_guest(struct kvm_vm *vm)
return get_kvm_intel_param_bool("unrestricted_guest");
}
+
+void kvm_selftest_arch_init(void)
+{
+ host_cpu_is_intel = this_cpu_is_intel();
+ host_cpu_is_amd = this_cpu_is_amd();
+}
diff --git a/tools/testing/selftests/kvm/memslot_perf_test.c b/tools/testing/selftests/kvm/memslot_perf_test.c
index e6587e193490..4210cd21d159 100644
--- a/tools/testing/selftests/kvm/memslot_perf_test.c
+++ b/tools/testing/selftests/kvm/memslot_perf_test.c
@@ -308,8 +308,6 @@ static bool prepare_vm(struct vm_data *data, int nslots, uint64_t *maxslots,
data->hva_slots = malloc(sizeof(*data->hva_slots) * data->nslots);
TEST_ASSERT(data->hva_slots, "malloc() fail");
- data->vm = __vm_create_with_one_vcpu(&data->vcpu, mempages, guest_code);
-
pr_info_v("Adding slots 1..%i, each slot with %"PRIu64" pages + %"PRIu64" extra pages last\n",
data->nslots, data->pages_per_slot, rempages);
@@ -349,6 +347,7 @@ static bool prepare_vm(struct vm_data *data, int nslots, uint64_t *maxslots,
virt_map(data->vm, MEM_GPA, MEM_GPA, data->npages);
sync = (typeof(sync))vm_gpa2hva(data, MEM_SYNC_GPA, NULL);
+ sync->guest_page_size = data->vm->page_size;
atomic_init(&sync->start_flag, false);
atomic_init(&sync->exit_flag, false);
atomic_init(&sync->sync_flag, false);
@@ -810,8 +809,6 @@ static bool test_execute(int nslots, uint64_t *maxslots,
}
sync = (typeof(sync))vm_gpa2hva(data, MEM_SYNC_GPA, NULL);
-
- sync->guest_page_size = data->vm->page_size;
if (tdata->prepare &&
!tdata->prepare(data, sync, maxslots)) {
ret = false;
diff --git a/tools/testing/selftests/kvm/s390x/memop.c b/tools/testing/selftests/kvm/s390x/memop.c
index 3fd81e58f40c..8e4b94d7b8dd 100644
--- a/tools/testing/selftests/kvm/s390x/memop.c
+++ b/tools/testing/selftests/kvm/s390x/memop.c
@@ -9,6 +9,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
+#include <pthread.h>
#include <linux/bits.h>
@@ -26,6 +27,7 @@ enum mop_target {
enum mop_access_mode {
READ,
WRITE,
+ CMPXCHG,
};
struct mop_desc {
@@ -44,57 +46,67 @@ struct mop_desc {
enum mop_access_mode mode;
void *buf;
uint32_t sida_offset;
+ void *old;
+ uint8_t old_value[16];
+ bool *cmpxchg_success;
uint8_t ar;
uint8_t key;
};
-static struct kvm_s390_mem_op ksmo_from_desc(struct mop_desc desc)
+const uint8_t NO_KEY = 0xff;
+
+static struct kvm_s390_mem_op ksmo_from_desc(struct mop_desc *desc)
{
struct kvm_s390_mem_op ksmo = {
- .gaddr = (uintptr_t)desc.gaddr,
- .size = desc.size,
- .buf = ((uintptr_t)desc.buf),
+ .gaddr = (uintptr_t)desc->gaddr,
+ .size = desc->size,
+ .buf = ((uintptr_t)desc->buf),
.reserved = "ignored_ignored_ignored_ignored"
};
- switch (desc.target) {
+ switch (desc->target) {
case LOGICAL:
- if (desc.mode == READ)
+ if (desc->mode == READ)
ksmo.op = KVM_S390_MEMOP_LOGICAL_READ;
- if (desc.mode == WRITE)
+ if (desc->mode == WRITE)
ksmo.op = KVM_S390_MEMOP_LOGICAL_WRITE;
break;
case SIDA:
- if (desc.mode == READ)
+ if (desc->mode == READ)
ksmo.op = KVM_S390_MEMOP_SIDA_READ;
- if (desc.mode == WRITE)
+ if (desc->mode == WRITE)
ksmo.op = KVM_S390_MEMOP_SIDA_WRITE;
break;
case ABSOLUTE:
- if (desc.mode == READ)
+ if (desc->mode == READ)
ksmo.op = KVM_S390_MEMOP_ABSOLUTE_READ;
- if (desc.mode == WRITE)
+ if (desc->mode == WRITE)
ksmo.op = KVM_S390_MEMOP_ABSOLUTE_WRITE;
+ if (desc->mode == CMPXCHG) {
+ ksmo.op = KVM_S390_MEMOP_ABSOLUTE_CMPXCHG;
+ ksmo.old_addr = (uint64_t)desc->old;
+ memcpy(desc->old_value, desc->old, desc->size);
+ }
break;
case INVALID:
ksmo.op = -1;
}
- if (desc.f_check)
+ if (desc->f_check)
ksmo.flags |= KVM_S390_MEMOP_F_CHECK_ONLY;
- if (desc.f_inject)
+ if (desc->f_inject)
ksmo.flags |= KVM_S390_MEMOP_F_INJECT_EXCEPTION;
- if (desc._set_flags)
- ksmo.flags = desc.set_flags;
- if (desc.f_key) {
+ if (desc->_set_flags)
+ ksmo.flags = desc->set_flags;
+ if (desc->f_key && desc->key != NO_KEY) {
ksmo.flags |= KVM_S390_MEMOP_F_SKEY_PROTECTION;
- ksmo.key = desc.key;
+ ksmo.key = desc->key;
}
- if (desc._ar)
- ksmo.ar = desc.ar;
+ if (desc->_ar)
+ ksmo.ar = desc->ar;
else
ksmo.ar = 0;
- if (desc._sida_offset)
- ksmo.sida_offset = desc.sida_offset;
+ if (desc->_sida_offset)
+ ksmo.sida_offset = desc->sida_offset;
return ksmo;
}
@@ -133,9 +145,13 @@ static void print_memop(struct kvm_vcpu *vcpu, const struct kvm_s390_mem_op *ksm
case KVM_S390_MEMOP_ABSOLUTE_WRITE:
printf("ABSOLUTE, WRITE, ");
break;
+ case KVM_S390_MEMOP_ABSOLUTE_CMPXCHG:
+ printf("ABSOLUTE, CMPXCHG, ");
+ break;
}
- printf("gaddr=%llu, size=%u, buf=%llu, ar=%u, key=%u",
- ksmo->gaddr, ksmo->size, ksmo->buf, ksmo->ar, ksmo->key);
+ printf("gaddr=%llu, size=%u, buf=%llu, ar=%u, key=%u, old_addr=%llx",
+ ksmo->gaddr, ksmo->size, ksmo->buf, ksmo->ar, ksmo->key,
+ ksmo->old_addr);
if (ksmo->flags & KVM_S390_MEMOP_F_CHECK_ONLY)
printf(", CHECK_ONLY");
if (ksmo->flags & KVM_S390_MEMOP_F_INJECT_EXCEPTION)
@@ -145,24 +161,30 @@ static void print_memop(struct kvm_vcpu *vcpu, const struct kvm_s390_mem_op *ksm
puts(")");
}
-static void memop_ioctl(struct test_info info, struct kvm_s390_mem_op *ksmo)
+static int err_memop_ioctl(struct test_info info, struct kvm_s390_mem_op *ksmo,
+ struct mop_desc *desc)
{
struct kvm_vcpu *vcpu = info.vcpu;
if (!vcpu)
- vm_ioctl(info.vm, KVM_S390_MEM_OP, ksmo);
+ return __vm_ioctl(info.vm, KVM_S390_MEM_OP, ksmo);
else
- vcpu_ioctl(vcpu, KVM_S390_MEM_OP, ksmo);
+ return __vcpu_ioctl(vcpu, KVM_S390_MEM_OP, ksmo);
}
-static int err_memop_ioctl(struct test_info info, struct kvm_s390_mem_op *ksmo)
+static void memop_ioctl(struct test_info info, struct kvm_s390_mem_op *ksmo,
+ struct mop_desc *desc)
{
- struct kvm_vcpu *vcpu = info.vcpu;
+ int r;
- if (!vcpu)
- return __vm_ioctl(info.vm, KVM_S390_MEM_OP, ksmo);
- else
- return __vcpu_ioctl(vcpu, KVM_S390_MEM_OP, ksmo);
+ r = err_memop_ioctl(info, ksmo, desc);
+ if (ksmo->op == KVM_S390_MEMOP_ABSOLUTE_CMPXCHG) {
+ if (desc->cmpxchg_success) {
+ int diff = memcmp(desc->old_value, desc->old, desc->size);
+ *desc->cmpxchg_success = !diff;
+ }
+ }
+ TEST_ASSERT(!r, __KVM_IOCTL_ERROR("KVM_S390_MEM_OP", r));
}
#define MEMOP(err, info_p, mop_target_p, access_mode_p, buf_p, size_p, ...) \
@@ -183,9 +205,9 @@ static int err_memop_ioctl(struct test_info info, struct kvm_s390_mem_op *ksmo)
else \
__desc.gaddr = __desc.gaddr_v; \
} \
- __ksmo = ksmo_from_desc(__desc); \
+ __ksmo = ksmo_from_desc(&__desc); \
print_memop(__info.vcpu, &__ksmo); \
- err##memop_ioctl(__info, &__ksmo); \
+ err##memop_ioctl(__info, &__ksmo, &__desc); \
})
#define MOP(...) MEMOP(, __VA_ARGS__)
@@ -199,6 +221,8 @@ static int err_memop_ioctl(struct test_info info, struct kvm_s390_mem_op *ksmo)
#define AR(a) ._ar = 1, .ar = (a)
#define KEY(a) .f_key = 1, .key = (a)
#define INJECT .f_inject = 1
+#define CMPXCHG_OLD(o) .old = (o)
+#define CMPXCHG_SUCCESS(s) .cmpxchg_success = (s)
#define CHECK_N_DO(f, ...) ({ f(__VA_ARGS__, CHECK_ONLY); f(__VA_ARGS__); })
@@ -208,8 +232,8 @@ static int err_memop_ioctl(struct test_info info, struct kvm_s390_mem_op *ksmo)
#define CR0_FETCH_PROTECTION_OVERRIDE (1UL << (63 - 38))
#define CR0_STORAGE_PROTECTION_OVERRIDE (1UL << (63 - 39))
-static uint8_t mem1[65536];
-static uint8_t mem2[65536];
+static uint8_t __aligned(PAGE_SIZE) mem1[65536];
+static uint8_t __aligned(PAGE_SIZE) mem2[65536];
struct test_default {
struct kvm_vm *kvm_vm;
@@ -241,6 +265,8 @@ enum stage {
STAGE_SKEYS_SET,
/* Guest copied memory (locations up to test case) */
STAGE_COPIED,
+ /* End of guest code reached */
+ STAGE_DONE,
};
#define HOST_SYNC(info_p, stage) \
@@ -252,6 +278,9 @@ enum stage {
\
vcpu_run(__vcpu); \
get_ucall(__vcpu, &uc); \
+ if (uc.cmd == UCALL_ABORT) { \
+ REPORT_GUEST_ASSERT_2(uc, "hints: %lu, %lu"); \
+ } \
ASSERT_EQ(uc.cmd, UCALL_SYNC); \
ASSERT_EQ(uc.args[1], __stage); \
}) \
@@ -268,34 +297,66 @@ static void prepare_mem12(void)
#define ASSERT_MEM_EQ(p1, p2, size) \
TEST_ASSERT(!memcmp(p1, p2, size), "Memory contents do not match!")
-#define DEFAULT_WRITE_READ(copy_cpu, mop_cpu, mop_target_p, size, ...) \
-({ \
- struct test_info __copy_cpu = (copy_cpu), __mop_cpu = (mop_cpu); \
- enum mop_target __target = (mop_target_p); \
- uint32_t __size = (size); \
- \
- prepare_mem12(); \
- CHECK_N_DO(MOP, __mop_cpu, __target, WRITE, mem1, __size, \
- GADDR_V(mem1), ##__VA_ARGS__); \
- HOST_SYNC(__copy_cpu, STAGE_COPIED); \
- CHECK_N_DO(MOP, __mop_cpu, __target, READ, mem2, __size, \
- GADDR_V(mem2), ##__VA_ARGS__); \
- ASSERT_MEM_EQ(mem1, mem2, __size); \
-})
+static void default_write_read(struct test_info copy_cpu, struct test_info mop_cpu,
+ enum mop_target mop_target, uint32_t size, uint8_t key)
+{
+ prepare_mem12();
+ CHECK_N_DO(MOP, mop_cpu, mop_target, WRITE, mem1, size,
+ GADDR_V(mem1), KEY(key));
+ HOST_SYNC(copy_cpu, STAGE_COPIED);
+ CHECK_N_DO(MOP, mop_cpu, mop_target, READ, mem2, size,
+ GADDR_V(mem2), KEY(key));
+ ASSERT_MEM_EQ(mem1, mem2, size);
+}
-#define DEFAULT_READ(copy_cpu, mop_cpu, mop_target_p, size, ...) \
-({ \
- struct test_info __copy_cpu = (copy_cpu), __mop_cpu = (mop_cpu); \
- enum mop_target __target = (mop_target_p); \
- uint32_t __size = (size); \
- \
- prepare_mem12(); \
- CHECK_N_DO(MOP, __mop_cpu, __target, WRITE, mem1, __size, \
- GADDR_V(mem1)); \
- HOST_SYNC(__copy_cpu, STAGE_COPIED); \
- CHECK_N_DO(MOP, __mop_cpu, __target, READ, mem2, __size, ##__VA_ARGS__);\
- ASSERT_MEM_EQ(mem1, mem2, __size); \
-})
+static void default_read(struct test_info copy_cpu, struct test_info mop_cpu,
+ enum mop_target mop_target, uint32_t size, uint8_t key)
+{
+ prepare_mem12();
+ CHECK_N_DO(MOP, mop_cpu, mop_target, WRITE, mem1, size, GADDR_V(mem1));
+ HOST_SYNC(copy_cpu, STAGE_COPIED);
+ CHECK_N_DO(MOP, mop_cpu, mop_target, READ, mem2, size,
+ GADDR_V(mem2), KEY(key));
+ ASSERT_MEM_EQ(mem1, mem2, size);
+}
+
+static void default_cmpxchg(struct test_default *test, uint8_t key)
+{
+ for (int size = 1; size <= 16; size *= 2) {
+ for (int offset = 0; offset < 16; offset += size) {
+ uint8_t __aligned(16) new[16] = {};
+ uint8_t __aligned(16) old[16];
+ bool succ;
+
+ prepare_mem12();
+ default_write_read(test->vcpu, test->vcpu, LOGICAL, 16, NO_KEY);
+
+ memcpy(&old, mem1, 16);
+ MOP(test->vm, ABSOLUTE, CMPXCHG, new + offset,
+ size, GADDR_V(mem1 + offset),
+ CMPXCHG_OLD(old + offset),
+ CMPXCHG_SUCCESS(&succ), KEY(key));
+ HOST_SYNC(test->vcpu, STAGE_COPIED);
+ MOP(test->vm, ABSOLUTE, READ, mem2, 16, GADDR_V(mem2));
+ TEST_ASSERT(succ, "exchange of values should succeed");
+ memcpy(mem1 + offset, new + offset, size);
+ ASSERT_MEM_EQ(mem1, mem2, 16);
+
+ memcpy(&old, mem1, 16);
+ new[offset]++;
+ old[offset]++;
+ MOP(test->vm, ABSOLUTE, CMPXCHG, new + offset,
+ size, GADDR_V(mem1 + offset),
+ CMPXCHG_OLD(old + offset),
+ CMPXCHG_SUCCESS(&succ), KEY(key));
+ HOST_SYNC(test->vcpu, STAGE_COPIED);
+ MOP(test->vm, ABSOLUTE, READ, mem2, 16, GADDR_V(mem2));
+ TEST_ASSERT(!succ, "exchange of values should not succeed");
+ ASSERT_MEM_EQ(mem1, mem2, 16);
+ ASSERT_MEM_EQ(&old, mem1, 16);
+ }
+ }
+}
static void guest_copy(void)
{
@@ -310,7 +371,7 @@ static void test_copy(void)
HOST_SYNC(t.vcpu, STAGE_INITED);
- DEFAULT_WRITE_READ(t.vcpu, t.vcpu, LOGICAL, t.size);
+ default_write_read(t.vcpu, t.vcpu, LOGICAL, t.size, NO_KEY);
kvm_vm_free(t.kvm_vm);
}
@@ -357,26 +418,268 @@ static void test_copy_key(void)
HOST_SYNC(t.vcpu, STAGE_SKEYS_SET);
/* vm, no key */
- DEFAULT_WRITE_READ(t.vcpu, t.vm, ABSOLUTE, t.size);
+ default_write_read(t.vcpu, t.vm, ABSOLUTE, t.size, NO_KEY);
/* vm/vcpu, machting key or key 0 */
- DEFAULT_WRITE_READ(t.vcpu, t.vcpu, LOGICAL, t.size, KEY(0));
- DEFAULT_WRITE_READ(t.vcpu, t.vcpu, LOGICAL, t.size, KEY(9));
- DEFAULT_WRITE_READ(t.vcpu, t.vm, ABSOLUTE, t.size, KEY(0));
- DEFAULT_WRITE_READ(t.vcpu, t.vm, ABSOLUTE, t.size, KEY(9));
+ default_write_read(t.vcpu, t.vcpu, LOGICAL, t.size, 0);
+ default_write_read(t.vcpu, t.vcpu, LOGICAL, t.size, 9);
+ default_write_read(t.vcpu, t.vm, ABSOLUTE, t.size, 0);
+ default_write_read(t.vcpu, t.vm, ABSOLUTE, t.size, 9);
/*
* There used to be different code paths for key handling depending on
* if the region crossed a page boundary.
* There currently are not, but the more tests the merrier.
*/
- DEFAULT_WRITE_READ(t.vcpu, t.vcpu, LOGICAL, 1, KEY(0));
- DEFAULT_WRITE_READ(t.vcpu, t.vcpu, LOGICAL, 1, KEY(9));
- DEFAULT_WRITE_READ(t.vcpu, t.vm, ABSOLUTE, 1, KEY(0));
- DEFAULT_WRITE_READ(t.vcpu, t.vm, ABSOLUTE, 1, KEY(9));
+ default_write_read(t.vcpu, t.vcpu, LOGICAL, 1, 0);
+ default_write_read(t.vcpu, t.vcpu, LOGICAL, 1, 9);
+ default_write_read(t.vcpu, t.vm, ABSOLUTE, 1, 0);
+ default_write_read(t.vcpu, t.vm, ABSOLUTE, 1, 9);
/* vm/vcpu, mismatching keys on read, but no fetch protection */
- DEFAULT_READ(t.vcpu, t.vcpu, LOGICAL, t.size, GADDR_V(mem2), KEY(2));
- DEFAULT_READ(t.vcpu, t.vm, ABSOLUTE, t.size, GADDR_V(mem1), KEY(2));
+ default_read(t.vcpu, t.vcpu, LOGICAL, t.size, 2);
+ default_read(t.vcpu, t.vm, ABSOLUTE, t.size, 2);
+
+ kvm_vm_free(t.kvm_vm);
+}
+
+static void test_cmpxchg_key(void)
+{
+ struct test_default t = test_default_init(guest_copy_key);
+
+ HOST_SYNC(t.vcpu, STAGE_SKEYS_SET);
+
+ default_cmpxchg(&t, NO_KEY);
+ default_cmpxchg(&t, 0);
+ default_cmpxchg(&t, 9);
+
+ kvm_vm_free(t.kvm_vm);
+}
+
+static __uint128_t cut_to_size(int size, __uint128_t val)
+{
+ switch (size) {
+ case 1:
+ return (uint8_t)val;
+ case 2:
+ return (uint16_t)val;
+ case 4:
+ return (uint32_t)val;
+ case 8:
+ return (uint64_t)val;
+ case 16:
+ return val;
+ }
+ GUEST_ASSERT_1(false, "Invalid size");
+ return 0;
+}
+
+static bool popcount_eq(__uint128_t a, __uint128_t b)
+{
+ unsigned int count_a, count_b;
+
+ count_a = __builtin_popcountl((uint64_t)(a >> 64)) +
+ __builtin_popcountl((uint64_t)a);
+ count_b = __builtin_popcountl((uint64_t)(b >> 64)) +
+ __builtin_popcountl((uint64_t)b);
+ return count_a == count_b;
+}
+
+static __uint128_t rotate(int size, __uint128_t val, int amount)
+{
+ unsigned int bits = size * 8;
+
+ amount = (amount + bits) % bits;
+ val = cut_to_size(size, val);
+ return (val << (bits - amount)) | (val >> amount);
+}
+
+const unsigned int max_block = 16;
+
+static void choose_block(bool guest, int i, int *size, int *offset)
+{
+ unsigned int rand;
+
+ rand = i;
+ if (guest) {
+ rand = rand * 19 + 11;
+ *size = 1 << ((rand % 3) + 2);
+ rand = rand * 19 + 11;
+ *offset = (rand % max_block) & ~(*size - 1);
+ } else {
+ rand = rand * 17 + 5;
+ *size = 1 << (rand % 5);
+ rand = rand * 17 + 5;
+ *offset = (rand % max_block) & ~(*size - 1);
+ }
+}
+
+static __uint128_t permutate_bits(bool guest, int i, int size, __uint128_t old)
+{
+ unsigned int rand;
+ int amount;
+ bool swap;
+
+ rand = i;
+ rand = rand * 3 + 1;
+ if (guest)
+ rand = rand * 3 + 1;
+ swap = rand % 2 == 0;
+ if (swap) {
+ int i, j;
+ __uint128_t new;
+ uint8_t byte0, byte1;
+
+ rand = rand * 3 + 1;
+ i = rand % size;
+ rand = rand * 3 + 1;
+ j = rand % size;
+ if (i == j)
+ return old;
+ new = rotate(16, old, i * 8);
+ byte0 = new & 0xff;
+ new &= ~0xff;
+ new = rotate(16, new, -i * 8);
+ new = rotate(16, new, j * 8);
+ byte1 = new & 0xff;
+ new = (new & ~0xff) | byte0;
+ new = rotate(16, new, -j * 8);
+ new = rotate(16, new, i * 8);
+ new = new | byte1;
+ new = rotate(16, new, -i * 8);
+ return new;
+ }
+ rand = rand * 3 + 1;
+ amount = rand % (size * 8);
+ return rotate(size, old, amount);
+}
+
+static bool _cmpxchg(int size, void *target, __uint128_t *old_addr, __uint128_t new)
+{
+ bool ret;
+
+ switch (size) {
+ case 4: {
+ uint32_t old = *old_addr;
+
+ asm volatile ("cs %[old],%[new],%[address]"
+ : [old] "+d" (old),
+ [address] "+Q" (*(uint32_t *)(target))
+ : [new] "d" ((uint32_t)new)
+ : "cc"
+ );
+ ret = old == (uint32_t)*old_addr;
+ *old_addr = old;
+ return ret;
+ }
+ case 8: {
+ uint64_t old = *old_addr;
+
+ asm volatile ("csg %[old],%[new],%[address]"
+ : [old] "+d" (old),
+ [address] "+Q" (*(uint64_t *)(target))
+ : [new] "d" ((uint64_t)new)
+ : "cc"
+ );
+ ret = old == (uint64_t)*old_addr;
+ *old_addr = old;
+ return ret;
+ }
+ case 16: {
+ __uint128_t old = *old_addr;
+
+ asm volatile ("cdsg %[old],%[new],%[address]"
+ : [old] "+d" (old),
+ [address] "+Q" (*(__uint128_t *)(target))
+ : [new] "d" (new)
+ : "cc"
+ );
+ ret = old == *old_addr;
+ *old_addr = old;
+ return ret;
+ }
+ }
+ GUEST_ASSERT_1(false, "Invalid size");
+ return 0;
+}
+
+const unsigned int cmpxchg_iter_outer = 100, cmpxchg_iter_inner = 10000;
+
+static void guest_cmpxchg_key(void)
+{
+ int size, offset;
+ __uint128_t old, new;
+
+ set_storage_key_range(mem1, max_block, 0x10);
+ set_storage_key_range(mem2, max_block, 0x10);
+ GUEST_SYNC(STAGE_SKEYS_SET);
+
+ for (int i = 0; i < cmpxchg_iter_outer; i++) {
+ do {
+ old = 1;
+ } while (!_cmpxchg(16, mem1, &old, 0));
+ for (int j = 0; j < cmpxchg_iter_inner; j++) {
+ choose_block(true, i + j, &size, &offset);
+ do {
+ new = permutate_bits(true, i + j, size, old);
+ } while (!_cmpxchg(size, mem2 + offset, &old, new));
+ }
+ }
+
+ GUEST_SYNC(STAGE_DONE);
+}
+
+static void *run_guest(void *data)
+{
+ struct test_info *info = data;
+
+ HOST_SYNC(*info, STAGE_DONE);
+ return NULL;
+}
+
+static char *quad_to_char(__uint128_t *quad, int size)
+{
+ return ((char *)quad) + (sizeof(*quad) - size);
+}
+
+static void test_cmpxchg_key_concurrent(void)
+{
+ struct test_default t = test_default_init(guest_cmpxchg_key);
+ int size, offset;
+ __uint128_t old, new;
+ bool success;
+ pthread_t thread;
+
+ HOST_SYNC(t.vcpu, STAGE_SKEYS_SET);
+ prepare_mem12();
+ MOP(t.vcpu, LOGICAL, WRITE, mem1, max_block, GADDR_V(mem2));
+ pthread_create(&thread, NULL, run_guest, &t.vcpu);
+
+ for (int i = 0; i < cmpxchg_iter_outer; i++) {
+ do {
+ old = 0;
+ new = 1;
+ MOP(t.vm, ABSOLUTE, CMPXCHG, &new,
+ sizeof(new), GADDR_V(mem1),
+ CMPXCHG_OLD(&old),
+ CMPXCHG_SUCCESS(&success), KEY(1));
+ } while (!success);
+ for (int j = 0; j < cmpxchg_iter_inner; j++) {
+ choose_block(false, i + j, &size, &offset);
+ do {
+ new = permutate_bits(false, i + j, size, old);
+ MOP(t.vm, ABSOLUTE, CMPXCHG, quad_to_char(&new, size),
+ size, GADDR_V(mem2 + offset),
+ CMPXCHG_OLD(quad_to_char(&old, size)),
+ CMPXCHG_SUCCESS(&success), KEY(1));
+ } while (!success);
+ }
+ }
+
+ pthread_join(thread, NULL);
+
+ MOP(t.vcpu, LOGICAL, READ, mem2, max_block, GADDR_V(mem2));
+ TEST_ASSERT(popcount_eq(*(__uint128_t *)mem1, *(__uint128_t *)mem2),
+ "Must retain number of set bits");
kvm_vm_free(t.kvm_vm);
}
@@ -409,7 +712,7 @@ static void test_copy_key_storage_prot_override(void)
HOST_SYNC(t.vcpu, STAGE_SKEYS_SET);
/* vcpu, mismatching keys, storage protection override in effect */
- DEFAULT_WRITE_READ(t.vcpu, t.vcpu, LOGICAL, t.size, KEY(2));
+ default_write_read(t.vcpu, t.vcpu, LOGICAL, t.size, 2);
kvm_vm_free(t.kvm_vm);
}
@@ -422,8 +725,8 @@ static void test_copy_key_fetch_prot(void)
HOST_SYNC(t.vcpu, STAGE_SKEYS_SET);
/* vm/vcpu, matching key, fetch protection in effect */
- DEFAULT_READ(t.vcpu, t.vcpu, LOGICAL, t.size, GADDR_V(mem2), KEY(9));
- DEFAULT_READ(t.vcpu, t.vm, ABSOLUTE, t.size, GADDR_V(mem2), KEY(9));
+ default_read(t.vcpu, t.vcpu, LOGICAL, t.size, 9);
+ default_read(t.vcpu, t.vm, ABSOLUTE, t.size, 9);
kvm_vm_free(t.kvm_vm);
}
@@ -454,9 +757,27 @@ static void test_errors_key(void)
/* vm/vcpu, mismatching keys, fetch protection in effect */
CHECK_N_DO(ERR_PROT_MOP, t.vcpu, LOGICAL, WRITE, mem1, t.size, GADDR_V(mem1), KEY(2));
- CHECK_N_DO(ERR_PROT_MOP, t.vcpu, LOGICAL, READ, mem2, t.size, GADDR_V(mem2), KEY(2));
+ CHECK_N_DO(ERR_PROT_MOP, t.vcpu, LOGICAL, READ, mem2, t.size, GADDR_V(mem1), KEY(2));
CHECK_N_DO(ERR_PROT_MOP, t.vm, ABSOLUTE, WRITE, mem1, t.size, GADDR_V(mem1), KEY(2));
- CHECK_N_DO(ERR_PROT_MOP, t.vm, ABSOLUTE, READ, mem2, t.size, GADDR_V(mem2), KEY(2));
+ CHECK_N_DO(ERR_PROT_MOP, t.vm, ABSOLUTE, READ, mem2, t.size, GADDR_V(mem1), KEY(2));
+
+ kvm_vm_free(t.kvm_vm);
+}
+
+static void test_errors_cmpxchg_key(void)
+{
+ struct test_default t = test_default_init(guest_copy_key_fetch_prot);
+ int i;
+
+ HOST_SYNC(t.vcpu, STAGE_INITED);
+ HOST_SYNC(t.vcpu, STAGE_SKEYS_SET);
+
+ for (i = 1; i <= 16; i *= 2) {
+ __uint128_t old = 0;
+
+ ERR_PROT_MOP(t.vm, ABSOLUTE, CMPXCHG, mem2, i, GADDR_V(mem2),
+ CMPXCHG_OLD(&old), KEY(2));
+ }
kvm_vm_free(t.kvm_vm);
}
@@ -518,7 +839,7 @@ static void guest_copy_key_fetch_prot_override(void)
GUEST_SYNC(STAGE_INITED);
set_storage_key_range(0, PAGE_SIZE, 0x18);
set_storage_key_range((void *)last_page_addr, PAGE_SIZE, 0x0);
- asm volatile ("sske %[key],%[addr]\n" :: [addr] "r"(0), [key] "r"(0x18) : "cc");
+ asm volatile ("sske %[key],%[addr]\n" :: [addr] "r"(0L), [key] "r"(0x18) : "cc");
GUEST_SYNC(STAGE_SKEYS_SET);
for (;;) {
@@ -606,7 +927,7 @@ static void test_errors_key_fetch_prot_override_enabled(void)
/*
* vcpu, mismatching keys on fetch,
- * fetch protection override does not apply because memory range acceeded
+ * fetch protection override does not apply because memory range exceeded
*/
CHECK_N_DO(ERR_PROT_MOP, t.vcpu, LOGICAL, READ, mem2, 2048 + 1, GADDR_V(0), KEY(2));
CHECK_N_DO(ERR_PROT_MOP, t.vcpu, LOGICAL, READ, mem2, PAGE_SIZE + 2048 + 1,
@@ -645,7 +966,9 @@ static void _test_errors_common(struct test_info info, enum mop_target target, i
/* Bad guest address: */
rv = ERR_MOP(info, target, WRITE, mem1, size, GADDR((void *)~0xfffUL), CHECK_ONLY);
- TEST_ASSERT(rv > 0, "ioctl does not report bad guest memory access");
+ TEST_ASSERT(rv > 0, "ioctl does not report bad guest memory address with CHECK_ONLY");
+ rv = ERR_MOP(info, target, WRITE, mem1, size, GADDR((void *)~0xfffUL));
+ TEST_ASSERT(rv > 0, "ioctl does not report bad guest memory address on write");
/* Bad host address: */
rv = ERR_MOP(info, target, WRITE, 0, size, GADDR_V(mem1));
@@ -694,85 +1017,138 @@ static void test_errors(void)
kvm_vm_free(t.kvm_vm);
}
-struct testdef {
- const char *name;
- void (*test)(void);
- int extension;
-} testlist[] = {
- {
- .name = "simple copy",
- .test = test_copy,
- },
- {
- .name = "generic error checks",
- .test = test_errors,
- },
- {
- .name = "copy with storage keys",
- .test = test_copy_key,
- .extension = 1,
- },
- {
- .name = "copy with key storage protection override",
- .test = test_copy_key_storage_prot_override,
- .extension = 1,
- },
- {
- .name = "copy with key fetch protection",
- .test = test_copy_key_fetch_prot,
- .extension = 1,
- },
- {
- .name = "copy with key fetch protection override",
- .test = test_copy_key_fetch_prot_override,
- .extension = 1,
- },
- {
- .name = "error checks with key",
- .test = test_errors_key,
- .extension = 1,
- },
- {
- .name = "termination",
- .test = test_termination,
- .extension = 1,
- },
- {
- .name = "error checks with key storage protection override",
- .test = test_errors_key_storage_prot_override,
- .extension = 1,
- },
- {
- .name = "error checks without key fetch prot override",
- .test = test_errors_key_fetch_prot_override_not_enabled,
- .extension = 1,
- },
- {
- .name = "error checks with key fetch prot override",
- .test = test_errors_key_fetch_prot_override_enabled,
- .extension = 1,
- },
-};
+static void test_errors_cmpxchg(void)
+{
+ struct test_default t = test_default_init(guest_idle);
+ __uint128_t old;
+ int rv, i, power = 1;
+
+ HOST_SYNC(t.vcpu, STAGE_INITED);
+
+ for (i = 0; i < 32; i++) {
+ if (i == power) {
+ power *= 2;
+ continue;
+ }
+ rv = ERR_MOP(t.vm, ABSOLUTE, CMPXCHG, mem1, i, GADDR_V(mem1),
+ CMPXCHG_OLD(&old));
+ TEST_ASSERT(rv == -1 && errno == EINVAL,
+ "ioctl allows bad size for cmpxchg");
+ }
+ for (i = 1; i <= 16; i *= 2) {
+ rv = ERR_MOP(t.vm, ABSOLUTE, CMPXCHG, mem1, i, GADDR((void *)~0xfffUL),
+ CMPXCHG_OLD(&old));
+ TEST_ASSERT(rv > 0, "ioctl allows bad guest address for cmpxchg");
+ }
+ for (i = 2; i <= 16; i *= 2) {
+ rv = ERR_MOP(t.vm, ABSOLUTE, CMPXCHG, mem1, i, GADDR_V(mem1 + 1),
+ CMPXCHG_OLD(&old));
+ TEST_ASSERT(rv == -1 && errno == EINVAL,
+ "ioctl allows bad alignment for cmpxchg");
+ }
+
+ kvm_vm_free(t.kvm_vm);
+}
int main(int argc, char *argv[])
{
int extension_cap, idx;
TEST_REQUIRE(kvm_has_cap(KVM_CAP_S390_MEM_OP));
+ extension_cap = kvm_check_cap(KVM_CAP_S390_MEM_OP_EXTENSION);
- ksft_print_header();
+ struct testdef {
+ const char *name;
+ void (*test)(void);
+ bool requirements_met;
+ } testlist[] = {
+ {
+ .name = "simple copy",
+ .test = test_copy,
+ .requirements_met = true,
+ },
+ {
+ .name = "generic error checks",
+ .test = test_errors,
+ .requirements_met = true,
+ },
+ {
+ .name = "copy with storage keys",
+ .test = test_copy_key,
+ .requirements_met = extension_cap > 0,
+ },
+ {
+ .name = "cmpxchg with storage keys",
+ .test = test_cmpxchg_key,
+ .requirements_met = extension_cap & 0x2,
+ },
+ {
+ .name = "concurrently cmpxchg with storage keys",
+ .test = test_cmpxchg_key_concurrent,
+ .requirements_met = extension_cap & 0x2,
+ },
+ {
+ .name = "copy with key storage protection override",
+ .test = test_copy_key_storage_prot_override,
+ .requirements_met = extension_cap > 0,
+ },
+ {
+ .name = "copy with key fetch protection",
+ .test = test_copy_key_fetch_prot,
+ .requirements_met = extension_cap > 0,
+ },
+ {
+ .name = "copy with key fetch protection override",
+ .test = test_copy_key_fetch_prot_override,
+ .requirements_met = extension_cap > 0,
+ },
+ {
+ .name = "error checks with key",
+ .test = test_errors_key,
+ .requirements_met = extension_cap > 0,
+ },
+ {
+ .name = "error checks for cmpxchg with key",
+ .test = test_errors_cmpxchg_key,
+ .requirements_met = extension_cap & 0x2,
+ },
+ {
+ .name = "error checks for cmpxchg",
+ .test = test_errors_cmpxchg,
+ .requirements_met = extension_cap & 0x2,
+ },
+ {
+ .name = "termination",
+ .test = test_termination,
+ .requirements_met = extension_cap > 0,
+ },
+ {
+ .name = "error checks with key storage protection override",
+ .test = test_errors_key_storage_prot_override,
+ .requirements_met = extension_cap > 0,
+ },
+ {
+ .name = "error checks without key fetch prot override",
+ .test = test_errors_key_fetch_prot_override_not_enabled,
+ .requirements_met = extension_cap > 0,
+ },
+ {
+ .name = "error checks with key fetch prot override",
+ .test = test_errors_key_fetch_prot_override_enabled,
+ .requirements_met = extension_cap > 0,
+ },
+ };
+ ksft_print_header();
ksft_set_plan(ARRAY_SIZE(testlist));
- extension_cap = kvm_check_cap(KVM_CAP_S390_MEM_OP_EXTENSION);
for (idx = 0; idx < ARRAY_SIZE(testlist); idx++) {
- if (extension_cap >= testlist[idx].extension) {
+ if (testlist[idx].requirements_met) {
testlist[idx].test();
ksft_test_result_pass("%s\n", testlist[idx].name);
} else {
- ksft_test_result_skip("%s - extension level %d not supported\n",
- testlist[idx].name,
- testlist[idx].extension);
+ ksft_test_result_skip("%s - requirements not met (kernel has extension cap %#x)\n",
+ testlist[idx].name, extension_cap);
}
}
diff --git a/tools/testing/selftests/kvm/s390x/sync_regs_test.c b/tools/testing/selftests/kvm/s390x/sync_regs_test.c
index 2ddde41c44ba..636a70ddac1e 100644
--- a/tools/testing/selftests/kvm/s390x/sync_regs_test.c
+++ b/tools/testing/selftests/kvm/s390x/sync_regs_test.c
@@ -126,10 +126,7 @@ void test_req_and_verify_all_valid_regs(struct kvm_vcpu *vcpu)
run->kvm_valid_regs = TEST_SYNC_FIELDS;
rv = _vcpu_run(vcpu);
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_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
TEST_ASSERT(run->s390_sieic.icptcode == 4 &&
(run->s390_sieic.ipa >> 8) == 0x83 &&
(run->s390_sieic.ipb >> 16) == 0x501,
@@ -165,10 +162,7 @@ void test_set_and_verify_various_reg_values(struct kvm_vcpu *vcpu)
rv = _vcpu_run(vcpu);
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_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
TEST_ASSERT(run->s.regs.gprs[11] == 0xBAD1DEA + 1,
"r11 sync regs value incorrect 0x%llx.",
run->s.regs.gprs[11]);
@@ -200,10 +194,7 @@ void test_clear_kvm_dirty_regs_bits(struct kvm_vcpu *vcpu)
run->s.regs.diag318 = 0x4B1D;
rv = _vcpu_run(vcpu);
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_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
TEST_ASSERT(run->s.regs.gprs[11] != 0xDEADBEEF,
"r11 sync regs value incorrect 0x%llx.",
run->s.regs.gprs[11]);
diff --git a/tools/testing/selftests/kvm/set_memory_region_test.c b/tools/testing/selftests/kvm/set_memory_region_test.c
index 2ef1d1b72ce4..a849ce23ca97 100644
--- a/tools/testing/selftests/kvm/set_memory_region_test.c
+++ b/tools/testing/selftests/kvm/set_memory_region_test.c
@@ -308,7 +308,6 @@ static void test_delete_memory_region(void)
static void test_zero_memory_regions(void)
{
struct kvm_vcpu *vcpu;
- struct kvm_run *run;
struct kvm_vm *vm;
pr_info("Testing KVM_RUN with zero added memory regions\n");
@@ -318,10 +317,7 @@ static void test_zero_memory_regions(void)
vm_ioctl(vm, KVM_SET_NR_MMU_PAGES, (void *)64ul);
vcpu_run(vcpu);
-
- run = vcpu->run;
- TEST_ASSERT(run->exit_reason == KVM_EXIT_INTERNAL_ERROR,
- "Unexpected exit_reason = %u\n", run->exit_reason);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_INTERNAL_ERROR);
kvm_vm_free(vm);
}
diff --git a/tools/testing/selftests/kvm/x86_64/amx_test.c b/tools/testing/selftests/kvm/x86_64/amx_test.c
index bd72c6eb3b67..b646cdb5055a 100644
--- a/tools/testing/selftests/kvm/x86_64/amx_test.c
+++ b/tools/testing/selftests/kvm/x86_64/amx_test.c
@@ -241,7 +241,6 @@ int main(int argc, char *argv[])
struct kvm_regs regs1, regs2;
struct kvm_vcpu *vcpu;
struct kvm_vm *vm;
- struct kvm_run *run;
struct kvm_x86_state *state;
int xsave_restore_size;
vm_vaddr_t amx_cfg, tiledata, xsavedata;
@@ -268,7 +267,6 @@ int main(int argc, char *argv[])
"KVM should enumerate max XSAVE size when XSAVE is supported");
xsave_restore_size = kvm_cpu_property(X86_PROPERTY_XSTATE_MAX_SIZE);
- run = vcpu->run;
vcpu_regs_get(vcpu, &regs1);
/* Register #NM handler */
@@ -291,10 +289,7 @@ int main(int argc, char *argv[])
for (stage = 1; ; stage++) {
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Stage %d: unexpected exit reason: %u (%s),\n",
- stage, run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
@@ -350,7 +345,6 @@ int main(int argc, char *argv[])
/* Restore state in a new VM. */
vcpu = vm_recreate_with_one_vcpu(vm);
vcpu_load_state(vcpu, state);
- run = vcpu->run;
kvm_x86_state_cleanup(state);
memset(&regs2, 0, sizeof(regs2));
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 1027a671c7d3..624dc725e14d 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
@@ -50,7 +50,6 @@ static void guest_code(void)
int main(int argc, char *argv[])
{
struct kvm_vcpu *vcpu;
- struct kvm_run *run;
struct kvm_vm *vm;
struct kvm_sregs sregs;
struct ucall uc;
@@ -58,15 +57,10 @@ int main(int argc, char *argv[])
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XSAVE));
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
- run = vcpu->run;
while (1) {
vcpu_run(vcpu);
-
- 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_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_SYNC:
diff --git a/tools/testing/selftests/kvm/x86_64/debug_regs.c b/tools/testing/selftests/kvm/x86_64/debug_regs.c
index 7ef99c3359a0..f6b295e0b2d2 100644
--- a/tools/testing/selftests/kvm/x86_64/debug_regs.c
+++ b/tools/testing/selftests/kvm/x86_64/debug_regs.c
@@ -204,7 +204,7 @@ int main(void)
vcpu_guest_debug_set(vcpu, &debug);
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "KVM_EXIT_IO");
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
cmd = get_ucall(vcpu, &uc);
TEST_ASSERT(cmd == UCALL_DONE, "UCALL_DONE");
diff --git a/tools/testing/selftests/kvm/x86_64/exit_on_emulation_failure_test.c b/tools/testing/selftests/kvm/x86_64/exit_on_emulation_failure_test.c
index 37c61f712fd5..e334844d6e1d 100644
--- a/tools/testing/selftests/kvm/x86_64/exit_on_emulation_failure_test.c
+++ b/tools/testing/selftests/kvm/x86_64/exit_on_emulation_failure_test.c
@@ -26,9 +26,6 @@ int main(int argc, char *argv[])
struct kvm_vcpu *vcpu;
struct kvm_vm *vm;
- /* Tell stdout not to buffer its content */
- setbuf(stdout, NULL);
-
TEST_REQUIRE(kvm_has_cap(KVM_CAP_EXIT_ON_EMULATION_FAILURE));
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
diff --git a/tools/testing/selftests/kvm/x86_64/fix_hypercall_test.c b/tools/testing/selftests/kvm/x86_64/fix_hypercall_test.c
index 32f7e09ef67c..0f728f05ea82 100644
--- a/tools/testing/selftests/kvm/x86_64/fix_hypercall_test.c
+++ b/tools/testing/selftests/kvm/x86_64/fix_hypercall_test.c
@@ -48,10 +48,10 @@ static void guest_main(void)
const uint8_t *other_hypercall_insn;
uint64_t ret;
- if (is_intel_cpu()) {
+ if (host_cpu_is_intel) {
native_hypercall_insn = vmx_vmcall;
other_hypercall_insn = svm_vmmcall;
- } else if (is_amd_cpu()) {
+ } else if (host_cpu_is_amd) {
native_hypercall_insn = svm_vmmcall;
other_hypercall_insn = vmx_vmcall;
} else {
diff --git a/tools/testing/selftests/kvm/x86_64/flds_emulation.h b/tools/testing/selftests/kvm/x86_64/flds_emulation.h
index e43a7df25f2c..0a1573d52882 100644
--- a/tools/testing/selftests/kvm/x86_64/flds_emulation.h
+++ b/tools/testing/selftests/kvm/x86_64/flds_emulation.h
@@ -24,10 +24,7 @@ static inline void handle_flds_emulation_failure_exit(struct kvm_vcpu *vcpu)
uint8_t *insn_bytes;
uint64_t flags;
- TEST_ASSERT(run->exit_reason == KVM_EXIT_INTERNAL_ERROR,
- "Unexpected exit reason: %u (%s)",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_INTERNAL_ERROR);
TEST_ASSERT(run->emulation_failure.suberror == KVM_INTERNAL_ERROR_EMULATION,
"Unexpected suberror: %u",
diff --git a/tools/testing/selftests/kvm/x86_64/hyperv_clock.c b/tools/testing/selftests/kvm/x86_64/hyperv_clock.c
index d576bc8ce823..f25749eaa6a8 100644
--- a/tools/testing/selftests/kvm/x86_64/hyperv_clock.c
+++ b/tools/testing/selftests/kvm/x86_64/hyperv_clock.c
@@ -104,7 +104,7 @@ static void guest_main(struct ms_hyperv_tsc_page *tsc_page, vm_paddr_t tsc_page_
/* Set Guest OS id to enable Hyper-V emulation */
GUEST_SYNC(1);
- wrmsr(HV_X64_MSR_GUEST_OS_ID, (u64)0x8100 << 48);
+ wrmsr(HV_X64_MSR_GUEST_OS_ID, HYPERV_LINUX_OS_ID);
GUEST_SYNC(2);
check_tsc_msr_rdtsc();
@@ -207,13 +207,11 @@ int main(void)
{
struct kvm_vcpu *vcpu;
struct kvm_vm *vm;
- struct kvm_run *run;
struct ucall uc;
vm_vaddr_t tsc_page_gva;
int stage;
vm = vm_create_with_one_vcpu(&vcpu, guest_main);
- run = vcpu->run;
vcpu_set_hv_cpuid(vcpu);
@@ -227,10 +225,7 @@ int main(void)
for (stage = 1;; stage++) {
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Stage %d: unexpected exit reason: %u (%s),\n",
- stage, run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
diff --git a/tools/testing/selftests/kvm/x86_64/hyperv_evmcs.c b/tools/testing/selftests/kvm/x86_64/hyperv_evmcs.c
index af29e5776d40..7bde0c4dfdbd 100644
--- a/tools/testing/selftests/kvm/x86_64/hyperv_evmcs.c
+++ b/tools/testing/selftests/kvm/x86_64/hyperv_evmcs.c
@@ -237,7 +237,6 @@ int main(int argc, char *argv[])
struct kvm_vcpu *vcpu;
struct kvm_vm *vm;
- struct kvm_run *run;
struct ucall uc;
int stage;
@@ -266,13 +265,8 @@ int main(int argc, char *argv[])
pr_info("Running L1 which uses EVMCS to run L2\n");
for (stage = 1;; stage++) {
- run = vcpu->run;
-
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Stage %d: unexpected exit reason: %u (%s),\n",
- stage, run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
diff --git a/tools/testing/selftests/kvm/x86_64/hyperv_extended_hypercalls.c b/tools/testing/selftests/kvm/x86_64/hyperv_extended_hypercalls.c
new file mode 100644
index 000000000000..73af44d2167f
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86_64/hyperv_extended_hypercalls.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Test Hyper-V extended hypercall, HV_EXT_CALL_QUERY_CAPABILITIES (0x8001),
+ * exit to userspace and receive result in guest.
+ *
+ * Negative tests are present in hyperv_features.c
+ *
+ * Copyright 2022 Google LLC
+ * Author: Vipin Sharma <vipinsh@google.com>
+ */
+
+#include "kvm_util.h"
+#include "processor.h"
+#include "hyperv.h"
+
+/* Any value is fine */
+#define EXT_CAPABILITIES 0xbull
+
+static void guest_code(vm_paddr_t in_pg_gpa, vm_paddr_t out_pg_gpa,
+ vm_vaddr_t out_pg_gva)
+{
+ uint64_t *output_gva;
+
+ wrmsr(HV_X64_MSR_GUEST_OS_ID, HYPERV_LINUX_OS_ID);
+ wrmsr(HV_X64_MSR_HYPERCALL, in_pg_gpa);
+
+ output_gva = (uint64_t *)out_pg_gva;
+
+ hyperv_hypercall(HV_EXT_CALL_QUERY_CAPABILITIES, in_pg_gpa, out_pg_gpa);
+
+ /* TLFS states output will be a uint64_t value */
+ GUEST_ASSERT_EQ(*output_gva, EXT_CAPABILITIES);
+
+ GUEST_DONE();
+}
+
+int main(void)
+{
+ vm_vaddr_t hcall_out_page;
+ vm_vaddr_t hcall_in_page;
+ struct kvm_vcpu *vcpu;
+ struct kvm_run *run;
+ struct kvm_vm *vm;
+ uint64_t *outval;
+ struct ucall uc;
+
+ /* Verify if extended hypercalls are supported */
+ if (!kvm_cpuid_has(kvm_get_supported_hv_cpuid(),
+ HV_ENABLE_EXTENDED_HYPERCALLS)) {
+ print_skip("Extended calls not supported by the kernel");
+ exit(KSFT_SKIP);
+ }
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+ run = vcpu->run;
+ vcpu_set_hv_cpuid(vcpu);
+
+ /* Hypercall input */
+ hcall_in_page = vm_vaddr_alloc_pages(vm, 1);
+ memset(addr_gva2hva(vm, hcall_in_page), 0x0, vm->page_size);
+
+ /* Hypercall output */
+ hcall_out_page = vm_vaddr_alloc_pages(vm, 1);
+ memset(addr_gva2hva(vm, hcall_out_page), 0x0, vm->page_size);
+
+ vcpu_args_set(vcpu, 3, addr_gva2gpa(vm, hcall_in_page),
+ addr_gva2gpa(vm, hcall_out_page), hcall_out_page);
+
+ vcpu_run(vcpu);
+
+ TEST_ASSERT(run->exit_reason == KVM_EXIT_HYPERV,
+ "Unexpected exit reason: %u (%s)",
+ run->exit_reason, exit_reason_str(run->exit_reason));
+
+ outval = addr_gpa2hva(vm, run->hyperv.u.hcall.params[1]);
+ *outval = EXT_CAPABILITIES;
+ run->hyperv.u.hcall.result = HV_STATUS_SUCCESS;
+
+ vcpu_run(vcpu);
+
+ TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
+ "Unexpected exit reason: %u (%s)",
+ run->exit_reason, exit_reason_str(run->exit_reason));
+
+ switch (get_ucall(vcpu, &uc)) {
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT_2(uc, "arg1 = %ld, arg2 = %ld");
+ break;
+ case UCALL_DONE:
+ break;
+ default:
+ TEST_FAIL("Unhandled ucall: %ld", uc.cmd);
+ }
+
+ kvm_vm_free(vm);
+ return 0;
+}
diff --git a/tools/testing/selftests/kvm/x86_64/hyperv_features.c b/tools/testing/selftests/kvm/x86_64/hyperv_features.c
index 3163c3e8db0a..78606de9385d 100644
--- a/tools/testing/selftests/kvm/x86_64/hyperv_features.c
+++ b/tools/testing/selftests/kvm/x86_64/hyperv_features.c
@@ -13,9 +13,17 @@
#include "processor.h"
#include "hyperv.h"
+/*
+ * HYPERV_CPUID_ENLIGHTMENT_INFO.EBX is not a 'feature' CPUID leaf
+ * but to activate the feature it is sufficient to set it to a non-zero
+ * value. Use BIT(0) for that.
+ */
+#define HV_PV_SPINLOCKS_TEST \
+ KVM_X86_CPU_FEATURE(HYPERV_CPUID_ENLIGHTMENT_INFO, 0, EBX, 0)
+
struct msr_data {
uint32_t idx;
- bool available;
+ bool fault_expected;
bool write;
u64 write_val;
};
@@ -26,22 +34,46 @@ struct hcall_data {
bool ud_expected;
};
+static bool is_write_only_msr(uint32_t msr)
+{
+ return msr == HV_X64_MSR_EOI;
+}
+
static void guest_msr(struct msr_data *msr)
{
- uint64_t ignored;
- uint8_t vector;
+ uint8_t vector = 0;
+ uint64_t msr_val = 0;
GUEST_ASSERT(msr->idx);
- if (!msr->write)
- vector = rdmsr_safe(msr->idx, &ignored);
- else
+ if (msr->write)
vector = wrmsr_safe(msr->idx, msr->write_val);
- if (msr->available)
- GUEST_ASSERT_2(!vector, msr->idx, vector);
+ if (!vector && (!msr->write || !is_write_only_msr(msr->idx)))
+ vector = rdmsr_safe(msr->idx, &msr_val);
+
+ if (msr->fault_expected)
+ GUEST_ASSERT_3(vector == GP_VECTOR, msr->idx, vector, GP_VECTOR);
else
- GUEST_ASSERT_2(vector == GP_VECTOR, msr->idx, vector);
+ GUEST_ASSERT_3(!vector, msr->idx, vector, 0);
+
+ if (vector || is_write_only_msr(msr->idx))
+ goto done;
+
+ if (msr->write)
+ GUEST_ASSERT_3(msr_val == msr->write_val, msr->idx,
+ msr_val, msr->write_val);
+
+ /* Invariant TSC bit appears when TSC invariant control MSR is written to */
+ if (msr->idx == HV_X64_MSR_TSC_INVARIANT_CONTROL) {
+ if (!this_cpu_has(HV_ACCESS_TSC_INVARIANT))
+ GUEST_ASSERT(this_cpu_has(X86_FEATURE_INVTSC));
+ else
+ GUEST_ASSERT(this_cpu_has(X86_FEATURE_INVTSC) ==
+ !!(msr_val & HV_INVARIANT_TSC_EXPOSED));
+ }
+
+done:
GUEST_DONE();
}
@@ -89,14 +121,13 @@ static void vcpu_reset_hv_cpuid(struct kvm_vcpu *vcpu)
static void guest_test_msrs_access(void)
{
struct kvm_cpuid2 *prev_cpuid = NULL;
- struct kvm_cpuid_entry2 *feat, *dbg;
struct kvm_vcpu *vcpu;
- struct kvm_run *run;
struct kvm_vm *vm;
struct ucall uc;
int stage = 0;
vm_vaddr_t msr_gva;
struct msr_data *msr;
+ bool has_invtsc = kvm_cpu_has(X86_FEATURE_INVTSC);
while (true) {
vm = vm_create_with_one_vcpu(&vcpu, guest_msr);
@@ -116,14 +147,9 @@ static void guest_test_msrs_access(void)
vcpu_init_cpuid(vcpu, prev_cpuid);
}
- feat = vcpu_get_cpuid_entry(vcpu, HYPERV_CPUID_FEATURES);
- dbg = vcpu_get_cpuid_entry(vcpu, HYPERV_CPUID_SYNDBG_PLATFORM_CAPABILITIES);
-
vm_init_descriptor_tables(vm);
vcpu_init_descriptor_tables(vcpu);
- run = vcpu->run;
-
/* TODO: Make this entire test easier to maintain. */
if (stage >= 21)
vcpu_enable_cap(vcpu, KVM_CAP_HYPERV_SYNIC2, 0);
@@ -134,133 +160,139 @@ static void guest_test_msrs_access(void)
* Only available when Hyper-V identification is set
*/
msr->idx = HV_X64_MSR_GUEST_OS_ID;
- msr->write = 0;
- msr->available = 0;
+ msr->write = false;
+ msr->fault_expected = true;
break;
case 1:
msr->idx = HV_X64_MSR_HYPERCALL;
- msr->write = 0;
- msr->available = 0;
+ msr->write = false;
+ msr->fault_expected = true;
break;
case 2:
- feat->eax |= HV_MSR_HYPERCALL_AVAILABLE;
+ vcpu_set_cpuid_feature(vcpu, HV_MSR_HYPERCALL_AVAILABLE);
/*
* HV_X64_MSR_GUEST_OS_ID has to be written first to make
* HV_X64_MSR_HYPERCALL available.
*/
msr->idx = HV_X64_MSR_GUEST_OS_ID;
- msr->write = 1;
+ msr->write = true;
msr->write_val = HYPERV_LINUX_OS_ID;
- msr->available = 1;
+ msr->fault_expected = false;
break;
case 3:
msr->idx = HV_X64_MSR_GUEST_OS_ID;
- msr->write = 0;
- msr->available = 1;
+ msr->write = false;
+ msr->fault_expected = false;
break;
case 4:
msr->idx = HV_X64_MSR_HYPERCALL;
- msr->write = 0;
- msr->available = 1;
+ msr->write = false;
+ msr->fault_expected = false;
break;
case 5:
msr->idx = HV_X64_MSR_VP_RUNTIME;
- msr->write = 0;
- msr->available = 0;
+ msr->write = false;
+ msr->fault_expected = true;
break;
case 6:
- feat->eax |= HV_MSR_VP_RUNTIME_AVAILABLE;
+ vcpu_set_cpuid_feature(vcpu, HV_MSR_VP_RUNTIME_AVAILABLE);
msr->idx = HV_X64_MSR_VP_RUNTIME;
- msr->write = 0;
- msr->available = 1;
+ msr->write = false;
+ msr->fault_expected = false;
break;
case 7:
/* Read only */
msr->idx = HV_X64_MSR_VP_RUNTIME;
- msr->write = 1;
+ msr->write = true;
msr->write_val = 1;
- msr->available = 0;
+ msr->fault_expected = true;
break;
case 8:
msr->idx = HV_X64_MSR_TIME_REF_COUNT;
- msr->write = 0;
- msr->available = 0;
+ msr->write = false;
+ msr->fault_expected = true;
break;
case 9:
- feat->eax |= HV_MSR_TIME_REF_COUNT_AVAILABLE;
+ vcpu_set_cpuid_feature(vcpu, HV_MSR_TIME_REF_COUNT_AVAILABLE);
msr->idx = HV_X64_MSR_TIME_REF_COUNT;
- msr->write = 0;
- msr->available = 1;
+ msr->write = false;
+ msr->fault_expected = false;
break;
case 10:
/* Read only */
msr->idx = HV_X64_MSR_TIME_REF_COUNT;
- msr->write = 1;
+ msr->write = true;
msr->write_val = 1;
- msr->available = 0;
+ msr->fault_expected = true;
break;
case 11:
msr->idx = HV_X64_MSR_VP_INDEX;
- msr->write = 0;
- msr->available = 0;
+ msr->write = false;
+ msr->fault_expected = true;
break;
case 12:
- feat->eax |= HV_MSR_VP_INDEX_AVAILABLE;
+ vcpu_set_cpuid_feature(vcpu, HV_MSR_VP_INDEX_AVAILABLE);
msr->idx = HV_X64_MSR_VP_INDEX;
- msr->write = 0;
- msr->available = 1;
+ msr->write = false;
+ msr->fault_expected = false;
break;
case 13:
/* Read only */
msr->idx = HV_X64_MSR_VP_INDEX;
- msr->write = 1;
+ msr->write = true;
msr->write_val = 1;
- msr->available = 0;
+ msr->fault_expected = true;
break;
case 14:
msr->idx = HV_X64_MSR_RESET;
- msr->write = 0;
- msr->available = 0;
+ msr->write = false;
+ msr->fault_expected = true;
break;
case 15:
- feat->eax |= HV_MSR_RESET_AVAILABLE;
+ vcpu_set_cpuid_feature(vcpu, HV_MSR_RESET_AVAILABLE);
msr->idx = HV_X64_MSR_RESET;
- msr->write = 0;
- msr->available = 1;
+ msr->write = false;
+ msr->fault_expected = false;
break;
case 16:
msr->idx = HV_X64_MSR_RESET;
- msr->write = 1;
+ msr->write = true;
+ /*
+ * TODO: the test only writes '0' to HV_X64_MSR_RESET
+ * at the moment, writing some other value there will
+ * trigger real vCPU reset and the code is not prepared
+ * to handle it yet.
+ */
msr->write_val = 0;
- msr->available = 1;
+ msr->fault_expected = false;
break;
case 17:
msr->idx = HV_X64_MSR_REFERENCE_TSC;
- msr->write = 0;
- msr->available = 0;
+ msr->write = false;
+ msr->fault_expected = true;
break;
case 18:
- feat->eax |= HV_MSR_REFERENCE_TSC_AVAILABLE;
+ vcpu_set_cpuid_feature(vcpu, HV_MSR_REFERENCE_TSC_AVAILABLE);
msr->idx = HV_X64_MSR_REFERENCE_TSC;
- msr->write = 0;
- msr->available = 1;
+ msr->write = false;
+ msr->fault_expected = false;
break;
case 19:
msr->idx = HV_X64_MSR_REFERENCE_TSC;
- msr->write = 1;
+ msr->write = true;
msr->write_val = 0;
- msr->available = 1;
+ msr->fault_expected = false;
break;
case 20:
msr->idx = HV_X64_MSR_EOM;
- msr->write = 0;
- msr->available = 0;
+ msr->write = false;
+ msr->fault_expected = true;
break;
case 21:
/*
@@ -268,149 +300,185 @@ static void guest_test_msrs_access(void)
* capability enabled and guest visible CPUID bit unset.
*/
msr->idx = HV_X64_MSR_EOM;
- msr->write = 0;
- msr->available = 0;
+ msr->write = false;
+ msr->fault_expected = true;
break;
case 22:
- feat->eax |= HV_MSR_SYNIC_AVAILABLE;
+ vcpu_set_cpuid_feature(vcpu, HV_MSR_SYNIC_AVAILABLE);
msr->idx = HV_X64_MSR_EOM;
- msr->write = 0;
- msr->available = 1;
+ msr->write = false;
+ msr->fault_expected = false;
break;
case 23:
msr->idx = HV_X64_MSR_EOM;
- msr->write = 1;
+ msr->write = true;
msr->write_val = 0;
- msr->available = 1;
+ msr->fault_expected = false;
break;
case 24:
msr->idx = HV_X64_MSR_STIMER0_CONFIG;
- msr->write = 0;
- msr->available = 0;
+ msr->write = false;
+ msr->fault_expected = true;
break;
case 25:
- feat->eax |= HV_MSR_SYNTIMER_AVAILABLE;
+ vcpu_set_cpuid_feature(vcpu, HV_MSR_SYNTIMER_AVAILABLE);
msr->idx = HV_X64_MSR_STIMER0_CONFIG;
- msr->write = 0;
- msr->available = 1;
+ msr->write = false;
+ msr->fault_expected = false;
break;
case 26:
msr->idx = HV_X64_MSR_STIMER0_CONFIG;
- msr->write = 1;
+ msr->write = true;
msr->write_val = 0;
- msr->available = 1;
+ msr->fault_expected = false;
break;
case 27:
/* Direct mode test */
msr->idx = HV_X64_MSR_STIMER0_CONFIG;
- msr->write = 1;
+ msr->write = true;
msr->write_val = 1 << 12;
- msr->available = 0;
+ msr->fault_expected = true;
break;
case 28:
- feat->edx |= HV_STIMER_DIRECT_MODE_AVAILABLE;
+ vcpu_set_cpuid_feature(vcpu, HV_STIMER_DIRECT_MODE_AVAILABLE);
msr->idx = HV_X64_MSR_STIMER0_CONFIG;
- msr->write = 1;
+ msr->write = true;
msr->write_val = 1 << 12;
- msr->available = 1;
+ msr->fault_expected = false;
break;
case 29:
msr->idx = HV_X64_MSR_EOI;
- msr->write = 0;
- msr->available = 0;
+ msr->write = false;
+ msr->fault_expected = true;
break;
case 30:
- feat->eax |= HV_MSR_APIC_ACCESS_AVAILABLE;
+ vcpu_set_cpuid_feature(vcpu, HV_MSR_APIC_ACCESS_AVAILABLE);
msr->idx = HV_X64_MSR_EOI;
- msr->write = 1;
+ msr->write = true;
msr->write_val = 1;
- msr->available = 1;
+ msr->fault_expected = false;
break;
case 31:
msr->idx = HV_X64_MSR_TSC_FREQUENCY;
- msr->write = 0;
- msr->available = 0;
+ msr->write = false;
+ msr->fault_expected = true;
break;
case 32:
- feat->eax |= HV_ACCESS_FREQUENCY_MSRS;
+ vcpu_set_cpuid_feature(vcpu, HV_ACCESS_FREQUENCY_MSRS);
msr->idx = HV_X64_MSR_TSC_FREQUENCY;
- msr->write = 0;
- msr->available = 1;
+ msr->write = false;
+ msr->fault_expected = false;
break;
case 33:
/* Read only */
msr->idx = HV_X64_MSR_TSC_FREQUENCY;
- msr->write = 1;
+ msr->write = true;
msr->write_val = 1;
- msr->available = 0;
+ msr->fault_expected = true;
break;
case 34:
msr->idx = HV_X64_MSR_REENLIGHTENMENT_CONTROL;
- msr->write = 0;
- msr->available = 0;
+ msr->write = false;
+ msr->fault_expected = true;
break;
case 35:
- feat->eax |= HV_ACCESS_REENLIGHTENMENT;
+ vcpu_set_cpuid_feature(vcpu, HV_ACCESS_REENLIGHTENMENT);
msr->idx = HV_X64_MSR_REENLIGHTENMENT_CONTROL;
- msr->write = 0;
- msr->available = 1;
+ msr->write = false;
+ msr->fault_expected = false;
break;
case 36:
msr->idx = HV_X64_MSR_REENLIGHTENMENT_CONTROL;
- msr->write = 1;
+ msr->write = true;
msr->write_val = 1;
- msr->available = 1;
+ msr->fault_expected = false;
break;
case 37:
/* Can only write '0' */
msr->idx = HV_X64_MSR_TSC_EMULATION_STATUS;
- msr->write = 1;
+ msr->write = true;
msr->write_val = 1;
- msr->available = 0;
+ msr->fault_expected = true;
break;
case 38:
msr->idx = HV_X64_MSR_CRASH_P0;
- msr->write = 0;
- msr->available = 0;
+ msr->write = false;
+ msr->fault_expected = true;
break;
case 39:
- feat->edx |= HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE;
+ vcpu_set_cpuid_feature(vcpu, HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE);
msr->idx = HV_X64_MSR_CRASH_P0;
- msr->write = 0;
- msr->available = 1;
+ msr->write = false;
+ msr->fault_expected = false;
break;
case 40:
msr->idx = HV_X64_MSR_CRASH_P0;
- msr->write = 1;
+ msr->write = true;
msr->write_val = 1;
- msr->available = 1;
+ msr->fault_expected = false;
break;
case 41:
msr->idx = HV_X64_MSR_SYNDBG_STATUS;
- msr->write = 0;
- msr->available = 0;
+ msr->write = false;
+ msr->fault_expected = true;
break;
case 42:
- feat->edx |= HV_FEATURE_DEBUG_MSRS_AVAILABLE;
- dbg->eax |= HV_X64_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING;
+ vcpu_set_cpuid_feature(vcpu, HV_FEATURE_DEBUG_MSRS_AVAILABLE);
+ vcpu_set_cpuid_feature(vcpu, HV_X64_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING);
msr->idx = HV_X64_MSR_SYNDBG_STATUS;
- msr->write = 0;
- msr->available = 1;
+ msr->write = false;
+ msr->fault_expected = false;
break;
case 43:
msr->idx = HV_X64_MSR_SYNDBG_STATUS;
- msr->write = 1;
+ msr->write = true;
msr->write_val = 0;
- msr->available = 1;
+ msr->fault_expected = false;
break;
case 44:
+ /* MSR is not available when CPUID feature bit is unset */
+ if (!has_invtsc)
+ continue;
+ msr->idx = HV_X64_MSR_TSC_INVARIANT_CONTROL;
+ msr->write = false;
+ msr->fault_expected = true;
+ break;
+ case 45:
+ /* MSR is vailable when CPUID feature bit is set */
+ if (!has_invtsc)
+ continue;
+ vcpu_set_cpuid_feature(vcpu, HV_ACCESS_TSC_INVARIANT);
+ msr->idx = HV_X64_MSR_TSC_INVARIANT_CONTROL;
+ msr->write = false;
+ msr->fault_expected = false;
+ break;
+ case 46:
+ /* Writing bits other than 0 is forbidden */
+ if (!has_invtsc)
+ continue;
+ msr->idx = HV_X64_MSR_TSC_INVARIANT_CONTROL;
+ msr->write = true;
+ msr->write_val = 0xdeadbeef;
+ msr->fault_expected = true;
+ break;
+ case 47:
+ /* Setting bit 0 enables the feature */
+ if (!has_invtsc)
+ continue;
+ msr->idx = HV_X64_MSR_TSC_INVARIANT_CONTROL;
+ msr->write = true;
+ msr->write_val = 1;
+ msr->fault_expected = false;
+ break;
+
+ default:
kvm_vm_free(vm);
return;
}
@@ -423,13 +491,11 @@ static void guest_test_msrs_access(void)
msr->idx, msr->write ? "write" : "read");
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "unexpected exit reason: %u (%s)",
- run->exit_reason, exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
- REPORT_GUEST_ASSERT_2(uc, "MSR = %lx, vector = %lx");
+ REPORT_GUEST_ASSERT_3(uc, "MSR = %lx, arg1 = %lx, arg2 = %lx");
return;
case UCALL_DONE:
break;
@@ -445,10 +511,8 @@ static void guest_test_msrs_access(void)
static void guest_test_hcalls_access(void)
{
- struct kvm_cpuid_entry2 *feat, *recomm, *dbg;
struct kvm_cpuid2 *prev_cpuid = NULL;
struct kvm_vcpu *vcpu;
- struct kvm_run *run;
struct kvm_vm *vm;
struct ucall uc;
int stage = 0;
@@ -480,15 +544,9 @@ static void guest_test_hcalls_access(void)
vcpu_init_cpuid(vcpu, prev_cpuid);
}
- feat = vcpu_get_cpuid_entry(vcpu, HYPERV_CPUID_FEATURES);
- recomm = vcpu_get_cpuid_entry(vcpu, HYPERV_CPUID_ENLIGHTMENT_INFO);
- dbg = vcpu_get_cpuid_entry(vcpu, HYPERV_CPUID_SYNDBG_PLATFORM_CAPABILITIES);
-
- run = vcpu->run;
-
switch (stage) {
case 0:
- feat->eax |= HV_MSR_HYPERCALL_AVAILABLE;
+ vcpu_set_cpuid_feature(vcpu, HV_MSR_HYPERCALL_AVAILABLE);
hcall->control = 0xbeef;
hcall->expect = HV_STATUS_INVALID_HYPERCALL_CODE;
break;
@@ -498,7 +556,7 @@ static void guest_test_hcalls_access(void)
hcall->expect = HV_STATUS_ACCESS_DENIED;
break;
case 2:
- feat->ebx |= HV_POST_MESSAGES;
+ vcpu_set_cpuid_feature(vcpu, HV_POST_MESSAGES);
hcall->control = HVCALL_POST_MESSAGE;
hcall->expect = HV_STATUS_INVALID_HYPERCALL_INPUT;
break;
@@ -508,7 +566,7 @@ static void guest_test_hcalls_access(void)
hcall->expect = HV_STATUS_ACCESS_DENIED;
break;
case 4:
- feat->ebx |= HV_SIGNAL_EVENTS;
+ vcpu_set_cpuid_feature(vcpu, HV_SIGNAL_EVENTS);
hcall->control = HVCALL_SIGNAL_EVENT;
hcall->expect = HV_STATUS_INVALID_HYPERCALL_INPUT;
break;
@@ -518,12 +576,12 @@ static void guest_test_hcalls_access(void)
hcall->expect = HV_STATUS_INVALID_HYPERCALL_CODE;
break;
case 6:
- dbg->eax |= HV_X64_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING;
+ vcpu_set_cpuid_feature(vcpu, HV_X64_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING);
hcall->control = HVCALL_RESET_DEBUG_SESSION;
hcall->expect = HV_STATUS_ACCESS_DENIED;
break;
case 7:
- feat->ebx |= HV_DEBUGGING;
+ vcpu_set_cpuid_feature(vcpu, HV_DEBUGGING);
hcall->control = HVCALL_RESET_DEBUG_SESSION;
hcall->expect = HV_STATUS_OPERATION_DENIED;
break;
@@ -533,7 +591,7 @@ static void guest_test_hcalls_access(void)
hcall->expect = HV_STATUS_ACCESS_DENIED;
break;
case 9:
- recomm->eax |= HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED;
+ vcpu_set_cpuid_feature(vcpu, HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED);
hcall->control = HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE;
hcall->expect = HV_STATUS_SUCCESS;
break;
@@ -542,7 +600,7 @@ static void guest_test_hcalls_access(void)
hcall->expect = HV_STATUS_ACCESS_DENIED;
break;
case 11:
- recomm->eax |= HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED;
+ vcpu_set_cpuid_feature(vcpu, HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED);
hcall->control = HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX;
hcall->expect = HV_STATUS_SUCCESS;
break;
@@ -552,7 +610,7 @@ static void guest_test_hcalls_access(void)
hcall->expect = HV_STATUS_ACCESS_DENIED;
break;
case 13:
- recomm->eax |= HV_X64_CLUSTER_IPI_RECOMMENDED;
+ vcpu_set_cpuid_feature(vcpu, HV_X64_CLUSTER_IPI_RECOMMENDED);
hcall->control = HVCALL_SEND_IPI;
hcall->expect = HV_STATUS_INVALID_HYPERCALL_INPUT;
break;
@@ -567,7 +625,7 @@ static void guest_test_hcalls_access(void)
hcall->expect = HV_STATUS_ACCESS_DENIED;
break;
case 16:
- recomm->ebx = 0xfff;
+ vcpu_set_cpuid_feature(vcpu, HV_PV_SPINLOCKS_TEST);
hcall->control = HVCALL_NOTIFY_LONG_SPIN_WAIT;
hcall->expect = HV_STATUS_SUCCESS;
break;
@@ -577,12 +635,21 @@ static void guest_test_hcalls_access(void)
hcall->ud_expected = true;
break;
case 18:
- feat->edx |= HV_X64_HYPERCALL_XMM_INPUT_AVAILABLE;
+ vcpu_set_cpuid_feature(vcpu, HV_X64_HYPERCALL_XMM_INPUT_AVAILABLE);
hcall->control = HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE | HV_HYPERCALL_FAST_BIT;
hcall->ud_expected = false;
hcall->expect = HV_STATUS_SUCCESS;
break;
case 19:
+ hcall->control = HV_EXT_CALL_QUERY_CAPABILITIES;
+ hcall->expect = HV_STATUS_ACCESS_DENIED;
+ break;
+ case 20:
+ vcpu_set_cpuid_feature(vcpu, HV_ENABLE_EXTENDED_HYPERCALLS);
+ hcall->control = HV_EXT_CALL_QUERY_CAPABILITIES | HV_HYPERCALL_FAST_BIT;
+ hcall->expect = HV_STATUS_INVALID_PARAMETER;
+ break;
+ case 21:
kvm_vm_free(vm);
return;
}
@@ -594,9 +661,7 @@ static void guest_test_hcalls_access(void)
pr_debug("Stage %d: testing hcall: 0x%lx\n", stage, hcall->control);
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "unexpected exit reason: %u (%s)",
- run->exit_reason, exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
diff --git a/tools/testing/selftests/kvm/x86_64/hyperv_ipi.c b/tools/testing/selftests/kvm/x86_64/hyperv_ipi.c
index 0cbb0e646ef8..6feb5ddb031d 100644
--- a/tools/testing/selftests/kvm/x86_64/hyperv_ipi.c
+++ b/tools/testing/selftests/kvm/x86_64/hyperv_ipi.c
@@ -243,7 +243,6 @@ int main(int argc, char *argv[])
{
struct kvm_vm *vm;
struct kvm_vcpu *vcpu[3];
- unsigned int exit_reason;
vm_vaddr_t hcall_page;
pthread_t threads[2];
int stage = 1, r;
@@ -283,10 +282,7 @@ int main(int argc, char *argv[])
while (true) {
vcpu_run(vcpu[0]);
- exit_reason = vcpu[0]->run->exit_reason;
- TEST_ASSERT(exit_reason == KVM_EXIT_IO,
- "unexpected exit reason: %u (%s)",
- exit_reason, exit_reason_str(exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu[0], KVM_EXIT_IO);
switch (get_ucall(vcpu[0], &uc)) {
case UCALL_SYNC:
diff --git a/tools/testing/selftests/kvm/x86_64/hyperv_svm_test.c b/tools/testing/selftests/kvm/x86_64/hyperv_svm_test.c
index 68a7d354ea07..e446d76d1c0c 100644
--- a/tools/testing/selftests/kvm/x86_64/hyperv_svm_test.c
+++ b/tools/testing/selftests/kvm/x86_64/hyperv_svm_test.c
@@ -156,7 +156,6 @@ int main(int argc, char *argv[])
vm_vaddr_t hcall_page;
struct kvm_vcpu *vcpu;
struct kvm_vm *vm;
- struct kvm_run *run;
struct ucall uc;
int stage;
@@ -165,7 +164,6 @@ int main(int argc, char *argv[])
/* Create VM */
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
vcpu_set_hv_cpuid(vcpu);
- run = vcpu->run;
vcpu_alloc_svm(vm, &nested_gva);
vcpu_alloc_hyperv_test_pages(vm, &hv_pages_gva);
@@ -177,10 +175,7 @@ int main(int argc, char *argv[])
for (stage = 1;; stage++) {
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Stage %d: unexpected exit reason: %u (%s),\n",
- stage, run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
diff --git a/tools/testing/selftests/kvm/x86_64/hyperv_tlb_flush.c b/tools/testing/selftests/kvm/x86_64/hyperv_tlb_flush.c
index 68f97ff720a7..4758b6ef5618 100644
--- a/tools/testing/selftests/kvm/x86_64/hyperv_tlb_flush.c
+++ b/tools/testing/selftests/kvm/x86_64/hyperv_tlb_flush.c
@@ -542,18 +542,13 @@ static void *vcpu_thread(void *arg)
struct ucall uc;
int old;
int r;
- unsigned int exit_reason;
r = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old);
TEST_ASSERT(!r, "pthread_setcanceltype failed on vcpu_id=%u with errno=%d",
vcpu->id, r);
vcpu_run(vcpu);
- exit_reason = vcpu->run->exit_reason;
-
- TEST_ASSERT(exit_reason == KVM_EXIT_IO,
- "vCPU %u exited with unexpected exit reason %u-%s, expected KVM_EXIT_IO",
- vcpu->id, exit_reason, exit_reason_str(exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
@@ -587,7 +582,6 @@ int main(int argc, char *argv[])
{
struct kvm_vm *vm;
struct kvm_vcpu *vcpu[3];
- unsigned int exit_reason;
pthread_t threads[2];
vm_vaddr_t test_data_page, gva;
vm_paddr_t gpa;
@@ -657,11 +651,7 @@ int main(int argc, char *argv[])
while (true) {
vcpu_run(vcpu[0]);
- exit_reason = vcpu[0]->run->exit_reason;
-
- TEST_ASSERT(exit_reason == KVM_EXIT_IO,
- "unexpected exit reason: %u (%s)",
- exit_reason, exit_reason_str(exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu[0], KVM_EXIT_IO);
switch (get_ucall(vcpu[0], &uc)) {
case UCALL_SYNC:
diff --git a/tools/testing/selftests/kvm/x86_64/kvm_clock_test.c b/tools/testing/selftests/kvm/x86_64/kvm_clock_test.c
index 813ce282cf56..1778704360a6 100644
--- a/tools/testing/selftests/kvm/x86_64/kvm_clock_test.c
+++ b/tools/testing/selftests/kvm/x86_64/kvm_clock_test.c
@@ -105,7 +105,6 @@ static void setup_clock(struct kvm_vm *vm, struct test_case *test_case)
static void enter_guest(struct kvm_vcpu *vcpu)
{
struct kvm_clock_data start, end;
- struct kvm_run *run = vcpu->run;
struct kvm_vm *vm = vcpu->vm;
struct ucall uc;
int i;
@@ -118,9 +117,7 @@ static void enter_guest(struct kvm_vcpu *vcpu)
vcpu_run(vcpu);
vm_ioctl(vm, KVM_GET_CLOCK, &end);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "unexpected exit reason: %u (%s)",
- run->exit_reason, exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_SYNC:
diff --git a/tools/testing/selftests/kvm/x86_64/kvm_pv_test.c b/tools/testing/selftests/kvm/x86_64/kvm_pv_test.c
index 619655c1a1f3..f774a9e62858 100644
--- a/tools/testing/selftests/kvm/x86_64/kvm_pv_test.c
+++ b/tools/testing/selftests/kvm/x86_64/kvm_pv_test.c
@@ -111,14 +111,11 @@ static void pr_hcall(struct ucall *uc)
static void enter_guest(struct kvm_vcpu *vcpu)
{
- struct kvm_run *run = vcpu->run;
struct ucall uc;
while (true) {
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "unexpected exit reason: %u (%s)",
- run->exit_reason, exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_PR_MSR:
diff --git a/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
index fb02581953a3..ce1ccc4c1503 100644
--- a/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
+++ b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c
@@ -93,7 +93,7 @@ int main(void)
{
int warnings_before, warnings_after;
- TEST_REQUIRE(is_intel_cpu());
+ TEST_REQUIRE(host_cpu_is_intel);
TEST_REQUIRE(!vm_is_unrestricted_guest(NULL));
diff --git a/tools/testing/selftests/kvm/x86_64/monitor_mwait_test.c b/tools/testing/selftests/kvm/x86_64/monitor_mwait_test.c
index 016070cad36e..72812644d7f5 100644
--- a/tools/testing/selftests/kvm/x86_64/monitor_mwait_test.c
+++ b/tools/testing/selftests/kvm/x86_64/monitor_mwait_test.c
@@ -64,7 +64,6 @@ int main(int argc, char *argv[])
{
uint64_t disabled_quirks;
struct kvm_vcpu *vcpu;
- struct kvm_run *run;
struct kvm_vm *vm;
struct ucall uc;
int testcase;
@@ -74,18 +73,12 @@ int main(int argc, char *argv[])
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
vcpu_clear_cpuid_feature(vcpu, X86_FEATURE_MWAIT);
- run = vcpu->run;
-
vm_init_descriptor_tables(vm);
vcpu_init_descriptor_tables(vcpu);
while (1) {
vcpu_run(vcpu);
-
- 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_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_SYNC:
diff --git a/tools/testing/selftests/kvm/x86_64/nested_exceptions_test.c b/tools/testing/selftests/kvm/x86_64/nested_exceptions_test.c
index ac33835f78f4..6502aa23c2f8 100644
--- a/tools/testing/selftests/kvm/x86_64/nested_exceptions_test.c
+++ b/tools/testing/selftests/kvm/x86_64/nested_exceptions_test.c
@@ -166,12 +166,9 @@ static void __attribute__((__flatten__)) l1_guest_code(void *test_data)
static void assert_ucall_vector(struct kvm_vcpu *vcpu, int vector)
{
- struct kvm_run *run = vcpu->run;
struct ucall uc;
- 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_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_SYNC:
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 310a104d94f0..c9a07963d68a 100644
--- a/tools/testing/selftests/kvm/x86_64/platform_info_test.c
+++ b/tools/testing/selftests/kvm/x86_64/platform_info_test.c
@@ -36,15 +36,12 @@ static void guest_code(void)
static void test_msr_platform_info_enabled(struct kvm_vcpu *vcpu)
{
- struct kvm_run *run = vcpu->run;
struct ucall uc;
vm_enable_cap(vcpu->vm, KVM_CAP_MSR_PLATFORM_INFO, true);
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Exit_reason other than KVM_EXIT_IO: %u (%s),\n",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+
get_ucall(vcpu, &uc);
TEST_ASSERT(uc.cmd == UCALL_SYNC,
"Received ucall other than UCALL_SYNC: %lu\n", uc.cmd);
@@ -56,14 +53,9 @@ static void test_msr_platform_info_enabled(struct kvm_vcpu *vcpu)
static void test_msr_platform_info_disabled(struct kvm_vcpu *vcpu)
{
- struct kvm_run *run = vcpu->run;
-
vm_enable_cap(vcpu->vm, KVM_CAP_MSR_PLATFORM_INFO, false);
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_SHUTDOWN,
- "Exit_reason other than KVM_EXIT_SHUTDOWN: %u (%s)\n",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_SHUTDOWN);
}
int main(int argc, char *argv[])
diff --git a/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c b/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c
index 2de98fce7edd..2feef25ba691 100644
--- a/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c
+++ b/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c
@@ -151,14 +151,10 @@ static void amd_guest_code(void)
*/
static uint64_t run_vcpu_to_sync(struct kvm_vcpu *vcpu)
{
- struct kvm_run *run = vcpu->run;
struct ucall uc;
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Exit_reason other than KVM_EXIT_IO: %u (%s)\n",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
get_ucall(vcpu, &uc);
TEST_ASSERT(uc.cmd == UCALL_SYNC,
"Received ucall other than UCALL_SYNC: %lu", uc.cmd);
@@ -198,14 +194,15 @@ static struct kvm_pmu_event_filter *alloc_pmu_event_filter(uint32_t nevents)
static struct kvm_pmu_event_filter *
-create_pmu_event_filter(const uint64_t event_list[],
- int nevents, uint32_t action)
+create_pmu_event_filter(const uint64_t event_list[], int nevents,
+ uint32_t action, uint32_t flags)
{
struct kvm_pmu_event_filter *f;
int i;
f = alloc_pmu_event_filter(nevents);
f->action = action;
+ f->flags = flags;
for (i = 0; i < nevents; i++)
f->events[i] = event_list[i];
@@ -216,7 +213,7 @@ static struct kvm_pmu_event_filter *event_filter(uint32_t action)
{
return create_pmu_event_filter(event_list,
ARRAY_SIZE(event_list),
- action);
+ action, 0);
}
/*
@@ -263,7 +260,7 @@ static void test_amd_deny_list(struct kvm_vcpu *vcpu)
struct kvm_pmu_event_filter *f;
uint64_t count;
- f = create_pmu_event_filter(&event, 1, KVM_PMU_EVENT_DENY);
+ f = create_pmu_event_filter(&event, 1, KVM_PMU_EVENT_DENY, 0);
count = test_with_filter(vcpu, f);
free(f);
@@ -363,7 +360,7 @@ static void test_pmu_config_disable(void (*guest_code)(void))
*/
static bool use_intel_pmu(void)
{
- return is_intel_cpu() &&
+ return host_cpu_is_intel &&
kvm_cpu_property(X86_PROPERTY_PMU_VERSION) &&
kvm_cpu_property(X86_PROPERTY_PMU_NR_GP_COUNTERS) &&
kvm_pmu_has(X86_PMU_FEATURE_BRANCH_INSNS_RETIRED);
@@ -397,19 +394,378 @@ static bool use_amd_pmu(void)
uint32_t family = kvm_cpu_family();
uint32_t model = kvm_cpu_model();
- return is_amd_cpu() &&
+ return host_cpu_is_amd &&
(is_zen1(family, model) ||
is_zen2(family, model) ||
is_zen3(family, model));
}
+/*
+ * "MEM_INST_RETIRED.ALL_LOADS", "MEM_INST_RETIRED.ALL_STORES", and
+ * "MEM_INST_RETIRED.ANY" from https://perfmon-events.intel.com/
+ * supported on Intel Xeon processors:
+ * - Sapphire Rapids, Ice Lake, Cascade Lake, Skylake.
+ */
+#define MEM_INST_RETIRED 0xD0
+#define MEM_INST_RETIRED_LOAD EVENT(MEM_INST_RETIRED, 0x81)
+#define MEM_INST_RETIRED_STORE EVENT(MEM_INST_RETIRED, 0x82)
+#define MEM_INST_RETIRED_LOAD_STORE EVENT(MEM_INST_RETIRED, 0x83)
+
+static bool supports_event_mem_inst_retired(void)
+{
+ uint32_t eax, ebx, ecx, edx;
+
+ cpuid(1, &eax, &ebx, &ecx, &edx);
+ if (x86_family(eax) == 0x6) {
+ switch (x86_model(eax)) {
+ /* Sapphire Rapids */
+ case 0x8F:
+ /* Ice Lake */
+ case 0x6A:
+ /* Skylake */
+ /* Cascade Lake */
+ case 0x55:
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * "LS Dispatch", from Processor Programming Reference
+ * (PPR) for AMD Family 17h Model 01h, Revision B1 Processors,
+ * Preliminary Processor Programming Reference (PPR) for AMD Family
+ * 17h Model 31h, Revision B0 Processors, and Preliminary Processor
+ * Programming Reference (PPR) for AMD Family 19h Model 01h, Revision
+ * B1 Processors Volume 1 of 2.
+ */
+#define LS_DISPATCH 0x29
+#define LS_DISPATCH_LOAD EVENT(LS_DISPATCH, BIT(0))
+#define LS_DISPATCH_STORE EVENT(LS_DISPATCH, BIT(1))
+#define LS_DISPATCH_LOAD_STORE EVENT(LS_DISPATCH, BIT(2))
+
+#define INCLUDE_MASKED_ENTRY(event_select, mask, match) \
+ KVM_PMU_ENCODE_MASKED_ENTRY(event_select, mask, match, false)
+#define EXCLUDE_MASKED_ENTRY(event_select, mask, match) \
+ KVM_PMU_ENCODE_MASKED_ENTRY(event_select, mask, match, true)
+
+struct perf_counter {
+ union {
+ uint64_t raw;
+ struct {
+ uint64_t loads:22;
+ uint64_t stores:22;
+ uint64_t loads_stores:20;
+ };
+ };
+};
+
+static uint64_t masked_events_guest_test(uint32_t msr_base)
+{
+ uint64_t ld0, ld1, st0, st1, ls0, ls1;
+ struct perf_counter c;
+ int val;
+
+ /*
+ * The acutal value of the counters don't determine the outcome of
+ * the test. Only that they are zero or non-zero.
+ */
+ ld0 = rdmsr(msr_base + 0);
+ st0 = rdmsr(msr_base + 1);
+ ls0 = rdmsr(msr_base + 2);
+
+ __asm__ __volatile__("movl $0, %[v];"
+ "movl %[v], %%eax;"
+ "incl %[v];"
+ : [v]"+m"(val) :: "eax");
+
+ ld1 = rdmsr(msr_base + 0);
+ st1 = rdmsr(msr_base + 1);
+ ls1 = rdmsr(msr_base + 2);
+
+ c.loads = ld1 - ld0;
+ c.stores = st1 - st0;
+ c.loads_stores = ls1 - ls0;
+
+ return c.raw;
+}
+
+static void intel_masked_events_guest_code(void)
+{
+ uint64_t r;
+
+ for (;;) {
+ wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0);
+
+ wrmsr(MSR_P6_EVNTSEL0 + 0, ARCH_PERFMON_EVENTSEL_ENABLE |
+ ARCH_PERFMON_EVENTSEL_OS | MEM_INST_RETIRED_LOAD);
+ wrmsr(MSR_P6_EVNTSEL0 + 1, ARCH_PERFMON_EVENTSEL_ENABLE |
+ ARCH_PERFMON_EVENTSEL_OS | MEM_INST_RETIRED_STORE);
+ wrmsr(MSR_P6_EVNTSEL0 + 2, ARCH_PERFMON_EVENTSEL_ENABLE |
+ ARCH_PERFMON_EVENTSEL_OS | MEM_INST_RETIRED_LOAD_STORE);
+
+ wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0x7);
+
+ r = masked_events_guest_test(MSR_IA32_PMC0);
+
+ GUEST_SYNC(r);
+ }
+}
+
+static void amd_masked_events_guest_code(void)
+{
+ uint64_t r;
+
+ for (;;) {
+ wrmsr(MSR_K7_EVNTSEL0, 0);
+ wrmsr(MSR_K7_EVNTSEL1, 0);
+ wrmsr(MSR_K7_EVNTSEL2, 0);
+
+ wrmsr(MSR_K7_EVNTSEL0, ARCH_PERFMON_EVENTSEL_ENABLE |
+ ARCH_PERFMON_EVENTSEL_OS | LS_DISPATCH_LOAD);
+ wrmsr(MSR_K7_EVNTSEL1, ARCH_PERFMON_EVENTSEL_ENABLE |
+ ARCH_PERFMON_EVENTSEL_OS | LS_DISPATCH_STORE);
+ wrmsr(MSR_K7_EVNTSEL2, ARCH_PERFMON_EVENTSEL_ENABLE |
+ ARCH_PERFMON_EVENTSEL_OS | LS_DISPATCH_LOAD_STORE);
+
+ r = masked_events_guest_test(MSR_K7_PERFCTR0);
+
+ GUEST_SYNC(r);
+ }
+}
+
+static struct perf_counter run_masked_events_test(struct kvm_vcpu *vcpu,
+ const uint64_t masked_events[],
+ const int nmasked_events)
+{
+ struct kvm_pmu_event_filter *f;
+ struct perf_counter r;
+
+ f = create_pmu_event_filter(masked_events, nmasked_events,
+ KVM_PMU_EVENT_ALLOW,
+ KVM_PMU_EVENT_FLAG_MASKED_EVENTS);
+ r.raw = test_with_filter(vcpu, f);
+ free(f);
+
+ return r;
+}
+
+/* Matches KVM_PMU_EVENT_FILTER_MAX_EVENTS in pmu.c */
+#define MAX_FILTER_EVENTS 300
+#define MAX_TEST_EVENTS 10
+
+#define ALLOW_LOADS BIT(0)
+#define ALLOW_STORES BIT(1)
+#define ALLOW_LOADS_STORES BIT(2)
+
+struct masked_events_test {
+ uint64_t intel_events[MAX_TEST_EVENTS];
+ uint64_t intel_event_end;
+ uint64_t amd_events[MAX_TEST_EVENTS];
+ uint64_t amd_event_end;
+ const char *msg;
+ uint32_t flags;
+};
+
+/*
+ * These are the test cases for the masked events tests.
+ *
+ * For each test, the guest enables 3 PMU counters (loads, stores,
+ * loads + stores). The filter is then set in KVM with the masked events
+ * provided. The test then verifies that the counters agree with which
+ * ones should be counting and which ones should be filtered.
+ */
+const struct masked_events_test test_cases[] = {
+ {
+ .intel_events = {
+ INCLUDE_MASKED_ENTRY(MEM_INST_RETIRED, 0xFF, 0x81),
+ },
+ .amd_events = {
+ INCLUDE_MASKED_ENTRY(LS_DISPATCH, 0xFF, BIT(0)),
+ },
+ .msg = "Only allow loads.",
+ .flags = ALLOW_LOADS,
+ }, {
+ .intel_events = {
+ INCLUDE_MASKED_ENTRY(MEM_INST_RETIRED, 0xFF, 0x82),
+ },
+ .amd_events = {
+ INCLUDE_MASKED_ENTRY(LS_DISPATCH, 0xFF, BIT(1)),
+ },
+ .msg = "Only allow stores.",
+ .flags = ALLOW_STORES,
+ }, {
+ .intel_events = {
+ INCLUDE_MASKED_ENTRY(MEM_INST_RETIRED, 0xFF, 0x83),
+ },
+ .amd_events = {
+ INCLUDE_MASKED_ENTRY(LS_DISPATCH, 0xFF, BIT(2)),
+ },
+ .msg = "Only allow loads + stores.",
+ .flags = ALLOW_LOADS_STORES,
+ }, {
+ .intel_events = {
+ INCLUDE_MASKED_ENTRY(MEM_INST_RETIRED, 0x7C, 0),
+ EXCLUDE_MASKED_ENTRY(MEM_INST_RETIRED, 0xFF, 0x83),
+ },
+ .amd_events = {
+ INCLUDE_MASKED_ENTRY(LS_DISPATCH, ~(BIT(0) | BIT(1)), 0),
+ },
+ .msg = "Only allow loads and stores.",
+ .flags = ALLOW_LOADS | ALLOW_STORES,
+ }, {
+ .intel_events = {
+ INCLUDE_MASKED_ENTRY(MEM_INST_RETIRED, 0x7C, 0),
+ EXCLUDE_MASKED_ENTRY(MEM_INST_RETIRED, 0xFF, 0x82),
+ },
+ .amd_events = {
+ INCLUDE_MASKED_ENTRY(LS_DISPATCH, 0xF8, 0),
+ EXCLUDE_MASKED_ENTRY(LS_DISPATCH, 0xFF, BIT(1)),
+ },
+ .msg = "Only allow loads and loads + stores.",
+ .flags = ALLOW_LOADS | ALLOW_LOADS_STORES
+ }, {
+ .intel_events = {
+ INCLUDE_MASKED_ENTRY(MEM_INST_RETIRED, 0xFE, 0x82),
+ },
+ .amd_events = {
+ INCLUDE_MASKED_ENTRY(LS_DISPATCH, 0xF8, 0),
+ EXCLUDE_MASKED_ENTRY(LS_DISPATCH, 0xFF, BIT(0)),
+ },
+ .msg = "Only allow stores and loads + stores.",
+ .flags = ALLOW_STORES | ALLOW_LOADS_STORES
+ }, {
+ .intel_events = {
+ INCLUDE_MASKED_ENTRY(MEM_INST_RETIRED, 0x7C, 0),
+ },
+ .amd_events = {
+ INCLUDE_MASKED_ENTRY(LS_DISPATCH, 0xF8, 0),
+ },
+ .msg = "Only allow loads, stores, and loads + stores.",
+ .flags = ALLOW_LOADS | ALLOW_STORES | ALLOW_LOADS_STORES
+ },
+};
+
+static int append_test_events(const struct masked_events_test *test,
+ uint64_t *events, int nevents)
+{
+ const uint64_t *evts;
+ int i;
+
+ evts = use_intel_pmu() ? test->intel_events : test->amd_events;
+ for (i = 0; i < MAX_TEST_EVENTS; i++) {
+ if (evts[i] == 0)
+ break;
+
+ events[nevents + i] = evts[i];
+ }
+
+ return nevents + i;
+}
+
+static bool bool_eq(bool a, bool b)
+{
+ return a == b;
+}
+
+static void run_masked_events_tests(struct kvm_vcpu *vcpu, uint64_t *events,
+ int nevents)
+{
+ int ntests = ARRAY_SIZE(test_cases);
+ struct perf_counter c;
+ int i, n;
+
+ for (i = 0; i < ntests; i++) {
+ const struct masked_events_test *test = &test_cases[i];
+
+ /* Do any test case events overflow MAX_TEST_EVENTS? */
+ assert(test->intel_event_end == 0);
+ assert(test->amd_event_end == 0);
+
+ n = append_test_events(test, events, nevents);
+
+ c = run_masked_events_test(vcpu, events, n);
+ TEST_ASSERT(bool_eq(c.loads, test->flags & ALLOW_LOADS) &&
+ bool_eq(c.stores, test->flags & ALLOW_STORES) &&
+ bool_eq(c.loads_stores,
+ test->flags & ALLOW_LOADS_STORES),
+ "%s loads: %u, stores: %u, loads + stores: %u",
+ test->msg, c.loads, c.stores, c.loads_stores);
+ }
+}
+
+static void add_dummy_events(uint64_t *events, int nevents)
+{
+ int i;
+
+ for (i = 0; i < nevents; i++) {
+ int event_select = i % 0xFF;
+ bool exclude = ((i % 4) == 0);
+
+ if (event_select == MEM_INST_RETIRED ||
+ event_select == LS_DISPATCH)
+ event_select++;
+
+ events[i] = KVM_PMU_ENCODE_MASKED_ENTRY(event_select, 0,
+ 0, exclude);
+ }
+}
+
+static void test_masked_events(struct kvm_vcpu *vcpu)
+{
+ int nevents = MAX_FILTER_EVENTS - MAX_TEST_EVENTS;
+ uint64_t events[MAX_FILTER_EVENTS];
+
+ /* Run the test cases against a sparse PMU event filter. */
+ run_masked_events_tests(vcpu, events, 0);
+
+ /* Run the test cases against a dense PMU event filter. */
+ add_dummy_events(events, MAX_FILTER_EVENTS);
+ run_masked_events_tests(vcpu, events, nevents);
+}
+
+static int run_filter_test(struct kvm_vcpu *vcpu, const uint64_t *events,
+ int nevents, uint32_t flags)
+{
+ struct kvm_pmu_event_filter *f;
+ int r;
+
+ f = create_pmu_event_filter(events, nevents, KVM_PMU_EVENT_ALLOW, flags);
+ r = __vm_ioctl(vcpu->vm, KVM_SET_PMU_EVENT_FILTER, f);
+ free(f);
+
+ return r;
+}
+
+static void test_filter_ioctl(struct kvm_vcpu *vcpu)
+{
+ uint64_t e = ~0ul;
+ int r;
+
+ /*
+ * Unfortunately having invalid bits set in event data is expected to
+ * pass when flags == 0 (bits other than eventsel+umask).
+ */
+ r = run_filter_test(vcpu, &e, 1, 0);
+ TEST_ASSERT(r == 0, "Valid PMU Event Filter is failing");
+
+ r = run_filter_test(vcpu, &e, 1, KVM_PMU_EVENT_FLAG_MASKED_EVENTS);
+ TEST_ASSERT(r != 0, "Invalid PMU Event Filter is expected to fail");
+
+ e = KVM_PMU_ENCODE_MASKED_ENTRY(0xff, 0xff, 0xff, 0xf);
+ r = run_filter_test(vcpu, &e, 1, KVM_PMU_EVENT_FLAG_MASKED_EVENTS);
+ TEST_ASSERT(r == 0, "Valid PMU Event Filter is failing");
+}
+
int main(int argc, char *argv[])
{
void (*guest_code)(void);
- struct kvm_vcpu *vcpu;
+ struct kvm_vcpu *vcpu, *vcpu2 = NULL;
struct kvm_vm *vm;
TEST_REQUIRE(kvm_has_cap(KVM_CAP_PMU_EVENT_FILTER));
+ TEST_REQUIRE(kvm_has_cap(KVM_CAP_PMU_EVENT_MASKED_EVENTS));
TEST_REQUIRE(use_intel_pmu() || use_amd_pmu());
guest_code = use_intel_pmu() ? intel_guest_code : amd_guest_code;
@@ -430,6 +786,17 @@ int main(int argc, char *argv[])
test_not_member_deny_list(vcpu);
test_not_member_allow_list(vcpu);
+ if (use_intel_pmu() &&
+ supports_event_mem_inst_retired() &&
+ kvm_cpu_property(X86_PROPERTY_PMU_NR_GP_COUNTERS) >= 3)
+ vcpu2 = vm_vcpu_add(vm, 2, intel_masked_events_guest_code);
+ else if (use_amd_pmu())
+ vcpu2 = vm_vcpu_add(vm, 2, amd_masked_events_guest_code);
+
+ if (vcpu2)
+ test_masked_events(vcpu2);
+ test_filter_ioctl(vcpu);
+
kvm_vm_free(vm);
test_pmu_config_disable(guest_code);
diff --git a/tools/testing/selftests/kvm/x86_64/smm_test.c b/tools/testing/selftests/kvm/x86_64/smm_test.c
index cb38a478e1f6..e18b86666e1f 100644
--- a/tools/testing/selftests/kvm/x86_64/smm_test.c
+++ b/tools/testing/selftests/kvm/x86_64/smm_test.c
@@ -133,7 +133,6 @@ int main(int argc, char *argv[])
struct kvm_vcpu *vcpu;
struct kvm_regs regs;
struct kvm_vm *vm;
- struct kvm_run *run;
struct kvm_x86_state *state;
int stage, stage_reported;
@@ -142,8 +141,6 @@ int main(int argc, char *argv[])
/* Create VM */
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
- run = vcpu->run;
-
vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, SMRAM_GPA,
SMRAM_MEMSLOT, SMRAM_PAGES, 0);
TEST_ASSERT(vm_phy_pages_alloc(vm, SMRAM_PAGES, SMRAM_GPA, SMRAM_MEMSLOT)
@@ -169,10 +166,7 @@ int main(int argc, char *argv[])
for (stage = 1;; stage++) {
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Stage %d: unexpected exit reason: %u (%s),\n",
- stage, run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
memset(&regs, 0, sizeof(regs));
vcpu_regs_get(vcpu, &regs);
@@ -208,7 +202,6 @@ int main(int argc, char *argv[])
vcpu = vm_recreate_with_one_vcpu(vm);
vcpu_load_state(vcpu, state);
- run = vcpu->run;
kvm_x86_state_cleanup(state);
}
diff --git a/tools/testing/selftests/kvm/x86_64/state_test.c b/tools/testing/selftests/kvm/x86_64/state_test.c
index ea578971fb9f..4c4925a8ab45 100644
--- a/tools/testing/selftests/kvm/x86_64/state_test.c
+++ b/tools/testing/selftests/kvm/x86_64/state_test.c
@@ -158,14 +158,12 @@ int main(int argc, char *argv[])
struct kvm_regs regs1, regs2;
struct kvm_vcpu *vcpu;
struct kvm_vm *vm;
- struct kvm_run *run;
struct kvm_x86_state *state;
struct ucall uc;
int stage;
/* Create VM */
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
- run = vcpu->run;
vcpu_regs_get(vcpu, &regs1);
@@ -183,10 +181,7 @@ int main(int argc, char *argv[])
for (stage = 1;; stage++) {
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Stage %d: unexpected exit reason: %u (%s),\n",
- stage, run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
@@ -214,7 +209,6 @@ int main(int argc, char *argv[])
/* Restore state in a new VM. */
vcpu = vm_recreate_with_one_vcpu(vm);
vcpu_load_state(vcpu, state);
- run = vcpu->run;
kvm_x86_state_cleanup(state);
memset(&regs2, 0, sizeof(regs2));
diff --git a/tools/testing/selftests/kvm/x86_64/svm_int_ctl_test.c b/tools/testing/selftests/kvm/x86_64/svm_int_ctl_test.c
index 4a07ba227b99..32bef39bec21 100644
--- a/tools/testing/selftests/kvm/x86_64/svm_int_ctl_test.c
+++ b/tools/testing/selftests/kvm/x86_64/svm_int_ctl_test.c
@@ -85,7 +85,6 @@ static void l1_guest_code(struct svm_test_data *svm)
int main(int argc, char *argv[])
{
struct kvm_vcpu *vcpu;
- struct kvm_run *run;
vm_vaddr_t svm_gva;
struct kvm_vm *vm;
struct ucall uc;
@@ -103,13 +102,8 @@ int main(int argc, char *argv[])
vcpu_alloc_svm(vm, &svm_gva);
vcpu_args_set(vcpu, 1, svm_gva);
- run = vcpu->run;
-
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
diff --git a/tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c b/tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c
index e73fcdef47bb..d6fcdcc3af31 100644
--- a/tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c
+++ b/tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c
@@ -42,7 +42,6 @@ static void l1_guest_code(struct svm_test_data *svm, struct idt_entry *idt)
int main(int argc, char *argv[])
{
struct kvm_vcpu *vcpu;
- struct kvm_run *run;
vm_vaddr_t svm_gva;
struct kvm_vm *vm;
@@ -55,13 +54,9 @@ int main(int argc, char *argv[])
vcpu_alloc_svm(vm, &svm_gva);
vcpu_args_set(vcpu, 2, svm_gva, vm->idt);
- run = vcpu->run;
vcpu_run(vcpu);
- 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));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_SHUTDOWN);
kvm_vm_free(vm);
}
diff --git a/tools/testing/selftests/kvm/x86_64/svm_nested_soft_inject_test.c b/tools/testing/selftests/kvm/x86_64/svm_nested_soft_inject_test.c
index b34980d45648..4e2479716da6 100644
--- a/tools/testing/selftests/kvm/x86_64/svm_nested_soft_inject_test.c
+++ b/tools/testing/selftests/kvm/x86_64/svm_nested_soft_inject_test.c
@@ -176,16 +176,12 @@ static void run_test(bool is_nmi)
memset(&debug, 0, sizeof(debug));
vcpu_guest_debug_set(vcpu, &debug);
- struct kvm_run *run = vcpu->run;
struct ucall uc;
alarm(2);
vcpu_run(vcpu);
alarm(0);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
diff --git a/tools/testing/selftests/kvm/x86_64/svm_vmcall_test.c b/tools/testing/selftests/kvm/x86_64/svm_vmcall_test.c
index c3ac45df7483..8a62cca28cfb 100644
--- a/tools/testing/selftests/kvm/x86_64/svm_vmcall_test.c
+++ b/tools/testing/selftests/kvm/x86_64/svm_vmcall_test.c
@@ -47,14 +47,10 @@ int main(int argc, char *argv[])
vcpu_args_set(vcpu, 1, svm_gva);
for (;;) {
- volatile struct kvm_run *run = vcpu->run;
struct ucall uc;
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
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 d2f9b5bdfab2..2da89fdc2471 100644
--- a/tools/testing/selftests/kvm/x86_64/sync_regs_test.c
+++ b/tools/testing/selftests/kvm/x86_64/sync_regs_test.c
@@ -132,10 +132,7 @@ int main(int argc, char *argv[])
/* TODO: BUILD TIME CHECK: TEST_ASSERT(KVM_SYNC_X86_NUM_FIELDS != 3); */
run->kvm_valid_regs = TEST_SYNC_FIELDS;
rv = _vcpu_run(vcpu);
- 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_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
vcpu_regs_get(vcpu, &regs);
compare_regs(&regs, &run->s.regs.regs);
@@ -154,10 +151,7 @@ int main(int argc, char *argv[])
run->kvm_valid_regs = TEST_SYNC_FIELDS;
run->kvm_dirty_regs = KVM_SYNC_X86_REGS | KVM_SYNC_X86_SREGS;
rv = _vcpu_run(vcpu);
- 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_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
TEST_ASSERT(run->s.regs.regs.rbx == 0xBAD1DEA + 1,
"rbx sync regs value incorrect 0x%llx.",
run->s.regs.regs.rbx);
@@ -181,10 +175,7 @@ int main(int argc, char *argv[])
run->kvm_dirty_regs = 0;
run->s.regs.regs.rbx = 0xDEADBEEF;
rv = _vcpu_run(vcpu);
- 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_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
TEST_ASSERT(run->s.regs.regs.rbx != 0xDEADBEEF,
"rbx sync regs value incorrect 0x%llx.",
run->s.regs.regs.rbx);
@@ -199,10 +190,7 @@ int main(int argc, char *argv[])
regs.rbx = 0xBAC0;
vcpu_regs_set(vcpu, &regs);
rv = _vcpu_run(vcpu);
- 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_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
TEST_ASSERT(run->s.regs.regs.rbx == 0xAAAA,
"rbx sync regs value incorrect 0x%llx.",
run->s.regs.regs.rbx);
@@ -219,10 +207,7 @@ int main(int argc, char *argv[])
run->kvm_dirty_regs = TEST_SYNC_FIELDS;
run->s.regs.regs.rbx = 0xBBBB;
rv = _vcpu_run(vcpu);
- 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_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
TEST_ASSERT(run->s.regs.regs.rbx == 0xBBBB,
"rbx sync regs value incorrect 0x%llx.",
run->s.regs.regs.rbx);
diff --git a/tools/testing/selftests/kvm/x86_64/triple_fault_event_test.c b/tools/testing/selftests/kvm/x86_64/triple_fault_event_test.c
index ead5d878a71c..56306a19144a 100644
--- a/tools/testing/selftests/kvm/x86_64/triple_fault_event_test.c
+++ b/tools/testing/selftests/kvm/x86_64/triple_fault_event_test.c
@@ -89,9 +89,7 @@ int main(void)
run = vcpu->run;
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Expected KVM_EXIT_IO, got: %u (%s)\n",
- run->exit_reason, exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
TEST_ASSERT(run->io.port == ARBITRARY_IO_PORT,
"Expected IN from port %d from L2, got port %d",
ARBITRARY_IO_PORT, run->io.port);
@@ -111,10 +109,7 @@ int main(void)
if (has_svm) {
- 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));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_SHUTDOWN);
} else {
switch (get_ucall(vcpu, &uc)) {
case UCALL_DONE:
diff --git a/tools/testing/selftests/kvm/x86_64/tsc_msrs_test.c b/tools/testing/selftests/kvm/x86_64/tsc_msrs_test.c
index 22d366c697f7..c9f67702f657 100644
--- a/tools/testing/selftests/kvm/x86_64/tsc_msrs_test.c
+++ b/tools/testing/selftests/kvm/x86_64/tsc_msrs_test.c
@@ -72,11 +72,16 @@ static void run_vcpu(struct kvm_vcpu *vcpu, int stage)
switch (get_ucall(vcpu, &uc)) {
case UCALL_SYNC:
- TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") &&
- uc.args[1] == stage + 1, "Stage %d: Unexpected register values vmexit, got %lx",
- stage + 1, (ulong)uc.args[1]);
+ if (!strcmp((const char *)uc.args[0], "hello") &&
+ uc.args[1] == stage + 1)
+ ksft_test_result_pass("stage %d passed\n", stage + 1);
+ else
+ ksft_test_result_fail(
+ "stage %d: Unexpected register values vmexit, got %lx",
+ stage + 1, (ulong)uc.args[1]);
return;
case UCALL_DONE:
+ ksft_test_result_pass("stage %d passed\n", stage + 1);
return;
case UCALL_ABORT:
REPORT_GUEST_ASSERT_2(uc, "values: %#lx, %#lx");
@@ -92,6 +97,9 @@ int main(void)
struct kvm_vm *vm;
uint64_t val;
+ ksft_print_header();
+ ksft_set_plan(5);
+
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
val = 0;
@@ -149,5 +157,5 @@ int main(void)
kvm_vm_free(vm);
- return 0;
+ ksft_finished(); /* Print results and exit() accordingly */
}
diff --git a/tools/testing/selftests/kvm/x86_64/tsc_scaling_sync.c b/tools/testing/selftests/kvm/x86_64/tsc_scaling_sync.c
index 47139aab7408..5b669818e39a 100644
--- a/tools/testing/selftests/kvm/x86_64/tsc_scaling_sync.c
+++ b/tools/testing/selftests/kvm/x86_64/tsc_scaling_sync.c
@@ -64,14 +64,10 @@ static void *run_vcpu(void *_cpu_nr)
pthread_spin_unlock(&create_lock);
for (;;) {
- volatile struct kvm_run *run = vcpu->run;
struct ucall uc;
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_DONE:
diff --git a/tools/testing/selftests/kvm/x86_64/ucna_injection_test.c b/tools/testing/selftests/kvm/x86_64/ucna_injection_test.c
index a897c7fd8abe..85f34ca7e49e 100644
--- a/tools/testing/selftests/kvm/x86_64/ucna_injection_test.c
+++ b/tools/testing/selftests/kvm/x86_64/ucna_injection_test.c
@@ -137,15 +137,11 @@ static void guest_gp_handler(struct ex_regs *regs)
static void run_vcpu_expect_gp(struct kvm_vcpu *vcpu)
{
- unsigned int exit_reason;
struct ucall uc;
vcpu_run(vcpu);
- exit_reason = vcpu->run->exit_reason;
- TEST_ASSERT(exit_reason == KVM_EXIT_IO,
- "exited with unexpected exit reason %u-%s, expected KVM_EXIT_IO",
- exit_reason, exit_reason_str(exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
TEST_ASSERT(get_ucall(vcpu, &uc) == UCALL_SYNC,
"Expect UCALL_SYNC\n");
TEST_ASSERT(uc.args[1] == SYNC_GP, "#GP is expected.");
@@ -182,7 +178,6 @@ static void *run_ucna_injection(void *arg)
struct ucall uc;
int old;
int r;
- unsigned int exit_reason;
r = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old);
TEST_ASSERT(r == 0,
@@ -191,10 +186,7 @@ static void *run_ucna_injection(void *arg)
vcpu_run(params->vcpu);
- exit_reason = params->vcpu->run->exit_reason;
- TEST_ASSERT(exit_reason == KVM_EXIT_IO,
- "unexpected exit reason %u-%s, expected KVM_EXIT_IO",
- exit_reason, exit_reason_str(exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(params->vcpu, KVM_EXIT_IO);
TEST_ASSERT(get_ucall(params->vcpu, &uc) == UCALL_SYNC,
"Expect UCALL_SYNC\n");
TEST_ASSERT(uc.args[1] == SYNC_FIRST_UCNA, "Injecting first UCNA.");
@@ -204,10 +196,7 @@ static void *run_ucna_injection(void *arg)
inject_ucna(params->vcpu, FIRST_UCNA_ADDR);
vcpu_run(params->vcpu);
- exit_reason = params->vcpu->run->exit_reason;
- TEST_ASSERT(exit_reason == KVM_EXIT_IO,
- "unexpected exit reason %u-%s, expected KVM_EXIT_IO",
- exit_reason, exit_reason_str(exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(params->vcpu, KVM_EXIT_IO);
TEST_ASSERT(get_ucall(params->vcpu, &uc) == UCALL_SYNC,
"Expect UCALL_SYNC\n");
TEST_ASSERT(uc.args[1] == SYNC_SECOND_UCNA, "Injecting second UCNA.");
@@ -217,10 +206,7 @@ static void *run_ucna_injection(void *arg)
inject_ucna(params->vcpu, SECOND_UCNA_ADDR);
vcpu_run(params->vcpu);
- exit_reason = params->vcpu->run->exit_reason;
- TEST_ASSERT(exit_reason == KVM_EXIT_IO,
- "unexpected exit reason %u-%s, expected KVM_EXIT_IO",
- exit_reason, exit_reason_str(exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(params->vcpu, KVM_EXIT_IO);
if (get_ucall(params->vcpu, &uc) == UCALL_ABORT) {
TEST_ASSERT(false, "vCPU assertion failure: %s.\n",
(const char *)uc.args[0]);
diff --git a/tools/testing/selftests/kvm/x86_64/userspace_io_test.c b/tools/testing/selftests/kvm/x86_64/userspace_io_test.c
index 91076c9787b4..0cb51fa42773 100644
--- a/tools/testing/selftests/kvm/x86_64/userspace_io_test.c
+++ b/tools/testing/selftests/kvm/x86_64/userspace_io_test.c
@@ -63,11 +63,7 @@ int main(int argc, char *argv[])
while (1) {
vcpu_run(vcpu);
-
- 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_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
if (get_ucall(vcpu, &uc))
break;
diff --git a/tools/testing/selftests/kvm/x86_64/userspace_msr_exit_test.c b/tools/testing/selftests/kvm/x86_64/userspace_msr_exit_test.c
index 25fa55344a10..3533dc2fbfee 100644
--- a/tools/testing/selftests/kvm/x86_64/userspace_msr_exit_test.c
+++ b/tools/testing/selftests/kvm/x86_64/userspace_msr_exit_test.c
@@ -410,10 +410,7 @@ static void process_rdmsr(struct kvm_vcpu *vcpu, uint32_t msr_index)
check_for_guest_assert(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_X86_RDMSR,
- "Unexpected exit reason: %u (%s),\n",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_X86_RDMSR);
TEST_ASSERT(run->msr.index == msr_index,
"Unexpected msr (0x%04x), expected 0x%04x",
run->msr.index, msr_index);
@@ -445,10 +442,7 @@ static void process_wrmsr(struct kvm_vcpu *vcpu, uint32_t msr_index)
check_for_guest_assert(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_X86_WRMSR,
- "Unexpected exit reason: %u (%s),\n",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_X86_WRMSR);
TEST_ASSERT(run->msr.index == msr_index,
"Unexpected msr (0x%04x), expected 0x%04x",
run->msr.index, msr_index);
@@ -472,15 +466,11 @@ static void process_wrmsr(struct kvm_vcpu *vcpu, uint32_t msr_index)
static void process_ucall_done(struct kvm_vcpu *vcpu)
{
- struct kvm_run *run = vcpu->run;
struct ucall uc;
check_for_guest_assert(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Unexpected exit reason: %u (%s)",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
TEST_ASSERT(get_ucall(vcpu, &uc) == UCALL_DONE,
"Unexpected ucall command: %lu, expected UCALL_DONE (%d)",
@@ -489,15 +479,11 @@ static void process_ucall_done(struct kvm_vcpu *vcpu)
static uint64_t process_ucall(struct kvm_vcpu *vcpu)
{
- struct kvm_run *run = vcpu->run;
struct ucall uc = {};
check_for_guest_assert(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Unexpected exit reason: %u (%s)",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_SYNC:
diff --git a/tools/testing/selftests/kvm/x86_64/vmx_apic_access_test.c b/tools/testing/selftests/kvm/x86_64/vmx_apic_access_test.c
index 5abecf06329e..2bed5fb3a0d6 100644
--- a/tools/testing/selftests/kvm/x86_64/vmx_apic_access_test.c
+++ b/tools/testing/selftests/kvm/x86_64/vmx_apic_access_test.c
@@ -96,21 +96,14 @@ int main(int argc, char *argv[])
vcpu_run(vcpu);
if (apic_access_addr == high_gpa) {
- TEST_ASSERT(run->exit_reason ==
- KVM_EXIT_INTERNAL_ERROR,
- "Got exit reason other than KVM_EXIT_INTERNAL_ERROR: %u (%s)\n",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_INTERNAL_ERROR);
TEST_ASSERT(run->internal.suberror ==
KVM_INTERNAL_ERROR_EMULATION,
"Got internal suberror other than KVM_INTERNAL_ERROR_EMULATION: %u\n",
run->internal.suberror);
break;
}
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
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 d79651b02740..dad988351493 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
@@ -64,10 +64,7 @@ int main(int argc, char *argv[])
struct ucall uc;
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
if (run->io.port == PORT_L0_EXIT)
break;
diff --git a/tools/testing/selftests/kvm/x86_64/vmx_dirty_log_test.c b/tools/testing/selftests/kvm/x86_64/vmx_dirty_log_test.c
index f0456fb031b1..e4ad5fef52ff 100644
--- a/tools/testing/selftests/kvm/x86_64/vmx_dirty_log_test.c
+++ b/tools/testing/selftests/kvm/x86_64/vmx_dirty_log_test.c
@@ -73,7 +73,6 @@ int main(int argc, char *argv[])
struct kvm_vcpu *vcpu;
struct kvm_vm *vm;
- struct kvm_run *run;
struct ucall uc;
bool done = false;
@@ -84,7 +83,6 @@ int main(int argc, char *argv[])
vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code);
vmx = vcpu_alloc_vmx(vm, &vmx_pages_gva);
vcpu_args_set(vcpu, 1, vmx_pages_gva);
- run = vcpu->run;
/* Add an extra memory slot for testing dirty logging */
vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
@@ -117,10 +115,7 @@ int main(int argc, char *argv[])
while (!done) {
memset(host_test_mem, 0xaa, TEST_MEM_PAGES * 4096);
vcpu_run(vcpu);
- 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_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
diff --git a/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c b/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c
index 2641b286b4ed..be0bdb8c6f78 100644
--- a/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c
+++ b/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c
@@ -26,9 +26,7 @@ static void __run_vcpu_with_invalid_state(struct kvm_vcpu *vcpu)
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_INTERNAL_ERROR,
- "Expected KVM_EXIT_INTERNAL_ERROR, got %d (%s)\n",
- run->exit_reason, exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_INTERNAL_ERROR);
TEST_ASSERT(run->emulation_failure.suberror == KVM_INTERNAL_ERROR_EMULATION,
"Expected emulation failure, got %d\n",
run->emulation_failure.suberror);
@@ -111,7 +109,7 @@ int main(int argc, char *argv[])
struct kvm_vcpu *vcpu;
struct kvm_vm *vm;
- TEST_REQUIRE(is_intel_cpu());
+ TEST_REQUIRE(host_cpu_is_intel);
TEST_REQUIRE(!vm_is_unrestricted_guest(NULL));
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
diff --git a/tools/testing/selftests/kvm/x86_64/vmx_invalid_nested_guest_state.c b/tools/testing/selftests/kvm/x86_64/vmx_invalid_nested_guest_state.c
index 6bfb4bb471ca..a100ee5f0009 100644
--- a/tools/testing/selftests/kvm/x86_64/vmx_invalid_nested_guest_state.c
+++ b/tools/testing/selftests/kvm/x86_64/vmx_invalid_nested_guest_state.c
@@ -74,9 +74,7 @@ int main(int argc, char *argv[])
* The first exit to L0 userspace should be an I/O access from L2.
* Running L1 should launch L2 without triggering an exit to userspace.
*/
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Expected KVM_EXIT_IO, got: %u (%s)\n",
- run->exit_reason, exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
TEST_ASSERT(run->io.port == ARBITRARY_IO_PORT,
"Expected IN from port %d from L2, got port %d",
diff --git a/tools/testing/selftests/kvm/x86_64/vmx_nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86_64/vmx_nested_tsc_scaling_test.c
index 465a9434d61c..d427eb146bc5 100644
--- a/tools/testing/selftests/kvm/x86_64/vmx_nested_tsc_scaling_test.c
+++ b/tools/testing/selftests/kvm/x86_64/vmx_nested_tsc_scaling_test.c
@@ -183,14 +183,10 @@ int main(int argc, char *argv[])
vcpu_ioctl(vcpu, KVM_SET_TSC_KHZ, (void *) (tsc_khz / l1_scale_factor));
for (;;) {
- volatile struct kvm_run *run = vcpu->run;
struct ucall uc;
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
diff --git a/tools/testing/selftests/kvm/x86_64/vmx_preemption_timer_test.c b/tools/testing/selftests/kvm/x86_64/vmx_preemption_timer_test.c
index 0efdc05969a5..affc32800158 100644
--- a/tools/testing/selftests/kvm/x86_64/vmx_preemption_timer_test.c
+++ b/tools/testing/selftests/kvm/x86_64/vmx_preemption_timer_test.c
@@ -157,7 +157,6 @@ int main(int argc, char *argv[])
struct kvm_regs regs1, regs2;
struct kvm_vm *vm;
- struct kvm_run *run;
struct kvm_vcpu *vcpu;
struct kvm_x86_state *state;
struct ucall uc;
@@ -173,7 +172,6 @@ int main(int argc, char *argv[])
/* Create VM */
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
- run = vcpu->run;
vcpu_regs_get(vcpu, &regs1);
@@ -182,10 +180,7 @@ int main(int argc, char *argv[])
for (stage = 1;; stage++) {
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Stage %d: unexpected exit reason: %u (%s),\n",
- stage, run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
@@ -237,7 +232,6 @@ int main(int argc, char *argv[])
/* Restore state in a new VM. */
vcpu = vm_recreate_with_one_vcpu(vm);
vcpu_load_state(vcpu, state);
- run = vcpu->run;
kvm_x86_state_cleanup(state);
memset(&regs2, 0, sizeof(regs2));
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 ff8ecdf32ae0..2ceb5c78c442 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
@@ -131,14 +131,10 @@ int main(int argc, char *argv[])
vcpu_args_set(vcpu, 1, vmx_pages_gva);
for (;;) {
- volatile struct kvm_run *run = vcpu->run;
struct ucall uc;
vcpu_run(vcpu);
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
diff --git a/tools/testing/selftests/kvm/x86_64/xapic_ipi_test.c b/tools/testing/selftests/kvm/x86_64/xapic_ipi_test.c
index 3d272d7f961e..67ac2a3292ef 100644
--- a/tools/testing/selftests/kvm/x86_64/xapic_ipi_test.c
+++ b/tools/testing/selftests/kvm/x86_64/xapic_ipi_test.c
@@ -198,7 +198,6 @@ static void *vcpu_thread(void *arg)
struct ucall uc;
int old;
int r;
- unsigned int exit_reason;
r = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old);
TEST_ASSERT(r == 0,
@@ -207,11 +206,8 @@ static void *vcpu_thread(void *arg)
fprintf(stderr, "vCPU thread running vCPU %u\n", vcpu->id);
vcpu_run(vcpu);
- exit_reason = vcpu->run->exit_reason;
- TEST_ASSERT(exit_reason == KVM_EXIT_IO,
- "vCPU %u exited with unexpected exit reason %u-%s, expected KVM_EXIT_IO",
- vcpu->id, exit_reason, exit_reason_str(exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
if (get_ucall(vcpu, &uc) == UCALL_ABORT) {
TEST_ASSERT(false,
diff --git a/tools/testing/selftests/kvm/x86_64/xapic_state_test.c b/tools/testing/selftests/kvm/x86_64/xapic_state_test.c
index d7d37dae3eeb..396c13f42457 100644
--- a/tools/testing/selftests/kvm/x86_64/xapic_state_test.c
+++ b/tools/testing/selftests/kvm/x86_64/xapic_state_test.c
@@ -132,6 +132,59 @@ static void test_icr(struct xapic_vcpu *x)
__test_icr(x, -1ull & ~APIC_DM_FIXED_MASK);
}
+static void __test_apic_id(struct kvm_vcpu *vcpu, uint64_t apic_base)
+{
+ uint32_t apic_id, expected;
+ struct kvm_lapic_state xapic;
+
+ vcpu_set_msr(vcpu, MSR_IA32_APICBASE, apic_base);
+
+ vcpu_ioctl(vcpu, KVM_GET_LAPIC, &xapic);
+
+ expected = apic_base & X2APIC_ENABLE ? vcpu->id : vcpu->id << 24;
+ apic_id = *((u32 *)&xapic.regs[APIC_ID]);
+
+ TEST_ASSERT(apic_id == expected,
+ "APIC_ID not set back to %s format; wanted = %x, got = %x",
+ (apic_base & X2APIC_ENABLE) ? "x2APIC" : "xAPIC",
+ expected, apic_id);
+}
+
+/*
+ * Verify that KVM switches the APIC_ID between xAPIC and x2APIC when userspace
+ * stuffs MSR_IA32_APICBASE. Setting the APIC_ID when x2APIC is enabled and
+ * when the APIC transitions for DISABLED to ENABLED is architectural behavior
+ * (on Intel), whereas the x2APIC => xAPIC transition behavior is KVM ABI since
+ * attempted to transition from x2APIC to xAPIC without disabling the APIC is
+ * architecturally disallowed.
+ */
+static void test_apic_id(void)
+{
+ const uint32_t NR_VCPUS = 3;
+ struct kvm_vcpu *vcpus[NR_VCPUS];
+ uint64_t apic_base;
+ struct kvm_vm *vm;
+ int i;
+
+ vm = vm_create_with_vcpus(NR_VCPUS, NULL, vcpus);
+ vm_enable_cap(vm, KVM_CAP_X2APIC_API, KVM_X2APIC_API_USE_32BIT_IDS);
+
+ for (i = 0; i < NR_VCPUS; i++) {
+ apic_base = vcpu_get_msr(vcpus[i], MSR_IA32_APICBASE);
+
+ TEST_ASSERT(apic_base & MSR_IA32_APICBASE_ENABLE,
+ "APIC not in ENABLED state at vCPU RESET");
+ TEST_ASSERT(!(apic_base & X2APIC_ENABLE),
+ "APIC not in xAPIC mode at vCPU RESET");
+
+ __test_apic_id(vcpus[i], apic_base);
+ __test_apic_id(vcpus[i], apic_base | X2APIC_ENABLE);
+ __test_apic_id(vcpus[i], apic_base);
+ }
+
+ kvm_vm_free(vm);
+}
+
int main(int argc, char *argv[])
{
struct xapic_vcpu x = {
@@ -157,4 +210,6 @@ int main(int argc, char *argv[])
virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA);
test_icr(&x);
kvm_vm_free(vm);
+
+ test_apic_id();
}
diff --git a/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c b/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c
index 13c75dc18c10..05898ad9f4d9 100644
--- a/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c
+++ b/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c
@@ -19,9 +19,6 @@
#include <sys/eventfd.h>
-/* Defined in include/linux/kvm_types.h */
-#define GPA_INVALID (~(ulong)0)
-
#define SHINFO_REGION_GVA 0xc0000000ULL
#define SHINFO_REGION_GPA 0xc0000000ULL
#define SHINFO_REGION_SLOT 10
@@ -29,6 +26,9 @@
#define DUMMY_REGION_GPA (SHINFO_REGION_GPA + (3 * PAGE_SIZE))
#define DUMMY_REGION_SLOT 11
+#define DUMMY_REGION_GPA_2 (SHINFO_REGION_GPA + (4 * PAGE_SIZE))
+#define DUMMY_REGION_SLOT_2 12
+
#define SHINFO_ADDR (SHINFO_REGION_GPA)
#define VCPU_INFO_ADDR (SHINFO_REGION_GPA + 0x40)
#define PVTIME_ADDR (SHINFO_REGION_GPA + PAGE_SIZE)
@@ -44,6 +44,37 @@
#define EVTCHN_TEST2 66
#define EVTCHN_TIMER 13
+enum {
+ TEST_INJECT_VECTOR = 0,
+ TEST_RUNSTATE_runnable,
+ TEST_RUNSTATE_blocked,
+ TEST_RUNSTATE_offline,
+ TEST_RUNSTATE_ADJUST,
+ TEST_RUNSTATE_DATA,
+ TEST_STEAL_TIME,
+ TEST_EVTCHN_MASKED,
+ TEST_EVTCHN_UNMASKED,
+ TEST_EVTCHN_SLOWPATH,
+ TEST_EVTCHN_SEND_IOCTL,
+ TEST_EVTCHN_HCALL,
+ TEST_EVTCHN_HCALL_SLOWPATH,
+ TEST_EVTCHN_HCALL_EVENTFD,
+ TEST_TIMER_SETUP,
+ TEST_TIMER_WAIT,
+ TEST_TIMER_RESTORE,
+ TEST_POLL_READY,
+ TEST_POLL_TIMEOUT,
+ TEST_POLL_MASKED,
+ TEST_POLL_WAKE,
+ TEST_TIMER_PAST,
+ TEST_LOCKING_SEND_RACE,
+ TEST_LOCKING_POLL_RACE,
+ TEST_LOCKING_POLL_TIMEOUT,
+ TEST_DONE,
+
+ TEST_GUEST_SAW_IRQ,
+};
+
#define XEN_HYPERCALL_MSR 0x40000000
#define MIN_STEAL_TIME 50000
@@ -147,7 +178,7 @@ static void evtchn_handler(struct ex_regs *regs)
vi->evtchn_pending_sel = 0;
guest_saw_irq = true;
- GUEST_SYNC(0x20);
+ GUEST_SYNC(TEST_GUEST_SAW_IRQ);
}
static void guest_wait_for_irq(void)
@@ -168,41 +199,41 @@ static void guest_code(void)
);
/* Trigger an interrupt injection */
- GUEST_SYNC(0);
+ GUEST_SYNC(TEST_INJECT_VECTOR);
guest_wait_for_irq();
/* Test having the host set runstates manually */
- GUEST_SYNC(RUNSTATE_runnable);
+ GUEST_SYNC(TEST_RUNSTATE_runnable);
GUEST_ASSERT(rs->time[RUNSTATE_runnable] != 0);
GUEST_ASSERT(rs->state == 0);
- GUEST_SYNC(RUNSTATE_blocked);
+ GUEST_SYNC(TEST_RUNSTATE_blocked);
GUEST_ASSERT(rs->time[RUNSTATE_blocked] != 0);
GUEST_ASSERT(rs->state == 0);
- GUEST_SYNC(RUNSTATE_offline);
+ GUEST_SYNC(TEST_RUNSTATE_offline);
GUEST_ASSERT(rs->time[RUNSTATE_offline] != 0);
GUEST_ASSERT(rs->state == 0);
/* Test runstate time adjust */
- GUEST_SYNC(4);
+ GUEST_SYNC(TEST_RUNSTATE_ADJUST);
GUEST_ASSERT(rs->time[RUNSTATE_blocked] == 0x5a);
GUEST_ASSERT(rs->time[RUNSTATE_offline] == 0x6b6b);
/* Test runstate time set */
- GUEST_SYNC(5);
+ GUEST_SYNC(TEST_RUNSTATE_DATA);
GUEST_ASSERT(rs->state_entry_time >= 0x8000);
GUEST_ASSERT(rs->time[RUNSTATE_runnable] == 0);
GUEST_ASSERT(rs->time[RUNSTATE_blocked] == 0x6b6b);
GUEST_ASSERT(rs->time[RUNSTATE_offline] == 0x5a);
/* sched_yield() should result in some 'runnable' time */
- GUEST_SYNC(6);
+ GUEST_SYNC(TEST_STEAL_TIME);
GUEST_ASSERT(rs->time[RUNSTATE_runnable] >= MIN_STEAL_TIME);
/* Attempt to deliver a *masked* interrupt */
- GUEST_SYNC(7);
+ GUEST_SYNC(TEST_EVTCHN_MASKED);
/* Wait until we see the bit set */
struct shared_info *si = (void *)SHINFO_VADDR;
@@ -210,71 +241,65 @@ static void guest_code(void)
__asm__ __volatile__ ("rep nop" : : : "memory");
/* Now deliver an *unmasked* interrupt */
- GUEST_SYNC(8);
+ GUEST_SYNC(TEST_EVTCHN_UNMASKED);
guest_wait_for_irq();
/* Change memslots and deliver an interrupt */
- GUEST_SYNC(9);
+ GUEST_SYNC(TEST_EVTCHN_SLOWPATH);
guest_wait_for_irq();
/* Deliver event channel with KVM_XEN_HVM_EVTCHN_SEND */
- GUEST_SYNC(10);
+ GUEST_SYNC(TEST_EVTCHN_SEND_IOCTL);
guest_wait_for_irq();
- GUEST_SYNC(11);
+ GUEST_SYNC(TEST_EVTCHN_HCALL);
/* Our turn. Deliver event channel (to ourselves) with
* EVTCHNOP_send hypercall. */
- unsigned long rax;
struct evtchn_send s = { .port = 127 };
- __asm__ __volatile__ ("vmcall" :
- "=a" (rax) :
- "a" (__HYPERVISOR_event_channel_op),
- "D" (EVTCHNOP_send),
- "S" (&s));
+ xen_hypercall(__HYPERVISOR_event_channel_op, EVTCHNOP_send, &s);
+
+ guest_wait_for_irq();
+
+ GUEST_SYNC(TEST_EVTCHN_HCALL_SLOWPATH);
- GUEST_ASSERT(rax == 0);
+ /*
+ * Same again, but this time the host has messed with memslots so it
+ * should take the slow path in kvm_xen_set_evtchn().
+ */
+ xen_hypercall(__HYPERVISOR_event_channel_op, EVTCHNOP_send, &s);
guest_wait_for_irq();
- GUEST_SYNC(12);
+ GUEST_SYNC(TEST_EVTCHN_HCALL_EVENTFD);
/* Deliver "outbound" event channel to an eventfd which
* happens to be one of our own irqfds. */
s.port = 197;
- __asm__ __volatile__ ("vmcall" :
- "=a" (rax) :
- "a" (__HYPERVISOR_event_channel_op),
- "D" (EVTCHNOP_send),
- "S" (&s));
-
- GUEST_ASSERT(rax == 0);
+ xen_hypercall(__HYPERVISOR_event_channel_op, EVTCHNOP_send, &s);
guest_wait_for_irq();
- GUEST_SYNC(13);
+ GUEST_SYNC(TEST_TIMER_SETUP);
/* Set a timer 100ms in the future. */
- __asm__ __volatile__ ("vmcall" :
- "=a" (rax) :
- "a" (__HYPERVISOR_set_timer_op),
- "D" (rs->state_entry_time + 100000000));
- GUEST_ASSERT(rax == 0);
+ xen_hypercall(__HYPERVISOR_set_timer_op,
+ rs->state_entry_time + 100000000, NULL);
- GUEST_SYNC(14);
+ GUEST_SYNC(TEST_TIMER_WAIT);
/* Now wait for the timer */
guest_wait_for_irq();
- GUEST_SYNC(15);
+ GUEST_SYNC(TEST_TIMER_RESTORE);
/* The host has 'restored' the timer. Just wait for it. */
guest_wait_for_irq();
- GUEST_SYNC(16);
+ GUEST_SYNC(TEST_POLL_READY);
/* Poll for an event channel port which is already set */
u32 ports[1] = { EVTCHN_TIMER };
@@ -284,65 +309,41 @@ static void guest_code(void)
.timeout = 0,
};
- __asm__ __volatile__ ("vmcall" :
- "=a" (rax) :
- "a" (__HYPERVISOR_sched_op),
- "D" (SCHEDOP_poll),
- "S" (&p));
-
- GUEST_ASSERT(rax == 0);
+ xen_hypercall(__HYPERVISOR_sched_op, SCHEDOP_poll, &p);
- GUEST_SYNC(17);
+ GUEST_SYNC(TEST_POLL_TIMEOUT);
/* Poll for an unset port and wait for the timeout. */
p.timeout = 100000000;
- __asm__ __volatile__ ("vmcall" :
- "=a" (rax) :
- "a" (__HYPERVISOR_sched_op),
- "D" (SCHEDOP_poll),
- "S" (&p));
+ xen_hypercall(__HYPERVISOR_sched_op, SCHEDOP_poll, &p);
- GUEST_ASSERT(rax == 0);
-
- GUEST_SYNC(18);
+ GUEST_SYNC(TEST_POLL_MASKED);
/* A timer will wake the masked port we're waiting on, while we poll */
p.timeout = 0;
- __asm__ __volatile__ ("vmcall" :
- "=a" (rax) :
- "a" (__HYPERVISOR_sched_op),
- "D" (SCHEDOP_poll),
- "S" (&p));
-
- GUEST_ASSERT(rax == 0);
+ xen_hypercall(__HYPERVISOR_sched_op, SCHEDOP_poll, &p);
- GUEST_SYNC(19);
+ GUEST_SYNC(TEST_POLL_WAKE);
/* A timer wake an *unmasked* port which should wake us with an
* actual interrupt, while we're polling on a different port. */
ports[0]++;
p.timeout = 0;
- __asm__ __volatile__ ("vmcall" :
- "=a" (rax) :
- "a" (__HYPERVISOR_sched_op),
- "D" (SCHEDOP_poll),
- "S" (&p));
-
- GUEST_ASSERT(rax == 0);
+ xen_hypercall(__HYPERVISOR_sched_op, SCHEDOP_poll, &p);
guest_wait_for_irq();
- GUEST_SYNC(20);
+ GUEST_SYNC(TEST_TIMER_PAST);
/* Timer should have fired already */
guest_wait_for_irq();
- GUEST_SYNC(21);
+ GUEST_SYNC(TEST_LOCKING_SEND_RACE);
/* Racing host ioctls */
guest_wait_for_irq();
- GUEST_SYNC(22);
+ GUEST_SYNC(TEST_LOCKING_POLL_RACE);
/* Racing vmcall against host ioctl */
ports[0] = 0;
@@ -363,24 +364,19 @@ wait_for_timer:
* timer IRQ is dropped due to an invalid event channel.
*/
for (i = 0; i < 100 && !guest_saw_irq; i++)
- asm volatile("vmcall"
- : "=a" (rax)
- : "a" (__HYPERVISOR_sched_op),
- "D" (SCHEDOP_poll),
- "S" (&p)
- : "memory");
+ __xen_hypercall(__HYPERVISOR_sched_op, SCHEDOP_poll, &p);
/*
* Re-send the timer IRQ if it was (likely) dropped due to the timer
* expiring while the event channel was invalid.
*/
if (!guest_saw_irq) {
- GUEST_SYNC(23);
+ GUEST_SYNC(TEST_LOCKING_POLL_TIMEOUT);
goto wait_for_timer;
}
guest_saw_irq = false;
- GUEST_SYNC(24);
+ GUEST_SYNC(TEST_DONE);
}
static int cmp_timespec(struct timespec *a, struct timespec *b)
@@ -412,21 +408,21 @@ static void *juggle_shinfo_state(void *arg)
{
struct kvm_vm *vm = (struct kvm_vm *)arg;
- struct kvm_xen_hvm_attr cache_init = {
+ struct kvm_xen_hvm_attr cache_activate = {
.type = KVM_XEN_ATTR_TYPE_SHARED_INFO,
.u.shared_info.gfn = SHINFO_REGION_GPA / PAGE_SIZE
};
- struct kvm_xen_hvm_attr cache_destroy = {
+ struct kvm_xen_hvm_attr cache_deactivate = {
.type = KVM_XEN_ATTR_TYPE_SHARED_INFO,
- .u.shared_info.gfn = GPA_INVALID
+ .u.shared_info.gfn = KVM_XEN_INVALID_GFN
};
for (;;) {
- __vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &cache_init);
- __vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &cache_destroy);
+ __vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &cache_activate);
+ __vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &cache_deactivate);
pthread_testcancel();
- };
+ }
return NULL;
}
@@ -626,15 +622,10 @@ int main(int argc, char *argv[])
bool evtchn_irq_expected = false;
for (;;) {
- volatile struct kvm_run *run = vcpu->run;
struct ucall uc;
vcpu_run(vcpu);
-
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
@@ -650,25 +641,26 @@ int main(int argc, char *argv[])
"runstate times don't add up");
switch (uc.args[1]) {
- case 0:
+ case TEST_INJECT_VECTOR:
if (verbose)
printf("Delivering evtchn upcall\n");
evtchn_irq_expected = true;
vinfo->evtchn_upcall_pending = 1;
break;
- case RUNSTATE_runnable...RUNSTATE_offline:
+ case TEST_RUNSTATE_runnable...TEST_RUNSTATE_offline:
TEST_ASSERT(!evtchn_irq_expected, "Event channel IRQ not seen");
if (!do_runstate_tests)
goto done;
if (verbose)
printf("Testing runstate %s\n", runstate_names[uc.args[1]]);
rst.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_CURRENT;
- rst.u.runstate.state = uc.args[1];
+ rst.u.runstate.state = uc.args[1] + RUNSTATE_runnable -
+ TEST_RUNSTATE_runnable;
vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &rst);
break;
- case 4:
+ case TEST_RUNSTATE_ADJUST:
if (verbose)
printf("Testing RUNSTATE_ADJUST\n");
rst.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADJUST;
@@ -683,7 +675,7 @@ int main(int argc, char *argv[])
vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &rst);
break;
- case 5:
+ case TEST_RUNSTATE_DATA:
if (verbose)
printf("Testing RUNSTATE_DATA\n");
rst.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_DATA;
@@ -695,7 +687,7 @@ int main(int argc, char *argv[])
vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &rst);
break;
- case 6:
+ case TEST_STEAL_TIME:
if (verbose)
printf("Testing steal time\n");
/* Yield until scheduler delay exceeds target */
@@ -705,7 +697,7 @@ int main(int argc, char *argv[])
} while (get_run_delay() < rundelay);
break;
- case 7:
+ case TEST_EVTCHN_MASKED:
if (!do_eventfd_tests)
goto done;
if (verbose)
@@ -715,7 +707,7 @@ int main(int argc, char *argv[])
alarm(1);
break;
- case 8:
+ case TEST_EVTCHN_UNMASKED:
if (verbose)
printf("Testing unmasked event channel\n");
/* Unmask that, but deliver the other one */
@@ -726,7 +718,7 @@ int main(int argc, char *argv[])
alarm(1);
break;
- case 9:
+ case TEST_EVTCHN_SLOWPATH:
TEST_ASSERT(!evtchn_irq_expected,
"Expected event channel IRQ but it didn't happen");
shinfo->evtchn_pending[1] = 0;
@@ -739,7 +731,7 @@ int main(int argc, char *argv[])
alarm(1);
break;
- case 10:
+ case TEST_EVTCHN_SEND_IOCTL:
TEST_ASSERT(!evtchn_irq_expected,
"Expected event channel IRQ but it didn't happen");
if (!do_evtchn_tests)
@@ -759,7 +751,7 @@ int main(int argc, char *argv[])
alarm(1);
break;
- case 11:
+ case TEST_EVTCHN_HCALL:
TEST_ASSERT(!evtchn_irq_expected,
"Expected event channel IRQ but it didn't happen");
shinfo->evtchn_pending[1] = 0;
@@ -770,7 +762,20 @@ int main(int argc, char *argv[])
alarm(1);
break;
- case 12:
+ case TEST_EVTCHN_HCALL_SLOWPATH:
+ TEST_ASSERT(!evtchn_irq_expected,
+ "Expected event channel IRQ but it didn't happen");
+ shinfo->evtchn_pending[0] = 0;
+
+ if (verbose)
+ printf("Testing guest EVTCHNOP_send direct to evtchn after memslot change\n");
+ vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
+ DUMMY_REGION_GPA_2, DUMMY_REGION_SLOT_2, 1, 0);
+ evtchn_irq_expected = true;
+ alarm(1);
+ break;
+
+ case TEST_EVTCHN_HCALL_EVENTFD:
TEST_ASSERT(!evtchn_irq_expected,
"Expected event channel IRQ but it didn't happen");
shinfo->evtchn_pending[0] = 0;
@@ -781,7 +786,7 @@ int main(int argc, char *argv[])
alarm(1);
break;
- case 13:
+ case TEST_TIMER_SETUP:
TEST_ASSERT(!evtchn_irq_expected,
"Expected event channel IRQ but it didn't happen");
shinfo->evtchn_pending[1] = 0;
@@ -790,7 +795,7 @@ int main(int argc, char *argv[])
printf("Testing guest oneshot timer\n");
break;
- case 14:
+ case TEST_TIMER_WAIT:
memset(&tmr, 0, sizeof(tmr));
tmr.type = KVM_XEN_VCPU_ATTR_TYPE_TIMER;
vcpu_ioctl(vcpu, KVM_XEN_VCPU_GET_ATTR, &tmr);
@@ -804,7 +809,7 @@ int main(int argc, char *argv[])
alarm(1);
break;
- case 15:
+ case TEST_TIMER_RESTORE:
TEST_ASSERT(!evtchn_irq_expected,
"Expected event channel IRQ but it didn't happen");
shinfo->evtchn_pending[0] = 0;
@@ -818,7 +823,7 @@ int main(int argc, char *argv[])
alarm(1);
break;
- case 16:
+ case TEST_POLL_READY:
TEST_ASSERT(!evtchn_irq_expected,
"Expected event channel IRQ but it didn't happen");
@@ -828,14 +833,14 @@ int main(int argc, char *argv[])
alarm(1);
break;
- case 17:
+ case TEST_POLL_TIMEOUT:
if (verbose)
printf("Testing SCHEDOP_poll timeout\n");
shinfo->evtchn_pending[0] = 0;
alarm(1);
break;
- case 18:
+ case TEST_POLL_MASKED:
if (verbose)
printf("Testing SCHEDOP_poll wake on masked event\n");
@@ -844,7 +849,7 @@ int main(int argc, char *argv[])
alarm(1);
break;
- case 19:
+ case TEST_POLL_WAKE:
shinfo->evtchn_pending[0] = shinfo->evtchn_mask[0] = 0;
if (verbose)
printf("Testing SCHEDOP_poll wake on unmasked event\n");
@@ -861,7 +866,7 @@ int main(int argc, char *argv[])
alarm(1);
break;
- case 20:
+ case TEST_TIMER_PAST:
TEST_ASSERT(!evtchn_irq_expected,
"Expected event channel IRQ but it didn't happen");
/* Read timer and check it is no longer pending */
@@ -878,7 +883,7 @@ int main(int argc, char *argv[])
alarm(1);
break;
- case 21:
+ case TEST_LOCKING_SEND_RACE:
TEST_ASSERT(!evtchn_irq_expected,
"Expected event channel IRQ but it didn't happen");
alarm(0);
@@ -900,7 +905,7 @@ int main(int argc, char *argv[])
__vm_ioctl(vm, KVM_XEN_HVM_EVTCHN_SEND, &uxe);
break;
- case 22:
+ case TEST_LOCKING_POLL_RACE:
TEST_ASSERT(!evtchn_irq_expected,
"Expected event channel IRQ but it didn't happen");
@@ -915,7 +920,7 @@ int main(int argc, char *argv[])
vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &tmr);
break;
- case 23:
+ case TEST_LOCKING_POLL_TIMEOUT:
/*
* Optional and possibly repeated sync point.
* Injecting the timer IRQ may fail if the
@@ -937,7 +942,7 @@ int main(int argc, char *argv[])
SHINFO_RACE_TIMEOUT * 1000000000ULL;
vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &tmr);
break;
- case 24:
+ case TEST_DONE:
TEST_ASSERT(!evtchn_irq_expected,
"Expected event channel IRQ but it didn't happen");
@@ -948,7 +953,7 @@ int main(int argc, char *argv[])
TEST_ASSERT(ret == 0, "pthread_join() failed: %s", strerror(ret));
goto done;
- case 0x20:
+ case TEST_GUEST_SAW_IRQ:
TEST_ASSERT(evtchn_irq_expected, "Unexpected event channel IRQ");
evtchn_irq_expected = false;
break;
diff --git a/tools/testing/selftests/kvm/x86_64/xen_vmcall_test.c b/tools/testing/selftests/kvm/x86_64/xen_vmcall_test.c
index 88914d48c65e..c94cde3b523f 100644
--- a/tools/testing/selftests/kvm/x86_64/xen_vmcall_test.c
+++ b/tools/testing/selftests/kvm/x86_64/xen_vmcall_test.c
@@ -122,10 +122,7 @@ int main(int argc, char *argv[])
continue;
}
- TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
- "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
index d5dab986f612..b6c4be3faf7a 100644
--- a/tools/testing/selftests/landlock/fs_test.c
+++ b/tools/testing/selftests/landlock/fs_test.c
@@ -11,6 +11,7 @@
#include <fcntl.h>
#include <linux/landlock.h>
#include <sched.h>
+#include <stdio.h>
#include <string.h>
#include <sys/capability.h>
#include <sys/mount.h>
@@ -89,6 +90,40 @@ static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3";
* └── s3d3
*/
+static bool fgrep(FILE *const inf, const char *const str)
+{
+ char line[32];
+ const int slen = strlen(str);
+
+ while (!feof(inf)) {
+ if (!fgets(line, sizeof(line), inf))
+ break;
+ if (strncmp(line, str, slen))
+ continue;
+
+ return true;
+ }
+
+ return false;
+}
+
+static bool supports_overlayfs(void)
+{
+ bool res;
+ FILE *const inf = fopen("/proc/filesystems", "r");
+
+ /*
+ * Consider that the filesystem is supported if we cannot get the
+ * supported ones.
+ */
+ if (!inf)
+ return true;
+
+ res = fgrep(inf, "nodev\toverlay\n");
+ fclose(inf);
+ return res;
+}
+
static void mkdir_parents(struct __test_metadata *const _metadata,
const char *const path)
{
@@ -4001,6 +4036,9 @@ FIXTURE(layout2_overlay) {};
FIXTURE_SETUP(layout2_overlay)
{
+ if (!supports_overlayfs())
+ SKIP(return, "overlayfs is not supported");
+
prepare_layout(_metadata);
create_directory(_metadata, LOWER_BASE);
@@ -4037,6 +4075,9 @@ FIXTURE_SETUP(layout2_overlay)
FIXTURE_TEARDOWN(layout2_overlay)
{
+ if (!supports_overlayfs())
+ SKIP(return, "overlayfs is not supported");
+
EXPECT_EQ(0, remove_path(lower_do1_fl3));
EXPECT_EQ(0, remove_path(lower_dl1_fl2));
EXPECT_EQ(0, remove_path(lower_fl1));
@@ -4068,6 +4109,9 @@ FIXTURE_TEARDOWN(layout2_overlay)
TEST_F_FORK(layout2_overlay, no_restriction)
{
+ if (!supports_overlayfs())
+ SKIP(return, "overlayfs is not supported");
+
ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY));
ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY));
ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY));
@@ -4231,6 +4275,9 @@ TEST_F_FORK(layout2_overlay, same_content_different_file)
size_t i;
const char *path_entry;
+ if (!supports_overlayfs())
+ SKIP(return, "overlayfs is not supported");
+
/* Sets rules on base directories (i.e. outside overlay scope). */
ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base);
ASSERT_LE(0, ruleset_fd);
diff --git a/tools/testing/selftests/landlock/ptrace_test.c b/tools/testing/selftests/landlock/ptrace_test.c
index c28ef98ff3ac..55e7871631a1 100644
--- a/tools/testing/selftests/landlock/ptrace_test.c
+++ b/tools/testing/selftests/landlock/ptrace_test.c
@@ -19,6 +19,12 @@
#include "common.h"
+/* Copied from security/yama/yama_lsm.c */
+#define YAMA_SCOPE_DISABLED 0
+#define YAMA_SCOPE_RELATIONAL 1
+#define YAMA_SCOPE_CAPABILITY 2
+#define YAMA_SCOPE_NO_ATTACH 3
+
static void create_domain(struct __test_metadata *const _metadata)
{
int ruleset_fd;
@@ -60,6 +66,25 @@ static int test_ptrace_read(const pid_t pid)
return 0;
}
+static int get_yama_ptrace_scope(void)
+{
+ int ret;
+ char buf[2] = {};
+ const int fd = open("/proc/sys/kernel/yama/ptrace_scope", O_RDONLY);
+
+ if (fd < 0)
+ return 0;
+
+ if (read(fd, buf, 1) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ ret = atoi(buf);
+ close(fd);
+ return ret;
+}
+
/* clang-format off */
FIXTURE(hierarchy) {};
/* clang-format on */
@@ -232,8 +257,51 @@ TEST_F(hierarchy, trace)
pid_t child, parent;
int status, err_proc_read;
int pipe_child[2], pipe_parent[2];
+ int yama_ptrace_scope;
char buf_parent;
long ret;
+ bool can_read_child, can_trace_child, can_read_parent, can_trace_parent;
+
+ yama_ptrace_scope = get_yama_ptrace_scope();
+ ASSERT_LE(0, yama_ptrace_scope);
+
+ if (yama_ptrace_scope > YAMA_SCOPE_DISABLED)
+ TH_LOG("Incomplete tests due to Yama restrictions (scope %d)",
+ yama_ptrace_scope);
+
+ /*
+ * can_read_child is true if a parent process can read its child
+ * process, which is only the case when the parent process is not
+ * isolated from the child with a dedicated Landlock domain.
+ */
+ can_read_child = !variant->domain_parent;
+
+ /*
+ * can_trace_child is true if a parent process can trace its child
+ * process. This depends on two conditions:
+ * - The parent process is not isolated from the child with a dedicated
+ * Landlock domain.
+ * - Yama allows tracing children (up to YAMA_SCOPE_RELATIONAL).
+ */
+ can_trace_child = can_read_child &&
+ yama_ptrace_scope <= YAMA_SCOPE_RELATIONAL;
+
+ /*
+ * can_read_parent is true if a child process can read its parent
+ * process, which is only the case when the child process is not
+ * isolated from the parent with a dedicated Landlock domain.
+ */
+ can_read_parent = !variant->domain_child;
+
+ /*
+ * can_trace_parent is true if a child process can trace its parent
+ * process. This depends on two conditions:
+ * - The child process is not isolated from the parent with a dedicated
+ * Landlock domain.
+ * - Yama is disabled (YAMA_SCOPE_DISABLED).
+ */
+ can_trace_parent = can_read_parent &&
+ yama_ptrace_scope <= YAMA_SCOPE_DISABLED;
/*
* Removes all effective and permitted capabilities to not interfere
@@ -264,16 +332,21 @@ TEST_F(hierarchy, trace)
/* Waits for the parent to be in a domain, if any. */
ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
- /* Tests PTRACE_ATTACH and PTRACE_MODE_READ on the parent. */
+ /* Tests PTRACE_MODE_READ on the parent. */
err_proc_read = test_ptrace_read(parent);
+ if (can_read_parent) {
+ EXPECT_EQ(0, err_proc_read);
+ } else {
+ EXPECT_EQ(EACCES, err_proc_read);
+ }
+
+ /* Tests PTRACE_ATTACH on the parent. */
ret = ptrace(PTRACE_ATTACH, parent, NULL, 0);
- if (variant->domain_child) {
+ if (can_trace_parent) {
+ EXPECT_EQ(0, ret);
+ } else {
EXPECT_EQ(-1, ret);
EXPECT_EQ(EPERM, errno);
- EXPECT_EQ(EACCES, err_proc_read);
- } else {
- EXPECT_EQ(0, ret);
- EXPECT_EQ(0, err_proc_read);
}
if (ret == 0) {
ASSERT_EQ(parent, waitpid(parent, &status, 0));
@@ -283,11 +356,11 @@ TEST_F(hierarchy, trace)
/* Tests child PTRACE_TRACEME. */
ret = ptrace(PTRACE_TRACEME);
- if (variant->domain_parent) {
+ if (can_trace_child) {
+ EXPECT_EQ(0, ret);
+ } else {
EXPECT_EQ(-1, ret);
EXPECT_EQ(EPERM, errno);
- } else {
- EXPECT_EQ(0, ret);
}
/*
@@ -296,7 +369,7 @@ TEST_F(hierarchy, trace)
*/
ASSERT_EQ(1, write(pipe_child[1], ".", 1));
- if (!variant->domain_parent) {
+ if (can_trace_child) {
ASSERT_EQ(0, raise(SIGSTOP));
}
@@ -321,7 +394,7 @@ TEST_F(hierarchy, trace)
ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1));
/* Tests child PTRACE_TRACEME. */
- if (!variant->domain_parent) {
+ if (can_trace_child) {
ASSERT_EQ(child, waitpid(child, &status, 0));
ASSERT_EQ(1, WIFSTOPPED(status));
ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0));
@@ -331,17 +404,23 @@ TEST_F(hierarchy, trace)
EXPECT_EQ(ESRCH, errno);
}
- /* Tests PTRACE_ATTACH and PTRACE_MODE_READ on the child. */
+ /* Tests PTRACE_MODE_READ on the child. */
err_proc_read = test_ptrace_read(child);
+ if (can_read_child) {
+ EXPECT_EQ(0, err_proc_read);
+ } else {
+ EXPECT_EQ(EACCES, err_proc_read);
+ }
+
+ /* Tests PTRACE_ATTACH on the child. */
ret = ptrace(PTRACE_ATTACH, child, NULL, 0);
- if (variant->domain_parent) {
+ if (can_trace_child) {
+ EXPECT_EQ(0, ret);
+ } else {
EXPECT_EQ(-1, ret);
EXPECT_EQ(EPERM, errno);
- EXPECT_EQ(EACCES, err_proc_read);
- } else {
- EXPECT_EQ(0, ret);
- EXPECT_EQ(0, err_proc_read);
}
+
if (ret == 0) {
ASSERT_EQ(child, waitpid(child, &status, 0));
ASSERT_EQ(1, WIFSTOPPED(status));
diff --git a/tools/testing/selftests/lib.mk b/tools/testing/selftests/lib.mk
index f7900e75d230..05400462c779 100644
--- a/tools/testing/selftests/lib.mk
+++ b/tools/testing/selftests/lib.mk
@@ -10,12 +10,14 @@ endif
CLANG_TARGET_FLAGS_arm := arm-linux-gnueabi
CLANG_TARGET_FLAGS_arm64 := aarch64-linux-gnu
CLANG_TARGET_FLAGS_hexagon := hexagon-linux-musl
+CLANG_TARGET_FLAGS_i386 := i386-linux-gnu
CLANG_TARGET_FLAGS_m68k := m68k-linux-gnu
CLANG_TARGET_FLAGS_mips := mipsel-linux-gnu
CLANG_TARGET_FLAGS_powerpc := powerpc64le-linux-gnu
CLANG_TARGET_FLAGS_riscv := riscv64-linux-gnu
CLANG_TARGET_FLAGS_s390 := s390x-linux-gnu
CLANG_TARGET_FLAGS_x86 := x86_64-linux-gnu
+CLANG_TARGET_FLAGS_x86_64 := x86_64-linux-gnu
CLANG_TARGET_FLAGS := $(CLANG_TARGET_FLAGS_$(ARCH))
ifeq ($(CROSS_COMPILE),)
diff --git a/tools/testing/selftests/media_tests/Makefile b/tools/testing/selftests/media_tests/Makefile
index 60826d7d37d4..471d83e61d95 100644
--- a/tools/testing/selftests/media_tests/Makefile
+++ b/tools/testing/selftests/media_tests/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
#
-CFLAGS += -I../ -I../../../../usr/include/
+CFLAGS += -I../ $(KHDR_INCLUDES)
TEST_GEN_PROGS := media_device_test media_device_open video_device_test
include ../lib.mk
diff --git a/tools/testing/selftests/membarrier/Makefile b/tools/testing/selftests/membarrier/Makefile
index 34d1c81a2324..fc840e06ff56 100644
--- a/tools/testing/selftests/membarrier/Makefile
+++ b/tools/testing/selftests/membarrier/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-CFLAGS += -g -I../../../../usr/include/
+CFLAGS += -g $(KHDR_INCLUDES)
LDLIBS += -lpthread
TEST_GEN_PROGS := membarrier_test_single_thread \
diff --git a/tools/testing/selftests/memfd/Makefile b/tools/testing/selftests/memfd/Makefile
index 4da8b565fa32..163b6f68631c 100644
--- a/tools/testing/selftests/memfd/Makefile
+++ b/tools/testing/selftests/memfd/Makefile
@@ -1,8 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
CFLAGS += -D_FILE_OFFSET_BITS=64
-CFLAGS += -I../../../../include/uapi/
-CFLAGS += -I../../../../include/
-CFLAGS += -I../../../../usr/include/
+CFLAGS += $(KHDR_INCLUDES)
TEST_GEN_PROGS := memfd_test
TEST_PROGS := run_fuse_test.sh run_hugetlbfs_test.sh
diff --git a/tools/testing/selftests/memfd/fuse_test.c b/tools/testing/selftests/memfd/fuse_test.c
index be675002f918..93798c8c5d54 100644
--- a/tools/testing/selftests/memfd/fuse_test.c
+++ b/tools/testing/selftests/memfd/fuse_test.c
@@ -22,6 +22,7 @@
#include <linux/falloc.h>
#include <fcntl.h>
#include <linux/memfd.h>
+#include <linux/types.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
diff --git a/tools/testing/selftests/memfd/memfd_test.c b/tools/testing/selftests/memfd/memfd_test.c
index 94df2692e6e4..ae71f15f790d 100644
--- a/tools/testing/selftests/memfd/memfd_test.c
+++ b/tools/testing/selftests/memfd/memfd_test.c
@@ -28,12 +28,46 @@
#define MFD_DEF_SIZE 8192
#define STACK_SIZE 65536
+#define F_SEAL_EXEC 0x0020
+
+#define F_WX_SEALS (F_SEAL_SHRINK | \
+ F_SEAL_GROW | \
+ F_SEAL_WRITE | \
+ F_SEAL_FUTURE_WRITE | \
+ F_SEAL_EXEC)
+
+#define MFD_NOEXEC_SEAL 0x0008U
+
/*
* Default is not to test hugetlbfs
*/
static size_t mfd_def_size = MFD_DEF_SIZE;
static const char *memfd_str = MEMFD_STR;
+static ssize_t fd2name(int fd, char *buf, size_t bufsize)
+{
+ char buf1[PATH_MAX];
+ int size;
+ ssize_t nbytes;
+
+ size = snprintf(buf1, PATH_MAX, "/proc/self/fd/%d", fd);
+ if (size < 0) {
+ printf("snprintf(%d) failed on %m\n", fd);
+ abort();
+ }
+
+ /*
+ * reserver one byte for string termination.
+ */
+ nbytes = readlink(buf1, buf, bufsize-1);
+ if (nbytes == -1) {
+ printf("readlink(%s) failed %m\n", buf1);
+ abort();
+ }
+ buf[nbytes] = '\0';
+ return nbytes;
+}
+
static int mfd_assert_new(const char *name, loff_t sz, unsigned int flags)
{
int r, fd;
@@ -54,6 +88,37 @@ static int mfd_assert_new(const char *name, loff_t sz, unsigned int flags)
return fd;
}
+static void sysctl_assert_write(const char *val)
+{
+ int fd = open("/proc/sys/vm/memfd_noexec", O_WRONLY | O_CLOEXEC);
+
+ if (fd < 0) {
+ printf("open sysctl failed\n");
+ abort();
+ }
+
+ if (write(fd, val, strlen(val)) < 0) {
+ printf("write sysctl failed\n");
+ abort();
+ }
+}
+
+static void sysctl_fail_write(const char *val)
+{
+ int fd = open("/proc/sys/vm/memfd_noexec", O_WRONLY | O_CLOEXEC);
+
+ if (fd < 0) {
+ printf("open sysctl failed\n");
+ abort();
+ }
+
+ if (write(fd, val, strlen(val)) >= 0) {
+ printf("write sysctl %s succeeded, but failure expected\n",
+ val);
+ abort();
+ }
+}
+
static int mfd_assert_reopen_fd(int fd_in)
{
int fd;
@@ -98,11 +163,14 @@ static unsigned int mfd_assert_get_seals(int fd)
static void mfd_assert_has_seals(int fd, unsigned int seals)
{
+ char buf[PATH_MAX];
+ int nbytes;
unsigned int s;
+ fd2name(fd, buf, PATH_MAX);
s = mfd_assert_get_seals(fd);
if (s != seals) {
- printf("%u != %u = GET_SEALS(%d)\n", seals, s, fd);
+ printf("%u != %u = GET_SEALS(%s)\n", seals, s, buf);
abort();
}
}
@@ -594,6 +662,64 @@ static void mfd_fail_grow_write(int fd)
}
}
+static void mfd_assert_mode(int fd, int mode)
+{
+ struct stat st;
+ char buf[PATH_MAX];
+ int nbytes;
+
+ fd2name(fd, buf, PATH_MAX);
+
+ if (fstat(fd, &st) < 0) {
+ printf("fstat(%s) failed: %m\n", buf);
+ abort();
+ }
+
+ if ((st.st_mode & 07777) != mode) {
+ printf("fstat(%s) wrong file mode 0%04o, but expected 0%04o\n",
+ buf, (int)st.st_mode & 07777, mode);
+ abort();
+ }
+}
+
+static void mfd_assert_chmod(int fd, int mode)
+{
+ char buf[PATH_MAX];
+ int nbytes;
+
+ fd2name(fd, buf, PATH_MAX);
+
+ if (fchmod(fd, mode) < 0) {
+ printf("fchmod(%s, 0%04o) failed: %m\n", buf, mode);
+ abort();
+ }
+
+ mfd_assert_mode(fd, mode);
+}
+
+static void mfd_fail_chmod(int fd, int mode)
+{
+ struct stat st;
+ char buf[PATH_MAX];
+ int nbytes;
+
+ fd2name(fd, buf, PATH_MAX);
+
+ if (fstat(fd, &st) < 0) {
+ printf("fstat(%s) failed: %m\n", buf);
+ abort();
+ }
+
+ if (fchmod(fd, mode) == 0) {
+ printf("fchmod(%s, 0%04o) didn't fail as expected\n",
+ buf, mode);
+ abort();
+ }
+
+ /* verify that file mode bits did not change */
+ mfd_assert_mode(fd, st.st_mode & 07777);
+}
+
static int idle_thread_fn(void *arg)
{
sigset_t set;
@@ -671,6 +797,9 @@ static void test_create(void)
mfd_fail_new("", ~0);
mfd_fail_new("", 0x80000000U);
+ /* verify EXEC and NOEXEC_SEAL can't both be set */
+ mfd_fail_new("", MFD_EXEC | MFD_NOEXEC_SEAL);
+
/* verify MFD_CLOEXEC is allowed */
fd = mfd_assert_new("", 0, MFD_CLOEXEC);
close(fd);
@@ -881,6 +1010,211 @@ static void test_seal_resize(void)
}
/*
+ * Test SEAL_EXEC
+ * Test fd is created with exec and allow sealing.
+ * chmod() cannot change x bits after sealing.
+ */
+static void test_exec_seal(void)
+{
+ int fd;
+
+ printf("%s SEAL-EXEC\n", memfd_str);
+
+ printf("%s Apply SEAL_EXEC\n", memfd_str);
+ fd = mfd_assert_new("kern_memfd_seal_exec",
+ mfd_def_size,
+ MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_EXEC);
+
+ mfd_assert_mode(fd, 0777);
+ mfd_assert_chmod(fd, 0644);
+
+ mfd_assert_has_seals(fd, 0);
+ mfd_assert_add_seals(fd, F_SEAL_EXEC);
+ mfd_assert_has_seals(fd, F_SEAL_EXEC);
+
+ mfd_assert_chmod(fd, 0600);
+ mfd_fail_chmod(fd, 0777);
+ mfd_fail_chmod(fd, 0670);
+ mfd_fail_chmod(fd, 0605);
+ mfd_fail_chmod(fd, 0700);
+ mfd_fail_chmod(fd, 0100);
+ mfd_assert_chmod(fd, 0666);
+ mfd_assert_write(fd);
+ close(fd);
+
+ printf("%s Apply ALL_SEALS\n", memfd_str);
+ fd = mfd_assert_new("kern_memfd_seal_exec",
+ mfd_def_size,
+ MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_EXEC);
+
+ mfd_assert_mode(fd, 0777);
+ mfd_assert_chmod(fd, 0700);
+
+ mfd_assert_has_seals(fd, 0);
+ mfd_assert_add_seals(fd, F_SEAL_EXEC);
+ mfd_assert_has_seals(fd, F_WX_SEALS);
+
+ mfd_fail_chmod(fd, 0711);
+ mfd_fail_chmod(fd, 0600);
+ mfd_fail_write(fd);
+ close(fd);
+}
+
+/*
+ * Test EXEC_NO_SEAL
+ * Test fd is created with exec and not allow sealing.
+ */
+static void test_exec_no_seal(void)
+{
+ int fd;
+
+ printf("%s EXEC_NO_SEAL\n", memfd_str);
+
+ /* Create with EXEC but without ALLOW_SEALING */
+ fd = mfd_assert_new("kern_memfd_exec_no_sealing",
+ mfd_def_size,
+ MFD_CLOEXEC | MFD_EXEC);
+ mfd_assert_mode(fd, 0777);
+ mfd_assert_has_seals(fd, F_SEAL_SEAL);
+ mfd_assert_chmod(fd, 0666);
+ close(fd);
+}
+
+/*
+ * Test memfd_create with MFD_NOEXEC flag
+ */
+static void test_noexec_seal(void)
+{
+ int fd;
+
+ printf("%s NOEXEC_SEAL\n", memfd_str);
+
+ /* Create with NOEXEC and ALLOW_SEALING */
+ fd = mfd_assert_new("kern_memfd_noexec",
+ mfd_def_size,
+ MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_NOEXEC_SEAL);
+ mfd_assert_mode(fd, 0666);
+ mfd_assert_has_seals(fd, F_SEAL_EXEC);
+ mfd_fail_chmod(fd, 0777);
+ close(fd);
+
+ /* Create with NOEXEC but without ALLOW_SEALING */
+ fd = mfd_assert_new("kern_memfd_noexec",
+ mfd_def_size,
+ MFD_CLOEXEC | MFD_NOEXEC_SEAL);
+ mfd_assert_mode(fd, 0666);
+ mfd_assert_has_seals(fd, F_SEAL_EXEC);
+ mfd_fail_chmod(fd, 0777);
+ close(fd);
+}
+
+static void test_sysctl_child(void)
+{
+ int fd;
+
+ printf("%s sysctl 0\n", memfd_str);
+ sysctl_assert_write("0");
+ fd = mfd_assert_new("kern_memfd_sysctl_0",
+ mfd_def_size,
+ MFD_CLOEXEC | MFD_ALLOW_SEALING);
+
+ mfd_assert_mode(fd, 0777);
+ mfd_assert_has_seals(fd, 0);
+ mfd_assert_chmod(fd, 0644);
+ close(fd);
+
+ printf("%s sysctl 1\n", memfd_str);
+ sysctl_assert_write("1");
+ fd = mfd_assert_new("kern_memfd_sysctl_1",
+ mfd_def_size,
+ MFD_CLOEXEC | MFD_ALLOW_SEALING);
+
+ mfd_assert_mode(fd, 0666);
+ mfd_assert_has_seals(fd, F_SEAL_EXEC);
+ mfd_fail_chmod(fd, 0777);
+ sysctl_fail_write("0");
+ close(fd);
+
+ printf("%s sysctl 2\n", memfd_str);
+ sysctl_assert_write("2");
+ mfd_fail_new("kern_memfd_sysctl_2",
+ MFD_CLOEXEC | MFD_ALLOW_SEALING);
+ sysctl_fail_write("0");
+ sysctl_fail_write("1");
+}
+
+static int newpid_thread_fn(void *arg)
+{
+ test_sysctl_child();
+ return 0;
+}
+
+static void test_sysctl_child2(void)
+{
+ int fd;
+
+ sysctl_fail_write("0");
+ fd = mfd_assert_new("kern_memfd_sysctl_1",
+ mfd_def_size,
+ MFD_CLOEXEC | MFD_ALLOW_SEALING);
+
+ mfd_assert_mode(fd, 0666);
+ mfd_assert_has_seals(fd, F_SEAL_EXEC);
+ mfd_fail_chmod(fd, 0777);
+ close(fd);
+}
+
+static int newpid_thread_fn2(void *arg)
+{
+ test_sysctl_child2();
+ return 0;
+}
+static pid_t spawn_newpid_thread(unsigned int flags, int (*fn)(void *))
+{
+ uint8_t *stack;
+ pid_t pid;
+
+ stack = malloc(STACK_SIZE);
+ if (!stack) {
+ printf("malloc(STACK_SIZE) failed: %m\n");
+ abort();
+ }
+
+ pid = clone(fn,
+ stack + STACK_SIZE,
+ SIGCHLD | flags,
+ NULL);
+ if (pid < 0) {
+ printf("clone() failed: %m\n");
+ abort();
+ }
+
+ return pid;
+}
+
+static void join_newpid_thread(pid_t pid)
+{
+ waitpid(pid, NULL, 0);
+}
+
+/*
+ * Test sysctl
+ * A very basic sealing test to see whether setting/retrieving seals works.
+ */
+static void test_sysctl(void)
+{
+ int pid = spawn_newpid_thread(CLONE_NEWPID, newpid_thread_fn);
+
+ join_newpid_thread(pid);
+
+ printf("%s child ns\n", memfd_str);
+ sysctl_assert_write("1");
+
+ pid = spawn_newpid_thread(CLONE_NEWPID, newpid_thread_fn2);
+ join_newpid_thread(pid);
+}
+
+/*
* Test sharing via dup()
* Test that seals are shared between dupped FDs and they're all equal.
*/
@@ -1053,6 +1387,9 @@ int main(int argc, char **argv)
test_create();
test_basic();
+ test_exec_seal();
+ test_exec_no_seal();
+ test_noexec_seal();
test_seal_write();
test_seal_future_write();
@@ -1074,6 +1411,8 @@ int main(int argc, char **argv)
test_share_fork("SHARE-FORK", SHARED_FT_STR);
join_idle_thread(pid);
+ test_sysctl();
+
printf("memfd: DONE\n");
return 0;
diff --git a/tools/testing/selftests/vm/.gitignore b/tools/testing/selftests/mm/.gitignore
index 1f8c36a9fa10..1f8c36a9fa10 100644
--- a/tools/testing/selftests/vm/.gitignore
+++ b/tools/testing/selftests/mm/.gitignore
diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/mm/Makefile
index 89c14e41bd43..c31d952cff68 100644
--- a/tools/testing/selftests/vm/Makefile
+++ b/tools/testing/selftests/mm/Makefile
@@ -1,11 +1,15 @@
# SPDX-License-Identifier: GPL-2.0
-# Makefile for vm selftests
+# Makefile for mm selftests
-LOCAL_HDRS += $(selfdir)/vm/local_config.h $(top_srcdir)/mm/gup_test.h
+LOCAL_HDRS += $(selfdir)/mm/local_config.h $(top_srcdir)/mm/gup_test.h
include local_config.mk
+ifeq ($(CROSS_COMPILE),)
uname_M := $(shell uname -m 2>/dev/null || echo not)
+else
+uname_M := $(shell echo $(CROSS_COMPILE) | grep -o '^[a-z0-9]\+')
+endif
MACHINE ?= $(shell echo $(uname_M) | sed -e 's/aarch64.*/arm64/' -e 's/ppc64.*/ppc64/')
# Without this, failed build products remain, with up-to-date timestamps,
@@ -25,7 +29,7 @@ MACHINE ?= $(shell echo $(uname_M) | sed -e 's/aarch64.*/arm64/' -e 's/ppc64.*/p
# LDLIBS.
MAKEFLAGS += --no-builtin-rules
-CFLAGS = -Wall -I $(top_srcdir) -I $(top_srcdir)/usr/include $(EXTRA_CFLAGS) $(KHDR_INCLUDES)
+CFLAGS = -Wall -I $(top_srcdir) $(EXTRA_CFLAGS) $(KHDR_INCLUDES)
LDLIBS = -lrt -lpthread
TEST_GEN_FILES = cow
TEST_GEN_FILES += compaction_test
@@ -56,6 +60,7 @@ TEST_GEN_PROGS += soft-dirty
TEST_GEN_PROGS += split_huge_page_test
TEST_GEN_FILES += ksm_tests
TEST_GEN_PROGS += ksm_functional_tests
+TEST_GEN_PROGS += mdwe_test
ifeq ($(MACHINE),x86_64)
CAN_BUILD_I386 := $(shell ./../x86/check_cc.sh "$(CC)" ../x86/trivial_32bit_program.c -m32)
diff --git a/tools/testing/selftests/vm/charge_reserved_hugetlb.sh b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh
index a5cb4b09a46c..a5cb4b09a46c 100644
--- a/tools/testing/selftests/vm/charge_reserved_hugetlb.sh
+++ b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh
diff --git a/tools/testing/selftests/vm/check_config.sh b/tools/testing/selftests/mm/check_config.sh
index bcba3af0acea..bcba3af0acea 100644
--- a/tools/testing/selftests/vm/check_config.sh
+++ b/tools/testing/selftests/mm/check_config.sh
diff --git a/tools/testing/selftests/vm/compaction_test.c b/tools/testing/selftests/mm/compaction_test.c
index 9b420140ba2b..9b420140ba2b 100644
--- a/tools/testing/selftests/vm/compaction_test.c
+++ b/tools/testing/selftests/mm/compaction_test.c
diff --git a/tools/testing/selftests/vm/config b/tools/testing/selftests/mm/config
index be087c4bc396..be087c4bc396 100644
--- a/tools/testing/selftests/vm/config
+++ b/tools/testing/selftests/mm/config
diff --git a/tools/testing/selftests/vm/cow.c b/tools/testing/selftests/mm/cow.c
index 26f6ea3079e2..0eb2e8180aa5 100644
--- a/tools/testing/selftests/vm/cow.c
+++ b/tools/testing/selftests/mm/cow.c
@@ -30,6 +30,13 @@
#include "../kselftest.h"
#include "vm_util.h"
+#ifndef MADV_PAGEOUT
+#define MADV_PAGEOUT 21
+#endif
+#ifndef MADV_COLLAPSE
+#define MADV_COLLAPSE 25
+#endif
+
static size_t pagesize;
static int pagemap_fd;
static size_t thpsize;
@@ -1178,6 +1185,228 @@ static int tests_per_anon_test_case(void)
return tests;
}
+enum anon_thp_collapse_test {
+ ANON_THP_COLLAPSE_UNSHARED,
+ ANON_THP_COLLAPSE_FULLY_SHARED,
+ ANON_THP_COLLAPSE_LOWER_SHARED,
+ ANON_THP_COLLAPSE_UPPER_SHARED,
+};
+
+static void do_test_anon_thp_collapse(char *mem, size_t size,
+ enum anon_thp_collapse_test test)
+{
+ struct comm_pipes comm_pipes;
+ char buf;
+ int ret;
+
+ ret = setup_comm_pipes(&comm_pipes);
+ if (ret) {
+ ksft_test_result_fail("pipe() failed\n");
+ return;
+ }
+
+ /*
+ * Trigger PTE-mapping the THP by temporarily mapping a single subpage
+ * R/O, such that we can try collapsing it later.
+ */
+ ret = mprotect(mem + pagesize, pagesize, PROT_READ);
+ if (ret) {
+ ksft_test_result_fail("mprotect() failed\n");
+ goto close_comm_pipes;
+ }
+ ret = mprotect(mem + pagesize, pagesize, PROT_READ | PROT_WRITE);
+ if (ret) {
+ ksft_test_result_fail("mprotect() failed\n");
+ goto close_comm_pipes;
+ }
+
+ switch (test) {
+ case ANON_THP_COLLAPSE_UNSHARED:
+ /* Collapse before actually COW-sharing the page. */
+ ret = madvise(mem, size, MADV_COLLAPSE);
+ if (ret) {
+ ksft_test_result_skip("MADV_COLLAPSE failed: %s\n",
+ strerror(errno));
+ goto close_comm_pipes;
+ }
+ break;
+ case ANON_THP_COLLAPSE_FULLY_SHARED:
+ /* COW-share the full PTE-mapped THP. */
+ break;
+ case ANON_THP_COLLAPSE_LOWER_SHARED:
+ /* Don't COW-share the upper part of the THP. */
+ ret = madvise(mem + size / 2, size / 2, MADV_DONTFORK);
+ if (ret) {
+ ksft_test_result_fail("MADV_DONTFORK failed\n");
+ goto close_comm_pipes;
+ }
+ break;
+ case ANON_THP_COLLAPSE_UPPER_SHARED:
+ /* Don't COW-share the lower part of the THP. */
+ ret = madvise(mem, size / 2, MADV_DONTFORK);
+ if (ret) {
+ ksft_test_result_fail("MADV_DONTFORK failed\n");
+ goto close_comm_pipes;
+ }
+ break;
+ default:
+ assert(false);
+ }
+
+ ret = fork();
+ if (ret < 0) {
+ ksft_test_result_fail("fork() failed\n");
+ goto close_comm_pipes;
+ } else if (!ret) {
+ switch (test) {
+ case ANON_THP_COLLAPSE_UNSHARED:
+ case ANON_THP_COLLAPSE_FULLY_SHARED:
+ exit(child_memcmp_fn(mem, size, &comm_pipes));
+ break;
+ case ANON_THP_COLLAPSE_LOWER_SHARED:
+ exit(child_memcmp_fn(mem, size / 2, &comm_pipes));
+ break;
+ case ANON_THP_COLLAPSE_UPPER_SHARED:
+ exit(child_memcmp_fn(mem + size / 2, size / 2,
+ &comm_pipes));
+ break;
+ default:
+ assert(false);
+ }
+ }
+
+ while (read(comm_pipes.child_ready[0], &buf, 1) != 1)
+ ;
+
+ switch (test) {
+ case ANON_THP_COLLAPSE_UNSHARED:
+ break;
+ case ANON_THP_COLLAPSE_UPPER_SHARED:
+ case ANON_THP_COLLAPSE_LOWER_SHARED:
+ /*
+ * Revert MADV_DONTFORK such that we merge the VMAs and are
+ * able to actually collapse.
+ */
+ ret = madvise(mem, size, MADV_DOFORK);
+ if (ret) {
+ ksft_test_result_fail("MADV_DOFORK failed\n");
+ write(comm_pipes.parent_ready[1], "0", 1);
+ wait(&ret);
+ goto close_comm_pipes;
+ }
+ /* FALLTHROUGH */
+ case ANON_THP_COLLAPSE_FULLY_SHARED:
+ /* Collapse before anyone modified the COW-shared page. */
+ ret = madvise(mem, size, MADV_COLLAPSE);
+ if (ret) {
+ ksft_test_result_skip("MADV_COLLAPSE failed: %s\n",
+ strerror(errno));
+ write(comm_pipes.parent_ready[1], "0", 1);
+ wait(&ret);
+ goto close_comm_pipes;
+ }
+ break;
+ default:
+ assert(false);
+ }
+
+ /* Modify the page. */
+ memset(mem, 0xff, size);
+ write(comm_pipes.parent_ready[1], "0", 1);
+
+ wait(&ret);
+ if (WIFEXITED(ret))
+ ret = WEXITSTATUS(ret);
+ else
+ ret = -EINVAL;
+
+ ksft_test_result(!ret, "No leak from parent into child\n");
+close_comm_pipes:
+ close_comm_pipes(&comm_pipes);
+}
+
+static void test_anon_thp_collapse_unshared(char *mem, size_t size)
+{
+ do_test_anon_thp_collapse(mem, size, ANON_THP_COLLAPSE_UNSHARED);
+}
+
+static void test_anon_thp_collapse_fully_shared(char *mem, size_t size)
+{
+ do_test_anon_thp_collapse(mem, size, ANON_THP_COLLAPSE_FULLY_SHARED);
+}
+
+static void test_anon_thp_collapse_lower_shared(char *mem, size_t size)
+{
+ do_test_anon_thp_collapse(mem, size, ANON_THP_COLLAPSE_LOWER_SHARED);
+}
+
+static void test_anon_thp_collapse_upper_shared(char *mem, size_t size)
+{
+ do_test_anon_thp_collapse(mem, size, ANON_THP_COLLAPSE_UPPER_SHARED);
+}
+
+/*
+ * Test cases that are specific to anonymous THP: pages in private mappings
+ * that may get shared via COW during fork().
+ */
+static const struct test_case anon_thp_test_cases[] = {
+ /*
+ * Basic COW test for fork() without any GUP when collapsing a THP
+ * before fork().
+ *
+ * Re-mapping a PTE-mapped anon THP using a single PMD ("in-place
+ * collapse") might easily get COW handling wrong when not collapsing
+ * exclusivity information properly.
+ */
+ {
+ "Basic COW after fork() when collapsing before fork()",
+ test_anon_thp_collapse_unshared,
+ },
+ /* Basic COW test, but collapse after COW-sharing a full THP. */
+ {
+ "Basic COW after fork() when collapsing after fork() (fully shared)",
+ test_anon_thp_collapse_fully_shared,
+ },
+ /*
+ * Basic COW test, but collapse after COW-sharing the lower half of a
+ * THP.
+ */
+ {
+ "Basic COW after fork() when collapsing after fork() (lower shared)",
+ test_anon_thp_collapse_lower_shared,
+ },
+ /*
+ * Basic COW test, but collapse after COW-sharing the upper half of a
+ * THP.
+ */
+ {
+ "Basic COW after fork() when collapsing after fork() (upper shared)",
+ test_anon_thp_collapse_upper_shared,
+ },
+};
+
+static void run_anon_thp_test_cases(void)
+{
+ int i;
+
+ if (!thpsize)
+ return;
+
+ ksft_print_msg("[INFO] Anonymous THP tests\n");
+
+ for (i = 0; i < ARRAY_SIZE(anon_thp_test_cases); i++) {
+ struct test_case const *test_case = &anon_thp_test_cases[i];
+
+ ksft_print_msg("[RUN] %s\n", test_case->desc);
+ do_run_with_thp(test_case->fn, THP_RUN_PMD);
+ }
+}
+
+static int tests_per_anon_thp_test_case(void)
+{
+ return thpsize ? 1 : 0;
+}
+
typedef void (*non_anon_test_fn)(char *mem, const char *smem, size_t size);
static void test_cow(char *mem, const char *smem, size_t size)
@@ -1518,6 +1747,7 @@ int main(int argc, char **argv)
ksft_print_header();
ksft_set_plan(ARRAY_SIZE(anon_test_cases) * tests_per_anon_test_case() +
+ ARRAY_SIZE(anon_thp_test_cases) * tests_per_anon_thp_test_case() +
ARRAY_SIZE(non_anon_test_cases) * tests_per_non_anon_test_case());
gup_fd = open("/sys/kernel/debug/gup_test", O_RDWR);
@@ -1526,6 +1756,7 @@ int main(int argc, char **argv)
ksft_exit_fail_msg("opening pagemap failed\n");
run_anon_test_cases();
+ run_anon_thp_test_cases();
run_non_anon_test_cases();
err = ksft_get_fail_cnt();
diff --git a/tools/testing/selftests/vm/gup_test.c b/tools/testing/selftests/mm/gup_test.c
index e43879291dac..e43879291dac 100644
--- a/tools/testing/selftests/vm/gup_test.c
+++ b/tools/testing/selftests/mm/gup_test.c
diff --git a/tools/testing/selftests/vm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c
index 4adaad1b822f..4adaad1b822f 100644
--- a/tools/testing/selftests/vm/hmm-tests.c
+++ b/tools/testing/selftests/mm/hmm-tests.c
diff --git a/tools/testing/selftests/vm/hugepage-mmap.c b/tools/testing/selftests/mm/hugepage-mmap.c
index 955ef87f382c..955ef87f382c 100644
--- a/tools/testing/selftests/vm/hugepage-mmap.c
+++ b/tools/testing/selftests/mm/hugepage-mmap.c
diff --git a/tools/testing/selftests/vm/hugepage-mremap.c b/tools/testing/selftests/mm/hugepage-mremap.c
index e53b5eaa8fce..e53b5eaa8fce 100644
--- a/tools/testing/selftests/vm/hugepage-mremap.c
+++ b/tools/testing/selftests/mm/hugepage-mremap.c
diff --git a/tools/testing/selftests/vm/hugepage-shm.c b/tools/testing/selftests/mm/hugepage-shm.c
index e2527f32005b..e2527f32005b 100644
--- a/tools/testing/selftests/vm/hugepage-shm.c
+++ b/tools/testing/selftests/mm/hugepage-shm.c
diff --git a/tools/testing/selftests/vm/hugepage-vmemmap.c b/tools/testing/selftests/mm/hugepage-vmemmap.c
index 557bdbd4f87e..557bdbd4f87e 100644
--- a/tools/testing/selftests/vm/hugepage-vmemmap.c
+++ b/tools/testing/selftests/mm/hugepage-vmemmap.c
diff --git a/tools/testing/selftests/vm/hugetlb-madvise.c b/tools/testing/selftests/mm/hugetlb-madvise.c
index 9a127a8fe176..9a127a8fe176 100644
--- a/tools/testing/selftests/vm/hugetlb-madvise.c
+++ b/tools/testing/selftests/mm/hugetlb-madvise.c
diff --git a/tools/testing/selftests/vm/hugetlb_reparenting_test.sh b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh
index bf2d2a684edf..bf2d2a684edf 100644
--- a/tools/testing/selftests/vm/hugetlb_reparenting_test.sh
+++ b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh
diff --git a/tools/testing/selftests/vm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c
index 64126c8cd561..64126c8cd561 100644
--- a/tools/testing/selftests/vm/khugepaged.c
+++ b/tools/testing/selftests/mm/khugepaged.c
diff --git a/tools/testing/selftests/vm/ksm_functional_tests.c b/tools/testing/selftests/mm/ksm_functional_tests.c
index b11b7e5115dc..d8b5b4930412 100644
--- a/tools/testing/selftests/vm/ksm_functional_tests.c
+++ b/tools/testing/selftests/mm/ksm_functional_tests.c
@@ -37,7 +37,7 @@ static bool range_maps_duplicates(char *addr, unsigned long size)
/*
* There is no easy way to check if there are KSM pages mapped into
* this range. We only check that the range does not map the same PFN
- * twice by comaring each pair of mapped pages.
+ * twice by comparing each pair of mapped pages.
*/
for (offs_a = 0; offs_a < size; offs_a += pagesize) {
pfn_a = pagemap_get_pfn(pagemap_fd, addr + offs_a);
diff --git a/tools/testing/selftests/vm/ksm_tests.c b/tools/testing/selftests/mm/ksm_tests.c
index f9eb4d67e0dd..f9eb4d67e0dd 100644
--- a/tools/testing/selftests/vm/ksm_tests.c
+++ b/tools/testing/selftests/mm/ksm_tests.c
diff --git a/tools/testing/selftests/vm/madv_populate.c b/tools/testing/selftests/mm/madv_populate.c
index 262eae6b58f2..262eae6b58f2 100644
--- a/tools/testing/selftests/vm/madv_populate.c
+++ b/tools/testing/selftests/mm/madv_populate.c
diff --git a/tools/testing/selftests/vm/map_fixed_noreplace.c b/tools/testing/selftests/mm/map_fixed_noreplace.c
index eed44322d1a6..eed44322d1a6 100644
--- a/tools/testing/selftests/vm/map_fixed_noreplace.c
+++ b/tools/testing/selftests/mm/map_fixed_noreplace.c
diff --git a/tools/testing/selftests/vm/map_hugetlb.c b/tools/testing/selftests/mm/map_hugetlb.c
index 312889edb84a..312889edb84a 100644
--- a/tools/testing/selftests/vm/map_hugetlb.c
+++ b/tools/testing/selftests/mm/map_hugetlb.c
diff --git a/tools/testing/selftests/vm/map_populate.c b/tools/testing/selftests/mm/map_populate.c
index 6b8aeaa0bf7a..6b8aeaa0bf7a 100644
--- a/tools/testing/selftests/vm/map_populate.c
+++ b/tools/testing/selftests/mm/map_populate.c
diff --git a/tools/testing/selftests/mm/mdwe_test.c b/tools/testing/selftests/mm/mdwe_test.c
new file mode 100644
index 000000000000..bc91bef5d254
--- /dev/null
+++ b/tools/testing/selftests/mm/mdwe_test.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifdef __aarch64__
+#include <asm/hwcap.h>
+#endif
+
+#include <linux/mman.h>
+#include <linux/prctl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/auxv.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../kselftest_harness.h"
+
+#ifndef __aarch64__
+# define PROT_BTI 0
+#endif
+
+TEST(prctl_flags)
+{
+ EXPECT_LT(prctl(PR_SET_MDWE, 7L, 0L, 0L, 0L), 0);
+ EXPECT_LT(prctl(PR_SET_MDWE, 0L, 7L, 0L, 0L), 0);
+ EXPECT_LT(prctl(PR_SET_MDWE, 0L, 0L, 7L, 0L), 0);
+ EXPECT_LT(prctl(PR_SET_MDWE, 0L, 0L, 0L, 7L), 0);
+
+ EXPECT_LT(prctl(PR_GET_MDWE, 7L, 0L, 0L, 0L), 0);
+ EXPECT_LT(prctl(PR_GET_MDWE, 0L, 7L, 0L, 0L), 0);
+ EXPECT_LT(prctl(PR_GET_MDWE, 0L, 0L, 7L, 0L), 0);
+ EXPECT_LT(prctl(PR_GET_MDWE, 0L, 0L, 0L, 7L), 0);
+}
+
+FIXTURE(mdwe)
+{
+ void *p;
+ int flags;
+ size_t size;
+ pid_t pid;
+};
+
+FIXTURE_VARIANT(mdwe)
+{
+ bool enabled;
+ bool forked;
+};
+
+FIXTURE_VARIANT_ADD(mdwe, stock)
+{
+ .enabled = false,
+ .forked = false,
+};
+
+FIXTURE_VARIANT_ADD(mdwe, enabled)
+{
+ .enabled = true,
+ .forked = false,
+};
+
+FIXTURE_VARIANT_ADD(mdwe, forked)
+{
+ .enabled = true,
+ .forked = true,
+};
+
+FIXTURE_SETUP(mdwe)
+{
+ int ret, status;
+
+ self->p = NULL;
+ self->flags = MAP_SHARED | MAP_ANONYMOUS;
+ self->size = getpagesize();
+
+ if (!variant->enabled)
+ return;
+
+ ret = prctl(PR_SET_MDWE, PR_MDWE_REFUSE_EXEC_GAIN, 0L, 0L, 0L);
+ ASSERT_EQ(ret, 0) {
+ TH_LOG("PR_SET_MDWE failed or unsupported");
+ }
+
+ ret = prctl(PR_GET_MDWE, 0L, 0L, 0L, 0L);
+ ASSERT_EQ(ret, 1);
+
+ if (variant->forked) {
+ self->pid = fork();
+ ASSERT_GE(self->pid, 0) {
+ TH_LOG("fork failed\n");
+ }
+
+ if (self->pid > 0) {
+ ret = waitpid(self->pid, &status, 0);
+ ASSERT_TRUE(WIFEXITED(status));
+ exit(WEXITSTATUS(status));
+ }
+ }
+}
+
+FIXTURE_TEARDOWN(mdwe)
+{
+ if (self->p && self->p != MAP_FAILED)
+ munmap(self->p, self->size);
+}
+
+TEST_F(mdwe, mmap_READ_EXEC)
+{
+ self->p = mmap(NULL, self->size, PROT_READ | PROT_EXEC, self->flags, 0, 0);
+ EXPECT_NE(self->p, MAP_FAILED);
+}
+
+TEST_F(mdwe, mmap_WRITE_EXEC)
+{
+ self->p = mmap(NULL, self->size, PROT_WRITE | PROT_EXEC, self->flags, 0, 0);
+ if (variant->enabled) {
+ EXPECT_EQ(self->p, MAP_FAILED);
+ } else {
+ EXPECT_NE(self->p, MAP_FAILED);
+ }
+}
+
+TEST_F(mdwe, mprotect_stay_EXEC)
+{
+ int ret;
+
+ self->p = mmap(NULL, self->size, PROT_READ | PROT_EXEC, self->flags, 0, 0);
+ ASSERT_NE(self->p, MAP_FAILED);
+
+ ret = mprotect(self->p, self->size, PROT_READ | PROT_EXEC);
+ EXPECT_EQ(ret, 0);
+}
+
+TEST_F(mdwe, mprotect_add_EXEC)
+{
+ int ret;
+
+ self->p = mmap(NULL, self->size, PROT_READ, self->flags, 0, 0);
+ ASSERT_NE(self->p, MAP_FAILED);
+
+ ret = mprotect(self->p, self->size, PROT_READ | PROT_EXEC);
+ if (variant->enabled) {
+ EXPECT_LT(ret, 0);
+ } else {
+ EXPECT_EQ(ret, 0);
+ }
+}
+
+TEST_F(mdwe, mprotect_WRITE_EXEC)
+{
+ int ret;
+
+ self->p = mmap(NULL, self->size, PROT_WRITE, self->flags, 0, 0);
+ ASSERT_NE(self->p, MAP_FAILED);
+
+ ret = mprotect(self->p, self->size, PROT_WRITE | PROT_EXEC);
+ if (variant->enabled) {
+ EXPECT_LT(ret, 0);
+ } else {
+ EXPECT_EQ(ret, 0);
+ }
+}
+
+TEST_F(mdwe, mmap_FIXED)
+{
+ void *p;
+
+ self->p = mmap(NULL, self->size, PROT_READ, self->flags, 0, 0);
+ ASSERT_NE(self->p, MAP_FAILED);
+
+ p = mmap(self->p + self->size, self->size, PROT_READ | PROT_EXEC,
+ self->flags | MAP_FIXED, 0, 0);
+ if (variant->enabled) {
+ EXPECT_EQ(p, MAP_FAILED);
+ } else {
+ EXPECT_EQ(p, self->p);
+ }
+}
+
+TEST_F(mdwe, arm64_BTI)
+{
+ int ret;
+
+#ifdef __aarch64__
+ if (!(getauxval(AT_HWCAP2) & HWCAP2_BTI))
+#endif
+ SKIP(return, "HWCAP2_BTI not supported");
+
+ self->p = mmap(NULL, self->size, PROT_EXEC, self->flags, 0, 0);
+ ASSERT_NE(self->p, MAP_FAILED);
+
+ ret = mprotect(self->p, self->size, PROT_EXEC | PROT_BTI);
+ EXPECT_EQ(ret, 0);
+}
+
+TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/vm/memfd_secret.c b/tools/testing/selftests/mm/memfd_secret.c
index 957b9e18c729..957b9e18c729 100644
--- a/tools/testing/selftests/vm/memfd_secret.c
+++ b/tools/testing/selftests/mm/memfd_secret.c
diff --git a/tools/testing/selftests/vm/migration.c b/tools/testing/selftests/mm/migration.c
index 1cec8425e3ca..1cec8425e3ca 100644
--- a/tools/testing/selftests/vm/migration.c
+++ b/tools/testing/selftests/mm/migration.c
diff --git a/tools/testing/selftests/vm/mlock-random-test.c b/tools/testing/selftests/mm/mlock-random-test.c
index 782ea94dee2f..782ea94dee2f 100644
--- a/tools/testing/selftests/vm/mlock-random-test.c
+++ b/tools/testing/selftests/mm/mlock-random-test.c
diff --git a/tools/testing/selftests/vm/mlock2-tests.c b/tools/testing/selftests/mm/mlock2-tests.c
index 11b2301f3aa3..11b2301f3aa3 100644
--- a/tools/testing/selftests/vm/mlock2-tests.c
+++ b/tools/testing/selftests/mm/mlock2-tests.c
diff --git a/tools/testing/selftests/vm/mlock2.h b/tools/testing/selftests/mm/mlock2.h
index 2a6e76c226bc..2a6e76c226bc 100644
--- a/tools/testing/selftests/vm/mlock2.h
+++ b/tools/testing/selftests/mm/mlock2.h
diff --git a/tools/testing/selftests/vm/mrelease_test.c b/tools/testing/selftests/mm/mrelease_test.c
index 6c62966ab5db..6c62966ab5db 100644
--- a/tools/testing/selftests/vm/mrelease_test.c
+++ b/tools/testing/selftests/mm/mrelease_test.c
diff --git a/tools/testing/selftests/vm/mremap_dontunmap.c b/tools/testing/selftests/mm/mremap_dontunmap.c
index f01dc4a85b0b..f01dc4a85b0b 100644
--- a/tools/testing/selftests/vm/mremap_dontunmap.c
+++ b/tools/testing/selftests/mm/mremap_dontunmap.c
diff --git a/tools/testing/selftests/vm/mremap_test.c b/tools/testing/selftests/mm/mremap_test.c
index 9496346973d4..5c3773de9f0f 100644
--- a/tools/testing/selftests/vm/mremap_test.c
+++ b/tools/testing/selftests/mm/mremap_test.c
@@ -119,47 +119,109 @@ static unsigned long long get_mmap_min_addr(void)
}
/*
- * This test validates that merge is called when expanding a mapping.
- * Mapping containing three pages is created, middle page is unmapped
- * and then the mapping containing the first page is expanded so that
- * it fills the created hole. The two parts should merge creating
- * single mapping with three pages.
+ * Using /proc/self/maps, assert that the specified address range is contained
+ * within a single mapping.
*/
-static void mremap_expand_merge(unsigned long page_size)
+static bool is_range_mapped(FILE *maps_fp, void *start, void *end)
{
- char *test_name = "mremap expand merge";
- FILE *fp;
char *line = NULL;
size_t len = 0;
bool success = false;
- char *start = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-
- munmap(start + page_size, page_size);
- mremap(start, page_size, 2 * page_size, 0);
- fp = fopen("/proc/self/maps", "r");
- if (fp == NULL) {
- ksft_test_result_fail("%s\n", test_name);
- return;
- }
+ rewind(maps_fp);
- while (getline(&line, &len, fp) != -1) {
+ while (getline(&line, &len, maps_fp) != -1) {
char *first = strtok(line, "- ");
void *first_val = (void *)strtol(first, NULL, 16);
char *second = strtok(NULL, "- ");
void *second_val = (void *) strtol(second, NULL, 16);
- if (first_val == start && second_val == start + 3 * page_size) {
+ if (first_val <= start && second_val >= end) {
success = true;
break;
}
}
+
+ return success;
+}
+
+/*
+ * This test validates that merge is called when expanding a mapping.
+ * Mapping containing three pages is created, middle page is unmapped
+ * and then the mapping containing the first page is expanded so that
+ * it fills the created hole. The two parts should merge creating
+ * single mapping with three pages.
+ */
+static void mremap_expand_merge(FILE *maps_fp, unsigned long page_size)
+{
+ char *test_name = "mremap expand merge";
+ bool success = false;
+ char *remap, *start;
+
+ start = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ if (start == MAP_FAILED) {
+ ksft_print_msg("mmap failed: %s\n", strerror(errno));
+ goto out;
+ }
+
+ munmap(start + page_size, page_size);
+ remap = mremap(start, page_size, 2 * page_size, 0);
+ if (remap == MAP_FAILED) {
+ ksft_print_msg("mremap failed: %s\n", strerror(errno));
+ munmap(start, page_size);
+ munmap(start + 2 * page_size, page_size);
+ goto out;
+ }
+
+ success = is_range_mapped(maps_fp, start, start + 3 * page_size);
+ munmap(start, 3 * page_size);
+
+out:
+ if (success)
+ ksft_test_result_pass("%s\n", test_name);
+ else
+ ksft_test_result_fail("%s\n", test_name);
+}
+
+/*
+ * Similar to mremap_expand_merge() except instead of removing the middle page,
+ * we remove the last then attempt to remap offset from the second page. This
+ * should result in the mapping being restored to its former state.
+ */
+static void mremap_expand_merge_offset(FILE *maps_fp, unsigned long page_size)
+{
+
+ char *test_name = "mremap expand merge offset";
+ bool success = false;
+ char *remap, *start;
+
+ start = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ if (start == MAP_FAILED) {
+ ksft_print_msg("mmap failed: %s\n", strerror(errno));
+ goto out;
+ }
+
+ /* Unmap final page to ensure we have space to expand. */
+ munmap(start + 2 * page_size, page_size);
+ remap = mremap(start + page_size, page_size, 2 * page_size, 0);
+ if (remap == MAP_FAILED) {
+ ksft_print_msg("mremap failed: %s\n", strerror(errno));
+ munmap(start, 2 * page_size);
+ goto out;
+ }
+
+ success = is_range_mapped(maps_fp, start, start + 3 * page_size);
+ munmap(start, 3 * page_size);
+
+out:
if (success)
ksft_test_result_pass("%s\n", test_name);
else
ksft_test_result_fail("%s\n", test_name);
- fclose(fp);
}
/*
@@ -380,11 +442,12 @@ int main(int argc, char **argv)
int i, run_perf_tests;
unsigned int threshold_mb = VALIDATION_DEFAULT_THRESHOLD;
unsigned int pattern_seed;
- int num_expand_tests = 1;
+ int num_expand_tests = 2;
struct test test_cases[MAX_TEST];
struct test perf_test_cases[MAX_PERF_TEST];
int page_size;
time_t t;
+ FILE *maps_fp;
pattern_seed = (unsigned int) time(&t);
@@ -458,7 +521,17 @@ int main(int argc, char **argv)
run_mremap_test_case(test_cases[i], &failures, threshold_mb,
pattern_seed);
- mremap_expand_merge(page_size);
+ maps_fp = fopen("/proc/self/maps", "r");
+
+ if (maps_fp == NULL) {
+ ksft_print_msg("Failed to read /proc/self/maps: %s\n", strerror(errno));
+ exit(KSFT_FAIL);
+ }
+
+ mremap_expand_merge(maps_fp, page_size);
+ mremap_expand_merge_offset(maps_fp, page_size);
+
+ fclose(maps_fp);
if (run_perf_tests) {
ksft_print_msg("\n%s\n",
diff --git a/tools/testing/selftests/vm/on-fault-limit.c b/tools/testing/selftests/mm/on-fault-limit.c
index 634d87dfb2a4..634d87dfb2a4 100644
--- a/tools/testing/selftests/vm/on-fault-limit.c
+++ b/tools/testing/selftests/mm/on-fault-limit.c
diff --git a/tools/testing/selftests/vm/pkey-helpers.h b/tools/testing/selftests/mm/pkey-helpers.h
index 92f3be3dd8e5..92f3be3dd8e5 100644
--- a/tools/testing/selftests/vm/pkey-helpers.h
+++ b/tools/testing/selftests/mm/pkey-helpers.h
diff --git a/tools/testing/selftests/vm/pkey-powerpc.h b/tools/testing/selftests/mm/pkey-powerpc.h
index 1ebb586b2fbc..1ebb586b2fbc 100644
--- a/tools/testing/selftests/vm/pkey-powerpc.h
+++ b/tools/testing/selftests/mm/pkey-powerpc.h
diff --git a/tools/testing/selftests/vm/pkey-x86.h b/tools/testing/selftests/mm/pkey-x86.h
index 72c14cd3ddc7..72c14cd3ddc7 100644
--- a/tools/testing/selftests/vm/pkey-x86.h
+++ b/tools/testing/selftests/mm/pkey-x86.h
diff --git a/tools/testing/selftests/vm/protection_keys.c b/tools/testing/selftests/mm/protection_keys.c
index 95f403a0c46d..95f403a0c46d 100644
--- a/tools/testing/selftests/vm/protection_keys.c
+++ b/tools/testing/selftests/mm/protection_keys.c
diff --git a/tools/testing/selftests/vm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
index 8984e0bb58c7..8984e0bb58c7 100755..100644
--- a/tools/testing/selftests/vm/run_vmtests.sh
+++ b/tools/testing/selftests/mm/run_vmtests.sh
diff --git a/tools/testing/selftests/vm/settings b/tools/testing/selftests/mm/settings
index 9abfc60e9e6f..9abfc60e9e6f 100644
--- a/tools/testing/selftests/vm/settings
+++ b/tools/testing/selftests/mm/settings
diff --git a/tools/testing/selftests/vm/soft-dirty.c b/tools/testing/selftests/mm/soft-dirty.c
index 21d8830c5f24..21d8830c5f24 100644
--- a/tools/testing/selftests/vm/soft-dirty.c
+++ b/tools/testing/selftests/mm/soft-dirty.c
diff --git a/tools/testing/selftests/vm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c
index 76e1c36dd9e5..76e1c36dd9e5 100644
--- a/tools/testing/selftests/vm/split_huge_page_test.c
+++ b/tools/testing/selftests/mm/split_huge_page_test.c
diff --git a/tools/testing/selftests/vm/test_hmm.sh b/tools/testing/selftests/mm/test_hmm.sh
index 46e19b5d648d..46e19b5d648d 100755..100644
--- a/tools/testing/selftests/vm/test_hmm.sh
+++ b/tools/testing/selftests/mm/test_hmm.sh
diff --git a/tools/testing/selftests/vm/test_vmalloc.sh b/tools/testing/selftests/mm/test_vmalloc.sh
index d73b846736f1..d73b846736f1 100755..100644
--- a/tools/testing/selftests/vm/test_vmalloc.sh
+++ b/tools/testing/selftests/mm/test_vmalloc.sh
diff --git a/tools/testing/selftests/vm/thuge-gen.c b/tools/testing/selftests/mm/thuge-gen.c
index 361ef7192cc6..361ef7192cc6 100644
--- a/tools/testing/selftests/vm/thuge-gen.c
+++ b/tools/testing/selftests/mm/thuge-gen.c
diff --git a/tools/testing/selftests/vm/transhuge-stress.c b/tools/testing/selftests/mm/transhuge-stress.c
index e3f00adb1b82..e3f00adb1b82 100644
--- a/tools/testing/selftests/vm/transhuge-stress.c
+++ b/tools/testing/selftests/mm/transhuge-stress.c
diff --git a/tools/testing/selftests/vm/userfaultfd.c b/tools/testing/selftests/mm/userfaultfd.c
index 7f22844ed704..7f22844ed704 100644
--- a/tools/testing/selftests/vm/userfaultfd.c
+++ b/tools/testing/selftests/mm/userfaultfd.c
diff --git a/tools/testing/selftests/vm/util.h b/tools/testing/selftests/mm/util.h
index b27d26199334..b27d26199334 100644
--- a/tools/testing/selftests/vm/util.h
+++ b/tools/testing/selftests/mm/util.h
diff --git a/tools/testing/selftests/vm/va_128TBswitch.c b/tools/testing/selftests/mm/va_128TBswitch.c
index 1d2068989883..1d2068989883 100644
--- a/tools/testing/selftests/vm/va_128TBswitch.c
+++ b/tools/testing/selftests/mm/va_128TBswitch.c
diff --git a/tools/testing/selftests/vm/va_128TBswitch.sh b/tools/testing/selftests/mm/va_128TBswitch.sh
index 41580751dc51..41580751dc51 100755..100644
--- a/tools/testing/selftests/vm/va_128TBswitch.sh
+++ b/tools/testing/selftests/mm/va_128TBswitch.sh
diff --git a/tools/testing/selftests/vm/virtual_address_range.c b/tools/testing/selftests/mm/virtual_address_range.c
index c0592646ed93..c0592646ed93 100644
--- a/tools/testing/selftests/vm/virtual_address_range.c
+++ b/tools/testing/selftests/mm/virtual_address_range.c
diff --git a/tools/testing/selftests/vm/vm_util.c b/tools/testing/selftests/mm/vm_util.c
index 40e795624ff3..40e795624ff3 100644
--- a/tools/testing/selftests/vm/vm_util.c
+++ b/tools/testing/selftests/mm/vm_util.c
diff --git a/tools/testing/selftests/vm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
index 1995ee911ef2..1995ee911ef2 100644
--- a/tools/testing/selftests/vm/vm_util.h
+++ b/tools/testing/selftests/mm/vm_util.h
diff --git a/tools/testing/selftests/vm/write_hugetlb_memory.sh b/tools/testing/selftests/mm/write_hugetlb_memory.sh
index 70a02301f4c2..70a02301f4c2 100644
--- a/tools/testing/selftests/vm/write_hugetlb_memory.sh
+++ b/tools/testing/selftests/mm/write_hugetlb_memory.sh
diff --git a/tools/testing/selftests/vm/write_to_hugetlbfs.c b/tools/testing/selftests/mm/write_to_hugetlbfs.c
index 6a2caba19ee1..6a2caba19ee1 100644
--- a/tools/testing/selftests/vm/write_to_hugetlbfs.c
+++ b/tools/testing/selftests/mm/write_to_hugetlbfs.c
diff --git a/tools/testing/selftests/mount_setattr/Makefile b/tools/testing/selftests/mount_setattr/Makefile
index 2250f7dcb81e..0c0d7b1234c1 100644
--- a/tools/testing/selftests/mount_setattr/Makefile
+++ b/tools/testing/selftests/mount_setattr/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
# Makefile for mount selftests.
-CFLAGS = -g -I../../../../usr/include/ -Wall -O2 -pthread
+CFLAGS = -g $(KHDR_INCLUDES) -Wall -O2 -pthread
-TEST_GEN_FILES += mount_setattr_test
+TEST_GEN_PROGS := mount_setattr_test
include ../lib.mk
diff --git a/tools/testing/selftests/mount_setattr/mount_setattr_test.c b/tools/testing/selftests/mount_setattr/mount_setattr_test.c
index 8c5fea68ae67..c6a8c732b802 100644
--- a/tools/testing/selftests/mount_setattr/mount_setattr_test.c
+++ b/tools/testing/selftests/mount_setattr/mount_setattr_test.c
@@ -18,6 +18,7 @@
#include <grp.h>
#include <stdbool.h>
#include <stdarg.h>
+#include <linux/mount.h>
#include "../kselftest_harness.h"
@@ -103,13 +104,6 @@
#else
#define __NR_mount_setattr 442
#endif
-
-struct mount_attr {
- __u64 attr_set;
- __u64 attr_clr;
- __u64 propagation;
- __u64 userns_fd;
-};
#endif
#ifndef __NR_open_tree
diff --git a/tools/testing/selftests/move_mount_set_group/Makefile b/tools/testing/selftests/move_mount_set_group/Makefile
index 80c2d86812b0..94235846b6f9 100644
--- a/tools/testing/selftests/move_mount_set_group/Makefile
+++ b/tools/testing/selftests/move_mount_set_group/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
# Makefile for mount selftests.
-CFLAGS = -g -I../../../../usr/include/ -Wall -O2
+CFLAGS = -g $(KHDR_INCLUDES) -Wall -O2
TEST_GEN_FILES += move_mount_set_group_test
diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore
index a6911cae368c..80f06aa62034 100644
--- a/tools/testing/selftests/net/.gitignore
+++ b/tools/testing/selftests/net/.gitignore
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
bind_bhash
bind_timewait
+bind_wildcard
csum
cmsg_sender
diag_uid
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 6cd8993454d7..80fbfe0330f6 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -80,6 +80,7 @@ TEST_GEN_FILES += sctp_hello
TEST_GEN_FILES += csum
TEST_GEN_FILES += nat6to4.o
TEST_GEN_FILES += ip_local_port_range
+TEST_GEN_FILES += bind_wildcard
TEST_FILES := settings
diff --git a/tools/testing/selftests/net/bind_wildcard.c b/tools/testing/selftests/net/bind_wildcard.c
new file mode 100644
index 000000000000..58edfc15d28b
--- /dev/null
+++ b/tools/testing/selftests/net/bind_wildcard.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright Amazon.com Inc. or its affiliates. */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "../kselftest_harness.h"
+
+FIXTURE(bind_wildcard)
+{
+ struct sockaddr_in addr4;
+ struct sockaddr_in6 addr6;
+ int expected_errno;
+};
+
+FIXTURE_VARIANT(bind_wildcard)
+{
+ const __u32 addr4_const;
+ const struct in6_addr *addr6_const;
+};
+
+FIXTURE_VARIANT_ADD(bind_wildcard, v4_any_v6_any)
+{
+ .addr4_const = INADDR_ANY,
+ .addr6_const = &in6addr_any,
+};
+
+FIXTURE_VARIANT_ADD(bind_wildcard, v4_any_v6_local)
+{
+ .addr4_const = INADDR_ANY,
+ .addr6_const = &in6addr_loopback,
+};
+
+FIXTURE_VARIANT_ADD(bind_wildcard, v4_local_v6_any)
+{
+ .addr4_const = INADDR_LOOPBACK,
+ .addr6_const = &in6addr_any,
+};
+
+FIXTURE_VARIANT_ADD(bind_wildcard, v4_local_v6_local)
+{
+ .addr4_const = INADDR_LOOPBACK,
+ .addr6_const = &in6addr_loopback,
+};
+
+FIXTURE_SETUP(bind_wildcard)
+{
+ self->addr4.sin_family = AF_INET;
+ self->addr4.sin_port = htons(0);
+ self->addr4.sin_addr.s_addr = htonl(variant->addr4_const);
+
+ self->addr6.sin6_family = AF_INET6;
+ self->addr6.sin6_port = htons(0);
+ self->addr6.sin6_addr = *variant->addr6_const;
+
+ if (variant->addr6_const == &in6addr_any)
+ self->expected_errno = EADDRINUSE;
+ else
+ self->expected_errno = 0;
+}
+
+FIXTURE_TEARDOWN(bind_wildcard)
+{
+}
+
+void bind_sockets(struct __test_metadata *_metadata,
+ FIXTURE_DATA(bind_wildcard) *self,
+ struct sockaddr *addr1, socklen_t addrlen1,
+ struct sockaddr *addr2, socklen_t addrlen2)
+{
+ int fd[2];
+ int ret;
+
+ fd[0] = socket(addr1->sa_family, SOCK_STREAM, 0);
+ ASSERT_GT(fd[0], 0);
+
+ ret = bind(fd[0], addr1, addrlen1);
+ ASSERT_EQ(ret, 0);
+
+ ret = getsockname(fd[0], addr1, &addrlen1);
+ ASSERT_EQ(ret, 0);
+
+ ((struct sockaddr_in *)addr2)->sin_port = ((struct sockaddr_in *)addr1)->sin_port;
+
+ fd[1] = socket(addr2->sa_family, SOCK_STREAM, 0);
+ ASSERT_GT(fd[1], 0);
+
+ ret = bind(fd[1], addr2, addrlen2);
+ if (self->expected_errno) {
+ ASSERT_EQ(ret, -1);
+ ASSERT_EQ(errno, self->expected_errno);
+ } else {
+ ASSERT_EQ(ret, 0);
+ }
+
+ close(fd[1]);
+ close(fd[0]);
+}
+
+TEST_F(bind_wildcard, v4_v6)
+{
+ bind_sockets(_metadata, self,
+ (struct sockaddr *)&self->addr4, sizeof(self->addr6),
+ (struct sockaddr *)&self->addr6, sizeof(self->addr6));
+}
+
+TEST_F(bind_wildcard, v6_v4)
+{
+ bind_sockets(_metadata, self,
+ (struct sockaddr *)&self->addr6, sizeof(self->addr6),
+ (struct sockaddr *)&self->addr4, sizeof(self->addr4));
+}
+
+TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/net/devlink_port_split.py b/tools/testing/selftests/net/devlink_port_split.py
index 2b5d6ff87373..2d84c7a0be6b 100755
--- a/tools/testing/selftests/net/devlink_port_split.py
+++ b/tools/testing/selftests/net/devlink_port_split.py
@@ -59,6 +59,8 @@ class devlink_ports(object):
assert stderr == ""
ports = json.loads(stdout)['port']
+ validate_devlink_output(ports, 'flavour')
+
for port in ports:
if dev in port:
if ports[port]['flavour'] == 'physical':
@@ -220,6 +222,27 @@ def split_splittable_port(port, k, lanes, dev):
unsplit(port.bus_info)
+def validate_devlink_output(devlink_data, target_property=None):
+ """
+ Determine if test should be skipped by checking:
+ 1. devlink_data contains values
+ 2. The target_property exist in devlink_data
+ """
+ skip_reason = None
+ if any(devlink_data.values()):
+ if target_property:
+ skip_reason = "{} not found in devlink output, test skipped".format(target_property)
+ for key in devlink_data:
+ if target_property in devlink_data[key]:
+ skip_reason = None
+ else:
+ skip_reason = 'devlink output is empty, test skipped'
+
+ if skip_reason:
+ print(skip_reason)
+ sys.exit(KSFT_SKIP)
+
+
def make_parser():
parser = argparse.ArgumentParser(description='A test for port splitting.')
parser.add_argument('--dev',
@@ -240,12 +263,9 @@ def main(cmdline=None):
stdout, stderr = run_command(cmd)
assert stderr == ""
+ validate_devlink_output(json.loads(stdout))
devs = json.loads(stdout)['dev']
- if devs:
- dev = list(devs.keys())[0]
- else:
- print("no devlink device was found, test skipped")
- sys.exit(KSFT_SKIP)
+ dev = list(devs.keys())[0]
cmd = "devlink dev show %s" % dev
stdout, stderr = run_command(cmd)
@@ -255,6 +275,7 @@ def main(cmdline=None):
ports = devlink_ports(dev)
+ found_max_lanes = False
for port in ports.if_names:
max_lanes = get_max_lanes(port.name)
@@ -277,6 +298,11 @@ def main(cmdline=None):
split_splittable_port(port, lane, max_lanes, dev)
lane //= 2
+ found_max_lanes = True
+
+ if not found_max_lanes:
+ print(f"Test not started, no port of device {dev} reports max_lanes")
+ sys.exit(KSFT_SKIP)
if __name__ == "__main__":
diff --git a/tools/testing/selftests/net/fib_tests.sh b/tools/testing/selftests/net/fib_tests.sh
index 70ea8798b1f6..7da8ec838c63 100755
--- a/tools/testing/selftests/net/fib_tests.sh
+++ b/tools/testing/selftests/net/fib_tests.sh
@@ -9,7 +9,7 @@ ret=0
ksft_skip=4
# all tests in this script. Can be overridden with -t option
-TESTS="unregister down carrier nexthop suppress ipv6_rt ipv4_rt ipv6_addr_metric ipv4_addr_metric ipv6_route_metrics ipv4_route_metrics ipv4_route_v6_gw rp_filter ipv4_del_addr ipv4_mangle ipv6_mangle ipv4_bcast_neigh"
+TESTS="unregister down carrier nexthop suppress ipv6_notify ipv4_notify ipv6_rt ipv4_rt ipv6_addr_metric ipv4_addr_metric ipv6_route_metrics ipv4_route_metrics ipv4_route_v6_gw rp_filter ipv4_del_addr ipv4_mangle ipv6_mangle ipv4_bcast_neigh"
VERBOSE=0
PAUSE_ON_FAIL=no
@@ -655,6 +655,98 @@ fib_nexthop_test()
cleanup
}
+fib6_notify_test()
+{
+ setup
+
+ echo
+ echo "Fib6 info length calculation in route notify test"
+ set -e
+
+ for i in 10 20 30 40 50 60 70;
+ do
+ $IP link add dummy_$i type dummy
+ $IP link set dev dummy_$i up
+ $IP -6 address add 2001:$i::1/64 dev dummy_$i
+ done
+
+ $NS_EXEC ip monitor route &> errors.txt &
+ sleep 2
+
+ $IP -6 route add 2001::/64 \
+ nexthop via 2001:10::2 dev dummy_10 \
+ nexthop encap ip6 dst 2002::20 via 2001:20::2 dev dummy_20 \
+ nexthop encap ip6 dst 2002::30 via 2001:30::2 dev dummy_30 \
+ nexthop encap ip6 dst 2002::40 via 2001:40::2 dev dummy_40 \
+ nexthop encap ip6 dst 2002::50 via 2001:50::2 dev dummy_50 \
+ nexthop encap ip6 dst 2002::60 via 2001:60::2 dev dummy_60 \
+ nexthop encap ip6 dst 2002::70 via 2001:70::2 dev dummy_70
+
+ set +e
+
+ err=`cat errors.txt |grep "Message too long"`
+ if [ -z "$err" ];then
+ ret=0
+ else
+ ret=1
+ fi
+
+ log_test $ret 0 "ipv6 route add notify"
+
+ { kill %% && wait %%; } 2>/dev/null
+
+ #rm errors.txt
+
+ cleanup &> /dev/null
+}
+
+
+fib_notify_test()
+{
+ setup
+
+ echo
+ echo "Fib4 info length calculation in route notify test"
+
+ set -e
+
+ for i in 10 20 30 40 50 60 70;
+ do
+ $IP link add dummy_$i type dummy
+ $IP link set dev dummy_$i up
+ $IP address add 20.20.$i.2/24 dev dummy_$i
+ done
+
+ $NS_EXEC ip monitor route &> errors.txt &
+ sleep 2
+
+ $IP route add 10.0.0.0/24 \
+ nexthop via 20.20.10.1 dev dummy_10 \
+ nexthop encap ip dst 192.168.10.20 via 20.20.20.1 dev dummy_20 \
+ nexthop encap ip dst 192.168.10.30 via 20.20.30.1 dev dummy_30 \
+ nexthop encap ip dst 192.168.10.40 via 20.20.40.1 dev dummy_40 \
+ nexthop encap ip dst 192.168.10.50 via 20.20.50.1 dev dummy_50 \
+ nexthop encap ip dst 192.168.10.60 via 20.20.60.1 dev dummy_60 \
+ nexthop encap ip dst 192.168.10.70 via 20.20.70.1 dev dummy_70
+
+ set +e
+
+ err=`cat errors.txt |grep "Message too long"`
+ if [ -z "$err" ];then
+ ret=0
+ else
+ ret=1
+ fi
+
+ log_test $ret 0 "ipv4 route add notify"
+
+ { kill %% && wait %%; } 2>/dev/null
+
+ rm errors.txt
+
+ cleanup &> /dev/null
+}
+
fib_suppress_test()
{
echo
@@ -2111,6 +2203,8 @@ do
fib_carrier_test|carrier) fib_carrier_test;;
fib_rp_filter_test|rp_filter) fib_rp_filter_test;;
fib_nexthop_test|nexthop) fib_nexthop_test;;
+ fib_notify_test|ipv4_notify) fib_notify_test;;
+ fib6_notify_test|ipv6_notify) fib6_notify_test;;
fib_suppress_test|suppress) fib_suppress_test;;
ipv6_route_test|ipv6_rt) ipv6_route_test;;
ipv4_route_test|ipv4_rt) ipv4_route_test;;
diff --git a/tools/testing/selftests/net/mptcp/userspace_pm.sh b/tools/testing/selftests/net/mptcp/userspace_pm.sh
index 66c5be25c13d..48e52f995a98 100755
--- a/tools/testing/selftests/net/mptcp/userspace_pm.sh
+++ b/tools/testing/selftests/net/mptcp/userspace_pm.sh
@@ -240,7 +240,7 @@ check_expected_one()
fi
stdbuf -o0 -e0 printf "\tExpected value for '%s': '%s', got '%s'.\n" \
- "${var}" "${!var}" "${!exp}"
+ "${var}" "${!exp}" "${!var}"
return 1
}
diff --git a/tools/testing/selftests/net/rps_default_mask.sh b/tools/testing/selftests/net/rps_default_mask.sh
index 0fd0d2db3abc..a26c5624429f 100755
--- a/tools/testing/selftests/net/rps_default_mask.sh
+++ b/tools/testing/selftests/net/rps_default_mask.sh
@@ -60,6 +60,7 @@ ip link set dev $VETH up
ip -n $NETNS link set dev $VETH up
chk_rps "changing rps_default_mask affect newly created devices" "" $VETH 3
chk_rps "changing rps_default_mask don't affect newly child netns[II]" $NETNS $VETH 0
+ip link del dev $VETH
ip netns del $NETNS
setup
diff --git a/tools/testing/selftests/netfilter/nft_nat.sh b/tools/testing/selftests/netfilter/nft_nat.sh
index 924ecb3f1f73..dd40d9f6f259 100755
--- a/tools/testing/selftests/netfilter/nft_nat.sh
+++ b/tools/testing/selftests/netfilter/nft_nat.sh
@@ -404,6 +404,8 @@ EOF
echo SERVER-$family | ip netns exec "$ns1" timeout 5 socat -u STDIN TCP-LISTEN:2000 &
sc_s=$!
+ sleep 1
+
result=$(ip netns exec "$ns0" timeout 1 socat TCP:$daddr:2000 STDOUT)
if [ "$result" = "SERVER-inet" ];then
diff --git a/tools/testing/selftests/netfilter/rpath.sh b/tools/testing/selftests/netfilter/rpath.sh
index f7311e66d219..5289c8447a41 100755
--- a/tools/testing/selftests/netfilter/rpath.sh
+++ b/tools/testing/selftests/netfilter/rpath.sh
@@ -62,10 +62,16 @@ ip -net "$ns1" a a fec0:42::2/64 dev v0 nodad
ip -net "$ns2" a a fec0:42::1/64 dev d0 nodad
# firewall matches to test
-[ -n "$iptables" ] && ip netns exec "$ns2" \
- "$iptables" -t raw -A PREROUTING -s 192.168.0.0/16 -m rpfilter
-[ -n "$ip6tables" ] && ip netns exec "$ns2" \
- "$ip6tables" -t raw -A PREROUTING -s fec0::/16 -m rpfilter
+[ -n "$iptables" ] && {
+ common='-t raw -A PREROUTING -s 192.168.0.0/16'
+ ip netns exec "$ns2" "$iptables" $common -m rpfilter
+ ip netns exec "$ns2" "$iptables" $common -m rpfilter --invert
+}
+[ -n "$ip6tables" ] && {
+ common='-t raw -A PREROUTING -s fec0::/16'
+ ip netns exec "$ns2" "$ip6tables" $common -m rpfilter
+ ip netns exec "$ns2" "$ip6tables" $common -m rpfilter --invert
+}
[ -n "$nft" ] && ip netns exec "$ns2" $nft -f - <<EOF
table inet t {
chain c {
@@ -89,6 +95,11 @@ ipt_zero_rule() { # (command)
[ -n "$1" ] || return 0
ip netns exec "$ns2" "$1" -t raw -vS | grep -q -- "-m rpfilter -c 0 0"
}
+ipt_zero_reverse_rule() { # (command)
+ [ -n "$1" ] || return 0
+ ip netns exec "$ns2" "$1" -t raw -vS | \
+ grep -q -- "-m rpfilter --invert -c 0 0"
+}
nft_zero_rule() { # (family)
[ -n "$nft" ] || return 0
ip netns exec "$ns2" "$nft" list chain inet t c | \
@@ -101,8 +112,7 @@ netns_ping() { # (netns, args...)
ip netns exec "$netns" ping -q -c 1 -W 1 "$@" >/dev/null
}
-testrun() {
- # clear counters first
+clear_counters() {
[ -n "$iptables" ] && ip netns exec "$ns2" "$iptables" -t raw -Z
[ -n "$ip6tables" ] && ip netns exec "$ns2" "$ip6tables" -t raw -Z
if [ -n "$nft" ]; then
@@ -111,6 +121,10 @@ testrun() {
ip netns exec "$ns2" $nft -s list table inet t;
) | ip netns exec "$ns2" $nft -f -
fi
+}
+
+testrun() {
+ clear_counters
# test 1: martian traffic should fail rpfilter matches
netns_ping "$ns1" -I v0 192.168.42.1 && \
@@ -120,9 +134,13 @@ testrun() {
ipt_zero_rule "$iptables" || die "iptables matched martian"
ipt_zero_rule "$ip6tables" || die "ip6tables matched martian"
+ ipt_zero_reverse_rule "$iptables" && die "iptables not matched martian"
+ ipt_zero_reverse_rule "$ip6tables" && die "ip6tables not matched martian"
nft_zero_rule ip || die "nft IPv4 matched martian"
nft_zero_rule ip6 || die "nft IPv6 matched martian"
+ clear_counters
+
# test 2: rpfilter match should pass for regular traffic
netns_ping "$ns1" 192.168.23.1 || \
die "regular ping 192.168.23.1 failed"
@@ -131,6 +149,8 @@ testrun() {
ipt_zero_rule "$iptables" && die "iptables match not effective"
ipt_zero_rule "$ip6tables" && die "ip6tables match not effective"
+ ipt_zero_reverse_rule "$iptables" || die "iptables match over-effective"
+ ipt_zero_reverse_rule "$ip6tables" || die "ip6tables match over-effective"
nft_zero_rule ip && die "nft IPv4 match not effective"
nft_zero_rule ip6 && die "nft IPv6 match not effective"
diff --git a/tools/testing/selftests/nolibc/Makefile b/tools/testing/selftests/nolibc/Makefile
index 22f1e1d73fa8..8fe61d3e3cce 100644
--- a/tools/testing/selftests/nolibc/Makefile
+++ b/tools/testing/selftests/nolibc/Makefile
@@ -14,21 +14,25 @@ endif
# kernel image names by architecture
IMAGE_i386 = arch/x86/boot/bzImage
+IMAGE_x86_64 = arch/x86/boot/bzImage
IMAGE_x86 = arch/x86/boot/bzImage
IMAGE_arm64 = arch/arm64/boot/Image
IMAGE_arm = arch/arm/boot/zImage
IMAGE_mips = vmlinuz
IMAGE_riscv = arch/riscv/boot/Image
+IMAGE_s390 = arch/s390/boot/bzImage
IMAGE = $(IMAGE_$(ARCH))
IMAGE_NAME = $(notdir $(IMAGE))
# default kernel configurations that appear to be usable
DEFCONFIG_i386 = defconfig
+DEFCONFIG_x86_64 = defconfig
DEFCONFIG_x86 = defconfig
DEFCONFIG_arm64 = defconfig
DEFCONFIG_arm = multi_v7_defconfig
DEFCONFIG_mips = malta_defconfig
DEFCONFIG_riscv = defconfig
+DEFCONFIG_s390 = defconfig
DEFCONFIG = $(DEFCONFIG_$(ARCH))
# optional tests to run (default = all)
@@ -36,20 +40,24 @@ TEST =
# QEMU_ARCH: arch names used by qemu
QEMU_ARCH_i386 = i386
+QEMU_ARCH_x86_64 = x86_64
QEMU_ARCH_x86 = x86_64
QEMU_ARCH_arm64 = aarch64
QEMU_ARCH_arm = arm
QEMU_ARCH_mips = mipsel # works with malta_defconfig
QEMU_ARCH_riscv = riscv64
+QEMU_ARCH_s390 = s390x
QEMU_ARCH = $(QEMU_ARCH_$(ARCH))
# QEMU_ARGS : some arch-specific args to pass to qemu
QEMU_ARGS_i386 = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(TEST:%=NOLIBC_TEST=%)"
+QEMU_ARGS_x86_64 = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(TEST:%=NOLIBC_TEST=%)"
QEMU_ARGS_x86 = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(TEST:%=NOLIBC_TEST=%)"
QEMU_ARGS_arm64 = -M virt -cpu cortex-a53 -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)"
QEMU_ARGS_arm = -M virt -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)"
QEMU_ARGS_mips = -M malta -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)"
QEMU_ARGS_riscv = -M virt -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
+QEMU_ARGS_s390 = -M s390-ccw-virtio -m 1G -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
QEMU_ARGS = $(QEMU_ARGS_$(ARCH))
# OUTPUT is only set when run from the main makefile, otherwise
@@ -62,7 +70,8 @@ else
Q=@
endif
-CFLAGS ?= -Os -fno-ident -fno-asynchronous-unwind-tables
+CFLAGS_s390 = -m64
+CFLAGS ?= -Os -fno-ident -fno-asynchronous-unwind-tables $(CFLAGS_$(ARCH))
LDFLAGS := -s
help:
@@ -71,6 +80,7 @@ help:
@echo " help this help"
@echo " sysroot create the nolibc sysroot here (uses \$$ARCH)"
@echo " nolibc-test build the executable (uses \$$CC and \$$CROSS_COMPILE)"
+ @echo " run-user runs the executable under QEMU (uses \$$ARCH, \$$TEST)"
@echo " initramfs prepare the initramfs with nolibc-test"
@echo " defconfig create a fresh new default config (uses \$$ARCH)"
@echo " kernel (re)build the kernel with the initramfs (uses \$$ARCH)"
@@ -104,6 +114,11 @@ nolibc-test: nolibc-test.c sysroot/$(ARCH)/include
$(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ \
-nostdlib -static -Isysroot/$(ARCH)/include $< -lgcc
+# qemu user-land test
+run-user: nolibc-test
+ $(Q)qemu-$(QEMU_ARCH) ./nolibc-test > "$(CURDIR)/run.out" || :
+ $(Q)grep -w FAIL "$(CURDIR)/run.out" && echo "See all results in $(CURDIR)/run.out" || echo "$$(grep -c ^[0-9].*OK $(CURDIR)/run.out) test(s) passed."
+
initramfs: nolibc-test
$(QUIET_MKDIR)mkdir -p initramfs
$(call QUIET_INSTALL, initramfs/init)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
index f14f5076fb6d..c4a0c915139c 100644
--- a/tools/testing/selftests/nolibc/nolibc-test.c
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -442,6 +442,35 @@ int test_getdents64(const char *dir)
return ret;
}
+static int test_getpagesize(void)
+{
+ long x = getpagesize();
+ int c;
+
+ if (x < 0)
+ return x;
+
+#if defined(__x86_64__) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__)
+ /*
+ * x86 family is always 4K page.
+ */
+ c = (x == 4096);
+#elif defined(__aarch64__)
+ /*
+ * Linux aarch64 supports three values of page size: 4K, 16K, and 64K
+ * which are selected at kernel compilation time.
+ */
+ c = (x == 4096 || x == (16 * 1024) || x == (64 * 1024));
+#else
+ /*
+ * Assuming other architectures must have at least 4K page.
+ */
+ c = (x >= 4096);
+#endif
+
+ return !c;
+}
+
/* Run syscall tests between IDs <min> and <max>.
* Return 0 on success, non-zero on failure.
*/
@@ -502,6 +531,7 @@ int run_syscall(int min, int max)
CASE_TEST(gettimeofday_bad2); EXPECT_SYSER(1, gettimeofday(NULL, (void *)1), -1, EFAULT); break;
CASE_TEST(gettimeofday_bad2); EXPECT_SYSER(1, gettimeofday(NULL, (void *)1), -1, EFAULT); break;
#endif
+ CASE_TEST(getpagesize); EXPECT_SYSZR(1, test_getpagesize()); break;
CASE_TEST(ioctl_tiocinq); EXPECT_SYSZR(1, ioctl(0, TIOCINQ, &tmp)); break;
CASE_TEST(ioctl_tiocinq); EXPECT_SYSZR(1, ioctl(0, TIOCINQ, &tmp)); break;
CASE_TEST(link_root1); EXPECT_SYSER(1, link("/", "/"), -1, EEXIST); break;
diff --git a/tools/testing/selftests/perf_events/Makefile b/tools/testing/selftests/perf_events/Makefile
index fcafa5f0d34c..db93c4ff081a 100644
--- a/tools/testing/selftests/perf_events/Makefile
+++ b/tools/testing/selftests/perf_events/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-CFLAGS += -Wl,-no-as-needed -Wall -I../../../../usr/include
+CFLAGS += -Wl,-no-as-needed -Wall $(KHDR_INCLUDES)
LDFLAGS += -lpthread
TEST_GEN_PROGS := sigtrap_threads remove_on_exec
diff --git a/tools/testing/selftests/pid_namespace/Makefile b/tools/testing/selftests/pid_namespace/Makefile
index edafaca1aeb3..9286a1d22cd3 100644
--- a/tools/testing/selftests/pid_namespace/Makefile
+++ b/tools/testing/selftests/pid_namespace/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-CFLAGS += -g -I../../../../usr/include/
+CFLAGS += -g $(KHDR_INCLUDES)
TEST_GEN_PROGS = regression_enomem
diff --git a/tools/testing/selftests/pidfd/Makefile b/tools/testing/selftests/pidfd/Makefile
index 778b6cdc8aed..d731e3e76d5b 100644
--- a/tools/testing/selftests/pidfd/Makefile
+++ b/tools/testing/selftests/pidfd/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-CFLAGS += -g -I../../../../usr/include/ -pthread -Wall
+CFLAGS += -g $(KHDR_INCLUDES) -pthread -Wall
TEST_GEN_PROGS := pidfd_test pidfd_fdinfo_test pidfd_open_test \
pidfd_poll_test pidfd_wait pidfd_getfd_test pidfd_setns_test
diff --git a/tools/testing/selftests/powerpc/dscr/dscr.h b/tools/testing/selftests/powerpc/dscr/dscr.h
index b703714e7d98..2c54998d4715 100644
--- a/tools/testing/selftests/powerpc/dscr/dscr.h
+++ b/tools/testing/selftests/powerpc/dscr/dscr.h
@@ -64,48 +64,26 @@ inline void set_dscr_usr(unsigned long val)
/* Default DSCR access */
unsigned long get_default_dscr(void)
{
- int fd = -1, ret;
- char buf[16];
+ int err;
unsigned long val;
- if (fd == -1) {
- fd = open(DSCR_DEFAULT, O_RDONLY);
- if (fd == -1) {
- perror("open() failed");
- exit(1);
- }
- }
- memset(buf, 0, sizeof(buf));
- lseek(fd, 0, SEEK_SET);
- ret = read(fd, buf, sizeof(buf));
- if (ret == -1) {
+ err = read_ulong(DSCR_DEFAULT, &val, 16);
+ if (err) {
perror("read() failed");
exit(1);
}
- sscanf(buf, "%lx", &val);
- close(fd);
return val;
}
void set_default_dscr(unsigned long val)
{
- int fd = -1, ret;
- char buf[16];
+ int err;
- if (fd == -1) {
- fd = open(DSCR_DEFAULT, O_RDWR);
- if (fd == -1) {
- perror("open() failed");
- exit(1);
- }
- }
- sprintf(buf, "%lx\n", val);
- ret = write(fd, buf, strlen(buf));
- if (ret == -1) {
+ err = write_ulong(DSCR_DEFAULT, val, 16);
+ if (err) {
perror("write() failed");
exit(1);
}
- close(fd);
}
double uniform_deviate(int seed)
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c b/tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c
index f20d1c166d1e..4f1fef6198fc 100644
--- a/tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c
+++ b/tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c
@@ -12,27 +12,16 @@
static int check_cpu_dscr_default(char *file, unsigned long val)
{
- char buf[10];
- int fd, rc;
+ unsigned long cpu_dscr;
+ int err;
- fd = open(file, O_RDWR);
- if (fd == -1) {
- perror("open() failed");
- return 1;
- }
-
- rc = read(fd, buf, sizeof(buf));
- if (rc == -1) {
- perror("read() failed");
- close(fd);
- return 1;
- }
- close(fd);
+ err = read_ulong(file, &cpu_dscr, 16);
+ if (err)
+ return err;
- buf[rc] = '\0';
- if (strtol(buf, NULL, 16) != val) {
+ if (cpu_dscr != val) {
printf("DSCR match failed: %ld (system) %ld (cpu)\n",
- val, strtol(buf, NULL, 16));
+ val, cpu_dscr);
return 1;
}
return 0;
diff --git a/tools/testing/selftests/powerpc/include/utils.h b/tools/testing/selftests/powerpc/include/utils.h
index e222a5858450..eed7dd7582b2 100644
--- a/tools/testing/selftests/powerpc/include/utils.h
+++ b/tools/testing/selftests/powerpc/include/utils.h
@@ -33,8 +33,24 @@ void *get_auxv_entry(int type);
int pick_online_cpu(void);
-int read_debugfs_file(char *debugfs_file, int *result);
-int write_debugfs_file(char *debugfs_file, int result);
+int parse_intmax(const char *buffer, size_t count, intmax_t *result, int base);
+int parse_uintmax(const char *buffer, size_t count, uintmax_t *result, int base);
+int parse_int(const char *buffer, size_t count, int *result, int base);
+int parse_uint(const char *buffer, size_t count, unsigned int *result, int base);
+int parse_long(const char *buffer, size_t count, long *result, int base);
+int parse_ulong(const char *buffer, size_t count, unsigned long *result, int base);
+
+int read_file(const char *path, char *buf, size_t count, size_t *len);
+int write_file(const char *path, const char *buf, size_t count);
+int read_file_alloc(const char *path, char **buf, size_t *len);
+int read_long(const char *path, long *result, int base);
+int write_long(const char *path, long result, int base);
+int read_ulong(const char *path, unsigned long *result, int base);
+int write_ulong(const char *path, unsigned long result, int base);
+int read_debugfs_file(const char *debugfs_file, char *buf, size_t count);
+int write_debugfs_file(const char *debugfs_file, const char *buf, size_t count);
+int read_debugfs_int(const char *debugfs_file, int *result);
+int write_debugfs_int(const char *debugfs_file, int result);
int read_sysfs_file(char *debugfs_file, char *result, size_t result_size);
int perf_event_open_counter(unsigned int type,
unsigned long config, int group_fd);
diff --git a/tools/testing/selftests/powerpc/nx-gzip/gzfht_test.c b/tools/testing/selftests/powerpc/nx-gzip/gzfht_test.c
index 095195a25687..4de079923ccb 100644
--- a/tools/testing/selftests/powerpc/nx-gzip/gzfht_test.c
+++ b/tools/testing/selftests/powerpc/nx-gzip/gzfht_test.c
@@ -143,54 +143,6 @@ int gzip_header_blank(char *buf)
return i;
}
-/* Caller must free the allocated buffer return nonzero on error. */
-int read_alloc_input_file(char *fname, char **buf, size_t *bufsize)
-{
- struct stat statbuf;
- FILE *fp;
- char *p;
- size_t num_bytes;
-
- if (stat(fname, &statbuf)) {
- perror(fname);
- return(-1);
- }
- fp = fopen(fname, "r");
- if (fp == NULL) {
- perror(fname);
- return(-1);
- }
- assert(NULL != (p = (char *) malloc(statbuf.st_size)));
- num_bytes = fread(p, 1, statbuf.st_size, fp);
- if (ferror(fp) || (num_bytes != statbuf.st_size)) {
- perror(fname);
- return(-1);
- }
- *buf = p;
- *bufsize = num_bytes;
- return 0;
-}
-
-/* Returns nonzero on error */
-int write_output_file(char *fname, char *buf, size_t bufsize)
-{
- FILE *fp;
- size_t num_bytes;
-
- fp = fopen(fname, "w");
- if (fp == NULL) {
- perror(fname);
- return(-1);
- }
- num_bytes = fwrite(buf, 1, bufsize, fp);
- if (ferror(fp) || (num_bytes != bufsize)) {
- perror(fname);
- return(-1);
- }
- fclose(fp);
- return 0;
-}
-
/*
* Z_SYNC_FLUSH as described in zlib.h.
* Returns number of appended bytes
@@ -257,7 +209,7 @@ int compress_file(int argc, char **argv, void *handle)
fprintf(stderr, "usage: %s <fname>\n", argv[0]);
exit(-1);
}
- if (read_alloc_input_file(argv[1], &inbuf, &inlen))
+ if (read_file_alloc(argv[1], &inbuf, &inlen))
exit(-1);
fprintf(stderr, "file %s read, %ld bytes\n", argv[1], inlen);
@@ -399,7 +351,7 @@ int compress_file(int argc, char **argv, void *handle)
assert(FNAME_MAX > (strlen(argv[1]) + strlen(FEXT)));
strcpy(outname, argv[1]);
strcat(outname, FEXT);
- if (write_output_file(outname, outbuf, dsttotlen)) {
+ if (write_file(outname, outbuf, dsttotlen)) {
fprintf(stderr, "write error: %s\n", outname);
exit(-1);
}
diff --git a/tools/testing/selftests/powerpc/pmu/lib.c b/tools/testing/selftests/powerpc/pmu/lib.c
index 88690b97b7b9..719f94f10d41 100644
--- a/tools/testing/selftests/powerpc/pmu/lib.c
+++ b/tools/testing/selftests/powerpc/pmu/lib.c
@@ -190,38 +190,14 @@ int parse_proc_maps(void)
bool require_paranoia_below(int level)
{
+ int err;
long current;
- char *end, buf[16];
- FILE *f;
- bool rc;
-
- rc = false;
-
- f = fopen(PARANOID_PATH, "r");
- if (!f) {
- perror("fopen");
- goto out;
- }
- if (!fgets(buf, sizeof(buf), f)) {
- printf("Couldn't read " PARANOID_PATH "?\n");
- goto out_close;
- }
-
- current = strtol(buf, &end, 10);
-
- if (end == buf) {
+ err = read_long(PARANOID_PATH, &current, 10);
+ if (err) {
printf("Couldn't parse " PARANOID_PATH "?\n");
- goto out_close;
+ return false;
}
- if (current >= level)
- goto out_close;
-
- rc = true;
-out_close:
- fclose(f);
-out:
- return rc;
+ return current < level;
}
-
diff --git a/tools/testing/selftests/powerpc/ptrace/Makefile b/tools/testing/selftests/powerpc/ptrace/Makefile
index 2f02cb54224d..cbeeaeae8837 100644
--- a/tools/testing/selftests/powerpc/ptrace/Makefile
+++ b/tools/testing/selftests/powerpc/ptrace/Makefile
@@ -33,7 +33,7 @@ TESTS_64 := $(patsubst %,$(OUTPUT)/%,$(TESTS_64))
$(TESTS_64): CFLAGS += -m64
$(TM_TESTS): CFLAGS += -I../tm -mhtm
-CFLAGS += -I../../../../../usr/include -fno-pie
+CFLAGS += $(KHDR_INCLUDES) -fno-pie
$(OUTPUT)/ptrace-gpr: ptrace-gpr.S
$(OUTPUT)/ptrace-pkey $(OUTPUT)/core-pkey: LDLIBS += -pthread
diff --git a/tools/testing/selftests/powerpc/ptrace/core-pkey.c b/tools/testing/selftests/powerpc/ptrace/core-pkey.c
index 4e8d0ce1ff58..f6f8596ce8e1 100644
--- a/tools/testing/selftests/powerpc/ptrace/core-pkey.c
+++ b/tools/testing/selftests/powerpc/ptrace/core-pkey.c
@@ -348,15 +348,11 @@ static int parent(struct shared_info *info, pid_t pid)
static int write_core_pattern(const char *core_pattern)
{
- size_t len = strlen(core_pattern), ret;
- FILE *f;
+ int err;
- f = fopen(core_pattern_file, "w");
- SKIP_IF_MSG(!f, "Try with root privileges");
-
- ret = fwrite(core_pattern, 1, len, f);
- fclose(f);
- if (ret != len) {
+ err = write_file(core_pattern_file, core_pattern, strlen(core_pattern));
+ if (err) {
+ SKIP_IF_MSG(err == -EPERM, "Try with root privileges");
perror("Error writing to core_pattern file");
return TEST_FAIL;
}
@@ -366,8 +362,8 @@ static int write_core_pattern(const char *core_pattern)
static int setup_core_pattern(char **core_pattern_, bool *changed_)
{
- FILE *f;
char *core_pattern;
+ size_t len;
int ret;
core_pattern = malloc(PATH_MAX);
@@ -376,22 +372,14 @@ static int setup_core_pattern(char **core_pattern_, bool *changed_)
return TEST_FAIL;
}
- f = fopen(core_pattern_file, "r");
- if (!f) {
- perror("Error opening core_pattern file");
- ret = TEST_FAIL;
- goto out;
- }
-
- ret = fread(core_pattern, 1, PATH_MAX - 1, f);
- fclose(f);
- if (!ret) {
+ ret = read_file(core_pattern_file, core_pattern, PATH_MAX - 1, &len);
+ if (ret) {
perror("Error reading core_pattern file");
ret = TEST_FAIL;
goto out;
}
- core_pattern[ret] = '\0';
+ core_pattern[len] = '\0';
/* Check whether we can predict the name of the core file. */
if (!strcmp(core_pattern, "core") || !strcmp(core_pattern, "core.%p"))
diff --git a/tools/testing/selftests/powerpc/security/Makefile b/tools/testing/selftests/powerpc/security/Makefile
index 7488315fd847..e0d979ab0204 100644
--- a/tools/testing/selftests/powerpc/security/Makefile
+++ b/tools/testing/selftests/powerpc/security/Makefile
@@ -5,7 +5,7 @@ TEST_PROGS := mitigation-patching.sh
top_srcdir = ../../../../..
-CFLAGS += -I../../../../../usr/include
+CFLAGS += $(KHDR_INCLUDES)
include ../../lib.mk
diff --git a/tools/testing/selftests/powerpc/security/entry_flush.c b/tools/testing/selftests/powerpc/security/entry_flush.c
index 68ce377b205e..e01c573deadd 100644
--- a/tools/testing/selftests/powerpc/security/entry_flush.c
+++ b/tools/testing/selftests/powerpc/security/entry_flush.c
@@ -34,18 +34,18 @@ int entry_flush_test(void)
// The PMU event we use only works on Power7 or later
SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06));
- if (read_debugfs_file("powerpc/rfi_flush", &rfi_flush_orig) < 0) {
+ if (read_debugfs_int("powerpc/rfi_flush", &rfi_flush_orig) < 0) {
perror("Unable to read powerpc/rfi_flush debugfs file");
SKIP_IF(1);
}
- if (read_debugfs_file("powerpc/entry_flush", &entry_flush_orig) < 0) {
+ if (read_debugfs_int("powerpc/entry_flush", &entry_flush_orig) < 0) {
perror("Unable to read powerpc/entry_flush debugfs file");
SKIP_IF(1);
}
if (rfi_flush_orig != 0) {
- if (write_debugfs_file("powerpc/rfi_flush", 0) < 0) {
+ if (write_debugfs_int("powerpc/rfi_flush", 0) < 0) {
perror("error writing to powerpc/rfi_flush debugfs file");
FAIL_IF(1);
}
@@ -105,7 +105,7 @@ again:
if (entry_flush == entry_flush_orig) {
entry_flush = !entry_flush_orig;
- if (write_debugfs_file("powerpc/entry_flush", entry_flush) < 0) {
+ if (write_debugfs_int("powerpc/entry_flush", entry_flush) < 0) {
perror("error writing to powerpc/entry_flush debugfs file");
return 1;
}
@@ -120,12 +120,12 @@ again:
set_dscr(0);
- if (write_debugfs_file("powerpc/rfi_flush", rfi_flush_orig) < 0) {
+ if (write_debugfs_int("powerpc/rfi_flush", rfi_flush_orig) < 0) {
perror("unable to restore original value of powerpc/rfi_flush debugfs file");
return 1;
}
- if (write_debugfs_file("powerpc/entry_flush", entry_flush_orig) < 0) {
+ if (write_debugfs_int("powerpc/entry_flush", entry_flush_orig) < 0) {
perror("unable to restore original value of powerpc/entry_flush debugfs file");
return 1;
}
diff --git a/tools/testing/selftests/powerpc/security/rfi_flush.c b/tools/testing/selftests/powerpc/security/rfi_flush.c
index f73484a6470f..6bedc86443a6 100644
--- a/tools/testing/selftests/powerpc/security/rfi_flush.c
+++ b/tools/testing/selftests/powerpc/security/rfi_flush.c
@@ -34,18 +34,18 @@ int rfi_flush_test(void)
// The PMU event we use only works on Power7 or later
SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06));
- if (read_debugfs_file("powerpc/rfi_flush", &rfi_flush_orig) < 0) {
+ if (read_debugfs_int("powerpc/rfi_flush", &rfi_flush_orig) < 0) {
perror("Unable to read powerpc/rfi_flush debugfs file");
SKIP_IF(1);
}
- if (read_debugfs_file("powerpc/entry_flush", &entry_flush_orig) < 0) {
+ if (read_debugfs_int("powerpc/entry_flush", &entry_flush_orig) < 0) {
have_entry_flush = 0;
} else {
have_entry_flush = 1;
if (entry_flush_orig != 0) {
- if (write_debugfs_file("powerpc/entry_flush", 0) < 0) {
+ if (write_debugfs_int("powerpc/entry_flush", 0) < 0) {
perror("error writing to powerpc/entry_flush debugfs file");
return 1;
}
@@ -105,7 +105,7 @@ again:
if (rfi_flush == rfi_flush_orig) {
rfi_flush = !rfi_flush_orig;
- if (write_debugfs_file("powerpc/rfi_flush", rfi_flush) < 0) {
+ if (write_debugfs_int("powerpc/rfi_flush", rfi_flush) < 0) {
perror("error writing to powerpc/rfi_flush debugfs file");
return 1;
}
@@ -120,13 +120,13 @@ again:
set_dscr(0);
- if (write_debugfs_file("powerpc/rfi_flush", rfi_flush_orig) < 0) {
+ if (write_debugfs_int("powerpc/rfi_flush", rfi_flush_orig) < 0) {
perror("unable to restore original value of powerpc/rfi_flush debugfs file");
return 1;
}
if (have_entry_flush) {
- if (write_debugfs_file("powerpc/entry_flush", entry_flush_orig) < 0) {
+ if (write_debugfs_int("powerpc/entry_flush", entry_flush_orig) < 0) {
perror("unable to restore original value of powerpc/entry_flush "
"debugfs file");
return 1;
diff --git a/tools/testing/selftests/powerpc/security/uaccess_flush.c b/tools/testing/selftests/powerpc/security/uaccess_flush.c
index cf80f960e38a..fcf23ea9b183 100644
--- a/tools/testing/selftests/powerpc/security/uaccess_flush.c
+++ b/tools/testing/selftests/powerpc/security/uaccess_flush.c
@@ -36,30 +36,30 @@ int uaccess_flush_test(void)
// The PMU event we use only works on Power7 or later
SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06));
- if (read_debugfs_file("powerpc/rfi_flush", &rfi_flush_orig) < 0) {
+ if (read_debugfs_int("powerpc/rfi_flush", &rfi_flush_orig) < 0) {
perror("Unable to read powerpc/rfi_flush debugfs file");
SKIP_IF(1);
}
- if (read_debugfs_file("powerpc/entry_flush", &entry_flush_orig) < 0) {
+ if (read_debugfs_int("powerpc/entry_flush", &entry_flush_orig) < 0) {
perror("Unable to read powerpc/entry_flush debugfs file");
SKIP_IF(1);
}
- if (read_debugfs_file("powerpc/uaccess_flush", &uaccess_flush_orig) < 0) {
+ if (read_debugfs_int("powerpc/uaccess_flush", &uaccess_flush_orig) < 0) {
perror("Unable to read powerpc/entry_flush debugfs file");
SKIP_IF(1);
}
if (rfi_flush_orig != 0) {
- if (write_debugfs_file("powerpc/rfi_flush", 0) < 0) {
+ if (write_debugfs_int("powerpc/rfi_flush", 0) < 0) {
perror("error writing to powerpc/rfi_flush debugfs file");
FAIL_IF(1);
}
}
if (entry_flush_orig != 0) {
- if (write_debugfs_file("powerpc/entry_flush", 0) < 0) {
+ if (write_debugfs_int("powerpc/entry_flush", 0) < 0) {
perror("error writing to powerpc/entry_flush debugfs file");
FAIL_IF(1);
}
@@ -119,7 +119,7 @@ again:
if (uaccess_flush == uaccess_flush_orig) {
uaccess_flush = !uaccess_flush_orig;
- if (write_debugfs_file("powerpc/uaccess_flush", uaccess_flush) < 0) {
+ if (write_debugfs_int("powerpc/uaccess_flush", uaccess_flush) < 0) {
perror("error writing to powerpc/uaccess_flush debugfs file");
return 1;
}
@@ -134,17 +134,17 @@ again:
set_dscr(0);
- if (write_debugfs_file("powerpc/rfi_flush", rfi_flush_orig) < 0) {
+ if (write_debugfs_int("powerpc/rfi_flush", rfi_flush_orig) < 0) {
perror("unable to restore original value of powerpc/rfi_flush debugfs file");
return 1;
}
- if (write_debugfs_file("powerpc/entry_flush", entry_flush_orig) < 0) {
+ if (write_debugfs_int("powerpc/entry_flush", entry_flush_orig) < 0) {
perror("unable to restore original value of powerpc/entry_flush debugfs file");
return 1;
}
- if (write_debugfs_file("powerpc/uaccess_flush", uaccess_flush_orig) < 0) {
+ if (write_debugfs_int("powerpc/uaccess_flush", uaccess_flush_orig) < 0) {
perror("unable to restore original value of powerpc/uaccess_flush debugfs file");
return 1;
}
diff --git a/tools/testing/selftests/powerpc/syscalls/Makefile b/tools/testing/selftests/powerpc/syscalls/Makefile
index b63f8459c704..ee1740ddfb0c 100644
--- a/tools/testing/selftests/powerpc/syscalls/Makefile
+++ b/tools/testing/selftests/powerpc/syscalls/Makefile
@@ -1,9 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-only
TEST_GEN_PROGS := ipc_unmuxed rtas_filter
-CFLAGS += -I../../../../../usr/include
+CFLAGS += $(KHDR_INCLUDES)
top_srcdir = ../../../../..
include ../../lib.mk
-$(TEST_GEN_PROGS): ../harness.c
+$(TEST_GEN_PROGS): ../harness.c ../utils.c
diff --git a/tools/testing/selftests/powerpc/syscalls/rtas_filter.c b/tools/testing/selftests/powerpc/syscalls/rtas_filter.c
index 03b487f18d00..9b17780f0b18 100644
--- a/tools/testing/selftests/powerpc/syscalls/rtas_filter.c
+++ b/tools/testing/selftests/powerpc/syscalls/rtas_filter.c
@@ -8,6 +8,7 @@
#include <byteswap.h>
#include <stdint.h>
#include <inttypes.h>
+#include <linux/limits.h>
#include <stdio.h>
#include <string.h>
#include <sys/syscall.h>
@@ -50,70 +51,16 @@ struct region {
struct region *next;
};
-int read_entire_file(int fd, char **buf, size_t *len)
-{
- size_t buf_size = 0;
- size_t off = 0;
- int rc;
-
- *buf = NULL;
- do {
- buf_size += BLOCK_SIZE;
- if (*buf == NULL)
- *buf = malloc(buf_size);
- else
- *buf = realloc(*buf, buf_size);
-
- if (*buf == NULL)
- return -ENOMEM;
-
- rc = read(fd, *buf + off, BLOCK_SIZE);
- if (rc < 0)
- return -EIO;
-
- off += rc;
- } while (rc == BLOCK_SIZE);
-
- if (len)
- *len = off;
-
- return 0;
-}
-
-static int open_prop_file(const char *prop_path, const char *prop_name, int *fd)
-{
- char *path;
- int len;
-
- /* allocate enough for two string, a slash and trailing NULL */
- len = strlen(prop_path) + strlen(prop_name) + 1 + 1;
- path = malloc(len);
- if (path == NULL)
- return -ENOMEM;
-
- snprintf(path, len, "%s/%s", prop_path, prop_name);
-
- *fd = open(path, O_RDONLY);
- free(path);
- if (*fd < 0)
- return -errno;
-
- return 0;
-}
-
static int get_property(const char *prop_path, const char *prop_name,
char **prop_val, size_t *prop_len)
{
- int rc, fd;
-
- rc = open_prop_file(prop_path, prop_name, &fd);
- if (rc)
- return rc;
+ char path[PATH_MAX];
- rc = read_entire_file(fd, prop_val, prop_len);
- close(fd);
+ int len = snprintf(path, sizeof(path), "%s/%s", prop_path, prop_name);
+ if (len < 0 || len >= sizeof(path))
+ return -ENOMEM;
- return rc;
+ return read_file_alloc(path, prop_val, prop_len);
}
int rtas_token(const char *call_name)
@@ -138,22 +85,14 @@ err:
static int read_kregion_bounds(struct region *kregion)
{
char *buf;
- int fd;
- int rc;
+ int err;
- fd = open("/proc/ppc64/rtas/rmo_buffer", O_RDONLY);
- if (fd < 0) {
- printf("Could not open rmo_buffer file\n");
+ err = read_file_alloc("/proc/ppc64/rtas/rmo_buffer", &buf, NULL);
+ if (err) {
+ perror("Could not open rmo_buffer file");
return RTAS_IO_ASSERT;
}
- rc = read_entire_file(fd, &buf, NULL);
- close(fd);
- if (rc) {
- free(buf);
- return rc;
- }
-
sscanf(buf, "%" SCNx64 " %x", &kregion->addr, &kregion->size);
free(buf);
diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile
index 5881e97c73c1..3876805c2f31 100644
--- a/tools/testing/selftests/powerpc/tm/Makefile
+++ b/tools/testing/selftests/powerpc/tm/Makefile
@@ -17,7 +17,7 @@ $(TEST_GEN_PROGS): ../harness.c ../utils.c
CFLAGS += -mhtm
$(OUTPUT)/tm-syscall: tm-syscall-asm.S
-$(OUTPUT)/tm-syscall: CFLAGS += -I../../../../../usr/include
+$(OUTPUT)/tm-syscall: CFLAGS += $(KHDR_INCLUDES)
$(OUTPUT)/tm-tmspr: CFLAGS += -pthread
$(OUTPUT)/tm-vmx-unavail: CFLAGS += -pthread -m64
$(OUTPUT)/tm-resched-dscr: ../pmu/lib.c
diff --git a/tools/testing/selftests/powerpc/utils.c b/tools/testing/selftests/powerpc/utils.c
index 1f36ee1a909a..7c8cfedb012a 100644
--- a/tools/testing/selftests/powerpc/utils.c
+++ b/tools/testing/selftests/powerpc/utils.c
@@ -8,6 +8,8 @@
#include <elf.h>
#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
#include <link.h>
#include <sched.h>
#include <stdio.h>
@@ -26,34 +28,360 @@
static char auxv[4096];
-int read_auxv(char *buf, ssize_t buf_size)
+int read_file(const char *path, char *buf, size_t count, size_t *len)
{
- ssize_t num;
- int rc, fd;
+ ssize_t rc;
+ int fd;
+ int err;
+ char eof;
- fd = open("/proc/self/auxv", O_RDONLY);
- if (fd == -1) {
- perror("open");
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
return -errno;
+
+ rc = read(fd, buf, count);
+ if (rc < 0) {
+ err = -errno;
+ goto out;
}
- num = read(fd, buf, buf_size);
- if (num < 0) {
- perror("read");
- rc = -EIO;
+ if (len)
+ *len = rc;
+
+ /* Overflow if there are still more bytes after filling the buffer */
+ if (rc == count) {
+ rc = read(fd, &eof, 1);
+ if (rc != 0) {
+ err = -EOVERFLOW;
+ goto out;
+ }
+ }
+
+ err = 0;
+
+out:
+ close(fd);
+ errno = -err;
+ return err;
+}
+
+int read_file_alloc(const char *path, char **buf, size_t *len)
+{
+ size_t read_offset = 0;
+ size_t buffer_len = 0;
+ char *buffer = NULL;
+ int err;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -errno;
+
+ /*
+ * We don't use stat & preallocate st_size because some non-files
+ * report 0 file size. Instead just dynamically grow the buffer
+ * as needed.
+ */
+ while (1) {
+ ssize_t rc;
+
+ if (read_offset >= buffer_len / 2) {
+ char *next_buffer;
+
+ buffer_len = buffer_len ? buffer_len * 2 : 4096;
+ next_buffer = realloc(buffer, buffer_len);
+ if (!next_buffer) {
+ err = -errno;
+ goto out;
+ }
+ buffer = next_buffer;
+ }
+
+ rc = read(fd, buffer + read_offset, buffer_len - read_offset);
+ if (rc < 0) {
+ err = -errno;
+ goto out;
+ }
+
+ if (rc == 0)
+ break;
+
+ read_offset += rc;
+ }
+
+ *buf = buffer;
+ if (len)
+ *len = read_offset;
+
+ err = 0;
+
+out:
+ close(fd);
+ if (err)
+ free(buffer);
+ errno = -err;
+ return err;
+}
+
+int write_file(const char *path, const char *buf, size_t count)
+{
+ int fd;
+ int err;
+ ssize_t rc;
+
+ fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0)
+ return -errno;
+
+ rc = write(fd, buf, count);
+ if (rc < 0) {
+ err = -errno;
goto out;
}
- if (num > buf_size) {
- printf("overflowed auxv buffer\n");
- rc = -EOVERFLOW;
+ if (rc != count) {
+ err = -EOVERFLOW;
goto out;
}
- rc = 0;
+ err = 0;
+
out:
close(fd);
- return rc;
+ errno = -err;
+ return err;
+}
+
+int read_auxv(char *buf, ssize_t buf_size)
+{
+ int err;
+
+ err = read_file("/proc/self/auxv", buf, buf_size, NULL);
+ if (err) {
+ perror("Error reading /proc/self/auxv");
+ return err;
+ }
+
+ return 0;
+}
+
+int read_debugfs_file(const char *subpath, char *buf, size_t count)
+{
+ char path[PATH_MAX] = "/sys/kernel/debug/";
+
+ strncat(path, subpath, sizeof(path) - strlen(path) - 1);
+
+ return read_file(path, buf, count, NULL);
+}
+
+int write_debugfs_file(const char *subpath, const char *buf, size_t count)
+{
+ char path[PATH_MAX] = "/sys/kernel/debug/";
+
+ strncat(path, subpath, sizeof(path) - strlen(path) - 1);
+
+ return write_file(path, buf, count);
+}
+
+static int validate_int_parse(const char *buffer, size_t count, char *end)
+{
+ int err = 0;
+
+ /* Require at least one digit */
+ if (end == buffer) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* Require all remaining characters be whitespace-ish */
+ for (; end < buffer + count; end++) {
+ if (*end == '\0')
+ break;
+
+ if (*end != ' ' && *end != '\n') {
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
+out:
+ errno = -err;
+ return err;
+}
+
+static int parse_bounded_int(const char *buffer, size_t count, intmax_t *result,
+ int base, intmax_t min, intmax_t max)
+{
+ int err;
+ char *end;
+
+ errno = 0;
+ *result = strtoimax(buffer, &end, base);
+
+ if (errno)
+ return -errno;
+
+ err = validate_int_parse(buffer, count, end);
+ if (err)
+ goto out;
+
+ if (*result < min || *result > max)
+ err = -EOVERFLOW;
+
+out:
+ errno = -err;
+ return err;
+}
+
+static int parse_bounded_uint(const char *buffer, size_t count, uintmax_t *result,
+ int base, uintmax_t max)
+{
+ int err = 0;
+ char *end;
+
+ errno = 0;
+ *result = strtoumax(buffer, &end, base);
+
+ if (errno)
+ return -errno;
+
+ err = validate_int_parse(buffer, count, end);
+ if (err)
+ goto out;
+
+ if (*result > max)
+ err = -EOVERFLOW;
+
+out:
+ errno = -err;
+ return err;
+}
+
+int parse_intmax(const char *buffer, size_t count, intmax_t *result, int base)
+{
+ return parse_bounded_int(buffer, count, result, base, INTMAX_MIN, INTMAX_MAX);
+}
+
+int parse_uintmax(const char *buffer, size_t count, uintmax_t *result, int base)
+{
+ return parse_bounded_uint(buffer, count, result, base, UINTMAX_MAX);
+}
+
+int parse_int(const char *buffer, size_t count, int *result, int base)
+{
+ intmax_t parsed;
+ int err = parse_bounded_int(buffer, count, &parsed, base, INT_MIN, INT_MAX);
+
+ *result = parsed;
+ return err;
+}
+
+int parse_uint(const char *buffer, size_t count, unsigned int *result, int base)
+{
+ uintmax_t parsed;
+ int err = parse_bounded_uint(buffer, count, &parsed, base, UINT_MAX);
+
+ *result = parsed;
+ return err;
+}
+
+int parse_long(const char *buffer, size_t count, long *result, int base)
+{
+ intmax_t parsed;
+ int err = parse_bounded_int(buffer, count, &parsed, base, LONG_MIN, LONG_MAX);
+
+ *result = parsed;
+ return err;
+}
+
+int parse_ulong(const char *buffer, size_t count, unsigned long *result, int base)
+{
+ uintmax_t parsed;
+ int err = parse_bounded_uint(buffer, count, &parsed, base, ULONG_MAX);
+
+ *result = parsed;
+ return err;
+}
+
+int read_long(const char *path, long *result, int base)
+{
+ int err;
+ char buffer[32] = {0};
+
+ err = read_file(path, buffer, sizeof(buffer) - 1, NULL);
+ if (err)
+ return err;
+
+ return parse_long(buffer, sizeof(buffer), result, base);
+}
+
+int read_ulong(const char *path, unsigned long *result, int base)
+{
+ int err;
+ char buffer[32] = {0};
+
+ err = read_file(path, buffer, sizeof(buffer) - 1, NULL);
+ if (err)
+ return err;
+
+ return parse_ulong(buffer, sizeof(buffer), result, base);
+}
+
+int write_long(const char *path, long result, int base)
+{
+ int err;
+ int len;
+ char buffer[32];
+
+ /* Decimal only for now: no format specifier for signed hex values */
+ if (base != 10) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ len = snprintf(buffer, sizeof(buffer), "%ld", result);
+ if (len < 0 || len >= sizeof(buffer)) {
+ err = -EOVERFLOW;
+ goto out;
+ }
+
+ err = write_file(path, buffer, len);
+
+out:
+ errno = -err;
+ return err;
+}
+
+int write_ulong(const char *path, unsigned long result, int base)
+{
+ int err;
+ int len;
+ char buffer[32];
+ char *fmt;
+
+ switch (base) {
+ case 10:
+ fmt = "%lu";
+ break;
+ case 16:
+ fmt = "%lx";
+ break;
+ default:
+ err = -EINVAL;
+ goto out;
+ }
+
+ len = snprintf(buffer, sizeof(buffer), fmt, result);
+ if (len < 0 || len >= sizeof(buffer)) {
+ err = -errno;
+ goto out;
+ }
+
+ err = write_file(path, buffer, len);
+
+out:
+ errno = -err;
+ return err;
}
void *find_auxv_entry(int type, char *auxv)
@@ -142,65 +470,31 @@ bool is_ppc64le(void)
int read_sysfs_file(char *fpath, char *result, size_t result_size)
{
char path[PATH_MAX] = "/sys/";
- int rc = -1, fd;
strncat(path, fpath, PATH_MAX - strlen(path) - 1);
- if ((fd = open(path, O_RDONLY)) < 0)
- return rc;
-
- rc = read(fd, result, result_size);
-
- close(fd);
-
- if (rc < 0)
- return rc;
-
- return 0;
+ return read_file(path, result, result_size, NULL);
}
-int read_debugfs_file(char *debugfs_file, int *result)
+int read_debugfs_int(const char *debugfs_file, int *result)
{
- int rc = -1, fd;
- char path[PATH_MAX];
- char value[16];
-
- strcpy(path, "/sys/kernel/debug/");
- strncat(path, debugfs_file, PATH_MAX - strlen(path) - 1);
+ int err;
+ char value[16] = {0};
- if ((fd = open(path, O_RDONLY)) < 0)
- return rc;
+ err = read_debugfs_file(debugfs_file, value, sizeof(value) - 1);
+ if (err)
+ return err;
- if ((rc = read(fd, value, sizeof(value))) < 0)
- return rc;
-
- value[15] = 0;
- *result = atoi(value);
- close(fd);
-
- return 0;
+ return parse_int(value, sizeof(value), result, 10);
}
-int write_debugfs_file(char *debugfs_file, int result)
+int write_debugfs_int(const char *debugfs_file, int result)
{
- int rc = -1, fd;
- char path[PATH_MAX];
char value[16];
- strcpy(path, "/sys/kernel/debug/");
- strncat(path, debugfs_file, PATH_MAX - strlen(path) - 1);
-
- if ((fd = open(path, O_WRONLY)) < 0)
- return rc;
-
snprintf(value, 16, "%d", result);
- if ((rc = write(fd, value, strlen(value))) < 0)
- return rc;
-
- close(fd);
-
- return 0;
+ return write_debugfs_file(debugfs_file, value, strlen(value));
}
static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
diff --git a/tools/testing/selftests/prctl/disable-tsc-ctxt-sw-stress-test.c b/tools/testing/selftests/prctl/disable-tsc-ctxt-sw-stress-test.c
index 62a93cc61b7c..6d1a5ee8eb28 100644
--- a/tools/testing/selftests/prctl/disable-tsc-ctxt-sw-stress-test.c
+++ b/tools/testing/selftests/prctl/disable-tsc-ctxt-sw-stress-test.c
@@ -79,7 +79,7 @@ int main(void)
{
int n_tasks = 100, i;
- fprintf(stderr, "[No further output means we're allright]\n");
+ fprintf(stderr, "[No further output means we're all right]\n");
for (i=0; i<n_tasks; i++)
if (fork() == 0)
diff --git a/tools/testing/selftests/prctl/disable-tsc-on-off-stress-test.c b/tools/testing/selftests/prctl/disable-tsc-on-off-stress-test.c
index 79950f9a26fd..d39511eb9b01 100644
--- a/tools/testing/selftests/prctl/disable-tsc-on-off-stress-test.c
+++ b/tools/testing/selftests/prctl/disable-tsc-on-off-stress-test.c
@@ -83,7 +83,7 @@ int main(void)
{
int n_tasks = 100, i;
- fprintf(stderr, "[No further output means we're allright]\n");
+ fprintf(stderr, "[No further output means we're all right]\n");
for (i=0; i<n_tasks; i++)
if (fork() == 0)
diff --git a/tools/testing/selftests/ptp/Makefile b/tools/testing/selftests/ptp/Makefile
index ef06de0898b7..8f57f88ecadd 100644
--- a/tools/testing/selftests/ptp/Makefile
+++ b/tools/testing/selftests/ptp/Makefile
@@ -1,10 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
-CFLAGS += -I../../../../usr/include/
-TEST_PROGS := testptp
+CFLAGS += $(KHDR_INCLUDES)
+TEST_GEN_PROGS := testptp
LDLIBS += -lrt
-all: $(TEST_PROGS)
+TEST_PROGS = phc.sh
include ../lib.mk
-
-clean:
- rm -fr $(TEST_PROGS)
diff --git a/tools/testing/selftests/ptrace/Makefile b/tools/testing/selftests/ptrace/Makefile
index 2f1f532c39db..96ffa94afb91 100644
--- a/tools/testing/selftests/ptrace/Makefile
+++ b/tools/testing/selftests/ptrace/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-CFLAGS += -std=c99 -pthread -iquote../../../../include/uapi -Wall
+CFLAGS += -std=c99 -pthread -Wall $(KHDR_INCLUDES)
TEST_GEN_PROGS := get_syscall_info peeksiginfo vmaccess
diff --git a/tools/testing/selftests/rcutorture/bin/functions.sh b/tools/testing/selftests/rcutorture/bin/functions.sh
index 66d0414d8e4b..b52d5069563c 100644
--- a/tools/testing/selftests/rcutorture/bin/functions.sh
+++ b/tools/testing/selftests/rcutorture/bin/functions.sh
@@ -159,6 +159,9 @@ identify_boot_image () {
qemu-system-aarch64)
echo arch/arm64/boot/Image
;;
+ qemu-system-s390x)
+ echo arch/s390/boot/bzImage
+ ;;
*)
echo vmlinux
;;
@@ -184,6 +187,9 @@ identify_qemu () {
elif echo $u | grep -q aarch64
then
echo qemu-system-aarch64
+ elif echo $u | grep -q 'IBM S/390'
+ then
+ echo qemu-system-s390x
elif uname -a | grep -q ppc64
then
echo qemu-system-ppc64
diff --git a/tools/testing/selftests/rcutorture/bin/mkinitrd.sh b/tools/testing/selftests/rcutorture/bin/mkinitrd.sh
index 70d62fd0d31d..71f0dfbb2a6d 100755
--- a/tools/testing/selftests/rcutorture/bin/mkinitrd.sh
+++ b/tools/testing/selftests/rcutorture/bin/mkinitrd.sh
@@ -64,7 +64,7 @@ ___EOF___
# build using nolibc on supported archs (smaller executable) and fall
# back to regular glibc on other ones.
if echo -e "#if __x86_64__||__i386__||__i486__||__i586__||__i686__" \
- "||__ARM_EABI__||__aarch64__\nyes\n#endif" \
+ "||__ARM_EABI__||__aarch64__||__s390x__\nyes\n#endif" \
| ${CROSS_COMPILE}gcc -E -nostdlib -xc - \
| grep -q '^yes'; then
# architecture supported by nolibc
diff --git a/tools/testing/selftests/rseq/Makefile b/tools/testing/selftests/rseq/Makefile
index 82a52810a649..b357ba24af06 100644
--- a/tools/testing/selftests/rseq/Makefile
+++ b/tools/testing/selftests/rseq/Makefile
@@ -4,7 +4,7 @@ ifneq ($(shell $(CC) --version 2>&1 | head -n 1 | grep clang),)
CLANG_FLAGS += -no-integrated-as
endif
-CFLAGS += -O2 -Wall -g -I./ -I../../../../usr/include/ -L$(OUTPUT) -Wl,-rpath=./ \
+CFLAGS += -O2 -Wall -g -I./ $(KHDR_INCLUDES) -L$(OUTPUT) -Wl,-rpath=./ \
$(CLANG_FLAGS)
LDLIBS += -lpthread -ldl
diff --git a/tools/testing/selftests/sched/Makefile b/tools/testing/selftests/sched/Makefile
index 10c72f14fea9..099ee9213557 100644
--- a/tools/testing/selftests/sched/Makefile
+++ b/tools/testing/selftests/sched/Makefile
@@ -4,7 +4,7 @@ ifneq ($(shell $(CC) --version 2>&1 | head -n 1 | grep clang),)
CLANG_FLAGS += -no-integrated-as
endif
-CFLAGS += -O2 -Wall -g -I./ -I../../../../usr/include/ -Wl,-rpath=./ \
+CFLAGS += -O2 -Wall -g -I./ $(KHDR_INCLUDES) -Wl,-rpath=./ \
$(CLANG_FLAGS)
LDLIBS += -lpthread
diff --git a/tools/testing/selftests/sched/cs_prctl_test.c b/tools/testing/selftests/sched/cs_prctl_test.c
index 8109b17dc764..25e0d95d3713 100644
--- a/tools/testing/selftests/sched/cs_prctl_test.c
+++ b/tools/testing/selftests/sched/cs_prctl_test.c
@@ -27,6 +27,7 @@
#include <sys/prctl.h>
#include <unistd.h>
#include <time.h>
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -151,12 +152,17 @@ static void create_threads(int num_threads, int thr_tids[])
static int child_func_process(void *arg)
{
struct child_args *ca = (struct child_args *)arg;
+ int ret;
close(ca->pfd[0]);
create_threads(ca->num_threads, ca->thr_tids);
- write(ca->pfd[1], &ca->thr_tids, sizeof(int) * ca->num_threads);
+ ret = write(ca->pfd[1], &ca->thr_tids, sizeof(int) * ca->num_threads);
+ if (ret == -1)
+ printf("write failed on pfd[%d] - error (%s)\n",
+ ca->pfd[1], strerror(errno));
+
close(ca->pfd[1]);
while (1)
@@ -169,7 +175,7 @@ static unsigned char child_func_process_stack[STACK_SIZE];
void create_processes(int num_processes, int num_threads, struct child_args proc[])
{
pid_t cpid;
- int i;
+ int i, ret;
for (i = 0; i < num_processes; ++i) {
proc[i].num_threads = num_threads;
@@ -184,7 +190,10 @@ void create_processes(int num_processes, int num_threads, struct child_args proc
}
for (i = 0; i < num_processes; ++i) {
- read(proc[i].pfd[0], &proc[i].thr_tids, sizeof(int) * proc[i].num_threads);
+ ret = read(proc[i].pfd[0], &proc[i].thr_tids, sizeof(int) * proc[i].num_threads);
+ if (ret == -1)
+ printf("read failed on proc[%d].pfd[0] error (%s)\n",
+ i, strerror(errno));
close(proc[i].pfd[0]);
}
}
diff --git a/tools/testing/selftests/seccomp/Makefile b/tools/testing/selftests/seccomp/Makefile
index f017c382c036..584fba487037 100644
--- a/tools/testing/selftests/seccomp/Makefile
+++ b/tools/testing/selftests/seccomp/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-CFLAGS += -Wl,-no-as-needed -Wall -isystem ../../../../usr/include/
+CFLAGS += -Wl,-no-as-needed -Wall $(KHDR_INCLUDES)
LDFLAGS += -lpthread
LDLIBS += -lcap
diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c
index 61386e499b77..43ec36b179dc 100644
--- a/tools/testing/selftests/seccomp/seccomp_bpf.c
+++ b/tools/testing/selftests/seccomp/seccomp_bpf.c
@@ -128,6 +128,8 @@ struct seccomp_data {
# define __NR_seccomp 277
# elif defined(__csky__)
# define __NR_seccomp 277
+# elif defined(__loongarch__)
+# define __NR_seccomp 277
# elif defined(__hppa__)
# define __NR_seccomp 338
# elif defined(__powerpc__)
@@ -1755,6 +1757,10 @@ TEST_F(TRACE_poke, getpid_runs_normally)
NT_ARM_SYSTEM_CALL, &__v)); \
} while (0)
# define SYSCALL_RET(_regs) (_regs).regs[0]
+#elif defined(__loongarch__)
+# define ARCH_REGS struct user_pt_regs
+# define SYSCALL_NUM(_regs) (_regs).regs[11]
+# define SYSCALL_RET(_regs) (_regs).regs[4]
#elif defined(__riscv) && __riscv_xlen == 64
# define ARCH_REGS struct user_regs_struct
# define SYSCALL_NUM(_regs) (_regs).a7
diff --git a/tools/testing/selftests/sigaltstack/current_stack_pointer.h b/tools/testing/selftests/sigaltstack/current_stack_pointer.h
new file mode 100644
index 000000000000..ea9bdf3a90b1
--- /dev/null
+++ b/tools/testing/selftests/sigaltstack/current_stack_pointer.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#if __alpha__
+register unsigned long sp asm("$30");
+#elif __arm__ || __aarch64__ || __csky__ || __m68k__ || __mips__ || __riscv
+register unsigned long sp asm("sp");
+#elif __i386__
+register unsigned long sp asm("esp");
+#elif __loongarch64
+register unsigned long sp asm("$sp");
+#elif __ppc__
+register unsigned long sp asm("r1");
+#elif __s390x__
+register unsigned long sp asm("%15");
+#elif __sh__
+register unsigned long sp asm("r15");
+#elif __x86_64__
+register unsigned long sp asm("rsp");
+#elif __XTENSA__
+register unsigned long sp asm("a1");
+#else
+#error "implement current_stack_pointer equivalent"
+#endif
diff --git a/tools/testing/selftests/sigaltstack/sas.c b/tools/testing/selftests/sigaltstack/sas.c
index c53b070755b6..98d37cb744fb 100644
--- a/tools/testing/selftests/sigaltstack/sas.c
+++ b/tools/testing/selftests/sigaltstack/sas.c
@@ -20,6 +20,7 @@
#include <sys/auxv.h>
#include "../kselftest.h"
+#include "current_stack_pointer.h"
#ifndef SS_AUTODISARM
#define SS_AUTODISARM (1U << 31)
@@ -46,12 +47,6 @@ void my_usr1(int sig, siginfo_t *si, void *u)
stack_t stk;
struct stk_data *p;
-#if __s390x__
- register unsigned long sp asm("%15");
-#else
- register unsigned long sp asm("sp");
-#endif
-
if (sp < (unsigned long)sstack ||
sp >= (unsigned long)sstack + stack_size) {
ksft_exit_fail_msg("SP is not on sigaltstack\n");
diff --git a/tools/testing/selftests/sync/Makefile b/tools/testing/selftests/sync/Makefile
index d0121a8a3523..df0f91bf6890 100644
--- a/tools/testing/selftests/sync/Makefile
+++ b/tools/testing/selftests/sync/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
CFLAGS += -O2 -g -std=gnu89 -pthread -Wall -Wextra
-CFLAGS += -I../../../../usr/include/
+CFLAGS += $(KHDR_INCLUDES)
LDFLAGS += -pthread
.PHONY: all clean
diff --git a/tools/testing/selftests/tdx/Makefile b/tools/testing/selftests/tdx/Makefile
index 8dd43517cd55..306e9c4d5ef7 100644
--- a/tools/testing/selftests/tdx/Makefile
+++ b/tools/testing/selftests/tdx/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
-CFLAGS += -O3 -Wl,-no-as-needed -Wall -static
+CFLAGS += -O3 -Wl,-no-as-needed -Wall $(KHDR_INCLUDES) -static
TEST_GEN_PROGS := tdx_guest_test
diff --git a/tools/testing/selftests/tdx/tdx_guest_test.c b/tools/testing/selftests/tdx/tdx_guest_test.c
index 2a2afd856798..81d8cb88ea1a 100644
--- a/tools/testing/selftests/tdx/tdx_guest_test.c
+++ b/tools/testing/selftests/tdx/tdx_guest_test.c
@@ -12,8 +12,8 @@
#include <errno.h>
#include <fcntl.h>
+#include <linux/tdx-guest.h>
#include "../kselftest_harness.h"
-#include "../../../../include/uapi/linux/tdx-guest.h"
#define TDX_GUEST_DEVNAME "/dev/tdx_guest"
#define HEX_DUMP_SIZE 8
diff --git a/tools/testing/selftests/tpm2/tpm2.py b/tools/testing/selftests/tpm2/tpm2.py
index c7363c6764fc..bba8cb54548e 100644
--- a/tools/testing/selftests/tpm2/tpm2.py
+++ b/tools/testing/selftests/tpm2/tpm2.py
@@ -344,7 +344,7 @@ def get_algorithm(name):
def hex_dump(d):
- d = [format(ord(x), '02x') for x in d]
+ d = [format(x, '02x') for x in d]
d = [d[i: i + 16] for i in range(0, len(d), 16)]
d = [' '.join(x) for x in d]
d = os.linesep.join(d)
diff --git a/tools/testing/selftests/user_events/Makefile b/tools/testing/selftests/user_events/Makefile
index c765d8635d9a..6b512b86aec3 100644
--- a/tools/testing/selftests/user_events/Makefile
+++ b/tools/testing/selftests/user_events/Makefile
@@ -1,7 +1,15 @@
# SPDX-License-Identifier: GPL-2.0
-CFLAGS += -Wl,-no-as-needed -Wall -I../../../../usr/include
+CFLAGS += -Wl,-no-as-needed -Wall $(KHDR_INCLUDES)
LDLIBS += -lrt -lpthread -lm
+# Note:
+# This test depends on <linux/user_events.h> exported in uapi
+# The following commit removed user_events.h out of uapi:
+# commit 5cfff569cab8bf544bab62c911c5d6efd5af5e05
+# tracing: Move user_events.h temporarily out of include/uapi
+# This test will not compile until user_events.h is added
+# back to uapi.
+
TEST_GEN_PROGS = ftrace_test dyn_test perf_test
TEST_FILES := settings
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 0388c4d60af0..ca9374b56ead 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -34,7 +34,7 @@ BINARIES_64 := $(TARGETS_C_64BIT_ALL:%=%_64)
BINARIES_32 := $(patsubst %,$(OUTPUT)/%,$(BINARIES_32))
BINARIES_64 := $(patsubst %,$(OUTPUT)/%,$(BINARIES_64))
-CFLAGS := -O2 -g -std=gnu99 -pthread -Wall
+CFLAGS := -O2 -g -std=gnu99 -pthread -Wall $(KHDR_INCLUDES)
# call32_from_64 in thunks.S uses absolute addresses.
ifeq ($(CAN_BUILD_WITH_NOPIE),1)
diff --git a/tools/testing/selftests/x86/amx.c b/tools/testing/selftests/x86/amx.c
index 625e42901237..d884fd69dd51 100644
--- a/tools/testing/selftests/x86/amx.c
+++ b/tools/testing/selftests/x86/amx.c
@@ -14,8 +14,10 @@
#include <sys/auxv.h>
#include <sys/mman.h>
#include <sys/shm.h>
+#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <sys/wait.h>
+#include <sys/uio.h>
#include "../kselftest.h" /* For __cpuid_count() */
@@ -583,6 +585,13 @@ static void test_dynamic_state(void)
_exit(0);
}
+static inline int __compare_tiledata_state(struct xsave_buffer *xbuf1, struct xsave_buffer *xbuf2)
+{
+ return memcmp(&xbuf1->bytes[xtiledata.xbuf_offset],
+ &xbuf2->bytes[xtiledata.xbuf_offset],
+ xtiledata.size);
+}
+
/*
* Save current register state and compare it to @xbuf1.'
*
@@ -599,9 +608,7 @@ static inline bool __validate_tiledata_regs(struct xsave_buffer *xbuf1)
fatal_error("failed to allocate XSAVE buffer\n");
xsave(xbuf2, XFEATURE_MASK_XTILEDATA);
- ret = memcmp(&xbuf1->bytes[xtiledata.xbuf_offset],
- &xbuf2->bytes[xtiledata.xbuf_offset],
- xtiledata.size);
+ ret = __compare_tiledata_state(xbuf1, xbuf2);
free(xbuf2);
@@ -826,6 +833,99 @@ static void test_context_switch(void)
free(finfo);
}
+/* Ptrace test */
+
+/*
+ * Make sure the ptracee has the expanded kernel buffer on the first
+ * use. Then, initialize the state before performing the state
+ * injection from the ptracer.
+ */
+static inline void ptracee_firstuse_tiledata(void)
+{
+ load_rand_tiledata(stashed_xsave);
+ init_xtiledata();
+}
+
+/*
+ * Ptracer injects the randomized tile data state. It also reads
+ * before and after that, which will execute the kernel's state copy
+ * functions. So, the tester is advised to double-check any emitted
+ * kernel messages.
+ */
+static void ptracer_inject_tiledata(pid_t target)
+{
+ struct xsave_buffer *xbuf;
+ struct iovec iov;
+
+ xbuf = alloc_xbuf();
+ if (!xbuf)
+ fatal_error("unable to allocate XSAVE buffer");
+
+ printf("\tRead the init'ed tiledata via ptrace().\n");
+
+ iov.iov_base = xbuf;
+ iov.iov_len = xbuf_size;
+
+ memset(stashed_xsave, 0, xbuf_size);
+
+ if (ptrace(PTRACE_GETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
+ fatal_error("PTRACE_GETREGSET");
+
+ if (!__compare_tiledata_state(stashed_xsave, xbuf))
+ printf("[OK]\tThe init'ed tiledata was read from ptracee.\n");
+ else
+ printf("[FAIL]\tThe init'ed tiledata was not read from ptracee.\n");
+
+ printf("\tInject tiledata via ptrace().\n");
+
+ load_rand_tiledata(xbuf);
+
+ memcpy(&stashed_xsave->bytes[xtiledata.xbuf_offset],
+ &xbuf->bytes[xtiledata.xbuf_offset],
+ xtiledata.size);
+
+ if (ptrace(PTRACE_SETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
+ fatal_error("PTRACE_SETREGSET");
+
+ if (ptrace(PTRACE_GETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
+ fatal_error("PTRACE_GETREGSET");
+
+ if (!__compare_tiledata_state(stashed_xsave, xbuf))
+ printf("[OK]\tTiledata was correctly written to ptracee.\n");
+ else
+ printf("[FAIL]\tTiledata was not correctly written to ptracee.\n");
+}
+
+static void test_ptrace(void)
+{
+ pid_t child;
+ int status;
+
+ child = fork();
+ if (child < 0) {
+ err(1, "fork");
+ } else if (!child) {
+ if (ptrace(PTRACE_TRACEME, 0, NULL, NULL))
+ err(1, "PTRACE_TRACEME");
+
+ ptracee_firstuse_tiledata();
+
+ raise(SIGTRAP);
+ _exit(0);
+ }
+
+ do {
+ wait(&status);
+ } while (WSTOPSIG(status) != SIGTRAP);
+
+ ptracer_inject_tiledata(child);
+
+ ptrace(PTRACE_DETACH, child, NULL, NULL);
+ wait(&status);
+ if (!WIFEXITED(status) || WEXITSTATUS(status))
+ err(1, "ptrace test");
+}
+
int main(void)
{
/* Check hardware availability at first */
@@ -846,6 +946,8 @@ int main(void)
ctxtswtest_config.num_threads = 5;
test_context_switch();
+ test_ptrace();
+
clearhandler(SIGILL);
free_stashed_xsave();
diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c
index 67e9f9df3a8c..12b97c92fbb2 100644
--- a/tools/testing/vsock/vsock_test.c
+++ b/tools/testing/vsock/vsock_test.c
@@ -860,6 +860,199 @@ static void test_stream_poll_rcvlowat_client(const struct test_opts *opts)
close(fd);
}
+#define INV_BUF_TEST_DATA_LEN 512
+
+static void test_inv_buf_client(const struct test_opts *opts, bool stream)
+{
+ unsigned char data[INV_BUF_TEST_DATA_LEN] = {0};
+ ssize_t ret;
+ int fd;
+
+ if (stream)
+ fd = vsock_stream_connect(opts->peer_cid, 1234);
+ else
+ fd = vsock_seqpacket_connect(opts->peer_cid, 1234);
+
+ if (fd < 0) {
+ perror("connect");
+ exit(EXIT_FAILURE);
+ }
+
+ control_expectln("SENDDONE");
+
+ /* Use invalid buffer here. */
+ ret = recv(fd, NULL, sizeof(data), 0);
+ if (ret != -1) {
+ fprintf(stderr, "expected recv(2) failure, got %zi\n", ret);
+ exit(EXIT_FAILURE);
+ }
+
+ if (errno != ENOMEM) {
+ fprintf(stderr, "unexpected recv(2) errno %d\n", errno);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = recv(fd, data, sizeof(data), MSG_DONTWAIT);
+
+ if (stream) {
+ /* For SOCK_STREAM we must continue reading. */
+ if (ret != sizeof(data)) {
+ fprintf(stderr, "expected recv(2) success, got %zi\n", ret);
+ exit(EXIT_FAILURE);
+ }
+ /* Don't check errno in case of success. */
+ } else {
+ /* For SOCK_SEQPACKET socket's queue must be empty. */
+ if (ret != -1) {
+ fprintf(stderr, "expected recv(2) failure, got %zi\n", ret);
+ exit(EXIT_FAILURE);
+ }
+
+ if (errno != EAGAIN) {
+ fprintf(stderr, "unexpected recv(2) errno %d\n", errno);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ control_writeln("DONE");
+
+ close(fd);
+}
+
+static void test_inv_buf_server(const struct test_opts *opts, bool stream)
+{
+ unsigned char data[INV_BUF_TEST_DATA_LEN] = {0};
+ ssize_t res;
+ int fd;
+
+ if (stream)
+ fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
+ else
+ fd = vsock_seqpacket_accept(VMADDR_CID_ANY, 1234, NULL);
+
+ if (fd < 0) {
+ perror("accept");
+ exit(EXIT_FAILURE);
+ }
+
+ res = send(fd, data, sizeof(data), 0);
+ if (res != sizeof(data)) {
+ fprintf(stderr, "unexpected send(2) result %zi\n", res);
+ exit(EXIT_FAILURE);
+ }
+
+ control_writeln("SENDDONE");
+
+ control_expectln("DONE");
+
+ close(fd);
+}
+
+static void test_stream_inv_buf_client(const struct test_opts *opts)
+{
+ test_inv_buf_client(opts, true);
+}
+
+static void test_stream_inv_buf_server(const struct test_opts *opts)
+{
+ test_inv_buf_server(opts, true);
+}
+
+static void test_seqpacket_inv_buf_client(const struct test_opts *opts)
+{
+ test_inv_buf_client(opts, false);
+}
+
+static void test_seqpacket_inv_buf_server(const struct test_opts *opts)
+{
+ test_inv_buf_server(opts, false);
+}
+
+#define HELLO_STR "HELLO"
+#define WORLD_STR "WORLD"
+
+static void test_stream_virtio_skb_merge_client(const struct test_opts *opts)
+{
+ ssize_t res;
+ int fd;
+
+ fd = vsock_stream_connect(opts->peer_cid, 1234);
+ if (fd < 0) {
+ perror("connect");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Send first skbuff. */
+ res = send(fd, HELLO_STR, strlen(HELLO_STR), 0);
+ if (res != strlen(HELLO_STR)) {
+ fprintf(stderr, "unexpected send(2) result %zi\n", res);
+ exit(EXIT_FAILURE);
+ }
+
+ control_writeln("SEND0");
+ /* Peer reads part of first skbuff. */
+ control_expectln("REPLY0");
+
+ /* Send second skbuff, it will be appended to the first. */
+ res = send(fd, WORLD_STR, strlen(WORLD_STR), 0);
+ if (res != strlen(WORLD_STR)) {
+ fprintf(stderr, "unexpected send(2) result %zi\n", res);
+ exit(EXIT_FAILURE);
+ }
+
+ control_writeln("SEND1");
+ /* Peer reads merged skbuff packet. */
+ control_expectln("REPLY1");
+
+ close(fd);
+}
+
+static void test_stream_virtio_skb_merge_server(const struct test_opts *opts)
+{
+ unsigned char buf[64];
+ ssize_t res;
+ int fd;
+
+ fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
+ if (fd < 0) {
+ perror("accept");
+ exit(EXIT_FAILURE);
+ }
+
+ control_expectln("SEND0");
+
+ /* Read skbuff partially. */
+ res = recv(fd, buf, 2, 0);
+ if (res != 2) {
+ fprintf(stderr, "expected recv(2) returns 2 bytes, got %zi\n", res);
+ exit(EXIT_FAILURE);
+ }
+
+ control_writeln("REPLY0");
+ control_expectln("SEND1");
+
+ res = recv(fd, buf + 2, sizeof(buf) - 2, 0);
+ if (res != 8) {
+ fprintf(stderr, "expected recv(2) returns 8 bytes, got %zi\n", res);
+ exit(EXIT_FAILURE);
+ }
+
+ res = recv(fd, buf, sizeof(buf) - 8 - 2, MSG_DONTWAIT);
+ if (res != -1) {
+ fprintf(stderr, "expected recv(2) failure, got %zi\n", res);
+ exit(EXIT_FAILURE);
+ }
+
+ if (memcmp(buf, HELLO_STR WORLD_STR, strlen(HELLO_STR WORLD_STR))) {
+ fprintf(stderr, "pattern mismatch\n");
+ exit(EXIT_FAILURE);
+ }
+
+ control_writeln("REPLY1");
+
+ close(fd);
+}
+
static struct test_case test_cases[] = {
{
.name = "SOCK_STREAM connection reset",
@@ -920,6 +1113,21 @@ static struct test_case test_cases[] = {
.run_client = test_seqpacket_bigmsg_client,
.run_server = test_seqpacket_bigmsg_server,
},
+ {
+ .name = "SOCK_STREAM test invalid buffer",
+ .run_client = test_stream_inv_buf_client,
+ .run_server = test_stream_inv_buf_server,
+ },
+ {
+ .name = "SOCK_SEQPACKET test invalid buffer",
+ .run_client = test_seqpacket_inv_buf_client,
+ .run_server = test_seqpacket_inv_buf_server,
+ },
+ {
+ .name = "SOCK_STREAM virtio skb merge",
+ .run_client = test_stream_virtio_skb_merge_client,
+ .run_server = test_stream_virtio_skb_merge_server,
+ },
{},
};
diff --git a/tools/tracing/latency/latency-collector.c b/tools/tracing/latency/latency-collector.c
index 59a7f2346eab..0fd9c747d396 100644
--- a/tools/tracing/latency/latency-collector.c
+++ b/tools/tracing/latency/latency-collector.c
@@ -1584,7 +1584,7 @@ static void *do_printloop(void *arg)
/*
* Toss a coin to decide if we want to sleep before printing
* out the backtrace. The reason for this is that opening
- * /sys/kernel/debug/tracing/trace will cause a blackout of
+ * /sys/kernel/tracing/trace will cause a blackout of
* hundreds of ms, where no latencies will be noted by the
* latency tracer. Thus by randomly sleeping we try to avoid
* missing traces systematically due to this. With this option
diff --git a/tools/tracing/rtla/Makefile b/tools/tracing/rtla/Makefile
index 22e28b76f800..2456a399eb9a 100644
--- a/tools/tracing/rtla/Makefile
+++ b/tools/tracing/rtla/Makefile
@@ -119,6 +119,8 @@ install: doc_install
$(STRIP) $(DESTDIR)$(BINDIR)/rtla
@test ! -f $(DESTDIR)$(BINDIR)/osnoise || rm $(DESTDIR)$(BINDIR)/osnoise
ln -s rtla $(DESTDIR)$(BINDIR)/osnoise
+ @test ! -f $(DESTDIR)$(BINDIR)/hwnoise || rm $(DESTDIR)$(BINDIR)/hwnoise
+ ln -s rtla $(DESTDIR)$(BINDIR)/hwnoise
@test ! -f $(DESTDIR)$(BINDIR)/timerlat || rm $(DESTDIR)$(BINDIR)/timerlat
ln -s rtla $(DESTDIR)$(BINDIR)/timerlat
diff --git a/tools/tracing/rtla/src/osnoise.c b/tools/tracing/rtla/src/osnoise.c
index 4dee343909b1..3ca7a3853943 100644
--- a/tools/tracing/rtla/src/osnoise.c
+++ b/tools/tracing/rtla/src/osnoise.c
@@ -734,6 +734,113 @@ void osnoise_put_tracing_thresh(struct osnoise_context *context)
context->orig_tracing_thresh = OSNOISE_OPTION_INIT_VAL;
}
+static int osnoise_options_get_option(char *option)
+{
+ char *options = tracefs_instance_file_read(NULL, "osnoise/options", NULL);
+ char no_option[128];
+ int retval = 0;
+ char *opt;
+
+ if (!options)
+ return OSNOISE_OPTION_INIT_VAL;
+
+ /*
+ * Check first if the option is disabled.
+ */
+ snprintf(no_option, sizeof(no_option), "NO_%s", option);
+
+ opt = strstr(options, no_option);
+ if (opt)
+ goto out_free;
+
+ /*
+ * Now that it is not disabled, if the string is there, it is
+ * enabled. If the string is not there, the option does not exist.
+ */
+ opt = strstr(options, option);
+ if (opt)
+ retval = 1;
+ else
+ retval = OSNOISE_OPTION_INIT_VAL;
+
+out_free:
+ free(options);
+ return retval;
+}
+
+static int osnoise_options_set_option(char *option, bool onoff)
+{
+ char no_option[128];
+
+ if (onoff)
+ return tracefs_instance_file_write(NULL, "osnoise/options", option);
+
+ snprintf(no_option, sizeof(no_option), "NO_%s", option);
+
+ return tracefs_instance_file_write(NULL, "osnoise/options", no_option);
+}
+
+static int osnoise_get_irq_disable(struct osnoise_context *context)
+{
+ if (context->opt_irq_disable != OSNOISE_OPTION_INIT_VAL)
+ return context->opt_irq_disable;
+
+ if (context->orig_opt_irq_disable != OSNOISE_OPTION_INIT_VAL)
+ return context->orig_opt_irq_disable;
+
+ context->orig_opt_irq_disable = osnoise_options_get_option("OSNOISE_IRQ_DISABLE");
+
+ return context->orig_opt_irq_disable;
+}
+
+int osnoise_set_irq_disable(struct osnoise_context *context, bool onoff)
+{
+ int opt_irq_disable = osnoise_get_irq_disable(context);
+ int retval;
+
+ if (opt_irq_disable == OSNOISE_OPTION_INIT_VAL)
+ return -1;
+
+ if (opt_irq_disable == onoff)
+ return 0;
+
+ retval = osnoise_options_set_option("OSNOISE_IRQ_DISABLE", onoff);
+ if (retval < 0)
+ return -1;
+
+ context->opt_irq_disable = onoff;
+
+ return 0;
+}
+
+static void osnoise_restore_irq_disable(struct osnoise_context *context)
+{
+ int retval;
+
+ if (context->orig_opt_irq_disable == OSNOISE_OPTION_INIT_VAL)
+ return;
+
+ if (context->orig_opt_irq_disable == context->opt_irq_disable)
+ goto out_done;
+
+ retval = osnoise_options_set_option("OSNOISE_IRQ_DISABLE", context->orig_opt_irq_disable);
+ if (retval < 0)
+ err_msg("Could not restore original OSNOISE_IRQ_DISABLE option\n");
+
+out_done:
+ context->orig_opt_irq_disable = OSNOISE_OPTION_INIT_VAL;
+}
+
+static void osnoise_put_irq_disable(struct osnoise_context *context)
+{
+ osnoise_restore_irq_disable(context);
+
+ if (context->orig_opt_irq_disable == OSNOISE_OPTION_INIT_VAL)
+ return;
+
+ context->orig_opt_irq_disable = OSNOISE_OPTION_INIT_VAL;
+}
+
/*
* enable_osnoise - enable osnoise tracer in the trace_instance
*/
@@ -798,6 +905,9 @@ struct osnoise_context *osnoise_context_alloc(void)
context->orig_tracing_thresh = OSNOISE_OPTION_INIT_VAL;
context->tracing_thresh = OSNOISE_OPTION_INIT_VAL;
+ context->orig_opt_irq_disable = OSNOISE_OPTION_INIT_VAL;
+ context->opt_irq_disable = OSNOISE_OPTION_INIT_VAL;
+
osnoise_get_context(context);
return context;
@@ -824,6 +934,7 @@ void osnoise_put_context(struct osnoise_context *context)
osnoise_put_timerlat_period_us(context);
osnoise_put_print_stack(context);
osnoise_put_tracing_thresh(context);
+ osnoise_put_irq_disable(context);
free(context);
}
@@ -958,3 +1069,9 @@ usage:
osnoise_usage(1);
exit(1);
}
+
+int hwnoise_main(int argc, char *argv[])
+{
+ osnoise_top_main(argc, argv);
+ exit(0);
+}
diff --git a/tools/tracing/rtla/src/osnoise.h b/tools/tracing/rtla/src/osnoise.h
index 04a4384cc544..4dcf22ccd704 100644
--- a/tools/tracing/rtla/src/osnoise.h
+++ b/tools/tracing/rtla/src/osnoise.h
@@ -38,6 +38,10 @@ struct osnoise_context {
/* -1 as init value because 0 is disabled */
long long orig_print_stack;
long long print_stack;
+
+ /* -1 as init value because 0 is off */
+ int orig_opt_irq_disable;
+ int opt_irq_disable;
};
/*
@@ -79,6 +83,8 @@ void osnoise_restore_print_stack(struct osnoise_context *context);
int osnoise_set_print_stack(struct osnoise_context *context,
long long print_stack);
+int osnoise_set_irq_disable(struct osnoise_context *context, bool onoff);
+
/*
* osnoise_tool - osnoise based tool definition.
*/
@@ -97,3 +103,4 @@ struct osnoise_tool *osnoise_init_trace_tool(char *tracer);
int osnoise_hist_main(int argc, char *argv[]);
int osnoise_top_main(int argc, char **argv);
int osnoise_main(int argc, char **argv);
+int hwnoise_main(int argc, char **argv);
diff --git a/tools/tracing/rtla/src/osnoise_hist.c b/tools/tracing/rtla/src/osnoise_hist.c
index 5d7ea479ac89..13e1233690bb 100644
--- a/tools/tracing/rtla/src/osnoise_hist.c
+++ b/tools/tracing/rtla/src/osnoise_hist.c
@@ -121,6 +121,7 @@ static void osnoise_hist_update_multiple(struct osnoise_tool *tool, int cpu,
{
struct osnoise_hist_params *params = tool->params;
struct osnoise_hist_data *data = tool->data;
+ unsigned long long total_duration;
int entries = data->entries;
int bucket;
int *hist;
@@ -131,10 +132,12 @@ static void osnoise_hist_update_multiple(struct osnoise_tool *tool, int cpu,
if (data->bucket_size)
bucket = duration / data->bucket_size;
+ total_duration = duration * count;
+
hist = data->hist[cpu].samples;
data->hist[cpu].count += count;
update_min(&data->hist[cpu].min_sample, &duration);
- update_sum(&data->hist[cpu].sum_sample, &duration);
+ update_sum(&data->hist[cpu].sum_sample, &total_duration);
update_max(&data->hist[cpu].max_sample, &duration);
if (bucket < entries)
@@ -332,8 +335,8 @@ osnoise_print_summary(struct osnoise_hist_params *params,
continue;
if (data->hist[cpu].count)
- trace_seq_printf(trace->seq, "%9llu ",
- data->hist[cpu].sum_sample / data->hist[cpu].count);
+ trace_seq_printf(trace->seq, "%9.2f ",
+ ((double) data->hist[cpu].sum_sample) / data->hist[cpu].count);
else
trace_seq_printf(trace->seq, " - ");
}
diff --git a/tools/tracing/rtla/src/osnoise_top.c b/tools/tracing/rtla/src/osnoise_top.c
index 76479bfb2922..562f2e4b18c5 100644
--- a/tools/tracing/rtla/src/osnoise_top.c
+++ b/tools/tracing/rtla/src/osnoise_top.c
@@ -14,6 +14,11 @@
#include "osnoise.h"
#include "utils.h"
+enum osnoise_mode {
+ MODE_OSNOISE = 0,
+ MODE_HWNOISE
+};
+
/*
* osnoise top parameters
*/
@@ -32,6 +37,7 @@ struct osnoise_top_params {
int set_sched;
struct sched_attr sched_param;
struct trace_events *events;
+ enum osnoise_mode mode;
};
struct osnoise_top_cpu {
@@ -143,15 +149,23 @@ osnoise_top_handler(struct trace_seq *s, struct tep_record *record,
*/
static void osnoise_top_header(struct osnoise_tool *top)
{
+ struct osnoise_top_params *params = top->params;
struct trace_seq *s = top->trace.seq;
char duration[26];
get_duration(top->start_time, duration, sizeof(duration));
trace_seq_printf(s, "\033[2;37;40m");
- trace_seq_printf(s, " Operating System Noise");
- trace_seq_printf(s, " ");
- trace_seq_printf(s, " ");
+ trace_seq_printf(s, " ");
+
+ if (params->mode == MODE_OSNOISE) {
+ trace_seq_printf(s, "Operating System Noise");
+ trace_seq_printf(s, " ");
+ } else if (params->mode == MODE_HWNOISE) {
+ trace_seq_printf(s, "Hardware-related Noise");
+ }
+
+ trace_seq_printf(s, " ");
trace_seq_printf(s, "\033[0;0;0m");
trace_seq_printf(s, "\n");
@@ -162,7 +176,14 @@ static void osnoise_top_header(struct osnoise_tool *top)
trace_seq_printf(s, " Noise ");
trace_seq_printf(s, " %% CPU Aval ");
trace_seq_printf(s, " Max Noise Max Single ");
- trace_seq_printf(s, " HW NMI IRQ Softirq Thread");
+ trace_seq_printf(s, " HW NMI");
+
+ if (params->mode == MODE_HWNOISE)
+ goto eol;
+
+ trace_seq_printf(s, " IRQ Softirq Thread");
+
+eol:
trace_seq_printf(s, "\033[0;0;0m");
trace_seq_printf(s, "\n");
}
@@ -181,6 +202,7 @@ static void clear_terminal(struct trace_seq *seq)
*/
static void osnoise_top_print(struct osnoise_tool *tool, int cpu)
{
+ struct osnoise_top_params *params = tool->params;
struct trace_seq *s = tool->trace.seq;
struct osnoise_top_cpu *cpu_data;
struct osnoise_top_data *data;
@@ -205,6 +227,12 @@ static void osnoise_top_print(struct osnoise_tool *tool, int cpu)
trace_seq_printf(s, "%12llu ", cpu_data->hw_count);
trace_seq_printf(s, "%12llu ", cpu_data->nmi_count);
+
+ if (params->mode == MODE_HWNOISE) {
+ trace_seq_printf(s, "\n");
+ return;
+ }
+
trace_seq_printf(s, "%12llu ", cpu_data->irq_count);
trace_seq_printf(s, "%12llu ", cpu_data->softirq_count);
trace_seq_printf(s, "%12llu\n", cpu_data->thread_count);
@@ -241,12 +269,12 @@ osnoise_print_stats(struct osnoise_top_params *params, struct osnoise_tool *top)
/*
* osnoise_top_usage - prints osnoise top usage message
*/
-void osnoise_top_usage(char *usage)
+static void osnoise_top_usage(struct osnoise_top_params *params, char *usage)
{
int i;
static const char * const msg[] = {
- " usage: rtla osnoise [top] [-h] [-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\",
+ " [-h] [-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\",
" [-T us] [-t[=file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\",
" [-c cpu-list] [-P priority]",
"",
@@ -277,9 +305,22 @@ void osnoise_top_usage(char *usage)
if (usage)
fprintf(stderr, "%s\n", usage);
- fprintf(stderr, "rtla osnoise top: a per-cpu summary of the OS noise (version %s)\n",
+ if (params->mode == MODE_OSNOISE) {
+ fprintf(stderr,
+ "rtla osnoise top: a per-cpu summary of the OS noise (version %s)\n",
VERSION);
+ fprintf(stderr, " usage: rtla osnoise [top]");
+ }
+
+ if (params->mode == MODE_HWNOISE) {
+ fprintf(stderr,
+ "rtla hwnoise: a summary of hardware-related noise (version %s)\n",
+ VERSION);
+
+ fprintf(stderr, " usage: rtla hwnoise");
+ }
+
for (i = 0; msg[i]; i++)
fprintf(stderr, "%s\n", msg[i]);
exit(1);
@@ -299,6 +340,9 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv)
if (!params)
exit(1);
+ if (strcmp(argv[0], "hwnoise") == 0)
+ params->mode = MODE_HWNOISE;
+
while (1) {
static struct option long_options[] = {
{"auto", required_argument, 0, 'a'},
@@ -345,7 +389,7 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv)
case 'c':
retval = parse_cpu_list(optarg, &params->monitored_cpus);
if (retval)
- osnoise_top_usage("\nInvalid -c cpu list\n");
+ osnoise_top_usage(params, "\nInvalid -c cpu list\n");
params->cpus = optarg;
break;
case 'D':
@@ -354,7 +398,7 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv)
case 'd':
params->duration = parse_seconds_duration(optarg);
if (!params->duration)
- osnoise_top_usage("Invalid -D duration\n");
+ osnoise_top_usage(params, "Invalid -D duration\n");
break;
case 'e':
tevent = trace_event_alloc(optarg);
@@ -370,17 +414,17 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv)
break;
case 'h':
case '?':
- osnoise_top_usage(NULL);
+ osnoise_top_usage(params, NULL);
break;
case 'p':
params->period = get_llong_from_str(optarg);
if (params->period > 10000000)
- osnoise_top_usage("Period longer than 10 s\n");
+ osnoise_top_usage(params, "Period longer than 10 s\n");
break;
case 'P':
retval = parse_prio(optarg, &params->sched_param);
if (retval == -1)
- osnoise_top_usage("Invalid -P priority");
+ osnoise_top_usage(params, "Invalid -P priority");
params->set_sched = 1;
break;
case 'q':
@@ -389,7 +433,7 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv)
case 'r':
params->runtime = get_llong_from_str(optarg);
if (params->runtime < 100)
- osnoise_top_usage("Runtime shorter than 100 us\n");
+ osnoise_top_usage(params, "Runtime shorter than 100 us\n");
break;
case 's':
params->stop_us = get_llong_from_str(optarg);
@@ -415,7 +459,7 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv)
exit(EXIT_FAILURE);
}
} else {
- osnoise_top_usage("--trigger requires a previous -e\n");
+ osnoise_top_usage(params, "--trigger requires a previous -e\n");
}
break;
case '1': /* filter */
@@ -426,11 +470,11 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv)
exit(EXIT_FAILURE);
}
} else {
- osnoise_top_usage("--filter requires a previous -e\n");
+ osnoise_top_usage(params, "--filter requires a previous -e\n");
}
break;
default:
- osnoise_top_usage("Invalid option");
+ osnoise_top_usage(params, "Invalid option");
}
}
@@ -495,6 +539,14 @@ osnoise_top_apply_config(struct osnoise_tool *tool, struct osnoise_top_params *p
}
}
+ if (params->mode == MODE_HWNOISE) {
+ retval = osnoise_set_irq_disable(tool->context, 1);
+ if (retval) {
+ err_msg("Failed to set OSNOISE_IRQ_DISABLE option\n");
+ goto out_err;
+ }
+ }
+
return 0;
out_err:
diff --git a/tools/tracing/rtla/src/rtla.c b/tools/tracing/rtla/src/rtla.c
index 52e8f1825281..7635c70123ab 100644
--- a/tools/tracing/rtla/src/rtla.c
+++ b/tools/tracing/rtla/src/rtla.c
@@ -26,6 +26,7 @@ static void rtla_usage(int err)
"",
" commands:",
" osnoise - gives information about the operating system noise (osnoise)",
+ " hwnoise - gives information about hardware-related noise",
" timerlat - measures the timer irq and thread latency",
"",
NULL,
@@ -47,6 +48,9 @@ int run_command(int argc, char **argv, int start_position)
if (strcmp(argv[start_position], "osnoise") == 0) {
osnoise_main(argc-start_position, &argv[start_position]);
goto ran;
+ } else if (strcmp(argv[start_position], "hwnoise") == 0) {
+ hwnoise_main(argc-start_position, &argv[start_position]);
+ goto ran;
} else if (strcmp(argv[start_position], "timerlat") == 0) {
timerlat_main(argc-start_position, &argv[start_position]);
goto ran;
diff --git a/tools/tracing/rtla/src/timerlat_aa.c b/tools/tracing/rtla/src/timerlat_aa.c
new file mode 100644
index 000000000000..ec4e0f4b0e6c
--- /dev/null
+++ b/tools/tracing/rtla/src/timerlat_aa.c
@@ -0,0 +1,990 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include "utils.h"
+#include "osnoise.h"
+#include "timerlat.h"
+
+enum timelat_state {
+ TIMERLAT_INIT = 0,
+ TIMERLAT_WAITING_IRQ,
+ TIMERLAT_WAITING_THREAD,
+};
+
+#define MAX_COMM 24
+
+/*
+ * Per-cpu data statistics and data.
+ */
+struct timerlat_aa_data {
+ /* Current CPU state */
+ int curr_state;
+
+ /* timerlat IRQ latency */
+ unsigned long long tlat_irq_seqnum;
+ unsigned long long tlat_irq_latency;
+ unsigned long long tlat_irq_timstamp;
+
+ /* timerlat Thread latency */
+ unsigned long long tlat_thread_seqnum;
+ unsigned long long tlat_thread_latency;
+ unsigned long long tlat_thread_timstamp;
+
+ /*
+ * Information about the thread running when the IRQ
+ * arrived.
+ *
+ * This can be blocking or interference, depending on the
+ * priority of the thread. Assuming timerlat is the highest
+ * prio, it is blocking. If timerlat has a lower prio, it is
+ * interference.
+ * note: "unsigned long long" because they are fetch using tep_get_field_val();
+ */
+ unsigned long long run_thread_pid;
+ char run_thread_comm[MAX_COMM];
+ unsigned long long thread_blocking_duration;
+ unsigned long long max_exit_idle_latency;
+
+ /* Information about the timerlat timer irq */
+ unsigned long long timer_irq_start_time;
+ unsigned long long timer_irq_start_delay;
+ unsigned long long timer_irq_duration;
+ unsigned long long timer_exit_from_idle;
+
+ /*
+ * Information about the last IRQ before the timerlat irq
+ * arrived.
+ *
+ * If now - timestamp is <= latency, it might have influenced
+ * in the timerlat irq latency. Otherwise, ignore it.
+ */
+ unsigned long long prev_irq_duration;
+ unsigned long long prev_irq_timstamp;
+
+ /*
+ * Interference sum.
+ */
+ unsigned long long thread_nmi_sum;
+ unsigned long long thread_irq_sum;
+ unsigned long long thread_softirq_sum;
+ unsigned long long thread_thread_sum;
+
+ /*
+ * Interference task information.
+ */
+ struct trace_seq *prev_irqs_seq;
+ struct trace_seq *nmi_seq;
+ struct trace_seq *irqs_seq;
+ struct trace_seq *softirqs_seq;
+ struct trace_seq *threads_seq;
+ struct trace_seq *stack_seq;
+
+ /*
+ * Current thread.
+ */
+ char current_comm[MAX_COMM];
+ unsigned long long current_pid;
+
+ /*
+ * Is the system running a kworker?
+ */
+ unsigned long long kworker;
+ unsigned long long kworker_func;
+};
+
+/*
+ * The analysis context and system wide view
+ */
+struct timerlat_aa_context {
+ int nr_cpus;
+ int dump_tasks;
+
+ /* per CPU data */
+ struct timerlat_aa_data *taa_data;
+
+ /*
+ * required to translate function names and register
+ * events.
+ */
+ struct osnoise_tool *tool;
+};
+
+/*
+ * The data is stored as a local variable, but accessed via a helper function.
+ *
+ * It could be stored inside the trace context. But every access would
+ * require container_of() + a series of pointers. Do we need it? Not sure.
+ *
+ * For now keep it simple. If needed, store it in the tool, add the *context
+ * as a parameter in timerlat_aa_get_ctx() and do the magic there.
+ */
+static struct timerlat_aa_context *__timerlat_aa_ctx;
+
+static struct timerlat_aa_context *timerlat_aa_get_ctx(void)
+{
+ return __timerlat_aa_ctx;
+}
+
+/*
+ * timerlat_aa_get_data - Get the per-cpu data from the timerlat context
+ */
+static struct timerlat_aa_data
+*timerlat_aa_get_data(struct timerlat_aa_context *taa_ctx, int cpu)
+{
+ return &taa_ctx->taa_data[cpu];
+}
+
+/*
+ * timerlat_aa_irq_latency - Handles timerlat IRQ event
+ */
+static int timerlat_aa_irq_latency(struct timerlat_aa_data *taa_data,
+ struct trace_seq *s, struct tep_record *record,
+ struct tep_event *event)
+{
+ /*
+ * For interference, we start now looking for things that can delay
+ * the thread.
+ */
+ taa_data->curr_state = TIMERLAT_WAITING_THREAD;
+ taa_data->tlat_irq_timstamp = record->ts;
+
+ /*
+ * Zero values.
+ */
+ taa_data->thread_nmi_sum = 0;
+ taa_data->thread_irq_sum = 0;
+ taa_data->thread_softirq_sum = 0;
+ taa_data->thread_blocking_duration = 0;
+ taa_data->timer_irq_start_time = 0;
+ taa_data->timer_irq_duration = 0;
+ taa_data->timer_exit_from_idle = 0;
+
+ /*
+ * Zero interference tasks.
+ */
+ trace_seq_reset(taa_data->nmi_seq);
+ trace_seq_reset(taa_data->irqs_seq);
+ trace_seq_reset(taa_data->softirqs_seq);
+ trace_seq_reset(taa_data->threads_seq);
+
+ /* IRQ latency values */
+ tep_get_field_val(s, event, "timer_latency", record, &taa_data->tlat_irq_latency, 1);
+ tep_get_field_val(s, event, "seqnum", record, &taa_data->tlat_irq_seqnum, 1);
+
+ /* The thread that can cause blocking */
+ tep_get_common_field_val(s, event, "common_pid", record, &taa_data->run_thread_pid, 1);
+
+ /*
+ * Get exit from idle case.
+ *
+ * If it is not idle thread:
+ */
+ if (taa_data->run_thread_pid)
+ return 0;
+
+ /*
+ * if the latency is shorter than the known exit from idle:
+ */
+ if (taa_data->tlat_irq_latency < taa_data->max_exit_idle_latency)
+ return 0;
+
+ /*
+ * To be safe, ignore the cases in which an IRQ/NMI could have
+ * interfered with the timerlat IRQ.
+ */
+ if (taa_data->tlat_irq_timstamp - taa_data->tlat_irq_latency
+ < taa_data->prev_irq_timstamp + taa_data->prev_irq_duration)
+ return 0;
+
+ taa_data->max_exit_idle_latency = taa_data->tlat_irq_latency;
+
+ return 0;
+}
+
+/*
+ * timerlat_aa_thread_latency - Handles timerlat thread event
+ */
+static int timerlat_aa_thread_latency(struct timerlat_aa_data *taa_data,
+ struct trace_seq *s, struct tep_record *record,
+ struct tep_event *event)
+{
+ /*
+ * For interference, we start now looking for things that can delay
+ * the IRQ of the next cycle.
+ */
+ taa_data->curr_state = TIMERLAT_WAITING_IRQ;
+ taa_data->tlat_thread_timstamp = record->ts;
+
+ /* Thread latency values */
+ tep_get_field_val(s, event, "timer_latency", record, &taa_data->tlat_thread_latency, 1);
+ tep_get_field_val(s, event, "seqnum", record, &taa_data->tlat_thread_seqnum, 1);
+
+ return 0;
+}
+
+/*
+ * timerlat_aa_handler - Handle timerlat events
+ *
+ * This function is called to handle timerlat events recording statistics.
+ *
+ * Returns 0 on success, -1 otherwise.
+ */
+int timerlat_aa_handler(struct trace_seq *s, struct tep_record *record,
+ struct tep_event *event, void *context)
+{
+ struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx();
+ struct timerlat_aa_data *taa_data = timerlat_aa_get_data(taa_ctx, record->cpu);
+ unsigned long long thread;
+
+ if (!taa_data)
+ return -1;
+
+ tep_get_field_val(s, event, "context", record, &thread, 1);
+ if (!thread)
+ return timerlat_aa_irq_latency(taa_data, s, record, event);
+ else
+ return timerlat_aa_thread_latency(taa_data, s, record, event);
+}
+
+/*
+ * timerlat_aa_nmi_handler - Handles NMI noise
+ *
+ * It is used to collect information about interferences from NMI. It is
+ * hooked to the osnoise:nmi_noise event.
+ */
+static int timerlat_aa_nmi_handler(struct trace_seq *s, struct tep_record *record,
+ struct tep_event *event, void *context)
+{
+ struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx();
+ struct timerlat_aa_data *taa_data = timerlat_aa_get_data(taa_ctx, record->cpu);
+ unsigned long long duration;
+ unsigned long long start;
+
+ tep_get_field_val(s, event, "duration", record, &duration, 1);
+ tep_get_field_val(s, event, "start", record, &start, 1);
+
+ if (taa_data->curr_state == TIMERLAT_WAITING_IRQ) {
+ taa_data->prev_irq_duration = duration;
+ taa_data->prev_irq_timstamp = start;
+
+ trace_seq_reset(taa_data->prev_irqs_seq);
+ trace_seq_printf(taa_data->prev_irqs_seq, "\t%24s \t\t\t%9.2f us\n",
+ "nmi", ns_to_usf(duration));
+ return 0;
+ }
+
+ taa_data->thread_nmi_sum += duration;
+ trace_seq_printf(taa_data->nmi_seq, " %24s \t\t\t%9.2f us\n",
+ "nmi", ns_to_usf(duration));
+
+ return 0;
+}
+
+/*
+ * timerlat_aa_irq_handler - Handles IRQ noise
+ *
+ * It is used to collect information about interferences from IRQ. It is
+ * hooked to the osnoise:irq_noise event.
+ *
+ * It is a little bit more complex than the other because it measures:
+ * - The IRQs that can delay the timer IRQ before it happened.
+ * - The Timerlat IRQ handler
+ * - The IRQs that happened between the timerlat IRQ and the timerlat thread
+ * (IRQ interference).
+ */
+static int timerlat_aa_irq_handler(struct trace_seq *s, struct tep_record *record,
+ struct tep_event *event, void *context)
+{
+ struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx();
+ struct timerlat_aa_data *taa_data = timerlat_aa_get_data(taa_ctx, record->cpu);
+ unsigned long long expected_start;
+ unsigned long long duration;
+ unsigned long long vector;
+ unsigned long long start;
+ char *desc;
+ int val;
+
+ tep_get_field_val(s, event, "duration", record, &duration, 1);
+ tep_get_field_val(s, event, "start", record, &start, 1);
+ tep_get_field_val(s, event, "vector", record, &vector, 1);
+ desc = tep_get_field_raw(s, event, "desc", record, &val, 1);
+
+ /*
+ * Before the timerlat IRQ.
+ */
+ if (taa_data->curr_state == TIMERLAT_WAITING_IRQ) {
+ taa_data->prev_irq_duration = duration;
+ taa_data->prev_irq_timstamp = start;
+
+ trace_seq_reset(taa_data->prev_irqs_seq);
+ trace_seq_printf(taa_data->prev_irqs_seq, "\t%24s:%-3llu \t\t%9.2f us\n",
+ desc, vector, ns_to_usf(duration));
+ return 0;
+ }
+
+ /*
+ * The timerlat IRQ: taa_data->timer_irq_start_time is zeroed at
+ * the timerlat irq handler.
+ */
+ if (!taa_data->timer_irq_start_time) {
+ expected_start = taa_data->tlat_irq_timstamp - taa_data->tlat_irq_latency;
+
+ taa_data->timer_irq_start_time = start;
+ taa_data->timer_irq_duration = duration;
+
+ taa_data->timer_irq_start_delay = taa_data->timer_irq_start_time - expected_start;
+
+ /*
+ * not exit from idle.
+ */
+ if (taa_data->run_thread_pid)
+ return 0;
+
+ if (expected_start > taa_data->prev_irq_timstamp + taa_data->prev_irq_duration)
+ taa_data->timer_exit_from_idle = taa_data->timer_irq_start_delay;
+
+ return 0;
+ }
+
+ /*
+ * IRQ interference.
+ */
+ taa_data->thread_irq_sum += duration;
+ trace_seq_printf(taa_data->irqs_seq, " %24s:%-3llu \t %9.2f us\n",
+ desc, vector, ns_to_usf(duration));
+
+ return 0;
+}
+
+static char *softirq_name[] = { "HI", "TIMER", "NET_TX", "NET_RX", "BLOCK",
+ "IRQ_POLL", "TASKLET", "SCHED", "HRTIMER", "RCU" };
+
+
+/*
+ * timerlat_aa_softirq_handler - Handles Softirq noise
+ *
+ * It is used to collect information about interferences from Softirq. It is
+ * hooked to the osnoise:softirq_noise event.
+ *
+ * It is only printed in the non-rt kernel, as softirqs become thread on RT.
+ */
+static int timerlat_aa_softirq_handler(struct trace_seq *s, struct tep_record *record,
+ struct tep_event *event, void *context)
+{
+ struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx();
+ struct timerlat_aa_data *taa_data = timerlat_aa_get_data(taa_ctx, record->cpu);
+ unsigned long long duration;
+ unsigned long long vector;
+ unsigned long long start;
+
+ if (taa_data->curr_state == TIMERLAT_WAITING_IRQ)
+ return 0;
+
+ tep_get_field_val(s, event, "duration", record, &duration, 1);
+ tep_get_field_val(s, event, "start", record, &start, 1);
+ tep_get_field_val(s, event, "vector", record, &vector, 1);
+
+ taa_data->thread_softirq_sum += duration;
+
+ trace_seq_printf(taa_data->softirqs_seq, "\t%24s:%-3llu \t %9.2f us\n",
+ softirq_name[vector], vector, ns_to_usf(duration));
+ return 0;
+}
+
+/*
+ * timerlat_aa_softirq_handler - Handles thread noise
+ *
+ * It is used to collect information about interferences from threads. It is
+ * hooked to the osnoise:thread_noise event.
+ *
+ * Note: if you see thread noise, your timerlat thread was not the highest prio one.
+ */
+static int timerlat_aa_thread_handler(struct trace_seq *s, struct tep_record *record,
+ struct tep_event *event, void *context)
+{
+ struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx();
+ struct timerlat_aa_data *taa_data = timerlat_aa_get_data(taa_ctx, record->cpu);
+ unsigned long long duration;
+ unsigned long long start;
+ unsigned long long pid;
+ const char *comm;
+ int val;
+
+ if (taa_data->curr_state == TIMERLAT_WAITING_IRQ)
+ return 0;
+
+ tep_get_field_val(s, event, "duration", record, &duration, 1);
+ tep_get_field_val(s, event, "start", record, &start, 1);
+
+ tep_get_common_field_val(s, event, "common_pid", record, &pid, 1);
+ comm = tep_get_field_raw(s, event, "comm", record, &val, 1);
+
+ if (pid == taa_data->run_thread_pid && !taa_data->thread_blocking_duration) {
+ taa_data->thread_blocking_duration = duration;
+
+ if (comm)
+ strncpy(taa_data->run_thread_comm, comm, MAX_COMM);
+ else
+ sprintf(taa_data->run_thread_comm, "<...>");
+
+ } else {
+ taa_data->thread_thread_sum += duration;
+
+ trace_seq_printf(taa_data->threads_seq, "\t%24s:%-3llu \t\t%9.2f us\n",
+ comm, pid, ns_to_usf(duration));
+ }
+
+ return 0;
+}
+
+/*
+ * timerlat_aa_stack_handler - Handles timerlat IRQ stack trace
+ *
+ * Saves and parse the stack trace generated by the timerlat IRQ.
+ */
+static int timerlat_aa_stack_handler(struct trace_seq *s, struct tep_record *record,
+ struct tep_event *event, void *context)
+{
+ struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx();
+ struct timerlat_aa_data *taa_data = timerlat_aa_get_data(taa_ctx, record->cpu);
+ unsigned long *caller;
+ const char *function;
+ int val, i;
+
+ trace_seq_reset(taa_data->stack_seq);
+
+ trace_seq_printf(taa_data->stack_seq, " Blocking thread stack trace\n");
+ caller = tep_get_field_raw(s, event, "caller", record, &val, 1);
+ if (caller) {
+ for (i = 0; ; i++) {
+ function = tep_find_function(taa_ctx->tool->trace.tep, caller[i]);
+ if (!function)
+ break;
+ trace_seq_printf(taa_data->stack_seq, "\t\t-> %s\n", function);
+ }
+ }
+ return 0;
+}
+
+/*
+ * timerlat_aa_sched_switch_handler - Tracks the current thread running on the CPU
+ *
+ * Handles the sched:sched_switch event to trace the current thread running on the
+ * CPU. It is used to display the threads running on the other CPUs when the trace
+ * stops.
+ */
+static int timerlat_aa_sched_switch_handler(struct trace_seq *s, struct tep_record *record,
+ struct tep_event *event, void *context)
+{
+ struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx();
+ struct timerlat_aa_data *taa_data = timerlat_aa_get_data(taa_ctx, record->cpu);
+ const char *comm;
+ int val;
+
+ tep_get_field_val(s, event, "next_pid", record, &taa_data->current_pid, 1);
+ comm = tep_get_field_raw(s, event, "next_comm", record, &val, 1);
+
+ strncpy(taa_data->current_comm, comm, MAX_COMM);
+
+ /*
+ * If this was a kworker, clean the last kworkers that ran.
+ */
+ taa_data->kworker = 0;
+ taa_data->kworker_func = 0;
+
+ return 0;
+}
+
+/*
+ * timerlat_aa_kworker_start_handler - Tracks a kworker running on the CPU
+ *
+ * Handles workqueue:workqueue_execute_start event, keeping track of
+ * the job that a kworker could be doing in the CPU.
+ *
+ * We already catch problems of hardware related latencies caused by work queues
+ * running driver code that causes hardware stall. For example, with DRM drivers.
+ */
+static int timerlat_aa_kworker_start_handler(struct trace_seq *s, struct tep_record *record,
+ struct tep_event *event, void *context)
+{
+ struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx();
+ struct timerlat_aa_data *taa_data = timerlat_aa_get_data(taa_ctx, record->cpu);
+
+ tep_get_field_val(s, event, "work", record, &taa_data->kworker, 1);
+ tep_get_field_val(s, event, "function", record, &taa_data->kworker_func, 1);
+ return 0;
+}
+
+/*
+ * timerlat_thread_analysis - Prints the analysis of a CPU that hit a stop tracing
+ *
+ * This is the core of the analysis.
+ */
+static void timerlat_thread_analysis(struct timerlat_aa_data *taa_data, int cpu,
+ int irq_thresh, int thread_thresh)
+{
+ unsigned long long exp_irq_ts;
+ int total;
+ int irq;
+
+ /*
+ * IRQ latency or Thread latency?
+ */
+ if (taa_data->tlat_irq_seqnum > taa_data->tlat_thread_seqnum) {
+ irq = 1;
+ total = taa_data->tlat_irq_latency;
+ } else {
+ irq = 0;
+ total = taa_data->tlat_thread_latency;
+ }
+
+ /*
+ * Expected IRQ arrival time using the trace clock as the base.
+ */
+ exp_irq_ts = taa_data->timer_irq_start_time - taa_data->timer_irq_start_delay;
+
+ if (exp_irq_ts < taa_data->prev_irq_timstamp + taa_data->prev_irq_duration)
+ printf(" Previous IRQ interference: \t up to %9.2f us",
+ ns_to_usf(taa_data->prev_irq_duration));
+
+ /*
+ * The delay that the IRQ suffered before starting.
+ */
+ printf(" IRQ handler delay: %16s %9.2f us (%.2f %%)\n",
+ (ns_to_usf(taa_data->timer_exit_from_idle) > 10) ? "(exit from idle)" : "",
+ ns_to_usf(taa_data->timer_irq_start_delay),
+ ns_to_per(total, taa_data->timer_irq_start_delay));
+
+ /*
+ * Timerlat IRQ.
+ */
+ printf(" IRQ latency: \t\t\t\t %9.2f us\n",
+ ns_to_usf(taa_data->tlat_irq_latency));
+
+ if (irq) {
+ /*
+ * If the trace stopped due to IRQ, the other events will not happen
+ * because... the trace stopped :-).
+ *
+ * That is all folks, the stack trace was printed before the stop,
+ * so it will be displayed, it is the key.
+ */
+ printf(" Blocking thread:\n");
+ printf(" %24s:%-9llu\n",
+ taa_data->run_thread_comm, taa_data->run_thread_pid);
+ } else {
+ /*
+ * The duration of the IRQ handler that handled the timerlat IRQ.
+ */
+ printf(" Timerlat IRQ duration: \t\t %9.2f us (%.2f %%)\n",
+ ns_to_usf(taa_data->timer_irq_duration),
+ ns_to_per(total, taa_data->timer_irq_duration));
+
+ /*
+ * The amount of time that the current thread postponed the scheduler.
+ *
+ * Recalling that it is net from NMI/IRQ/Softirq interference, so there
+ * is no need to compute values here.
+ */
+ printf(" Blocking thread: \t\t\t %9.2f us (%.2f %%)\n",
+ ns_to_usf(taa_data->thread_blocking_duration),
+ ns_to_per(total, taa_data->thread_blocking_duration));
+
+ printf(" %24s:%-9llu %9.2f us\n",
+ taa_data->run_thread_comm, taa_data->run_thread_pid,
+ ns_to_usf(taa_data->thread_blocking_duration));
+ }
+
+ /*
+ * Print the stack trace!
+ */
+ trace_seq_do_printf(taa_data->stack_seq);
+
+ /*
+ * NMIs can happen during the IRQ, so they are always possible.
+ */
+ if (taa_data->thread_nmi_sum)
+ printf(" NMI interference \t\t\t %9.2f us (%.2f %%)\n",
+ ns_to_usf(taa_data->thread_nmi_sum),
+ ns_to_per(total, taa_data->thread_nmi_sum));
+
+ /*
+ * If it is an IRQ latency, the other factors can be skipped.
+ */
+ if (irq)
+ goto print_total;
+
+ /*
+ * Prints the interference caused by IRQs to the thread latency.
+ */
+ if (taa_data->thread_irq_sum) {
+ printf(" IRQ interference \t\t\t %9.2f us (%.2f %%)\n",
+ ns_to_usf(taa_data->thread_irq_sum),
+ ns_to_per(total, taa_data->thread_irq_sum));
+
+ trace_seq_do_printf(taa_data->irqs_seq);
+ }
+
+ /*
+ * Prints the interference caused by Softirqs to the thread latency.
+ */
+ if (taa_data->thread_softirq_sum) {
+ printf(" Softirq interference \t\t\t %9.2f us (%.2f %%)\n",
+ ns_to_usf(taa_data->thread_softirq_sum),
+ ns_to_per(total, taa_data->thread_softirq_sum));
+
+ trace_seq_do_printf(taa_data->softirqs_seq);
+ }
+
+ /*
+ * Prints the interference caused by other threads to the thread latency.
+ *
+ * If this happens, your timerlat is not the highest prio. OK, migration
+ * thread can happen. But otherwise, you are not measuring the "scheduling
+ * latency" only, and here is the difference from scheduling latency and
+ * timer handling latency.
+ */
+ if (taa_data->thread_thread_sum) {
+ printf(" Thread interference \t\t\t %9.2f us (%.2f %%)\n",
+ ns_to_usf(taa_data->thread_thread_sum),
+ ns_to_per(total, taa_data->thread_thread_sum));
+
+ trace_seq_do_printf(taa_data->threads_seq);
+ }
+
+ /*
+ * Done.
+ */
+print_total:
+ printf("------------------------------------------------------------------------\n");
+ printf(" %s latency: \t\t\t %9.2f us (100%%)\n", irq ? "IRQ" : "Thread",
+ ns_to_usf(total));
+}
+
+/**
+ * timerlat_auto_analysis - Analyze the collected data
+ */
+void timerlat_auto_analysis(int irq_thresh, int thread_thresh)
+{
+ struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx();
+ unsigned long long max_exit_from_idle = 0;
+ struct timerlat_aa_data *taa_data;
+ int max_exit_from_idle_cpu;
+ struct tep_handle *tep;
+ int cpu;
+
+ /* bring stop tracing to the ns scale */
+ irq_thresh = irq_thresh * 1000;
+ thread_thresh = thread_thresh * 1000;
+
+ for (cpu = 0; cpu < taa_ctx->nr_cpus; cpu++) {
+ taa_data = timerlat_aa_get_data(taa_ctx, cpu);
+
+ if (irq_thresh && taa_data->tlat_irq_latency >= irq_thresh) {
+ printf("## CPU %d hit stop tracing, analyzing it ##\n", cpu);
+ timerlat_thread_analysis(taa_data, cpu, irq_thresh, thread_thresh);
+ } else if (thread_thresh && (taa_data->tlat_thread_latency) >= thread_thresh) {
+ printf("## CPU %d hit stop tracing, analyzing it ##\n", cpu);
+ timerlat_thread_analysis(taa_data, cpu, irq_thresh, thread_thresh);
+ }
+
+ if (taa_data->max_exit_idle_latency > max_exit_from_idle) {
+ max_exit_from_idle = taa_data->max_exit_idle_latency;
+ max_exit_from_idle_cpu = cpu;
+ }
+
+ }
+
+ if (max_exit_from_idle) {
+ printf("\n");
+ printf("Max timerlat IRQ latency from idle: %.2f us in cpu %d\n",
+ ns_to_usf(max_exit_from_idle), max_exit_from_idle_cpu);
+ }
+ if (!taa_ctx->dump_tasks)
+ return;
+
+ printf("\n");
+ printf("Printing CPU tasks:\n");
+ for (cpu = 0; cpu < taa_ctx->nr_cpus; cpu++) {
+ taa_data = timerlat_aa_get_data(taa_ctx, cpu);
+ tep = taa_ctx->tool->trace.tep;
+
+ printf(" [%.3d] %24s:%llu", cpu, taa_data->current_comm, taa_data->current_pid);
+
+ if (taa_data->kworker_func)
+ printf(" kworker:%s:%s",
+ tep_find_function(tep, taa_data->kworker) ? : "<...>",
+ tep_find_function(tep, taa_data->kworker_func));
+ printf("\n");
+ }
+
+}
+
+/*
+ * timerlat_aa_destroy_seqs - Destroy seq files used to store parsed data
+ */
+static void timerlat_aa_destroy_seqs(struct timerlat_aa_context *taa_ctx)
+{
+ struct timerlat_aa_data *taa_data;
+ int i;
+
+ if (!taa_ctx->taa_data)
+ return;
+
+ for (i = 0; i < taa_ctx->nr_cpus; i++) {
+ taa_data = timerlat_aa_get_data(taa_ctx, i);
+
+ if (taa_data->prev_irqs_seq) {
+ trace_seq_destroy(taa_data->prev_irqs_seq);
+ free(taa_data->prev_irqs_seq);
+ }
+
+ if (taa_data->nmi_seq) {
+ trace_seq_destroy(taa_data->nmi_seq);
+ free(taa_data->nmi_seq);
+ }
+
+ if (taa_data->irqs_seq) {
+ trace_seq_destroy(taa_data->irqs_seq);
+ free(taa_data->irqs_seq);
+ }
+
+ if (taa_data->softirqs_seq) {
+ trace_seq_destroy(taa_data->softirqs_seq);
+ free(taa_data->softirqs_seq);
+ }
+
+ if (taa_data->threads_seq) {
+ trace_seq_destroy(taa_data->threads_seq);
+ free(taa_data->threads_seq);
+ }
+
+ if (taa_data->stack_seq) {
+ trace_seq_destroy(taa_data->stack_seq);
+ free(taa_data->stack_seq);
+ }
+ }
+}
+
+/*
+ * timerlat_aa_init_seqs - Init seq files used to store parsed information
+ *
+ * Instead of keeping data structures to store raw data, use seq files to
+ * store parsed data.
+ *
+ * Allocates and initialize seq files.
+ *
+ * Returns 0 on success, -1 otherwise.
+ */
+static int timerlat_aa_init_seqs(struct timerlat_aa_context *taa_ctx)
+{
+ struct timerlat_aa_data *taa_data;
+ int i;
+
+ for (i = 0; i < taa_ctx->nr_cpus; i++) {
+
+ taa_data = timerlat_aa_get_data(taa_ctx, i);
+
+ taa_data->prev_irqs_seq = calloc(1, sizeof(*taa_data->prev_irqs_seq));
+ if (!taa_data->prev_irqs_seq)
+ goto out_err;
+
+ trace_seq_init(taa_data->prev_irqs_seq);
+
+ taa_data->nmi_seq = calloc(1, sizeof(*taa_data->nmi_seq));
+ if (!taa_data->nmi_seq)
+ goto out_err;
+
+ trace_seq_init(taa_data->nmi_seq);
+
+ taa_data->irqs_seq = calloc(1, sizeof(*taa_data->irqs_seq));
+ if (!taa_data->irqs_seq)
+ goto out_err;
+
+ trace_seq_init(taa_data->irqs_seq);
+
+ taa_data->softirqs_seq = calloc(1, sizeof(*taa_data->softirqs_seq));
+ if (!taa_data->softirqs_seq)
+ goto out_err;
+
+ trace_seq_init(taa_data->softirqs_seq);
+
+ taa_data->threads_seq = calloc(1, sizeof(*taa_data->threads_seq));
+ if (!taa_data->threads_seq)
+ goto out_err;
+
+ trace_seq_init(taa_data->threads_seq);
+
+ taa_data->stack_seq = calloc(1, sizeof(*taa_data->stack_seq));
+ if (!taa_data->stack_seq)
+ goto out_err;
+
+ trace_seq_init(taa_data->stack_seq);
+ }
+
+ return 0;
+
+out_err:
+ timerlat_aa_destroy_seqs(taa_ctx);
+ return -1;
+}
+
+/*
+ * timerlat_aa_unregister_events - Unregister events used in the auto-analysis
+ */
+static void timerlat_aa_unregister_events(struct osnoise_tool *tool, int dump_tasks)
+{
+ tracefs_event_disable(tool->trace.inst, "osnoise", NULL);
+
+ tep_unregister_event_handler(tool->trace.tep, -1, "osnoise", "nmi_noise",
+ timerlat_aa_nmi_handler, tool);
+
+ tep_unregister_event_handler(tool->trace.tep, -1, "osnoise", "irq_noise",
+ timerlat_aa_irq_handler, tool);
+
+ tep_unregister_event_handler(tool->trace.tep, -1, "osnoise", "softirq_noise",
+ timerlat_aa_softirq_handler, tool);
+
+ tep_unregister_event_handler(tool->trace.tep, -1, "osnoise", "thread_noise",
+ timerlat_aa_thread_handler, tool);
+
+ tep_unregister_event_handler(tool->trace.tep, -1, "ftrace", "kernel_stack",
+ timerlat_aa_stack_handler, tool);
+ if (!dump_tasks)
+ return;
+
+ tracefs_event_disable(tool->trace.inst, "sched", "sched_switch");
+ tep_unregister_event_handler(tool->trace.tep, -1, "sched", "sched_switch",
+ timerlat_aa_sched_switch_handler, tool);
+
+ tracefs_event_disable(tool->trace.inst, "workqueue", "workqueue_execute_start");
+ tep_unregister_event_handler(tool->trace.tep, -1, "workqueue", "workqueue_execute_start",
+ timerlat_aa_kworker_start_handler, tool);
+}
+
+/*
+ * timerlat_aa_register_events - Register events used in the auto-analysis
+ *
+ * Returns 0 on success, -1 otherwise.
+ */
+static int timerlat_aa_register_events(struct osnoise_tool *tool, int dump_tasks)
+{
+ int retval;
+
+ /*
+ * register auto-analysis handlers.
+ */
+ retval = tracefs_event_enable(tool->trace.inst, "osnoise", NULL);
+ if (retval < 0 && !errno) {
+ err_msg("Could not find osnoise events\n");
+ goto out_err;
+ }
+
+ tep_register_event_handler(tool->trace.tep, -1, "osnoise", "nmi_noise",
+ timerlat_aa_nmi_handler, tool);
+
+ tep_register_event_handler(tool->trace.tep, -1, "osnoise", "irq_noise",
+ timerlat_aa_irq_handler, tool);
+
+ tep_register_event_handler(tool->trace.tep, -1, "osnoise", "softirq_noise",
+ timerlat_aa_softirq_handler, tool);
+
+ tep_register_event_handler(tool->trace.tep, -1, "osnoise", "thread_noise",
+ timerlat_aa_thread_handler, tool);
+
+ tep_register_event_handler(tool->trace.tep, -1, "ftrace", "kernel_stack",
+ timerlat_aa_stack_handler, tool);
+
+ if (!dump_tasks)
+ return 0;
+
+ /*
+ * Dump task events.
+ */
+ retval = tracefs_event_enable(tool->trace.inst, "sched", "sched_switch");
+ if (retval < 0 && !errno) {
+ err_msg("Could not find sched_switch\n");
+ goto out_err;
+ }
+
+ tep_register_event_handler(tool->trace.tep, -1, "sched", "sched_switch",
+ timerlat_aa_sched_switch_handler, tool);
+
+ retval = tracefs_event_enable(tool->trace.inst, "workqueue", "workqueue_execute_start");
+ if (retval < 0 && !errno) {
+ err_msg("Could not find workqueue_execute_start\n");
+ goto out_err;
+ }
+
+ tep_register_event_handler(tool->trace.tep, -1, "workqueue", "workqueue_execute_start",
+ timerlat_aa_kworker_start_handler, tool);
+
+ return 0;
+
+out_err:
+ timerlat_aa_unregister_events(tool, dump_tasks);
+ return -1;
+}
+
+/**
+ * timerlat_aa_destroy - Destroy timerlat auto-analysis
+ */
+void timerlat_aa_destroy(void)
+{
+ struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx();
+
+ if (!taa_ctx)
+ return;
+
+ if (!taa_ctx->taa_data)
+ goto out_ctx;
+
+ timerlat_aa_unregister_events(taa_ctx->tool, taa_ctx->dump_tasks);
+ timerlat_aa_destroy_seqs(taa_ctx);
+ free(taa_ctx->taa_data);
+out_ctx:
+ free(taa_ctx);
+}
+
+/**
+ * timerlat_aa_init - Initialize timerlat auto-analysis
+ *
+ * Returns 0 on success, -1 otherwise.
+ */
+int timerlat_aa_init(struct osnoise_tool *tool, int nr_cpus, int dump_tasks)
+{
+ struct timerlat_aa_context *taa_ctx;
+ int retval;
+
+ taa_ctx = calloc(1, sizeof(*taa_ctx));
+ if (!taa_ctx)
+ return -1;
+
+ __timerlat_aa_ctx = taa_ctx;
+
+ taa_ctx->nr_cpus = nr_cpus;
+ taa_ctx->tool = tool;
+ taa_ctx->dump_tasks = dump_tasks;
+
+ taa_ctx->taa_data = calloc(nr_cpus, sizeof(*taa_ctx->taa_data));
+ if (!taa_ctx->taa_data)
+ goto out_err;
+
+ retval = timerlat_aa_init_seqs(taa_ctx);
+ if (retval)
+ goto out_err;
+
+ retval = timerlat_aa_register_events(tool, dump_tasks);
+ if (retval)
+ goto out_err;
+
+ return 0;
+
+out_err:
+ timerlat_aa_destroy();
+ return -1;
+}
diff --git a/tools/tracing/rtla/src/timerlat_aa.h b/tools/tracing/rtla/src/timerlat_aa.h
new file mode 100644
index 000000000000..d4f6ca7e342a
--- /dev/null
+++ b/tools/tracing/rtla/src/timerlat_aa.h
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
+ */
+
+int timerlat_aa_init(struct osnoise_tool *tool, int nr_cpus, int dump_task);
+void timerlat_aa_destroy(void);
+
+int timerlat_aa_handler(struct trace_seq *s, struct tep_record *record,
+ struct tep_event *event, void *context);
+
+void timerlat_auto_analysis(int irq_thresh, int thread_thresh);
diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c
index 334271935222..eea5b3357e27 100644
--- a/tools/tracing/rtla/src/timerlat_top.c
+++ b/tools/tracing/rtla/src/timerlat_top.c
@@ -10,10 +10,12 @@
#include <unistd.h>
#include <stdio.h>
#include <time.h>
+#include <errno.h>
#include "utils.h"
#include "osnoise.h"
#include "timerlat.h"
+#include "timerlat_aa.h"
struct timerlat_top_params {
char *cpus;
@@ -30,6 +32,8 @@ struct timerlat_top_params {
int quiet;
int set_sched;
int dma_latency;
+ int no_aa;
+ int dump_tasks;
struct sched_attr sched_param;
struct trace_events *events;
};
@@ -130,17 +134,22 @@ timerlat_top_handler(struct trace_seq *s, struct tep_record *record,
struct tep_event *event, void *context)
{
struct trace_instance *trace = context;
+ struct timerlat_top_params *params;
unsigned long long latency, thread;
struct osnoise_tool *top;
int cpu = record->cpu;
top = container_of(trace, struct osnoise_tool, trace);
+ params = top->params;
tep_get_field_val(s, event, "context", record, &thread, 1);
tep_get_field_val(s, event, "timer_latency", record, &latency, 1);
timerlat_top_update(top, cpu, thread, latency);
+ if (!params->no_aa)
+ timerlat_aa_handler(s, record, event, context);
+
return 0;
}
@@ -281,11 +290,13 @@ static void timerlat_top_usage(char *usage)
" -c/--cpus cpus: run the tracer only on the given cpus",
" -d/--duration time[m|h|d]: duration of the session in seconds",
" -D/--debug: print debug info",
+ " --dump-tasks: prints the task running on all CPUs if stop conditions are met (depends on !--no-aa)",
" -t/--trace[=file]: save the stopped trace to [file|timerlat_trace.txt]",
" -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed",
" --filter <command>: enable a trace event filter to the previous -e event",
" --trigger <command>: enable a trace event trigger to the previous -e event",
" -n/--nano: display data in nanoseconds",
+ " --no-aa: disable auto-analysis, reducing rtla timerlat cpu usage",
" -q/--quiet print only a summary at the end",
" --dma-latency us: set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency",
" -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters",
@@ -349,13 +360,15 @@ static struct timerlat_top_params
{"trigger", required_argument, 0, '0'},
{"filter", required_argument, 0, '1'},
{"dma-latency", required_argument, 0, '2'},
+ {"no-aa", no_argument, 0, '3'},
+ {"dump-tasks", no_argument, 0, '4'},
{0, 0, 0, 0}
};
/* getopt_long stores the option index here. */
int option_index = 0;
- c = getopt_long(argc, argv, "a:c:d:De:hi:np:P:qs:t::T:0:1:2:",
+ c = getopt_long(argc, argv, "a:c:d:De:hi:np:P:qs:t::T:0:1:2:34",
long_options, &option_index);
/* detect the end of the options. */
@@ -368,13 +381,13 @@ static struct timerlat_top_params
/* set thread stop to auto_thresh */
params->stop_total_us = auto_thresh;
+ params->stop_us = auto_thresh;
/* get stack trace */
params->print_stack = auto_thresh;
/* set trace */
params->trace_output = "timerlat_trace.txt";
-
break;
case 'c':
retval = parse_cpu_list(optarg, &params->monitored_cpus);
@@ -437,6 +450,7 @@ static struct timerlat_top_params
params->trace_output = &optarg[1];
else
params->trace_output = "timerlat_trace.txt";
+
break;
case '0': /* trigger */
if (params->events) {
@@ -467,6 +481,12 @@ static struct timerlat_top_params
exit(EXIT_FAILURE);
}
break;
+ case '3': /* no-aa */
+ params->no_aa = 1;
+ break;
+ case '4':
+ params->dump_tasks = 1;
+ break;
default:
timerlat_top_usage("Invalid option");
}
@@ -477,6 +497,12 @@ static struct timerlat_top_params
exit(EXIT_FAILURE);
}
+ /*
+ * Auto analysis only happens if stop tracing, thus:
+ */
+ if (!params->stop_us && !params->stop_total_us)
+ params->no_aa = 1;
+
return params;
}
@@ -547,6 +573,7 @@ static struct osnoise_tool
{
struct osnoise_tool *top;
int nr_cpus;
+ int retval;
nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
@@ -563,6 +590,16 @@ static struct osnoise_tool
tep_register_event_handler(top->trace.tep, -1, "ftrace", "timerlat",
timerlat_top_handler, top);
+ /*
+ * If no auto analysis, we are ready.
+ */
+ if (params->no_aa)
+ return top;
+
+ retval = timerlat_aa_init(top, nr_cpus, params->dump_tasks);
+ if (retval)
+ goto out_err;
+
return top;
out_err:
@@ -688,6 +725,10 @@ int timerlat_top_main(int argc, char *argv[])
if (trace_is_off(&top->trace, &record->trace)) {
printf("rtla timerlat hit stop tracing\n");
+
+ if (!params->no_aa)
+ timerlat_auto_analysis(params->stop_us, params->stop_total_us);
+
if (params->trace_output) {
printf(" Saving trace to %s\n", params->trace_output);
save_trace_to_file(record->trace.inst, params->trace_output);
@@ -701,6 +742,7 @@ out_top:
params->events = NULL;
out_free:
timerlat_free_top(top->data);
+ timerlat_aa_destroy();
osnoise_destroy_tool(record);
osnoise_destroy_tool(top);
free(params);
diff --git a/tools/tracing/rtla/src/utils.h b/tools/tracing/rtla/src/utils.h
index 5571afd3b549..90e4f52a030b 100644
--- a/tools/tracing/rtla/src/utils.h
+++ b/tools/tracing/rtla/src/utils.h
@@ -56,3 +56,6 @@ struct sched_attr {
int parse_prio(char *arg, struct sched_attr *sched_param);
int set_comm_sched_attr(const char *comm_prefix, struct sched_attr *attr);
int set_cpu_dma_latency(int32_t latency);
+
+#define ns_to_usf(x) (((double)x/1000))
+#define ns_to_per(total, part) ((part * 100) / (double)total)
diff --git a/tools/verification/rv/src/in_kernel.c b/tools/verification/rv/src/in_kernel.c
index 50848d79b38b..ad28582bcf2b 100644
--- a/tools/verification/rv/src/in_kernel.c
+++ b/tools/verification/rv/src/in_kernel.c
@@ -519,7 +519,7 @@ static void ikm_usage_print_reactors(void)
start = ++end;
end = strstr(start, "\n");
- };
+ }
fprintf(stderr, "\n");
}
diff --git a/tools/virtio/.gitignore b/tools/virtio/.gitignore
index 075588c4da08..9934d48d9a55 100644
--- a/tools/virtio/.gitignore
+++ b/tools/virtio/.gitignore
@@ -2,3 +2,4 @@
*.d
virtio_test
vringh_test
+virtio-trace/trace-agent
diff --git a/tools/virtio/Makefile b/tools/virtio/Makefile
index 1b25cc7c64bb..7b7139d97d74 100644
--- a/tools/virtio/Makefile
+++ b/tools/virtio/Makefile
@@ -4,7 +4,7 @@ test: virtio_test vringh_test
virtio_test: virtio_ring.o virtio_test.o
vringh_test: vringh_test.o vringh.o virtio_ring.o
-CFLAGS += -g -O2 -Werror -Wno-maybe-uninitialized -Wall -I. -I../include/ -I ../../usr/include/ -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -U_FORTIFY_SOURCE -include ../../include/linux/kconfig.h
+CFLAGS += -g -O2 -Werror -Wno-maybe-uninitialized -Wall -I. -I../include/ -I ../../usr/include/ -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -U_FORTIFY_SOURCE -include ../../include/linux/kconfig.h -mfunction-return=thunk -fcf-protection=none -mindirect-branch-register
CFLAGS += -pthread
LDFLAGS += -pthread
vpath %.c ../../drivers/virtio ../../drivers/vhost
diff --git a/tools/virtio/virtio-trace/README b/tools/virtio/virtio-trace/README
index b64845b823ab..4fb9368bf751 100644
--- a/tools/virtio/virtio-trace/README
+++ b/tools/virtio/virtio-trace/README
@@ -61,7 +61,7 @@ and
id=channel0,name=agent-ctl-path\
##data path##
-chardev pipe,id=charchannel1,path=/tmp/virtio-trace/trace-path-cpu0\
- -device virtserialport,bus=virtio-serial0.0,nr=2,chardev=charchannel0,\
+ -device virtserialport,bus=virtio-serial0.0,nr=2,chardev=charchannel1,\
id=channel1,name=trace-path-cpu0\
...