summaryrefslogtreecommitdiffstats
path: root/fs/btrfs/tests/chunk-allocation-tests.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-24 08:46:03 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-24 08:46:03 -0700
commitcf9610f9116d55c5a66ef9f1faecacabd93a9322 (patch)
tree757a3d280760c654767631cab5ad8af2d86d1848 /fs/btrfs/tests/chunk-allocation-tests.c
downloadlinux-stable-cf9610f9116d55c5a66ef9f1faecacabd93a9322.tar.gz
linux-stable-cf9610f9116d55c5a66ef9f1faecacabd93a9322.zip
Merge tag 'pinctrl-v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrlgrafted
Pull pin control updates from Linus Walleij: "Core changes: - Use the non-blocking mux_state_try_select() in the generic MUX pin control back-end - Free pin maps on pinctrl_generic_to_map() failure in the core helpers New hardware pin control: - Qualcomm Maili TLMM SoC, PMG1110 PMIC, and Eliza LPASS LPI TLMM SoC (this is for the low power audio, LPASS = Low Power Audio SubSystem portions) - Upboard support on top of the Intel pin controller - Apple T6030 and T6031 support (just compatible strings) - Samsung Exynos 8855 SoC - Mediatek MT6858 SoC - Rockchip RK3308B and RV1106 SoCs - Airoha EN7523 and AN7563 SoCs Improvements: - Qualcomm TLMM GPIOs are unconditionally marked as wakeup capable - Qualcomm improvements on top of the Qualcomm PDC (power domain controller) irqchip improvements from tglx:s tree - Qualcomm IPQ806x and IPQ9650 non-urgent pin grop fixups - The NPCM8xx driver has been put into shape fixing many pin and group definitions - Some non-urgent Tegra 264 pin assignments are fixed up - Some non-urgent fixups to Airoha AN7581 and AN7583 registers and pin assignments, missing features etc" * tag 'pinctrl-v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl: (130 commits) dt-bindings: pinctrl: Convert TI DA850 pupd to DT schema pinctrl: meson: sync some modify from A4 pinctrl: meson: a4: Add input enable pin configuration pinctrl: realtek: rtd1625: remove unused group name spdif_sel pinctrl: realtek: rtd1625: fix base_bit for VE4 GPIO 13 dt-bindings: pinctrl: microchip,pic32mzda-pinctrl: Convert to DT schema Revert "Merge branch 'ib-rsk7204' into devel" pinctrl: rockchip: Add RV1106 pinctrl support dt-bindings: pinctrl: rockchip: Add RV1106 compatible pinctrl: rockchip: Decode drive strength in the get function pinctrl: fix PINCTRL_GENERIC_MUX not always being selectable pinctrl: airoha: add support of an7563 SoC dt-bindings: pinctrl: airoha: add support of an7563 pin controller pinctrl: airoha: try to find chip scu node by phandle first pinctrl: airoha: add support of en7523 SoC dt-bindings: pinctrl: airoha: add support of en7523 pin controller pinctrl: airoha: an7583: add support for olt pinmux pinctrl: airoha: an7583: add support for pon_alt pinmux pinctrl: airoha: an7583: add support for npu_uart pinmux dt-bindings: pinctrl: airoha: an7583: add missed features ...
Diffstat (limited to 'fs/btrfs/tests/chunk-allocation-tests.c')
-rw-r--r--fs/btrfs/tests/chunk-allocation-tests.c476
1 files changed, 476 insertions, 0 deletions
diff --git a/fs/btrfs/tests/chunk-allocation-tests.c b/fs/btrfs/tests/chunk-allocation-tests.c
new file mode 100644
index 000000000..9beb0602f
--- /dev/null
+++ b/fs/btrfs/tests/chunk-allocation-tests.c
@@ -0,0 +1,476 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Meta. All rights reserved.
+ */
+
+#include <linux/sizes.h>
+#include "btrfs-tests.h"
+#include "../volumes.h"
+#include "../disk-io.h"
+#include "../extent-io-tree.h"
+
+/*
+ * Tests for chunk allocator pending extent internals.
+ * These two functions form the core of searching the chunk allocation pending
+ * extent bitmap and have relatively easily definable semantics, so unit
+ * testing them can help ensure the correctness of chunk allocation.
+ */
+
+/*
+ * Describes the inputs to the system and expected results
+ * when testing btrfs_find_hole_in_pending_extents().
+ */
+struct pending_extent_test_case {
+ const char *name;
+ /* Input range to search. */
+ u64 hole_start;
+ u64 hole_len;
+ /* The size of hole we are searching for. */
+ u64 min_hole_size;
+ /*
+ * Pending extents to set up (up to 2 for up to 3 holes)
+ * If len == 0, then it is skipped.
+ */
+ struct {
+ u64 start;
+ u64 len;
+ } pending_extents[2];
+ /* Expected outputs. */
+ bool expected_found;
+ u64 expected_start;
+ u64 expected_len;
+};
+
+static const struct pending_extent_test_case find_hole_tests[] = {
+ {
+ .name = "no pending extents",
+ .hole_start = 0,
+ .hole_len = 10ULL * SZ_1G,
+ .min_hole_size = SZ_1G,
+ .pending_extents = { },
+ .expected_found = true,
+ .expected_start = 0,
+ .expected_len = 10ULL * SZ_1G,
+ },
+ {
+ .name = "pending extent at start of range",
+ .hole_start = 0,
+ .hole_len = 10ULL * SZ_1G,
+ .min_hole_size = SZ_1G,
+ .pending_extents = {
+ { .start = 0, .len = SZ_1G },
+ },
+ .expected_found = true,
+ .expected_start = SZ_1G,
+ .expected_len = 9ULL * SZ_1G,
+ },
+ {
+ .name = "pending extent overlapping start of range",
+ .hole_start = SZ_1G,
+ .hole_len = 9ULL * SZ_1G,
+ .min_hole_size = SZ_1G,
+ .pending_extents = {
+ { .start = 0, .len = SZ_2G },
+ },
+ .expected_found = true,
+ .expected_start = SZ_2G,
+ .expected_len = 8ULL * SZ_1G,
+ },
+ {
+ .name = "two holes; first hole is exactly big enough",
+ .hole_start = 0,
+ .hole_len = 10ULL * SZ_1G,
+ .min_hole_size = SZ_1G,
+ .pending_extents = {
+ { .start = SZ_1G, .len = SZ_1G },
+ },
+ .expected_found = true,
+ .expected_start = 0,
+ .expected_len = SZ_1G,
+ },
+ {
+ .name = "two holes; first hole is big enough",
+ .hole_start = 0,
+ .hole_len = 10ULL * SZ_1G,
+ .min_hole_size = SZ_1G,
+ .pending_extents = {
+ { .start = SZ_2G, .len = SZ_1G },
+ },
+ .expected_found = true,
+ .expected_start = 0,
+ .expected_len = SZ_2G,
+ },
+ {
+ .name = "two holes; second hole is big enough",
+ .hole_start = 0,
+ .hole_len = 10ULL * SZ_1G,
+ .min_hole_size = SZ_2G,
+ .pending_extents = {
+ { .start = SZ_1G, .len = SZ_1G },
+ },
+ .expected_found = true,
+ .expected_start = SZ_2G,
+ .expected_len = 8ULL * SZ_1G,
+ },
+ {
+ .name = "three holes; first hole big enough",
+ .hole_start = 0,
+ .hole_len = 10ULL * SZ_1G,
+ .min_hole_size = SZ_2G,
+ .pending_extents = {
+ { .start = SZ_2G, .len = SZ_1G },
+ { .start = 4ULL * SZ_1G, .len = SZ_1G },
+ },
+ .expected_found = true,
+ .expected_start = 0,
+ .expected_len = SZ_2G,
+ },
+ {
+ .name = "three holes; second hole big enough",
+ .hole_start = 0,
+ .hole_len = 10ULL * SZ_1G,
+ .min_hole_size = SZ_2G,
+ .pending_extents = {
+ { .start = SZ_1G, .len = SZ_1G },
+ { .start = 5ULL * SZ_1G, .len = SZ_1G },
+ },
+ .expected_found = true,
+ .expected_start = SZ_2G,
+ .expected_len = 3ULL * SZ_1G,
+ },
+ {
+ .name = "three holes; third hole big enough",
+ .hole_start = 0,
+ .hole_len = 10ULL * SZ_1G,
+ .min_hole_size = SZ_2G,
+ .pending_extents = {
+ { .start = SZ_1G, .len = SZ_1G },
+ { .start = 3ULL * SZ_1G, .len = 5ULL * SZ_1G },
+ },
+ .expected_found = true,
+ .expected_start = 8ULL * SZ_1G,
+ .expected_len = SZ_2G,
+ },
+ {
+ .name = "three holes; all holes too small",
+ .hole_start = 0,
+ .hole_len = 10ULL * SZ_1G,
+ .min_hole_size = SZ_2G,
+ .pending_extents = {
+ { .start = SZ_1G, .len = SZ_1G },
+ { .start = 3ULL * SZ_1G, .len = 6ULL * SZ_1G },
+ },
+ .expected_found = false,
+ .expected_start = 0,
+ .expected_len = SZ_1G,
+ },
+ {
+ .name = "three holes; all holes too small; first biggest",
+ .hole_start = 0,
+ .hole_len = 10ULL * SZ_1G,
+ .min_hole_size = 3ULL * SZ_1G,
+ .pending_extents = {
+ { .start = SZ_2G, .len = SZ_1G },
+ { .start = 4ULL * SZ_1G, .len = 5ULL * SZ_1G },
+ },
+ .expected_found = false,
+ .expected_start = 0,
+ .expected_len = SZ_2G,
+ },
+ {
+ .name = "three holes; all holes too small; second biggest",
+ .hole_start = 0,
+ .hole_len = 10ULL * SZ_1G,
+ .min_hole_size = 3ULL * SZ_1G,
+ .pending_extents = {
+ { .start = SZ_1G, .len = SZ_1G },
+ { .start = 4ULL * SZ_1G, .len = 5ULL * SZ_1G },
+ },
+ .expected_found = false,
+ .expected_start = SZ_2G,
+ .expected_len = SZ_2G,
+ },
+ {
+ .name = "three holes; all holes too small; third biggest",
+ .hole_start = 0,
+ .hole_len = 10ULL * SZ_1G,
+ .min_hole_size = 3ULL * SZ_1G,
+ .pending_extents = {
+ { .start = SZ_1G, .len = SZ_1G },
+ { .start = 3ULL * SZ_1G, .len = 5ULL * SZ_1G },
+ },
+ .expected_found = false,
+ .expected_start = 8ULL * SZ_1G,
+ .expected_len = SZ_2G,
+ },
+ {
+ .name = "hole entirely allocated by pending",
+ .hole_start = 0,
+ .hole_len = 10ULL * SZ_1G,
+ .min_hole_size = SZ_1G,
+ .pending_extents = {
+ { .start = 0, .len = 10ULL * SZ_1G },
+ },
+ .expected_found = false,
+ .expected_start = 10ULL * SZ_1G,
+ .expected_len = 0,
+ },
+ {
+ .name = "pending extent at end of range",
+ .hole_start = 0,
+ .hole_len = 10ULL * SZ_1G,
+ .min_hole_size = SZ_1G,
+ .pending_extents = {
+ { .start = 9ULL * SZ_1G, .len = SZ_2G },
+ },
+ .expected_found = true,
+ .expected_start = 0,
+ .expected_len = 9ULL * SZ_1G,
+ },
+ {
+ .name = "zero length input",
+ .hole_start = SZ_1G,
+ .hole_len = 0,
+ .min_hole_size = SZ_1G,
+ .pending_extents = { },
+ .expected_found = false,
+ .expected_start = SZ_1G,
+ .expected_len = 0,
+ },
+};
+
+static int test_find_hole_in_pending(u32 sectorsize, u32 nodesize)
+{
+ struct btrfs_fs_info *fs_info;
+ struct btrfs_device *device;
+ int ret = 0;
+
+ test_msg("running find_hole_in_pending_extents tests");
+
+ fs_info = btrfs_alloc_dummy_fs_info(nodesize, sectorsize);
+ if (!fs_info) {
+ test_std_err(TEST_ALLOC_FS_INFO);
+ return -ENOMEM;
+ }
+
+ device = btrfs_alloc_dummy_device(fs_info);
+ if (IS_ERR(device)) {
+ test_err("failed to allocate dummy device");
+ ret = PTR_ERR(device);
+ goto out_free_fs_info;
+ }
+ device->fs_info = fs_info;
+
+ for (int i = 0; i < ARRAY_SIZE(find_hole_tests); i++) {
+ const struct pending_extent_test_case *test_case = &find_hole_tests[i];
+ u64 hole_start = test_case->hole_start;
+ u64 hole_len = test_case->hole_len;
+ bool found;
+
+ for (int j = 0; j < ARRAY_SIZE(test_case->pending_extents); j++) {
+ u64 start = test_case->pending_extents[j].start;
+ u64 len = test_case->pending_extents[j].len;
+
+ if (!len)
+ continue;
+ btrfs_set_extent_bit(&device->alloc_state,
+ start, start + len - 1,
+ CHUNK_ALLOCATED, NULL);
+ }
+
+ mutex_lock(&fs_info->chunk_mutex);
+ found = btrfs_find_hole_in_pending_extents(device, &hole_start, &hole_len,
+ test_case->min_hole_size);
+ mutex_unlock(&fs_info->chunk_mutex);
+
+ if (found != test_case->expected_found) {
+ test_err("%s: expected found=%d, got found=%d",
+ test_case->name, test_case->expected_found, found);
+ ret = -EINVAL;
+ goto out_clear_pending_extents;
+ }
+ if (hole_start != test_case->expected_start ||
+ hole_len != test_case->expected_len) {
+ test_err("%s: expected [%llu, %llu), got [%llu, %llu)",
+ test_case->name, test_case->expected_start,
+ test_case->expected_start +
+ test_case->expected_len,
+ hole_start, hole_start + hole_len);
+ ret = -EINVAL;
+ goto out_clear_pending_extents;
+ }
+out_clear_pending_extents:
+ btrfs_clear_extent_bit(&device->alloc_state, 0, (u64)-1,
+ CHUNK_ALLOCATED, NULL);
+ if (ret)
+ break;
+ }
+
+out_free_fs_info:
+ btrfs_free_dummy_fs_info(fs_info);
+ return ret;
+}
+
+/*
+ * Describes the inputs to the system and expected results
+ * when testing btrfs_first_pending_extent().
+ */
+struct first_pending_test_case {
+ const char *name;
+ /* The range to look for a pending extent in. */
+ u64 hole_start;
+ u64 hole_len;
+ /* The pending extent to look for. */
+ struct {
+ u64 start;
+ u64 len;
+ } pending_extent;
+ /* Expected outputs. */
+ bool expected_found;
+ u64 expected_pending_start;
+ u64 expected_pending_end;
+};
+
+static const struct first_pending_test_case first_pending_tests[] = {
+ {
+ .name = "no pending extent",
+ .hole_start = 0,
+ .hole_len = 10ULL * SZ_1G,
+ .pending_extent = { 0, 0 },
+ .expected_found = false,
+ },
+ {
+ .name = "pending extent at search start",
+ .hole_start = SZ_1G,
+ .hole_len = 9ULL * SZ_1G,
+ .pending_extent = { SZ_1G, SZ_1G },
+ .expected_found = true,
+ .expected_pending_start = SZ_1G,
+ .expected_pending_end = SZ_2G - 1,
+ },
+ {
+ .name = "pending extent overlapping search start",
+ .hole_start = SZ_1G,
+ .hole_len = 9ULL * SZ_1G,
+ .pending_extent = { 0, SZ_2G },
+ .expected_found = true,
+ .expected_pending_start = 0,
+ .expected_pending_end = SZ_2G - 1,
+ },
+ {
+ .name = "pending extent inside search range",
+ .hole_start = 0,
+ .hole_len = 10ULL * SZ_1G,
+ .pending_extent = { SZ_2G, SZ_1G },
+ .expected_found = true,
+ .expected_pending_start = SZ_2G,
+ .expected_pending_end = 3ULL * SZ_1G - 1,
+ },
+ {
+ .name = "pending extent outside search range",
+ .hole_start = 0,
+ .hole_len = SZ_1G,
+ .pending_extent = { SZ_2G, SZ_1G },
+ .expected_found = false,
+ },
+ {
+ .name = "pending extent overlapping end of search range",
+ .hole_start = 0,
+ .hole_len = SZ_2G,
+ .pending_extent = { SZ_1G, SZ_2G },
+ .expected_found = true,
+ .expected_pending_start = SZ_1G,
+ .expected_pending_end = 3ULL * SZ_1G - 1,
+ },
+};
+
+static int test_first_pending_extent(u32 sectorsize, u32 nodesize)
+{
+ struct btrfs_fs_info *fs_info;
+ struct btrfs_device *device;
+ int ret = 0;
+
+ test_msg("running first_pending_extent tests");
+
+ fs_info = btrfs_alloc_dummy_fs_info(nodesize, sectorsize);
+ if (!fs_info) {
+ test_std_err(TEST_ALLOC_FS_INFO);
+ return -ENOMEM;
+ }
+
+ device = btrfs_alloc_dummy_device(fs_info);
+ if (IS_ERR(device)) {
+ test_err("failed to allocate dummy device");
+ ret = PTR_ERR(device);
+ goto out_free_fs_info;
+ }
+
+ device->fs_info = fs_info;
+
+ for (int i = 0; i < ARRAY_SIZE(first_pending_tests); i++) {
+ const struct first_pending_test_case *test_case = &first_pending_tests[i];
+ u64 start = test_case->pending_extent.start;
+ u64 len = test_case->pending_extent.len;
+ u64 pending_start, pending_end;
+ bool found;
+
+ if (len) {
+ btrfs_set_extent_bit(&device->alloc_state,
+ start, start + len - 1,
+ CHUNK_ALLOCATED, NULL);
+ }
+
+ mutex_lock(&fs_info->chunk_mutex);
+ found = btrfs_first_pending_extent(device, test_case->hole_start,
+ test_case->hole_len,
+ &pending_start, &pending_end);
+ mutex_unlock(&fs_info->chunk_mutex);
+
+ if (found != test_case->expected_found) {
+ test_err("%s: expected found=%d, got found=%d",
+ test_case->name, test_case->expected_found, found);
+ ret = -EINVAL;
+ goto out_clear_pending_extents;
+ }
+ if (!found)
+ goto out_clear_pending_extents;
+
+ if (pending_start != test_case->expected_pending_start ||
+ pending_end != test_case->expected_pending_end) {
+ test_err("%s: expected pending [%llu, %llu], got [%llu, %llu]",
+ test_case->name,
+ test_case->expected_pending_start,
+ test_case->expected_pending_end,
+ pending_start, pending_end);
+ ret = -EINVAL;
+ goto out_clear_pending_extents;
+ }
+
+out_clear_pending_extents:
+ btrfs_clear_extent_bit(&device->alloc_state, 0, (u64)-1,
+ CHUNK_ALLOCATED, NULL);
+ if (ret)
+ break;
+ }
+
+out_free_fs_info:
+ btrfs_free_dummy_fs_info(fs_info);
+ return ret;
+}
+
+int btrfs_test_chunk_allocation(u32 sectorsize, u32 nodesize)
+{
+ int ret;
+
+ test_msg("running chunk allocation tests");
+
+ ret = test_first_pending_extent(sectorsize, nodesize);
+ if (ret)
+ return ret;
+
+ ret = test_find_hole_in_pending(sectorsize, nodesize);
+ if (ret)
+ return ret;
+
+ return 0;
+}