[PATCH v2 15/15] selftests: add Ceph BLOG smoke test and collection helpers
Alex Markuze <[email protected]> Mon, 6 Jul 2026 14:38:43 +0000
| Newsgroups | org.kernel.vger.ceph-devel |
|---|---|
| Message-ID | <[email protected]> |
Add kselftests: blog_smoke_test.sh (dual-mount isolation, enable/ disable), blog_collect.sh, blog_analyze.sh, and kselftest wiring. Add F: pattern for tools/testing/selftests/ceph_blog/ to the CephFS MAINTAINERS entry. Signed-off-by: Alex Markuze <[email protected]> --- MAINTAINERS | 1 + tools/testing/selftests/Makefile | 1 + tools/testing/selftests/ceph_blog/Makefile | 7 + .../selftests/ceph_blog/blog_analyze.sh | 124 ++++++ .../selftests/ceph_blog/blog_collect.sh | 116 ++++++ .../selftests/ceph_blog/blog_smoke_test.sh | 363 ++++++++++++++++++ tools/testing/selftests/ceph_blog/config | 2 + 7 files changed, 614 insertions(+) create mode 100644 tools/testing/selftests/ceph_blog/Makefile create mode 100644 tools/testing/selftests/ceph_blog/blog_analyze.sh create mode 100644 tools/testing/selftests/ceph_blog/blog_collect.sh create mode 100644 tools/testing/selftests/ceph_blog/blog_smoke_test.sh create mode 100644 tools/testing/selftests/ceph_blog/config diff --git a/MAINTAINERS b/MAINTAINERS index 441b556b9671..9a7265ce7de5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5909,6 +5909,7 @@ B: https://tracker.ceph.com/ T: git https://github.com/ceph/ceph-client.git F: Documentation/filesystems/ceph.rst F: fs/ceph/ +F: tools/testing/selftests/ceph_blog/ F: tools/testing/selftests/filesystems/ceph/ CERTIFICATE HANDLING diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index ab254ae793a9..d7e0138fcf3a 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -7,6 +7,7 @@ TARGETS += bpf TARGETS += breakpoints TARGETS += cachestat TARGETS += capabilities +TARGETS += ceph_blog TARGETS += cgroup TARGETS += clone3 TARGETS += connector diff --git a/tools/testing/selftests/ceph_blog/Makefile b/tools/testing/selftests/ceph_blog/Makefile new file mode 100644 index 000000000000..39f6d0c8990c --- /dev/null +++ b/tools/testing/selftests/ceph_blog/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 + +TEST_PROGS := blog_smoke_test.sh +TEST_PROGS += blog_collect.sh +TEST_PROGS += blog_analyze.sh + +include ../lib.mk diff --git a/tools/testing/selftests/ceph_blog/blog_analyze.sh b/tools/testing/selftests/ceph_blog/blog_analyze.sh new file mode 100644 index 000000000000..1f0bf049fbb9 --- /dev/null +++ b/tools/testing/selftests/ceph_blog/blog_analyze.sh @@ -0,0 +1,124 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# blog_analyze.sh - Analyze BLOG trace snapshots +# +# Parses the output of blog_collect.sh or blog_smoke_test.sh and +# produces a summary: entry counts, source-file breakdown, timeline, +# and per-client statistics. +# +# Usage: +# ./blog_analyze.sh <snapshot_dir> +# ./blog_analyze.sh /tmp/blog_snapshot_20260413_120000 +# +set -euo pipefail + +DIR="${1:?Usage: $0 <snapshot_dir>}" +[[ -d "$DIR" ]] || { echo "Not a directory: $DIR"; exit 1; } + +ENTRIES="$DIR/entries.txt" +SOURCES="$DIR/sources.txt" +CLIENTS="$DIR/clients.txt" +STATS="$DIR/stats.txt" + +echo "==========================================" +echo " BLOG Trace Analysis" +echo " Source: $DIR" +echo "==========================================" +echo "" + +# ---------- entry summary ---------- +if [[ -f "$ENTRIES" ]]; then + TOTAL=$(wc -l < "$ENTRIES") + echo "--- Entries ---" + echo " Total lines: $TOTAL" + + if (( TOTAL > 0 )); then + FIRST=$(head -1 "$ENTRIES" | awk '{print $1, $2}') + LAST=$(tail -1 "$ENTRIES" | awk '{print $1, $2}') + echo " First entry: $FIRST" + echo " Last entry: $LAST" + + echo "" + echo " Top 10 source locations:" + # entries format: "YYYY-MM-DD HH:MM:SS.mmm [file:func:line] message" + grep -oE '\[[^]]+\]' "$ENTRIES" 2>/dev/null | sed 's/^\[//;s/\]$//' | \ + sort | uniq -c | sort -rn | head -10 | \ + while read -r count loc; do + printf " %6d %s\n" "$count" "$loc" + done + + echo "" + echo " Top 10 most frequent messages (first 60 chars):" + # strip timestamp and source prefix, take first 60 chars + sed 's/^[0-9-]* [0-9:.]* \[[^]]*\] //' "$ENTRIES" | \ + cut -c1-60 | sort | uniq -c | sort -rn | head -10 | \ + while read -r count msg; do + printf " %6d %s\n" "$count" "$msg" + done + + echo "" + echo " Per-second entry rate:" + # extract HH:MM:SS, count per second + awk '{print $1" "$2}' "$ENTRIES" | \ + sed 's/\.[0-9]*//' | sort | uniq -c | \ + awk '{sum+=$1; count++} END { + if(count>0) printf " avg %.1f entries/sec over %d seconds\n", sum/count, count + }' + fi +else + echo " (no entries.txt found)" +fi + +echo "" + +# ---------- source summary ---------- +if [[ -f "$SOURCES" ]]; then + echo "--- Registered Sources ---" + SOURCE_COUNT=$(grep -c '^ID ' "$SOURCES" 2>/dev/null || echo 0) + echo " Total registered: $SOURCE_COUNT" + + if (( SOURCE_COUNT > 0 )); then + echo "" + echo " By file:" + grep '^ID ' "$SOURCES" | \ + sed 's/^ID [0-9]*: //' | cut -d: -f1 | \ + sort | uniq -c | sort -rn | \ + while read -r count file; do + printf " %4d %s\n" "$count" "$file" + done + fi +else + echo " (no sources.txt found)" +fi + +echo "" + +# ---------- client summary ---------- +if [[ -f "$CLIENTS" ]]; then + echo "--- Registered Clients ---" + CLIENT_COUNT=$(grep -c '^Client ID' "$CLIENTS" 2>/dev/null || echo 0) + echo " Total registered: $CLIENT_COUNT" + + if (( CLIENT_COUNT > 0 )); then + grep -A2 '^Client ID' "$CLIENTS" | \ + while IFS= read -r line; do + echo " $line" + done + fi +else + echo " (no clients.txt found)" +fi + +echo "" + +# ---------- stats summary ---------- +if [[ -f "$STATS" ]]; then + echo "--- Logger Statistics ---" + sed 's/^/ /' "$STATS" +else + echo " (no stats.txt found)" +fi + +echo "" +echo "==========================================" diff --git a/tools/testing/selftests/ceph_blog/blog_collect.sh b/tools/testing/selftests/ceph_blog/blog_collect.sh new file mode 100644 index 000000000000..9401724bf43d --- /dev/null +++ b/tools/testing/selftests/ceph_blog/blog_collect.sh @@ -0,0 +1,116 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# blog_collect.sh - Collect BLOG debugfs snapshots from a mounted CephFS +# +# Finds the Ceph BLOG debugfs directory, snapshots all files, and +# optionally tails the entries file for live monitoring. +# +# Usage: +# ./blog_collect.sh [snapshot|tail|clear|stats|sources|clients] [-d blog_dir] [-o dir] +# +set -euo pipefail + +ACTION="${1:-snapshot}" +OUTDIR="" +TARGET_BLOG_DIR="${BLOG_DIR:-}" + +shift || true +while getopts "d:o:" opt; do + case $opt in + d) TARGET_BLOG_DIR="$OPTARG" ;; + o) OUTDIR="$OPTARG" ;; + *) ;; + esac +done + +DEBUGFS_BASE="/sys/kernel/debug/ceph" +shopt -s nullglob + +find_blog_dir() { + local dirs=("$DEBUGFS_BASE"/*/blog) + + if [[ -n "$TARGET_BLOG_DIR" ]]; then + [[ -d "$TARGET_BLOG_DIR" ]] || { + echo "ERROR: BLOG debugfs directory does not exist: $TARGET_BLOG_DIR" >&2 + return 1 + } + echo "$TARGET_BLOG_DIR" + return 0 + fi + + if (( ${#dirs[@]} == 0 )); then + echo "ERROR: No BLOG debugfs directory found under $DEBUGFS_BASE" >&2 + echo "Ensure CephFS is mounted and CONFIG_CEPH_FS=y|m" >&2 + return 1 + fi + + if (( ${#dirs[@]} > 1 )); then + echo "ERROR: Multiple BLOG debugfs directories found:" >&2 + printf ' %s\n' "${dirs[@]}" >&2 + echo "Select one with -d <dir> or BLOG_DIR=<dir>." >&2 + return 1 + fi + + echo "${dirs[0]}" +} + +BLOG_DIR=$(find_blog_dir) +echo "BLOG debugfs: $BLOG_DIR" + +case "$ACTION" in + snapshot) + OUTDIR="${OUTDIR:-/tmp/blog_snapshot_$(date +%Y%m%d_%H%M%S)}" + mkdir -p "$OUTDIR" + for f in enabled entries stats sources clients; do + [[ -r "$BLOG_DIR/$f" ]] && cat "$BLOG_DIR/$f" > "$OUTDIR/$f.txt" + done + echo "Snapshot saved to $OUTDIR/" + if [[ -f "$OUTDIR/enabled.txt" ]]; then + echo " enabled: $(tr -d '[:space:]' < "$OUTDIR/enabled.txt")" + fi + echo " entries: $(wc -l < "$OUTDIR/entries.txt") lines" + echo " sources: $(grep -c '^ID ' "$OUTDIR/sources.txt" 2>/dev/null || echo 0)" + echo " clients: $(grep -c '^FSID:' "$OUTDIR/clients.txt" 2>/dev/null || echo 0)" + ;; + + tail) + echo "--- Live BLOG entries (Ctrl-C to stop) ---" + # Show new entries by polling; entries file is a seq_file so we + # re-read the whole thing each interval and diff. + PREV=$(mktemp) + CURR="" + trap 'rm -f "$PREV" "$CURR"' EXIT INT TERM + cat "$BLOG_DIR/entries" > "$PREV" + while true; do + sleep 1 + CURR=$(mktemp) + cat "$BLOG_DIR/entries" > "$CURR" + diff "$PREV" "$CURR" | grep '^>' | sed 's/^> //' || true + mv "$CURR" "$PREV" + CURR="" + done + ;; + + clear) + echo "clear" > "$BLOG_DIR/clear" + echo "BLOG entries cleared." + ;; + + stats) + cat "$BLOG_DIR/stats" + ;; + + sources) + cat "$BLOG_DIR/sources" + ;; + + clients) + cat "$BLOG_DIR/clients" + ;; + + *) + echo "Usage: $0 [snapshot|tail|clear|stats|sources|clients] [-d blog_dir] [-o dir]" + exit 1 + ;; +esac diff --git a/tools/testing/selftests/ceph_blog/blog_smoke_test.sh b/tools/testing/selftests/ceph_blog/blog_smoke_test.sh new file mode 100644 index 000000000000..1623630261c2 --- /dev/null +++ b/tools/testing/selftests/ceph_blog/blog_smoke_test.sh @@ -0,0 +1,363 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# blog_smoke_test.sh - Per-superblock smoke test for Ceph BLOG +# +# Verifies that BLOG stays disabled by default, that enabling BLOG on one +# noshare-mounted CephFS instance only affects that instance, and that a second +# noshare mount remains quiet until it is enabled separately. +# +# Usage: +# ./blog_smoke_test.sh -m <mon_addr> [-k <secret>] [-n <client_name>] +# [-p <mount_a>] [-q <mount_b>] [-o <output_dir>] +# +# Environment variables: +# CEPH_MON - monitor address(es) +# CEPH_SECRET - CephX secret key +# CEPH_CLIENT - client name, default "admin" +# CEPH_MOUNT - mount A, default "/mnt/ceph_blog_test_a" +# CEPH_MOUNT_B - mount B, default "${CEPH_MOUNT}_b" +# BLOG_OUTPUT - output directory for collected traces +# +set -euo pipefail +shopt -s nullglob + +MON="${CEPH_MON:-}" +SECRET="${CEPH_SECRET:-}" +CLIENT="${CEPH_CLIENT:-admin}" +MNT_A="${CEPH_MOUNT:-/mnt/ceph_blog_test_a}" +MNT_B="${CEPH_MOUNT_B:-}" +OUTDIR="${BLOG_OUTPUT:-/tmp/blog_test_$(date +%Y%m%d_%H%M%S)}" + +TMP_STATE_DIR="" +DEBUGFS_A="" +DEBUGFS_B="" +MOUNTED_A=0 +MOUNTED_B=0 + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NC='\033[0m' + +pass() { echo -e " ${GREEN}PASS${NC} $*"; } +fail() { echo -e " ${RED}FAIL${NC} $*"; } +info() { echo -e " ${YELLOW}----${NC} $*"; } +die() { echo -e "${RED}ERROR:${NC} $*" >&2; exit 1; } + +usage() { + sed -n '/^# Usage:/,/^$/p' "$0" | sed 's/^# //' >&2 + exit 1 +} + +while getopts "m:k:n:p:q:o:h" opt; do + case $opt in + m) MON="$OPTARG" ;; + k) SECRET="$OPTARG" ;; + n) CLIENT="$OPTARG" ;; + p) MNT_A="$OPTARG" ;; + q) MNT_B="$OPTARG" ;; + o) OUTDIR="$OPTARG" ;; + h) usage ;; + *) usage ;; + esac +done + +[[ -z "$MON" ]] && die "Monitor address required (-m or CEPH_MON)" +[[ $(id -u) -ne 0 ]] && die "Must run as root" +[[ -z "$MNT_B" ]] && MNT_B="${MNT_A}_b" + +cleanup() { + info "Cleaning up..." + if (( MOUNTED_B )); then + umount "$MNT_B" 2>/dev/null || true + fi + if (( MOUNTED_A )); then + umount "$MNT_A" 2>/dev/null || true + fi + [[ -n "$TMP_STATE_DIR" && -d "$TMP_STATE_DIR" ]] && rm -rf "$TMP_STATE_DIR" +} +trap cleanup EXIT + +assert_eq() { + local got="$1" + local want="$2" + local msg="$3" + + if [[ "$got" != "$want" ]]; then + fail "$msg: expected '$want', got '$got'" + exit 1 + fi + pass "$msg: $got" +} + +assert_gt_zero() { + local value="$1" + local msg="$2" + + if [[ "$value" -le 0 ]]; then + fail "$msg: expected > 0, got '$value'" + exit 1 + fi + pass "$msg: $value" +} + +have_kconfig() { + local pattern="$1" + + grep -qE "$pattern" /boot/config-"$(uname -r)" 2>/dev/null || + zgrep -qE "$pattern" /proc/config.gz 2>/dev/null +} + +capture_blog_dirs() { + local dest="$1" + local dirs=(/sys/kernel/debug/ceph/*/blog) + + : > "$dest" + if (( ${#dirs[@]} > 0 )); then + printf '%s\n' "${dirs[@]}" | sort -u > "$dest" + fi +} + +find_new_blog_dir() { + local before="$1" + local after="$2" + local label="$3" + local new_dirs=() + + mapfile -t new_dirs < <(comm -13 "$before" "$after" | sed '/^$/d') + if (( ${#new_dirs[@]} != 1 )); then + echo "Expected exactly one new BLOG debugfs directory for $label" >&2 + echo "Before:" >&2 + cat "$before" >&2 + echo "After:" >&2 + cat "$after" >&2 + return 1 + fi + + echo "${new_dirs[0]}" +} + +mount_ceph() { + local mnt="$1" + local opts="name=$CLIENT,noshare" + + mkdir -p "$mnt" + [[ -n "$SECRET" ]] && opts="${opts},secret=$SECRET" + mount -t ceph "$MON":/ "$mnt" -o "$opts" +} + +ensure_blog_dir() { + local dir="$1" + + [[ -d "$dir" ]] || die "BLOG debugfs directory missing: $dir" + [[ -r "$dir/entries" ]] || die "BLOG entries file missing: $dir/entries" + [[ -r "$dir/stats" ]] || die "BLOG stats file missing: $dir/stats" + [[ -r "$dir/sources" ]] || die "BLOG sources file missing: $dir/sources" + [[ -r "$dir/clients" ]] || die "BLOG clients file missing: $dir/clients" + [[ -w "$dir/enabled" ]] || die "BLOG enabled file missing: $dir/enabled" +} + +read_enabled() { + tr -d '[:space:]' < "$1/enabled" +} + +set_blog_enabled() { + local dir="$1" + local value="$2" + local actual + + echo "$value" > "$dir/enabled" + actual="$(read_enabled "$dir")" + case "$value" in + 0) + [[ "$actual" == "N" || "$actual" == "0" ]] || + die "Failed to disable BLOG in $dir (got '$actual')" + ;; + 1) + [[ "$actual" == "Y" || "$actual" == "1" ]] || + die "Failed to enable BLOG in $dir (got '$actual')" + ;; + *) + die "Unsupported BLOG enabled value: $value" + ;; + esac +} + +clear_blog() { + local dir="$1" + + echo "clear" > "$dir/clear" +} + +count_entries() { + local dir="$1" + local count + + count="$(grep -Ec '^[0-9]{4}-[0-9]{2}-[0-9]{2} ' "$dir/entries" 2>/dev/null || true)" + echo "${count:-0}" +} + +count_sources() { + local dir="$1" + local count + + count="$(grep -c '^ID ' "$dir/sources" 2>/dev/null || true)" + echo "${count:-0}" +} + +count_contexts() { + local dir="$1" + local count + + count="$(awk -F': ' '/Total contexts allocated/{print $2; exit}' \ + "$dir/stats" 2>/dev/null || true)" + echo "${count:-0}" +} + +count_mount_clients() { + local dir="$1" + local count + + count="$(grep -c '^FSID:' "$dir/clients" 2>/dev/null || true)" + echo "${count:-0}" +} + +require_timestamped_entries() { + local dir="$1" + local msg="$2" + + if ! grep -Eq '^[0-9]{4}-[0-9]{2}-[0-9]{2} ' "$dir/entries"; then + fail "$msg: no timestamped BLOG entries found" + exit 1 + fi + pass "$msg" +} + +snapshot_blog() { + local dir="$1" + local tag="$2" + local dest="$OUTDIR/$tag" + + mkdir -p "$dest" + for f in enabled entries stats sources clients; do + cat "$dir/$f" > "$dest/$f.txt" + done +} + +run_mount_io() { + local mnt="$1" + local tag="$2" + local root="$mnt/blog_smoke_$tag" + + rm -rf "$root" + mkdir -p "$root/subdir" + printf 'ceph-blog-%s %s\n' "$tag" "$(date +%s%N)" > "$root/file.txt" + cat "$root/file.txt" > /dev/null + stat "$root/file.txt" > /dev/null + mv "$root/file.txt" "$root/subdir/file.txt" + ln -sf "$root/subdir/file.txt" "$root/link.txt" + readlink "$root/link.txt" > /dev/null + ls -l "$root" > /dev/null + rm -f "$root/link.txt" "$root/subdir/file.txt" + rmdir "$root/subdir" + rmdir "$root" +} + +echo "==========================================" +echo " Ceph BLOG Per-Superblock Smoke Test" +echo "==========================================" +echo "" + +KSFT_SKIP=4 +info "Checking kernel BLOG support..." +if ! have_kconfig 'CONFIG_CEPH_FS=(y|m)'; then + info "SKIP: Cannot confirm CONFIG_CEPH_FS=y|m" + exit "$KSFT_SKIP" +fi +if ! have_kconfig 'CONFIG_DEBUG_FS=y'; then + info "SKIP: Cannot confirm CONFIG_DEBUG_FS=y" + exit "$KSFT_SKIP" +fi +if have_kconfig 'CONFIG_CEPH_FS=m'; then + modprobe ceph 2>/dev/null || die "Failed to load ceph module" +fi + +mkdir -p "$OUTDIR" "$MNT_A" "$MNT_B" +TMP_STATE_DIR="$(mktemp -d /tmp/ceph_blog_smoke.XXXXXX)" + +capture_blog_dirs "$TMP_STATE_DIR/before_a" +info "Mounting CephFS A with noshare: $MON -> $MNT_A" +mount_ceph "$MNT_A" || die "Mount A failed" +MOUNTED_A=1 +capture_blog_dirs "$TMP_STATE_DIR/after_a" +DEBUGFS_A="$(find_new_blog_dir "$TMP_STATE_DIR/before_a" \ + "$TMP_STATE_DIR/after_a" "mount A")" || die "Failed to identify BLOG dir for mount A" +ensure_blog_dir "$DEBUGFS_A" +pass "Mount A BLOG debugfs: $DEBUGFS_A" + +capture_blog_dirs "$TMP_STATE_DIR/before_b" +info "Mounting CephFS B with noshare: $MON -> $MNT_B" +mount_ceph "$MNT_B" || die "Mount B failed" +MOUNTED_B=1 +capture_blog_dirs "$TMP_STATE_DIR/after_b" +DEBUGFS_B="$(find_new_blog_dir "$TMP_STATE_DIR/before_b" \ + "$TMP_STATE_DIR/after_b" "mount B")" || die "Failed to identify BLOG dir for mount B" +ensure_blog_dir "$DEBUGFS_B" +pass "Mount B BLOG debugfs: $DEBUGFS_B" + +assert_eq "$(count_mount_clients "$DEBUGFS_A")" 1 "mount A clients view is local" +assert_eq "$(count_mount_clients "$DEBUGFS_B")" 1 "mount B clients view is local" + +info "Phase 1: BLOG disabled by default on both mounts" +set_blog_enabled "$DEBUGFS_A" 0 +set_blog_enabled "$DEBUGFS_B" 0 +run_mount_io "$MNT_A" "disabled_a" +assert_eq "$(count_entries "$DEBUGFS_A")" 0 "mount A stays quiet when disabled" +assert_eq "$(count_entries "$DEBUGFS_B")" 0 "mount B stays quiet while mount A runs" +snapshot_blog "$DEBUGFS_A" "00_disabled_a" +snapshot_blog "$DEBUGFS_B" "00_disabled_b" + +info "Phase 2: Enable BLOG only on mount A" +set_blog_enabled "$DEBUGFS_A" 1 +set_blog_enabled "$DEBUGFS_B" 0 +clear_blog "$DEBUGFS_A" +clear_blog "$DEBUGFS_B" +run_mount_io "$MNT_A" "enabled_a" + +A_ENTRIES="$(count_entries "$DEBUGFS_A")" +B_ENTRIES="$(count_entries "$DEBUGFS_B")" +assert_gt_zero "$A_ENTRIES" "mount A produces BLOG entries when enabled" +assert_eq "$B_ENTRIES" 0 "mount B remains quiet while only mount A is enabled" +assert_gt_zero "$(count_sources "$DEBUGFS_A")" "mount A registers BLOG sources" +assert_gt_zero "$(count_contexts "$DEBUGFS_A")" "mount A allocates BLOG contexts" +require_timestamped_entries "$DEBUGFS_A" "mount A entries deserialize with timestamps" +snapshot_blog "$DEBUGFS_A" "01_enabled_a" +snapshot_blog "$DEBUGFS_B" "01_enabled_a_other_mount" + +info "Phase 3: Disable mount A, enable mount B" +A_BEFORE_B="$A_ENTRIES" +set_blog_enabled "$DEBUGFS_A" 0 +set_blog_enabled "$DEBUGFS_B" 1 +clear_blog "$DEBUGFS_B" +run_mount_io "$MNT_B" "enabled_b" + +A_AFTER_B="$(count_entries "$DEBUGFS_A")" +B_AFTER_B="$(count_entries "$DEBUGFS_B")" +assert_eq "$A_AFTER_B" "$A_BEFORE_B" "mount A entries stay unchanged while mount B runs" +assert_gt_zero "$B_AFTER_B" "mount B produces BLOG entries when enabled" +assert_gt_zero "$(count_sources "$DEBUGFS_B")" "mount B registers BLOG sources" +assert_gt_zero "$(count_contexts "$DEBUGFS_B")" "mount B allocates BLOG contexts" +require_timestamped_entries "$DEBUGFS_B" "mount B entries deserialize with timestamps" +snapshot_blog "$DEBUGFS_A" "02_enabled_b_other_mount" +snapshot_blog "$DEBUGFS_B" "02_enabled_b" + +echo "" +echo "==========================================" +echo " Results Summary" +echo "==========================================" +pass "Default-off behavior verified on both mounts" +pass "Enable-on-A only behavior verified" +pass "Cross-mount non-interference verified with noshare" +pass "Enable-on-B behavior verified" +info "Traces saved to: $OUTDIR/" diff --git a/tools/testing/selftests/ceph_blog/config b/tools/testing/selftests/ceph_blog/config new file mode 100644 index 000000000000..7c5706ed8be8 --- /dev/null +++ b/tools/testing/selftests/ceph_blog/config @@ -0,0 +1,2 @@ +CONFIG_CEPH_FS=m +CONFIG_DEBUG_FS=y -- 2.34.1