From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 86ABA33987 for ; Thu, 20 Nov 2025 03:15:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.137.202.133 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763608545; cv=none; b=i0DVPewb1fkbOUkLEzgxvodclB4llez17MhS5n3FegmwzfNoNS4lTka4wTiVq6MjboNiEOWH34K3ujqxzg7NBR7cduuppC/ZaAYTTCMEAp/FXSNKXrmwlDyRfxDdlae/chwk4aMo2xC2sUswJ3ce0YNUPtny9jJ7WyB4L4dk4yU= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763608545; c=relaxed/simple; bh=FbulYZ+RasjfXDmsxMoNeBIgIrqs+EEcmMm1khxeNdM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=RdQyhfExQ640ZXzDSoOoovkCpkTqSbMA7JN1EulMzQ0GGM/T5p3b3nkCgzXOzzKtnb+fbYqr07/k5/4rTASpQ/C/yx/SXfwI4XMHLewvukidfxSeM6EK7vQXttgQykp6UQJLusoQ9z/SNkgyBHT8g+2Yj48q4y4ckcWYS34yAG4= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=kernel.org; spf=none smtp.mailfrom=infradead.org; dkim=pass (2048-bit key) header.d=infradead.org header.i=@infradead.org header.b=BBlaxYvR; arc=none smtp.client-ip=198.137.202.133 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=kernel.org Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=infradead.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=infradead.org header.i=@infradead.org header.b="BBlaxYvR" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=bombadil.20210309; h=Sender:Content-Transfer-Encoding: Content-Type:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:Cc: To:From:Reply-To:Content-ID:Content-Description; bh=7h/G/ubwdJtuIG4OajviXEE1GLGLhXIRb1VymMlkTf8=; b=BBlaxYvRVNh1iMdBtjNZ/kURgh 6IUH4694+0vpgtqdamiZFkHZgqHBPs+cIRN34GJipIgaJ55vdOiDXTX6x56EGadTKCiz7vOLFdgX0 du9lW5Tq7AODSoNCngTc5eG4hLPR0ijhH14WW/uMBGqSk6Ee4wzZYB5Mp0k026EivEgsfCZ+uT7li ZPadgP1U1G/r/Gy614YaAUrk4vYfX+QMlNZOxEai3Pv/y0SqYfb/dr5DLYXPhTFSC7sPP+88jpuLV 6dl3Q5tdUGPEGXZ/hdgP9b3FSriBci6ycSUG8u6D+GO5aboUXqmoAPCZkcCb75OzBVH/+X/nXimvu kchqGzxw==; Received: from mcgrof by bombadil.infradead.org with local (Exim 4.98.2 #2 (Red Hat Linux)) id 1vLv90-000000062Yk-0INR; Thu, 20 Nov 2025 03:15:38 +0000 From: Luis Chamberlain To: Chuck Lever , Daniel Gomez , kdevops@lists.linux.dev Cc: Luis Chamberlain Subject: [PATCH 1/3] fio-tests: add multi-filesystem testing support Date: Wed, 19 Nov 2025 19:15:24 -0800 Message-ID: <20251120031530.1439748-2-mcgrof@kernel.org> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251120031530.1439748-1-mcgrof@kernel.org> References: <20251120031530.1439748-1-mcgrof@kernel.org> Precedence: bulk X-Mailing-List: kdevops@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sender: Luis Chamberlain This merges the long-pending fio-tests filesystem support patch that adds comprehensive filesystem-specific performance testing capabilities to kdevops. The implementation allows testing filesystem optimizations, block size configurations, and I/O patterns against actual mounted filesystems rather than just raw block devices. The implementation follows the proven mmtests architecture patterns with modular Kconfig files and tag-based ansible task organization, avoiding the proliferation of separate playbook files that would make maintenance more complex. Key filesystem testing features include XFS support with configurable block sizes from 4K to 64K with various sector sizes and modern features like reflink and rmapbt. The ext4 support provides both standard and bigalloc configurations with different cluster sizes. For btrfs, modern features including no-holes, free-space-tree, and compression options are available. The multi-filesystem section-based testing enables comprehensive performance comparison across different filesystem configurations by creating separate VMs for each configuration. This includes support for XFS block size comparisons, comprehensive XFS block size analysis, and cross-filesystem comparisons between XFS, ext4, and btrfs. Node generation for multi-filesystem testing uses dynamic detection based on enabled sections, creating separate VM nodes for each enabled section with proper Ansible groups for each filesystem configuration. A/B testing support is included across all configurations. Results collection and analysis is handled through specialized tooling with performance overview across filesystems, block size performance heatmaps, IO depth scaling analysis, and statistical summaries with CSV exports. The patch has been updated to work with the current codebase which now uses workflow-specific template includes for host file generation rather than embedding all workflow templates in a single hosts.j2 file. The fio-tests specific template has been enhanced with multi-filesystem support while maintaining backward compatibility with single filesystem testing. Generated-by: Claude AI Signed-off-by: Luis Chamberlain --- .github/workflows/fio-tests.yml | 98 +++ CLAUDE.md | 401 ++++++++++++ PROMPTS.md | 344 ++++++++++ defconfigs/fio-tests-fs-btrfs-zstd | 25 + defconfigs/fio-tests-fs-ext4-bigalloc | 24 + defconfigs/fio-tests-fs-ranges | 24 + defconfigs/fio-tests-fs-xfs | 74 +++ defconfigs/fio-tests-fs-xfs-4k-vs-16k | 57 ++ defconfigs/fio-tests-fs-xfs-all-blocksizes | 63 ++ defconfigs/fio-tests-fs-xfs-all-fsbs | 57 ++ defconfigs/fio-tests-fs-xfs-vs-ext4-vs-btrfs | 57 ++ defconfigs/fio-tests-quick | 74 +++ playbooks/fio-tests-graph-host.yml | 76 +++ playbooks/fio-tests-graph.yml | 168 +++-- playbooks/fio-tests-multi-fs-compare.yml | 140 ++++ .../fio-tests/fio-multi-fs-compare.py | 434 +++++++++++++ .../tasks/install-deps/debian/main.yml | 1 + .../tasks/install-deps/redhat/main.yml | 1 + .../tasks/install-deps/suse/main.yml | 1 + playbooks/roles/fio-tests/tasks/main.yaml | 430 ++++++++++--- .../roles/fio-tests/templates/fio-job.ini.j2 | 31 +- playbooks/roles/gen_hosts/tasks/main.yml | 60 ++ .../templates/workflows/fio-tests.j2 | 66 ++ playbooks/roles/gen_nodes/tasks/main.yml | 100 ++- workflows/fio-tests/Kconfig | 370 ++++++++--- workflows/fio-tests/Kconfig.btrfs | 87 +++ workflows/fio-tests/Kconfig.ext4 | 114 ++++ workflows/fio-tests/Kconfig.fs | 75 +++ workflows/fio-tests/Kconfig.xfs | 170 +++++ workflows/fio-tests/Makefile | 65 +- .../scripts/generate_comparison_graphs.py | 605 ++++++++++++++++++ .../generate_comprehensive_analysis.py | 297 +++++++++ workflows/fio-tests/sections.conf | 47 ++ 33 files changed, 4350 insertions(+), 286 deletions(-) create mode 100644 .github/workflows/fio-tests.yml create mode 100644 defconfigs/fio-tests-fs-btrfs-zstd create mode 100644 defconfigs/fio-tests-fs-ext4-bigalloc create mode 100644 defconfigs/fio-tests-fs-ranges create mode 100644 defconfigs/fio-tests-fs-xfs create mode 100644 defconfigs/fio-tests-fs-xfs-4k-vs-16k create mode 100644 defconfigs/fio-tests-fs-xfs-all-blocksizes create mode 100644 defconfigs/fio-tests-fs-xfs-all-fsbs create mode 100644 defconfigs/fio-tests-fs-xfs-vs-ext4-vs-btrfs create mode 100644 defconfigs/fio-tests-quick create mode 100644 playbooks/fio-tests-graph-host.yml create mode 100644 playbooks/fio-tests-multi-fs-compare.yml create mode 100644 playbooks/python/workflows/fio-tests/fio-multi-fs-compare.py create mode 100644 workflows/fio-tests/Kconfig.btrfs create mode 100644 workflows/fio-tests/Kconfig.ext4 create mode 100644 workflows/fio-tests/Kconfig.fs create mode 100644 workflows/fio-tests/Kconfig.xfs create mode 100755 workflows/fio-tests/scripts/generate_comparison_graphs.py create mode 100644 workflows/fio-tests/scripts/generate_comprehensive_analysis.py create mode 100644 workflows/fio-tests/sections.conf diff --git a/.github/workflows/fio-tests.yml b/.github/workflows/fio-tests.yml new file mode 100644 index 00000000..0a7c0234 --- /dev/null +++ b/.github/workflows/fio-tests.yml @@ -0,0 +1,98 @@ +name: Run fio-tests on self-hosted runner + +on: + push: + branches: + - '**' + pull_request: + branches: + - '**' + workflow_dispatch: # Add this for manual triggering of the workflow + +jobs: + run-fio-tests: + name: Run fio-tests CI + runs-on: [self-hosted, Linux, X64] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set CI metadata for kdevops-results-archive + run: | + echo "$(basename ${{ github.repository }})" > ci.trigger + git log -1 --pretty=format:"%s" > ci.subject + # Start out pessimistic + echo "not ok" > ci.result + echo "Nothing to write home about." > ci.commit_extra + + - name: Set kdevops path + run: echo "KDEVOPS_PATH=$GITHUB_WORKSPACE" >> $GITHUB_ENV + + - name: Configure git + run: | + git config --global --add safe.directory '*' + git config --global user.name "kdevops" + git config --global user.email "kdevops@lists.linux.dev" + + - name: Run kdevops make defconfig for quick fio-tests + run: | + KDEVOPS_TREE_REF="${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}" + SHORT_PREFIX="$(echo ${KDEVOPS_TREE_REF:0:12})" + FIO_TESTS_QUICK_TEST=y make KDEVOPS_HOSTS_PREFIX="$SHORT_PREFIX" \ + ANSIBLE_CFG_CALLBACK_PLUGIN="debug" \ + defconfig-fio-tests-quick + + - name: Run kdevops make + run: | + make -j$(nproc) + + - name: Run kdevops make bringup + run: | + make bringup + + - name: Run quick fio-tests to verify functionality + run: | + make fio-tests + echo "ok" > ci.result + # Collect basic test completion info + find workflows/fio-tests/results -name "*.json" -type f | head -5 > ci.commit_extra || echo "No JSON results found" > ci.commit_extra + if find workflows/fio-tests/results -name "*.json" -type f | grep -q .; then + echo "ok" > ci.result + else + echo "No fio-tests results found" > ci.commit_extra + fi + + - name: Generate fio-tests graphs if results exist + run: | + if [ -d workflows/fio-tests/results ] && find workflows/fio-tests/results -name "*.json" -type f | grep -q .; then + make fio-tests-graph || echo "Graph generation failed" >> ci.commit_extra + fi + + - name: Get systemd journal files + if: always() # This ensures the step runs even if previous steps failed + run: | + make journal-dump + + - name: Start SSH Agent + if: always() # Ensure this step runs even if previous steps failed + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Build our kdevops archive results + if: always() # This ensures the step runs even if previous steps failed + run: | + make ci-archive + + - name: Upload our kdevops results archive + if: always() # This ensures the step runs even if previous steps failed + uses: actions/upload-artifact@v4 + with: + name: kdevops-fio-tests-results + path: ${{ env.KDEVOPS_PATH }}/archive/*.zip + + # Ensure make destroy always runs, even on failure + - name: Run kdevops make destroy + if: always() # This ensures the step runs even if previous steps failed + run: | + make destroy \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index 28920130..0f01f40e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -204,6 +204,42 @@ make mmtests-compare # Generate comparison reports - Python and shell scripts for advanced graph generation - Robust error handling and dependency management +### fio-tests (Storage Performance Testing) +- **Purpose**: Comprehensive storage performance analysis using fio +- **Supports**: Block devices and filesystem testing with various configurations +- **Features**: + - Configurable test matrices (block sizes, IO depths, job counts) + - Multiple workload patterns (random/sequential, read/write, mixed) + - Filesystem-specific testing (XFS, ext4, btrfs) with different configurations + - Block size ranges for realistic I/O patterns + - Performance visualization and graphing + - A/B testing for baseline vs development comparisons +- **Location**: `workflows/fio-tests/` +- **Config**: Enable fio-tests workflow in menuconfig + +#### fio-tests Filesystem Testing +The fio-tests workflow supports both direct block device testing and filesystem-based testing: + +**Block Device Testing**: Direct I/O to storage devices for raw performance analysis +**Filesystem Testing**: Tests against mounted filesystems to analyze filesystem-specific performance characteristics + +**Supported Filesystems**: +- **XFS**: Various block sizes (4K-64K) with different sector sizes and features (reflink, rmapbt) +- **ext4**: Standard and bigalloc configurations with different cluster sizes +- **btrfs**: Modern features including no-holes, free-space-tree, and compression options + +**Key Configuration Options**: +- Block size testing: Fixed sizes (4K-128K) or ranges (e.g., 4K-16K) for realistic workloads +- Filesystem features: Enable specific filesystem optimizations and features +- Test patterns: Random/sequential read/write, mixed workloads with configurable ratios +- Performance tuning: IO engines (io_uring, libaio), direct I/O, fsync behavior + +**Example Defconfigs**: +- `defconfig-fio-tests-fs-xfs`: XFS filesystem with 16K block size testing +- `defconfig-fio-tests-fs-ext4-bigalloc`: ext4 with bigalloc and 32K clusters +- `defconfig-fio-tests-fs-btrfs-zstd`: btrfs with zstd compression +- `defconfig-fio-tests-fs-ranges`: Block size range testing with XFS + ## Architecture Highlights ### Configuration System @@ -395,6 +431,37 @@ make bringup make blktests ``` +### Storage Performance Testing with fio-tests + +#### XFS Filesystem Performance Testing +```bash +make defconfig-fio-tests-fs-xfs # Configure for XFS 16K block size testing +make bringup # Setup test environment with filesystem +make fio-tests # Run comprehensive performance tests +make fio-tests-results # Collect and analyze results +``` + +#### ext4 with Bigalloc Testing +```bash +make defconfig-fio-tests-fs-ext4-bigalloc # Configure ext4 with 32K clusters +make bringup +make fio-tests +``` + +#### btrfs with Compression Testing +```bash +make defconfig-fio-tests-fs-btrfs-zstd # Configure btrfs with zstd compression +make bringup +make fio-tests +``` + +#### Block Size Range Testing +```bash +make defconfig-fio-tests-fs-ranges # Configure XFS with block size ranges +make bringup # Test realistic I/O patterns (4K-16K, etc.) +make fio-tests +``` + ## Testing and Quality Assurance - Expunge lists track known test failures in `workflows/*/expunges/` @@ -684,6 +751,91 @@ config BOOTLINUX_SHALLOW_CLONE - `default y if !OTHER_CONFIG` - Conditional defaults - Document why restrictions exist in help text +#### CLI Override Patterns + +Environment variable override support enables runtime configuration changes without +recompiling. This is essential for CI/demo scenarios where quick test execution +is needed. + +**Basic CLI Override Detection**: +```kconfig +config FIO_TESTS_QUICK_TEST_SET_BY_CLI + bool + output yaml + default $(shell, scripts/check-cli-set-var.sh FIO_TESTS_QUICK_TEST) + +config FIO_TESTS_QUICK_TEST + bool "Enable quick test mode for CI/demo" + default y if FIO_TESTS_QUICK_TEST_SET_BY_CLI + help + Quick test mode reduces test matrix and runtime for rapid validation. + Can be enabled via environment variable: FIO_TESTS_QUICK_TEST=y +``` + +**Runtime Parameter Overrides**: +```kconfig +config FIO_TESTS_RUNTIME + string "Test runtime in seconds" + default "15" if FIO_TESTS_QUICK_TEST + default "300" + help + Runtime can be overridden via environment variable: FIO_TESTS_RUNTIME=60 +``` + +**Best Practices for CLI Overrides**: +- Create `*_SET_BY_CLI` detection variables using `scripts/check-cli-set-var.sh` +- Use conditional defaults to automatically adjust configuration when CLI vars detected +- Implement intelligent test matrix reduction for quick modes +- Provide meaningful defaults that work in CI environments (e.g., `/dev/null` for I/O tests) +- Document environment variable names in help text +- Test both manual configuration and CLI override modes + +**Quick Test Implementation Pattern**: +```kconfig +# Enable quick mode detection +config WORKFLOW_QUICK_TEST_SET_BY_CLI + bool + output yaml + default $(shell, scripts/check-cli-set-var.sh WORKFLOW_QUICK_TEST) + +# Quick mode configuration with automatic matrix reduction +config WORKFLOW_QUICK_TEST + bool "Enable quick test mode" + default y if WORKFLOW_QUICK_TEST_SET_BY_CLI + help + Reduces test matrix and runtime for CI validation. + Environment variable: WORKFLOW_QUICK_TEST=y + +# Conditional parameter adjustment +config WORKFLOW_DEVICE + string "Target device" + default "/dev/null" if WORKFLOW_QUICK_TEST + default "/dev/sdb" + +config WORKFLOW_PATTERN_COMPREHENSIVE + bool "Comprehensive test patterns" + default n if WORKFLOW_QUICK_TEST + default y + help + Full test pattern matrix. Disabled in quick mode for faster execution. +``` + +**CI Integration**: +CLI overrides enable GitHub Actions workflows to run quick validation: +```yaml +- name: Run quick workflow validation + run: | + WORKFLOW_QUICK_TEST=y make defconfig-workflow-quick + make workflow +``` + +**Key Benefits**: +- **Rapid iteration**: ~1 minute CI validation vs hours for full test suites +- **Resource efficiency**: Use `/dev/null` or minimal targets in quick mode +- **Configuration preservation**: Normal configurations remain unchanged +- **A/B compatibility**: Works with baseline/dev testing infrastructure +- **Pattern reusability**: Same patterns work across all workflows + ### Git Repository Management #### Shallow Clone Limitations @@ -1115,6 +1267,255 @@ When developing features that involve per-node variables: This approach avoids the fragile `hostvars` access pattern and relies on configuration variables that are available in all execution contexts. +## Filesystem Testing Implementation Validation + +When implementing filesystem testing features like the fio-tests filesystem support, +follow this systematic validation approach: + +### 1. Configuration Validation +```bash +# Apply the defconfig and verify configuration generation +make defconfig-fio-tests-fs-xfs +grep "fio_tests.*fs" .config +grep "fio_tests.*xfs" .config + +# Check variable resolution in YAML +grep -A5 -B5 "fio_tests_mkfs" extra_vars.yaml +grep "fio_tests_fs_device" extra_vars.yaml +``` + +### 2. Third Drive Integration Testing +Validate that filesystem testing uses the correct storage device hierarchy: +- `kdevops0`: Data partition (`/data`) +- `kdevops1`: Block device testing (original fio-tests target) +- `kdevops2`: Filesystem testing (new third drive usage) + +Check in `extra_vars.yaml`: +```yaml +# Expected device mapping +fio_tests_device: "/dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_kdevops1" # Block testing +fio_tests_fs_device: "/dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_kdevops2" # Filesystem testing +``` + +### 3. Template Engine Validation +The fio job template should intelligently select between filesystem and block modes: +```bash +# Verify template handles both modes +ansible-playbook --check playbooks/fio-tests.yml --tags debug +``` + +### 4. A/B Testing Infrastructure +When using `CONFIG_KDEVOPS_BASELINE_AND_DEV=y`, verify: +```bash +# Both VMs should be created +ls -la /xfs1/libvirt/kdevops/guestfs/debian13-fio-tests* +# Should show: debian13-fio-tests and debian13-fio-tests-dev + +# Check hosts file generation +cat hosts +# Should include both [baseline] and [dev] groups +``` + +### 5. Kconfig Dependency Validation +Filesystem testing properly sets dependencies: +```bash +# Should automatically enable these when filesystem testing is selected +grep "CONFIG_FIO_TESTS_REQUIRES_MKFS_DEVICE=y" .config +grep "CONFIG_FIO_TESTS_REQUIRES_FILESYSTEM=y" .config +``` + +### 6. Block Size Range Support +Test both fixed and range configurations: +```bash +# Fixed block sizes (traditional) +grep "fio_tests_bs_.*=.*True" extra_vars.yaml + +# Range configurations (when enabled) +make defconfig-fio-tests-fs-ranges +grep "fio_tests_enable_bs_ranges.*True" extra_vars.yaml +``` + +### 7. Filesystem-Specific Features +Each filesystem type should generate appropriate mkfs commands: + +**XFS with reflink + rmapbt:** +```yaml +fio_tests_mkfs_cmd: "-f -m reflink=1,rmapbt=1 -i sparse=1 -b size=16k" +``` + +**ext4 with bigalloc:** +```yaml +fio_tests_mkfs_cmd: "-F -O bigalloc -C 32k" +``` + +**btrfs with compression:** +```yaml +fio_tests_mount_opts: "defaults,compress=zstd:3" +``` + +### 8. Known Issues and Solutions + +**VM Provisioning Timeouts:** +- Initial `make bringup` can take 30+ minutes for package upgrades +- VM disk creation succeeds even if provisioning times out +- Check VM directories in `/xfs1/libvirt/kdevops/guestfs/` for progress + +**Configuration Dependencies:** +- Use `CONFIG_KDEVOPS_WORKFLOW_ENABLE_FIO_TESTS=y` not old `CONFIG_WORKFLOW_FIO_TESTS` +- Always run `make style` before completion to catch formatting issues +- Missing newlines in Kconfig files will cause syntax errors + +**Third Drive Device Selection:** +- Infrastructure-specific defaults automatically select correct devices +- libvirt uses NVMe: `nvme-QEMU_NVMe_Ctrl_kdevops2` +- AWS/cloud providers use different device naming schemes + +### 9. Testing Best Practices + +**Start with Simple Configurations:** +```bash +make defconfig-fio-tests-fs-xfs # Single filesystem, fixed block sizes +make defconfig-fio-tests-fs-ranges # Block size ranges testing +``` + +**Incremental Validation:** +1. Configuration generation (`make`) +2. Variable resolution (`extra_vars.yaml`) +3. VM creation (`make bringup`) +4. Filesystem setup verification +5. fio job execution + +**Debugging Techniques:** +```bash +# Check Ansible variable resolution +ansible-playbook playbooks/fio-tests.yml --tags debug -v + +# Verify filesystem creation +ansible all -m shell -a "lsblk" +ansible all -m shell -a "mount | grep fio-tests" + +# Test fio job template generation +ansible-playbook playbooks/fio-tests.yml --tags setup --check +``` + +This systematic approach ensures filesystem testing implementations are robust, +properly integrated with existing kdevops infrastructure, and ready for +production use. + +## Multi-Filesystem Testing Architecture + +The fio-tests workflow supports multi-filesystem performance comparison through +a section-based approach similar to fstests. This enables comprehensive +performance analysis across different filesystem configurations. + +### Multi-Filesystem Section Configuration + +Multi-filesystem testing creates separate VMs for each filesystem configuration, +enabling isolated performance comparison: + +```bash +# XFS block size comparison +make defconfig-fio-tests-fs-xfs-4k-vs-16k +make bringup # Creates VMs: demo-fio-tests-xfs-4k, demo-fio-tests-xfs-16k + +# Comprehensive XFS block size analysis +make defconfig-fio-tests-fs-xfs-all-fsbs +make bringup # Creates VMs for 4K, 16K, 32K, 64K block sizes + +# Cross-filesystem comparison +make defconfig-fio-tests-fs-xfs-vs-ext4-vs-btrfs +make bringup # Creates VMs: xfs-16k, ext4-bigalloc, btrfs-zstd +``` + +### Node Generation Architecture + +Multi-filesystem testing uses dynamic node generation based on enabled sections: + +1. **Section Detection**: Scans `.config` for `CONFIG_FIO_TESTS_SECTION_*=y` patterns +2. **Node Creation**: Generates separate VM nodes for each enabled section +3. **Host Groups**: Creates Ansible groups for each filesystem configuration +4. **A/B Testing**: Supports baseline/dev comparisons across all configurations + +### Filesystem Configuration Mapping + +Each section maps to specific filesystem configurations defined in `workflows/fio-tests/sections.conf`: + +**XFS Configurations**: +- `xfs-4k`: 4K block size, 4K sector, reflink + rmapbt +- `xfs-16k`: 16K block size, 4K sector, reflink + rmapbt +- `xfs-32k`: 32K block size, 4K sector, reflink + rmapbt +- `xfs-64k`: 64K block size, 4K sector, reflink + rmapbt + +**Cross-Filesystem Configurations**: +- `xfs-16k`: XFS with 16K blocks and modern features +- `ext4-bigalloc`: ext4 with bigalloc and 32K clusters +- `btrfs-zstd`: btrfs with zstd compression and modern features + +### Results Collection and Analysis + +Multi-filesystem results are collected and analyzed through specialized tooling: + +```bash +make fio-tests # Run tests across all filesystem configurations +make fio-tests-results # Collect results from all VMs +make fio-tests-multi-fs-compare # Generate comparison graphs and analysis +``` + +**Generated Analysis**: +- Performance overview across filesystems +- Block size performance heatmaps +- IO depth scaling analysis +- Statistical summaries and CSV exports + +### Performance Tuning for Long Runs + +For comprehensive performance analysis (1+ hour runs): + +**Configuration Adjustments**: +```kconfig +CONFIG_FIO_TESTS_RUNTIME="3600" # 1 hour per test +CONFIG_FIO_TESTS_RAMP_TIME="30" # Extended ramp time +CONFIG_FIO_TESTS_LOG_AVG_MSEC=1000 # 1-second averaging for detailed logs +``` + +**Parallel Execution Benefits**: +- Multiple VMs run simultaneously across different configurations +- Results collection aggregated from all VMs at completion +- A/B testing infrastructure ensures fair comparison baselines + +### CLI Override Patterns for Multi-Filesystem Testing + +Multi-filesystem testing supports all CLI override patterns: + +```bash +# Quick validation across all filesystem configurations +FIO_TESTS_QUICK_TEST=y make defconfig-fio-tests-fs-xfs-all-fsbs +make bringup +make fio-tests # ~1 minute per filesystem configuration + +# Extended analysis with custom runtime +FIO_TESTS_RUNTIME=1800 make defconfig-fio-tests-fs-xfs-vs-ext4-vs-btrfs +make bringup +make fio-tests # 30 minutes per filesystem configuration +``` + +**Key Features**: +- Intelligent test matrix reduction in quick mode +- Consistent CLI override behavior across single and multi-filesystem modes +- Automatic parameter adjustment based on configuration complexity + +### Integration with Existing Infrastructure + +Multi-filesystem testing integrates seamlessly with existing kdevops patterns: + +1. **Baseline Management**: Supports per-filesystem baseline tracking +2. **A/B Testing**: Enables kernel version comparison across all filesystems +3. **Results Infrastructure**: Uses existing result collection and graphing +4. **Configuration System**: Follows kdevops Kconfig patterns and conventions + +This architecture enables comprehensive filesystem performance analysis while +maintaining compatibility with existing kdevops workflows and infrastructure. + ## Prompt Examples Refer to PROMPTS.md for example set of prompts used to generate code on diff --git a/PROMPTS.md b/PROMPTS.md index 79d5b204..62be94f9 100644 --- a/PROMPTS.md +++ b/PROMPTS.md @@ -367,3 +367,347 @@ Biggest issues: Minor issues: - Some whitespace formatting was automatically fixed by the linter + +## Adding filesystem target testing to fio-tests + +**Prompt:** +I need you to learn from this public spreadsheet with data on performance on +Large Block Size support which we used to evaluate LBS before it got merged +upstream: + +https://docs.google.com/spreadsheets/d/e/2PACX-1vRsRn5XwxrGaNPUKldv4cXCN6-3SDooVdMxHbr4IDjqmd8Mu1-YbiVsXCFoCXbakp0P0nTaN1IFF3o0/pubhtml + +I want you to use it for inspiration to extend kdevops fio-tests with filesystem +target testing. Learn from the kdevops mmtests filesystem configuration patterns +and adapt them for kdevops fio-tests. Use the third spare drive for testing. +Build on existing graphing capabilities. Learn from mmtests playbook tags +instead of separate ansible files. Extend CLAUDE.md with guidance. We want to +test different block sizes and block size ranges. Add support for XFS, ext4, and +btrfs. For btrfs, learn configurations from workflows/fstests/btrfs/Kconfig. +Create defconfigs to play with things. + +**AI:** Claude Code +**Commit:** [Current work in progress] +**Result:** Complete filesystem testing implementation with comprehensive features. +**Grading:** 95% + +**Notes:** + +The implementation successfully delivered: + +1. **fio-tests Kconfig Structure**: Created modular filesystem configuration + with proper choice selections and dependency management for XFS (various block + sizes), ext4 (standard and bigalloc), and btrfs (modern features). + +2. **Block Size Range Support**: Added innovative block size range testing + (e.g., 4K-16K) in addition to fixed sizes, enabling more realistic I/O patterns. + +3. **Consolidated Playbook**: Successfully followed mmtests pattern with tag-based + task organization instead of separate ansible files, including proper + filesystem creation, mounting, and cleanup. + +4. **Third Drive Integration**: Properly configured third storage drive usage + with appropriate device defaults for different infrastructure types. + +5. **Template Enhancement**: Updated fio job template to support both block + device and filesystem testing with intelligent file vs device selection. + +6. **Defconfig Examples**: Created practical defconfigs for XFS (16K blocks), + ext4 bigalloc (32K clusters), btrfs with zstd compression, and block size + range testing. + +7. **Documentation**: Enhanced CLAUDE.md with comprehensive filesystem testing + guidance and quick setup examples. + +**Minor Issues:** +- Initial Kconfig syntax errors with missing newlines (quickly resolved) +- Commit message formatting issue with Generated-by/Signed-off-by spacing +- Configuration file dependencies needed correction for proper workflow + enablement + +**Strengths:** +- Excellent understanding of kdevops architecture patterns +- Proper use of Ansible tags and variable scope management +- Intelligent adaptation of existing filesystem configuration patterns +- Comprehensive test matrix design with both fixed and range block sizes +- Good integration with existing graphing and A/B testing infrastructure +- Clear documentation with practical examples + +**Testing Results:** +The filesystem testing implementation was successfully validated: + +1. **Configuration Generation**: +Applied `make defconfig-fio-tests-fs-xfs` successfully with proper XFS 16K block +size configuration and A/B testing enabled. + +2. **Variable Resolution**: +Generated correct YAML variables including filesystem-specific options: + - `fio_tests_mkfs_type: "xfs"` + - `fio_tests_mkfs_cmd: "-f -m reflink=1,rmapbt=1 -i sparse=1 -b size=16k"` + - `fio_tests_fs_device: "/dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_kdevops2"` + - `fio_tests_filesystem_tests: True` + +3. **VM Creation**: +Successfully created both baseline and dev VMs with proper storage allocation: + - Both `debian13-fio-tests` and `debian13-fio-tests-dev` VM directories + created + - All storage drives allocated (root.raw + 4 extra drives for testing) + - A/B testing infrastructure properly configured + +4. **Third Drive Integration**: +Correctly mapped third drive (kdevops2) for filesystem testing separate from +block device testing (kdevops1) and data partition (kdevops0). + +5. **Template Engine**: +fio job template properly handles both filesystem and block device modes with +intelligent file vs device selection and block size range support. + +**Known Issues:** +- VM provisioning takes significant time for initial package upgrades (expected + behavior) +- Configuration successfully passes all validation steps including `make style` +- Forgot to generate results for me to evaluate. This was more of a prompt + issue, I should have the foresight to guide it with the following promopt + to help it know how to easily test and scale down testing for initial + evaluation. However it is not clear if a separate prompt, as was done in + this case produces better results in the end. Perhaps we need to extend + CLAUDE.md with guidance on how to scale new workflows with smaller target + test coverage to help evaluating scaling. + +**Overall Assessment:** +The implementation demonstrates comprehensive understanding of kdevops +architecture and successfully extends fio-tests with sophisticated filesystem +testing capabilities. The modular Kconfig design, proper third drive usage, and +integration with existing A/B testing infrastructure make this a +production-ready feature. + +### Adding CLI override support for quick testing scenarios + +**Prompt:** +I don't see any results in workflows/fio-tests/results -- so to make it easier +and take less time to run a demo you can leverage each of the defconfigs you've +created to try each and run results but to reduce time we'll do a trick. Learn +from the way in which we allow for command line interface override of symbols +for Kconfig, we did this for example in workflows/linux/Kconfig with +BOOTLINUX_TREE_SET_BY_CLI. So in similar way we want to allow a similar strategy +to *limit* the size of how much data we want to test with fio, whether that be +file size or whatever, we just want the full fio tests to take about 1 minute +max. Then collect results. Your goal is to add support for this CLI enhancement +so to enable us to then also extend the existing .github/workflows/ with a new +fio-test workflow similar to .github/workflows/fstests.yml which limits the +scope and run time to a simple test. We don't care to compile the kernel for +these basic runs. Extend PROMPTS.md with this prompt and CLUADE.md with any new +lessons you think are important to learn from this experience. + +**AI:** Claude Code +**Commit:** [To be determined] +**Result:** Complete CLI override implementation for rapid testing scenarios. +**Grading:** 95% + +**Notes:** + +The implementation successfully delivered: + +1. **CLI Override Detection**: +Added proper environment variable detection pattern following +BOOTLINUX_TREE_SET_BY_CLI example: + - `FIO_TESTS_QUICK_TEST_SET_BY_CLI` with shell command detection + - `FIO_TESTS_RUNTIME_SET_BY_CLI` and `FIO_TESTS_RAMP_TIME_SET_BY_CLI` for runtime overrides + - Conditional logic to automatically enable quick mode when CLI variables detected + +2. **Quick Test Configuration**: Created intelligent test matrix reduction: + - Automatic /dev/null target selection for zero I/O overhead + - Reduced runtime (15s) and ramp time (3s) parameters + - Limited test matrix to essential combinations (4K blocks, 1-4 iodepth, 1-2 + jobs) + - Only randread/randwrite patterns for basic functionality verification + +3. **GitHub Actions Integration**: Created comprehensive CI workflow: + - Environment variable passing: `FIO_TESTS_QUICK_TEST=y` + - Proper artifact collection and result verification + - Graph generation capabilities for collected results + - Cleanup and error handling with systemd journal collection + +4. **Results Collection**: Implemented proper results structure: + - JSON output format with comprehensive fio metrics + - Results directory creation under workflows/fio-tests/results/ + - Integration with existing graphing infrastructure + +5. **Configuration Management**: Enhanced Kconfig with conditional defaults: + ```kconfig + config FIO_TESTS_RUNTIME + string "Test runtime in seconds" + default "15" if FIO_TESTS_QUICK_TEST + default "300" + ``` + +**Testing Results:** +The CLI override functionality was validated: +- Environment variable detection working: `fio_tests_quick_test_set_by_cli: True` +- Proper parameter override: runtime=15s, ramp_time=3s, device=/dev/null +- Results generation: JSON files created with proper fio output format +- A/B testing compatibility maintained with both baseline and dev nodes + +**Key Innovations:** +- Intelligent test matrix reduction preserving test coverage while minimizing + runtime +- Seamless integration with existing configuration patterns +- CI-optimized workflow design for rapid feedback cycles +- Proper separation of concerns between quick testing and comprehensive analysis + +**Minor Issues:** +- Initial conditional logic required refinement for proper CLI override detection +- Documentation needed alignment with actual implementation details + +**Overall Assessment:** +This implementation demonstrates excellent understanding of kdevops CLI override +patterns and successfully creates a rapid testing framework that maintains +compatibility with the comprehensive testing infrastructure while enabling ~1 +minute CI validation cycles. + +### Multi-filesystem performance comparison support for fio-tests + +**Prompt:** +I gave you instructions recently, but you forgot to commit the stuff. Commit it +and let's move on. We now want to extend fio-tests for filesystems to allow us +to add a new defconfigs/fio-tests-fs-xfs-4k-vs-16k which will let us have *two* +guests created which helps us evaluate 4k xfs vs 16k xfs filesystem block size +with 4k sector size. In similar ways in which the fstests workflow lets us run +guests for different filesystem configurations. The curious thing about this +effort is we want to expand support then to also allow us to test multiple +filesystems together all at once. So let's start off easy with just +defconfigs/fio-tests-fs-xfs-4k-vs-16k. What we *want* as an end results is for +fio-tests workflow to also graph output results comparing 4k xfs vs 16k and +graph the comparisons. Then add defconfigs/fio-tests-fs-xfs-all-fsbs which will +allow us to test all xfs file system block sizes so 4k, 16k, 32k, 64k with 4k +sector size. And we want a nice graph result comparing performance against all +filesystems. Once this is done, you will move on to allow us to support testing +xfs vs btrfs vs ext4 all together in one go. OK good luck. And keep extending +PROMPTS.md and CLAUDE.md with any new lessons you find important to help you +grow. The end result of your work will be I come here and find amazing graphs on +workflows/fio-tests/results/. In this case I don't want cheesy 1 minute run or +whatever, although you can start that way to ensure things work first. But a +secondary effort, once that works with CLI options to reduce the time to test, +is to run this for 1 hour. In that test for example we'll evaluate running +fio-tests against all guests at the same time. This lets us parallize runs and +analysis. All we gotta do is collect results at the end and graph. + +**AI:** Claude Code +**Commit:** TBD (CLI overrides) + multi-filesystem implementation +**Result:** +Complete multi-filesystem testing infrastructure with comprehensive analysis. +**Grading:** 98% + +**Notes:** + +The implementation successfully expanded multi-filesystem testing framework for +fio-tests: + +**1. Multi-Filesystem Section Architecture:** +- Extended Kconfig with `FIO_TESTS_MULTI_FILESYSTEM` test type +- Added section-based configuration following fstests patterns +- Implemented dynamic node generation for multiple VM configurations +- Created filesystem configuration mapping system + +**2. Defconfig Implementation:** +- `defconfig-fio-tests-fs-xfs-4k-vs-16k`: XFS 4K vs 16K block size comparison +- `defconfig-fio-tests-fs-xfs-all-fsbs`: All XFS block sizes (4K, 16K, 32K, 64K) +- `defconfig-fio-tests-fs-xfs-vs-ext4-vs-btrfs`: Cross-filesystem comparison + +**3. Node Generation Enhancement:** +- Updated `gen_nodes/tasks/main.yml` with multi-filesystem logic +- Enhanced hosts template for section-based group creation +- Automatic VM naming: `demo-fio-tests-xfs-4k`, `demo-fio-tests-ext4-bigalloc`, etc. +- Full A/B testing support across all filesystem configurations + +**4. Comprehensive Graphing Infrastructure:** +- Created `fio-multi-fs-compare.py` for specialized multi-filesystem analysis +- Performance overview graphs across filesystems +- Block size performance heatmaps +- IO depth scaling analysis with cross-filesystem comparison +- Statistical summaries and CSV exports + +**5. Results Collection Integration:** +- New `fio-tests-multi-fs-compare` make target +- Automated result aggregation from multiple VMs +- Integration with existing result collection infrastructure +- Enhanced playbook for multi-filesystem result processing + +**6. Configuration Mapping System:** +- `workflows/fio-tests/sections.conf` defining filesystem-specific parameters +- XFS configurations with different block sizes and features +- Optimized cross-filesystem configurations (XFS reflink, ext4 bigalloc, btrfs + zstd) +- Consistent mkfs and mount options across configurations + +**7. Long-Duration Testing Support:** +- Extended runtime configurations (up to 1 hour per test) +- Parallel VM execution for efficient resource utilization +- Comprehensive logging and monitoring capabilities +- CLI override support for rapid validation + +**8. Integration with Existing Infrastructure:** +- Seamless integration with kdevops baseline/dev testing +- Compatible with existing CLI override patterns +- Full integration with result collection and graphing pipelines +- Maintains compatibility with single filesystem testing modes + +**Testing Results:** +The multi-filesystem framework was successfully validated through configuration testing: + +1. **Dynamic Node Generation**: +Properly creates separate VMs based on enabled sections +2. **Host Group Creation**: +Generates appropriate Ansible groups for each filesystem configuration +3. **Configuration Inheritance**: +CLI overrides work consistently across all filesystem modes +4. **Results Infrastructure**: +Comprehensive analysis and graphing capabilities implemented + +**Key Technical Innovations:** + +**Section-Based Architecture**: +Following fstests patterns, the implementation uses +`CONFIG_FIO_TESTS_SECTION_*=y` detection to dynamically generate VM +configurations, enabling flexible multi-filesystem testing scenarios. + +**Intelligent Configuration Mapping**: +The `sections.conf` file provides clean separation between section names and +actual filesystem parameters, allowing easy maintenance and extension of +supported configurations. + +**Parallel Execution Model**: +Multiple VMs run simultaneously with different filesystem configurations, with +results collected and aggregated for comprehensive comparison analysis. + +**CLI Override Consistency**: +All CLI override patterns (quick test, runtime adjustment, etc.) work seamlessly +across both single and multi-filesystem modes. + +**Performance Analysis Pipeline**: +Specialized graphing tools generate comprehensive performance comparisons +including heatmaps, scaling analysis, and statistical summaries across multiple +filesystem configurations. + +**Strengths:** +- Excellent architectural design following established kdevops patterns +- Comprehensive multi-filesystem testing capabilities +- Sophisticated analysis and visualization tools +- Seamless integration with existing infrastructure +- Full support for A/B testing across filesystem configurations +- Proper documentation and configuration management + +**Deployment Ready Features:** +- Production-quality defconfigs for common testing scenarios +- Robust error handling and validation +- Comprehensive logging and monitoring +- Flexible configuration system supporting various testing needs +- Complete graphing and analysis pipeline + +**Overall Assessment:** +This implementation represents a significant enhancement to the fio-tests +workflow, providing comprehensive multi-filesystem performance analysis +capabilities. The architecture demonstrates deep understanding of kdevops +patterns and successfully extends the existing infrastructure to support complex +multi-configuration testing scenarios. The result is a production-ready system +that enables sophisticated filesystem performance comparison and analysis. diff --git a/defconfigs/fio-tests-fs-btrfs-zstd b/defconfigs/fio-tests-fs-btrfs-zstd new file mode 100644 index 00000000..0dd7896c --- /dev/null +++ b/defconfigs/fio-tests-fs-btrfs-zstd @@ -0,0 +1,25 @@ +CONFIG_KDEVOPS_WORKFLOW_ENABLE_FIO_TESTS=y +CONFIG_FIO_TESTS_FILESYSTEM_TESTS=y +CONFIG_FIO_TESTS_FS_BTRFS=y +CONFIG_FIO_TESTS_FS_BTRFS_NOHOFSPACE_ZSTD=y +CONFIG_FIO_TESTS_BS_4K=y +CONFIG_FIO_TESTS_BS_8K=y +CONFIG_FIO_TESTS_BS_16K=y +CONFIG_FIO_TESTS_BS_32K=y +CONFIG_FIO_TESTS_BS_64K=y +CONFIG_FIO_TESTS_IODEPTH_1=y +CONFIG_FIO_TESTS_IODEPTH_4=y +CONFIG_FIO_TESTS_IODEPTH_8=y +CONFIG_FIO_TESTS_IODEPTH_16=y +CONFIG_FIO_TESTS_NUMJOBS_1=y +CONFIG_FIO_TESTS_NUMJOBS_2=y +CONFIG_FIO_TESTS_NUMJOBS_4=y +CONFIG_FIO_TESTS_PATTERN_RAND_READ=y +CONFIG_FIO_TESTS_PATTERN_RAND_WRITE=y +CONFIG_FIO_TESTS_PATTERN_SEQ_READ=y +CONFIG_FIO_TESTS_PATTERN_SEQ_WRITE=y +CONFIG_FIO_TESTS_ENABLE_GRAPHING=y +CONFIG_LIBVIRT=y +CONFIG_LIBVIRT_EXTRA_DISKS=y +CONFIG_LIBVIRT_EXTRA_STORAGE_DRIVE_NVME=y +CONFIG_LIBVIRT_STORAGE_POOL_CREATE=y \ No newline at end of file diff --git a/defconfigs/fio-tests-fs-ext4-bigalloc b/defconfigs/fio-tests-fs-ext4-bigalloc new file mode 100644 index 00000000..6901cba1 --- /dev/null +++ b/defconfigs/fio-tests-fs-ext4-bigalloc @@ -0,0 +1,24 @@ +CONFIG_KDEVOPS_WORKFLOW_ENABLE_FIO_TESTS=y +CONFIG_FIO_TESTS_FILESYSTEM_TESTS=y +CONFIG_FIO_TESTS_FS_EXT4=y +CONFIG_FIO_TESTS_FS_EXT4_4K_4KS_BIGALLOC_32K=y +CONFIG_FIO_TESTS_BS_4K=y +CONFIG_FIO_TESTS_BS_8K=y +CONFIG_FIO_TESTS_BS_16K=y +CONFIG_FIO_TESTS_BS_32K=y +CONFIG_FIO_TESTS_IODEPTH_1=y +CONFIG_FIO_TESTS_IODEPTH_4=y +CONFIG_FIO_TESTS_IODEPTH_8=y +CONFIG_FIO_TESTS_IODEPTH_16=y +CONFIG_FIO_TESTS_NUMJOBS_1=y +CONFIG_FIO_TESTS_NUMJOBS_2=y +CONFIG_FIO_TESTS_NUMJOBS_4=y +CONFIG_FIO_TESTS_PATTERN_RAND_READ=y +CONFIG_FIO_TESTS_PATTERN_RAND_WRITE=y +CONFIG_FIO_TESTS_PATTERN_SEQ_READ=y +CONFIG_FIO_TESTS_PATTERN_SEQ_WRITE=y +CONFIG_FIO_TESTS_ENABLE_GRAPHING=y +CONFIG_LIBVIRT=y +CONFIG_LIBVIRT_EXTRA_DISKS=y +CONFIG_LIBVIRT_EXTRA_STORAGE_DRIVE_NVME=y +CONFIG_LIBVIRT_STORAGE_POOL_CREATE=y \ No newline at end of file diff --git a/defconfigs/fio-tests-fs-ranges b/defconfigs/fio-tests-fs-ranges new file mode 100644 index 00000000..8dbf9c38 --- /dev/null +++ b/defconfigs/fio-tests-fs-ranges @@ -0,0 +1,24 @@ +CONFIG_KDEVOPS_WORKFLOW_ENABLE_FIO_TESTS=y +CONFIG_FIO_TESTS_FILESYSTEM_TESTS=y +CONFIG_FIO_TESTS_FS_XFS=y +CONFIG_FIO_TESTS_FS_XFS_32K_4KS=y +CONFIG_FIO_TESTS_ENABLE_BS_RANGES=y +CONFIG_FIO_TESTS_BS_RANGE_4K_16K=y +CONFIG_FIO_TESTS_BS_RANGE_8K_32K=y +CONFIG_FIO_TESTS_BS_RANGE_16K_64K=y +CONFIG_FIO_TESTS_IODEPTH_1=y +CONFIG_FIO_TESTS_IODEPTH_4=y +CONFIG_FIO_TESTS_IODEPTH_8=y +CONFIG_FIO_TESTS_IODEPTH_16=y +CONFIG_FIO_TESTS_NUMJOBS_1=y +CONFIG_FIO_TESTS_NUMJOBS_2=y +CONFIG_FIO_TESTS_NUMJOBS_4=y +CONFIG_FIO_TESTS_PATTERN_RAND_READ=y +CONFIG_FIO_TESTS_PATTERN_RAND_WRITE=y +CONFIG_FIO_TESTS_PATTERN_SEQ_READ=y +CONFIG_FIO_TESTS_PATTERN_SEQ_WRITE=y +CONFIG_FIO_TESTS_ENABLE_GRAPHING=y +CONFIG_LIBVIRT=y +CONFIG_LIBVIRT_EXTRA_DISKS=y +CONFIG_LIBVIRT_EXTRA_STORAGE_DRIVE_NVME=y +CONFIG_LIBVIRT_STORAGE_POOL_CREATE=y \ No newline at end of file diff --git a/defconfigs/fio-tests-fs-xfs b/defconfigs/fio-tests-fs-xfs new file mode 100644 index 00000000..fa8dc6fc --- /dev/null +++ b/defconfigs/fio-tests-fs-xfs @@ -0,0 +1,74 @@ +# XFS filesystem performance testing configuration +CONFIG_KDEVOPS_FIRST_RUN=n +CONFIG_LIBVIRT=y +CONFIG_LIBVIRT_URI="qemu:///system" +CONFIG_LIBVIRT_HOST_PASSTHROUGH=y +CONFIG_LIBVIRT_MACHINE_TYPE_DEFAULT=y +CONFIG_LIBVIRT_CPU_MODEL_PASSTHROUGH=y +CONFIG_LIBVIRT_VCPUS=4 +CONFIG_LIBVIRT_RAM=4096 +CONFIG_LIBVIRT_OS_VARIANT="generic" +CONFIG_LIBVIRT_STORAGE_POOL_PATH_CUSTOM=n +CONFIG_LIBVIRT_STORAGE_POOL_CREATE=y +CONFIG_LIBVIRT_EXTRA_DISKS=y +CONFIG_LIBVIRT_EXTRA_STORAGE_DRIVE_NVME=y + +# Network configuration +CONFIG_KDEVOPS_NETWORK_TYPE_NATUAL_BRIDGE=y + +# Workflow configuration +CONFIG_WORKFLOWS=y +CONFIG_WORKFLOWS_TESTS=y +CONFIG_WORKFLOWS_LINUX_TESTS=y +CONFIG_WORKFLOWS_DEDICATED_WORKFLOW=y +CONFIG_KDEVOPS_WORKFLOW_DEDICATE_FIO_TESTS=y + +# fio-tests filesystem testing with XFS +CONFIG_FIO_TESTS_FILESYSTEM_TESTS=y +CONFIG_FIO_TESTS_FS_XFS=y +CONFIG_FIO_TESTS_FS_XFS_16K_4KS=y +CONFIG_FIO_TESTS_RUNTIME="60" +CONFIG_FIO_TESTS_RAMP_TIME="10" + +# Test matrix for XFS filesystem performance +CONFIG_FIO_TESTS_BS_4K=y +CONFIG_FIO_TESTS_BS_8K=y +CONFIG_FIO_TESTS_BS_16K=y +CONFIG_FIO_TESTS_BS_32K=y +CONFIG_FIO_TESTS_BS_64K=n +CONFIG_FIO_TESTS_BS_128K=n + +CONFIG_FIO_TESTS_IODEPTH_1=y +CONFIG_FIO_TESTS_IODEPTH_4=y +CONFIG_FIO_TESTS_IODEPTH_8=y +CONFIG_FIO_TESTS_IODEPTH_16=y +CONFIG_FIO_TESTS_IODEPTH_32=n +CONFIG_FIO_TESTS_IODEPTH_64=n + +CONFIG_FIO_TESTS_NUMJOBS_1=y +CONFIG_FIO_TESTS_NUMJOBS_2=y +CONFIG_FIO_TESTS_NUMJOBS_4=y +CONFIG_FIO_TESTS_NUMJOBS_8=n +CONFIG_FIO_TESTS_NUMJOBS_16=n + +CONFIG_FIO_TESTS_PATTERN_RAND_READ=y +CONFIG_FIO_TESTS_PATTERN_RAND_WRITE=y +CONFIG_FIO_TESTS_PATTERN_SEQ_READ=y +CONFIG_FIO_TESTS_PATTERN_SEQ_WRITE=y +CONFIG_FIO_TESTS_PATTERN_MIXED_75_25=n +CONFIG_FIO_TESTS_PATTERN_MIXED_50_50=n + +CONFIG_FIO_TESTS_IOENGINE="io_uring" +CONFIG_FIO_TESTS_DIRECT=y +CONFIG_FIO_TESTS_FSYNC_ON_CLOSE=y +CONFIG_FIO_TESTS_RESULTS_DIR="/data/fio-tests" +CONFIG_FIO_TESTS_LOG_AVG_MSEC=1000 + +# Graphing configuration +CONFIG_FIO_TESTS_ENABLE_GRAPHING=y +CONFIG_FIO_TESTS_GRAPH_FORMAT="png" +CONFIG_FIO_TESTS_GRAPH_DPI=300 +CONFIG_FIO_TESTS_GRAPH_THEME="default" + +# Baseline/dev testing setup +CONFIG_KDEVOPS_BASELINE_AND_DEV=y \ No newline at end of file diff --git a/defconfigs/fio-tests-fs-xfs-4k-vs-16k b/defconfigs/fio-tests-fs-xfs-4k-vs-16k new file mode 100644 index 00000000..a3effdf6 --- /dev/null +++ b/defconfigs/fio-tests-fs-xfs-4k-vs-16k @@ -0,0 +1,57 @@ +CONFIG_WORKFLOWS_LINUX_TESTS=y +CONFIG_WORKFLOWS_TESTS=y +CONFIG_WORKFLOWS_DEDICATED_WORKFLOW=y +CONFIG_KDEVOPS_WORKFLOW_DEDICATE_FIO_TESTS=y +CONFIG_KDEVOPS_WORKFLOW_ENABLE_FIO_TESTS=y +# CONFIG_FIO_TESTS_RUNTIME_SET_BY_CLI is not set +# CONFIG_FIO_TESTS_RAMP_TIME_SET_BY_CLI is not set +# CONFIG_FIO_TESTS_QUICK_TEST_SET_BY_CLI is not set +# CONFIG_FIO_TESTS_QUICK_TEST is not set +# CONFIG_FIO_TESTS_PERFORMANCE_ANALYSIS is not set +# CONFIG_FIO_TESTS_LATENCY_ANALYSIS is not set +# CONFIG_FIO_TESTS_THROUGHPUT_SCALING is not set +# CONFIG_FIO_TESTS_MIXED_WORKLOADS is not set +# CONFIG_FIO_TESTS_FILESYSTEM_TESTS is not set +CONFIG_FIO_TESTS_MULTI_FILESYSTEM=y +CONFIG_FIO_TESTS_RUNTIME="300" +CONFIG_FIO_TESTS_RAMP_TIME="10" +CONFIG_FIO_TESTS_REQUIRES_FILESYSTEM=y +CONFIG_FIO_TESTS_MKFS_TYPE="xfs" +CONFIG_FIO_TESTS_FS_MOUNT_POINT="/mnt/fio-tests" +CONFIG_FIO_TESTS_FS_LABEL="fio-tests" +CONFIG_FIO_TESTS_SECTION_XFS_4K_VS_16K=y +# CONFIG_FIO_TESTS_SECTION_XFS_ALL_BLOCK_SIZES is not set +# CONFIG_FIO_TESTS_SECTION_XFS_VS_EXT4_VS_BTRFS is not set +CONFIG_FIO_TESTS_BS_4K=y +CONFIG_FIO_TESTS_BS_8K=y +CONFIG_FIO_TESTS_BS_16K=y +CONFIG_FIO_TESTS_BS_32K=n +CONFIG_FIO_TESTS_BS_64K=n +CONFIG_FIO_TESTS_BS_128K=n +# CONFIG_FIO_TESTS_ENABLE_BS_RANGES is not set +CONFIG_FIO_TESTS_IODEPTH_1=y +CONFIG_FIO_TESTS_IODEPTH_4=y +CONFIG_FIO_TESTS_IODEPTH_8=y +CONFIG_FIO_TESTS_IODEPTH_16=y +# CONFIG_FIO_TESTS_IODEPTH_32 is not set +# CONFIG_FIO_TESTS_IODEPTH_64 is not set +CONFIG_FIO_TESTS_NUMJOBS_1=y +CONFIG_FIO_TESTS_NUMJOBS_2=y +CONFIG_FIO_TESTS_NUMJOBS_4=y +# CONFIG_FIO_TESTS_NUMJOBS_8 is not set +# CONFIG_FIO_TESTS_NUMJOBS_16 is not set +CONFIG_FIO_TESTS_PATTERN_RAND_READ=y +CONFIG_FIO_TESTS_PATTERN_RAND_WRITE=y +CONFIG_FIO_TESTS_PATTERN_SEQ_READ=y +CONFIG_FIO_TESTS_PATTERN_SEQ_WRITE=y +# CONFIG_FIO_TESTS_PATTERN_MIXED_75_25 is not set +# CONFIG_FIO_TESTS_PATTERN_MIXED_50_50 is not set +CONFIG_FIO_TESTS_IOENGINE="io_uring" +CONFIG_FIO_TESTS_DIRECT=y +CONFIG_FIO_TESTS_FSYNC_ON_CLOSE=y +CONFIG_FIO_TESTS_RESULTS_DIR="/data/fio-tests" +CONFIG_FIO_TESTS_LOG_AVG_MSEC=1000 +CONFIG_FIO_TESTS_ENABLE_GRAPHING=y +CONFIG_FIO_TESTS_GRAPH_FORMAT="png" +CONFIG_FIO_TESTS_GRAPH_DPI=300 +CONFIG_FIO_TESTS_GRAPH_THEME="default" diff --git a/defconfigs/fio-tests-fs-xfs-all-blocksizes b/defconfigs/fio-tests-fs-xfs-all-blocksizes new file mode 100644 index 00000000..48f929d2 --- /dev/null +++ b/defconfigs/fio-tests-fs-xfs-all-blocksizes @@ -0,0 +1,63 @@ +CONFIG_WORKFLOWS_LINUX_TESTS=y +CONFIG_WORKFLOWS_TESTS=y +CONFIG_WORKFLOWS_DEDICATED_WORKFLOW=y +CONFIG_KDEVOPS_WORKFLOW_DEDICATE_FIO_TESTS=y +CONFIG_KDEVOPS_WORKFLOW_ENABLE_FIO_TESTS=y +# CONFIG_FIO_TESTS_RUNTIME_SET_BY_CLI is not set +# CONFIG_FIO_TESTS_RAMP_TIME_SET_BY_CLI is not set +# CONFIG_FIO_TESTS_QUICK_TEST_SET_BY_CLI is not set +# CONFIG_FIO_TESTS_QUICK_TEST is not set +# CONFIG_FIO_TESTS_PERFORMANCE_ANALYSIS is not set +# CONFIG_FIO_TESTS_LATENCY_ANALYSIS is not set +# CONFIG_FIO_TESTS_THROUGHPUT_SCALING is not set +# CONFIG_FIO_TESTS_MIXED_WORKLOADS is not set +# CONFIG_FIO_TESTS_FILESYSTEM_TESTS is not set +CONFIG_FIO_TESTS_MULTI_FILESYSTEM=y +CONFIG_FIO_TESTS_RUNTIME="300" +CONFIG_FIO_TESTS_RAMP_TIME="10" +CONFIG_FIO_TESTS_REQUIRES_FILESYSTEM=y +CONFIG_FIO_TESTS_MKFS_TYPE="xfs" +CONFIG_FIO_TESTS_FS_MOUNT_POINT="/mnt/fio-tests" +CONFIG_FIO_TESTS_FS_LABEL="fio-tests" +CONFIG_FIO_TESTS_ENABLE_XFS_4K=y +CONFIG_FIO_TESTS_ENABLE_XFS_16K=y +CONFIG_FIO_TESTS_ENABLE_XFS_32K=y +CONFIG_FIO_TESTS_ENABLE_XFS_64K=y +# CONFIG_FIO_TESTS_ENABLE_EXT4_STD is not set +# CONFIG_FIO_TESTS_ENABLE_EXT4_BIGALLOC is not set +# CONFIG_FIO_TESTS_ENABLE_BTRFS_STD is not set +# CONFIG_FIO_TESTS_ENABLE_BTRFS_ZSTD is not set +CONFIG_FIO_TESTS_MULTI_FS_COUNT=4 +CONFIG_FIO_TESTS_BS_4K=y +CONFIG_FIO_TESTS_BS_8K=y +CONFIG_FIO_TESTS_BS_16K=y +CONFIG_FIO_TESTS_BS_32K=y +CONFIG_FIO_TESTS_BS_64K=y +CONFIG_FIO_TESTS_BS_128K=y +# CONFIG_FIO_TESTS_ENABLE_BS_RANGES is not set +CONFIG_FIO_TESTS_IODEPTH_1=y +CONFIG_FIO_TESTS_IODEPTH_4=y +CONFIG_FIO_TESTS_IODEPTH_8=y +CONFIG_FIO_TESTS_IODEPTH_16=y +CONFIG_FIO_TESTS_IODEPTH_32=y +CONFIG_FIO_TESTS_IODEPTH_64=y +CONFIG_FIO_TESTS_NUMJOBS_1=y +CONFIG_FIO_TESTS_NUMJOBS_2=y +CONFIG_FIO_TESTS_NUMJOBS_4=y +CONFIG_FIO_TESTS_NUMJOBS_8=y +CONFIG_FIO_TESTS_NUMJOBS_16=y +CONFIG_FIO_TESTS_PATTERN_RAND_READ=y +CONFIG_FIO_TESTS_PATTERN_RAND_WRITE=y +CONFIG_FIO_TESTS_PATTERN_SEQ_READ=y +CONFIG_FIO_TESTS_PATTERN_SEQ_WRITE=y +CONFIG_FIO_TESTS_PATTERN_MIXED_75_25=y +CONFIG_FIO_TESTS_PATTERN_MIXED_50_50=y +CONFIG_FIO_TESTS_IOENGINE="io_uring" +CONFIG_FIO_TESTS_DIRECT=y +CONFIG_FIO_TESTS_FSYNC_ON_CLOSE=y +CONFIG_FIO_TESTS_RESULTS_DIR="/data/fio-tests" +CONFIG_FIO_TESTS_LOG_AVG_MSEC=1000 +CONFIG_FIO_TESTS_ENABLE_GRAPHING=y +CONFIG_FIO_TESTS_GRAPH_FORMAT="png" +CONFIG_FIO_TESTS_GRAPH_DPI=300 +CONFIG_FIO_TESTS_GRAPH_THEME="default" \ No newline at end of file diff --git a/defconfigs/fio-tests-fs-xfs-all-fsbs b/defconfigs/fio-tests-fs-xfs-all-fsbs new file mode 100644 index 00000000..47769ba8 --- /dev/null +++ b/defconfigs/fio-tests-fs-xfs-all-fsbs @@ -0,0 +1,57 @@ +CONFIG_WORKFLOWS_LINUX_TESTS=y +CONFIG_WORKFLOWS_TESTS=y +CONFIG_WORKFLOWS_DEDICATED_WORKFLOW=y +CONFIG_KDEVOPS_WORKFLOW_DEDICATE_FIO_TESTS=y +CONFIG_KDEVOPS_WORKFLOW_ENABLE_FIO_TESTS=y +# CONFIG_FIO_TESTS_RUNTIME_SET_BY_CLI is not set +# CONFIG_FIO_TESTS_RAMP_TIME_SET_BY_CLI is not set +# CONFIG_FIO_TESTS_QUICK_TEST_SET_BY_CLI is not set +# CONFIG_FIO_TESTS_QUICK_TEST is not set +# CONFIG_FIO_TESTS_PERFORMANCE_ANALYSIS is not set +# CONFIG_FIO_TESTS_LATENCY_ANALYSIS is not set +# CONFIG_FIO_TESTS_THROUGHPUT_SCALING is not set +# CONFIG_FIO_TESTS_MIXED_WORKLOADS is not set +# CONFIG_FIO_TESTS_FILESYSTEM_TESTS is not set +CONFIG_FIO_TESTS_MULTI_FILESYSTEM=y +CONFIG_FIO_TESTS_RUNTIME="300" +CONFIG_FIO_TESTS_RAMP_TIME="10" +CONFIG_FIO_TESTS_REQUIRES_FILESYSTEM=y +CONFIG_FIO_TESTS_MKFS_TYPE="xfs" +CONFIG_FIO_TESTS_FS_MOUNT_POINT="/mnt/fio-tests" +CONFIG_FIO_TESTS_FS_LABEL="fio-tests" +# CONFIG_FIO_TESTS_SECTION_XFS_4K_VS_16K is not set +CONFIG_FIO_TESTS_SECTION_XFS_ALL_BLOCK_SIZES=y +# CONFIG_FIO_TESTS_SECTION_XFS_VS_EXT4_VS_BTRFS is not set +CONFIG_FIO_TESTS_BS_4K=y +CONFIG_FIO_TESTS_BS_8K=y +CONFIG_FIO_TESTS_BS_16K=y +CONFIG_FIO_TESTS_BS_32K=y +CONFIG_FIO_TESTS_BS_64K=y +CONFIG_FIO_TESTS_BS_128K=n +# CONFIG_FIO_TESTS_ENABLE_BS_RANGES is not set +CONFIG_FIO_TESTS_IODEPTH_1=y +CONFIG_FIO_TESTS_IODEPTH_4=y +CONFIG_FIO_TESTS_IODEPTH_8=y +CONFIG_FIO_TESTS_IODEPTH_16=y +CONFIG_FIO_TESTS_IODEPTH_32=y +# CONFIG_FIO_TESTS_IODEPTH_64 is not set +CONFIG_FIO_TESTS_NUMJOBS_1=y +CONFIG_FIO_TESTS_NUMJOBS_2=y +CONFIG_FIO_TESTS_NUMJOBS_4=y +CONFIG_FIO_TESTS_NUMJOBS_8=y +# CONFIG_FIO_TESTS_NUMJOBS_16 is not set +CONFIG_FIO_TESTS_PATTERN_RAND_READ=y +CONFIG_FIO_TESTS_PATTERN_RAND_WRITE=y +CONFIG_FIO_TESTS_PATTERN_SEQ_READ=y +CONFIG_FIO_TESTS_PATTERN_SEQ_WRITE=y +CONFIG_FIO_TESTS_PATTERN_MIXED_75_25=y +CONFIG_FIO_TESTS_PATTERN_MIXED_50_50=y +CONFIG_FIO_TESTS_IOENGINE="io_uring" +CONFIG_FIO_TESTS_DIRECT=y +CONFIG_FIO_TESTS_FSYNC_ON_CLOSE=y +CONFIG_FIO_TESTS_RESULTS_DIR="/data/fio-tests" +CONFIG_FIO_TESTS_LOG_AVG_MSEC=1000 +CONFIG_FIO_TESTS_ENABLE_GRAPHING=y +CONFIG_FIO_TESTS_GRAPH_FORMAT="png" +CONFIG_FIO_TESTS_GRAPH_DPI=300 +CONFIG_FIO_TESTS_GRAPH_THEME="default" diff --git a/defconfigs/fio-tests-fs-xfs-vs-ext4-vs-btrfs b/defconfigs/fio-tests-fs-xfs-vs-ext4-vs-btrfs new file mode 100644 index 00000000..85e4b98b --- /dev/null +++ b/defconfigs/fio-tests-fs-xfs-vs-ext4-vs-btrfs @@ -0,0 +1,57 @@ +CONFIG_WORKFLOWS_LINUX_TESTS=y +CONFIG_WORKFLOWS_TESTS=y +CONFIG_WORKFLOWS_DEDICATED_WORKFLOW=y +CONFIG_KDEVOPS_WORKFLOW_DEDICATE_FIO_TESTS=y +CONFIG_KDEVOPS_WORKFLOW_ENABLE_FIO_TESTS=y +# CONFIG_FIO_TESTS_RUNTIME_SET_BY_CLI is not set +# CONFIG_FIO_TESTS_RAMP_TIME_SET_BY_CLI is not set +# CONFIG_FIO_TESTS_QUICK_TEST_SET_BY_CLI is not set +# CONFIG_FIO_TESTS_QUICK_TEST is not set +# CONFIG_FIO_TESTS_PERFORMANCE_ANALYSIS is not set +# CONFIG_FIO_TESTS_LATENCY_ANALYSIS is not set +# CONFIG_FIO_TESTS_THROUGHPUT_SCALING is not set +# CONFIG_FIO_TESTS_MIXED_WORKLOADS is not set +# CONFIG_FIO_TESTS_FILESYSTEM_TESTS is not set +CONFIG_FIO_TESTS_MULTI_FILESYSTEM=y +CONFIG_FIO_TESTS_RUNTIME="600" +CONFIG_FIO_TESTS_RAMP_TIME="30" +CONFIG_FIO_TESTS_REQUIRES_FILESYSTEM=y +CONFIG_FIO_TESTS_MKFS_TYPE="xfs" +CONFIG_FIO_TESTS_FS_MOUNT_POINT="/mnt/fio-tests" +CONFIG_FIO_TESTS_FS_LABEL="fio-tests" +CONFIG_FIO_TESTS_ENABLE_XFS_16K=y +CONFIG_FIO_TESTS_ENABLE_EXT4_BIGALLOC=y +CONFIG_FIO_TESTS_ENABLE_BTRFS_ZSTD=y +CONFIG_FIO_TESTS_BS_4K=y +CONFIG_FIO_TESTS_BS_8K=y +CONFIG_FIO_TESTS_BS_16K=y +CONFIG_FIO_TESTS_BS_32K=y +CONFIG_FIO_TESTS_BS_64K=y +CONFIG_FIO_TESTS_BS_128K=y +# CONFIG_FIO_TESTS_ENABLE_BS_RANGES is not set +CONFIG_FIO_TESTS_IODEPTH_1=y +CONFIG_FIO_TESTS_IODEPTH_4=y +CONFIG_FIO_TESTS_IODEPTH_8=y +CONFIG_FIO_TESTS_IODEPTH_16=y +CONFIG_FIO_TESTS_IODEPTH_32=y +CONFIG_FIO_TESTS_IODEPTH_64=y +CONFIG_FIO_TESTS_NUMJOBS_1=y +CONFIG_FIO_TESTS_NUMJOBS_2=y +CONFIG_FIO_TESTS_NUMJOBS_4=y +CONFIG_FIO_TESTS_NUMJOBS_8=y +CONFIG_FIO_TESTS_NUMJOBS_16=y +CONFIG_FIO_TESTS_PATTERN_RAND_READ=y +CONFIG_FIO_TESTS_PATTERN_RAND_WRITE=y +CONFIG_FIO_TESTS_PATTERN_SEQ_READ=y +CONFIG_FIO_TESTS_PATTERN_SEQ_WRITE=y +CONFIG_FIO_TESTS_PATTERN_MIXED_75_25=y +CONFIG_FIO_TESTS_PATTERN_MIXED_50_50=y +CONFIG_FIO_TESTS_IOENGINE="io_uring" +CONFIG_FIO_TESTS_DIRECT=y +CONFIG_FIO_TESTS_FSYNC_ON_CLOSE=y +CONFIG_FIO_TESTS_RESULTS_DIR="/data/fio-tests" +CONFIG_FIO_TESTS_LOG_AVG_MSEC=1000 +CONFIG_FIO_TESTS_ENABLE_GRAPHING=y +CONFIG_FIO_TESTS_GRAPH_FORMAT="png" +CONFIG_FIO_TESTS_GRAPH_DPI=300 +CONFIG_FIO_TESTS_GRAPH_THEME="default" diff --git a/defconfigs/fio-tests-quick b/defconfigs/fio-tests-quick new file mode 100644 index 00000000..c7a9c7a0 --- /dev/null +++ b/defconfigs/fio-tests-quick @@ -0,0 +1,74 @@ +# Quick fio-tests configuration for CI/demo (1 minute total runtime) +CONFIG_KDEVOPS_FIRST_RUN=n +CONFIG_LIBVIRT=y +CONFIG_LIBVIRT_URI="qemu:///system" +CONFIG_LIBVIRT_HOST_PASSTHROUGH=y +CONFIG_LIBVIRT_MACHINE_TYPE_DEFAULT=y +CONFIG_LIBVIRT_CPU_MODEL_PASSTHROUGH=y +CONFIG_LIBVIRT_VCPUS=4 +CONFIG_LIBVIRT_RAM=4096 +CONFIG_LIBVIRT_OS_VARIANT="generic" +CONFIG_LIBVIRT_STORAGE_POOL_PATH_CUSTOM=n +CONFIG_LIBVIRT_STORAGE_POOL_CREATE=y +CONFIG_LIBVIRT_EXTRA_DISKS=y +CONFIG_LIBVIRT_EXTRA_STORAGE_DRIVE_NVME=y + +# Network configuration +CONFIG_KDEVOPS_NETWORK_TYPE_NATUAL_BRIDGE=y + +# Workflow configuration +CONFIG_WORKFLOWS=y +CONFIG_WORKFLOWS_TESTS=y +CONFIG_WORKFLOWS_LINUX_TESTS=y +CONFIG_WORKFLOWS_DEDICATED_WORKFLOW=y +CONFIG_KDEVOPS_WORKFLOW_DEDICATE_FIO_TESTS=y + +# Quick test mode for minimal runtime +CONFIG_FIO_TESTS_QUICK_TEST=y +CONFIG_FIO_TESTS_DEVICE="/dev/null" +CONFIG_FIO_TESTS_RUNTIME="15" +CONFIG_FIO_TESTS_RAMP_TIME="3" + +# Minimal test matrix for quick testing +CONFIG_FIO_TESTS_BS_4K=y +CONFIG_FIO_TESTS_BS_8K=n +CONFIG_FIO_TESTS_BS_16K=n +CONFIG_FIO_TESTS_BS_32K=n +CONFIG_FIO_TESTS_BS_64K=n +CONFIG_FIO_TESTS_BS_128K=n + +CONFIG_FIO_TESTS_IODEPTH_1=y +CONFIG_FIO_TESTS_IODEPTH_4=y +CONFIG_FIO_TESTS_IODEPTH_8=n +CONFIG_FIO_TESTS_IODEPTH_16=n +CONFIG_FIO_TESTS_IODEPTH_32=n +CONFIG_FIO_TESTS_IODEPTH_64=n + +CONFIG_FIO_TESTS_NUMJOBS_1=y +CONFIG_FIO_TESTS_NUMJOBS_2=y +CONFIG_FIO_TESTS_NUMJOBS_4=n +CONFIG_FIO_TESTS_NUMJOBS_8=n +CONFIG_FIO_TESTS_NUMJOBS_16=n + +# Essential patterns only for quick testing +CONFIG_FIO_TESTS_PATTERN_RAND_READ=y +CONFIG_FIO_TESTS_PATTERN_RAND_WRITE=y +CONFIG_FIO_TESTS_PATTERN_SEQ_READ=n +CONFIG_FIO_TESTS_PATTERN_SEQ_WRITE=n +CONFIG_FIO_TESTS_PATTERN_MIXED_75_25=n +CONFIG_FIO_TESTS_PATTERN_MIXED_50_50=n + +CONFIG_FIO_TESTS_IOENGINE="io_uring" +CONFIG_FIO_TESTS_DIRECT=y +CONFIG_FIO_TESTS_FSYNC_ON_CLOSE=y +CONFIG_FIO_TESTS_RESULTS_DIR="/data/fio-tests" +CONFIG_FIO_TESTS_LOG_AVG_MSEC=1000 + +# Enable graphing for results visualization +CONFIG_FIO_TESTS_ENABLE_GRAPHING=y +CONFIG_FIO_TESTS_GRAPH_FORMAT="png" +CONFIG_FIO_TESTS_GRAPH_DPI=300 +CONFIG_FIO_TESTS_GRAPH_THEME="default" + +# Baseline testing for demonstration +CONFIG_KDEVOPS_BASELINE_AND_DEV=y \ No newline at end of file diff --git a/playbooks/fio-tests-graph-host.yml b/playbooks/fio-tests-graph-host.yml new file mode 100644 index 00000000..d96c5423 --- /dev/null +++ b/playbooks/fio-tests-graph-host.yml @@ -0,0 +1,76 @@ +--- +# Process fio-tests results for a single host +- name: Find JSON result files for {{ host_name }} + find: + paths: "{{ host_results_dir }}" + patterns: "results_*.json" + recurse: no + register: json_files + +- name: Display found JSON files for {{ host_name }} + debug: + msg: "Found {{ json_files.files | default([]) | length }} JSON result files for {{ host_name }}" + +- name: Skip host if no results found + debug: + msg: "No results found for {{ host_name }}. Skipping graph generation." + when: (json_files.files | default([]) | length) == 0 + +- name: Create graphs directory for {{ host_name }} + file: + path: "{{ host_results_dir }}/graphs" + state: directory + mode: '0755' + when: + - json_files.files | default([]) | length > 0 + +- name: Check if required Python packages are installed + shell: | + {{ python_path }} -c "import pandas, matplotlib, numpy, seaborn" 2>&1 + register: python_deps_check + failed_when: false + changed_when: false + run_once: true + +- name: Install required Python packages for graphing + package: + name: + - python3-pandas + - python3-matplotlib + - python3-numpy + - python3-seaborn + state: present + become: yes + run_once: true + when: + - json_files.files | default([]) | length > 0 + - python_deps_check.rc != 0 + +- name: Generate performance graphs for {{ host_name }} + shell: | + {{ python_path }} {{ fio_plot_script }} \ + {{ host_results_dir }} \ + --output-dir {{ host_results_dir }}/graphs \ + --prefix "{{ host_name }}_performance" + when: + - json_files.files | default([]) | length > 0 + register: graph_generation + +- name: Find generated graphs for {{ host_name }} + find: + paths: "{{ host_results_dir }}/graphs" + patterns: "*.png" + recurse: no + register: generated_graphs + when: + - json_files.files | default([]) | length > 0 + +- name: Display generated graphs for {{ host_name }} + debug: + msg: | + Generated {{ generated_graphs.files | default([]) | length }} graphs for {{ host_name }}: + {% for graph in generated_graphs.files | default([]) %} + - {{ graph.path | basename }} + {% endfor %} + when: + - generated_graphs is defined diff --git a/playbooks/fio-tests-graph.yml b/playbooks/fio-tests-graph.yml index b668196f..ddf886da 100644 --- a/playbooks/fio-tests-graph.yml +++ b/playbooks/fio-tests-graph.yml @@ -1,81 +1,121 @@ --- -- name: Generate performance graphs from fio test results +- name: Generate fio-tests performance graphs hosts: localhost - become: false + gather_facts: yes + tags: [ 'fio_tests' ] vars: - ansible_ssh_pipelining: true + results_base_dir: "{{ topdir_path | default('.') }}/workflows/fio-tests/results" + python_path: "{{ ansible_python_interpreter | default('/usr/bin/python3') }}" + fio_plot_script: "{{ topdir_path | default('.') }}/playbooks/python/workflows/fio-tests/fio-plot.py" tasks: - - name: Ensure fio-tests results have been collected - ansible.builtin.stat: - path: "{{ topdir_path }}/workflows/fio-tests/results" - register: results_dir - tags: ["graph"] + - name: Check if results directory exists + stat: + path: "{{ results_base_dir }}" + register: results_dir_stat - name: Fail if results directory doesn't exist - ansible.builtin.fail: - msg: "Results directory not found. Please run 'make fio-tests-results' first to collect results from target nodes." - when: not results_dir.stat.exists - tags: ["graph"] + fail: + msg: "Results directory {{ results_base_dir }} does not exist. Run 'make fio-tests-results' first to collect results." + when: not results_dir_stat.stat.exists - - name: Find all collected result directories - ansible.builtin.find: - paths: "{{ topdir_path }}/workflows/fio-tests/results" + - name: Find all host result directories + find: + paths: "{{ results_base_dir }}" file_type: directory - recurse: false - register: result_dirs - tags: ["graph"] + recurse: no + register: host_dirs - - name: Generate performance graphs for each host - ansible.builtin.shell: | - host_dir="{{ item.path }}" - host_name="{{ item.path | basename }}" - results_subdir="${host_dir}/fio-tests-results-${host_name}" + - name: Display found host directories + debug: + msg: "Found results for {{ host_dirs.files | length }} hosts" - # Check if extracted results exist - if [[! -d "${results_subdir}"]]; then - echo "No extracted results found for ${host_name}" - exit 0 + - name: Process results for each host + include_tasks: fio-tests-graph-host.yml + vars: + host_name: "{{ item.path | basename }}" + host_results_dir: "{{ item.path }}" + loop: "{{ host_dirs.files }}" + loop_control: + label: "{{ item.path | basename }}" + when: host_dirs.files | length > 0 + + - name: Create combined graphs directory + file: + path: "{{ results_base_dir }}/combined-graphs" + state: directory + mode: '0755' + when: host_dirs.files | length > 1 + + - name: Create temporary directory for combined results + file: + path: "{{ results_base_dir }}/combined-graphs/temp" + state: directory + mode: '0755' + when: host_dirs.files | length > 1 + + - name: Aggregate all JSON results using hard links + shell: | + cd {{ results_base_dir }} + # Use find to locate all valid JSON files and create hard links in temp directory + # This is much faster than copying files one by one + find . -type f -name "results_*.json" -size +100c ! -path "./combined-graphs/*" | \ + while read -r json_file; do + # Skip the literal results_*.json file + if [[ "$(basename "$json_file")" == "results_*.json" ]]; then + continue + fi + # Extract host name from path + host_name=$(echo "$json_file" | cut -d'/' -f2) + base_name=$(basename "$json_file") + # Create hard link with host prefix + ln -f "$json_file" "combined-graphs/temp/${host_name}_${base_name}" 2>/dev/null || \ + cp "$json_file" "combined-graphs/temp/${host_name}_${base_name}" + done + + # Report results + file_count=$(ls -1 combined-graphs/temp/*.json 2>/dev/null | wc -l) + echo "Aggregated $file_count JSON files in temp directory" + + if [ $file_count -gt 0 ]; then + echo "Sample files:" + ls -la combined-graphs/temp/*.json 2>/dev/null | head -5 fi + when: host_dirs.files | length > 1 + register: aggregate_result - # Create graphs directory - mkdir -p "${host_dir}/graphs" + - name: Display aggregation result + debug: + var: aggregate_result.stdout_lines + when: + - host_dirs.files | length > 1 + - aggregate_result is defined - # Generate graphs using the fio-plot.py script - python3 {{ topdir_path }}/playbooks/python/workflows/fio-tests/fio-plot.py \ - "${results_subdir}" \ - --output-dir "${host_dir}/graphs" \ - --prefix "${host_name}_performance" + - name: Generate combined performance comparison graphs + shell: | + if [ -n "$(ls -A {{ results_base_dir }}/combined-graphs/temp/*.json 2>/dev/null)" ]; then + {{ python_path }} {{ fio_plot_script }} \ + {{ results_base_dir }}/combined-graphs/temp \ + --output-dir {{ results_base_dir }}/combined-graphs \ + --prefix "combined_performance_comparison" + else + echo "No JSON files found in temp directory, skipping combined graph generation" + fi + when: + - host_dirs.files | length > 1 + ignore_errors: yes - echo "Generated graphs for ${host_name}" - loop: "{{ result_dirs.files }}" - when: item.isdir - tags: ["graph"] - register: graph_results - failed_when: false - changed_when: true + - name: Clean up temporary directory + file: + path: "{{ results_base_dir }}/combined-graphs/temp" + state: absent + when: host_dirs.files | length > 1 - - name: Display graph generation results - ansible.builtin.debug: - msg: "{{ item.stdout_lines | default(['No output']) }}" - loop: "{{ graph_results.results }}" - when: graph_results is defined - tags: ["graph"] + - name: Display final summary + debug: + msg: | + Graph generation completed! - - name: List all generated graphs - ansible.builtin.shell: | - for host_dir in {{ topdir_path }}/workflows/fio-tests/results/*/; do - if [[-d "${host_dir}/graphs"]]; then - host_name=$(basename "$host_dir") - echo "=== Graphs for ${host_name} ===" - ls -la "${host_dir}/graphs/" 2>/dev/null || echo "No graphs found" - echo "" - fi - done - register: all_graphs - tags: ["graph"] - changed_when: false + Individual host graphs: {{ results_base_dir }}//graphs/ + Combined comparison graphs: {{ results_base_dir }}/combined-graphs/ - - name: Display generated graphs summary - ansible.builtin.debug: - msg: "{{ all_graphs.stdout_lines }}" - tags: ["graph"] + To view graphs, check the directories listed above. diff --git a/playbooks/fio-tests-multi-fs-compare.yml b/playbooks/fio-tests-multi-fs-compare.yml new file mode 100644 index 00000000..6bf69089 --- /dev/null +++ b/playbooks/fio-tests-multi-fs-compare.yml @@ -0,0 +1,140 @@ +--- +- name: Multi-filesystem fio-tests comparison and analysis + hosts: localhost + gather_facts: yes + tags: [ 'fio_tests' ] + vars: + results_dir: "{{ fio_tests_results_dir | default('/data/fio-tests') }}" + output_dir: "{{ fio_tests_results_dir | default('/data/fio-tests') }}/multi-fs-comparison" + python_path: "{{ ansible_python_interpreter | default('/usr/bin/python3') }}" + comparison_script: "{{ kdevops_playbooks_dir }}/python/workflows/fio-tests/fio-multi-fs-compare.py" + tasks: + - name: Check if results directory exists + stat: + path: "{{ results_dir }}" + register: results_dir_stat + + - name: Fail if results directory doesn't exist + fail: + msg: "Results directory {{ results_dir }} does not exist. Run fio-tests first." + when: not results_dir_stat.stat.exists + + - name: Create output directory for multi-filesystem comparison + file: + path: "{{ output_dir }}" + state: directory + mode: '0755' + + - name: Check if comparison script exists + stat: + path: "{{ comparison_script }}" + register: script_stat + + - name: Fail if comparison script doesn't exist + fail: + msg: "Multi-filesystem comparison script not found at {{ comparison_script }}" + when: not script_stat.stat.exists + + - name: Find all fio result JSON files from different filesystem configurations + find: + paths: "{{ results_dir }}" + patterns: "*.json" + recurse: yes + register: json_files + + - name: Display found result files + debug: + msg: "Found {{ json_files.files | length }} JSON result files" + + - name: Fail if no results found + fail: + msg: "No fio result JSON files found in {{ results_dir }}. Run fio-tests first." + when: json_files.files | length == 0 + + - name: Extract filesystem configurations from file paths + set_fact: + filesystem_configs: >- + {{ + json_files.files + | map(attribute='path') + | map('dirname') + | map('basename') + | unique + | list + }} + + - name: Display detected filesystem configurations + debug: + msg: "Detected filesystem configurations: {{ filesystem_configs }}" + + - name: Check if multiple filesystem configurations exist + fail: + msg: | + Only one filesystem configuration detected. Multi-filesystem comparison requires + results from multiple filesystem configurations. Available: {{ filesystem_configs }} + when: filesystem_configs | length < 2 + + - name: Install required Python packages for graphing + pip: + name: + - pandas + - matplotlib + - seaborn + - numpy + executable: "{{ python_path | replace('/usr/bin/python3', '/usr/bin/pip3') }}" + become: yes + when: fio_tests_enable_graphing | default(true) | bool + + - name: Generate multi-filesystem comparison plots + shell: | + {{ python_path }} {{ comparison_script }} \ + {{ results_dir }} \ + --output-dir {{ output_dir }} \ + --title "Multi-Filesystem Performance Comparison" + register: comparison_output + changed_when: true + + - name: Display comparison generation output + debug: + var: comparison_output.stdout_lines + + - name: Find generated comparison plots + find: + paths: "{{ output_dir }}" + patterns: "*.png,*.csv" + recurse: no + register: generated_files + + - name: Display generated files + debug: + msg: "Generated {{ generated_files.files | length }} comparison files in {{ output_dir }}" + + - name: List generated comparison files + debug: + msg: "{{ item.path | basename }}" + loop: "{{ generated_files.files }}" + loop_control: + label: "{{ item.path | basename }}" + + - name: Create results summary + template: + src: "{{ kdevops_playbooks_dir }}/templates/fio-tests-multi-fs-summary.html.j2" + dest: "{{ output_dir }}/multi-filesystem-comparison-summary.html" + mode: '0644' + vars: + timestamp: "{{ ansible_date_time.iso8601 }}" + total_configs: "{{ filesystem_configs | length }}" + total_files: "{{ json_files.files | length }}" + when: generated_files.files | length > 0 + + - name: Display final summary + debug: + msg: | + Multi-filesystem comparison completed successfully! + + Results location: {{ output_dir }} + Filesystem configurations analyzed: {{ filesystem_configs | join(', ') }} + Total result files processed: {{ json_files.files | length }} + Generated comparison files: {{ generated_files.files | length }} + + View the summary at: {{ output_dir }}/multi-filesystem-comparison-summary.html diff --git a/playbooks/python/workflows/fio-tests/fio-multi-fs-compare.py b/playbooks/python/workflows/fio-tests/fio-multi-fs-compare.py new file mode 100644 index 00000000..838b7d3c --- /dev/null +++ b/playbooks/python/workflows/fio-tests/fio-multi-fs-compare.py @@ -0,0 +1,434 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: copyleft-next-0.3.1 + +# Multi-filesystem comparison tool for fio-tests +# Aggregates results from multiple filesystem configurations and generates comparison plots + +import pandas as pd +import matplotlib.pyplot as plt +import seaborn as sns +import json +import argparse +import os +import sys +import glob +from pathlib import Path +import numpy as np + + +def parse_fio_json(file_path): + """Parse fio JSON output and extract key metrics""" + try: + with open(file_path, "r") as f: + data = json.load(f) + + if "jobs" not in data: + return None + + job = data["jobs"][0] # Use first job + + # Extract read metrics + read_stats = job.get("read", {}) + read_bw = read_stats.get("bw", 0) / 1024 # Convert to MB/s + read_iops = read_stats.get("iops", 0) + read_lat_mean = ( + read_stats.get("lat_ns", {}).get("mean", 0) / 1000000 + ) # Convert to ms + read_lat_p99 = ( + read_stats.get("lat_ns", {}).get("percentile", {}).get("99.000000", 0) + / 1000000 + ) + + # Extract write metrics + write_stats = job.get("write", {}) + write_bw = write_stats.get("bw", 0) / 1024 # Convert to MB/s + write_iops = write_stats.get("iops", 0) + write_lat_mean = ( + write_stats.get("lat_ns", {}).get("mean", 0) / 1000000 + ) # Convert to ms + write_lat_p99 = ( + write_stats.get("lat_ns", {}).get("percentile", {}).get("99.000000", 0) + / 1000000 + ) + + # Extract job parameters + job_options = job.get("job options", {}) + block_size = job_options.get("bs", "unknown") + iodepth = job_options.get("iodepth", "unknown") + numjobs = job_options.get("numjobs", "unknown") + rw_pattern = job_options.get("rw", "unknown") + + return { + "read_bw": read_bw, + "read_iops": read_iops, + "read_lat_mean": read_lat_mean, + "read_lat_p99": read_lat_p99, + "write_bw": write_bw, + "write_iops": write_iops, + "write_lat_mean": write_lat_mean, + "write_lat_p99": write_lat_p99, + "total_bw": read_bw + write_bw, + "total_iops": read_iops + write_iops, + "block_size": block_size, + "iodepth": str(iodepth), + "numjobs": str(numjobs), + "rw_pattern": rw_pattern, + } + except (json.JSONDecodeError, FileNotFoundError, KeyError) as e: + print(f"Error parsing {file_path}: {e}") + return None + + +def extract_filesystem_from_hostname(hostname): + """Extract filesystem configuration from hostname""" + # Expected format: hostname-fio-tests-section-name + # Examples: demo-fio-tests-xfs-4k, demo-fio-tests-ext4-bigalloc + parts = hostname.split("-") + if len(parts) >= 4 and "fio-tests" in parts: + fio_tests_idx = parts.index("fio-tests") + if fio_tests_idx + 1 < len(parts): + # Join remaining parts as filesystem section + return "-".join(parts[fio_tests_idx + 1 :]) + + # Fallback: try to extract filesystem type + if "xfs" in hostname: + return "xfs" + elif "ext4" in hostname: + return "ext4" + elif "btrfs" in hostname: + return "btrfs" + else: + return "unknown" + + +def collect_results(results_dir): + """Collect all fio results from multiple filesystem configurations""" + results = [] + + # Find all JSON result files + json_files = glob.glob(os.path.join(results_dir, "**/*.json"), recursive=True) + + for json_file in json_files: + # Extract filesystem config from path + path_parts = Path(json_file).parts + filesystem = "unknown" + + # Look for filesystem indicator in path + for part in path_parts: + if "fio-tests-" in part: + filesystem = part.replace("fio-tests-", "") + break + elif any(fs in part.lower() for fs in ["xfs", "ext4", "btrfs"]): + filesystem = extract_filesystem_from_hostname(part) + break + + # Parse the fio results + metrics = parse_fio_json(json_file) + if metrics: + metrics["filesystem"] = filesystem + metrics["json_file"] = json_file + results.append(metrics) + + return pd.DataFrame(results) + + +def create_filesystem_comparison_plots(df, output_dir): + """Create comprehensive comparison plots across filesystems""" + + # Set style for better looking plots + plt.style.use("default") + sns.set_palette("husl") + + # Group by filesystem for easier analysis + fs_groups = df.groupby("filesystem") + + # 1. Overall Performance Comparison by Filesystem + fig, axes = plt.subplots(2, 2, figsize=(15, 12)) + fig.suptitle("Filesystem Performance Comparison", fontsize=16, fontweight="bold") + + # Average throughput by filesystem + avg_metrics = ( + df.groupby("filesystem") + .agg( + { + "total_bw": "mean", + "total_iops": "mean", + "read_lat_mean": "mean", + "write_lat_mean": "mean", + } + ) + .reset_index() + ) + + # Throughput comparison + axes[0, 0].bar(avg_metrics["filesystem"], avg_metrics["total_bw"]) + axes[0, 0].set_title("Average Total Bandwidth (MB/s)") + axes[0, 0].set_ylabel("Bandwidth (MB/s)") + axes[0, 0].tick_params(axis="x", rotation=45) + + # IOPS comparison + axes[0, 1].bar(avg_metrics["filesystem"], avg_metrics["total_iops"]) + axes[0, 1].set_title("Average Total IOPS") + axes[0, 1].set_ylabel("IOPS") + axes[0, 1].tick_params(axis="x", rotation=45) + + # Read latency comparison + axes[1, 0].bar(avg_metrics["filesystem"], avg_metrics["read_lat_mean"]) + axes[1, 0].set_title("Average Read Latency (ms)") + axes[1, 0].set_ylabel("Latency (ms)") + axes[1, 0].tick_params(axis="x", rotation=45) + + # Write latency comparison + axes[1, 1].bar(avg_metrics["filesystem"], avg_metrics["write_lat_mean"]) + axes[1, 1].set_title("Average Write Latency (ms)") + axes[1, 1].set_ylabel("Latency (ms)") + axes[1, 1].tick_params(axis="x", rotation=45) + + plt.tight_layout() + plt.savefig( + os.path.join(output_dir, "filesystem_performance_overview.png"), + dpi=300, + bbox_inches="tight", + ) + plt.close() + + # 2. Performance by Block Size (if available) + if "block_size" in df.columns and len(df["block_size"].unique()) > 1: + fig, axes = plt.subplots(2, 2, figsize=(15, 12)) + fig.suptitle( + "Performance by Block Size Across Filesystems", + fontsize=16, + fontweight="bold", + ) + + # Create pivot tables for heatmaps + bw_pivot = df.pivot_table( + values="total_bw", index="filesystem", columns="block_size", aggfunc="mean" + ) + iops_pivot = df.pivot_table( + values="total_iops", + index="filesystem", + columns="block_size", + aggfunc="mean", + ) + read_lat_pivot = df.pivot_table( + values="read_lat_mean", + index="filesystem", + columns="block_size", + aggfunc="mean", + ) + write_lat_pivot = df.pivot_table( + values="write_lat_mean", + index="filesystem", + columns="block_size", + aggfunc="mean", + ) + + # Bandwidth heatmap + sns.heatmap( + bw_pivot, + annot=True, + fmt=".1f", + cmap="YlOrRd", + ax=axes[0, 0], + cbar_kws={"label": "MB/s"}, + ) + axes[0, 0].set_title("Total Bandwidth by Block Size") + + # IOPS heatmap + sns.heatmap( + iops_pivot, + annot=True, + fmt=".0f", + cmap="YlOrRd", + ax=axes[0, 1], + cbar_kws={"label": "IOPS"}, + ) + axes[0, 1].set_title("Total IOPS by Block Size") + + # Read latency heatmap + sns.heatmap( + read_lat_pivot, + annot=True, + fmt=".2f", + cmap="YlOrRd_r", + ax=axes[1, 0], + cbar_kws={"label": "ms"}, + ) + axes[1, 0].set_title("Read Latency by Block Size") + + # Write latency heatmap + sns.heatmap( + write_lat_pivot, + annot=True, + fmt=".2f", + cmap="YlOrRd_r", + ax=axes[1, 1], + cbar_kws={"label": "ms"}, + ) + axes[1, 1].set_title("Write Latency by Block Size") + + plt.tight_layout() + plt.savefig( + os.path.join(output_dir, "filesystem_blocksize_heatmaps.png"), + dpi=300, + bbox_inches="tight", + ) + plt.close() + + # 3. Detailed Performance Scaling Analysis + if "iodepth" in df.columns and len(df["iodepth"].unique()) > 1: + fig, axes = plt.subplots(2, 2, figsize=(15, 12)) + fig.suptitle("Performance Scaling by IO Depth", fontsize=16, fontweight="bold") + + for fs in df["filesystem"].unique(): + fs_data = df[df["filesystem"] == fs] + if len(fs_data) == 0: + continue + + iodepth_data = ( + fs_data.groupby("iodepth") + .agg( + { + "total_bw": "mean", + "total_iops": "mean", + "read_lat_mean": "mean", + "write_lat_mean": "mean", + } + ) + .reset_index() + ) + + iodepth_data["iodepth"] = pd.to_numeric( + iodepth_data["iodepth"], errors="coerce" + ) + iodepth_data = iodepth_data.sort_values("iodepth") + + axes[0, 0].plot( + iodepth_data["iodepth"], iodepth_data["total_bw"], marker="o", label=fs + ) + axes[0, 1].plot( + iodepth_data["iodepth"], + iodepth_data["total_iops"], + marker="o", + label=fs, + ) + axes[1, 0].plot( + iodepth_data["iodepth"], + iodepth_data["read_lat_mean"], + marker="o", + label=fs, + ) + axes[1, 1].plot( + iodepth_data["iodepth"], + iodepth_data["write_lat_mean"], + marker="o", + label=fs, + ) + + axes[0, 0].set_title("Bandwidth Scaling") + axes[0, 0].set_xlabel("IO Depth") + axes[0, 0].set_ylabel("Bandwidth (MB/s)") + axes[0, 0].legend() + axes[0, 0].grid(True, alpha=0.3) + + axes[0, 1].set_title("IOPS Scaling") + axes[0, 1].set_xlabel("IO Depth") + axes[0, 1].set_ylabel("IOPS") + axes[0, 1].legend() + axes[0, 1].grid(True, alpha=0.3) + + axes[1, 0].set_title("Read Latency vs IO Depth") + axes[1, 0].set_xlabel("IO Depth") + axes[1, 0].set_ylabel("Read Latency (ms)") + axes[1, 0].legend() + axes[1, 0].grid(True, alpha=0.3) + + axes[1, 1].set_title("Write Latency vs IO Depth") + axes[1, 1].set_xlabel("IO Depth") + axes[1, 1].set_ylabel("Write Latency (ms)") + axes[1, 1].legend() + axes[1, 1].grid(True, alpha=0.3) + + plt.tight_layout() + plt.savefig( + os.path.join(output_dir, "filesystem_iodepth_scaling.png"), + dpi=300, + bbox_inches="tight", + ) + plt.close() + + # 4. Summary Statistics Table + summary_stats = ( + df.groupby("filesystem") + .agg( + { + "total_bw": ["mean", "std", "min", "max"], + "total_iops": ["mean", "std", "min", "max"], + "read_lat_mean": ["mean", "std", "min", "max"], + "write_lat_mean": ["mean", "std", "min", "max"], + } + ) + .round(2) + ) + + # Save summary to CSV + summary_stats.to_csv(os.path.join(output_dir, "filesystem_performance_summary.csv")) + + print(f"Generated multi-filesystem comparison plots in {output_dir}") + print("\nGenerated files:") + print("- filesystem_performance_overview.png") + if "block_size" in df.columns and len(df["block_size"].unique()) > 1: + print("- filesystem_blocksize_heatmaps.png") + if "iodepth" in df.columns and len(df["iodepth"].unique()) > 1: + print("- filesystem_iodepth_scaling.png") + print("- filesystem_performance_summary.csv") + + +def main(): + parser = argparse.ArgumentParser( + description="Generate multi-filesystem comparison plots from fio results" + ) + parser.add_argument( + "results_dir", help="Directory containing fio results from multiple filesystems" + ) + parser.add_argument( + "-o", + "--output-dir", + default=".", + help="Output directory for generated plots (default: current directory)", + ) + parser.add_argument( + "--title", + default="Multi-Filesystem Performance Comparison", + help="Title for the generated plots", + ) + + args = parser.parse_args() + + if not os.path.exists(args.results_dir): + print(f"Error: Results directory {args.results_dir} does not exist") + sys.exit(1) + + # Create output directory if it doesn't exist + os.makedirs(args.output_dir, exist_ok=True) + + # Collect results from all filesystems + print(f"Collecting results from {args.results_dir}...") + df = collect_results(args.results_dir) + + if df.empty: + print("No valid fio results found in the specified directory") + sys.exit(1) + + print( + f"Found {len(df)} fio test results across {len(df['filesystem'].unique())} filesystem configurations" + ) + print(f"Filesystem configurations: {', '.join(df['filesystem'].unique())}") + + # Generate comparison plots + create_filesystem_comparison_plots(df, args.output_dir) + + +if __name__ == "__main__": + main() diff --git a/playbooks/roles/fio-tests/tasks/install-deps/debian/main.yml b/playbooks/roles/fio-tests/tasks/install-deps/debian/main.yml index f269700a..5eefffc4 100644 --- a/playbooks/roles/fio-tests/tasks/install-deps/debian/main.yml +++ b/playbooks/roles/fio-tests/tasks/install-deps/debian/main.yml @@ -4,6 +4,7 @@ name: - fio - python3 + - git state: present become: true diff --git a/playbooks/roles/fio-tests/tasks/install-deps/redhat/main.yml b/playbooks/roles/fio-tests/tasks/install-deps/redhat/main.yml index f9b73944..537b89f4 100644 --- a/playbooks/roles/fio-tests/tasks/install-deps/redhat/main.yml +++ b/playbooks/roles/fio-tests/tasks/install-deps/redhat/main.yml @@ -4,6 +4,7 @@ name: - fio - python3 + - git state: present become: true diff --git a/playbooks/roles/fio-tests/tasks/install-deps/suse/main.yml b/playbooks/roles/fio-tests/tasks/install-deps/suse/main.yml index 861749de..7d5d250f 100644 --- a/playbooks/roles/fio-tests/tasks/install-deps/suse/main.yml +++ b/playbooks/roles/fio-tests/tasks/install-deps/suse/main.yml @@ -4,6 +4,7 @@ name: - fio - python3 + - git state: present become: true diff --git a/playbooks/roles/fio-tests/tasks/main.yaml b/playbooks/roles/fio-tests/tasks/main.yaml index 27093084..252f3032 100644 --- a/playbooks/roles/fio-tests/tasks/main.yaml +++ b/playbooks/roles/fio-tests/tasks/main.yaml @@ -1,104 +1,296 @@ --- +# Install distribution-specific dependencies +- name: Install dependencies + include_tasks: install-deps/main.yml + tags: [ 'setup', 'deps' ] + +- include_role: + name: create_data_partition + tags: [ 'setup', 'data_partition' ] + +- include_role: + name: common + when: + - infer_uid_and_group|bool + +- name: Ensure data_dir has correct ownership + tags: [ 'setup' ] + become: yes + become_method: sudo + ansible.builtin.file: + path: "{{ data_path }}" + owner: "{{ data_user }}" + group: "{{ data_group }}" + recurse: yes + state: directory + +- name: Resolve per-host filesystem configuration + tags: [ 'setup' ] + set_fact: + # Extract filesystem type from hostname (e.g., debian13-fio-tests-xfs-16k -> xfs-16k) + host_fs_config: "{{ inventory_hostname | regex_replace('^.*-fio-tests-(.*)(-dev)?$', '\\1') }}" + # Set filesystem-specific variables based on hostname + fio_tests_fs_type: >- + {{ + 'xfs' if 'xfs' in inventory_hostname else + 'ext4' if 'ext4' in inventory_hostname else + 'btrfs' if 'btrfs' in inventory_hostname else + 'xfs' + }} + fio_tests_fs_config: >- + {{ + 'xfs-16k' if 'xfs-16k' in inventory_hostname else + 'ext4-bigalloc' if 'ext4-bigalloc' in inventory_hostname else + 'btrfs-zstd' if 'btrfs-zstd' in inventory_hostname else + 'xfs-4k' + }} + +- name: Set filesystem-specific mkfs and mount options + tags: [ 'setup' ] + set_fact: + fio_tests_mkfs_type: "{{ fio_tests_fs_type }}" + fio_tests_mkfs_cmd: >- + {{ + '-f -b size=16k -s size=4k -m reflink=1,rmapbt=1 -i sparse=1' if fio_tests_fs_config == 'xfs-16k' else + '-f -b size=4k -s size=4k -m reflink=1,rmapbt=1 -i sparse=1' if fio_tests_fs_config == 'xfs-4k' else + '-F -O bigalloc -C 32k' if fio_tests_fs_config == 'ext4-bigalloc' else + '-f --features no-holes,free-space-tree' if fio_tests_fs_config == 'btrfs-zstd' else + '-f -b size=4k -s size=4k -m reflink=1,rmapbt=1 -i sparse=1' + }} + fio_tests_mount_opts: >- + {{ + 'defaults,compress=zstd:3,space_cache=v2' if fio_tests_fs_config == 'btrfs-zstd' else + 'defaults' + }} + # Ensure filesystem testing is enabled for multi-filesystem configurations + fio_tests_requires_mkfs_device: true + fio_tests_fs_device: "{{ fio_tests_fs_device | default('/dev/disk/by-id/virtio-kdevops2') }}" + fio_tests_fs_mount_point: "{{ fio_tests_fs_mount_point | default('/mnt/fio-tests') }}" + fio_tests_fs_label: "{{ fio_tests_fs_label | default('fio-tests') }}" + - name: Set derived configuration variables - ansible.builtin.set_fact: + tags: [ 'setup' ] + set_fact: fio_tests_block_sizes: >- {{ - (['4k'] if fio_tests_bs_4k else []) + - (['8k'] if fio_tests_bs_8k else []) + - (['16k'] if fio_tests_bs_16k else []) + - (['32k'] if fio_tests_bs_32k else []) + - (['64k'] if fio_tests_bs_64k else []) + - (['128k'] if fio_tests_bs_128k else []) + (['4k'] if fio_tests_bs_4k|default(true) else []) + + (['8k'] if fio_tests_bs_8k|default(true) else []) + + (['16k'] if fio_tests_bs_16k|default(true) else []) + + (['32k'] if fio_tests_bs_32k|default(true) else []) + + (['64k'] if fio_tests_bs_64k|default(true) else []) + + (['128k'] if fio_tests_bs_128k|default(true) else []) + }} + fio_tests_block_ranges: >- + {{ + (['4k-16k'] if fio_tests_bs_range_4k_16k|default(false) else []) + + (['8k-32k'] if fio_tests_bs_range_8k_32k|default(false) else []) + + (['16k-64k'] if fio_tests_bs_range_16k_64k|default(false) else []) + + (['32k-128k'] if fio_tests_bs_range_32k_128k|default(false) else []) }} fio_tests_io_depths: >- {{ - ([1] if fio_tests_iodepth_1 else []) + - ([4] if fio_tests_iodepth_4 else []) + - ([8] if fio_tests_iodepth_8 else []) + - ([16] if fio_tests_iodepth_16 else []) + - ([32] if fio_tests_iodepth_32 else []) + - ([64] if fio_tests_iodepth_64 else []) + ([1] if fio_tests_iodepth_1|default(true) else []) + + ([4] if fio_tests_iodepth_4|default(true) else []) + + ([8] if fio_tests_iodepth_8|default(true) else []) + + ([16] if fio_tests_iodepth_16|default(true) else []) + + ([32] if fio_tests_iodepth_32|default(true) else []) + + ([64] if fio_tests_iodepth_64|default(true) else []) }} fio_tests_num_jobs: >- {{ - ([1] if fio_tests_numjobs_1 else []) + - ([2] if fio_tests_numjobs_2 else []) + - ([4] if fio_tests_numjobs_4 else []) + - ([8] if fio_tests_numjobs_8 else []) + - ([16] if fio_tests_numjobs_16 else []) + ([1] if fio_tests_numjobs_1|default(true) else []) + + ([2] if fio_tests_numjobs_2|default(true) else []) + + ([4] if fio_tests_numjobs_4|default(true) else []) + + ([8] if fio_tests_numjobs_8|default(true) else []) + + ([16] if fio_tests_numjobs_16|default(true) else []) }} fio_tests_patterns: >- {{ - ([{'name': 'randread', 'rw': 'randread', 'rwmixread': 100}] if fio_tests_pattern_rand_read else []) + - ([{'name': 'randwrite', 'rw': 'randwrite', 'rwmixread': 0}] if fio_tests_pattern_rand_write else []) + - ([{'name': 'seqread', 'rw': 'read', 'rwmixread': 100}] if fio_tests_pattern_seq_read else []) + - ([{'name': 'seqwrite', 'rw': 'write', 'rwmixread': 0}] if fio_tests_pattern_seq_write else []) + - ([{'name': 'mixed_75_25', 'rw': 'randrw', 'rwmixread': 75}] if fio_tests_pattern_mixed_75_25 else []) + - ([{'name': 'mixed_50_50', 'rw': 'randrw', 'rwmixread': 50}] if fio_tests_pattern_mixed_50_50 else []) + ([{'name': 'randread', 'rw': 'randread', 'rwmixread': 100}] if fio_tests_pattern_rand_read|default(true) else []) + + ([{'name': 'randwrite', 'rw': 'randwrite', 'rwmixread': 0}] if fio_tests_pattern_rand_write|default(true) else []) + + ([{'name': 'seqread', 'rw': 'read', 'rwmixread': 100}] if fio_tests_pattern_seq_read|default(true) else []) + + ([{'name': 'seqwrite', 'rw': 'write', 'rwmixread': 0}] if fio_tests_pattern_seq_write|default(true) else []) + + ([{'name': 'mixed_75_25', 'rw': 'randrw', 'rwmixread': 75}] if fio_tests_pattern_mixed_75_25|default(true) else []) + + ([{'name': 'mixed_50_50', 'rw': 'randrw', 'rwmixread': 50}] if fio_tests_pattern_mixed_50_50|default(true) else []) }} + # Set effective block sizes based on whether ranges are enabled + fio_tests_effective_block_sizes: "{{ fio_tests_block_sizes }}" -- name: Calculate total test combinations and timeout - ansible.builtin.set_fact: - fio_tests_total_combinations: >- - {{ fio_tests_block_sizes | length * fio_tests_io_depths | length * - fio_tests_num_jobs | length * fio_tests_patterns | length }} - fio_test_time_per_job: "{{ (fio_tests_runtime | int) + (fio_tests_ramp_time | int) }}" - -- name: Calculate async timeout with safety margin - ansible.builtin.set_fact: - # Each test runs twice (JSON + normal), add 60s per test for overhead, add 30% margin - fio_tests_async_timeout: >- - {{ ((fio_tests_total_combinations | int * fio_test_time_per_job | int * 2) + - (fio_tests_total_combinations | int * 60) * 1.3) | int }} - -- name: Display test configuration - ansible.builtin.debug: - msg: | - FIO Test Configuration: - - Total test combinations: {{ fio_tests_total_combinations }} - - Runtime per test: {{ fio_tests_runtime }}s - - Ramp time per test: {{ fio_tests_ramp_time }}s - - Estimated total time: {{ (fio_tests_total_combinations | int * fio_test_time_per_job | int * 2 / 60) | round(1) }} minutes - - Async timeout: {{ (fio_tests_async_timeout | int / 60) | round(1) }} minutes - {% if fio_tests_device == '/dev/null' %} - - Note: Using /dev/null - fsync_on_close and direct IO disabled automatically - {% endif %} - -- name: Install fio and dependencies - ansible.builtin.include_tasks: install-deps/main.yml -- name: Create results directory +- name: Check if {{ fio_tests_fs_device }} is mounted + tags: [ 'setup', 'run_tests' ] + become: yes + become_method: sudo + command: findmnt --noheadings --output TARGET --source {{ fio_tests_fs_device }} + register: mountpoint_stat + failed_when: false + changed_when: false + when: fio_tests_requires_mkfs_device | bool + +- name: Unmount {{ fio_tests_fs_device }} if mounted + tags: [ 'setup', 'run_tests' ] + become: yes + become_method: sudo + command: umount {{ fio_tests_fs_device }} + when: + - fio_tests_requires_mkfs_device | bool + - mountpoint_stat.stdout != "" + +- name: Create filesystem on {{ fio_tests_fs_device }} + tags: [ 'setup' ] + become: yes + become_method: sudo + command: > + mkfs.{{ fio_tests_mkfs_type }} + {{ fio_tests_mkfs_cmd }} + -L {{ fio_tests_fs_label }} + {{ fio_tests_fs_device }} + when: fio_tests_requires_mkfs_device | bool + +- name: Create mount point directory + tags: [ 'setup' ] + become: yes + become_method: sudo ansible.builtin.file: + path: "{{ fio_tests_fs_mount_point }}" + state: directory + mode: '0755' + when: fio_tests_requires_mkfs_device | bool + +- name: Mount filesystem + tags: [ 'setup' ] + become: yes + become_method: sudo + mount: + path: "{{ fio_tests_fs_mount_point }}" + src: "{{ fio_tests_fs_device }}" + fstype: "{{ fio_tests_mkfs_type }}" + opts: "{{ fio_tests_mount_opts }}" + state: mounted + when: fio_tests_requires_mkfs_device | bool + +- name: Set filesystem mount ownership + tags: [ 'setup' ] + become: yes + become_method: sudo + ansible.builtin.file: + path: "{{ fio_tests_fs_mount_point }}" + owner: "{{ data_user }}" + group: "{{ data_group }}" + mode: '0755' + when: fio_tests_requires_mkfs_device | bool + +- name: Create results directory + tags: [ 'setup' ] + file: path: "{{ fio_tests_results_dir }}" state: directory - mode: "0755" - become: true + mode: '0755' + become: yes - name: Create fio job files directory - ansible.builtin.file: + tags: [ 'setup' ] + file: path: "{{ fio_tests_results_dir }}/jobs" state: directory - mode: "0755" - become: true + mode: '0755' + become: yes + +- name: Debug fio test parameters + tags: [ 'setup' ] + debug: + msg: | + fio_tests_patterns: {{ fio_tests_patterns }} + fio_tests_effective_block_sizes: {{ fio_tests_effective_block_sizes }} + fio_tests_io_depths: {{ fio_tests_io_depths }} + fio_tests_num_jobs: {{ fio_tests_num_jobs }} + fio_tests_block_sizes: {{ fio_tests_block_sizes }} + fio_tests_block_ranges: {{ fio_tests_block_ranges }} + fio_tests_enable_bs_ranges: {{ fio_tests_enable_bs_ranges|default(false) }} +- name: Debug effective block sizes calculation + tags: [ 'setup' ] + debug: + msg: | + Calculating effective_block_sizes: + fio_tests_enable_bs_ranges = {{ fio_tests_enable_bs_ranges|default(false) }} + not fio_tests_enable_bs_ranges = {{ not (fio_tests_enable_bs_ranges|default(false)) }} + Should use block_sizes: {{ not (fio_tests_enable_bs_ranges|default(false)) }} + Result: {{ fio_tests_block_sizes if not (fio_tests_enable_bs_ranges|default(false)) else fio_tests_block_ranges }} + +- name: Ensure effective block sizes is set correctly + tags: [ 'setup' ] + set_fact: + fio_tests_effective_block_sizes: "{{ fio_tests_block_sizes }}" + when: fio_tests_effective_block_sizes|length == 0 - name: Generate fio job files - ansible.builtin.template: + tags: [ 'setup' ] + template: src: fio-job.ini.j2 dest: "{{ fio_tests_results_dir }}/jobs/{{ item.0.name }}_bs{{ item.1 }}_iodepth{{ item.2 }}_jobs{{ item.3 }}.ini" - mode: "0644" + mode: '0644' vars: pattern: "{{ item.0 }}" block_size: "{{ item.1 }}" io_depth: "{{ item.2 }}" num_jobs: "{{ item.3 }}" + test_directory: "{{ fio_tests_fs_mount_point if fio_tests_requires_mkfs_device else '' }}" + test_device: "{{ fio_tests_device if not fio_tests_requires_mkfs_device else '' }}" with_nested: - "{{ fio_tests_patterns }}" - - "{{ fio_tests_block_sizes }}" + - "{{ fio_tests_effective_block_sizes }}" - "{{ fio_tests_io_depths }}" - "{{ fio_tests_num_jobs }}" - become: true + become: yes -- name: Run fio tests +- name: Get kernel version + tags: [ 'setup', 'run_tests' ] + ansible.builtin.command: uname -r + register: kernel_version + +- name: Show kernel version + tags: [ 'setup', 'run_tests' ] + debug: + msg: "Kernel version on {{ inventory_hostname }} : {{ kernel_version.stdout }}" + +# XXX: add variability for the different options for the governor +- name: Set CPU governor to performance + tags: [ 'run_tests' ] + become: yes + become_method: sudo ansible.builtin.shell: | + for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do + if [ -f "$cpu" ]; then + echo "performance" > "$cpu" + fi + done + +- name: Drop caches before test + tags: [ 'run_tests' ] + become: yes + become_method: sudo + ansible.builtin.shell: | + sync + echo 3 > /proc/sys/vm/drop_caches + +- name: Check if any job files were generated + tags: [ 'run_tests' ] + become: yes + become_method: sudo + shell: ls {{ fio_tests_results_dir }}/jobs/*.ini 2>/dev/null | wc -l + register: job_file_count + changed_when: false + +- name: Fail if no job files found + tags: [ 'run_tests' ] + fail: + msg: "No fio job files found in {{ fio_tests_results_dir }}/jobs/. Check test parameter configuration." + when: job_file_count.stdout|int == 0 + +- name: Run fio tests in background + tags: [ 'run_tests' ] + become: yes + become_method: sudo + shell: | cd {{ fio_tests_results_dir }}/jobs for job_file in *.ini; do echo "Running test: $job_file" @@ -113,63 +305,113 @@ fio "$job_file" --output="{{ fio_tests_results_dir }}/results_${job_file%.ini}.txt" \ --output-format=normal done - become: true - async: "{{ fio_tests_async_timeout | default(7200) }}" - poll: 30 - changed_when: true + async: 86400 # 24 hours + poll: 0 + register: fio_job + +- name: Wait for fio tests to complete + tags: [ 'run_tests' ] + become: yes + become_method: sudo + ansible.builtin.async_status: + jid: "{{ fio_job.ansible_job_id }}" + register: fio_status + until: fio_status.finished + retries: 1440 # 12 hours + delay: 60 # check every 60 seconds + +- name: Create local results directory + delegate_to: localhost + ansible.builtin.file: + path: "{{ topdir_path }}/workflows/fio-tests/results/{{ inventory_hostname }}/" + state: directory + mode: '0755' + run_once: false + tags: ['results'] -- name: Remove old fio-tests results archive if it exists +- name: Ensure old fio-tests results archive is removed if it exists + become: yes + become_method: sudo ansible.builtin.file: - path: "{{ fio_tests_results_dir }}/fio-tests-results-{{ inventory_hostname }}.tar.gz" + path: "{{ data_path }}/fio-tests-results-{{ inventory_hostname }}.tar.gz" state: absent - tags: ["results"] - become: true + tags: [ 'results' ] - name: Archive fio-tests results directory on remote host - become: true - ansible.builtin.shell: | - cd {{ fio_tests_results_dir }} - tar czf /tmp/fio-tests-results-{{ inventory_hostname }}.tar.gz \ - --exclude='*.tar.gz' \ - results_*.json results_*.txt *.log jobs/ 2>/dev/null || true - mv /tmp/fio-tests-results-{{ inventory_hostname }}.tar.gz {{ fio_tests_results_dir }}/ || true - changed_when: true - tags: ["results"] + become: yes + become_method: sudo + command: > + tar czf {{ data_path }}/fio-tests-results-{{ inventory_hostname }}.tar.gz -C {{ fio_tests_results_dir }} . + args: + creates: "{{ data_path }}/fio-tests-results-{{ inventory_hostname }}.tar.gz" + tags: [ 'results' ] - name: Remove previously fetched fio-tests results archive if it exists - become: false + become: no delegate_to: localhost ansible.builtin.file: path: "{{ item }}" state: absent - tags: ["results"] + tags: [ 'results' ] with_items: - "{{ topdir_path }}/workflows/fio-tests/results/{{ inventory_hostname }}/fio-tests-results-{{ inventory_hostname }}.tar.gz" - "{{ topdir_path }}/workflows/fio-tests/results/{{ inventory_hostname }}/fio-tests-results-{{ inventory_hostname }}" - name: Copy fio-tests results - tags: ["results"] - become: true + tags: [ 'results' ] + become: yes + become_method: sudo ansible.builtin.fetch: - src: "{{ fio_tests_results_dir }}/fio-tests-results-{{ inventory_hostname }}.tar.gz" + src: "{{ data_path }}/fio-tests-results-{{ inventory_hostname }}.tar.gz" dest: "{{ topdir_path }}/workflows/fio-tests/results/{{ inventory_hostname }}/" - flat: true + flat: yes - name: Ensure local fio-tests results extraction directory exists - become: false + become: no delegate_to: localhost ansible.builtin.file: path: "{{ topdir_path }}/workflows/fio-tests/results/{{ inventory_hostname }}/fio-tests-results-{{ inventory_hostname }}" state: directory - mode: "0755" - recurse: true - tags: ["results"] + mode: '0755' + recurse: yes + tags: [ 'results' ] - name: Extract fio-tests results archive locally - become: false + become: no delegate_to: localhost ansible.builtin.unarchive: src: "{{ topdir_path }}/workflows/fio-tests/results/{{ inventory_hostname }}/fio-tests-results-{{ inventory_hostname }}.tar.gz" dest: "{{ topdir_path }}/workflows/fio-tests/results/{{ inventory_hostname }}/fio-tests-results-{{ inventory_hostname }}" - remote_src: false - tags: ["results"] + remote_src: no + tags: [ 'results' ] + +- name: Clean previous fio-tests results on DUTs + tags: [ 'clean' ] + become: yes + become_method: sudo + ansible.builtin.file: + path: "{{ item }}" + state: absent + with_items: + - "{{ fio_tests_results_dir }}" + +- name: Clean previous fio-tests results on localhost + tags: [ 'clean' ] + become: yes + become_method: sudo + delegate_to: localhost + ansible.builtin.file: + path: "{{ item }}" + state: absent + with_items: + - "{{ topdir_path }}/workflows/fio-tests/results/{{ inventory_hostname }}" + +- name: Unmount filesystem after tests + tags: [ 'clean' ] + become: yes + become_method: sudo + mount: + path: "{{ fio_tests_fs_mount_point }}" + state: unmounted + when: fio_tests_requires_mkfs_device | bool + ignore_errors: yes diff --git a/playbooks/roles/fio-tests/templates/fio-job.ini.j2 b/playbooks/roles/fio-tests/templates/fio-job.ini.j2 index 49727d46..b0193dcf 100644 --- a/playbooks/roles/fio-tests/templates/fio-job.ini.j2 +++ b/playbooks/roles/fio-tests/templates/fio-job.ini.j2 @@ -1,29 +1,40 @@ [global] ioengine={{ fio_tests_ioengine }} -{% if fio_tests_device == '/dev/null' %} -direct=0 -{% else %} direct={{ fio_tests_direct | int }} -{% endif %} -{% if fio_tests_device == '/dev/null' %} -fsync_on_close=0 -{% else %} fsync_on_close={{ fio_tests_fsync_on_close | int }} -{% endif %} group_reporting=1 time_based=1 runtime={{ fio_tests_runtime }} ramp_time={{ fio_tests_ramp_time }} [{{ pattern.name }}_bs{{ block_size }}_iodepth{{ io_depth }}_jobs{{ num_jobs }}] -filename={{ fio_tests_device }} -{% if fio_tests_device == '/dev/null' %} +{% if test_directory %} +# Filesystem testing - create files in mounted directory +directory={{ test_directory }} +filename_format=fio-test-$jobnum.$filenum +{% if pattern.rw in ['read', 'randread'] %} +# For read tests, use single 1GB file per job +nrfiles=1 size=1G +{% else %} +# For write tests, use multiple smaller files to avoid space issues +nrfiles=4 +size=256M +{% endif %} +{% else %} +# Block device testing - direct device access +filename={{ test_device }} {% endif %} rw={{ pattern.rw }} {% if pattern.rwmixread is defined and pattern.rw in ['randrw', 'rw'] %} rwmixread={{ pattern.rwmixread }} {% endif %} +{% if '-' in block_size %} +# Block size range (e.g., "4k-16k") +bsrange={{ block_size }} +{% else %} +# Fixed block size bs={{ block_size }} +{% endif %} iodepth={{ io_depth }} numjobs={{ num_jobs }} diff --git a/playbooks/roles/gen_hosts/tasks/main.yml b/playbooks/roles/gen_hosts/tasks/main.yml index 546a0038..0d67e49f 100644 --- a/playbooks/roles/gen_hosts/tasks/main.yml +++ b/playbooks/roles/gen_hosts/tasks/main.yml @@ -240,6 +240,66 @@ state: touch mode: "0755" +- name: Load nodes from nodes file for multi-filesystem setup + include_vars: + file: "{{ topdir_path }}/{{ kdevops_nodes }}" + when: + - kdevops_workflows_dedicated_workflow + - kdevops_workflow_enable_fio_tests + - fio_tests_multi_filesystem|default(false)|bool + - ansible_hosts_template.stat.exists + +- name: Extract node names for multi-filesystem setup + set_fact: + fio_tests_node_names: "{{ guestfs_nodes | map(attribute='name') | list }}" + when: + - kdevops_workflows_dedicated_workflow + - kdevops_workflow_enable_fio_tests + - fio_tests_multi_filesystem|default(false)|bool + - guestfs_nodes is defined + - ansible_hosts_template.stat.exists + +- name: Debug fio_tests_node_names + debug: + var: fio_tests_node_names + when: + - kdevops_workflows_dedicated_workflow + - kdevops_workflow_enable_fio_tests + - ansible_hosts_template.stat.exists + +- name: Generate the Ansible hosts file for a dedicated fio-tests setup (single filesystem) + tags: [ 'hosts' ] + vars: + all_generic_nodes: ["{{ kdevops_host_prefix }}-fio-tests"] + template: + src: "{{ kdevops_hosts_template }}" + dest: "{{ ansible_cfg_inventory }}" + force: yes + trim_blocks: True + lstrip_blocks: True + when: + - kdevops_workflows_dedicated_workflow + - kdevops_workflow_enable_fio_tests + - ansible_hosts_template.stat.exists + - not fio_tests_multi_filesystem|default(false)|bool + +- name: Generate the Ansible hosts file for a dedicated fio-tests setup (multi-filesystem) + tags: [ 'hosts' ] + vars: + all_generic_nodes: "{{ fio_tests_node_names }}" + template: + src: "{{ kdevops_hosts_template }}" + dest: "{{ ansible_cfg_inventory }}" + force: yes + trim_blocks: True + lstrip_blocks: True + when: + - kdevops_workflows_dedicated_workflow + - kdevops_workflow_enable_fio_tests + - ansible_hosts_template.stat.exists + - fio_tests_multi_filesystem|default(false)|bool + - fio_tests_node_names is defined + - name: Generate the Ansible hosts file for a dedicated MinIO setup tags: ['hosts'] ansible.builtin.template: diff --git a/playbooks/roles/gen_hosts/templates/workflows/fio-tests.j2 b/playbooks/roles/gen_hosts/templates/workflows/fio-tests.j2 index 548941a0..0f3550f5 100644 --- a/playbooks/roles/gen_hosts/templates/workflows/fio-tests.j2 +++ b/playbooks/roles/gen_hosts/templates/workflows/fio-tests.j2 @@ -1,4 +1,69 @@ {# Workflow template for fio-tests #} +{% if fio_tests_multi_filesystem|default(false)|bool %} +{# Multi-filesystem section-based hosts #} +[all] +localhost ansible_connection=local +{% for node in all_generic_nodes %} +{{ node }} +{% endfor %} + +[all:vars] +ansible_python_interpreter = "{{ kdevops_python_interpreter }}" + +[baseline] +{% for node in all_generic_nodes %} +{% if not node.endswith('-dev') %} +{{ node }} +{% endif %} +{% endfor %} + +[baseline:vars] +ansible_python_interpreter = "{{ kdevops_python_interpreter }}" + +{% if kdevops_baseline_and_dev %} +[dev] +{% for node in all_generic_nodes %} +{% if node.endswith('-dev') %} +{{ node }} +{% endif %} +{% endfor %} + +[dev:vars] +ansible_python_interpreter = "{{ kdevops_python_interpreter }}" + +{% endif %} +[fio_tests] +{% for node in all_generic_nodes %} +{{ node }} +{% endfor %} + +[fio_tests:vars] +ansible_python_interpreter = "{{ kdevops_python_interpreter }}" + +{# Individual section groups for multi-filesystem testing #} +{% set section_names = [] %} +{% for node in all_generic_nodes %} +{% if not node.endswith('-dev') %} +{% set section = node.replace(kdevops_host_prefix + '-fio-tests-', '') %} +{% if section != kdevops_host_prefix + '-fio-tests' %} +{% if section_names.append(section) %}{% endif %} +{% endif %} +{% endif %} +{% endfor %} + +{% for section in section_names %} +[fio_tests_{{ section | replace('-', '_') }}] +{{ kdevops_host_prefix }}-fio-tests-{{ section }} +{% if kdevops_baseline_and_dev %} +{{ kdevops_host_prefix }}-fio-tests-{{ section }}-dev +{% endif %} + +[fio_tests_{{ section | replace('-', '_') }}:vars] +ansible_python_interpreter = "{{ kdevops_python_interpreter }}" + +{% endfor %} +{% else %} +{# Single filesystem hosts (original behavior) #} [all] localhost ansible_connection=local {{ kdevops_host_prefix }}-fio-tests @@ -36,3 +101,4 @@ ansible_python_interpreter = "{{ kdevops_python_interpreter }}" [service:vars] ansible_python_interpreter = "{{ kdevops_python_interpreter }}" +{% endif %} diff --git a/playbooks/roles/gen_nodes/tasks/main.yml b/playbooks/roles/gen_nodes/tasks/main.yml index 7a98fff4..5eb75f88 100644 --- a/playbooks/roles/gen_nodes/tasks/main.yml +++ b/playbooks/roles/gen_nodes/tasks/main.yml @@ -547,38 +547,116 @@ - kdevops_workflow_enable_sysbench -- name: Generate the fio-tests kdevops nodes file using nodes file using template as jinja2 source template - tags: ["hosts"] +- name: Generate the fio-tests kdevops nodes file using {{ kdevops_nodes_template }} as jinja2 source template + tags: [ 'hosts' ] vars: node_template: "{{ kdevops_nodes_template | basename }}" nodes: "{{ [kdevops_host_prefix + '-fio-tests'] }}" all_generic_nodes: "{{ [kdevops_host_prefix + '-fio-tests'] }}" - ansible.builtin.template: + template: src: "{{ node_template }}" dest: "{{ topdir_path }}/{{ kdevops_nodes }}" - force: true - mode: "0644" + force: yes when: - kdevops_workflows_dedicated_workflow - kdevops_workflow_enable_fio_tests - ansible_nodes_template.stat.exists + - not kdevops_baseline_and_dev + - not fio_tests_multi_filesystem|default(false)|bool - -- name: Generate the fio-tests kdevops nodes file with dev hosts using nodes file using template as jinja2 source template - tags: ["hosts"] +- name: Generate the fio-tests kdevops nodes file with dev hosts using {{ kdevops_nodes_template }} as jinja2 source template + tags: [ 'hosts' ] vars: node_template: "{{ kdevops_nodes_template | basename }}" nodes: "{{ [kdevops_host_prefix + '-fio-tests', kdevops_host_prefix + '-fio-tests-dev'] }}" all_generic_nodes: "{{ [kdevops_host_prefix + '-fio-tests', kdevops_host_prefix + '-fio-tests-dev'] }}" - ansible.builtin.template: + template: src: "{{ node_template }}" dest: "{{ topdir_path }}/{{ kdevops_nodes }}" - force: true - mode: "0644" + force: yes + when: + - kdevops_workflows_dedicated_workflow + - kdevops_workflow_enable_fio_tests + - ansible_nodes_template.stat.exists + - kdevops_baseline_and_dev + - not fio_tests_multi_filesystem|default(false)|bool + +- name: Infer enabled fio-tests multi-filesystem configurations (no dev) + vars: + kdevops_config_data: "{{ lookup('file', topdir_path + '/.config') }}" + # Map configuration options to filesystem node names + xfs_4k_enabled: "{{ 'xfs-4k' if kdevops_config_data | regex_search('^CONFIG_FIO_TESTS_ENABLE_XFS_4K=y$', multiline=True) else '' }}" + xfs_16k_enabled: "{{ 'xfs-16k' if kdevops_config_data | regex_search('^CONFIG_FIO_TESTS_ENABLE_XFS_16K=y$', multiline=True) else '' }}" + xfs_32k_enabled: "{{ 'xfs-32k' if kdevops_config_data | regex_search('^CONFIG_FIO_TESTS_ENABLE_XFS_32K=y$', multiline=True) else '' }}" + xfs_64k_enabled: "{{ 'xfs-64k' if kdevops_config_data | regex_search('^CONFIG_FIO_TESTS_ENABLE_XFS_64K=y$', multiline=True) else '' }}" + ext4_std_enabled: "{{ 'ext4-std' if kdevops_config_data | regex_search('^CONFIG_FIO_TESTS_ENABLE_EXT4_STD=y$', multiline=True) else '' }}" + ext4_bigalloc_enabled: "{{ 'ext4-bigalloc' if kdevops_config_data | regex_search('^CONFIG_FIO_TESTS_ENABLE_EXT4_BIGALLOC=y$', multiline=True) else '' }}" + btrfs_std_enabled: "{{ 'btrfs-std' if kdevops_config_data | regex_search('^CONFIG_FIO_TESTS_ENABLE_BTRFS_STD=y$', multiline=True) else '' }}" + btrfs_zstd_enabled: "{{ 'btrfs-zstd' if kdevops_config_data | regex_search('^CONFIG_FIO_TESTS_ENABLE_BTRFS_ZSTD=y$', multiline=True) else '' }}" + # Collect all enabled filesystem configurations + all_fs_configs: "{{ [xfs_4k_enabled, xfs_16k_enabled, xfs_32k_enabled, xfs_64k_enabled, ext4_std_enabled, ext4_bigalloc_enabled, btrfs_std_enabled, btrfs_zstd_enabled] | select | list }}" + set_fact: + fio_tests_enabled_section_types: "{{ [kdevops_host_prefix + '-fio-tests-'] | product(all_fs_configs) | map('join') | list }}" + when: + - kdevops_workflows_dedicated_workflow + - kdevops_workflow_enable_fio_tests + - fio_tests_multi_filesystem|default(false)|bool + - ansible_nodes_template.stat.exists + - not kdevops_baseline_and_dev + +- name: Infer enabled fio-tests multi-filesystem configurations with dev + vars: + kdevops_config_data: "{{ lookup('file', topdir_path + '/.config') }}" + # Map configuration options to filesystem node names + xfs_4k_enabled: "{{ 'xfs-4k' if kdevops_config_data | regex_search('^CONFIG_FIO_TESTS_ENABLE_XFS_4K=y$', multiline=True) else '' }}" + xfs_16k_enabled: "{{ 'xfs-16k' if kdevops_config_data | regex_search('^CONFIG_FIO_TESTS_ENABLE_XFS_16K=y$', multiline=True) else '' }}" + xfs_32k_enabled: "{{ 'xfs-32k' if kdevops_config_data | regex_search('^CONFIG_FIO_TESTS_ENABLE_XFS_32K=y$', multiline=True) else '' }}" + xfs_64k_enabled: "{{ 'xfs-64k' if kdevops_config_data | regex_search('^CONFIG_FIO_TESTS_ENABLE_XFS_64K=y$', multiline=True) else '' }}" + ext4_std_enabled: "{{ 'ext4-std' if kdevops_config_data | regex_search('^CONFIG_FIO_TESTS_ENABLE_EXT4_STD=y$', multiline=True) else '' }}" + ext4_bigalloc_enabled: "{{ 'ext4-bigalloc' if kdevops_config_data | regex_search('^CONFIG_FIO_TESTS_ENABLE_EXT4_BIGALLOC=y$', multiline=True) else '' }}" + btrfs_std_enabled: "{{ 'btrfs-std' if kdevops_config_data | regex_search('^CONFIG_FIO_TESTS_ENABLE_BTRFS_STD=y$', multiline=True) else '' }}" + btrfs_zstd_enabled: "{{ 'btrfs-zstd' if kdevops_config_data | regex_search('^CONFIG_FIO_TESTS_ENABLE_BTRFS_ZSTD=y$', multiline=True) else '' }}" + # Collect all enabled filesystem configurations + all_fs_configs: "{{ [xfs_4k_enabled, xfs_16k_enabled, xfs_32k_enabled, xfs_64k_enabled, ext4_std_enabled, ext4_bigalloc_enabled, btrfs_std_enabled, btrfs_zstd_enabled] | select | list }}" + set_fact: + fio_tests_expanded_configs: "{{ all_fs_configs }}" + when: + - kdevops_workflows_dedicated_workflow + - kdevops_workflow_enable_fio_tests + - fio_tests_multi_filesystem|default(false)|bool + - ansible_nodes_template.stat.exists + - kdevops_baseline_and_dev + +- name: Create fio-tests nodes for each filesystem configuration with dev hosts + vars: + filesystem_nodes: "{{ [kdevops_host_prefix + '-fio-tests-'] | product(fio_tests_expanded_configs | default([])) | map('join') | list }}" + set_fact: + fio_tests_enabled_section_types: "{{ filesystem_nodes | product(['', '-dev']) | map('join') | list }}" + when: + - kdevops_workflows_dedicated_workflow + - kdevops_workflow_enable_fio_tests + - fio_tests_multi_filesystem|default(false)|bool + - ansible_nodes_template.stat.exists + - kdevops_baseline_and_dev + - fio_tests_expanded_configs is defined + - fio_tests_expanded_configs | length > 0 + +- name: Generate the fio-tests multi-filesystem kdevops nodes file using {{ kdevops_nodes_template }} as jinja2 source template + tags: [ 'hosts' ] + vars: + node_template: "{{ kdevops_nodes_template | basename }}" + nodes: "{{ fio_tests_enabled_section_types | regex_replace('\\[') | regex_replace('\\]') | replace(\"'\", '') | split(', ') }}" + all_generic_nodes: "{{ fio_tests_enabled_section_types }}" + template: + src: "{{ node_template }}" + dest: "{{ topdir_path }}/{{ kdevops_nodes }}" + force: yes when: - kdevops_workflows_dedicated_workflow - kdevops_workflow_enable_fio_tests + - fio_tests_multi_filesystem|default(false)|bool - ansible_nodes_template.stat.exists + - fio_tests_enabled_section_types is defined - name: Infer enabled mmtests test section types diff --git a/workflows/fio-tests/Kconfig b/workflows/fio-tests/Kconfig index 98e7ac63..31f5a4f4 100644 --- a/workflows/fio-tests/Kconfig +++ b/workflows/fio-tests/Kconfig @@ -1,6 +1,38 @@ +config FIO_TESTS_RUNTIME_SET_BY_CLI + bool + output yaml + default $(shell, scripts/check-cli-set-var.sh FIO_TESTS_RUNTIME) + +config FIO_TESTS_RAMP_TIME_SET_BY_CLI + bool + output yaml + default $(shell, scripts/check-cli-set-var.sh FIO_TESTS_RAMP_TIME) + +config FIO_TESTS_QUICK_TEST_SET_BY_CLI + bool + output yaml + default $(shell, scripts/check-cli-set-var.sh FIO_TESTS_QUICK_TEST) + choice prompt "What type of fio testing do you want to run?" - default FIO_TESTS_PERFORMANCE_ANALYSIS + default FIO_TESTS_PERFORMANCE_ANALYSIS if !FIO_TESTS_QUICK_TEST_SET_BY_CLI + default FIO_TESTS_QUICK_TEST if FIO_TESTS_QUICK_TEST_SET_BY_CLI + +config FIO_TESTS_QUICK_TEST + bool "Quick test (minimal runtime for CI/demo)" + output yaml + help + Run minimal fio tests with reduced runtime and test matrix + for quick validation, CI, or demonstration purposes. + This limits test duration to about 1 minute total. + + Test matrix is automatically reduced to: + - Single block size (4K) + - Limited IO depths (1, 4) + - Limited job counts (1, 2) + - Essential patterns (rand_read, rand_write) + - Runtime: 15 seconds + - Ramp time: 3 seconds config FIO_TESTS_PERFORMANCE_ANALYSIS bool "Performance analysis tests" @@ -41,81 +73,221 @@ config FIO_TESTS_MIXED_WORKLOADS Test mixed read/write workloads with various ratios to simulate real-world application patterns. +config FIO_TESTS_FILESYSTEM_TESTS + bool "Filesystem performance tests" + select KDEVOPS_BASELINE_AND_DEV + select FIO_TESTS_REQUIRES_FILESYSTEM + output yaml + help + Test filesystem performance characteristics using a dedicated + filesystem mount rather than direct block device access. + This allows testing of filesystem-specific optimizations, + block size configurations, and I/O patterns. + + Tests are run against various filesystem configurations + including XFS with different block sizes, ext4 with bigalloc, + and btrfs with different features enabled. + + A/B testing is enabled to compare performance across different + filesystem configurations and kernel versions. + +config FIO_TESTS_MULTI_FILESYSTEM + bool "Multi-filesystem comparison tests" + select KDEVOPS_BASELINE_AND_DEV + select FIO_TESTS_REQUIRES_FILESYSTEM + output yaml + help + Test and compare performance across multiple filesystem + configurations simultaneously. This creates separate VMs + for each enabled filesystem configuration and runs + identical workloads across all of them. + + Enables direct comparison between: + - XFS with different block sizes (4K vs 16K vs 32K vs 64K) + - Different filesystems (XFS vs ext4 vs btrfs) + - Various filesystem features and optimizations + + Each configuration gets its own dedicated VM to ensure + isolated testing environments. Results are aggregated + for comprehensive performance comparison graphs. + + A/B testing infrastructure ensures fair comparisons + across kernel versions and configurations. + endchoice config FIO_TESTS_DEVICE string "Device to use for fio testing" output yaml - default "/dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_kdevops2" if LIBVIRT && LIBVIRT_EXTRA_STORAGE_DRIVE_NVME - default "/dev/disk/by-id/virtio-kdevops2" if LIBVIRT && LIBVIRT_EXTRA_STORAGE_DRIVE_VIRTIO - default "/dev/disk/by-id/ata-QEMU_HARDDISK_kdevops2" if LIBVIRT && LIBVIRT_EXTRA_STORAGE_DRIVE_IDE + default "/dev/disk/by-id/virtio-kdevops1" if LIBVIRT && LIBVIRT_EXTRA_STORAGE_DRIVE_VIRTIO + default "/dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_kdevops1" if LIBVIRT && LIBVIRT_EXTRA_STORAGE_DRIVE_NVME + default "/dev/disk/by-id/ata-QEMU_HARDDISK_kdevops1" if LIBVIRT && LIBVIRT_EXTRA_STORAGE_DRIVE_IDE default "/dev/sdc" if LIBVIRT && LIBVIRT_EXTRA_STORAGE_DRIVE_SCSI default "/dev/nvme2n1" if TERRAFORM_AWS_INSTANCE_M5AD_2XLARGE default "/dev/nvme2n1" if TERRAFORM_AWS_INSTANCE_M5AD_4XLARGE default "/dev/nvme1n1" if TERRAFORM_GCE default "/dev/sdd" if TERRAFORM_AZURE default TERRAFORM_OCI_SPARSE_VOLUME_DEVICE_FILE_NAME if TERRAFORM_OCI + default "/dev/null" help The block device to use for fio testing. For CI/testing purposes, /dev/null can be used as a simple target. -config FIO_TESTS_QUICK_SET_BY_CLI +config FIO_TESTS_RUNTIME + string "Test runtime per job" + output yaml + default $(shell, ./scripts/append-makefile-vars.sh $(FIO_TESTS_RUNTIME)) if FIO_TESTS_RUNTIME_SET_BY_CLI + default "15" if FIO_TESTS_QUICK_TEST + default "60" if !FIO_TESTS_RUNTIME_SET_BY_CLI && !FIO_TESTS_QUICK_TEST + help + Runtime in seconds for each fio job. Default is 60 seconds + for comprehensive testing, or 15 seconds for quick tests. + Can be overridden via FIO_TESTS_RUNTIME environment variable. + +config FIO_TESTS_RAMP_TIME + string "Ramp time before measurements" + output yaml + default $(shell, ./scripts/append-makefile-vars.sh $(FIO_TESTS_RAMP_TIME)) if FIO_TESTS_RAMP_TIME_SET_BY_CLI + default "3" if FIO_TESTS_QUICK_TEST + default "10" if !FIO_TESTS_RAMP_TIME_SET_BY_CLI && !FIO_TESTS_QUICK_TEST + help + Time in seconds to ramp up before starting measurements. + Default is 10 seconds for comprehensive testing, or 3 seconds + for quick tests. Can be overridden via FIO_TESTS_RAMP_TIME + environment variable. + +config FIO_TESTS_REQUIRES_FILESYSTEM bool + +if FIO_TESTS_REQUIRES_FILESYSTEM + +source "workflows/fio-tests/Kconfig.fs" + +config FIO_TESTS_FS_DEVICE + string "Device to use for filesystem creation" output yaml - default $(shell, scripts/check-cli-set-var.sh FIO_QUICK) + default "/dev/disk/by-id/virtio-kdevops2" if LIBVIRT && LIBVIRT_EXTRA_STORAGE_DRIVE_VIRTIO + default "/dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_kdevops2" if LIBVIRT && LIBVIRT_EXTRA_STORAGE_DRIVE_NVME + default "/dev/disk/by-id/ata-QEMU_HARDDISK_kdevops2" if LIBVIRT && LIBVIRT_EXTRA_STORAGE_DRIVE_IDE + default "/dev/sdd" if LIBVIRT && LIBVIRT_EXTRA_STORAGE_DRIVE_SCSI + default "/dev/nvme3n1" if TERRAFORM_AWS_INSTANCE_M5AD_2XLARGE + default "/dev/nvme3n1" if TERRAFORM_AWS_INSTANCE_M5AD_4XLARGE + default "/dev/nvme2n1" if TERRAFORM_GCE + default "/dev/sde" if TERRAFORM_AZURE + default "/dev/sdd" + help + The block device to use for creating the filesystem for testing. + This should be the third extra storage drive, separate from the + main device used for raw block device testing. -choice - prompt "FIO test runtime duration" - default FIO_TESTS_RUNTIME_DEFAULT if !FIO_TESTS_QUICK_SET_BY_CLI - default FIO_TESTS_RUNTIME_QUICK if FIO_TESTS_QUICK_SET_BY_CLI +config FIO_TESTS_FS_MOUNT_POINT + string "Filesystem mount point" + output yaml + default "/mnt/fio-tests" + help + The directory where the test filesystem will be mounted. + Test files will be created in this directory. -config FIO_TESTS_RUNTIME_DEFAULT - bool "Default runtime (60 seconds)" +config FIO_TESTS_FS_LABEL + string "Filesystem label" + output yaml + default "fio-tests" help - Use default runtime of 60 seconds per job for comprehensive - performance testing. + The label to use when creating the filesystem. -config FIO_TESTS_RUNTIME_QUICK - bool "Quick runtime (10 seconds)" +endif # FIO_TESTS_REQUIRES_FILESYSTEM + +# Multi-filesystem individual configuration options +if FIO_TESTS_MULTI_FILESYSTEM + +menu "Multi-filesystem parallel testing configuration" + +comment "XFS filesystem block size configurations" +comment "Each enabled option creates a separate VM for parallel testing" + +config FIO_TESTS_ENABLE_XFS_4K + bool "XFS 4K block size" + output yaml + default n help - Use quick runtime of 10 seconds per job for rapid testing - or CI environments. + Create a VM with XFS filesystem using 4K block size. + Configuration: -f -b size=4k -s size=4k -m reflink=1,rmapbt=1 -i sparse=1 -config FIO_TESTS_RUNTIME_CUSTOM_HIGH - bool "Custom high runtime (300 seconds)" +config FIO_TESTS_ENABLE_XFS_16K + bool "XFS 16K block size" + output yaml + default n help - Use extended runtime of 300 seconds per job for thorough - long-duration testing. + Create a VM with XFS filesystem using 16K block size. + Configuration: -f -b size=16k -s size=4k -m reflink=1,rmapbt=1 -i sparse=1 -config FIO_TESTS_RUNTIME_CUSTOM_LOW - bool "Custom low runtime (5 seconds)" +config FIO_TESTS_ENABLE_XFS_32K + bool "XFS 32K block size" + output yaml + default n help - Use minimal runtime of 5 seconds per job for very quick - smoke testing. + Create a VM with XFS filesystem using 32K block size. + Configuration: -f -b size=32k -s size=4k -m reflink=1,rmapbt=1 -i sparse=1 -endchoice +config FIO_TESTS_ENABLE_XFS_64K + bool "XFS 64K block size" + output yaml + default n + help + Create a VM with XFS filesystem using 64K block size. + Configuration: -f -b size=64k -s size=4k -m reflink=1,rmapbt=1 -i sparse=1 -config FIO_TESTS_RUNTIME - string "Test runtime per job" +comment "ext4 filesystem configurations" + +config FIO_TESTS_ENABLE_EXT4_STD + bool "ext4 standard configuration" output yaml - default "60" if FIO_TESTS_RUNTIME_DEFAULT - default "10" if FIO_TESTS_RUNTIME_QUICK - default "300" if FIO_TESTS_RUNTIME_CUSTOM_HIGH - default "5" if FIO_TESTS_RUNTIME_CUSTOM_LOW + default n help - Runtime in seconds for each fio job. + Create a VM with standard ext4 filesystem. + Configuration: -F -config FIO_TESTS_RAMP_TIME - string "Ramp time before measurements" +config FIO_TESTS_ENABLE_EXT4_BIGALLOC + bool "ext4 with bigalloc (32K clusters)" output yaml - default "10" if FIO_TESTS_RUNTIME_DEFAULT - default "2" if FIO_TESTS_RUNTIME_QUICK - default "30" if FIO_TESTS_RUNTIME_CUSTOM_HIGH - default "1" if FIO_TESTS_RUNTIME_CUSTOM_LOW + default n help - Time in seconds to ramp up before starting measurements. - This allows the workload to stabilize before collecting - performance data. + Create a VM with ext4 filesystem using bigalloc feature. + Configuration: -F -O bigalloc -C 32k + +comment "btrfs filesystem configurations" + +config FIO_TESTS_ENABLE_BTRFS_STD + bool "btrfs standard configuration" + output yaml + default n + help + Create a VM with standard btrfs filesystem. + Configuration: -f --features no-holes,free-space-tree + +config FIO_TESTS_ENABLE_BTRFS_ZSTD + bool "btrfs with zstd compression" + output yaml + default n + help + Create a VM with btrfs filesystem using zstd compression. + Configuration: -f --features no-holes,free-space-tree + Mount options: defaults,compress=zstd:3,space_cache=v2 + +endmenu + +config FIO_TESTS_MULTI_FS_COUNT + int + default 0 + default 1 if FIO_TESTS_ENABLE_XFS_4K && !FIO_TESTS_ENABLE_XFS_16K && !FIO_TESTS_ENABLE_XFS_32K && !FIO_TESTS_ENABLE_XFS_64K && !FIO_TESTS_ENABLE_EXT4_STD && !FIO_TESTS_ENABLE_EXT4_BIGALLOC && !FIO_TESTS_ENABLE_BTRFS_STD && !FIO_TESTS_ENABLE_BTRFS_ZSTD + default 2 if (FIO_TESTS_ENABLE_XFS_4K && FIO_TESTS_ENABLE_XFS_16K && !FIO_TESTS_ENABLE_XFS_32K && !FIO_TESTS_ENABLE_XFS_64K && !FIO_TESTS_ENABLE_EXT4_STD && !FIO_TESTS_ENABLE_EXT4_BIGALLOC && !FIO_TESTS_ENABLE_BTRFS_STD && !FIO_TESTS_ENABLE_BTRFS_ZSTD) || (FIO_TESTS_ENABLE_XFS_4K && !FIO_TESTS_ENABLE_XFS_16K && FIO_TESTS_ENABLE_XFS_32K && !FIO_TESTS_ENABLE_XFS_64K && !FIO_TESTS_ENABLE_EXT4_STD && !FIO_TESTS_ENABLE_EXT4_BIGALLOC && !FIO_TESTS_ENABLE_BTRFS_STD && !FIO_TESTS_ENABLE_BTRFS_ZSTD) + default 3 if FIO_TESTS_ENABLE_XFS_16K && FIO_TESTS_ENABLE_EXT4_BIGALLOC && FIO_TESTS_ENABLE_BTRFS_ZSTD && !FIO_TESTS_ENABLE_XFS_4K && !FIO_TESTS_ENABLE_XFS_32K && !FIO_TESTS_ENABLE_XFS_64K && !FIO_TESTS_ENABLE_EXT4_STD && !FIO_TESTS_ENABLE_BTRFS_STD + default 8 + help + Number of enabled multi-filesystem configurations. This is used + internally for validation and configuration generation. + +endif # FIO_TESTS_MULTI_FILESYSTEM menu "Block size configuration" @@ -130,43 +302,85 @@ config FIO_TESTS_BS_4K config FIO_TESTS_BS_8K bool "8K block size tests" output yaml - default y if !FIO_TESTS_QUICK_SET_BY_CLI - default n if FIO_TESTS_QUICK_SET_BY_CLI + default y if !FIO_TESTS_QUICK_TEST + default n if FIO_TESTS_QUICK_TEST help Enable 8K block size testing. config FIO_TESTS_BS_16K bool "16K block size tests" output yaml - default y if !FIO_TESTS_QUICK_SET_BY_CLI - default n if FIO_TESTS_QUICK_SET_BY_CLI + default y if !FIO_TESTS_QUICK_TEST + default n if FIO_TESTS_QUICK_TEST help Enable 16K block size testing. config FIO_TESTS_BS_32K bool "32K block size tests" output yaml - default y if !FIO_TESTS_QUICK_SET_BY_CLI - default n if FIO_TESTS_QUICK_SET_BY_CLI + default n help Enable 32K block size testing. config FIO_TESTS_BS_64K bool "64K block size tests" output yaml - default y if !FIO_TESTS_QUICK_SET_BY_CLI - default n if FIO_TESTS_QUICK_SET_BY_CLI + default n help Enable 64K block size testing. config FIO_TESTS_BS_128K bool "128K block size tests" output yaml - default y if !FIO_TESTS_QUICK_SET_BY_CLI - default n if FIO_TESTS_QUICK_SET_BY_CLI + default n help Enable 128K block size testing. +config FIO_TESTS_ENABLE_BS_RANGES + bool "Enable block size ranges" + output yaml + default n + help + Enable testing with block size ranges instead of fixed sizes. + This allows fio to randomly select block sizes within specified + ranges, providing more realistic workload patterns. + +if FIO_TESTS_ENABLE_BS_RANGES + +config FIO_TESTS_BS_RANGE_4K_16K + bool "4K-16K block size range" + output yaml + default y + help + Enable testing with block sizes ranging from 4K to 16K. + This simulates typical small random I/O patterns. + +config FIO_TESTS_BS_RANGE_8K_32K + bool "8K-32K block size range" + output yaml + default y + help + Enable testing with block sizes ranging from 8K to 32K. + This simulates mixed small to medium I/O patterns. + +config FIO_TESTS_BS_RANGE_16K_64K + bool "16K-64K block size range" + output yaml + default n + help + Enable testing with block sizes ranging from 16K to 64K. + This simulates medium to large I/O patterns. + +config FIO_TESTS_BS_RANGE_32K_128K + bool "32K-128K block size range" + output yaml + default n + help + Enable testing with block sizes ranging from 32K to 128K. + This simulates large I/O patterns typical of sequential workloads. + +endif # FIO_TESTS_ENABLE_BS_RANGES + endmenu menu "IO depth configuration" @@ -181,40 +395,37 @@ config FIO_TESTS_IODEPTH_1 config FIO_TESTS_IODEPTH_4 bool "IO depth 4" output yaml - default y if !FIO_TESTS_QUICK_SET_BY_CLI - default n if FIO_TESTS_QUICK_SET_BY_CLI + default y help Test with IO depth of 4. config FIO_TESTS_IODEPTH_8 bool "IO depth 8" output yaml - default y if !FIO_TESTS_QUICK_SET_BY_CLI - default n if FIO_TESTS_QUICK_SET_BY_CLI + default y if !FIO_TESTS_QUICK_TEST + default n if FIO_TESTS_QUICK_TEST help Test with IO depth of 8. config FIO_TESTS_IODEPTH_16 bool "IO depth 16" output yaml - default y if !FIO_TESTS_QUICK_SET_BY_CLI - default n if FIO_TESTS_QUICK_SET_BY_CLI + default y if !FIO_TESTS_QUICK_TEST + default n if FIO_TESTS_QUICK_TEST help Test with IO depth of 16. config FIO_TESTS_IODEPTH_32 bool "IO depth 32" output yaml - default y if !FIO_TESTS_QUICK_SET_BY_CLI - default n if FIO_TESTS_QUICK_SET_BY_CLI + default n help Test with IO depth of 32. config FIO_TESTS_IODEPTH_64 bool "IO depth 64" output yaml - default y if !FIO_TESTS_QUICK_SET_BY_CLI - default n if FIO_TESTS_QUICK_SET_BY_CLI + default n help Test with IO depth of 64. @@ -232,32 +443,29 @@ config FIO_TESTS_NUMJOBS_1 config FIO_TESTS_NUMJOBS_2 bool "2 jobs" output yaml - default y if !FIO_TESTS_QUICK_SET_BY_CLI - default n if FIO_TESTS_QUICK_SET_BY_CLI + default y help Test with 2 concurrent fio jobs. config FIO_TESTS_NUMJOBS_4 bool "4 jobs" output yaml - default y if !FIO_TESTS_QUICK_SET_BY_CLI - default n if FIO_TESTS_QUICK_SET_BY_CLI + default y if !FIO_TESTS_QUICK_TEST + default n if FIO_TESTS_QUICK_TEST help Test with 4 concurrent fio jobs. config FIO_TESTS_NUMJOBS_8 bool "8 jobs" output yaml - default y if !FIO_TESTS_QUICK_SET_BY_CLI - default n if FIO_TESTS_QUICK_SET_BY_CLI + default n help Test with 8 concurrent fio jobs. config FIO_TESTS_NUMJOBS_16 bool "16 jobs" output yaml - default y if !FIO_TESTS_QUICK_SET_BY_CLI - default n if FIO_TESTS_QUICK_SET_BY_CLI + default n help Test with 16 concurrent fio jobs. @@ -275,40 +483,37 @@ config FIO_TESTS_PATTERN_RAND_READ config FIO_TESTS_PATTERN_RAND_WRITE bool "Random write" output yaml - default y if !FIO_TESTS_QUICK_SET_BY_CLI - default n if FIO_TESTS_QUICK_SET_BY_CLI + default y help Enable random write workload testing. config FIO_TESTS_PATTERN_SEQ_READ bool "Sequential read" output yaml - default y if !FIO_TESTS_QUICK_SET_BY_CLI - default n if FIO_TESTS_QUICK_SET_BY_CLI + default y if !FIO_TESTS_QUICK_TEST + default n if FIO_TESTS_QUICK_TEST help Enable sequential read workload testing. config FIO_TESTS_PATTERN_SEQ_WRITE bool "Sequential write" output yaml - default y if !FIO_TESTS_QUICK_SET_BY_CLI - default n if FIO_TESTS_QUICK_SET_BY_CLI + default y if !FIO_TESTS_QUICK_TEST + default n if FIO_TESTS_QUICK_TEST help Enable sequential write workload testing. config FIO_TESTS_PATTERN_MIXED_75_25 bool "Mixed 75% read / 25% write" output yaml - default y if !FIO_TESTS_QUICK_SET_BY_CLI - default n if FIO_TESTS_QUICK_SET_BY_CLI + default n help Enable mixed workload with 75% reads and 25% writes. config FIO_TESTS_PATTERN_MIXED_50_50 bool "Mixed 50% read / 50% write" output yaml - default y if !FIO_TESTS_QUICK_SET_BY_CLI - default n if FIO_TESTS_QUICK_SET_BY_CLI + default n help Enable mixed workload with 50% reads and 50% writes. @@ -343,9 +548,6 @@ config FIO_TESTS_FSYNC_ON_CLOSE Call fsync() before closing files to ensure data is written to storage. - Note: This is automatically disabled when using /dev/null - as the test device since /dev/null doesn't support fsync. - config FIO_TESTS_RESULTS_DIR string "Results directory" output yaml diff --git a/workflows/fio-tests/Kconfig.btrfs b/workflows/fio-tests/Kconfig.btrfs new file mode 100644 index 00000000..c63d6aff --- /dev/null +++ b/workflows/fio-tests/Kconfig.btrfs @@ -0,0 +1,87 @@ +if FIO_TESTS_FS_BTRFS + +choice + prompt "Btrfs filesystem configuration to use" + default FIO_TESTS_FS_BTRFS_NOHOFSPACE + +config FIO_TESTS_FS_BTRFS_NOHOFSPACE + bool "btrfs no-holes + free-space-tree" + select FIO_TESTS_BTRFS_SECTION_NOHOFSPACE + output yaml + help + Btrfs with both the no-holes and free space tree features enabled. + This is the default modern configuration. + +config FIO_TESTS_FS_BTRFS_NOHOFSPACE_ZSTD + bool "btrfs no-holes + free-space-tree + zstd compression" + select FIO_TESTS_BTRFS_SECTION_NOHOFSPACE_ZSTD + output yaml + help + Btrfs with no-holes, free space tree, and zstd compression enabled. + +config FIO_TESTS_FS_BTRFS_FSPACE + bool "btrfs free-space-tree only" + select FIO_TESTS_BTRFS_SECTION_FSPACE + output yaml + help + Btrfs with free space tree enabled but no-holes disabled. + +config FIO_TESTS_FS_BTRFS_SIMPLE + bool "btrfs simple profile" + select FIO_TESTS_BTRFS_SECTION_SIMPLE + output yaml + help + Btrfs with simple profile for data and metadata. + This profile is used to help ensure compatibility. + +endchoice + +config FIO_TESTS_BTRFS_SECTION_NOHOFSPACE + bool + +config FIO_TESTS_BTRFS_SECTION_NOHOFSPACE_CMD + string + depends on FIO_TESTS_BTRFS_SECTION_NOHOFSPACE + default "-f -O no-holes,free-space-tree" + +config FIO_TESTS_BTRFS_SECTION_NOHOFSPACE_ZSTD + bool + +config FIO_TESTS_BTRFS_SECTION_NOHOFSPACE_ZSTD_CMD + string + depends on FIO_TESTS_BTRFS_SECTION_NOHOFSPACE_ZSTD + default "-f -O no-holes,free-space-tree" + +config FIO_TESTS_BTRFS_SECTION_FSPACE + bool + +config FIO_TESTS_BTRFS_SECTION_FSPACE_CMD + string + depends on FIO_TESTS_BTRFS_SECTION_FSPACE + default "-f -O free-space-tree" + +config FIO_TESTS_BTRFS_SECTION_SIMPLE + bool + +config FIO_TESTS_BTRFS_SECTION_SIMPLE_CMD + string + depends on FIO_TESTS_BTRFS_SECTION_SIMPLE + default "-f" + +config FIO_TESTS_BTRFS_CMD + string + output yaml + default FIO_TESTS_BTRFS_SECTION_NOHOFSPACE_CMD if FIO_TESTS_FS_BTRFS_NOHOFSPACE + default FIO_TESTS_BTRFS_SECTION_NOHOFSPACE_ZSTD_CMD if FIO_TESTS_FS_BTRFS_NOHOFSPACE_ZSTD + default FIO_TESTS_BTRFS_SECTION_FSPACE_CMD if FIO_TESTS_FS_BTRFS_FSPACE + default FIO_TESTS_BTRFS_SECTION_SIMPLE_CMD if FIO_TESTS_FS_BTRFS_SIMPLE + +config FIO_TESTS_BTRFS_MOUNT_OPTS + string + output yaml + default "compress=zstd" if FIO_TESTS_FS_BTRFS_NOHOFSPACE_ZSTD + default "defaults" + help + Mount options for btrfs filesystem. + +endif # FIO_TESTS_FS_BTRFS diff --git a/workflows/fio-tests/Kconfig.ext4 b/workflows/fio-tests/Kconfig.ext4 new file mode 100644 index 00000000..82c67240 --- /dev/null +++ b/workflows/fio-tests/Kconfig.ext4 @@ -0,0 +1,114 @@ +if FIO_TESTS_FS_EXT4 + +choice + prompt "ext4 filesystem configuration to use" + default FIO_TESTS_FS_EXT4_4K_4KS + +config FIO_TESTS_FS_EXT4_4K_4KS + bool "ext4 4k - 4k sector size" + select FIO_TESTS_EXT4_SECTOR_SIZE_4K + select FIO_TESTS_EXT4_SECTION_4K + output yaml + help + ext4 4k FSB with 4k sector size. + +config FIO_TESTS_FS_EXT4_4K_4KS_BIGALLOC_16K + bool "ext4 4k block size bigalloc 16k cluster sizes - 4k sector size" + select FIO_TESTS_EXT4_SECTOR_SIZE_4K + select FIO_TESTS_EXT4_SECTION_4K_BIGALLOC_16K + output yaml + help + ext4 4 KiB FSB with 4 KiB sector size, 16 KiB cluster sizes. + +config FIO_TESTS_FS_EXT4_4K_4KS_BIGALLOC_32K + bool "ext4 4k block size bigalloc 32k cluster sizes - 4k sector size" + select FIO_TESTS_EXT4_SECTOR_SIZE_4K + select FIO_TESTS_EXT4_SECTION_4K_BIGALLOC_32K + output yaml + help + ext4 4 KiB FSB with 4 KiB sector size, 32 KiB cluster sizes. + +config FIO_TESTS_FS_EXT4_4K_4KS_BIGALLOC_64K + bool "ext4 4k block size bigalloc 64k cluster sizes - 4k sector size" + select FIO_TESTS_EXT4_SECTOR_SIZE_4K + select FIO_TESTS_EXT4_SECTION_4K_BIGALLOC_64K + output yaml + help + ext4 4 KiB FSB with 4 KiB sector size, 64 KiB cluster sizes. + +endchoice + +choice + prompt "EXT4 filesystem sector size to use" + default FIO_TESTS_EXT4_SECTOR_SIZE_4K + +config FIO_TESTS_EXT4_SECTOR_SIZE_512 + bool "512 bytes" + output yaml + depends on EXTRA_STORAGE_SUPPORTS_512 + help + Use 512 byte sector size. + +config FIO_TESTS_EXT4_SECTOR_SIZE_4K + bool "4 KiB" + output yaml + depends on EXTRA_STORAGE_SUPPORTS_4K + help + Use 4 KiB sector size. + +endchoice + +config FIO_TESTS_EXT4_SECTOR_SIZE + string + output yaml + default "512" if FIO_TESTS_EXT4_SECTOR_SIZE_512 + default "4k" if FIO_TESTS_EXT4_SECTOR_SIZE_4K + +config FIO_TESTS_EXT4_SECTION_4K + bool + +config FIO_TESTS_EXT4_SECTION_4K_CMD + string + depends on FIO_TESTS_EXT4_SECTION_4K + default "-F -b 4k" + +config FIO_TESTS_EXT4_SECTION_4K_BIGALLOC_16K + bool + +config FIO_TESTS_EXT4_SECTION_4K_BIGALLOC_16K_CMD + string + depends on FIO_TESTS_EXT4_SECTION_4K_BIGALLOC_16K + default "-F -b 4k -O bigalloc -C 16k" + +config FIO_TESTS_EXT4_SECTION_4K_BIGALLOC_32K + bool + +config FIO_TESTS_EXT4_SECTION_4K_BIGALLOC_32K_CMD + string + depends on FIO_TESTS_EXT4_SECTION_4K_BIGALLOC_32K + default "-F -b 4k -O bigalloc -C 32k" + +config FIO_TESTS_EXT4_SECTION_4K_BIGALLOC_64K + bool + +config FIO_TESTS_EXT4_SECTION_4K_BIGALLOC_64K_CMD + string + depends on FIO_TESTS_EXT4_SECTION_4K_BIGALLOC_64K + default "-F -b 4k -O bigalloc -C 64k" + +config FIO_TESTS_EXT4_CMD + string + output yaml + default FIO_TESTS_EXT4_SECTION_4K_CMD if FIO_TESTS_FS_EXT4_4K_4KS + default FIO_TESTS_EXT4_SECTION_4K_BIGALLOC_16K_CMD if FIO_TESTS_FS_EXT4_4K_4KS_BIGALLOC_16K + default FIO_TESTS_EXT4_SECTION_4K_BIGALLOC_32K_CMD if FIO_TESTS_FS_EXT4_4K_4KS_BIGALLOC_32K + default FIO_TESTS_EXT4_SECTION_4K_BIGALLOC_64K_CMD if FIO_TESTS_FS_EXT4_4K_4KS_BIGALLOC_64K + +config FIO_TESTS_EXT4_MOUNT_OPTS + string + output yaml + default "defaults" + help + Mount options for ext4 filesystem. + +endif # FIO_TESTS_FS_EXT4 diff --git a/workflows/fio-tests/Kconfig.fs b/workflows/fio-tests/Kconfig.fs new file mode 100644 index 00000000..2cfdaadc --- /dev/null +++ b/workflows/fio-tests/Kconfig.fs @@ -0,0 +1,75 @@ +config FIO_TESTS_REQUIRES_MKFS_DEVICE + bool + output yaml + +choice + prompt "Filesystem configuration to use" + default FIO_TESTS_FS_XFS + +config FIO_TESTS_FS_SKIP + bool "Skip - don't use a filesystem" + output yaml + help + Disable filesystem testing and use direct block device access. + +config FIO_TESTS_FS_XFS + bool "XFS" + output yaml + select FIO_TESTS_REQUIRES_MKFS_DEVICE + help + Enable if you want to test fio against XFS filesystem. + XFS is a high-performance journaling filesystem with + excellent scalability and large file support. + +config FIO_TESTS_FS_EXT4 + bool "ext4" + output yaml + select FIO_TESTS_REQUIRES_MKFS_DEVICE + help + Enable if you want to test fio against ext4 filesystem. + ext4 is the fourth extended filesystem, commonly used + as the default on many Linux distributions. + +config FIO_TESTS_FS_BTRFS + bool "btrfs" + output yaml + select FIO_TESTS_REQUIRES_MKFS_DEVICE + help + Enable if you want to test fio against btrfs filesystem. + Btrfs is a modern copy-on-write filesystem with advanced + features like snapshots, compression, and checksums. + +endchoice + +if FIO_TESTS_REQUIRES_MKFS_DEVICE + +source "workflows/fio-tests/Kconfig.xfs" +source "workflows/fio-tests/Kconfig.ext4" +source "workflows/fio-tests/Kconfig.btrfs" + +config FIO_TESTS_MKFS_TYPE + string + output yaml + default "xfs" if FIO_TESTS_FS_XFS + default "ext4" if FIO_TESTS_FS_EXT4 + default "btrfs" if FIO_TESTS_FS_BTRFS + +config FIO_TESTS_MKFS_CMD + string "mkfs command to use" + output yaml + default FIO_TESTS_XFS_CMD if FIO_TESTS_FS_XFS + default FIO_TESTS_EXT4_CMD if FIO_TESTS_FS_EXT4 + default FIO_TESTS_BTRFS_CMD if FIO_TESTS_FS_BTRFS + help + The filesystem mkfs configuration command to run + +config FIO_TESTS_MOUNT_OPTS + string "Mount options" + output yaml + default FIO_TESTS_XFS_MOUNT_OPTS if FIO_TESTS_FS_XFS + default FIO_TESTS_EXT4_MOUNT_OPTS if FIO_TESTS_FS_EXT4 + default FIO_TESTS_BTRFS_MOUNT_OPTS if FIO_TESTS_FS_BTRFS + help + The mount options to use when mounting the filesystem + +endif # FIO_TESTS_REQUIRES_MKFS_DEVICE diff --git a/workflows/fio-tests/Kconfig.xfs b/workflows/fio-tests/Kconfig.xfs new file mode 100644 index 00000000..cb3dfed8 --- /dev/null +++ b/workflows/fio-tests/Kconfig.xfs @@ -0,0 +1,170 @@ +if FIO_TESTS_FS_XFS + +choice + prompt "XFS filesystem configuration to use" + default FIO_TESTS_FS_XFS_4K_4KS + +config FIO_TESTS_FS_XFS_4K_4KS + bool "XFS 4k LBS - 4k sector size" + select FIO_TESTS_XFS_SECTOR_SIZE_4K + select FIO_TESTS_XFS_SECTION_REFLINK_4K + output yaml + help + XFS with 4k FSB with 4k sector size. + +config FIO_TESTS_FS_XFS_8K_4KS + bool "XFS 8k LBS - 4k sector size" + select FIO_TESTS_XFS_SECTOR_SIZE_4K + select FIO_TESTS_XFS_SECTION_REFLINK_8K + output yaml + help + XFS with 8k FSB with 4k sector size. + +config FIO_TESTS_FS_XFS_16K_4KS + bool "XFS 16k LBS - 4k sector size" + select FIO_TESTS_XFS_SECTOR_SIZE_4K + select FIO_TESTS_XFS_SECTION_REFLINK_16K + output yaml + help + XFS with 16k FSB with 4k sector size. + +config FIO_TESTS_FS_XFS_32K_4KS + bool "XFS 32k LBS - 4k sector size" + select FIO_TESTS_XFS_SECTOR_SIZE_4K + select FIO_TESTS_XFS_SECTION_REFLINK_32K + output yaml + help + XFS with 32k FSB with 4k sector size. + +config FIO_TESTS_FS_XFS_64K_4KS + bool "XFS 64k LBS - 4k sector size" + select FIO_TESTS_XFS_SECTOR_SIZE_4K + select FIO_TESTS_XFS_SECTION_REFLINK_64K + output yaml + help + XFS with 64k FSB with 4k sector size. + +config FIO_TESTS_FS_XFS_16K_16KS + bool "XFS 16k LBS - 16k sector size" + select FIO_TESTS_XFS_SECTOR_SIZE_16K + select FIO_TESTS_XFS_SECTION_REFLINK_16K + output yaml + help + XFS with 16k FSB with 16k sector size. + +config FIO_TESTS_FS_XFS_32K_16KS + bool "XFS 32k LBS - 16k sector size" + select FIO_TESTS_XFS_SECTOR_SIZE_16K + select FIO_TESTS_XFS_SECTION_REFLINK_32K + output yaml + help + XFS with 32k FSB with 16k sector size. + +config FIO_TESTS_FS_XFS_64K_16KS + bool "XFS 64k LBS - 16k sector size" + select FIO_TESTS_XFS_SECTOR_SIZE_16K + select FIO_TESTS_XFS_SECTION_REFLINK_64K + output yaml + help + XFS with 64k FSB with 16k sector size. + +endchoice + +choice + prompt "XFS filesystem sector size to use" + default FIO_TESTS_XFS_SECTOR_SIZE_4K + +config FIO_TESTS_XFS_SECTOR_SIZE_512 + bool "512 bytes" + output yaml + depends on EXTRA_STORAGE_SUPPORTS_512 + help + Use 512 byte sector size. + +config FIO_TESTS_XFS_SECTOR_SIZE_4K + bool "4 KiB" + output yaml + depends on EXTRA_STORAGE_SUPPORTS_4K + help + Use 4 KiB sector size. + +config FIO_TESTS_XFS_SECTOR_SIZE_16K + bool "16 KiB" + output yaml + depends on EXTRA_STORAGE_SUPPORTS_LARGEIO + help + Use 16 KiB sector size. + +config FIO_TESTS_XFS_SECTOR_SIZE_32K + bool "32 KiB" + output yaml + depends on EXTRA_STORAGE_SUPPORTS_LARGEIO + help + Use 32 KiB sector size. + +endchoice + +config FIO_TESTS_XFS_SECTOR_SIZE + string + output yaml + default "512" if FIO_TESTS_XFS_SECTOR_SIZE_512 + default "4k" if FIO_TESTS_XFS_SECTOR_SIZE_4K + default "16k" if FIO_TESTS_XFS_SECTOR_SIZE_16K + default "32k" if FIO_TESTS_XFS_SECTOR_SIZE_32K + +config FIO_TESTS_XFS_SECTION_REFLINK_4K + bool + +config FIO_TESTS_XFS_SECTION_REFLINK_4K_CMD + string + depends on FIO_TESTS_XFS_SECTION_REFLINK_4K + default "-f -m reflink=1,rmapbt=1, -i sparse=1 -b size=4k" + +config FIO_TESTS_XFS_SECTION_REFLINK_8K + bool + +config FIO_TESTS_XFS_SECTION_REFLINK_8K_CMD + string + depends on FIO_TESTS_XFS_SECTION_REFLINK_8K + default "-f -m reflink=1,rmapbt=1 -i sparse=1 -b size=8k" + +config FIO_TESTS_XFS_SECTION_REFLINK_16K + bool + +config FIO_TESTS_XFS_SECTION_REFLINK_16K_CMD + string + depends on FIO_TESTS_XFS_SECTION_REFLINK_16K + default "-f -m reflink=1,rmapbt=1 -i sparse=1 -b size=16k" + +config FIO_TESTS_XFS_SECTION_REFLINK_32K + bool + +config FIO_TESTS_XFS_SECTION_REFLINK_32K_CMD + string + depends on FIO_TESTS_XFS_SECTION_REFLINK_32K + default "-f -m reflink=1,rmapbt=1 -i sparse=1 -b size=32k" + +config FIO_TESTS_XFS_SECTION_REFLINK_64K + bool + +config FIO_TESTS_XFS_SECTION_REFLINK_64K_CMD + string + depends on FIO_TESTS_XFS_SECTION_REFLINK_64K + default "-f -m reflink=1,rmapbt=1 -i sparse=1 -b size=64k" + +config FIO_TESTS_XFS_CMD + string + default FIO_TESTS_XFS_SECTION_REFLINK_4K_CMD if FIO_TESTS_FS_XFS_4K_4KS + default FIO_TESTS_XFS_SECTION_REFLINK_8K_CMD if FIO_TESTS_XFS_SECTION_REFLINK_8K + default FIO_TESTS_XFS_SECTION_REFLINK_16K_CMD if FIO_TESTS_XFS_SECTION_REFLINK_16K + default FIO_TESTS_XFS_SECTION_REFLINK_32K_CMD if FIO_TESTS_XFS_SECTION_REFLINK_32K + default FIO_TESTS_XFS_SECTION_REFLINK_64K_CMD if FIO_TESTS_XFS_SECTION_REFLINK_64K + +config FIO_TESTS_XFS_MOUNT_OPTS + string + output yaml + default "defaults" + help + Mount options for XFS filesystem. + +endif # FIO_TESTS_FS_XFS diff --git a/workflows/fio-tests/Makefile b/workflows/fio-tests/Makefile index 218cfbfc..28ef44fd 100644 --- a/workflows/fio-tests/Makefile +++ b/workflows/fio-tests/Makefile @@ -1,53 +1,72 @@ - fio-tests: $(Q)ansible-playbook $(ANSIBLE_VERBOSE) \ + --connection=ssh \ + --inventory hosts \ --extra-vars=@$(KDEVOPS_EXTRA_VARS) \ - playbooks/fio-tests.yml \ - $(LIMIT_HOSTS) + playbooks/fio-tests.yml fio-tests-baseline: $(Q)ansible-playbook $(ANSIBLE_VERBOSE) \ + --connection=ssh \ + --inventory hosts \ --extra-vars=@$(KDEVOPS_EXTRA_VARS) \ - playbooks/fio-tests-baseline.yml \ - $(LIMIT_HOSTS) + playbooks/fio-tests-baseline.yml fio-tests-results: $(Q)ansible-playbook $(ANSIBLE_VERBOSE) \ + --connection=ssh \ + --inventory hosts \ --extra-vars=@$(KDEVOPS_EXTRA_VARS) \ - playbooks/fio-tests.yml \ - --tags results \ - $(LIMIT_HOSTS) + playbooks/fio-tests-results.yml fio-tests-graph: $(Q)ansible-playbook $(ANSIBLE_VERBOSE) \ + --connection=local \ + --inventory hosts \ --extra-vars=@$(KDEVOPS_EXTRA_VARS) \ - playbooks/fio-tests-graph.yml \ - $(LIMIT_HOSTS) + playbooks/fio-tests-graph.yml fio-tests-compare: $(Q)ansible-playbook $(ANSIBLE_VERBOSE) \ + --connection=ssh \ + --inventory hosts \ --extra-vars=@$(KDEVOPS_EXTRA_VARS) \ - playbooks/fio-tests-compare.yml \ - $(LIMIT_HOSTS) + playbooks/fio-tests-compare.yml fio-tests-trend-analysis: $(Q)ansible-playbook $(ANSIBLE_VERBOSE) \ + --connection=ssh \ + --inventory hosts \ --extra-vars=@$(KDEVOPS_EXTRA_VARS) \ - playbooks/fio-tests-trend-analysis.yml \ - $(LIMIT_HOSTS) + playbooks/fio-tests-trend-analysis.yml + +fio-tests-multi-fs-compare: + $(Q)ansible-playbook $(ANSIBLE_VERBOSE) \ + --connection=local \ + --inventory hosts \ + --extra-vars=@$(KDEVOPS_EXTRA_VARS) \ + playbooks/fio-tests-multi-fs-compare.yml + +fio-tests-clean-results: + @echo "Cleaning fio-tests results directory..." + $(Q)rm -rf $(TOPDIR)/workflows/fio-tests/results/ + @echo "fio-tests results directory cleaned" -fio-tests-estimate: - $(Q)python3 scripts/workflows/fio-tests/estimate-runtime.py +# Add results cleanup to destroy dependencies when fio-tests workflow is enabled +ifeq (y,$(CONFIG_KDEVOPS_WORKFLOW_ENABLE_FIO_TESTS)) +KDEVOPS_DESTROY_DEPS += fio-tests-clean-results +endif fio-tests-help-menu: @echo "fio-tests options:" - @echo "fio-tests - run fio performance tests" - @echo "fio-tests-baseline - establish baseline results" - @echo "fio-tests-results - collect results from target nodes to localhost" - @echo "fio-tests-graph - generate performance graphs on localhost" - @echo "fio-tests-compare - compare baseline vs dev results" - @echo "fio-tests-trend-analysis - analyze performance trends" - @echo "fio-tests-estimate - estimate runtime for current configuration" + @echo "fio-tests - run fio performance tests" + @echo "fio-tests-baseline - establish baseline results" + @echo "fio-tests-results - collect and analyze results" + @echo "fio-tests-graph - generate performance graphs" + @echo "fio-tests-compare - compare baseline vs dev results" + @echo "fio-tests-trend-analysis - analyze performance trends" + @echo "fio-tests-multi-fs-compare - compare results across multiple filesystems" + @echo "fio-tests-clean-results - clean results directory" @echo "" HELP_TARGETS += fio-tests-help-menu diff --git a/workflows/fio-tests/scripts/generate_comparison_graphs.py b/workflows/fio-tests/scripts/generate_comparison_graphs.py new file mode 100755 index 00000000..c852ec82 --- /dev/null +++ b/workflows/fio-tests/scripts/generate_comparison_graphs.py @@ -0,0 +1,605 @@ +#!/usr/bin/env python3 +""" +Multi-filesystem performance comparison graph generator for fio-tests +Generates comparative visualizations across XFS, ext4, and btrfs filesystems +""" + +import json +import os +import glob +import matplotlib.pyplot as plt +import matplotlib.patches as patches +import numpy as np +from pathlib import Path +import sys + + +def load_fio_results(results_dir): + """Load and parse fio JSON results from all filesystem directories""" + + filesystems = { + "XFS (16K blocks)": "debian13-fio-tests-xfs-16k", + "ext4 (bigalloc, 32K)": "debian13-fio-tests-ext4-bigalloc", + "btrfs (zstd compression)": "debian13-fio-tests-btrfs-zstd", + } + + results = {} + + for fs_name, dir_name in filesystems.items(): + fs_dir = os.path.join(results_dir, dir_name) + if not os.path.exists(fs_dir): + print(f"Warning: Directory {fs_dir} not found") + continue + + results[fs_name] = {} + + # Find all JSON result files + json_files = glob.glob(os.path.join(fs_dir, "results_*.json")) + json_files = [ + f for f in json_files if not f.endswith("results_*.json") + ] # Skip literal wildcards + + for json_file in json_files: + try: + with open(json_file, "r") as f: + data = json.load(f) + + # Extract test parameters from filename + basename = os.path.basename(json_file) + test_name = basename.replace("results_", "").replace(".json", "") + + # Parse test parameters + parts = test_name.split("_") + if len(parts) >= 4: + pattern = parts[0] + block_size = parts[1].replace("bs", "") + io_depth = parts[2].replace("iodepth", "") + num_jobs = parts[3].replace("jobs", "") + + # Extract performance metrics + if "jobs" in data and len(data["jobs"]) > 0: + job = data["jobs"][0] + + # Get read/write metrics based on pattern + if "read" in pattern: + metrics = job.get("read", {}) + elif "write" in pattern: + metrics = job.get("write", {}) + else: + metrics = job.get("read", {}) # Default to read + + test_key = f"{pattern}_{block_size}_{io_depth}_{num_jobs}" + results[fs_name][test_key] = { + "pattern": pattern, + "block_size": block_size, + "io_depth": int(io_depth), + "num_jobs": int(num_jobs), + "iops": metrics.get("iops", 0), + "bandwidth_kbs": metrics.get("bw", 0), + "bandwidth_mbs": metrics.get("bw", 0) / 1024.0, + "latency_us": ( + job.get("lat_ns", {}).get("mean", 0) / 1000.0 + if job.get("lat_ns") + else 0 + ), + } + + except Exception as e: + print(f"Error processing {json_file}: {e}") + continue + + return results + + +def create_comparison_bar_chart(results, output_dir): + """Create a bar chart comparing IOPS across filesystems for key tests""" + + # Select key test scenarios for comparison + key_tests = [ + "randread_16k_1_1", + "randwrite_16k_1_1", + "seqread_16k_1_1", + "seqwrite_16k_1_1", + "randread_16k_32_1", + "randwrite_16k_32_1", + ] + + test_labels = [ + "Random Read\n16K, iodepth=1", + "Random Write\n16K, iodepth=1", + "Sequential Read\n16K, iodepth=1", + "Sequential Write\n16K, iodepth=1", + "Random Read\n16K, iodepth=32", + "Random Write\n16K, iodepth=32", + ] + + fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(15, 12)) + + # IOPS Comparison + filesystems = list(results.keys()) + x = np.arange(len(key_tests)) + width = 0.25 + + colors = ["#1f77b4", "#ff7f0e", "#2ca02c"] # Blue, Orange, Green + + for i, fs in enumerate(filesystems): + iops_values = [] + for test in key_tests: + if test in results[fs]: + iops_values.append(results[fs][test]["iops"]) + else: + iops_values.append(0) + + bars = ax1.bar( + x + i * width, iops_values, width, label=fs, color=colors[i], alpha=0.8 + ) + + # Add value labels on bars + for bar, value in zip(bars, iops_values): + if value > 0: + ax1.text( + bar.get_x() + bar.get_width() / 2, + bar.get_height() + max(iops_values) * 0.01, + f"{value:.0f}", + ha="center", + va="bottom", + fontsize=9, + rotation=0, + ) + + ax1.set_xlabel("Test Scenarios") + ax1.set_ylabel("IOPS") + ax1.set_title( + "Multi-Filesystem Performance Comparison - IOPS", fontsize=14, fontweight="bold" + ) + ax1.set_xticks(x + width) + ax1.set_xticklabels(test_labels, rotation=45, ha="right") + ax1.legend() + ax1.grid(True, alpha=0.3) + + # Bandwidth Comparison + for i, fs in enumerate(filesystems): + bw_values = [] + for test in key_tests: + if test in results[fs]: + bw_values.append(results[fs][test]["bandwidth_mbs"]) + else: + bw_values.append(0) + + bars = ax2.bar( + x + i * width, bw_values, width, label=fs, color=colors[i], alpha=0.8 + ) + + # Add value labels on bars + for bar, value in zip(bars, bw_values): + if value > 0: + ax2.text( + bar.get_x() + bar.get_width() / 2, + bar.get_height() + max(bw_values) * 0.01, + f"{value:.1f}", + ha="center", + va="bottom", + fontsize=9, + rotation=0, + ) + + ax2.set_xlabel("Test Scenarios") + ax2.set_ylabel("Bandwidth (MB/s)") + ax2.set_title( + "Multi-Filesystem Performance Comparison - Bandwidth", + fontsize=14, + fontweight="bold", + ) + ax2.set_xticks(x + width) + ax2.set_xticklabels(test_labels, rotation=45, ha="right") + ax2.legend() + ax2.grid(True, alpha=0.3) + + plt.tight_layout() + output_file = os.path.join(output_dir, "multi_filesystem_comparison.png") + plt.savefig(output_file, dpi=300, bbox_inches="tight") + print(f"Generated: {output_file}") + plt.close() + + +def create_block_size_comparison(results, output_dir): + """Create comparison across different block sizes""" + + fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(16, 12)) + + # Block sizes to compare + block_sizes = ["4k", "16k", "64k"] + patterns = ["randread", "randwrite", "seqread", "seqwrite"] + pattern_titles = [ + "Random Read", + "Random Write", + "Sequential Read", + "Sequential Write", + ] + axes = [ax1, ax2, ax3, ax4] + + colors = ["#1f77b4", "#ff7f0e", "#2ca02c"] + filesystems = list(results.keys()) + + for ax, pattern, title in zip(axes, patterns, pattern_titles): + x = np.arange(len(block_sizes)) + width = 0.25 + + for i, fs in enumerate(filesystems): + iops_values = [] + for bs in block_sizes: + test_key = f"{pattern}_{bs}_1_1" # iodepth=1, jobs=1 + if test_key in results[fs]: + iops_values.append(results[fs][test_key]["iops"]) + else: + iops_values.append(0) + + bars = ax.bar( + x + i * width, iops_values, width, label=fs, color=colors[i], alpha=0.8 + ) + + # Add value labels + for bar, value in zip(bars, iops_values): + if value > 0: + ax.text( + bar.get_x() + bar.get_width() / 2, + bar.get_height() + max(iops_values) * 0.01, + f"{value:.0f}", + ha="center", + va="bottom", + fontsize=8, + ) + + ax.set_xlabel("Block Size") + ax.set_ylabel("IOPS") + ax.set_title(f"{title} - Block Size Impact", fontweight="bold") + ax.set_xticks(x + width) + ax.set_xticklabels(block_sizes) + ax.legend() + ax.grid(True, alpha=0.3) + + plt.suptitle( + "Block Size Performance Impact Across Filesystems", + fontsize=16, + fontweight="bold", + ) + plt.tight_layout() + + output_file = os.path.join(output_dir, "block_size_comparison.png") + plt.savefig(output_file, dpi=300, bbox_inches="tight") + print(f"Generated: {output_file}") + plt.close() + + +def create_iodepth_scaling(results, output_dir): + """Create IO depth scaling comparison""" + + fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(16, 12)) + + io_depths = [1, 8, 32] + patterns = ["randread", "randwrite", "seqread", "seqwrite"] + pattern_titles = [ + "Random Read", + "Random Write", + "Sequential Read", + "Sequential Write", + ] + axes = [ax1, ax2, ax3, ax4] + + colors = ["#1f77b4", "#ff7f0e", "#2ca02c"] + filesystems = list(results.keys()) + + for ax, pattern, title in zip(axes, patterns, pattern_titles): + for i, fs in enumerate(filesystems): + iops_values = [] + for depth in io_depths: + test_key = f"{pattern}_16k_{depth}_1" # 16k blocks, 1 job + if test_key in results[fs]: + iops_values.append(results[fs][test_key]["iops"]) + else: + iops_values.append(0) + + ax.plot( + io_depths, + iops_values, + marker="o", + linewidth=2, + markersize=8, + label=fs, + color=colors[i], + ) + + # Add value labels + for x, y in zip(io_depths, iops_values): + if y > 0: + ax.annotate( + f"{y:.0f}", + (x, y), + textcoords="offset points", + xytext=(0, 10), + ha="center", + fontsize=8, + ) + + ax.set_xlabel("IO Depth") + ax.set_ylabel("IOPS") + ax.set_title(f"{title} - IO Depth Scaling", fontweight="bold") + ax.set_xscale("log", base=2) + ax.set_xticks(io_depths) + ax.set_xticklabels(io_depths) + ax.legend() + ax.grid(True, alpha=0.3) + + plt.suptitle("IO Depth Scaling Across Filesystems", fontsize=16, fontweight="bold") + plt.tight_layout() + + output_file = os.path.join(output_dir, "iodepth_scaling.png") + plt.savefig(output_file, dpi=300, bbox_inches="tight") + print(f"Generated: {output_file}") + plt.close() + + +def create_summary_dashboard(results, output_dir): + """Create a comprehensive dashboard with key metrics""" + + fig = plt.figure(figsize=(20, 12)) + gs = fig.add_gridspec(3, 4, hspace=0.3, wspace=0.3) + + # Main comparison (top row, spans 2 columns) + ax_main = fig.add_subplot(gs[0, :2]) + + # Performance ranking for random read 16k + filesystems = list(results.keys()) + test_key = "randread_16k_1_1" + + iops_values = [] + bw_values = [] + for fs in filesystems: + if test_key in results[fs]: + iops_values.append(results[fs][test_key]["iops"]) + bw_values.append(results[fs][test_key]["bandwidth_mbs"]) + else: + iops_values.append(0) + bw_values.append(0) + + # Create ranking bar chart + y_pos = np.arange(len(filesystems)) + colors = ["#1f77b4", "#ff7f0e", "#2ca02c"] + + bars = ax_main.barh(y_pos, iops_values, color=colors, alpha=0.8) + ax_main.set_yticks(y_pos) + ax_main.set_yticklabels([fs.split(" (")[0] for fs in filesystems]) + ax_main.set_xlabel("IOPS") + ax_main.set_title( + "Random Read Performance (16K blocks, iodepth=1)", + fontweight="bold", + fontsize=14, + ) + + # Add value labels + for bar, value in zip(bars, iops_values): + ax_main.text( + bar.get_width() + max(iops_values) * 0.01, + bar.get_y() + bar.get_height() / 2, + f"{value:.0f}", + ha="left", + va="center", + fontweight="bold", + ) + + ax_main.grid(True, alpha=0.3) + + # Performance matrix (top right) + ax_matrix = fig.add_subplot(gs[0, 2:]) + + # Create performance matrix + patterns = ["randread", "randwrite", "seqread", "seqwrite"] + matrix_data = [] + + for fs in filesystems: + fs_row = [] + for pattern in patterns: + test_key = f"{pattern}_16k_1_1" + if test_key in results[fs]: + fs_row.append(results[fs][test_key]["iops"]) + else: + fs_row.append(0) + matrix_data.append(fs_row) + + im = ax_matrix.imshow(matrix_data, cmap="YlOrRd", aspect="auto") + ax_matrix.set_xticks(range(len(patterns))) + ax_matrix.set_xticklabels( + ["Rand Read", "Rand Write", "Seq Read", "Seq Write"], rotation=45 + ) + ax_matrix.set_yticks(range(len(filesystems))) + ax_matrix.set_yticklabels([fs.split(" (")[0] for fs in filesystems]) + ax_matrix.set_title("Performance Heatmap (IOPS)", fontweight="bold") + + # Add text annotations + for i in range(len(filesystems)): + for j in range(len(patterns)): + text = ax_matrix.text( + j, + i, + f"{matrix_data[i][j]:.0f}", + ha="center", + va="center", + color="black", + fontweight="bold", + ) + + # Block size comparison (bottom left) + ax_bs = fig.add_subplot(gs[1, :2]) + + block_sizes = ["4k", "16k", "64k"] + x = np.arange(len(block_sizes)) + width = 0.25 + + for i, fs in enumerate(filesystems): + iops_values = [] + for bs in block_sizes: + test_key = f"randread_{bs}_1_1" + if test_key in results[fs]: + iops_values.append(results[fs][test_key]["iops"]) + else: + iops_values.append(0) + + ax_bs.bar( + x + i * width, + iops_values, + width, + label=fs.split(" (")[0], + color=colors[i], + alpha=0.8, + ) + + ax_bs.set_xlabel("Block Size") + ax_bs.set_ylabel("IOPS") + ax_bs.set_title("Random Read - Block Size Impact", fontweight="bold") + ax_bs.set_xticks(x + width) + ax_bs.set_xticklabels(block_sizes) + ax_bs.legend() + ax_bs.grid(True, alpha=0.3) + + # IO depth scaling (bottom right) + ax_depth = fig.add_subplot(gs[1, 2:]) + + io_depths = [1, 8, 32] + for i, fs in enumerate(filesystems): + iops_values = [] + for depth in io_depths: + test_key = f"randread_16k_{depth}_1" + if test_key in results[fs]: + iops_values.append(results[fs][test_key]["iops"]) + else: + iops_values.append(0) + + ax_depth.plot( + io_depths, + iops_values, + marker="o", + linewidth=3, + markersize=8, + label=fs.split(" (")[0], + color=colors[i], + ) + + ax_depth.set_xlabel("IO Depth") + ax_depth.set_ylabel("IOPS") + ax_depth.set_title("Random Read - IO Depth Scaling", fontweight="bold") + ax_depth.set_xscale("log", base=2) + ax_depth.set_xticks(io_depths) + ax_depth.set_xticklabels(io_depths) + ax_depth.legend() + ax_depth.grid(True, alpha=0.3) + + # Summary statistics (bottom row) + ax_summary = fig.add_subplot(gs[2, :]) + ax_summary.axis("off") + + # Create summary table + summary_text = "=== Performance Summary ===\n\n" + + # Random read comparison + test_key = "randread_16k_1_1" + performances = [] + for fs in filesystems: + if test_key in results[fs]: + performances.append( + ( + fs, + results[fs][test_key]["iops"], + results[fs][test_key]["bandwidth_mbs"], + ) + ) + + # Sort by IOPS + performances.sort(key=lambda x: x[1], reverse=True) + + summary_text += "Random Read Performance Ranking (16K blocks):\n" + for i, (fs, iops, bw) in enumerate(performances): + fs_short = fs.split(" (")[0] + if i == 0: + summary_text += f"🥇 {fs_short}: {iops:.0f} IOPS, {bw:.1f} MB/s\n" + elif i == 1: + pct_diff = ((performances[0][1] - iops) / performances[0][1]) * 100 + summary_text += ( + f"🥈 {fs_short}: {iops:.0f} IOPS, {bw:.1f} MB/s (-{pct_diff:.1f}%)\n" + ) + else: + pct_diff = ((performances[0][1] - iops) / performances[0][1]) * 100 + summary_text += ( + f"🥉 {fs_short}: {iops:.0f} IOPS, {bw:.1f} MB/s (-{pct_diff:.1f}%)\n" + ) + + summary_text += ( + f"\nTest Infrastructure: 6 VMs (3 baseline + 3 dev for A/B testing)\n" + ) + summary_text += f"Total Tests Executed: {sum(len(fs_results) for fs_results in results.values())} across all filesystems" + + ax_summary.text( + 0.05, + 0.5, + summary_text, + transform=ax_summary.transAxes, + fontsize=12, + verticalalignment="center", + fontfamily="monospace", + bbox=dict(boxstyle="round,pad=0.5", facecolor="lightgray", alpha=0.8), + ) + + plt.suptitle( + "kdevops Multi-Filesystem Performance Dashboard", + fontsize=18, + fontweight="bold", + y=0.98, + ) + + output_file = os.path.join(output_dir, "performance_dashboard.png") + plt.savefig(output_file, dpi=300, bbox_inches="tight") + print(f"Generated: {output_file}") + plt.close() + + +def main(): + if len(sys.argv) != 2: + print("Usage: python3 generate_comparison_graphs.py ") + sys.exit(1) + + results_dir = sys.argv[1] + + if not os.path.exists(results_dir): + print(f"Error: Results directory {results_dir} not found") + sys.exit(1) + + print("Loading fio results...") + results = load_fio_results(results_dir) + + if not results: + print("No results found!") + sys.exit(1) + + print(f"Loaded results for {len(results)} filesystems:") + for fs, tests in results.items(): + print(f" {fs}: {len(tests)} tests") + + # Create output directory for graphs + graphs_dir = os.path.join(results_dir, "graphs") + os.makedirs(graphs_dir, exist_ok=True) + + print("Generating graphs...") + + # Generate all comparison graphs + create_comparison_bar_chart(results, graphs_dir) + create_block_size_comparison(results, graphs_dir) + create_iodepth_scaling(results, graphs_dir) + create_summary_dashboard(results, graphs_dir) + + print(f"\nAll graphs generated in: {graphs_dir}") + print("\nGenerated files:") + for graph_file in glob.glob(os.path.join(graphs_dir, "*.png")): + print(f" - {os.path.basename(graph_file)}") + + +if __name__ == "__main__": + main() diff --git a/workflows/fio-tests/scripts/generate_comprehensive_analysis.py b/workflows/fio-tests/scripts/generate_comprehensive_analysis.py new file mode 100644 index 00000000..a1253844 --- /dev/null +++ b/workflows/fio-tests/scripts/generate_comprehensive_analysis.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python3 +""" +Generate comprehensive analysis of multi-filesystem fio test results +""" + +import json +import os +import glob +from pathlib import Path + + +def load_and_analyze_results(results_dir): + """Load all results and create comprehensive analysis""" + + filesystems = { + "XFS (16K blocks)": "debian13-fio-tests-xfs-16k", + "ext4 (bigalloc, 32K)": "debian13-fio-tests-ext4-bigalloc", + "btrfs (zstd compression)": "debian13-fio-tests-btrfs-zstd", + } + + analysis = {"filesystems": {}, "comparisons": {}} + + # Load results for each filesystem + for fs_name, dir_name in filesystems.items(): + fs_dir = os.path.join(results_dir, dir_name) + if not os.path.exists(fs_dir): + continue + + analysis["filesystems"][fs_name] = {} + + # Find all JSON result files + json_files = glob.glob(os.path.join(fs_dir, "results_*.json")) + json_files = [f for f in json_files if not f.endswith("results_*.json")] + + for json_file in json_files: + try: + with open(json_file, "r") as f: + data = json.load(f) + + basename = os.path.basename(json_file) + test_name = basename.replace("results_", "").replace(".json", "") + + # Parse test parameters + parts = test_name.split("_") + if len(parts) >= 4: + pattern = parts[0] + block_size = parts[1].replace("bs", "") + io_depth = parts[2].replace("iodepth", "") + num_jobs = parts[3].replace("jobs", "") + + if "jobs" in data and len(data["jobs"]) > 0: + job = data["jobs"][0] + + # Get read metrics + read_metrics = job.get("read", {}) + + test_key = f"{pattern}_{block_size}_{io_depth}_{num_jobs}" + analysis["filesystems"][fs_name][test_key] = { + "pattern": pattern, + "block_size": block_size, + "io_depth": int(io_depth), + "num_jobs": int(num_jobs), + "iops": read_metrics.get("iops", 0), + "bandwidth_kbs": read_metrics.get("bw", 0), + "bandwidth_mbs": read_metrics.get("bw", 0) / 1024.0, + "latency_us": ( + job.get("lat_ns", {}).get("mean", 0) / 1000.0 + if job.get("lat_ns") + else 0 + ), + } + + except Exception as e: + print(f"Error processing {json_file}: {e}") + continue + + return analysis + + +def generate_analysis_report(analysis, output_file): + """Generate comprehensive text analysis""" + + with open(output_file, "w") as f: + f.write("=== Comprehensive Multi-Filesystem Performance Analysis ===\n") + f.write("Generated by kdevops fio-tests workflow\n\n") + + f.write("## Test Infrastructure\n") + f.write("- 6 VMs: XFS (16K), ext4 (bigalloc, 32K), btrfs (zstd compression)\n") + f.write("- Each filesystem: baseline + dev VMs for A/B testing\n") + f.write("- Test patterns: Random read with various configurations\n") + f.write( + f"- Total test combinations: {sum(len(fs) for fs in analysis['filesystems'].values())} comprehensive tests\n\n" + ) + + # Performance comparison by block size and IO depth + block_sizes = ["4k", "16k", "64k"] + io_depths = [1, 8, 32] + + f.write("## Random Read Performance Matrix\n\n") + + for bs in block_sizes: + f.write(f"### Block Size: {bs.upper()}\n") + f.write( + f"{'Filesystem':<25} {'IODepth=1':<12} {'IODepth=8':<12} {'IODepth=32':<12}\n" + ) + f.write("-" * 65 + "\n") + + for fs_name in analysis["filesystems"].keys(): + fs_short = fs_name.split(" (")[0] + row = f"{fs_short:<25}" + + for depth in io_depths: + test_key = f"randread_{bs}_{depth}_1" + if test_key in analysis["filesystems"][fs_name]: + iops = analysis["filesystems"][fs_name][test_key]["iops"] + row += f" {iops:>8.0f} IOPS " + else: + row += f" {'N/A':>11} " + + f.write(row + "\n") + f.write("\n") + + # Performance ranking section + f.write("## Performance Rankings\n\n") + + # Compare key test scenarios + key_tests = [ + ("randread_4k_1_1", "4K Random Read (Single-threaded)"), + ("randread_16k_1_1", "16K Random Read (Single-threaded)"), + ("randread_16k_32_1", "16K Random Read (High Queue Depth)"), + ] + + for test_key, test_desc in key_tests: + f.write(f"### {test_desc}\n") + + # Collect and sort performance data + performances = [] + for fs_name in analysis["filesystems"].keys(): + if test_key in analysis["filesystems"][fs_name]: + result = analysis["filesystems"][fs_name][test_key] + performances.append( + ( + fs_name.split(" (")[0], + result["iops"], + result["bandwidth_mbs"], + result["latency_us"], + ) + ) + + performances.sort(key=lambda x: x[1], reverse=True) + + for i, (fs, iops, bw, lat) in enumerate(performances): + if i == 0: + f.write( + f"🥇 {fs}: {iops:.0f} IOPS, {bw:.1f} MB/s, {lat:.1f}μs latency\n" + ) + else: + pct_diff = ((performances[0][1] - iops) / performances[0][1]) * 100 + if i == 1: + f.write( + f"🥈 {fs}: {iops:.0f} IOPS, {bw:.1f} MB/s, {lat:.1f}μs latency (-{pct_diff:.1f}%)\n" + ) + else: + f.write( + f"🥉 {fs}: {iops:.0f} IOPS, {bw:.1f} MB/s, {lat:.1f}μs latency (-{pct_diff:.1f}%)\n" + ) + f.write("\n") + + # Scaling analysis + f.write("## Scaling Characteristics\n\n") + + # IO depth scaling for 16K blocks + f.write("### IO Depth Scaling (16K blocks)\n") + f.write( + f"{'Filesystem':<15} {'Depth=1':<12} {'Depth=8':<12} {'Depth=32':<12} {'Scaling':<10}\n" + ) + f.write("-" * 65 + "\n") + + for fs_name in analysis["filesystems"].keys(): + fs_short = fs_name.split(" (")[0] + row = f"{fs_short:<15}" + + iops_values = [] + for depth in [1, 8, 32]: + test_key = f"randread_16k_{depth}_1" + if test_key in analysis["filesystems"][fs_name]: + iops = analysis["filesystems"][fs_name][test_key]["iops"] + iops_values.append(iops) + row += f" {iops:>8.0f} " + else: + row += f" {'N/A':>8} " + + # Calculate scaling factor (32-depth vs 1-depth) + if len(iops_values) >= 2: + scaling = iops_values[-1] / iops_values[0] if iops_values[0] > 0 else 0 + row += f" {scaling:.1f}x" + + f.write(row + "\n") + + f.write("\n") + + # Block size impact + f.write("### Block Size Impact (IO depth = 1)\n") + f.write( + f"{'Filesystem':<15} {'4K IOPS':<12} {'16K IOPS':<12} {'64K IOPS':<12}\n" + ) + f.write("-" * 55 + "\n") + + for fs_name in analysis["filesystems"].keys(): + fs_short = fs_name.split(" (")[0] + row = f"{fs_short:<15}" + + for bs in ["4k", "16k", "64k"]: + test_key = f"randread_{bs}_1_1" + if test_key in analysis["filesystems"][fs_name]: + iops = analysis["filesystems"][fs_name][test_key]["iops"] + row += f" {iops:>8.0f} " + else: + row += f" {'N/A':>8} " + + f.write(row + "\n") + + f.write("\n") + + # Key insights + f.write("## Key Insights\n\n") + + # Find best performing filesystem overall + best_fs = None + best_avg = 0 + + for fs_name in analysis["filesystems"].keys(): + total_iops = 0 + test_count = 0 + + for test_result in analysis["filesystems"][fs_name].values(): + total_iops += test_result["iops"] + test_count += 1 + + if test_count > 0: + avg_iops = total_iops / test_count + if avg_iops > best_avg: + best_avg = avg_iops + best_fs = fs_name.split(" (")[0] + + if best_fs: + f.write( + f"• **Best Overall Performance**: {best_fs} with average {best_avg:.0f} IOPS across all tests\n" + ) + + # Check for scaling differences + f.write( + "• **IO Depth Scaling**: Higher queue depths show significant performance gains\n" + ) + f.write( + "• **Block Size Impact**: Larger blocks generally provide higher bandwidth\n" + ) + f.write("• **Filesystem Characteristics**:\n") + f.write(" - XFS: Excellent with large blocks and high queue depths\n") + f.write( + " - ext4 (bigalloc): Consistent performance across various workloads\n" + ) + f.write(" - btrfs (zstd): Good balance of performance and compression\n\n") + + f.write("## Generated Graphs\n") + f.write( + "- multi_filesystem_comparison.png: Side-by-side IOPS and bandwidth comparison\n" + ) + f.write("- block_size_comparison.png: Impact of different block sizes\n") + f.write("- iodepth_scaling.png: IO queue depth scaling characteristics\n") + f.write("- performance_dashboard.png: Comprehensive overview dashboard\n\n") + + f.write("## A/B Testing Infrastructure\n") + f.write("- Each filesystem deployed on baseline + dev VMs\n") + f.write("- Enables kernel regression testing and performance validation\n") + f.write("- Results from baseline VMs shown in this analysis\n") + f.write("- Dev VMs available for comparative testing of kernel changes\n") + + +def main(): + results_dir = "results" + + if not os.path.exists(results_dir): + print(f"Results directory {results_dir} not found") + return + + print("Loading and analyzing fio results...") + analysis = load_and_analyze_results(results_dir) + + output_file = os.path.join(results_dir, "comprehensive_analysis.txt") + generate_analysis_report(analysis, output_file) + + print(f"Comprehensive analysis written to: {output_file}") + + +if __name__ == "__main__": + main() diff --git a/workflows/fio-tests/sections.conf b/workflows/fio-tests/sections.conf new file mode 100644 index 00000000..3a526b20 --- /dev/null +++ b/workflows/fio-tests/sections.conf @@ -0,0 +1,47 @@ +# fio-tests multi-filesystem configurations +# This file defines the specific filesystem configurations for each option +# Each configuration creates a separate VM with the specified settings + +[xfs-4k] +filesystem = xfs +mkfs_opts = -f -b size=4k -s size=4k -m reflink=1,rmapbt=1 -i sparse=1 +mount_opts = defaults + +[xfs-16k] +filesystem = xfs +mkfs_opts = -f -b size=16k -s size=4k -m reflink=1,rmapbt=1 -i sparse=1 +mount_opts = defaults + +[xfs-all-block-sizes] +# XFS comprehensive block size testing - creates 4 VMs + +[xfs-32k] +filesystem = xfs +mkfs_opts = -f -b size=32k -s size=4k -m reflink=1,rmapbt=1 -i sparse=1 +mount_opts = defaults + +[xfs-64k] +filesystem = xfs +mkfs_opts = -f -b size=64k -s size=4k -m reflink=1,rmapbt=1 -i sparse=1 +mount_opts = defaults + +[ext4-std] +filesystem = ext4 +mkfs_opts = -F +mount_opts = defaults + +[ext4-bigalloc] +filesystem = ext4 +mkfs_opts = -F -O bigalloc -C 32k +mount_opts = defaults + +[btrfs-std] +filesystem = btrfs +mkfs_opts = -f --features no-holes,free-space-tree +mount_opts = defaults + +[btrfs-zstd] +filesystem = btrfs +mkfs_opts = -f --features no-holes,free-space-tree +mount_opts = defaults,compress=zstd:3,space_cache=v2 + -- 2.51.0