#!/bin/bash

scriptdir="$(cd "$(dirname "$0")" && pwd)"

function usage()
{
	echo "Usage: $(basename $0) [options]"
	echo "Options:"
	echo "    -h | --help      : Display this help message and exit"
	echo "    -v               : Be verbose (display final config)"
	echo "    -cap <size (GB)> : set device capacity (default: 8)"
	echo "                       For zoned devices, capacity is determined"
	echo "                       with zone size and total number of zones"
	echo "    -bs <size (B)>   : set sector size (default: 512)"
	echo "    -m               : enable memory backing (default: false)"
	echo "    -z               : create a zoned device (default: false)"
	echo "    -qm <mode>       : set queue mode (default: 2)"
	echo "                       0=bio, 1=rq, 2=multiqueue"
	echo "    -sq <num>        : set number of submission queues"
	echo "                       (default: nproc)"
	echo "    -qd <depth>      : set queue depth (default: 64)"
	echo "    -im <mode>       : set IRQ mode (default: 0)"
	echo "                       0=none, 1=softirq, 2=timer"
	echo "    -c <nsecs>       : set completion time for timer completion"
	echo "                       (default: 10000 ns)"
	echo "Options for zoned devices:"
	echo "    -zs <size (MB)>  : set zone size (default: 8 MB)"
	echo "    -zc <size (MB)>  : set zone capacity (default: zone size)"
	echo "    -znc <num>       : set number of conv zones (default: 0)"
	echo "    -zns <num>       : set number of swr zones (default: 8)"
	echo "    -zr              : add a smaller runt swr zone (default: none)"
	echo "    -zmo <num>       : set max open zones (default: no limit)"
	echo "    -zma <num>       : set max active zones (default: no limit)"

	exit 0
}

function get_nullb_id()
{
	local nid=0

	while [ 1 ]; do
		if [ ! -b "/dev/nullb${nid}" ]; then
			break
		fi
		nid=$(( nid + 1 ))
	done

	echo "$nid"
}

# Set config defaults
cap=8
bs=512
m=0
qm=2
sq=$(nproc)
qd=64
im=0
c=10000

z=0
zs=8
zc=0
znc=0
zns=8
zr=0
zmo=0
zma=0

v=0

# Parse command line
while [[ $# -gt 0 ]]; do
	case "$1" in
	"-h" | "--help")
		usage "$0" ;;
	"-v")
		v=1 ;;
	"-cap")
		shift; cap=$1 ;;
	"-bs")
		shift; bs=$1 ;;
	"-m")
		m=1 ;;
	"-qm")
		shift; qm=$1 ;;
	"-sq")
		shift; sq=$1 ;;
	"-qd")
		shift; qd=$1 ;;
	"-im")
		shift; im=$1 ;;
	"-c")
		shift; c=$1 ;;
	"-z")
		z=1 ;;
	"-zs")
		shift; zs=$1 ;;
	"-zc")
		shift; zc=$1 ;;
	"-znc")
		shift; znc=$1 ;;
	"-zns")
		shift; zns=$1 ;;
	"-zr")
		zr=1 ;;
	"-zmo")
		shift; zmo=$1 ;;
	"-zma")
		shift; zma=$1 ;;
	*)
		echo "Invalid option \"$1\" (use -h option for help)"
		exit 1 ;;
	esac

	shift
done

# Calculate zoned device capacity
if [ $z == 1 ]; then
	cap=$(( zs * (znc + zns) ))
	if [ $zr == 1 ]; then
		cap=$(( gb + zns - 1 ))
	fi
else
	cap=$(( cap * 1024 ))
fi

# Create device config
modprobe null_blk nr_devices=0 || return $?
nid=$(get_nullb_id)
dev="/sys/kernel/config/nullb/nullb${nid}"
mkdir "${dev}"

echo $cap > "${dev}"/size
echo $bs > "${dev}"/blocksize
echo $m > "${dev}"/memory_backed
echo $qm > "${dev}"/queue_mode
echo $sq > "${dev}"/submit_queues
echo $qd > "${dev}"/hw_queue_depth
echo $im > "${dev}"/irqmode
if [ $im == 2 ]; then
	echo $c > "${dev}"/completion_nsec
fi

echo $z > "${dev}"/zoned
if [ $z == 1 ]; then
	echo $zs > "${dev}"/zone_size
	echo $zc > "${dev}"/zone_capacity
	echo $znc > "${dev}"/zone_nr_conv
	echo $zmo > "${dev}"/zone_max_open
	echo $zma > "${dev}"/zone_max_active
fi

# Enable device
echo 1 > "${dev}"/power
echo "Created /dev/nullb${nid}"

if [ $v == 1 ]; then
	echo "Device configuration:"
	grep -r . ${dev}
fi
