#!/bin/bash
# SPDX-License-Identifier: GPL-3.0+
# Copyright (C) 2025 Oracle and/or its affiliates
#
# Test SCSI Atomic Writes with scsi_debug

. tests/scsi/rc
. common/scsi_debug
. common/xfs

DESCRIPTION="test scsi atomic writes"
QUICK=1

requires() {
	_have_driver scsi_debug
	_have_xfs_io_atomic_write
}

device_requires() {
	_require_device_support_atomic_writes
}

fallback_device() {
	local scsi_debug_params=(
		delay=0
		atomic_wr=1
	)
	if ! _configure_scsi_debug "${scsi_debug_params[@]}"; then
		return 1
		fi
	echo "/dev/${SCSI_DEBUG_DEVICES[0]}"
}

cleanup_fallback_device() {
	_exit_scsi_debug
}

test_device() {
	local scsi_debug_atomic_wr_max_length
	local scsi_debug_atomic_wr_gran
	local scsi_atomic_max_bytes
	local scsi_atomic_min_bytes
	local sysfs_max_hw_sectors_kb
	local max_hw_bytes
	local sysfs_logical_block_size
	local sysfs_atomic_max_bytes
	local sysfs_atomic_unit_max_bytes
	local sysfs_atomic_unit_min_bytes
	local statx_atomic_min
	local statx_atomic_max
	local bytes_to_write
	local bytes_written
	local test_desc

	echo "Running ${TEST_NAME}"

	sysfs_logical_block_size=$(< "${TEST_DEV_SYSFS}"/queue/logical_block_size)
	sysfs_max_hw_sectors_kb=$(< "${TEST_DEV_SYSFS}"/queue/max_hw_sectors_kb)
	max_hw_bytes=$(( "$sysfs_max_hw_sectors_kb" * 1024 ))
	sysfs_atomic_max_bytes=$(< "${TEST_DEV_SYSFS}"/queue/atomic_write_max_bytes)
	sysfs_atomic_unit_max_bytes=$(< "${TEST_DEV_SYSFS}"/queue/atomic_write_unit_max_bytes)
	sysfs_atomic_unit_min_bytes=$(< "${TEST_DEV_SYSFS}"/queue/atomic_write_unit_min_bytes)
	scsi_debug_atomic_wr_max_length=$(< /sys/module/scsi_debug/parameters/atomic_wr_max_length)
	scsi_debug_atomic_wr_gran=$(< /sys/module/scsi_debug/parameters/atomic_wr_gran)
	scsi_atomic_max_bytes=$(( "$scsi_debug_atomic_wr_max_length" * "$sysfs_logical_block_size" ))
	scsi_atomic_min_bytes=$(( "$scsi_debug_atomic_wr_gran" * "$sysfs_logical_block_size" ))

	test_desc="TEST 1 - Verify sysfs atomic attributes"
	if [ "$max_hw_bytes" -ge "$sysfs_atomic_max_bytes" ] &&
		[ "$sysfs_atomic_max_bytes" -ge "$sysfs_atomic_unit_max_bytes" ] &&
		[ "$sysfs_atomic_unit_max_bytes" -ge "$sysfs_atomic_unit_min_bytes" ]
	then
		echo "$test_desc - pass"
	else
		echo "$test_desc - fail $max_hw_bytes - $sysfs_max_hw_sectors_kb -" \
			"$sysfs_atomic_max_bytes - $sysfs_atomic_unit_max_bytes -" \
			"$sysfs_atomic_unit_min_bytes"
	fi

	test_desc="TEST 2 - check scsi_debug atomic_wr_max_length is the same as sysfs atomic_write_max_bytes"
	if [ "$scsi_atomic_max_bytes" -le "$max_hw_bytes" ]
	then
		if [ "$scsi_atomic_max_bytes" = "$sysfs_atomic_max_bytes" ]
		then
			echo "$test_desc - pass"
		else
			echo "$test_desc - fail $scsi_atomic_max_bytes - $max_hw_bytes -" \
				"$sysfs_atomic_max_bytes"
		fi
	else
		if [ "$sysfs_atomic_max_bytes" = "$max_hw_bytes" ]
		then
			echo "$test_desc - pass"
		else
			echo "$test_desc - fail $scsi_atomic_max_bytes - $max_hw_bytes -" \
				"$sysfs_atomic_max_bytes"
		fi
	fi

	test_desc="TEST 3 - check sysfs atomic_write_unit_max_bytes <= scsi_debug atomic_wr_max_length"
	if (("$sysfs_atomic_unit_max_bytes" <= "$scsi_atomic_max_bytes"))
	then
		echo "$test_desc - pass"
	else
		echo "$test_desc - fail $sysfs_atomic_unit_max_bytes - $scsi_atomic_max_bytes"
	fi

	test_desc="TEST 4 - check sysfs atomic_write_unit_min_bytes = scsi_debug atomic_wr_gran"
	if [ "$sysfs_atomic_unit_min_bytes" = "$scsi_atomic_min_bytes" ]
	then
		echo "$test_desc - pass"
	else
		echo "$test_desc - fail $sysfs_atomic_unit_min_bytes - $scsi_atomic_min_bytes"
	fi

	test_desc="TEST 5 - check statx stx_atomic_write_unit_min"
	statx_atomic_min=$(run_xfs_io_xstat "$TEST_DEV" "stat.atomic_write_unit_min")
	if [ "$statx_atomic_min" = "$scsi_atomic_min_bytes" ]
	then
		echo "$test_desc - pass"
	else
		echo "$test_desc - fail $statx_atomic_min - $scsi_atomic_min_bytes"
	fi

	test_desc="TEST 6 - check statx stx_atomic_write_unit_max"
	statx_atomic_max=$(run_xfs_io_xstat "$TEST_DEV" "stat.atomic_write_unit_max")
	if [ "$statx_atomic_max" = "$sysfs_atomic_unit_max_bytes" ]
	then
		echo "$test_desc - pass"
	else
		echo "$test_desc - fail $statx_atomic_max - $sysfs_atomic_unit_max_bytes"
	fi

	test_desc="TEST 7 - perform a pwritev2 with size of sysfs_atomic_unit_max_bytes with "
	test_desc+="RWF_ATOMIC flag - pwritev2 should be succesful"
	bytes_written=$(run_xfs_io_pwritev2_atomic "$TEST_DEV" "$sysfs_atomic_unit_max_bytes")
	if [ "$bytes_written" = "$sysfs_atomic_unit_max_bytes" ]
	then
		echo "$test_desc - pass"
	else
		echo "$test_desc - fail $bytes_written - $sysfs_atomic_unit_max_bytes"
	fi

	test_desc="TEST 8 - perform a pwritev2 with size of sysfs_atomic_unit_max_bytes + 512 "
	test_desc+="bytes with RWF_ATOMIC flag - pwritev2 should not be succesful"
	bytes_written=$(run_xfs_io_pwritev2_atomic "$TEST_DEV" "$bytes_to_write")
	if [ "$bytes_written" = "" ]
	then
		echo "$test_desc - pass"
	else
		echo "$test_desc - fail $bytes_written - $bytes_to_write"
	fi

	test_desc="TEST 9 - perform a pwritev2 with size of sysfs_atomic_unit_min_bytes "
	test_desc+="with RWF_ATOMIC flag - pwritev2 should be succesful"
	bytes_written=$(run_xfs_io_pwritev2_atomic "$TEST_DEV" "$sysfs_atomic_unit_min_bytes")
	if [ "$bytes_written" = "$sysfs_atomic_unit_min_bytes" ]
	then
		echo "$test_desc - pass"
	else
		echo "$test_desc - fail $bytes_written - $scsi_atomic_min_bytes"
	fi

	bytes_to_write=$(( "${sysfs_atomic_unit_min_bytes}" - "${sysfs_logical_block_size}" ))
	test_desc="TEST 10 - perform a pwritev2 with a size of sysfs_atomic_unit_min_bytes - 512 "
	test_desc+="bytes with RWF_ATOMIC flag - pwritev2 should fail"
	if [ "$bytes_to_write" = 0 ]
	then
		echo "$test_desc - pass"
	else
		bytes_written=$(run_xfs_io_pwritev2_atomic "$TEST_DEV" "$bytes_to_write")
		if [ "$bytes_written" = "" ]
		then
			echo "$test_desc - pass"
		else
			echo "$test_desc - fail $bytes_written - $bytes_to_write"
		fi
	fi

	_exit_scsi_debug

	echo "Test complete"
}
