[RFC PATCH blktests v1] nvme: check user queue limits across namespace rescans

Yao Sang sangyao at kylinos.cn
Thu Jul 9 01:48:26 PDT 2026


NVMe namespace rescans should not reset queue user caps on the namespace
head. Add a regression test that lowers max_sectors_kb, optionally lowers
discard_max_bytes, optionally disables write_zeroes_unmap_max_bytes, runs
nvme ns-rescan, and verifies that the user values are preserved.

Mark the test as zoned-capable so it can run on ZNS devices as well.

Signed-off-by: Yao Sang <sangyao at kylinos.cn>
---
 tests/nvme/068     | 151 +++++++++++++++++++++++++++++++++++++++++++++
 tests/nvme/068.out |   2 +
 2 files changed, 153 insertions(+)
 create mode 100755 tests/nvme/068
 create mode 100644 tests/nvme/068.out

diff --git a/tests/nvme/068 b/tests/nvme/068
new file mode 100755
index 0000000..a7786db
--- /dev/null
+++ b/tests/nvme/068
@@ -0,0 +1,151 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-3.0+
+#
+# Test queue user limits across NVMe namespace rescans.
+
+. tests/nvme/rc
+
+DESCRIPTION="check queue user limits across NVMe namespace rescans"
+QUICK=1
+CAN_BE_ZONED=1
+
+requires() {
+	_nvme_requires
+	_have_program nvme
+}
+
+_test_dev_nvme_ctrl_dev() {
+	local ctrl
+
+	ctrl="$(_nvme_get_ctrl_list | head -n 1)"
+	if [[ -z "$ctrl" ]]; then
+		return 1
+	fi
+
+	echo "/dev/$(basename "$ctrl")"
+}
+
+device_requires() {
+	local ctrl_dev
+
+	_require_test_dev_is_nvme
+	_require_test_dev_sysfs queue/max_sectors_kb queue/max_hw_sectors_kb
+
+	if ! ctrl_dev="$(_test_dev_nvme_ctrl_dev)"; then
+		SKIP_REASONS+=("unable to find NVMe controller for $TEST_DEV")
+		return 1
+	fi
+	if [[ ! -c "$ctrl_dev" ]]; then
+		SKIP_REASONS+=("$ctrl_dev is not a character device")
+		return 1
+	fi
+}
+
+_cleanup_queue_limits() {
+	if [[ -z "${nvme068_queue_path:-}" ]]; then
+		return
+	fi
+
+	echo "$nvme068_max_sectors_kb" > "$nvme068_queue_path/max_sectors_kb"
+	if [[ -n "${nvme068_discard_max_bytes:-}" ]]; then
+		echo "$nvme068_discard_max_bytes" > \
+			"$nvme068_queue_path/discard_max_bytes"
+	fi
+	if [[ -n "${nvme068_wzeroes_unmap_max_bytes:-}" ]]; then
+		echo "$nvme068_wzeroes_unmap_max_bytes" > \
+			"$nvme068_queue_path/write_zeroes_unmap_max_bytes"
+	fi
+}
+
+_check_queue_limit() {
+	local queue_path=$1
+	local attr=$2
+	local expected=$3
+	local actual
+
+	actual="$(cat "$queue_path/$attr")"
+	if [[ "$actual" -ne "$expected" ]]; then
+		echo "ERROR: $attr changed from $expected to $actual"
+	fi
+}
+
+test_device() {
+	local queue_path="$TEST_DEV_SYSFS/queue"
+	local ctrl_dev
+	local max_hw_sectors_kb
+	local max_sectors_kb
+	local discard_hw_bytes
+	local discard_granularity
+	local discard_max_bytes=""
+	local wzeroes_unmap_hw_bytes
+	local wzeroes_unmap_max_bytes=""
+
+	echo "Running ${TEST_NAME}"
+
+	ctrl_dev="$(_test_dev_nvme_ctrl_dev)"
+	nvme068_queue_path="$queue_path"
+	nvme068_max_sectors_kb="$(cat "$queue_path/max_sectors_kb")"
+	nvme068_discard_max_bytes=""
+	nvme068_wzeroes_unmap_max_bytes=""
+	_register_test_cleanup _cleanup_queue_limits
+
+	max_hw_sectors_kb="$(cat "$queue_path/max_hw_sectors_kb")"
+	max_sectors_kb=$((max_hw_sectors_kb / 2))
+	if ((max_sectors_kb == 0)); then
+		max_sectors_kb=1
+	fi
+
+	if [[ -e "$queue_path/discard_max_bytes" &&
+	      -e "$queue_path/discard_max_hw_bytes" &&
+	      -e "$queue_path/discard_granularity" ]]; then
+		nvme068_discard_max_bytes="$(cat "$queue_path/discard_max_bytes")"
+		if echo "$nvme068_discard_max_bytes" > \
+				"$queue_path/discard_max_bytes" 2>/dev/null; then
+			discard_hw_bytes="$(cat "$queue_path/discard_max_hw_bytes")"
+			discard_granularity="$(cat "$queue_path/discard_granularity")"
+			if ((discard_granularity > 0 &&
+			     discard_hw_bytes >= discard_granularity * 2)); then
+				discard_max_bytes=$(((discard_hw_bytes / 2) /
+					discard_granularity * discard_granularity))
+			fi
+		else
+			nvme068_discard_max_bytes=""
+		fi
+	fi
+
+	if [[ -e "$queue_path/write_zeroes_unmap_max_bytes" &&
+	      -e "$queue_path/write_zeroes_unmap_max_hw_bytes" ]]; then
+		nvme068_wzeroes_unmap_max_bytes="$(<"$queue_path/write_zeroes_unmap_max_bytes")"
+		wzeroes_unmap_hw_bytes="$(<"$queue_path/write_zeroes_unmap_max_hw_bytes")"
+		if ((wzeroes_unmap_hw_bytes > 0)); then
+			wzeroes_unmap_max_bytes=0
+		fi
+	fi
+
+	echo "$max_sectors_kb" > "$queue_path/max_sectors_kb"
+	if [[ -n "$discard_max_bytes" ]]; then
+		echo "$discard_max_bytes" > "$queue_path/discard_max_bytes"
+	fi
+	if [[ -n "$wzeroes_unmap_max_bytes" ]]; then
+		echo "$wzeroes_unmap_max_bytes" > \
+			"$queue_path/write_zeroes_unmap_max_bytes"
+	fi
+
+	if ! nvme ns-rescan "$ctrl_dev" >> "$FULL" 2>&1; then
+		echo "ERROR: ns-rescan failed"
+	fi
+
+	_check_queue_limit "$queue_path" max_sectors_kb "$max_sectors_kb"
+	if [[ -n "$discard_max_bytes" ]]; then
+		_check_queue_limit "$queue_path" discard_max_bytes \
+			"$discard_max_bytes"
+	fi
+	if [[ -n "$wzeroes_unmap_max_bytes" ]]; then
+		_check_queue_limit "$queue_path" write_zeroes_unmap_max_bytes \
+			"$wzeroes_unmap_max_bytes"
+	fi
+
+	_cleanup_queue_limits
+
+	echo "Test complete"
+}
diff --git a/tests/nvme/068.out b/tests/nvme/068.out
new file mode 100644
index 0000000..28407c6
--- /dev/null
+++ b/tests/nvme/068.out
@@ -0,0 +1,2 @@
+Running nvme/068
+Test complete
-- 
2.25.1




More information about the Linux-nvme mailing list