#!/bin/bash
# SPDX-License-Identifier: MIT or GPL-2.0-only

. common/fio_common

echo -e "\ttest nosrv (state after ublk server is killed) and recovery behavior"
echo -e "\tfor all valid recovery options"
echo

DD_PID=0

# submit an I/O async and store pid into DD_PID
submit_io()
{
	dd if=$1 of=/dev/null iflag=direct count=1 bs=4k 2>/dev/null &
	DD_PID=$!
}

# check the status of the I/O issued by DD_PID
# 0 - I/O succeeded
# 1 - I/O error
# 2 - I/O queued
check_io_status()
{
	sleep 1
	# if process is still alive after 1 second, I/O is likely queued
	if ps -p $DD_PID > /dev/null 2>/dev/null; then
		return 2
	else
		if wait $DD_PID; then return 0; else return 1; fi
	fi
}

del_dev()
{
	sleep 2
	RES=`__remove_ublk_dev_return $1`
	if [ $RES -ne 0 ]; then
		echo -e "\t\tdelete $1 failed"
		return 1
	fi
	wait
	sleep 3
}

ublk_run_recovery_test()
{
	export T_TYPE_PARAMS="-t null -r $RECOVERY -i $RECOVERY_REISSUE -e $RECOVERY_FAIL_IO"
	echo -e "\trunning with params: $T_TYPE_PARAMS"
	DEV=`__create_ublk_dev`

	echo -e "\t\tcheck behavior before nosrv - expect no error"
	submit_io $DEV
	check_io_status
	RES=$?
	if [ $RES -ne 0 ]; then
		echo -e "\t\tI/O error while ublk server still up!"
		return 1
	fi

	pid1=`__ublk_get_pid $DEV`
	kill -9 $pid1
	sleep 2
	echo -ne "\t\tcheck behavior during nosrv - "
	submit_io $DEV
	check_io_status
	RES=$?
	if [ $RECOVERY_FAIL_IO -ne 0 ]; then
		echo "expect I/O error"
		if [ $RES -ne 1 ]; then
			echo -e "\t\tincorrect nosrv behavior!"
			echo -e "\t\texpected io error, got $RES"
			return 1
		fi
	elif [ $RECOVERY -ne 0 ]; then
		echo "expect I/O queued"
		if [ $RES -ne 2 ]; then
			echo -e "\t\tincorrect nosrv behavior!"
			echo -e "\t\texpected queued io, got $RES"
			return 1
		fi
	else
		echo "expect I/O error" # because device should be gone
		if [ $RES -ne 1 ]; then
			echo -e "\t\tincorrect nosrv behavior!"
			echo -e "\t\texpected io error, got $RES"
			return 1
		fi
	fi

	echo -e "\t\ttry to recover the device"
	secs=0
	while [ $secs -lt 10 ]; do
		RES=`__recover_ublk_dev $DEV`
		[ $RES -eq 0 ] && break
		sleep 1
		let secs++
	done
	if [ $RES -ne 0 ]; then
		echo -e "\t\tfailed to recover device!"
		if [ $RECOVERY -ne 0 ]; then
			return 1
		else
			echo -e "\t\tforgiving expected recovery failure"
			del_dev $DEV
			echo
			return 0
		fi
	else
		if [ $RECOVERY -eq 0 ]; then
			echo -e "\t\trecovery unexpectedly succeeded!"
			return 1
		fi
	fi

	# if I/O queued before, make sure it completes now
	if [ $RECOVERY_FAIL_IO -eq 0 ] && [ $RECOVERY -ne 0 ]; then
		echo -e "\t\tchecking that I/O completed after recovery"
		check_io_status
		RES=$?
		if [ $RES -ne 0 ]; then
			echo -e "\t\tpreviously queued I/O did not succeed!"
			echo -e "\t\texpected success got $RES"
			return 1
		fi
	fi

	echo -e "\t\tcheck behavior after recovery - expect no error"
	submit_io $DEV
	check_io_status
	RES=$?
	if [ $RES -ne 0 ]; then
		echo -e "\t\tI/O error after recovery!"
		return 1
	fi

	# cleanup
	pid2=`__ublk_get_pid $DEV`
	kill -9 $pid2
	del_dev $DEV

	echo
}

RECOVERY=0
RECOVERY_REISSUE=0
RECOVERY_FAIL_IO=0
ublk_run_recovery_test

RECOVERY=1
RECOVERY_REISSUE=0
RECOVERY_FAIL_IO=0
ublk_run_recovery_test

RECOVERY=1
RECOVERY_REISSUE=1
RECOVERY_FAIL_IO=0
ublk_run_recovery_test

RECOVERY=1
RECOVERY_REISSUE=0
RECOVERY_FAIL_IO=1
ublk_run_recovery_test
