diff --git a/tools/testing/selftests/drivers/net/dpll/Makefile b/tools/testing/selftests/drivers/net/dpll/Makefile
index 7cb42c93b0c9a1..2e8128dac8fa65 100644
--- a/tools/testing/selftests/drivers/net/dpll/Makefile
+++ b/tools/testing/selftests/drivers/net/dpll/Makefile
@@ -9,6 +9,7 @@ TEST_INCLUDES := \
TEST_PROGS := \
dpll_basic.sh \
dpll_features.sh \
+ dpll_reftrack.sh \
dpll_stress.sh \
# end of TEST_PROGS
diff --git a/tools/testing/selftests/drivers/net/dpll/config b/tools/testing/selftests/drivers/net/dpll/config
index e2c90b6970bd43..71931de5cfdb2e 100644
--- a/tools/testing/selftests/drivers/net/dpll/config
+++ b/tools/testing/selftests/drivers/net/dpll/config
@@ -1,3 +1,6 @@
CONFIG_CONFIGFS_FS=y
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_KERNEL=y
CONFIG_DPLL=y
+CONFIG_DPLL_REFCNT_TRACKER=y
CONFIG_DPLLSIM=m
diff --git a/tools/testing/selftests/drivers/net/dpll/dpll_reftrack.sh b/tools/testing/selftests/drivers/net/dpll/dpll_reftrack.sh
new file mode 100755
index 00000000000000..8e624e8631776a
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/dpll/dpll_reftrack.sh
@@ -0,0 +1,184 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# DPLL reference tracking test. With CONFIG_DPLL_REFCNT_TRACKER=y the DPLL
+# core tracks device and pin references through ref_tracker; the debugfs
+# files dpll_device@<ptr> / dpll_pin@<ptr> list the stack traces of
+# currently unreleased references. This test records the entries that
+# exist before it starts, registers dpllsim devices and verifies that
+# every entry that appeared since is released again after unregistration.
+#
+# Copyright (c) 2026 Red Hat, Inc.
+
+SCRIPTDIR=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")
+
+source "$SCRIPTDIR/../../../net/lib.sh"
+source "$SCRIPTDIR/../lib/sh/dpll_lib.sh"
+
+ALL_TESTS="
+ test_ref_tracker_probe
+ test_basic_reftrack
+ test_final_leak_check
+"
+
+CREATED_DEVICE=""
+
+# The device stays recorded when its removal fails, so that the cleanup
+# handler tries again.
+destroy_device()
+{
+ [ -n "$CREATED_DEVICE" ] || return 0
+
+ dpllsim_delete_device "$CREATED_DEVICE" || return 1
+ CREATED_DEVICE=""
+}
+
+cleanup()
+{
+ # A second signal must not abort the teardown half way.
+ trap '' INT TERM
+
+ defer_scopes_cleanup
+ destroy_device || log_info "cannot delete device $CREATED_DEVICE"
+ # The device whose creation a signal interrupted is recorded by the
+ # library alone, a double delete of a tracked one is harmless.
+ if [ -n "$DPLLSIM_CREATED_NAME" ]; then
+ dpllsim_delete_device "$DPLLSIM_CREATED_NAME" ||
+ log_info "cannot delete device $DPLLSIM_CREATED_NAME"
+ fi
+ if [ -n "$DPLLSIM_PROBE_DEVICE" ]; then
+ dpllsim_delete_device "$DPLLSIM_PROBE_DEVICE" ||
+ log_info "cannot delete device $DPLLSIM_PROBE_DEVICE"
+ fi
+ [ -n "$REF_SNAPSHOT" ] && rm -f "$REF_SNAPSHOT"
+ dpll_lib_cleanup
+}
+
+##############################################################################
+
+# Probe for the reference tracker and record the entries that exist
+# before the first test that creates devices, so that every entry that
+# appears from here on is attributed to this script. A probe device
+# that cannot be created says nothing about the tracker, the simulator
+# is broken. A probe device whose references do not drain is the kind
+# of leak the tests look for, found before the first test. One that
+# cannot be deleted is a failure too, it stays recorded for the cleanup
+# handler to try again. The tests skip when REF_TRACKER is 0 afterwards.
+test_ref_tracker_probe()
+{
+ RET=0
+
+ REF_TRACKER=0
+ if ! dpll_debugfs_available || ! [ -d "$DPLL_REF_TRACKER_DIR" ]; then
+ log_test_skip "DPLL ref_tracker probe" \
+ "ref_tracker debugfs not available"
+ return
+ fi
+
+ # The snapshot file doubles as the scratch file of the probe, so
+ # it is needed before anything is recorded.
+ if ! REF_SNAPSHOT=$(dpll_ref_tracker_snapshot_file); then
+ check_err 1 "cannot create a temporary file for the snapshot"
+ log_test "DPLL ref_tracker probe"
+ return
+ fi
+
+ dpll_ref_tracker_probe "$REF_SNAPSHOT"
+ case $? in
+ 0)
+ REF_TRACKER=1
+ ;;
+ 1)
+ log_test_skip "DPLL ref_tracker probe" \
+ "CONFIG_DPLL_REFCNT_TRACKER not enabled"
+ return
+ ;;
+ 3)
+ REF_TRACKER=1
+ check_err 1 "references of the probe device not released"
+ ;;
+ 4)
+ check_err 1 "cannot delete the dpllsim probe device"
+ ;;
+ *)
+ check_err 1 "cannot create dpllsim probe device"
+ ;;
+ esac
+ dpll_ref_tracker_snapshot "$REF_SNAPSHOT"
+
+ log_test "DPLL ref_tracker probe"
+}
+
+test_basic_reftrack()
+{
+ RET=0
+
+ if [ "$REF_TRACKER" -eq 0 ]; then
+ log_test_skip "DPLL basic reference tracking" \
+ "DPLL reference tracker not available"
+ return
+ fi
+
+ if ! dpllsim_setup_standard_device; then
+ check_err 1 "cannot create dpllsim device"
+ log_test "DPLL basic reference tracking"
+ return
+ fi
+ CREATED_DEVICE=$DPLLSIM_CREATED_NAME
+
+ destroy_device
+ check_err $? "cannot delete device $CREATED_DEVICE"
+
+ if ! dpll_device_wait_gone_by_clock_id "$DPLLSIM_CLOCK_ID" dpllsim; then
+ check_err 1 "device still on netlink after delete"
+ fi
+
+ dpll_ref_tracker_check_leaks "$REF_SNAPSHOT"
+ check_err $? "leaked references after device removal"
+
+ log_test "DPLL basic reference tracking"
+}
+
+test_final_leak_check()
+{
+ RET=0
+
+ if [ "$REF_TRACKER" -eq 0 ]; then
+ log_test_skip "DPLL final ref_tracker check" \
+ "DPLL reference tracker not available"
+ return
+ fi
+ # A device an earlier test could not remove still holds its
+ # references, the failure to report is that one.
+ if [ -n "$CREATED_DEVICE" ]; then
+ log_test_skip "DPLL final ref_tracker check" \
+ "device from an earlier test still present"
+ return
+ fi
+
+ dpll_ref_tracker_check_leaks "$REF_SNAPSHOT"
+ check_err $? "leaked DPLL references at end of test"
+
+ log_test "DPLL final ref_tracker check"
+}
+
+##############################################################################
+
+dpll_lib_check_prereqs
+# Another run would find the devices of this one left behind and see
+# them as leaks, see the lock in the library.
+dpll_lib_lock
+
+REF_SNAPSHOT=""
+REF_TRACKER=0
+# The module check and the probe test may mount configfs and debugfs, so
+# the exit trap that unmounts them again has to be in place before. INT
+# and TERM exit through it as well, so that a timeout imposed by the
+# harness still removes the device.
+trap cleanup EXIT
+trap 'exit 1' INT TERM
+dpllsim_check_module
+
+tests_run
+
+exit $EXIT_STATUSdiff --git a/tools/testing/selftests/drivers/net/dpll/dpll_stress.sh b/tools/testing/selftests/drivers/net/dpll/dpll_stress.sh
index 2a6813871f34ea..9db6583ccfa938 100755
--- a/tools/testing/selftests/drivers/net/dpll/dpll_stress.sh
+++ b/tools/testing/selftests/drivers/net/dpll/dpll_stress.sh
@@ -14,11 +14,13 @@ source "$SCRIPTDIR/../../../net/lib.sh"
source "$SCRIPTDIR/../lib/sh/dpll_lib.sh"
ALL_TESTS="
+ test_ref_tracker_probe
test_rapid_create_delete
test_concurrent_workers
test_pin_state_flooding
test_max_devices
test_deploy_cycle
+ test_ref_leaks
"
STRESS_ITERATIONS=${STRESS_ITERATIONS:-20}@@ -153,6 +155,11 @@ cleanup()
dpllsim_delete_device "$DPLLSIM_CREATED_NAME" ||
log_info "cannot delete device $DPLLSIM_CREATED_NAME"
fi
+ if [ -n "$DPLLSIM_PROBE_DEVICE" ]; then
+ dpllsim_delete_device "$DPLLSIM_PROBE_DEVICE" ||
+ log_info "cannot delete device $DPLLSIM_PROBE_DEVICE"
+ fi
+ [ -n "$REF_SNAPSHOT" ] && rm -f "$REF_SNAPSHOT"
dpll_lib_cleanup
}
@@ -208,6 +215,60 @@ create_stress_device()
##############################################################################
+# Probe for the reference tracker and record the entries that exist
+# before the first test that creates devices, so that the final check
+# attributes every entry that appears from here on to this script. A
+# probe device that cannot be created says nothing about the tracker,
+# the simulator is broken. A probe device whose references do not drain
+# is the kind of leak the final check looks for, found before the first
+# test. One that cannot be deleted is a failure too, it stays recorded
+# for the cleanup handler to try again. REF_TRACKER tells the leak
+# check whether the tracker was seen at work.
+test_ref_tracker_probe()
+{
+ RET=0
+
+ REF_TRACKER=0
+ if ! dpll_debugfs_available || ! [ -d "$DPLL_REF_TRACKER_DIR" ]; then
+ log_test_skip "DPLL ref_tracker probe" \
+ "ref_tracker debugfs not available"
+ return
+ fi
+
+ # The snapshot file doubles as the scratch file of the probe, so
+ # it is needed before anything is recorded.
+ if ! REF_SNAPSHOT=$(dpll_ref_tracker_snapshot_file); then
+ check_err 1 "cannot create a temporary file for the snapshot"
+ log_test "DPLL ref_tracker probe"
+ return
+ fi
+
+ dpll_ref_tracker_probe "$REF_SNAPSHOT"
+ case $? in
+ 0)
+ REF_TRACKER=1
+ ;;
+ 1)
+ log_test_skip "DPLL ref_tracker probe" \
+ "CONFIG_DPLL_REFCNT_TRACKER not enabled"
+ return
+ ;;
+ 3)
+ REF_TRACKER=1
+ check_err 1 "references of the probe device not released"
+ ;;
+ 4)
+ check_err 1 "cannot delete the dpllsim probe device"
+ ;;
+ *)
+ check_err 1 "cannot create dpllsim probe device"
+ ;;
+ esac
+ dpll_ref_tracker_snapshot "$REF_SNAPSHOT"
+
+ log_test "DPLL ref_tracker probe"
+}
+
test_rapid_create_delete()
{
local i clock_id name@@ -462,16 +523,36 @@ test_deploy_cycle()
log_test "DPLL deploy/undeploy cycling"
}
+test_ref_leaks()
+{
+ RET=0
+
+ # The probe test found the tracker inactive or could not run.
+ if [ "$REF_TRACKER" -eq 0 ]; then
+ log_test_skip "DPLL stress ref_tracker check" \
+ "DPLL reference tracker not available"
+ return
+ fi
+
+ dpll_ref_tracker_check_leaks "$REF_SNAPSHOT"
+ check_err $? "leaked DPLL references after stress"
+
+ log_test "DPLL stress ref_tracker check"
+}
+
##############################################################################
dpll_lib_check_prereqs
-# Another run would find the devices of this one left behind, see the
-# lock in the library.
+# Another run would find the devices of this one left behind and see
+# them as leaks, see the lock in the library.
dpll_lib_lock
-# The module check may mount configfs, so the exit trap that unmounts it
-# again has to be in place before. INT and TERM exit through it as well,
-# so that a timeout imposed by the harness still removes the devices.
+REF_SNAPSHOT=""
+REF_TRACKER=0
+# The module check and the probe test may mount configfs and debugfs, so
+# the exit trap that unmounts them again has to be in place before. INT
+# and TERM exit through it as well, so that a timeout imposed by the
+# harness still removes the devices.
trap cleanup EXIT
trap 'exit 1' INT TERM
dpllsim_check_modulediff --git a/tools/testing/selftests/drivers/net/lib/sh/dpll_lib.sh b/tools/testing/selftests/drivers/net/lib/sh/dpll_lib.sh
index ada10ee4394fd5..d851237bdac440 100644
--- a/tools/testing/selftests/drivers/net/lib/sh/dpll_lib.sh
+++ b/tools/testing/selftests/drivers/net/lib/sh/dpll_lib.sh
@@ -15,6 +15,8 @@
CONFIGFS_DIR="/sys/kernel/config"
DPLLSIM_CFG="$CONFIGFS_DIR/dpllsim"
+DEBUGFS_DIR="/sys/kernel/debug"
+DPLL_REF_TRACKER_DIR="$DEBUGFS_DIR/ref_tracker"
# Next clock-id handed out by dpllsim_alloc_clock_id. The lookups of the
# dpllsim helpers match on the module name as well, which is what keeps
@@ -52,12 +54,12 @@ dpll_lib_check_prereqs()
}
# The scripts that create dpllsim devices share the one dpllsim directory
-# and cannot run alongside each other, so each takes a lock on
-# DPLL_LOCK_FILE before it mounts or creates anything and skips when
-# another run holds it. The workers a script forks inherit the open file
-# and keep the lock until they exit. The file lives under /run rather
-# than in the world-writable /tmp, where a planted symlink could
-# redirect the open.
+# and the leak check attributes every new ref_tracker entry to the run,
+# so each takes a lock on DPLL_LOCK_FILE before it mounts or creates
+# anything and skips when another run holds it. The workers a script
+# forks inherit the open file and keep the lock until they exit. The
+# files live under /run rather than in the world-writable /tmp, where a
+# planted symlink could redirect the open.
DPLL_RUN_DIR="/run/dpll-selftest"
DPLL_LOCK_FILE="$DPLL_RUN_DIR/lock"
DPLL_LOCK_FD=""
@@ -438,9 +440,10 @@ dpll_pin_set_phase_adjust()
# structure (pins, clock_id, pin attributes) can only be changed while the
# device is not live.
#
-# dpllsim_available mounts configfs when nothing else did, which is the
-# one side effect the library has outside the dpllsim directory. Scripts
-# call dpll_lib_cleanup last from their exit trap to undo it.
+# dpllsim_available mounts configfs when nothing else did, like
+# dpll_debugfs_available below does for debugfs, which are the only side
+# effects the library has outside the dpllsim directory. Scripts call
+# dpll_lib_cleanup last from their exit trap to undo them.
DPLLSIM_MOUNTED_CONFIGFS=0
@@ -456,24 +459,38 @@ dpllsim_available()
[ -d "$DPLLSIM_CFG" ]
}
-# Unmount configfs again when dpllsim_available mounted it. Call after
-# the devices of the script are gone. A dpllsim device the script could
-# not remove keeps the mount, so that what is left can be looked at.
+__dpll_lib_umount()
+{
+ umount "$1" 2>/dev/null || log_info "cannot unmount $1"
+}
+
+# Unmount configfs and debugfs again when the library mounted them. Call
+# after the devices of the script are gone. A dpllsim device the script
+# could not remove keeps both mounted, so that what is left can be
+# looked at, the device in configfs and the references it still holds
+# in the ref_tracker entries of debugfs.
dpll_lib_cleanup()
{
- local dev_path name
+ local dev_path name dir kept
+ local mounted=()
- [ "$DPLLSIM_MOUNTED_CONFIGFS" -eq 1 ] || return 0
+ [ "$DPLLSIM_MOUNTED_CONFIGFS" -eq 1 ] && mounted+=("$CONFIGFS_DIR")
+ [ "$DPLL_MOUNTED_DEBUGFS" -eq 1 ] && mounted+=("$DEBUGFS_DIR")
DPLLSIM_MOUNTED_CONFIGFS=0
+ DPLL_MOUNTED_DEBUGFS=0
+ [ "${#mounted[@]}" -gt 0 ] || return 0
for dev_path in "$DPLLSIM_CFG"/*/; do
[ -d "$dev_path" ] || continue
name=$(basename "$dev_path")
- log_info "device $name left behind, $CONFIGFS_DIR kept mounted"
+ kept=${mounted[*]}
+ kept="${kept/ / and } kept mounted"
+ log_info "device $name left behind, $kept"
return
done
- umount "$CONFIGFS_DIR" 2>/dev/null ||
- log_info "cannot unmount $CONFIGFS_DIR"
+ for dir in "${mounted[@]}"; do
+ __dpll_lib_umount "$dir"
+ done
}
dpllsim_check_module()@@ -622,3 +639,212 @@ dpllsim_setup_standard_device()
return 1
fi
}
+
+##############################################################################
+# ref_tracker leak checking
+#
+# With CONFIG_DPLL_REFCNT_TRACKER=y the DPLL core creates regular files
+# named dpll_device@<ptr> / dpll_pin@<ptr> under the ref_tracker debugfs
+# dir. Reading one prints the stack traces of currently unreleased
+# references; an empty (or removed) file means no leaks.
+#
+# The entries do not name their owner, so leaks are attributed by
+# snapshot, a non-empty entry whose name is not in the snapshot taken
+# before the test created its devices is a leak, an entry that is in it
+# belongs to another driver and is ignored even when its content changed.
+
+DPLL_MOUNTED_DEBUGFS=0
+# The snapshot files are kept in DPLL_RUN_DIR, see the lock above for
+# why that is not /tmp, as refsnap.<pid>.<random>.
+DPLL_REF_SNAPSHOT_PREFIX="$DPLL_RUN_DIR/refsnap"
+# Name of the probe device while it exists, so that the cleanup handler
+# of a script can remove it when the probe could not.
+DPLLSIM_PROBE_DEVICE=""
+
+# Create the file a snapshot is recorded in and print its name. The
+# scripts take the lock first, which creates DPLL_RUN_DIR. A run that
+# is killed with SIGKILL cannot remove its file, so the files of earlier
+# runs whose process is gone are removed first, the pid of the run is
+# the component after the first dot of the name. A pid that was reused
+# by a process that lives long would keep such a file forever, so files
+# older than a day are removed whatever their pid is. Return 1 when the
+# file cannot be created.
+dpll_ref_tracker_snapshot_file()
+{
+ local file pid
+
+ find "$DPLL_RUN_DIR" -maxdepth 1 \
+ -name "${DPLL_REF_SNAPSHOT_PREFIX##*/}.*" -mmin +1440 -delete \
+ 2>/dev/null
+ for file in "$DPLL_REF_SNAPSHOT_PREFIX".*; do
+ [ -f "$file" ] || continue
+ pid=${file#"$DPLL_REF_SNAPSHOT_PREFIX".}
+ pid=${pid%%.*}
+ [[ "$pid" =~ ^[0-9]+$ ]] || continue
+ [ -d "/proc/$pid" ] || rm -f "$file"
+ done
+ mktemp "$DPLL_REF_SNAPSHOT_PREFIX.$$.XXXXXX"
+}
+
+# Mount debugfs when nothing else did, so that the ref_tracker entries
+# can be read, and remember that for dpll_lib_cleanup. Return 0 when
+# debugfs is mounted afterwards, whether the tracker dir exists is for
+# the caller to check.
+dpll_debugfs_available()
+{
+ if ! mountpoint -q "$DEBUGFS_DIR"; then
+ mount -t debugfs none "$DEBUGFS_DIR" &> /dev/null &&
+ DPLL_MOUNTED_DEBUGFS=1
+ fi
+ mountpoint -q "$DEBUGFS_DIR"
+}
+
+__dpll_ref_tracker_hash()
+{
+ printf '%s' "$1" | md5sum | cut -d' ' -f1
+}
+
+# Write "<name> <content hash>" for every existing dpll_device@* /
+# dpll_pin@* entry to the given file, one entry per line. The file ends
+# up empty when the tracker dir is missing.
+dpll_ref_tracker_snapshot()
+{
+ local file=$1
+ local entry content
+
+ for entry in "$DPLL_REF_TRACKER_DIR"/dpll_device@* \
+ "$DPLL_REF_TRACKER_DIR"/dpll_pin@*; do
+ [ -f "$entry" ] || continue
+ content=$(cat "$entry" 2>/dev/null)
+ echo "$(basename "$entry")" \
+ "$(__dpll_ref_tracker_hash "$content")"
+ done > "$file"
+}
+
+# Print the leak candidates, the non-empty entries whose name is not in
+# the snapshot, each followed by its content. Prints nothing when there
+# is no candidate.
+__dpll_ref_tracker_new_entries()
+{
+ local file=$1
+ local entry name content
+
+ for entry in "$DPLL_REF_TRACKER_DIR"/dpll_device@* \
+ "$DPLL_REF_TRACKER_DIR"/dpll_pin@*; do
+ [ -f "$entry" ] || continue
+ name=$(basename "$entry")
+ grep -q "^$name " "$file" && continue
+ content=$(cat "$entry" 2>/dev/null)
+ [ -n "$content" ] || continue
+ echo "Unreleased references in $name:"
+ echo "$content"
+ done
+}
+
+# Return 0 when there is no leak candidate. Otherwise print the
+# candidates and return 1, busywait hands the output of its last failed
+# check to the caller when it times out.
+__dpll_ref_tracker_settled()
+{
+ local leaks
+
+ leaks=$(__dpll_ref_tracker_new_entries "$1")
+ [ -z "$leaks" ] && return 0
+
+ echo "$leaks"
+ return 1
+}
+
+# Log the entries of the snapshot whose content changed since and record
+# the new content, so that the same change is logged once.
+__dpll_ref_tracker_note_changed()
+{
+ local file=$1
+ local entry name content hash changed=""
+
+ for entry in "$DPLL_REF_TRACKER_DIR"/dpll_device@* \
+ "$DPLL_REF_TRACKER_DIR"/dpll_pin@*; do
+ [ -f "$entry" ] || continue
+ name=$(basename "$entry")
+ grep -q "^$name " "$file" || continue
+ content=$(cat "$entry" 2>/dev/null)
+ hash=$(__dpll_ref_tracker_hash "$content")
+ grep -qx "$name $hash" "$file" && continue
+ sed -i "s/^$name .*/$name $hash/" "$file"
+ changed="$changed $name"
+ done
+
+ [ -n "$changed" ] && log_info \
+ "references of another driver changed (ignored):$changed"
+ return 0
+}
+
+# $1: snapshot file written by dpll_ref_tracker_snapshot before the
+# objects under test were created. Return 0 when no reference to an
+# object created since the snapshot is held any more. A concurrent dump
+# holds references for a moment, so wait for the candidates to drain. A
+# timeout means the references never drained, the candidates seen by the
+# last check are then printed and count as leaked.
+dpll_ref_tracker_check_leaks()
+{
+ local file=$1
+ local leaks ret=0
+
+ [ -d "$DPLL_REF_TRACKER_DIR" ] || return 0
+
+ if ! leaks=$(busywait "$BUSYWAIT_TIMEOUT" __dpll_ref_tracker_settled \
+ "$file"); then
+ echo "References not released within $BUSYWAIT_TIMEOUT ms:"
+ echo "$leaks"
+ ret=1
+ fi
+ __dpll_ref_tracker_note_changed "$file"
+
+ return "$ret"
+}
+
+# Probe whether the kernel tracks DPLL references with a throwaway
+# dpllsim device, the entries that exist before it are recorded in the
+# scratch file $1. Return 0 when the tracker works, 1 when no entry
+# appeared (no CONFIG_DPLL_REFCNT_TRACKER), 2 when the probe device
+# could not be created or deployed, 3 when its entries did not drain
+# after its deletion and 4 when it could not be deleted, its name then
+# stays in DPLLSIM_PROBE_DEVICE for the cleanup handler to retry. Must
+# not be called from a subshell, it allocates clock-ids.
+dpll_ref_tracker_probe()
+{
+ local file=$1
+ local name attempt ret=2
+
+ dpll_ref_tracker_snapshot "$file"
+
+ # A directory of that name left behind by an earlier run takes a
+ # fresh clock-id, anything else is a broken simulator.
+ for attempt in 1 2 3; do
+ dpllsim_alloc_clock_id
+ name="probe$DPLLSIM_CLOCK_ID"
+ dpllsim_create_device "$name" "$DPLLSIM_CLOCK_ID" && break
+ [ -d "$DPLLSIM_CFG/$name" ] || return 2
+ [ "$attempt" -lt 3 ] || return 2
+ done
+ DPLLSIM_PROBE_DEVICE=$name
+
+ if dpllsim_add_pin "$name" ext0 ext 10000000 input &&
+ dpllsim_deploy "$name"; then
+ ret=1
+ [ -n "$(__dpll_ref_tracker_new_entries "$file")" ] && ret=0
+ fi
+
+ if dpllsim_delete_device "$name"; then
+ DPLLSIM_PROBE_DEVICE=""
+ else
+ log_info "cannot delete device $name"
+ return 4
+ fi
+ if [ "$ret" -eq 0 ] &&
+ ! busywait "$BUSYWAIT_TIMEOUT" __dpll_ref_tracker_settled "$file" \
+ > /dev/null; then
+ ret=3
+ fi
+ return "$ret"
+}--
2.56.0