#! /bin/bash
#
# Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# This software product is a proprietary product of Mellanox Technologies Ltd.
# (the "Company") and all right, title, and interest in and to the software
# product, including all associated intellectual property rights, are and
# shall remain exclusively with the Company.
#
# This software product is governed by the End User License Agreement
# provided with the software product.
#

# Helper functions for JSON manipulation using built-in Linux commands only

# Read a JSON field value
json_get() {
	local file="$1"
	local field="$2"
	local default="$3"

	if [[ ! -f "$file" ]]; then
		echo "${default:-null}"
		return
	fi

	# Use grep/sed/awk to extract JSON field value
	local value=$(grep -o "\"${field}\"[[:space:]]*:[[:space:]]*[^,}]*" "$file" 2>/dev/null | \
		     sed 's/.*:[[:space:]]*//' | \
		     sed 's/^"\(.*\)"$/\1/' | \
		     sed 's/[[:space:]]*$//')

	if [[ -z "$value" ]]; then
		echo "${default:-null}"
	else
		echo "$value"
	fi
}

# Set a JSON field value
json_set() {
	local file="$1"
	local field="$2"
	local value="$3"

	if [[ -f "$file" ]]; then
		# Check if field already exists
		if grep -q "\"${field}\"[[:space:]]*:" "$file"; then
			# Update existing field
			sed -i "s/\"${field}\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"${field}\": \"${value}\"/g" "$file"
		else
			# Add new field - insert before the final closing brace with proper formatting
			# Remove any trailing whitespace and ensure proper line ending
			sed -i 's/[[:space:]]*$//' "$file"

			# Check if this is a single-line JSON or multi-line
			# Handle case where file has no trailing newline (wc -l returns 0)
			local line_count=$(wc -l < "$file")
			if [[ $line_count -le 1 ]]; then
				# Single line JSON (including files with no trailing newline): add comma and field
				sed -i 's/}[[:space:]]*$/,\n  "'"${field}"'": "'"${value}"'"\n}/' "$file"
			else
				# Multi-line JSON: add comma and field with proper indentation
				sed -i '$s/}[[:space:]]*$/,\n  "'"${field}"'": "'"${value}"'"\n}/' "$file"
			fi
			# Clean up if it was an empty object
			sed -i 's/{,/{/g' "$file"
		fi
	else
		# Create new file
		echo "{\"${field}\": \"${value}\"}" > "$file"
	fi
}

mftconfig=mstconfig
if [ -x /usr/bin/mlxconfig ]; then
	mftconfig=mlxconfig
fi

devlink=devlink
if [ -x /opt/mellanox/iproute2/sbin/devlink ]; then
	devlink=/opt/mellanox/iproute2/sbin/devlink
fi

get_eswitch_mode()
{
	pci_dev=$1
	shift

	$devlink dev eswitch show pci/${pci_dev} 2> /dev/null | cut -d ' ' -f 3
}

pci=`lspci -nD -d 15b3: | grep 'a2d[26c]\|101d\|1021' | head -n 1`
emu_manager=`echo $pci | cut -d ' ' -f 1`

if [[ -f /opt/mellanox/mlnx_virtnet/virtnet.conf ]]; then
	_emu_manager=$(json_get "/opt/mellanox/mlnx_virtnet/virtnet.conf" "ib_dev_p0" "")
	if [[ ! -z $_emu_manager ]] && [[ $_emu_manager != "null" ]]; then
		# Check if the infiniband device path exists
		if [[ -L "/sys/class/infiniband/$_emu_manager/device" ]]; then
			device_path=$(readlink "/sys/class/infiniband/$_emu_manager/device")
			if [[ ! -z "$device_path" ]]; then
				emu_manager=`basename "$device_path"`
			fi
		fi
	fi
fi

if [[ -z $emu_manager ]]; then
	echo "Error: no valid emulation manager is defined"
	exit 1
fi

is_emu_per_pf=$($mftconfig -d ${emu_manager} -e q VIRTIO_NET_EMU_MNG_ENABLE |
	grep -o "VIRTIO_NET_EMU_MNG_ENABLE.*" | awk '{print $3}' | grep True)
if [[ -z $is_emu_per_pf ]]; then
	is_emu_global=$($mftconfig -d ${emu_manager} -e q VIRTIO_NET_EMULATION_ENABLE |
		grep -o "VIRTIO_NET_EMULATION_ENABLE.*" | awk '{print $3}' | grep True)
	if [[ -z $is_emu_global ]]; then
		echo "${emu_manager} doesn't have VIRTIO_NET_EMULATION_ENABLE or VIRTIO_NET_EMU_MNG_ENABLE set"
		exit 250
	fi
fi

eswitch_mode="get_eswitch_mode ${emu_manager}"
COUNT=0
until [ $COUNT -eq 120 ] || [ "`${eswitch_mode}`" == "switchdev" ]; do
	sleep 1
	echo "Waiting for device ${emu_manager} to be switchdev mode for ${COUNT} seconds"
	let $(( COUNT++ ))
done

if [ "`${eswitch_mode}`" != "switchdev" ]; then
	echo "${emu_manager} is not in switch dev mode"
	exit 2
fi

config_file="/opt/mellanox/mlnx_virtnet/virtnet.conf"
p2=`$mftconfig -d ${emu_manager} -e q LINK_TYPE_P2`
if [[ -n `echo "$p2" 2> /dev/null | grep "Unknown Parameter"` ]]; then
	echo "Single port NIC"
	json_set "$config_file" "single_port" "1"
fi

if [[ -z `modinfo mlxdevm 2>&1 | grep -E "ERROR.*mlxdevm not found"` ]]; then
	if [[ -f $config_file ]]; then
		current_sf_provider=$(json_get "$config_file" "sf_provider" "")
		if [[ "$current_sf_provider" != "devlink" ]]; then
			echo "mlxdevm as SF provider"
			json_set "$config_file" "sf_provider" "mlxdevm"
		else
			echo "mlxdevm available, keeping existing devlink SF provider"
		fi
	fi
else
	echo "devlink as SF provider"
	json_set "$config_file" "sf_provider" "devlink"
fi

bf3=`echo $pci | grep 'a2dc\|1021'`

if [[ -n $bf3 ]]; then
	if [[ ! -e /opt/mellanox/mlnx_virtnet/providers/dpa.provider ]]; then
		echo "Generating default dpa.provider for BlueField-3/ConnectX-7"
		echo -e "Provider=libprovider-dpa\nScore=200" > \
		      /opt/mellanox/mlnx_virtnet/providers/dpa.provider
	fi
fi

recovery_dir="/opt/mellanox/mlnx_virtnet/recovery"

is_lag_cur=0
if [[ -f $config_file ]]; then
	is_lag_cur=$(json_get "$config_file" "is_lag" "0")
fi

mkdir -p ${recovery_dir}
if [[ -f "${recovery_dir}/.virtnet_conf_save" ]]; then
	is_lag_save=$(json_get "${recovery_dir}/.virtnet_conf_save" "is_lag" "0")
	if [[ "$is_lag_save" != "$is_lag_cur" ]]; then
		cd $recovery_dir
		find . -type f -not \( -name ".virtnet_conf_save" -or -name ".mlxconfig_save" \) -delete
	fi
fi

if [[ -f $config_file ]]; then
	cp "$config_file" "${recovery_dir}/.virtnet_conf_save"
else
	echo '{"is_lag": 0}' > "${recovery_dir}/.virtnet_conf_save"
fi

curr_map_count=`cat /proc/sys/vm/max_map_count`

#Define map threshold value to 131060 (65530 * 2) to support upto 2K devices
max_map_threshold=131060

if [[ "$curr_map_count" -lt "$max_map_threshold" ]]; then
	echo $max_map_threshold > /proc/sys/vm/max_map_count
fi

fields=(PF_BAR2_ENABLE \
	HIDE_PORT2_PF \
	PER_PF_NUM_SF \
	SRIOV_EN \
	PF_SF_BAR_SIZE \
	PF_TOTAL_SF)

fields_emu_per_pf=(VIRTIO_NET_EMU_MNG_ENABLE \
	PCI_SWITCH_EMU_MNG_ENABLE \
	PCI_SWITCH_EMU_MNG_NUM_PORT \
	VIRTIO_NET_EMU_MNG_NUM_VF \
	VIRTIO_NET_EMU_MNG_NUM_PF \
	VIRTIO_NET_EMU_MNG_NUM_MSIX)

fields_emu_global=(VIRTIO_NET_EMULATION_ENABLE \
	PCI_SWITCH_EMULATION_ENABLE \
	PCI_SWITCH_EMULATION_NUM_PORT \
	VIRTIO_NET_EMULATION_NUM_VF \
	VIRTIO_NET_EMULATION_NUM_PF \
	VIRTIO_NET_EMULATION_NUM_MSIX)

if [[ -z $is_emu_per_pf ]]; then
	fields=("${fields[@]}" "${fields_emu_global[@]}")
else
	fields=("${fields[@]}" "${fields_emu_per_pf[@]}")
fi
configs=$($mftconfig -d ${emu_manager} -e q "${fields[@]}")

fields_str=""
for f in ${fields[@]};
do
	if [[ $fields_str != "" ]]; then
		fields_str="${fields_str}|${f}"
	else
		fields_str="${f}"
	fi
done

mlxconfig_cur=`echo "$configs" | grep -E $fields_str | tr -s ' '`

if [[ ! -f "${recovery_dir}/.mlxconfig_save" ]]; then
	mkdir -p ${recovery_dir}
	echo "$mlxconfig_cur" > ${recovery_dir}/.mlxconfig_save
	exit 0
fi

mlxconfig_save=`cat ${recovery_dir}/.mlxconfig_save | grep -E $fields_str | tr -s ' '`

for f in ${fields[@]};
do
	val_save=`echo "$mlxconfig_save" | grep -o "$f.*" | awk '{print $3}'`
	val_cur=`echo "$mlxconfig_cur" | grep -o "$f.*" | awk '{print $3}'`
	if [[ "$val_save" != "$val_cur" ]]; then
		echo "$mlxconfig_cur" > ${recovery_dir}/.mlxconfig_save
		cd $recovery_dir
		find . -type f -not \( -name ".virtnet_conf_save" -or -name ".mlxconfig_save" \) -delete
		exit 0
	fi
done

exit 0
