Add page_fault_loop to repeatedly discard an anonymous page with MADV_DONTNEED and read it again. Each demand fault is resolved without signal delivery, so the load must be retried at the same PC.
For example, an expected int/iret pair is:
int 5ccadc => ffff800080010c00 insn: 00 00 40 39 ... iret ffff800080012284 => 5ccadc insn: e0 03 9f d6
The LDRB W0, [X0] at 0x5ccadc faults before completing. After resolving the page fault, the kernel returns via ERET to the same PC to retry it.
Assisted-by: Codex:gpt-6 Signed-off-by: Leo Yan leo.yan@arm.com --- tools/perf/tests/builtin-test.c | 1 + .../perf/tests/shell/coresight/abort_entry_exit.sh | 21 ++++++++++++ tools/perf/tests/tests.h | 1 + tools/perf/tests/workloads/Build | 2 ++ tools/perf/tests/workloads/page_fault_loop.c | 37 ++++++++++++++++++++++ 5 files changed, 62 insertions(+)
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 8eab221f115e75a3ef90be975d4ed0558ceb2b16..1af8dfd45d1a2ff5c3206234cd75fb173e122d88 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -172,6 +172,7 @@ static struct test_workload *workloads[] = { &workload__landlock, &workload__traploop, &workload__branch_not_taken_loop, + &workload__page_fault_loop, &workload__inlineloop, &workload__jitdump, &workload__context_switch_loop, diff --git a/tools/perf/tests/shell/coresight/abort_entry_exit.sh b/tools/perf/tests/shell/coresight/abort_entry_exit.sh new file mode 100755 index 0000000000000000000000000000000000000000..483cf39b3966e89591d4e06d03a3fc6709fc3dc9 --- /dev/null +++ b/tools/perf/tests/shell/coresight/abort_entry_exit.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# CoreSight abort entry and exit (exclusive) + +# shellcheck source=../lib/coresight_exception.sh +. "$(dirname "$0")/../lib/coresight_exception.sh" + +test_abort_entry_exit() +{ + # A demand page fault retries the load at the same PC after resolution. + if ! record_trace page_fault_loop; then + cat "$tmpdir/record.log" + echo "Failed to record the page fault workload" + return 1 + fi + decode_trace || return 1 + check_exception_pairs "int" "iret" 0 || return 1 +} + +setup_exception_test || exit $? +test_abort_entry_exit diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 0a61129b31ab6997616f7d04d29af73961f4f861..0fc02c5b060133ef27b80de5c868d798d65a7c53 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -247,6 +247,7 @@ DECLARE_WORKLOAD(datasym); DECLARE_WORKLOAD(landlock); DECLARE_WORKLOAD(traploop); DECLARE_WORKLOAD(branch_not_taken_loop); +DECLARE_WORKLOAD(page_fault_loop); DECLARE_WORKLOAD(inlineloop); DECLARE_WORKLOAD(jitdump); DECLARE_WORKLOAD(context_switch_loop); diff --git a/tools/perf/tests/workloads/Build b/tools/perf/tests/workloads/Build index ca22a82073ed08de5d4a27d0a3b33ddcba2e3f3e..e18fdaa6fd1927ed406e703a7ecbcc19a111caf3 100644 --- a/tools/perf/tests/workloads/Build +++ b/tools/perf/tests/workloads/Build @@ -10,6 +10,7 @@ perf-test-y += datasym.o perf-test-y += landlock.o perf-test-y += traploop.o perf-test-y += branch_not_taken_loop.o +perf-test-y += page_fault_loop.o perf-test-y += inlineloop.o perf-test-y += jitdump.o perf-test-y += context_switch_loop.o @@ -27,6 +28,7 @@ CFLAGS_brstack.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE CFLAGS_datasym.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE CFLAGS_traploop.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE CFLAGS_branch_not_taken_loop.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE +CFLAGS_page_fault_loop.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE CFLAGS_inlineloop.o = -g -O2 CFLAGS_deterministic.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE CFLAGS_named_threads.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE diff --git a/tools/perf/tests/workloads/page_fault_loop.c b/tools/perf/tests/workloads/page_fault_loop.c new file mode 100644 index 0000000000000000000000000000000000000000..4afbc4ab0a55400360facdc3a99ea5a5a9d52596 --- /dev/null +++ b/tools/perf/tests/workloads/page_fault_loop.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/compiler.h> +#include <stdio.h> +#include <sys/mman.h> +#include <unistd.h> +#include "../tests.h" + +static int page_fault_loop(int argc __maybe_unused, const char **argv __maybe_unused) +{ + size_t size = getpagesize(); + char *page; + int ret = 1; + + page = mmap(NULL, size, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (page == MAP_FAILED) { + perror("mmap"); + return 1; + } + + for (int i = 0; i < 256; i++) { + /* Drop the PTE so the kernel must resolve a fault on each read. */ + if (madvise(page, size, MADV_DONTNEED)) { + perror("madvise"); + goto out; + } + if (READ_ONCE(*page)) { + fprintf(stderr, "Anonymous page is not zero-filled\n"); + goto out; + } + } + ret = 0; +out: + munmap(page, size); + return ret; +} + +DEFINE_WORKLOAD(page_fault_loop);