#!/bin/bash
#
# $1 == iSCSI Channel to start/stop for each loop iteration
#
# $2 == iSCSI Logical Unit to perform domain validation tests upon.
#       Note that this storage must have rw access.
#
# Copyright (c) 2005, 2006  <nab@kernel.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version
# 2 of the License, or (at your option) any later version.
#

CHANNEL=$1
ISCSI_LUN=$2

usage () {
	echo "Usage: core-iscsi-dv.sh CHANNEL ISCSI_LUN"
	exit -1
}

if [ ! $CHANNEL ]; then
	usage	
fi

if [ ! $ISCSI_LUN ]; then
	usage
fi

CAN_QUEUE=128
CMD_LUN=64
SG_TABLE=32
ISCSI_CHANNELS=64
CONN_COUNT=1
IP=192.168.0.231
PORT=3260
TRANSPORT=0
SYSCONFIG="/etc/sysconfig/initiator"
SLEEP=1 # seconds
ISCSI_DV=1

if [ -f /etc/init.d/initiator ]; then
	INITD="/etc/init.d/initiator"
fi
if [ -f /etc/rc.d/init.d/initiator ]; then
	INITD="/etc/rc.d/init.d/initiator"
fi
if [ -f /sbin/initiator-ctl ]; then
	INITIATOR_CTL="/sbin/initiator-ctl"
else
	echo " /sbin/initiator-ctl does not exist!"
fi

$INITD status $CHANNEL > /dev/null
RET=$?

if [ $RET == 0 ]; then
	echo "*************************************************************************************************************************"
	echo "A iSCSI Nexus already exists on this channel.  Please bring the channel back to init status before using core-iscsi-dv.sh"
	echo " Stopping processing with ERROR"
	echo "*************************************************************************************************************************"
	exit -1
fi

dd_read() {

        echo "Reading 4k from iSCSI_LUN[$ISCSI_LUN].."
        dd if=$1 of=/dev/null bs=2048 count=2

	return 0
}

surface_test() {
	
	echo "Before surface_test"
	/usr/sbin/surface_test $1
	RET=$?
	echo "After surface_test"

	if [ $RET == 8 ]; then
		echo "surface_test failed";
		return -1
	fi
	if [ $RET == 2 ]; then
		echo "surface_test failed";
		return -1
	fi

	return 0
}

get_blockdev() {

	if [ -d /sys/class/iscsi_initiator_core ]; then
		if [ -d /sys/class/iscsi_initiator_core/channel_$CHANNEL ]; then
			DEV=`cat /sys/class/iscsi_initiator_core/channel_$CHANNEL/scsi | grep "$CHANNEL/0/$ISCSI_LUN " | cut -d ":" -f2`
			DEV=${DEV:1:4}
		fi
	else
		if [ -d /proc/iscsi_initiator/channel_$CHANNEL ]; then
			DEV=`cat /proc/iscsi_initiator/channel_$CHANNEL/scsi_info | grep "$CHANNEL/0/$ISCSI_LUN " | cut -d ":" -f2`
			DEV=${DEV:1:4}
		fi
	fi

	BLOCKDEV="/dev/$DEV"

	if [ ! $BLOCKDEV ]; then
		echo "Using iSCSI_CHANNEL[$CHANNEL] LUN[$ISCSI_LUN]: -> NO MAPPING"
		return -1
	else 
		echo "Using iSCSI_CHANNEL[$CHANNEL] LUN[$ISCSI_LUN]: -> $BLOCKDEV"
		return 0
	fi

}

param[0]="DefaultTime2Wait";
DefaultTime2Wait_value=0;
DefaultTime2Wait_diff=1
DefaultTime2Wait_end=65536;
DefaultTime2Wait_enable=0;
param[1]="DefaultTime2Retain";
DefaultTime2Retain_value=0;
DefaultTime2Retain_diff=1
DefaultTime2Retain_end=65536;
DefaultTime2Retain_enable=0;
param[2]="HeaderDigest";
HeaderDigest_value="CRC32C,None";
HeaderDigest_current=0
HeaderDigest_enable=1;
param[3]="DataDigest";
DataDigest_value="CRC32C,None";
DataDigest_current=0
DataDigest_enable=1;
param[4]="MaxRecvDataSegmentLength"
MaxRecvDataSegmentLength_value=512;
MaxRecvDataSegmentLength_diff=512;
MaxRecvDataSegmentLength_orig=512;
MaxRecvDataSegmentLength_end=262144;
MaxRecvDataSegmentLength_enable=1;
param[5]="ErrorRecoveryLevel"
ErrorRecoveryLevel_value=0;
ErrorRecoveryLevel_diff=1
ErrorRecoveryLevel_orig=0
ErrorRecoveryLevel_end=2
ErrorRecoveryLevel_enable=0
param[6]="IFMarker"
IFMarker_value="Yes"
IFMarker_enable=0 # TODO
param[7]="OFMarker"
OFMarker_value="Yes"
OFMarker_enable=0 # TODO
param[8]="IFMarkInt"
IFMarkInt_enable=0 # TODO
param[9]="OFMarkInt"
OFMarkInt_enable=0 # TODO 
param[10]="DataPDUInOrder"
DataPDUInOrder_value="Yes"
DataPDUInOrder_current=0
DataPDUInOrder_enable=0
param[11]="DataSequenceInOrder"
DataSequenceInOrder_value="Yes"
DataSequenceInOrder_current=0
DataSequenceInOrder_enable=0
param[12]="FirstBurstLength"
FirstBurstLength_value=512
FirstBurstLength_diff=512
FirstBurstLength_orig=512
FirstBurstLength_end=262144
FirstBurstLength_enable=0
param[13]="MaxBurstLength"
MaxBurstLength_value=512
MaxBurstLength_diff=512
MaxBurstLength_orig=512
MaxBurstLength_end=262144
MaxBurstLength_enable=0
param[14]="MaxOutstandingR2T"
MaxOutstandingR2T_value=1
MaxOutstandingR2T_diff=1
MaxOutstandingR2T_orig=1
MaxOutstandingR2T_end=32
MaxOutstandingR2T_enable=0
param[15]="ImmediateData"
ImmediateData_value="Yes"
ImmediateData_current=0
ImmediateData_enable=0
param[16]="InitialR2T"
InitialR2T_value="Yes"
InitialR2T_current=0
InitialR2T_enable=0

DONE=0

DefaultTime2Wait () {
	value[0]=$DefaultTime2Wait_value;
	
	if [ $1 == 0 ]; then
		return 0
	fi
	
	DefaultTime2Wait_value=$(($DefaultTime2Wait_value+$DefaultTime2Wait_diff))
	if [ $DefaultTime2Wait_value == $DefaultTime2Wait_end ]; then
		DONE=1
	fi
}

DefaultTime2Retain () {
	value[1]=$DefaultTime2Retain_value;
}

HeaderDigest () {
	value[2]=$HeaderDigest_value;

	if [ $1 == 0 ]; then
		return 0
	fi

	if [ $HeaderDigest_current == 1 ]; then	
		HeaderDigest_current=0
		HeaderDigest_value="CRC32C,None"
		return 1
	else
		HeaderDigest_current=1
		HeaderDigest_value="None"
		return 0
	fi

}

DataDigest () {
	value[3]=$DataDigest_value;

	if [ $1 == 0 ]; then
		return 0
	fi

	if [ $DataDigest_current == 1 ]; then
		DataDigest_current=0;
		DataDigest_value="CRC32C,None"
		return 1
	else
		DataDigest_current=1;
		DataDigest_value="None";
		return 0
	fi

}

MaxRecvDataSegmentLength () {
	value[4]=$MaxRecvDataSegmentLength_value;

	if [ $1 == 0 ]; then
		return 0
	fi
	
	VAL=$(($MaxRecvDataSegmentLength_value+$MaxRecvDataSegmentLength_diff))
	if [ $MaxRecvDataSegmentLength_value == $MaxRecvDataSegmentLength_end ]; then
		MaxRecvDataSegmentLength_value=$MaxRecvDataSegmentLength_orig
		return 1
	fi

	MaxRecvDataSegmentLength_value=$VAL
}

ErrorRecoveryLevel () {
	value[5]=$ErrorRecoveryLevel_value;

	if [ $1 == 0 ]; then
		return 0
	fi
	
	VAL=$(($ErrorRecoveryLevel_value+$ErrorRecoveryLevel_diff))
	if [ $ErrorRecoveryLevel_value == $ErrorRecoveryLevel_end ]; then
		ErrorRecoveryLevel_value=$ErrorRecoveryLevel_orig;
		return 1
	fi

	ErrorRecoveryLevel_value=$VAL
}

IFMarker () {
	value[6]=$IFMarker_value;
}

OFMarker () {
	value[7]=$OFMarker_value;
}

DataPDUInOrder () {
	value[10]=$DataPDUInOrder_value;

	if [ $1 == 0 ]; then
		return 0;
	fi

        if [ $DataPDUInOrder_current == 1 ]; then
                DataPDUInOrder_current=0;
                DataPDUInOrder_value="Yes"
                return 1
	else
                DataPDUInOrder_current=1;
                DataPDUInOrder_value="No";
	        return 0
	fi
}

DataSequenceInOrder () {
	value[11]=$DataSequenceInOrder_value;

	if [ $1 == 0 ]; then
		return 0;
	fi

	if [ $DataSequenceInOrder_current == 1 ]; then
		DataSequenceInOrder_current=0;
		DataSequenceInOrder_value="Yes"
		return 1
	else
		DataSequenceInOrder_current=1
		DataSequenceInOrder_value="No"
		return 0
	fi
}

FirstBurstLength () {
	value[12]=$FirstBurstLength_value;

	if [ $1 == 0 ]; then
		return 0
	fi
	
	VAL=$(($FirstBurstLength_value+$FirstBurstLength_diff))
	if [ $FirstBurstLength_value == $FirstBurstLength_end ]; then
		FirstBurstLength_value=$FirstBurstLength_orig;
		return 1
	fi

	FirstBurstLength_value=$VAL
}

MaxBurstLength () {
	value[13]=$MaxBurstLength_value;

	if [ $1 == 0 ]; then
		return 0
	fi

	VAL=$(($MaxBurstLength_value+$MaxBurstLength_diff))
	if [ $MaxBurstLength_value == $MaxBurstLength_end ]; then
		MaxBurstLength_value=$MaxBurstLength_orig;
		return 1
	fi

	MaxBurstLength_value=$VAL
}

MaxOutstandingR2T () {
	value[14]=$MaxOutstandingR2T_value;

	if [ $1 == 0 ]; then
		return 0;
	fi

	VAL=$((MaxOutstandingR2T_value+$MaxOutstandingR2T_diff))
	if [ $MaxOutstandingR2T_value == $MaxOutstandingR2T_end ]; then
		MaxOutstandingR2T_value=$MaxOutstandingR2T_orig;
		return 1;
	fi

	MaxOutstandingR2T_value=$VAL
}

ImmediateData () {
	value[15]=$ImmediateData_value;

	if [ $1 == 0 ]; then
		return 0;
	fi

	if [ $ImmediateData_current == 1 ]; then
		ImmediateData_current=0;
		ImmediateData_value="Yes"
		return 1
	else
		ImmediateData_current=1
		ImmediateData_value="No"
		return 0
	fi
}

InitialR2T () {
	value[16]=$InitialR2T_value;

	if [ $1 == 0 ]; then
		return 0;
	fi

	if [ $InitialR2T_current == 1 ]; then
		InitialR2T_current=0;
		InitialR2T_value="Yes"
		return 1
	else
		InitialR2T_current=1
		InitialR2T_value="No"
		return 0
	fi
}

TEST_COUNT=0

determine_test_count () {
	if [ $MaxRecvDataSegmentLength_enable == 1 ]; then
		TMP=$(($MaxRecvDataSegmentLength_end/$MaxRecvDataSegmentLength_diff));
		TEST_COUNT=$(($TEST_COUNT+$TMP))
	fi

	if [ $FirstBurstLength_enable == 1 ]; then
		TMP=$(($FirstBurstLength_end/$FirstBurstLength_diff));
		TEST_COUNT=$(($TEST_COUNT*$TMP))
	fi

	if [ $MaxBurstLength_enable == 1 ]; then
		TMP=$(($MaxBurstLength_end/$MaxBurstLength_diff));
		TEST_COUNT=$(($TEST_COUNT*$TMP))
	fi

	if [ $MaxOutstandingR2T_enable == 1 ]; then
		TMP=$(($MaxOutstandingR2T_end/$MaxOutstandingR2T_diff));
		TEST_COUNT=$(($TEST_COUNT*$TMP))
	fi

	if [ $ImmediateData_enable == 1 ]; then
		TEST_COUNT=$(($TEST_COUNT*2))
	fi

	if [ $InitialR2T_enable == 1 ]; then
		TEST_COUNT=$(($TEST_COUNT*2))
	fi

	if [ $DataPDUInOrder_enable == 1 ]; then
		TEST_COUNT=$(($TEST_COUNT*2))
	fi

	if [ $DataSequenceInOrder_enable == 1 ]; then
		TEST_COUNT=$(($TEST_COUNT*2))
	fi

	if [ $HeaderDigest_enable == 1 ]; then
		TEST_COUNT=$(($TEST_COUNT*2))
	fi

	if [ $DataDigest_enable == 1 ]; then
		TEST_COUNT=$(($TEST_COUNT*2))
	fi

	if [ $ErrorRecoveryLevel_enable == 1 ]; then
		TEST_COUNT=$(($TEST_COUNT*3))
	fi
}

determine_test_count;
echo "Test_count: $TEST_COUNT possible validation vectors.."

if [ -f /etc/sysconfig/initiator ]; then
	echo "*************************************************************************************************************************"
	echo "An existing /etc/sysconfig/initiator configuration file exists.  Please backup this file and delete before proceeding."
	echo "*************************************************************************************************************************"
	exit -1
fi
for ((i = 0; i < $TEST_COUNT; i++)) 
do
	if [ $MaxRecvDataSegmentLength_enable == 1 ]; then
		MaxRecvDataSegmentLength 1;
		RET=$?
		PARAMS=$PARAMS"${param[4]}=${value[4]};";
	fi

	if [ $FirstBurstLength_enable == 1 ]; then
		FirstBurstLength 0;
		PARAMS=$PARAMS"${param[12]}=${value[12]};";
		if [ $RET == 1 ]; then
			FirstBurstLength 1;
			RET=$?
		fi
	fi

	if [ $MaxBurstLength_enable == 1 ]; then
		MaxBurstLength 0;
		PARAMS=$PARAMS"${param[13]}=${value[13]};";
		if [ $RET == 1 ]; then
			MaxBurstLength 1;
			RET=$?
		fi
	fi

	if [ $MaxOutstandingR2T_enable == 1 ]; then
		MaxOutstandingR2T 0
		PARAMS=$PARAMS"${param[14]}=${value[14]};";
		if [ $RET == 1 ]; then
			MaxOutstandingR2T 1;
			RET=$?
		fi
	fi

	if [ $ImmediateData_enable == 1 ]; then
		ImmediateData 0;
		PARAMS=$PARAMS"${param[15]}=${value[15]};";
		if [ $RET == 1 ]; then
			ImmediateData 1;
			RET=$?
		fi
	fi

	if [ $InitialR2T_enable == 1 ]; then
		InitialR2T 0;
		PARAMS=$PARAMS"${param[16]}=${value[16]};";
		if [ $RET == 1 ]; then
			 InitialR2T 1;
			 RET=$?
		fi
	fi

	if [ $DataPDUInOrder_enable == 1 ]; then
		DataPDUInOrder 0;
		PARAMS=$PARAMS"${param[10]}=${value[10]};";
		if [ $RET == 1 ]; then
			DataPDUInOrder 1;
			RET=$?
		fi
	fi

	if [ $DataSequenceInOrder_enable == 1 ]; then
		DataSequenceInOrder 0;
		PARAMS=$PARAMS"${param[11]}=${value[11]};";
		if [ $RET == 1 ]; then
			DataSequenceInOrder 1;
			RET=$?
		fi
	fi

	if [ $HeaderDigest_enable == 1 ]; then
		HeaderDigest 0;
		PARAMS=$PARAMS"${param[2]}=${value[2]};";
		if [ $RET == 1 ]; then
			HeaderDigest 1
			RET=$?
		fi
	fi

	if [ $DataDigest_enable == 1 ]; then
		DataDigest 0;
		PARAMS=$PARAMS"${param[3]}=${value[3]};";
		if [ $RET == 1 ]; then
			DataDigest 1
			RET=$?
		fi
	fi

	if [ $ErrorRecoveryLevel_enable == 1 ]; then
		ErrorRecoveryLevel 0;
		PARAMS=$PARAMS"${param[5]}=${value[5]};";
		if [ $RET == 1 ]; then
			ErrorRecoveryLevel 1;
			RET=$?
		fi
	fi
		
	echo "ISCSI_DV=1" > $SYSCONFIG
	echo "CAN_QUEUE=$CAN_QUEUE" >> $SYSCONFIG
	echo "CMD_LUN=$CMD_LUN" >> $SYSCONFIG
	echo "SG_TABLE=$SG_TABLE" >> $SYSCONFIG
	echo "ISCSI_CHANNELS=$ISCSI_CHANNELS" >> $SYSCONFIG

	echo "CHANNEL=\"$CHANNEL $CONN_COUNT NULL $IP $PORT $TRANSPORT $PARAMS\""
	echo "CHANNEL=\"$CHANNEL $CONN_COUNT NULL $IP $PORT $TRANSPORT $PARAMS\"" >> $SYSCONFIG

	PARAMS=""

	$INITD start $CHANNEL
	RET=$?
	if [ $RET != 0 ]; then
		echo "initiator start failed for iSCSI Channel: $CHANNEL"
		exit 1
	fi

	get_blockdev
	RET=$?
	if [ $RET != 0 ]; then
		exit 1
	fi
	
	# 
	# One or more tests can be added here that use $BLOCKDEV
	# as an arguement.
	#
	
#	dd_read $BLOCKDEV
	surface_test $BLOCKDEV
	
	sleep $SLEEP

	$INITD stop $CHANNEL
	RET=$?
	if [ $RET != 0 ]; then
		echo "initiator stop failed for iSCSI Channel: $CHANNEL"	
		exit 1
	fi

	if [ $DONE == 1 ]; then
		echo "Finished test";
		exit 0
	fi

done

