new file mode 100755
@@ -0,0 +1,309 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-3.0+
+# Copyright (C) 2024 Western Digital Corporation or its affiliates.
+#
+# Test DM zone resource limits stacking (max open zones and max active zones
+# limits). The limits shall follow these rules:
+# 1) For a target mapping an entire zoned block device, the limits for the
+# target are set to the limits of the device.
+# 2) For a target partially mapping a zoned block device, the number of
+# mapped sequential zones is used to determine the limits: if the
+# target maps more sequential write required zones than the device
+# limits, then the limits of the device are used as-is. If the number
+# of mapped sequential zones is lower than the limits, then we assume
+# that the target has no limits (limits set to 0).
+# As this evaluation is done for each target, the zone resource limits
+# for the mapped device are evaluated as the non-zero minimum of the
+# limits of all the targets.
+
+. tests/zbd/rc
+
+DESCRIPTION="DM zone resource limits stacking"
+QUICK=1
+
+requires() {
+ _have_kver 6 11
+ _have_driver dm-mod
+ _have_driver dm-crypt
+ _have_program dmsetup
+ _have_program cryptsetup
+}
+
+# setup_dm: <map_type> <zoned device> <start zone> [nr zones]>
+# Create a DM device using dm-linear, dm-error or dm-crypt.
+# If <nr_zones> is omitted, then all zones from <start zone> are mapped.
+setup_dm() {
+ local type devpath dname nrz mapzstart mapnrz zsz maplen mapoffset
+ local keyfile="$TMPDIR/keyfile"
+
+ type="$1"
+ devpath="$2"
+ dname="${devpath##*/}"
+ nrz=$(<"/sys/block/${dname}/queue/nr_zones")
+
+ mapzstart="$3"
+ if [ $# == 3 ]; then
+ mapnrz=$((nrz - mapzstart))
+ else
+ mapnrz="$4"
+ if ((mapnrz == 0)) || (( mapzstart + mapnrz > nrz )); then
+ mapnrz=$((nrz - mapzstart))
+ fi
+ fi
+
+ zsz=$(<"/sys/block/${dname}/queue/chunk_sectors")
+ maplen=$((mapnrz * zsz))
+ mapoffset=$((mapzstart * zsz))
+
+ case "$type" in
+ linear|error)
+ if echo "0 ${maplen} ${type} ${devpath} ${mapoffset}" | \
+ dmsetup create "zbd_011-${type}"; then
+ echo "zbd_011-${type}"
+ fi
+ ;;
+ crypt)
+ # Generate a key (4096-bits)
+ dd if=/dev/random of="$keyfile" bs=1 count=512 &> /dev/null
+ if cryptsetup open --type plain --cipher null --key-size 512 \
+ --key-file "$keyfile" --size ${maplen} \
+ --offset ${mapoffset} "${devpath}" \
+ "zbd_011-crypt"; then
+ echo "zbd_011-crypt"
+ fi
+ ;;
+ esac
+}
+
+# setup_concat: <zoned dev0> <start_zone0> <nr zones0> <zoned dev1> <start_zone1> <nr zones1>
+# Use dm-linear to concatenate 2 sets of zones into a zoned block device.
+# If <nr_zonesX> is 0, then all zones from <start_zoneX> are mapped.
+setup_concat() {
+ local dev0 dname0 nrz0 mapzstart0 mapnrz0
+ local dev1 dname1 nrz1 mapzstart1 mapnrz1
+
+ dev0="$(realpath "$1")"
+ dname0="$(basename "${dev0}")"
+
+ dev1="$(realpath "$4")"
+ dname1="$(basename "${dev1}")"
+
+ nrz0=$(< "/sys/block/${dname0}/queue/nr_zones")
+ mapzstart0=$2
+ if ((mapzstart0 >= nrz0)); then
+ echo "Invalid start zone ${mapzstart0} / ${nrz0}"
+ exit 1
+ fi
+
+ mapnrz0=$3
+ if ((mapnrz0 == 0 || mapzstart0 + mapnrz0 > nrz0)); then
+ mapnrz0=$((nrz0 - mapzstart0))
+ fi
+
+ nrz1=$(< "/sys/block/${dname1}/queue/nr_zones")
+ mapzstart1=$5
+ if ((mapzstart1 >= nrz1)); then
+ echo "Invalid start zone ${mapzstart1} / ${nrz1}"
+ return 1
+ fi
+
+ mapnrz1=$6
+ if ((mapnrz1 == 0 || mapzstart1 + mapnrz1 > nrz1)); then
+ mapnrz1=$((nrz1 - mapzstart1))
+ fi
+
+ zsz=$(< "/sys/block/${dname0}/queue/chunk_sectors")
+ maplen0=$((mapnrz0 * zsz))
+ mapofst0=$((mapzstart0 * zsz))
+ maplen1=$((mapnrz1 * zsz))
+ mapofst1=$((mapzstart1 * zsz))
+
+ # Linear table entries: "start length linear device offset"
+ # start: starting block in virtual device
+ # length: length of this segment
+ # device: block device, referenced by the device name or by major:minor
+ # offset: starting offset of the mapping on the device
+
+ if echo -e "0 ${maplen0} linear /dev/${dname0} ${mapofst0}\n" \
+ "${maplen0} ${maplen1} linear /dev/${dname1} ${mapofst1}" | \
+ dmsetup create "zbd_011-concat"; then
+ echo "zbd_011-concat"
+ fi
+}
+
+# check_limits: <dev> <zoned model> <number of zones> <max open limit> <max active limit>
+# Check that the zoned model, number of zones and zone resource limits of a DM
+# device match the values of the arguments passed.
+check_limits() {
+ local ret=0
+ local devpath dname sysqueue model nrz moz maz
+
+ devpath=$(realpath "$1")
+ sysqueue="/sys/block/${devpath##*/}/queue"
+ model="$(< "${sysqueue}/zoned")"
+
+ if [[ "$model" != "$2" ]]; then
+ echo "Invalid zoned model: ${model} should be $2"
+ return 1
+ fi
+
+ nrz=$(< "${sysqueue}/nr_zones")
+ if [[ ${nrz} -ne $3 ]]; then
+ echo "Invalid number of zones: ${nrz} should be $3"
+ ret=1
+ fi
+
+ # Non-zoned block devices do not have max_open_zones and
+ # max_active_zones sysfs attributes.
+ [[ "$2" == "none" ]] && return $ret
+
+ moz=$(< "${sysqueue}/max_open_zones")
+ if [[ ${moz} -ne $4 ]]; then
+ echo "Invalid max open zones limit: ${moz} should be $4"
+ ret=1
+ fi
+
+ maz=$(< "${sysqueue}/max_active_zones")
+ if [[ ${maz} -ne $5 ]]; then
+ echo "Invalid max active zones limit: ${maz} should be $5"
+ ret=1
+ fi
+
+ return $ret
+}
+
+declare -a TEST_DESCRIPTIONS
+declare -a SETUP_COMMANDS
+declare -a EXPECTED_LIMITS
+
+# Test 1
+TEST_DESCRIPTIONS+=("Map all zones of the 1st nullb")
+SETUP_COMMANDS+=("setup_dm linear /dev/nullb_zbd_011_1 0")
+EXPECTED_LIMITS+=("host-managed 1152 64 64")
+
+# Test 2
+TEST_DESCRIPTIONS+=("Map all zones of the 2nd nullb")
+SETUP_COMMANDS+=("setup_dm linear /dev/nullb_zbd_011_2 0")
+EXPECTED_LIMITS+=("host-managed 512 48 0")
+
+# Test 3
+TEST_DESCRIPTIONS+=("Map all CNV zones of the 1st nullb")
+SETUP_COMMANDS+=("setup_dm linear /dev/nullb_zbd_011_1 0 128")
+EXPECTED_LIMITS+=("none 0 0 0")
+
+# Test 4
+TEST_DESCRIPTIONS+=("Map all SWR zones of the 1st nullb")
+SETUP_COMMANDS+=("setup_dm linear /dev/nullb_zbd_011_1 128")
+EXPECTED_LIMITS+=("host-managed 1024 64 64")
+
+# Test 5
+TEST_DESCRIPTIONS+=("Map 32 SWR zones of the 1st nullb")
+SETUP_COMMANDS+=("setup_dm linear /dev/nullb_zbd_011_1 128 32")
+EXPECTED_LIMITS+=("host-managed 32 0 0")
+
+# Test 6
+TEST_DESCRIPTIONS+=("Map 64 SWR zones of the 1st nullb")
+SETUP_COMMANDS+=("setup_dm linear /dev/nullb_zbd_011_1 128 64")
+EXPECTED_LIMITS+=("host-managed 64 0 0")
+
+# Test 7
+TEST_DESCRIPTIONS+=("Map 128 SWR zones of the 1st nullb")
+SETUP_COMMANDS+=("setup_dm linear /dev/nullb_zbd_011_1 128 128")
+EXPECTED_LIMITS+=("host-managed 128 64 64")
+
+# Test 8
+TEST_DESCRIPTIONS+=("Concatenate all zones of the 1st and 2nd nullb")
+SETUP_COMMANDS+=("setup_concat /dev/nullb_zbd_011_1 0 0 /dev/nullb_zbd_011_2 0 0")
+EXPECTED_LIMITS+=("host-managed 1664 48 64")
+
+# Test 9
+TEST_DESCRIPTIONS+=("Map 32 CNV zones of the 1st nullb and all SWR zones of the 2nd nullb")
+SETUP_COMMANDS+=("setup_concat /dev/nullb_zbd_011_1 0 32 /dev/nullb_zbd_011_2 0 0")
+EXPECTED_LIMITS+=("host-managed 544 48 0")
+
+# Test 10
+TEST_DESCRIPTIONS+=("Map all SWR zones of the 1st nullb and all SWR zones of the 2nd nullb")
+SETUP_COMMANDS+=("setup_concat /dev/nullb_zbd_011_1 128 0 /dev/nullb_zbd_011_2 0 0")
+EXPECTED_LIMITS+=("host-managed 1536 48 64")
+
+# Test 11
+TEST_DESCRIPTIONS+=("Map 32 SWR zones of the 1st nullb and all SWR zones of the 2nd nullb")
+SETUP_COMMANDS+=("setup_concat /dev/nullb_zbd_011_1 128 32 /dev/nullb_zbd_011_2 0 0")
+EXPECTED_LIMITS+=("host-managed 544 48 0")
+
+# Test 12
+TEST_DESCRIPTIONS+=("Map 128 SWR zones of the 1st nullb and 16 SWR zones of the 2nd nullb")
+SETUP_COMMANDS+=("setup_concat /dev/nullb_zbd_011_1 128 128 /dev/nullb_zbd_011_2 0 16")
+EXPECTED_LIMITS+=("host-managed 144 64 64")
+
+# Test 13
+TEST_DESCRIPTIONS+=("Map 32 SWR zones of the 1st nullb and 16 SWR zones of the 2nd nullb")
+SETUP_COMMANDS+=("setup_concat /dev/nullb_zbd_011_1 128 32 /dev/nullb_zbd_011_2 0 16")
+EXPECTED_LIMITS+=("host-managed 48 0 0")
+
+# Test 14
+TEST_DESCRIPTIONS+=("Map 32 SWR zones of the 1st nullb and 48 SWR zones of the 2nd nullb")
+SETUP_COMMANDS+=("setup_concat /dev/nullb_zbd_011_1 128 32 /dev/nullb_zbd_011_2 0 48")
+EXPECTED_LIMITS+=("host-managed 80 0 0")
+
+# Test 15
+TEST_DESCRIPTIONS+=("Map 32 SWR zones of the 1st nullb and 64 SWR zones of the 2nd nullb")
+SETUP_COMMANDS+=("setup_concat /dev/nullb_zbd_011_1 128 32 /dev/nullb_zbd_011_2 0 64")
+EXPECTED_LIMITS+=("host-managed 96 48 0")
+
+# Test 16
+TEST_DESCRIPTIONS+=("Insert dm-error on the 1st nullb")
+SETUP_COMMANDS+=("setup_dm error /dev/nullb_zbd_011_1 0")
+EXPECTED_LIMITS+=("host-managed 1152 64 64")
+
+# Test 17
+TEST_DESCRIPTIONS+=("Map all zones of the 1st nullb with dm-crypt")
+SETUP_COMMANDS+=("setup_dm crypt /dev/nullb_zbd_011_1 0")
+EXPECTED_LIMITS+=("host-managed 1152 64 64")
+
+# Test 18
+TEST_DESCRIPTIONS+=("Map all CNV zones of the 1st nullb with dm-crypt")
+SETUP_COMMANDS+=("setup_dm crypt /dev/nullb_zbd_011_1 0 128")
+EXPECTED_LIMITS+=("none 0 0 0")
+
+# Test 19
+TEST_DESCRIPTIONS+=("Map all CNV zones and 128 SWR zones of the 1st nullb with dm-crypt")
+SETUP_COMMANDS+=("setup_dm crypt /dev/nullb_zbd_011_1 0 256")
+EXPECTED_LIMITS+=("host-managed 256 64 64")
+
+# Test 20
+TEST_DESCRIPTIONS+=("Map 64 SWR zones of the 1st nullb with dm-crypt")
+SETUP_COMMANDS+=("setup_dm crypt /dev/nullb_zbd_011_1 128 64")
+EXPECTED_LIMITS+=("host-managed 64 0 0")
+
+# Test 21
+TEST_DESCRIPTIONS+=("Map all SWR zones of the 2nd nullb with dm-crypt")
+SETUP_COMMANDS+=("setup_dm crypt /dev/nullb_zbd_011_2 0")
+EXPECTED_LIMITS+=("host-managed 512 48 0")
+
+test() {
+ local i dm_name check_cmd
+
+ echo "Running ${TEST_NAME}"
+
+ _configure_null_blk nullb_zbd_011_1 size=2304 zoned=1 \
+ zone_size=2 zone_nr_conv=128 \
+ zone_max_open=64 zone_max_active=64 power=1
+ _configure_null_blk nullb_zbd_011_2 size=1024 zoned=1 \
+ zone_size=2 zone_nr_conv=0 \
+ zone_max_open=48 zone_max_active=0 power=1
+
+ for ((i = 0; i < ${#TEST_DESCRIPTIONS[@]}; i++)); do
+ dm_name=$(eval "${SETUP_COMMANDS[i]}")
+ check_cmd="check_limits /dev/mapper/$dm_name"
+ check_cmd+=" ${EXPECTED_LIMITS[i]}"
+ if ! eval "$check_cmd"; then
+ echo "Test $((i + 1)) failed: ${TEST_DESCRIPTIONS[i]}"
+ fi
+ dmsetup remove "$dm_name"
+ done
+
+ _exit_null_blk
+
+ echo "Test complete"
+}
new file mode 100644
@@ -0,0 +1,2 @@
+Running zbd/011
+Test complete