[PATCH blktests v2 6/6] nvme/080: test connect with offline CPUs

Daniel Wagner dwagner at suse.de
Wed Nov 4 06:25:18 PST 2026


blk-mq maps all possible CPUs to the hardware contexts, offline CPUs
included. If a hardware context is mapped only to offline CPUs,
blk_mq_alloc_request_hctx has no online CPU to select the software
context from and fails with -EXDEV. The fabrics transports allocate the
Connect command for each I/O queue this way, so connecting fails while
CPUs are offline.

Offline the upper half of the CPUs and connect. Check that a hardware
context is mapped only to offline CPUs, bring them online, run I/O from
one of them and trace that its commands are sent on the queue of that
hardware context.

The bug was introduced by kernel commit 20e4d8139319 ("blk-mq: simplify
queue mapping & schedule with each possisble CPU") and is fixed by
kernel commit XXX ("blk-mq: allow passthrough requests on hctxs without
online CPUs").

Signed-off-by: Daniel Wagner <dwagner at suse.de>
---
 tests/nvme/080     | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/nvme/080.out |   2 +
 2 files changed, 144 insertions(+)

diff --git a/tests/nvme/080 b/tests/nvme/080
new file mode 100755
index 000000000000..eea4ed237218
--- /dev/null
+++ b/tests/nvme/080
@@ -0,0 +1,142 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-3.0+
+# Copyright (c) 2026 SUSE LLC and contributors
+#
+# Test connecting with offline CPUs. blk-mq maps all possible CPUs to the
+# hardware contexts, the offline ones too. If only offline CPUs are mapped to
+# a hardware context, blk_mq_alloc_request_hctx has no online CPU to take the
+# software context from and fails with -EXDEV. The fabrics transports allocate
+# the Connect command for each I/O queue this way, so the connect fails.
+#
+# Offline the upper half of the CPUs and connect. Check that a hardware context
+# has only offline CPUs mapped, bring them online, run I/O from one of them and
+# trace the queue IDs of the commands to verify that the hardware context is
+# used and works.
+#
+# The bug was introduced by commit 20e4d8139319 ("blk-mq: simplify queue
+# mapping & schedule with each possisble CPU").
+#
+# Regression test for commit XXX ("blk-mq: allow passthrough requests on hctxs
+# without online CPUs").
+
+. tests/nvme/rc
+. common/cpuhotplug
+
+DESCRIPTION="Test connect with offline CPUs"
+QUICK=1
+
+requires() {
+	_nvme_requires
+	_have_fio
+	_have_loop
+	_require_nvme_trtype_is_fabrics
+	_require_min_cpus 4
+	_have_cpu_hotplug
+	_have_tracefs
+}
+
+set_conditions() {
+	_set_nvme_trtype "$@"
+}
+
+offline_upper_cpus() {
+	local -a cpus
+	local cpu
+
+	mapfile -t cpus < <(printf '%s\n' "${HOTPLUGGABLE_CPUS[@]}" | sort -n)
+	for cpu in "${cpus[@]:$((${#cpus[@]} / 2))}"; do
+		_offline_cpu "${cpu}"
+	done
+}
+
+# Print the hctx directory and the CPUs of the first hardware context which
+# only has offline CPUs mapped.
+find_offline_hctx() {
+	local disk="$1"
+	local hctx
+	local cpu
+	local cpus
+
+	for hctx in /sys/block/"${disk}"/mq/*; do
+		cpus=$(tr -d ' ' < "${hctx}/cpu_list" | tr ',' ' ')
+		for cpu in ${cpus}; do
+			if [[ $(cat "/sys/devices/system/cpu/cpu${cpu}/online" \
+					2>/dev/null || echo 1) == 1 ]]; then
+				continue 2
+			fi
+		done
+		echo "$(basename "${hctx}") ${cpus}"
+		return 0
+	done
+	return 1
+}
+
+test() {
+	echo "Running ${TEST_NAME}"
+
+	local nvmedev
+	local disk
+	local ns
+	local hctx
+	local cpu_list
+	local -a cpus
+	local cpu
+	local nr_cmds
+	local trace_dir="/sys/kernel/debug/tracing/instances/blktests-nvme-080"
+	local skipped=false
+
+	_setup_nvmet
+
+	offline_upper_cpus
+
+	_nvmet_target_setup
+
+	if _nvme_connect_subsys; then
+		_nvmf_wait_for_state "${def_subsysnqn}" "live" || echo FAIL
+		nvmedev=$(_find_nvme_dev "${def_subsysnqn}")
+		disk=$(basename /sys/class/nvme/"${nvmedev}"/nvme*n*)
+		ns=$(_find_nvme_ns "${def_subsys_uuid}")
+
+		# The CPUs are spread over the hardware contexts in topology
+		# order, so usually the upper ones end up without online CPU.
+		if read -r hctx cpu_list < <(find_offline_hctx "${disk}"); then
+			read -r -a cpus <<< "${cpu_list}"
+			for cpu in "${HOTPLUGGABLE_CPUS[@]}"; do
+				_online_cpu "${cpu}"
+			done
+
+			cpu=${cpus[0]}
+			mkdir "${trace_dir}"
+			if _nvme_have_setup_cmd_event "${trace_dir}"; then
+				_nvme_trace_qid_start "${trace_dir}" \
+					"${nvmedev#nvme}" $((hctx + 1))
+				_run_fio_verify_io --size=32m \
+					--filename="/dev/${ns}" \
+					--cpus_allowed="${cpu}" ||
+					echo "FAIL: fio verify on CPU ${cpu}"
+				nr_cmds=$(_nvme_trace_qid_stop "${trace_dir}")
+				if [[ "${nr_cmds}" -eq 0 ]]; then
+					echo "FAIL: no commands from CPU ${cpu} on hctx ${hctx}"
+				fi
+			else
+				skipped=true
+			fi
+			rmdir "${trace_dir}"
+		else
+			SKIP_REASONS+=("no hardware context with only offline CPUs")
+			skipped=true
+		fi
+
+		_nvme_disconnect_subsys
+	else
+		echo "FAIL: controller not ready"
+	fi
+
+	_nvmet_target_cleanup
+
+	if [[ "${skipped}" = true ]]; then
+		return 1
+	fi
+
+	echo "Test complete"
+}
diff --git a/tests/nvme/080.out b/tests/nvme/080.out
new file mode 100644
index 000000000000..15119b8d7a83
--- /dev/null
+++ b/tests/nvme/080.out
@@ -0,0 +1,2 @@
+Running nvme/080
+Test complete

-- 
2.56.0




More information about the Linux-nvme mailing list