Linux Trace Kernel
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Mathieu Desnoyers" <mathieu.desnoyers@efficios.com>
Cc: bpf@vger.kernel.org, linux-trace-kernel@vger.kernel.org
Subject: Re: [RFC PATCH 15/16] selftests/membarrier: Check the memory barriers of each thread
Date: Fri, 13 Nov 2026 13:46:03 +0000	[thread overview]
Message-ID: <sashiko-outbox-212919@kernel.org> (raw)
In-Reply-To: <20261113131903.157186-16-mathieu.desnoyers@efficios.com>

Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [Medium] selftests/membarrier: run_target() pins CPU before
  registering BPF slot
- [Medium] selftests/membarrier: bpf_setup() fails instead of skipping
  on missing BPF configs
- [Low] selftests/membarrier: reset_shared() reuses BPF counter slots of
  exiting tasks
--

commit 3ab38ee940d58df6774d85bc12aa7c4563dfeb02
Author: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>

selftests/membarrier: Check the memory barriers of each thread

This commit adds the membarrier_test_barrier selftest, which uses BPF
kprobes and the sched_switch tracepoint on x86-64 to count per-thread
memory barriers executed during expedited membarrier commands and context
switches.

> diff --git a/tools/testing/selftests/membarrier/membarrier_test_barrier.c b/tools/testing/selftests/membarrier/membarrier_test_barrier.c
> new file mode 100644
> index 0000000000000..46502d2712425
> --- /dev/null
> +++ b/tools/testing/selftests/membarrier/membarrier_test_barrier.c
> @@ -0,0 +1,1253 @@

[ ... ]

> +static const char *bpf_setup(void)
> +{
> +	struct bpf_program *prog;
> +	libbpf_print_fn_t print;
> +	struct bpf_map *map;
> +	const char *err;
> +
> +	setup_pid = getpid();
> +	atexit(bpf_teardown);
> +	if (geteuid())
> +		return "this test needs root";
> +	if (access("/sys/kernel/btf/vmlinux", R_OK))
> +		return "the kernel has no BTF (CONFIG_DEBUG_INFO_BTF)";
> +	kcore_fd = open("/proc/kcore", O_RDONLY | O_CLOEXEC);
> +	if (kcore_fd < 0)
> +		return "cannot read /proc/kcore (CONFIG_PROC_KCORE)";
> +	setup_broken = true;
> +	bpf_obj = bpf_object__open_file(BPF_OBJECT, NULL);
> +	if (!bpf_obj)
> +		return "cannot open " BPF_OBJECT;
> +	if (bpf_object__load(bpf_obj))
> +		return "cannot load the BPF programs";
> +	site_prog = bpf_object__find_program_by_name(bpf_obj, "barrier_site");
> +	prog = bpf_object__find_program_by_name(bpf_obj, "register_thread");
> +	map = bpf_object__find_map_by_name(bpf_obj, "counters");
> +	if (!site_prog || !prog || !map)
> +		return "incomplete BPF object";
> +	register_fd = bpf_program__fd(prog);
> +	counters_fd = bpf_map__fd(map);
> +	map = bpf_object__find_map_by_name(bpf_obj, "site_hits");
> +	hits_fd = map ? bpf_map__fd(map) : -1;
> +	/* The exec test case passes these two to the new program. */
> +	if (fcntl(register_fd, F_SETFD, 0) || fcntl(counters_fd, F_SETFD, 0))
> +		return "fcntl failed";
> +	signal(SIGINT, restore_optprobes);
> +	signal(SIGTERM, restore_optprobes);
> +	signal(SIGHUP, restore_optprobes);
> +	if (!set_optprobes('0'))
> +		return "cannot write " OPTPROBES_FILE;
> +	prog = bpf_object__find_program_by_name(bpf_obj, "sched_switch");
> +	switch_link = prog ? bpf_program__attach(prog) : NULL;
> +	if (!switch_link)
> +		return "cannot attach to the sched_switch tracepoint";

[Severity: Medium]
Does setting setup_broken = true before bpf_object__load() and
bpf_program__attach() cause main() to fail instead of skipping when the
kernel lacks required BPF features?

A kernel with CONFIG_DEBUG_INFO_BTF=y and CONFIG_PROC_KCORE=y can still
lack CONFIG_BPF_EVENTS=y (needed to load kprobe/tracing programs and
BPF_MAP_TYPE_TASK_STORAGE) or have BPF JIT disabled (needed to attach
tp_btf/sched_switch).  When bpf_object__load() or bpf_program__attach()
fails for those reasons, setup_broken is already true, so main() exits via
ksft_exit_fail_msg() rather than ksft_exit_skip().

[ ... ]

> +/* Run the checks of a target. */
> +static void run_target(const struct target *target)
> +{
> +	struct target_stats *stats = &shared->stats[target->idx];
> +	const uint64_t *counters;
> +	long deadline = now_ms() + target->ms;
> +	unsigned long checks = 0;
> +	uint64_t first_ipi, first_sched_in;
> +
> +	if (target->cpu >= 0 && pin_to_cpu(target->cpu))
> +		setup_error("sched_setaffinity");
> +	counters = map_counters();

[Severity: Medium]
Does calling pin_to_cpu(target->cpu) before map_counters() miss the
lazy-TLB context switch in global_expedited_lazy_tlb?

In global_expedited_lazy_tlb, the child pins itself to cpus[1] and then
cpus[2] so that cpus[1]'s idle thread borrows its mm, and then calls
run_target() with target.cpu = cpus[1].  Because pin_to_cpu() migrates the
child back onto cpus[1] before map_counters() runs register_thread to
populate task_slot, the switch from the lazy-TLB idle thread back to the
child on cpus[1] runs while slot_of(next) is still 0:

tools/testing/selftests/membarrier/membarrier_test_barrier.bpf.c:sched_switch() {
    ...
	slot = slot_of(next);
	state->slot = slot;
	state->owed = 0;
	/* The cases which need a barrier after the store to rq->curr. */
	if (slot && next->mm && prev->mm != next->mm) {
		state->owed = OWES_BARRIER;
		count(slot, 1U << MBT_SCHED_IN);
	}
    ...
}

As a result, sched_switch never sets OWES_BARRIER or increments
MBT_SCHED_IN for that lazy-TLB switch.  Calling map_counters() before
pin_to_cpu(target->cpu) registers the thread in task_slot before it
migrates back to cpus[1].

[ ... ]

> +static void reset_shared(void)
> +{
> +	memset(shared, 0, sizeof(*shared));
> +}

[Severity: Low]
Can zeroing shared->next_slot in reset_shared() let an exiting task from a
finished test case clobber the BPF counters of a new thread in the next
test case?

pthread_join() and waitpid() return during do_exit() before the dying task
reaches its final context switch in do_task_dead(), while its task_slot
entry remains attached until free_task().

CPU0                                  CPU1
join_targets() / child_ok()
reset_shared()
  shared->next_slot = 0
start_targets()
  map_counters()
    register_thread() (slot 1)
                                      do_exit()
                                        do_task_dead()
                                          __schedule()
                                            sched_switch() (prev slot 1)
                                              count(1, 1U << MBT_SCHED_OUT)
                                              <-- increments new thread's
                                                  counters

Preserving shared->next_slot across reset_shared() avoids reusing counter
slots across test cases.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261113131903.157186-1-mathieu.desnoyers@efficios.com?part=15

  reply	other threads:[~2026-11-13 13:46 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-13 13:18 [RFC PATCH 00/16] membarrier: fixes, selftests and documentation Mathieu Desnoyers
2026-11-13 13:18 ` [RFC PATCH 01/16] sched/membarrier: Fix stale runqueue state after a lazy mm switch Mathieu Desnoyers
2026-11-13 13:18 ` [RFC PATCH 02/16] sched/membarrier: Report only completed registrations Mathieu Desnoyers
2026-11-13 13:18 ` [RFC PATCH 03/16] selftests/membarrier: Rebuild the tests when their header changes Mathieu Desnoyers
2026-11-13 13:18 ` [RFC PATCH 04/16] selftests/membarrier: Issue the sync core command in its test Mathieu Desnoyers
2026-11-13 13:18 ` [RFC PATCH 05/16] selftests/membarrier: Run the tests on nohz_full kernels Mathieu Desnoyers
2026-11-13 13:18 ` [RFC PATCH 06/16] selftests/membarrier: Test which CPUs the expedited commands interrupt Mathieu Desnoyers
2026-11-13 13:18 ` [RFC PATCH 07/16] selftests/membarrier: Test command arguments and registration lifetime Mathieu Desnoyers
2026-11-13 13:18 ` [RFC PATCH 08/16] LoongArch: membarrier: Add full memory barrier in switch_mm() Mathieu Desnoyers
2026-11-13 13:18 ` [RFC PATCH 09/16] parisc: " Mathieu Desnoyers
2026-11-13 13:18 ` [RFC PATCH 10/16] Documentation/membarrier: Update the per-architecture barrier list Mathieu Desnoyers
2026-11-13 13:18 ` [RFC PATCH 11/16] Documentation/features: Update the membarrier sync core requirements Mathieu Desnoyers
2026-11-13 13:18 ` [RFC PATCH 12/16] x86/entry: Remove stale membarrier sync core comments Mathieu Desnoyers
2026-11-13 13:19 ` [RFC PATCH 13/16] selftests/membarrier: Add litmus tests of the memory ordering Mathieu Desnoyers
2026-11-13 13:19 ` [RFC PATCH 14/16] MAINTAINERS: Add the membarrier selftests to MEMBARRIER SUPPORT Mathieu Desnoyers
2026-11-13 13:19 ` [RFC PATCH 15/16] selftests/membarrier: Check the memory barriers of each thread Mathieu Desnoyers
2026-11-13 13:46   ` sashiko-bot [this message]
2026-11-13 14:37     ` Mathieu Desnoyers
2026-11-13 13:19 ` [RFC PATCH 16/16] selftests/membarrier: Check the core serialization of the sync core command Mathieu Desnoyers

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-212919@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=linux-trace-kernel@vger.kernel.org \
    --cc=mathieu.desnoyers@efficios.com \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox