#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright 2020 Google LLC
#
# Trigger the blk_mq_realloc_hw_ctxs() error path. Regression test for commit
# d0930bb8f46b ("blk-mq: Fix a recently introduced regression in
# blk_mq_realloc_hw_ctxs()").

. tests/block/rc
. common/null_blk

DESCRIPTION="trigger the blk_mq_realloc_hw_ctxs() error path"
QUICK=1

requires() {
	_have_null_blk && _have_module_param null_blk init_hctx
}

test() {
	local faultb=faultb0
	local i sq

	: "${TIMEOUT:=30}"
	# Set <space> to $(nproc) + 1 to make loading of null_blk succeed.
	if ! _configure_null_blk "$faultb" completion_nsec=0 blocksize=512 size=16\
	     submit_queues="$(nproc)" memory_backed=1 \
	     init_hctx_fault_inject/interval="$(nproc)" \
	     init_hctx_fault_inject/probability=100 \
	     init_hctx_fault_inject/space="$(($(nproc) + 1))" \
	     init_hctx_fault_inject/times=-1 \
	     init_hctx_fault_inject/verbose=0 power=1 \
	     > /dev/null 2>&1; then
		rmdir /sys/kernel/config/nullb/"$faultb"
		if _module_file_exists null_blk; then
			faultb=nullb0

			# Fall back to set up with module parameter. The format
			# is "<interval>,<probability>,<space>,<times>"
			if ! _init_null_blk nr_devices=0 \
			    "init_hctx=$(nproc),100,$(($(nproc) + 1)),-1"; then
				echo "Loading null_blk failed"
				return 1
			fi
			if ! _configure_null_blk "$faultb" completion_nsec=0 blocksize=512 size=16\
			    submit_queues="$(nproc)" memory_backed=1 power=1; then
				echo "Configuring null_blk failed"
				return 1
			fi
		else
			SKIP_REASONS+=("requires fault injection via configfs or modular null_blk")
			return 1
		fi
	fi
	# Since older null_blk versions do not allow "submit_queues" to be
	# modified, check first whether that configs attribute is writeable.
	# Each iteration of the loop below triggers $(nproc) + 1
	# null_init_hctx() calls. Since <interval>=$(nproc), all possible
	# blk_mq_realloc_hw_ctxs() error paths will be triggered. Whether or
	# not this test succeeds depends on whether or not _check_dmesg()
	# detects a kernel warning.
	sq=/sys/kernel/config/nullb/"$faultb"/submit_queues
	if { echo "$(<"$sq")" >$sq; } 2>/dev/null; then
		for ((i = 0; i < 100; i++)); do
			echo 1 > $sq
			{ nproc > $sq; } >>"$FULL" 2>&1
		done
	else
		SKIP_REASONS+=("Skipping test because $sq cannot be modified")
	fi
	rmdir /sys/kernel/config/nullb/"$faultb"
	_exit_null_blk
	echo Passed
}
