So far we are missing tests for bcache. Besides a relative simple setup/teardown tests add also the corresponding infrastructure. More tests are to be expected to depend on this. _create_bcache/_remove_bcache are tracking the resources and if anything is missing it will complain. Signed-off-by: Daniel Wagner --- tests/bcache/001 | 32 +++++++ tests/bcache/001.out | 1 + tests/bcache/rc | 259 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 292 insertions(+) diff --git a/tests/bcache/001 b/tests/bcache/001 new file mode 100644 index 000000000000..4a6e01113b6b --- /dev/null +++ b/tests/bcache/001 @@ -0,0 +1,32 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2026 Daniel Wagner, SUSE Labs +# +# Test bcache setup and teardown + +. tests/bcache/rc + +DESCRIPTION="test bcache setup and teardown" + +requires() { + _bcache_requires +} + +test_device_array() { + echo "Running ${TEST_NAME}" + + if [[ ${#TEST_DEV_ARRAY[@]} -lt 3 ]]; then + SKIP_REASONS+=("requires at least 3 devices") + return 1 + fi + + _create_bcache \ + --cache "${TEST_DEV_ARRAY[0]##*/}" \ + --bdev "${TEST_DEV_ARRAY[1]##*/}" + _remove_bcache + + _create_bcache \ + --cache "${TEST_DEV_ARRAY[0]##*/}" \ + --bdev "${TEST_DEV_ARRAY[1]##*/}" "${TEST_DEV_ARRAY[2]##*/}" + _remove_bcache +} diff --git a/tests/bcache/001.out b/tests/bcache/001.out new file mode 100644 index 000000000000..f890aed2736c --- /dev/null +++ b/tests/bcache/001.out @@ -0,0 +1 @@ +Running bcache/001 diff --git a/tests/bcache/rc b/tests/bcache/rc new file mode 100644 index 000000000000..3dba6c85b1ee --- /dev/null +++ b/tests/bcache/rc @@ -0,0 +1,259 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2026 Daniel Wagner, SUSE Labs + +. common/rc + +declare -a BCACHE_DEVS=() +declare -a BCACHE_BDEVS=() +declare -a BCACHE_CSETS=() + +BCACHE_MAX_RETRIES=5 + +_bcache_requires() { + _have_kernel_options MD BCACHE BCACHE_DEBUG AUTOFS_FS + _have_program make-bcache + _have_crypto_algorithm crc32c +} + +_bcache_wipe_devs() { + for dev in "${BCACHE_DEVS[@]}"; do + # Attempt a clean wipe first + if wipefs --all --quiet "${dev}" 2>/dev/null; then + continue + fi + + # Overwrite the first 10MB to clear stubborn partition tables or metadata + if ! dd if=/dev/zero of="${dev}" bs=1M count=10 conv=notrunc status=none; then + echo "Error: dd failed on ${dev}" >&2 + fi + + # Try wiping again after clearing the headers + if ! wipefs --all --quiet --force "${dev}"; then + echo "Warning: Failed to wipe ${dev} even after dd." >&2 + fi + done +} + +_bcache_register() { + if [[ ! -w /sys/fs/bcache/register ]]; then + echo "ERROR: bcache registration interface not found." >&2 + return 1 + fi + + for dev in "${BCACHE_DEVS[@]}"; do + if ! echo "${dev}" > /sys/fs/bcache/register 2>/tmp/bcache_err; then + err_msg=$(< /tmp/bcache_err) + + if [[ "${err_msg}" != *"Device or resource busy"* ]]; then + echo "ERROR: Failed to register ${dev}: ${err_msg:-"Unknown error"}" >&2 + fi + fi + done +} + +_create_bcache() { + local -a cdevs=() + local -a bdevs=() + local -a ARGS=() + local bucket_size="64k" + local block_size="4k" + + _register_test_cleanup _cleanup_bcache + + while [[ $# -gt 0 ]]; do + case $1 in + --cache) + shift + # Collect arguments until the next flag or end of input + while [[ $# -gt 0 && ! $1 =~ ^-- ]]; do + cdevs+=("$1") + shift + done + ;; + --bdev) + shift + # Collect arguments until the next flag or end of input + while [[ $# -gt 0 && ! $1 =~ ^-- ]]; do + bdevs+=("$1") + shift + done + ;; + --bucket-size) + bucket_size="$2" + shift 2 + ;; + --block-size) + block_size="$2" + shift 2 + ;; + --writeback) + ARGS+=(--writeback) + shift 1 + ;; + --discard) + ARGS+=(--discard) + shift 1 + ;; + *) + echo "WARNING: unknown argument: $1" + shift + ;; + esac + done + + # add /dev prefix to device names + cdevs=( "${cdevs[@]/#/\/dev\/}" ) + bdevs=( "${bdevs[@]/#/\/dev\/}" ) + + # make-bcache expects empty/cleared devices + BCACHE_DEVS+=("${cdevs[@]}" "${bdevs[@]}") + _bcache_wipe_devs + + local -a cdevs_args=() + for dev in "${cdevs[@]}"; do + cdevs_args+=("--cache" "${dev}") + done + + local -a bdevs_args=() + for dev in "${bdevs[@]}"; do + bdevs_args+=("--bdev" "${dev}") + done + + local output cmd + cmd=(make-bcache \ + --wipe-bcache \ + --bucket "${bucket_size}" \ + --block "${block_size}" \ + "${cdevs_args[@]}" \ + "${bdevs_args[@]}" \ + "${ARGS[@]}") + + output=$("${cmd[@]}" 2>&1) + local rc=$? + if [[ "${rc}" -ne 0 ]]; then + echo "ERROR: make-bcache failed:" >&2 + echo "$output" >&2 + return 1 + fi + + local cset_uuid + cset_uuid=$(echo "$output" | awk '/Set UUID:/ {print $3}' | head -n 1) + if [[ -z "${cset_uuid}" ]]; then + echo "ERROR: Could not extract cset UUID from make-bcache output" >&2 + return 1 + fi + BCACHE_CSETS+=("${cset_uuid}") + + local -a bdev_uuids + mapfile -t bdev_uuids < <(echo "$output" | awk ' + $1 == "UUID:" { last_uuid = $2 } + $1 == "version:" && $2 == "1" { print last_uuid} + ') + + udevadm settle + + _bcache_register + + for uuid in "${bdev_uuids[@]}"; do + local link found attempt + + link=/dev/bcache/by-uuid/"${uuid}" + found=false + attempt=0 + + while (( attempt < BCACHE_MAX_RETRIES )); do + if [[ -L "$link" ]]; then + BCACHE_BDEVS+=("${uuid}") + found=true + break + fi + + (( attempt++ )) + sleep 1 + done + + if [[ "$found" == "false" ]]; then + echo "WARNING: Could not find device node for UUID ${uuid} after ${BCACHE_MAX_RETRIES}s" >&2 + fi + done +} + +_remove_bcache() { + local uuid + + for uuid in "${BCACHE_BDEVS[@]}"; do + local dev_path + + dev_path=$(blkid -U "${uuid}") + if [ -n "$dev_path" ]; then + local dev_name + + dev_name="${dev_path##*/}" + if [ -f "/sys/block/${dev_name}/bcache/stop" ] ; then + echo 1 > "/sys/block/${dev_name}/bcache/stop" + fi + fi + done + + for uuid in "${BCACHE_CSETS[@]}"; do + if [ -f /sys/fs/bcache/"${uuid}"/unregister ] ; then + echo 1 > /sys/fs/bcache/"${uuid}"/unregister + fi + done + + udevadm settle + + local timeout + timeout=0 + for uuid in "${BCACHE_CSETS[@]}"; do + while [[ -d "/sys/fs/bcache/${uuid}" ]] && (( timeout < 10 )); do + sleep 0.5 + (( timeout++ )) + done + done + + _bcache_wipe_devs + + BCACHE_CSETS=() + BCACHE_BDEVS=() +} + +_cleanup_bcache() { + local cset dev + + shopt -s nullglob + for dev in /sys/block/bcache* ; do + [ -e "${dev}" ] || continue + + dev=$(basename "${dev}") + echo "WARNING: bcache device ${dev} found" + + if [[ -f /sys/block/"${dev}"/bcache/stop ]]; then + echo 1 > /sys/block/"${dev}"/bcache/stop 2>/dev/null || true + fi + done + + for cset in /sys/fs/bcache/*-*-*-*-*; do + if [[ -d "${cset}" ]]; then + echo "WARNING: Unregistering cset $(basename "${cset}")" + echo 1 > "${cset}"/unregister 2>/dev/null || true + fi + done + shopt -u nullglob + + udevadm settle + + local timeout + timeout=0 + for cset in /sys/fs/bcache/*-*-*-*-*; do + while [[ -d "${cset}" ]] && (( timeout < 10 )); do + sleep 0.5 + (( timeout++ )) + done + done + + _bcache_wipe_devs + + BCACHE_DEVS=() +} -- 2.52.0