[PATCH net-next v2 02/14] selftests: dpll: add test library and device lifecycle tests

From
Petr Oros <hidden>
Date
2026-11-05 15:19:24
Also in
linux-doc, linux-kselftest
Reply to
Petr Oros, 2026-11-05
Add a shell library for DPLL selftests with wrappers around the iproute2
dpll tool and around the dpllsim configfs interface, and the first two
test scripts built on it. The library resolves devices by the clock ID
chosen at creation time, since netlink ids depend on what else exists
on the system, and the dpllsim helpers restrict the lookup to their
module, so that a device of another driver with the same clock ID is
never picked up. Every query runs the tool first and jq on what it
printed, so that a failed query is told apart from a missing attribute.
The library mounts configfs when nothing else did, and the exit trap of
each script undoes that, removes the devices the script created and
runs on INT and TERM as well, so that a timeout imposed by the harness
still cleans up.

Two runs of scripts that create dpllsim devices would share the one
dpllsim directory and could draw the same clock IDs, so every such
script takes a lock before it mounts or creates anything and skips when
another run holds it. The lock file lives under /run rather than in
/tmp, since the scripts run as root and open the file for writing, and
a symlink planted in the world-writable /tmp would redirect that open.

dpll_basic.sh checks that a device reports the mandatory attributes
over netlink. It runs against the device named by DPLL_DEV_ID or the
first device that is not a dpllsim one, and creates a dpllsim device
when there is none, so the same script serves the simulator and real
drivers. dpll_stress.sh is simulator only and exercises the lifecycle
paths of the core by creating and deleting devices in a loop and by
cycling one device through repeated register and unregister
transitions. The dpllsim MAINTAINERS entry covers the new directory
and the shared library.

Signed-off-by: Petr Oros <redacted>
---
v2:
- sort the config file alphabetically (netdev CI check_selftest)
- quote the ksft_skip and ret expansions and add shellcheck directives
  for the globals read by the scripts, for ksft_skip, which comes from
  net/lib.sh, and for the optional argument of
  dpllsim_setup_standard_device() (netdev CI shellcheck)

v1: https://lore.kernel.org/all/20261103093910.596225-3-poros@redhat.com/ (local)
---
 MAINTAINERS                                   |   2 +
 tools/testing/selftests/Makefile              |   1 +
 .../selftests/drivers/net/dpll/Makefile       |  14 +
 .../testing/selftests/drivers/net/dpll/config |   3 +
 .../selftests/drivers/net/dpll/dpll_basic.sh  |  93 +++++
 .../selftests/drivers/net/dpll/dpll_stress.sh | 164 +++++++++
 .../selftests/drivers/net/dpll/settings       |   1 +
 .../selftests/drivers/net/lib/sh/dpll_lib.sh  | 334 ++++++++++++++++++
 8 files changed, 612 insertions(+)
 create mode 100644 tools/testing/selftests/drivers/net/dpll/Makefile
 create mode 100644 tools/testing/selftests/drivers/net/dpll/config
 create mode 100755 tools/testing/selftests/drivers/net/dpll/dpll_basic.sh
 create mode 100755 tools/testing/selftests/drivers/net/dpll/dpll_stress.sh
 create mode 100644 tools/testing/selftests/drivers/net/dpll/settings
 create mode 100644 tools/testing/selftests/drivers/net/lib/sh/dpll_lib.sh
diff --git a/MAINTAINERS b/MAINTAINERS
index 147a5466902bc7..d4e0a7e97d3808 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8076,6 +8076,8 @@ L:	netdev@vger.kernel.org
 S:	Maintained
 F:	Documentation/driver-api/dpllsim.rst
 F:	drivers/dpll/dpllsim/
+F:	tools/testing/selftests/drivers/net/dpll/
+F:	tools/testing/selftests/drivers/net/lib/sh/dpll_lib.sh
 
 DPLL SUBSYSTEM
 M:	Vadim Fedorenko <vadim.fedorenko@linux.dev>
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 6c77beba0956ef..d8991f43c70e46 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -24,6 +24,7 @@ TARGETS += drivers/ntsync
 TARGETS += drivers/s390x/uvdevice
 TARGETS += drivers/net
 TARGETS += drivers/net/bonding
+TARGETS += drivers/net/dpll
 TARGETS += drivers/net/netconsole
 TARGETS += drivers/net/team
 TARGETS += drivers/net/virtio_net
diff --git a/tools/testing/selftests/drivers/net/dpll/Makefile b/tools/testing/selftests/drivers/net/dpll/Makefile
new file mode 100644
index 00000000000000..7752fb518b6e9b
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/dpll/Makefile
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+
+TEST_INCLUDES := \
+	$(wildcard ../../../net/lib/sh/*.sh) \
+	../lib/sh/dpll_lib.sh \
+	../../../net/lib.sh \
+# end of TEST_INCLUDES
+
+TEST_PROGS := \
+	dpll_basic.sh \
+	dpll_stress.sh \
+# end of TEST_PROGS
+
+include ../../../lib.mk
diff --git a/tools/testing/selftests/drivers/net/dpll/config b/tools/testing/selftests/drivers/net/dpll/config
new file mode 100644
index 00000000000000..e2c90b6970bd43
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/dpll/config
@@ -0,0 +1,3 @@
+CONFIG_CONFIGFS_FS=y
+CONFIG_DPLL=y
+CONFIG_DPLLSIM=m
diff --git a/tools/testing/selftests/drivers/net/dpll/dpll_basic.sh b/tools/testing/selftests/drivers/net/dpll/dpll_basic.sh
new file mode 100755
index 00000000000000..0d823fbc943a84
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/dpll/dpll_basic.sh
@@ -0,0 +1,93 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Basic DPLL subsystem test. Read-only validation of the device properties
+# exposed over netlink. Runs against a hardware DPLL device when one is
+# present (or when DPLL_DEV_ID is set), otherwise against a freshly
+# created dpllsim device.
+#
+# 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_device_properties
+"
+
+test_device_properties()
+{
+	local json
+
+	RET=0
+
+	# A device that cannot be queried at all would be reported as one
+	# that misses every attribute, so bail out on that first.
+	if ! json=$(dpll_device_json "$DPLL_DEV_ID") || [ -z "$json" ]; then
+		check_err 1 "device query failed"
+		log_test "DPLL device properties"
+		return
+	fi
+
+	local field
+	for field in '.id' '.["module-name"]' '.["clock-id"]' '.type' \
+		     '.mode' '.["lock-status"]'; do
+		if ! jq -e "$field" <<< "$json" &> /dev/null; then
+			check_err 1 "device is missing the $field attribute"
+		fi
+	done
+
+	log_test "DPLL device properties"
+}
+
+##############################################################################
+
+cleanup()
+{
+	# A second signal must not abort the teardown half way.
+	trap '' INT TERM
+
+	if [ -n "$CREATED_DEVICE" ]; then
+		dpllsim_delete_device "$CREATED_DEVICE" ||
+			log_info "cannot delete device $CREATED_DEVICE"
+	fi
+	# 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
+	dpll_lib_cleanup
+}
+
+dpll_lib_check_prereqs
+# Another run would find the devices of this one left behind, see the
+# lock in the library.
+dpll_lib_lock
+dpll_lib_init
+
+CREATED_DEVICE=""
+# 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 device.
+trap cleanup EXIT
+trap 'exit 1' INT TERM
+
+if [ -z "$DPLL_DEV_ID" ]; then
+	dpllsim_check_module
+	if ! dpllsim_setup_standard_device; then
+		echo "SKIP: cannot create dpllsim device"
+		exit "$ksft_skip"
+	fi
+	CREATED_DEVICE=$DPLLSIM_CREATED_NAME
+	DPLL_DEV_ID=$DPLLSIM_DEV_ID
+	DPLL_MODULE_NAME="dpllsim"
+fi
+
+log_info "testing DPLL device $DPLL_DEV_ID (module $DPLL_MODULE_NAME)"
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/drivers/net/dpll/dpll_stress.sh b/tools/testing/selftests/drivers/net/dpll/dpll_stress.sh
new file mode 100755
index 00000000000000..74bb6e8b3a8ed4
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/dpll/dpll_stress.sh
@@ -0,0 +1,164 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# DPLL subsystem stress test. Uses dpllsim to exercise rapid device
+# registration/unregistration, concurrent operations from multiple
+# processes, pin state change flooding and notification load, then
+# verifies via ref_tracker that no references leaked.
+#
+# 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_rapid_create_delete
+	test_deploy_cycle
+"
+
+STRESS_ITERATIONS=${STRESS_ITERATIONS:-20}
+
+CREATED_DEVICES=()
+
+track_device()
+{
+	CREATED_DEVICES+=("$1")
+}
+
+untrack_device()
+{
+	local name=$1 dev
+	local remaining=()
+
+	for dev in "${CREATED_DEVICES[@]}"; do
+		[ "$dev" = "$name" ] || remaining+=("$dev")
+	done
+	CREATED_DEVICES=("${remaining[@]}")
+}
+
+cleanup()
+{
+	local dev
+
+	# A second signal must not abort the teardown half way.
+	trap '' INT TERM
+
+	for dev in "${CREATED_DEVICES[@]}"; do
+		dpllsim_delete_device "$dev" ||
+			log_info "cannot delete device $dev"
+	done
+	# 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
+	dpll_lib_cleanup
+}
+
+# Create a device named after the given clock-id and deploy it. Sets
+# DPLLSIM_CREATED_NAME.
+create_stress_device()
+{
+	local clock_id=$1
+	local name="st$clock_id"
+
+	dpllsim_create_device "$name" "$clock_id" || return 1
+
+	if ! dpllsim_deploy "$name"; then
+		dpllsim_delete_device "$name"
+		return 1
+	fi
+}
+
+##############################################################################
+
+test_rapid_create_delete()
+{
+	local i clock_id name
+
+	RET=0
+
+	for i in $(seq 1 "$STRESS_ITERATIONS"); do
+		dpllsim_alloc_clock_id; clock_id=$DPLLSIM_CLOCK_ID
+
+		if ! create_stress_device "$clock_id"; then
+			check_err 1 "failed to create device (iteration $i)"
+			break
+		fi
+		name=$DPLLSIM_CREATED_NAME
+		track_device "$name"
+
+		if ! dpllsim_delete_device "$name"; then
+			check_err 1 "failed to delete device (iteration $i)"
+			break
+		fi
+		untrack_device "$name"
+	done
+
+	log_test "DPLL rapid create/delete ($STRESS_ITERATIONS iterations)"
+}
+
+test_deploy_cycle()
+{
+	local clock_id name i
+
+	RET=0
+
+	dpllsim_alloc_clock_id; clock_id=$DPLLSIM_CLOCK_ID
+	name="cycle$clock_id"
+	if ! dpllsim_create_device "$name" "$clock_id"; then
+		check_err 1 "cannot create device"
+		log_test "DPLL deploy/undeploy cycling"
+		return
+	fi
+	track_device "$name"
+
+	for i in $(seq 1 "$STRESS_ITERATIONS"); do
+		if ! dpllsim_deploy "$name"; then
+			check_err 1 "deploy failed (iteration $i)"
+			break
+		fi
+		if ! dpll_device_wait_by_clock_id "$clock_id" dpllsim \
+		     > /dev/null; then
+			check_err 1 "device not on netlink (iteration $i)"
+			break
+		fi
+		if ! dpllsim_undeploy "$name"; then
+			check_err 1 "undeploy failed (iteration $i)"
+			break
+		fi
+		if ! dpll_device_wait_gone_by_clock_id "$clock_id" dpllsim; then
+			check_err 1 "device still on netlink (iteration $i)"
+			break
+		fi
+	done
+
+	if dpllsim_delete_device "$name"; then
+		untrack_device "$name"
+	else
+		check_err 1 "cannot delete device $name"
+	fi
+
+	log_test "DPLL deploy/undeploy cycling"
+}
+
+##############################################################################
+
+dpll_lib_check_prereqs
+# Another run would find the devices of this one left behind, 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.
+trap cleanup EXIT
+trap 'exit 1' INT TERM
+dpllsim_check_module
+
+tests_run
+
+exit $EXIT_STATUS
diff --git a/tools/testing/selftests/drivers/net/dpll/settings b/tools/testing/selftests/drivers/net/dpll/settings
new file mode 100644
index 00000000000000..a62d2fa1275c6b
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/dpll/settings
@@ -0,0 +1 @@
+timeout=600
diff --git a/tools/testing/selftests/drivers/net/lib/sh/dpll_lib.sh b/tools/testing/selftests/drivers/net/lib/sh/dpll_lib.sh
new file mode 100644
index 00000000000000..fc81f149d1d0b7
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/lib/sh/dpll_lib.sh
@@ -0,0 +1,334 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Helpers for DPLL selftests. Provides wrappers around the iproute2 dpll
+# tool and around the dpllsim configfs interface for creating virtual DPLL
+# devices. Sourcing this file has no side effects; call
+# dpll_lib_check_prereqs and dpll_lib_init explicitly. net/lib.sh must be
+# sourced first, it provides ksft_skip, busywait and log_info.
+#
+# Copyright (c) 2026 Red Hat, Inc.
+#
+# shellcheck disable=SC2034,SC2154 # the globals set here are read by the
+# scripts and ksft_skip is defined by net/lib.sh
+
+CONFIGFS_DIR="/sys/kernel/config"
+DPLLSIM_CFG="$CONFIGFS_DIR/dpllsim"
+
+# 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
+# the tests away from the devices of other drivers whatever clock-id
+# those use. The random start only avoids collisions between runs, as
+# the devices are named after their clock-id in configfs.
+DPLLSIM_NEXT_CLOCK_ID=$((96000 + RANDOM))
+
+##############################################################################
+# Prerequisites and device selection
+
+dpll_lib_check_prereqs()
+{
+	if [ "$EUID" -ne 0 ]; then
+		echo "SKIP: must be run as root"
+		exit "$ksft_skip"
+	fi
+
+	local cmd
+	for cmd in jq dpll; do
+		if ! command -v "$cmd" &> /dev/null; then
+			echo "SKIP: $cmd not found"
+			exit "$ksft_skip"
+		fi
+	done
+
+	if ! dpll device help 2>&1 | grep -q "show"; then
+		echo "SKIP: dpll tool too old, missing 'device show'"
+		exit "$ksft_skip"
+	fi
+}
+
+# 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 file lives 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=""
+
+# Create DPLL_RUN_DIR when it is missing, skip when that fails.
+dpll_lib_run_dir()
+{
+	mkdir -p "$DPLL_RUN_DIR" 2>/dev/null
+	if ! [ -d "$DPLL_RUN_DIR" ]; then
+		echo "SKIP: cannot create $DPLL_RUN_DIR"
+		exit "$ksft_skip"
+	fi
+}
+
+dpll_lib_lock()
+{
+	if ! command -v flock &> /dev/null; then
+		echo "SKIP: flock not found"
+		exit "$ksft_skip"
+	fi
+	dpll_lib_run_dir
+	if ! { exec {DPLL_LOCK_FD}> "$DPLL_LOCK_FILE"; } 2>/dev/null; then
+		echo "SKIP: cannot open $DPLL_LOCK_FILE"
+		exit "$ksft_skip"
+	fi
+	if ! flock -n "$DPLL_LOCK_FD"; then
+		echo "SKIP: another dpll selftest run is in progress"
+		exit "$ksft_skip"
+	fi
+}
+
+# Detect the DPLL device to test. DPLL_DEV_ID may be set by the user to
+# force a specific device; an empty value means auto-detect, which takes
+# the first device that is not a dpllsim one, so that an instance of the
+# simulator left behind by an earlier run is not mistaken for hardware.
+# Sets DPLL_DEV_ID (may end up empty) and DPLL_MODULE_NAME.
+dpll_lib_init()
+{
+	DPLL_MODULE_NAME=""
+
+	if [ -n "${DPLL_DEV_ID:-}" ]; then
+		if ! dpll device show id "$DPLL_DEV_ID" &> /dev/null; then
+			echo "SKIP: DPLL device \"$DPLL_DEV_ID\" not found"
+			exit "$ksft_skip"
+		fi
+	else
+		DPLL_DEV_ID=$(dpll device show -j 2>/dev/null | \
+			      jq -r '[.device[]? |
+				      select(.["module-name"] != "dpllsim")
+				     ][0].id // empty')
+	fi
+
+	if [ -n "$DPLL_DEV_ID" ]; then
+		DPLL_MODULE_NAME=$(dpll_device_get_module "$DPLL_DEV_ID")
+	fi
+}
+
+##############################################################################
+# Device queries
+#
+# "dpll device show id X -j" may print either a flat object or a dump-style
+# {"device":[...]} wrapper depending on the iproute2 version, so normalize
+# with (.device[0]? // .) before extracting fields. The tool is run first
+# and jq only on what it printed, so that a failed query returns its
+# status instead of the empty output a missing field produces too.
+
+dpll_device_json()
+{
+	local dev_id=$1
+	local out
+
+	out=$(dpll device show id "$dev_id" -j 2>/dev/null) || return 1
+	jq '(.device[0]? // .)' <<< "$out"
+}
+
+__dpll_device_field()
+{
+	local dev_id=$1; shift
+	local out
+
+	out=$(dpll device show id "$dev_id" -j 2>/dev/null) || return 1
+	jq -r "(.device[0]? // .) | $1 // empty" <<< "$out"
+}
+
+dpll_device_get_module()
+{
+	__dpll_device_field "$1" '.["module-name"]'
+}
+
+# Look up the netlink device id by the clock-id chosen at create time,
+# restricted to the devices of the given module when one is named. The
+# clock-id is the only reliable handle on a device the test created, as
+# netlink ids depend on what else exists, and the module name keeps a
+# lookup for a dpllsim device from returning a device of another driver
+# that happens to use the same clock-id. The clock-id is compared as a
+# string, so that it does not matter whether the tool prints it as a
+# JSON number or as a string. Return 1 when the dump itself failed, a
+# caller must not take that for a device that is gone.
+# $1: clock-id, $2: module name (optional)
+dpll_device_get_id_by_clock_id()
+{
+	local clock_id=$1
+	local module=${2:-}
+	local out
+
+	out=$(dpll device show -j 2>/dev/null) || return 1
+	jq -r --arg cid "$clock_id" --arg mod "$module" \
+	   '[.device[]? |
+	     select((.["clock-id"] | tostring) == $cid and
+		    ($mod == "" or .["module-name"] == $mod))
+	    ][0].id // empty' <<< "$out"
+}
+
+# The two checks below fail when the lookup itself failed, so that a
+# failed dump neither counts as a device that is present nor as one
+# that is gone, the wait helpers keep polling instead.
+__dpll_device_present_by_clock_id()
+{
+	local dev_id
+
+	dev_id=$(dpll_device_get_id_by_clock_id "$@") || return 1
+	[ -n "$dev_id" ] && echo "$dev_id"
+}
+
+__dpll_device_absent_by_clock_id()
+{
+	local dev_id
+
+	dev_id=$(dpll_device_get_id_by_clock_id "$@") || return 1
+	[ -z "$dev_id" ]
+}
+
+# Wait until a device with the given clock-id (and module, when given) is
+# visible on netlink and print its netlink id. Callers must check the
+# exit status rather than the output, busywait prints the output of the
+# last failed check when it times out.
+dpll_device_wait_by_clock_id()
+{
+	busywait "$BUSYWAIT_TIMEOUT" __dpll_device_present_by_clock_id "$@"
+}
+
+dpll_device_wait_gone_by_clock_id()
+{
+	busywait "$BUSYWAIT_TIMEOUT" __dpll_device_absent_by_clock_id "$@"
+}
+
+##############################################################################
+# dpllsim configfs interface
+#
+# Each directory under $DPLLSIM_CFG is one simulated device. Writing 1 to
+# <dev>/live registers the device with the DPLL core, writing 0
+# unregisters it. The structure (clock_id) 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_MOUNTED_CONFIGFS=0
+
+dpllsim_available()
+{
+	[ -d "$DPLLSIM_CFG" ] && return 0
+
+	modprobe dpllsim &> /dev/null
+	if ! mountpoint -q "$CONFIGFS_DIR"; then
+		mount -t configfs none "$CONFIGFS_DIR" &> /dev/null &&
+			DPLLSIM_MOUNTED_CONFIGFS=1
+	fi
+	[ -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_cleanup()
+{
+	local dev_path name
+
+	[ "$DPLLSIM_MOUNTED_CONFIGFS" -eq 1 ] || return 0
+	DPLLSIM_MOUNTED_CONFIGFS=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"
+		return
+	done
+	umount "$CONFIGFS_DIR" 2>/dev/null ||
+		log_info "cannot unmount $CONFIGFS_DIR"
+}
+
+dpllsim_check_module()
+{
+	if ! dpllsim_available; then
+		echo "SKIP: dpllsim module not available"
+		exit "$ksft_skip"
+	fi
+}
+
+# $1: device name, $2: clock-id. Sets DPLLSIM_CREATED_NAME as soon as the
+# directory exists, so that the cleanup handler of a script can remove a
+# device whose creation a signal interrupted before the script recorded
+# it. The name stays set once the device is complete.
+dpllsim_create_device()
+{
+	local name=$1
+	local clock_id=$2
+	local dev_path="$DPLLSIM_CFG/$name"
+
+	# Record the name before the directory exists, so that a cleanup
+	# run by a signal between the two still finds it.
+	DPLLSIM_CREATED_NAME=$name
+	if ! mkdir "$dev_path" 2>/dev/null; then
+		DPLLSIM_CREATED_NAME=""
+		return 1
+	fi
+	if ! echo "$clock_id" 2>/dev/null > "$dev_path/clock_id"; then
+		rmdir "$dev_path" 2>/dev/null
+		DPLLSIM_CREATED_NAME=""
+		return 1
+	fi
+}
+
+dpllsim_deploy()
+{
+	echo 1 2>/dev/null > "$DPLLSIM_CFG/$1/live"
+}
+
+dpllsim_undeploy()
+{
+	echo 0 2>/dev/null > "$DPLLSIM_CFG/$1/live"
+}
+
+# Take the device offline and remove it. Every step is attempted even
+# when an earlier one failed, the return value is non-zero when any did.
+# A device that does not exist counts as deleted.
+dpllsim_delete_device()
+{
+	local dev_path="$DPLLSIM_CFG/$1"
+	local ret=0
+
+	[ -d "$dev_path" ] || return 0
+
+	dpllsim_undeploy "$1" || ret=1
+	rmdir "$dev_path" || ret=1
+
+	return "$ret"
+}
+
+# Set DPLLSIM_CLOCK_ID to a clock-id unique within this test run. Must
+# not be called from a subshell (the counter increment would be lost);
+# derive per-worker id ranges in the parent instead.
+dpllsim_alloc_clock_id()
+{
+	DPLLSIM_CLOCK_ID=$((DPLLSIM_NEXT_CLOCK_ID++))
+}
+
+# Create a device, deploy it and resolve the netlink device id. Sets
+# DPLLSIM_CREATED_NAME, DPLLSIM_CLOCK_ID and DPLLSIM_DEV_ID.
+# shellcheck disable=SC2120 # the argument is optional
+dpllsim_setup_standard_device()
+{
+	local clock_id name
+
+	dpllsim_alloc_clock_id
+	clock_id=$DPLLSIM_CLOCK_ID
+	name="std$clock_id"
+	dpllsim_create_device "$name" "$clock_id" || return 1
+
+	if ! dpllsim_deploy "$name"; then
+		dpllsim_delete_device "$name"
+		return 1
+	fi
+
+	if ! DPLLSIM_DEV_ID=$(dpll_device_wait_by_clock_id "$clock_id" \
+							  dpllsim); then
+		dpllsim_delete_device "$name"
+		return 1
+	fi
+}
-- 
2.56.0
Thread (30 messages) 30 messages, 3 authors, last reply 2d ago
Keyboard shortcuts
hback out one level
jnext message in thread / next series-diff position
kprevious message in thread / previous series-diff position
ldrill in
Escclose help / fold thread tree
?toggle this help