Re: [PATCH v6 8/9] selftests/verification: Add tlob selftests
From: Gabriele Monaco <gmonaco@redhat.com>
Date: 2026-08-28 09:49:55
Also in:
lkml
Subsystem:
kernel selftest framework, runtime verification (rv), the rest · Maintainers:
Shuah Khan, Shuah Khan, Steven Rostedt, Gabriele Monaco, Linus Torvalds
On Fri, 2026-08-21 at 00:45 +0800, wen.yang@linux.dev wrote:
From: Wen Yang <redacted>
+
+/* start probe; busy-spin so running_ns dominates */
+noinline void tlob_busy_work(unsigned long duration_ms)
+{
+ struct timespec start, now;
+ unsigned long elapsed;
+
+ clock_gettime(CLOCK_MONOTONIC, &start);
+ do {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ elapsed = (unsigned long)(now.tv_sec - start.tv_sec)
+ * 1000000000UL
+ + (unsigned long)(now.tv_nsec - start.tv_nsec);
+ } while (elapsed < duration_ms * 1000000UL);I really don't like repeated code, cannot this go to a static inline __tlob_busy_wait(duration_ms) and you call that from tlob_busy_work() and tlob_preempt_work() ?
+
+ tlob_busy_work_done();
+}
+
+/* stop probe; noinline keeps the entry point visible to uprobes */
+noinline void tlob_sleep_work_done(void)
+{
+ asm volatile("" ::: "memory");
+}
+
+/* start probe; nanosleep so sleeping_ns dominates */
+noinline void tlob_sleep_work(unsigned long duration_ms)
+{
+ struct timespec ts = {
+ .tv_sec = duration_ms / 1000,
+ .tv_nsec = (long)(duration_ms % 1000) * 1000000L,
+ };
+ nanosleep(&ts, NULL);
+ tlob_sleep_work_done();
+}
+
+/* stop probe; noinline keeps the entry point visible to uprobes */
+noinline void tlob_preempt_work_done(void)
+{
+ asm volatile("" ::: "memory");
+}
+
+/*
+ * start probe; busy-spin so an RT competitor on the same CPU drives
+ * waiting_ns (prev_state==0 -> preempt event, task stays runnable off-CPU).
+ */
+noinline void tlob_preempt_work(unsigned long duration_ms)
+{
+ struct timespec start, now;
+ unsigned long elapsed;
+
+ clock_gettime(CLOCK_MONOTONIC, &start);
+ do {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ elapsed = (unsigned long)(now.tv_sec - start.tv_sec)
+ * 1000000000UL
+ + (unsigned long)(now.tv_nsec - start.tv_nsec);
+ } while (elapsed < duration_ms * 1000000UL);So here you'd just call __tlob_busy_wait(duration_ms).
+ + tlob_preempt_work_done(); +}
Tests look good. Apparently all that started_list/started_node thing in tlob.c is for multiple tasks sharing the same binding right. Shouldn't that be tested? I just got a simple test for that, it's generated and manually fixed but you may want to double check if it's really relevant for this. Anyway that isn't too important, for now: Reviewed-by: Gabriele Monaco <gmonaco@redhat.com> Thanks, Gabriele
diff --git a/tools/testing/selftests/verification/test.d/tlob/uprobe_multi_instance.tc b/tools/testing/selftests/verification/test.d/tlob/uprobe_multi_instance.tc
new file mode 100644
index 000000000000..33404683d29a
--- /dev/null
+++ b/tools/testing/selftests/verification/test.d/tlob/uprobe_multi_instance.tc@@ -0,0 +1,68 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# description: Test tlob monitor multiple instances of same uprobe binding (concurrent tasks on same binding) +# requires: tlob:monitor + +# Fall back to the verification directory relative to FTRACETEST_ROOT when not +# set by make (e.g. in installed kselftest environments). +: "${VERIFICATIONTEST_BINDIR:="$FTRACETEST_ROOT/../verification"}" + +UPROBE_TARGET="${VERIFICATIONTEST_BINDIR}/tlob_target" +TLOB_SYM="${VERIFICATIONTEST_BINDIR}/tlob_sym" +TLOB_MONITOR=monitors/tlob/monitor +UPROBE_COMM=$(basename ${UPROBE_TARGET}) + +busy_offset=$("$TLOB_SYM" sym_offset "$UPROBE_TARGET" tlob_busy_work 2>/dev/null) +busy_stop=$("$TLOB_SYM" sym_offset "$UPROBE_TARGET" tlob_busy_work_done 2>/dev/null) + +# Start 3 concurrent instances of the target running the same binary probe +"$UPROBE_TARGET" 30000 & +pid1=$! +"$UPROBE_TARGET" 30000 & +pid2=$! +"$UPROBE_TARGET" 30000 & +pid3=$! + +teardown() { + kill "$pid1" 2>/dev/null || true; wait "$pid1" 2>/dev/null || true + kill "$pid2" 2>/dev/null || true; wait "$pid2" 2>/dev/null || true + kill "$pid3" 2>/dev/null || true; wait "$pid3" 2>/dev/null || true +} +trap teardown EXIT +sleep 0.05 + +echo 1 > ../events/rv/event_tlob/enable +echo 1 > ../tracing_on +echo 1 > monitors/tlob/enable +echo > ../trace + +# 5 s budget on the busy probe - must not fire in 200 ms loops +echo "p ${UPROBE_TARGET}:${busy_offset} ${busy_stop} threshold=5000000000" > "$TLOB_MONITOR" + +# Wait up to 2 s for all three pids to be registered and transition through start. +# This proves multiple tasks can hit the same uprobe binding concurrently. +found1=0; found2=0; found3=0 +i=0 +while [ "$i" -lt 20 ]; do + sleep 0.1 + grep "event_tlob" ../trace | grep -wq "${UPROBE_COMM}-${pid1}" && found1=1 + grep "event_tlob" ../trace | grep -wq "${UPROBE_COMM}-${pid2}" && found2=1 + grep "event_tlob" ../trace | grep -wq "${UPROBE_COMM}-${pid3}" && found3=1 + if [ "$found1" = "1" ] && [ "$found2" = "1" ] && [ "$found3" = "1" ]; then + break + fi + i=$((i+1)) +done + +[ "$found1" = "1" ] +[ "$found2" = "1" ] +[ "$found3" = "1" ] + +# Removing the uprobe while tasks are active must succeed cleanly and unbind them. +# Active tasks will be detached (binding set to NULL), parked tasks will be destroyed. +echo "-${UPROBE_TARGET}:${busy_offset}" > "$TLOB_MONITOR" +! grep -q "^p .*:0x${busy_offset#0x} " "$TLOB_MONITOR" || false + +echo 0 > monitors/tlob/enable +echo 0 > ../events/rv/event_tlob/enable +echo > ../trace