Add a test for read and write IOPS throttling. The test implementation has been generated by Antigravity and Gemini with a few manual edits. Signed-off-by: Bart Van Assche --- tests/throtl/008 | 152 +++++++++++++++++++++++++++++++++++++++++++ tests/throtl/008.out | 2 + 2 files changed, 154 insertions(+) create mode 100755 tests/throtl/008 create mode 100644 tests/throtl/008.out diff --git a/tests/throtl/008 b/tests/throtl/008 new file mode 100755 index 000000000000..0570fc0188e0 --- /dev/null +++ b/tests/throtl/008 @@ -0,0 +1,152 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-3.0+ +# Copyright (C) 2026 Google LLC +# +# Test cgroup iocost IOPS limiting. + +. tests/block/rc +. common/null_blk +. common/cgroup +. common/fio + +DESCRIPTION="test cgroup iocost controller limits" + +requires() { + _have_cgroup2_controller io + _have_null_blk + _have_fio + _have_program bc + if [[ ! -e "$(_cgroup2_base_dir)/io.cost.qos" ]]; then + SKIP_REASONS+=("iocost controller not supported (CONFIG_BLK_CGROUP_IOCOST)") + return 1 + fi +} + +test() { + echo "Running ${TEST_NAME}" + + # Create a null_blk instance + local null_blk_params=( + blocksize=4096 + completion_nsec=0 + memory_backed=0 + size=1024 # MB + submit_queues=1 + power=1 + ) + _init_null_blk nr_devices=0 queue_mode=2 && + if ! _configure_null_blk nullb0 "${null_blk_params[@]}"; then + echo "Configuring null_blk failed" + return 1 + fi + + local dev_t + dev_t=$( "$(_cgroup2_base_dir)/cgroup.subtree_control"; then + echo "Failed to enable io controller on cgroup root" + _exit_cgroup2 + _exit_null_blk + return 1 + fi + deactivate_io_ctrlr=true + fi + + if ! echo "+io" > "$CGROUP2_DIR/cgroup.subtree_control"; then + echo "Failed to enable io controller on $CGROUP2_DIR" + if [[ $deactivate_io_ctrlr == true ]]; then + echo "-io" > "$(_cgroup2_base_dir)/cgroup.subtree_control" + fi + _exit_cgroup2 + _exit_null_blk + return 1 + fi + + # Configure iocost controller globally for the device + # min=100.00 max=100.00 forces vrate to be fixed at 100% + if ! echo "$dev_t enable=1 min=100.00 max=100.00" > "$(_cgroup2_base_dir)/io.cost.qos"; then + echo "Failed to configure io.cost.qos" + if [[ $deactivate_io_ctrlr == true ]]; then + echo "-io" > "$(_cgroup2_base_dir)/cgroup.subtree_control" + fi + _exit_cgroup2 + _exit_null_blk + return 1 + fi + + # Limit read iops to 100 and write iops to 10. + # Setting rbps/wbps to 0 means they are ignored (costs calculated purely based on IOPS) + if ! echo "$dev_t ctrl=user model=linear rbps=0 rseqiops=100 rrandiops=100 wbps=0 wseqiops=10 wrandiops=10" > "$(_cgroup2_base_dir)/io.cost.model"; then + echo "Failed to configure io.cost.model" + echo "$dev_t enable=0" > "$(_cgroup2_base_dir)/io.cost.qos" + if [[ $deactivate_io_ctrlr == true ]]; then + echo "-io" > "$(_cgroup2_base_dir)/cgroup.subtree_control" + fi + _exit_cgroup2 + _exit_null_blk + return 1 + fi + + # Create a child cgroup for test + local cg_name="testgrp" + local cg_path="$CGROUP2_DIR/$cg_name" + mkdir "$cg_path" + + # 1. Run FIO read test + local -a FIO_PERF_FIELDS + FIO_PERF_FIELDS=("read iops") + if ! _fio_perf --bs=4k --rw=randread --name=read-test --filename=/dev/nullb0 \ + --ioengine=libaio --direct=1 --iodepth=64 --time_based --runtime=10 \ + --cgroup="blktests/$cg_name" > /dev/null 2>&1; then + echo "FIO read test failed" + else + local read_iops=${TEST_RUN["read iops"]} + if [[ -z $read_iops ]]; then + echo "Read IOPS is empty" + elif (( "$(echo "$read_iops < 90 || $read_iops > 110" | bc -l)" )); then + echo "Read IOPS $read_iops not in expected range [90, 110]" + fi + fi + + # 2. Run FIO write test + FIO_PERF_FIELDS=("write iops") + if ! _fio_perf --bs=4k --rw=randwrite --name=write-test --filename=/dev/nullb0 \ + --ioengine=libaio --direct=1 --iodepth=64 --time_based --runtime=10 \ + --cgroup="blktests/$cg_name" > /dev/null 2>&1; then + echo "FIO write test failed" + else + local write_iops=${TEST_RUN["write iops"]} + if [[ -z $write_iops ]]; then + echo "Write IOPS is empty" + elif (( "$(echo "$write_iops < 8 || $write_iops > 12" | bc -l)" )); then + echo "Write IOPS $write_iops not in expected range [8, 12]" + fi + fi + + # Clean up cgroups + rmdir "$cg_path" + if [[ $deactivate_io_ctrlr == true ]]; then + { echo "-io" > "$(_cgroup2_base_dir)/cgroup.subtree_control"; } &> "${FULL}" + fi + _exit_cgroup2 + + # Clean up null_blk + _exit_null_blk + + echo "Test complete" +} diff --git a/tests/throtl/008.out b/tests/throtl/008.out new file mode 100644 index 000000000000..890ff7f226d1 --- /dev/null +++ b/tests/throtl/008.out @@ -0,0 +1,2 @@ +Running throtl/008 +Test complete