#!/bin/bash
# SPDX-License-Identifier: GPL-3.0+
# Copyright (C) 2019 Western Digital Corporation or its affiliates.
#
# Test zones are mapped correctly between a logical device and its container
# device. Move write pointers of sequential write required zones on the
# container devices, and confirm same write pointer positions of zones on the
# logical devices.

. tests/zbd/rc

DESCRIPTION="zone mapping between logical and container devices"
CAN_BE_ZONED=1
QUICK=1

requires() {
	_have_program dmsetup
}

device_requires() {
	_require_test_dev_is_logical
}

# Select test target zones. Pick up the first sequential required zones. If
# available, add one or two more sequential required zones. One is at the last
# end of TEST_DEV. The other is in middle between the first and the last zones.
select_zones() {
	local -i zone_idx
	local -a zones

	zone_idx=$(_find_first_sequential_zone) || return $?
	zones=( "${zone_idx}" )
	if zone_idx=$(_find_last_sequential_zone); then
		zones+=( "${zone_idx}" )
		if zone_idx=$(_find_sequential_zone_in_middle \
				      "${zones[0]}" "${zones[1]}"); then
			zones+=( "${zone_idx}" )
		fi
	fi
	echo "${zones[@]}"
}

test_device() {
	local -i bs
	local -a test_z # test target zones
	local -a test_z_start

	echo "Running ${TEST_NAME}"

	# Get physical block size to meet zoned block device I/O requirement
	_get_sysfs_variable "${TEST_DEV}" || return $?
	bs=${SYSFS_VARS[SV_PHYS_BLK_SIZE]}
	_put_sysfs_variable

	# Get test target zones
	_get_blkzone_report "${TEST_DEV}" || return $?
	read -r -a test_z < <(select_zones)
	for ((i = 0; i < ${#test_z[@]}; i++)); do
		test_z_start+=("${ZONE_STARTS[test_z[i]]}")
	done
	echo "${test_z[*]}" >> "$FULL"
	echo "${test_z_start[*]}" >> "$FULL"
	_put_blkzone_report
	if ((!${#test_z[@]})); then
		echo "Test target zones not available on ${TEST_DEV}"
		return 1
	fi

	# Reset and move write pointers of the container device
	for ((i=0; i < ${#test_z[@]}; i++)); do
		local -a arr opts

		read -r -a arr < <(_get_dev_container_and_sector \
					   "${test_z_start[i]}")
		container_dev="${arr[0]}"
		container_start="${arr[1]}"

		echo "${container_dev}" "${container_start}" >> "$FULL"

		opts=(-o "${container_start}" -c 1)
		if blkzone -h | grep -q -e --force; then
			opts+=(--force)
		fi
		if ! blkzone reset "${opts[@]}" "${container_dev}"; then
			echo "Reset zone failed"
			return 1
		fi

		if ! dd if=/dev/zero of="${container_dev}" bs="${bs}" \
		     count=$((4096 * (i + 1) / bs)) oflag=direct \
		     seek=$((container_start * 512 / bs)) \
		     >> "$FULL" 2>&1 ; then
			echo "dd failed"
		fi

		# Wait for partition table re-read event settles
		udevadm settle
	done

	# Check write pointer positions on the logical device
	_get_blkzone_report "${TEST_DEV}" || return $?
	for ((i=0; i < ${#test_z[@]}; i++)); do
		if ((ZONE_WPTRS[test_z[i]] != 8 * (i + 1))); then
			echo "Unexpected write pointer position"
			echo -n "zone=${i}, wp=${ZONE_WPTRS[i]}, "
			echo "dev=${TEST_DEV}"
		fi
		echo "${ZONE_WPTRS[${test_z[i]}]}" >> "$FULL"
	done
	_put_blkzone_report

	# When the logical devices is dm-crypt, the write pointer moves on
	# its container device break data contents on the logical device. Reset
	# zones of the logical device to wipe out the broken data.
	for ((i=0; i < ${#test_z[@]}; i++)); do
		blkzone reset -o "${test_z_start[i]}" -c 1 "${TEST_DEV}"
	done

	echo "Test complete"
}
