]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
btrfs: tests: zoned: add tests cases for zoned code
authorNaohiro Aota <naohiro.aota@wdc.com>
Wed, 4 Feb 2026 07:31:25 +0000 (16:31 +0900)
committerDavid Sterba <dsterba@suse.com>
Tue, 7 Apr 2026 16:55:52 +0000 (18:55 +0200)
Add a test function for the zoned code, for now it tests
btrfs_load_block_group_by_raid_type() with various test cases. The
load_zone_info_tests[] array defines the test cases.

Signed-off-by: Naohiro Aota <naohiro.aota@wdc.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/Makefile
fs/btrfs/tests/btrfs-tests.c
fs/btrfs/tests/btrfs-tests.h
fs/btrfs/tests/zoned-tests.c [new file with mode: 0644]
fs/btrfs/zoned.c

index 31b00b93258849011248b63164f9cbd547b1fdc9..b15122aa26f95fa6d088fc095d179fb0e2e13384 100644 (file)
@@ -45,3 +45,7 @@ btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o \
        tests/free-space-tree-tests.o tests/extent-map-tests.o \
        tests/raid-stripe-tree-tests.o tests/delayed-refs-tests.o \
        tests/chunk-allocation-tests.o
+
+ifeq ($(CONFIG_BLK_DEV_ZONED),y)
+btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/zoned-tests.o
+endif
index 90e50e62dd1725e48a6bcd546bd3c8e9c79a99d3..19c127ac6d10cb4b625cb8089d3f2961a640514a 100644 (file)
@@ -303,6 +303,9 @@ int btrfs_run_sanity_tests(void)
                }
        }
        ret = btrfs_test_extent_map();
+       if (ret)
+               goto out;
+       ret = btrfs_test_zoned();
 
 out:
        btrfs_destroy_test_fs();
index b03d85a6e5ef7ec7887b85d0d2e0a1293c286ec7..cea58fe84a6d5ffcb60adecaf4a8f8d81c388d02 100644 (file)
@@ -63,6 +63,16 @@ void btrfs_init_dummy_trans(struct btrfs_trans_handle *trans,
                            struct btrfs_fs_info *fs_info);
 void btrfs_init_dummy_transaction(struct btrfs_transaction *trans, struct btrfs_fs_info *fs_info);
 struct btrfs_device *btrfs_alloc_dummy_device(struct btrfs_fs_info *fs_info);
+
+#ifdef CONFIG_BLK_DEV_ZONED
+int btrfs_test_zoned(void);
+#else
+static inline int btrfs_test_zoned(void)
+{
+       return 0;
+}
+#endif
+
 #else
 static inline int btrfs_run_sanity_tests(void)
 {
diff --git a/fs/btrfs/tests/zoned-tests.c b/fs/btrfs/tests/zoned-tests.c
new file mode 100644 (file)
index 0000000..da21c7a
--- /dev/null
@@ -0,0 +1,675 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Western Digital.  All rights reserved.
+ */
+
+#include <linux/cleanup.h>
+#include <linux/sizes.h>
+
+#include "btrfs-tests.h"
+#include "../space-info.h"
+#include "../volumes.h"
+#include "../zoned.h"
+
+#define WP_MISSING_DEV                         ((u64)-1)
+#define WP_CONVENTIONAL                                ((u64)-2)
+#define ZONE_SIZE                              SZ_256M
+
+#define HALF_STRIPE_LEN                                (BTRFS_STRIPE_LEN >> 1)
+
+struct load_zone_info_test_vector {
+       u64 raid_type;
+       u64 num_stripes;
+       u64 alloc_offsets[8];
+       u64 last_alloc;
+       u64 bg_length;
+       bool degraded;
+
+       int expected_result;
+       u64 expected_alloc_offset;
+
+       const char *description;
+};
+
+struct zone_info {
+       u64 physical;
+       u64 capacity;
+       u64 alloc_offset;
+};
+
+static int test_load_zone_info(struct btrfs_fs_info *fs_info,
+                              const struct load_zone_info_test_vector *test)
+{
+       struct btrfs_block_group *bg __free(btrfs_free_dummy_block_group) = NULL;
+       struct btrfs_chunk_map *map __free(btrfs_free_chunk_map) = NULL;
+       struct zone_info AUTO_KFREE(zone_info);
+       unsigned long AUTO_KFREE(active);
+       int ret;
+
+       bg = btrfs_alloc_dummy_block_group(fs_info, test->bg_length);
+       if (!bg) {
+               test_std_err(TEST_ALLOC_BLOCK_GROUP);
+               return -ENOMEM;
+       }
+
+       map = btrfs_alloc_chunk_map(test->num_stripes, GFP_KERNEL);
+       if (!map) {
+               test_std_err(TEST_ALLOC_EXTENT_MAP);
+               return -ENOMEM;
+       }
+
+       zone_info = kcalloc(test->num_stripes, sizeof(*zone_info), GFP_KERNEL);
+       if (!zone_info) {
+               test_err("cannot allocate zone info");
+               return -ENOMEM;
+       }
+
+       active = bitmap_zalloc(test->num_stripes, GFP_KERNEL);
+       if (!zone_info) {
+               test_err("cannot allocate active bitmap");
+               return -ENOMEM;
+       }
+
+       map->type = test->raid_type;
+       map->num_stripes = test->num_stripes;
+       if (test->raid_type == BTRFS_BLOCK_GROUP_RAID10)
+               map->sub_stripes = 2;
+       for (int i = 0; i < test->num_stripes; i++) {
+               zone_info[i].physical = 0;
+               zone_info[i].alloc_offset = test->alloc_offsets[i];
+               zone_info[i].capacity = ZONE_SIZE;
+               if (zone_info[i].alloc_offset && zone_info[i].alloc_offset < ZONE_SIZE)
+                       __set_bit(i, active);
+       }
+       if (test->degraded)
+               btrfs_set_opt(fs_info->mount_opt, DEGRADED);
+       else
+               btrfs_clear_opt(fs_info->mount_opt, DEGRADED);
+
+       ret = btrfs_load_block_group_by_raid_type(bg, map, zone_info, active,
+                                                 test->last_alloc);
+
+       if (ret != test->expected_result) {
+               test_err("unexpected return value: ret %d expected %d", ret,
+                        test->expected_result);
+               return -EINVAL;
+       }
+
+       if (!ret && bg->alloc_offset != test->expected_alloc_offset) {
+               test_err("unexpected alloc_offset: alloc_offset %llu expected %llu",
+                        bg->alloc_offset, test->expected_alloc_offset);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct load_zone_info_test_vector load_zone_info_tests[] = {
+       /* SINGLE */
+       {
+               .description = "SINGLE: load write pointer from sequential zone",
+               .raid_type = 0,
+               .num_stripes = 1,
+               .alloc_offsets = {
+                       SZ_1M,
+               },
+               .expected_alloc_offset = SZ_1M,
+       },
+       /*
+        * SINGLE block group on a conventional zone sets last_alloc outside of
+        * btrfs_load_block_group_*(). Do not test that case.
+        */
+
+       /* DUP */
+       /* Normal case */
+       {
+               .description = "DUP: having matching write pointers",
+               .raid_type = BTRFS_BLOCK_GROUP_DUP,
+               .num_stripes = 2,
+               .alloc_offsets = {
+                       SZ_1M, SZ_1M,
+               },
+               .expected_alloc_offset = SZ_1M,
+       },
+       /*
+        * One sequential zone and one conventional zone, having matching
+        * last_alloc.
+        */
+       {
+               .description = "DUP: seq zone and conv zone, matching last_alloc",
+               .raid_type = BTRFS_BLOCK_GROUP_DUP,
+               .num_stripes = 2,
+               .alloc_offsets = {
+                       SZ_1M, WP_CONVENTIONAL,
+               },
+               .last_alloc = SZ_1M,
+               .expected_alloc_offset = SZ_1M,
+       },
+       /*
+        * One sequential and one conventional zone, but having smaller
+        * last_alloc than write pointer.
+        */
+       {
+               .description = "DUP: seq zone and conv zone, smaller last_alloc",
+               .raid_type = BTRFS_BLOCK_GROUP_DUP,
+               .num_stripes = 2,
+               .alloc_offsets = {
+                       SZ_1M, WP_CONVENTIONAL,
+               },
+               .last_alloc = 0,
+               .expected_alloc_offset = SZ_1M,
+       },
+       /* Error case: having different write pointers. */
+       {
+               .description = "DUP: fail: different write pointers",
+               .raid_type = BTRFS_BLOCK_GROUP_DUP,
+               .num_stripes = 2,
+               .alloc_offsets = {
+                       SZ_1M, SZ_2M,
+               },
+               .expected_result = -EIO,
+       },
+       /* Error case: partial missing device should not happen on DUP. */
+       {
+               .description = "DUP: fail: missing device",
+               .raid_type = BTRFS_BLOCK_GROUP_DUP,
+               .num_stripes = 2,
+               .alloc_offsets = {
+                       SZ_1M, WP_MISSING_DEV,
+               },
+               .expected_result = -EIO,
+       },
+       /*
+        * Error case: one sequential and one conventional zone, but having larger
+        * last_alloc than write pointer.
+        */
+       {
+               .description = "DUP: fail: seq zone and conv zone, larger last_alloc",
+               .raid_type = BTRFS_BLOCK_GROUP_DUP,
+               .num_stripes = 2,
+               .alloc_offsets = {
+                       SZ_1M, WP_CONVENTIONAL,
+               },
+               .last_alloc = SZ_2M,
+               .expected_result = -EIO,
+       },
+
+       /* RAID1 */
+       /* Normal case */
+       {
+               .description = "RAID1: having matching write pointers",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID1,
+               .num_stripes = 2,
+               .alloc_offsets = {
+                       SZ_1M, SZ_1M,
+               },
+               .expected_alloc_offset = SZ_1M,
+       },
+       /*
+        * One sequential zone and one conventional zone, having matching
+        * last_alloc.
+        */
+       {
+               .description = "RAID1: seq zone and conv zone, matching last_alloc",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID1,
+               .num_stripes = 2,
+               .alloc_offsets = {
+                       SZ_1M, WP_CONVENTIONAL,
+               },
+               .last_alloc = SZ_1M,
+               .expected_alloc_offset = SZ_1M,
+       },
+       /*
+        * One sequential and one conventional zone, but having smaller
+        * last_alloc than write pointer.
+        */
+       {
+               .description = "RAID1: seq zone and conv zone, smaller last_alloc",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID1,
+               .num_stripes = 2,
+               .alloc_offsets = {
+                       SZ_1M, WP_CONVENTIONAL,
+               },
+               .last_alloc = 0,
+               .expected_alloc_offset = SZ_1M,
+       },
+       /* Partial missing device should be recovered on DEGRADED mount */
+       {
+               .description = "RAID1: fail: missing device on DEGRADED",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID1,
+               .num_stripes = 2,
+               .alloc_offsets = {
+                       SZ_1M, WP_MISSING_DEV,
+               },
+               .degraded = true,
+               .expected_alloc_offset = SZ_1M,
+       },
+       /* Error case: having different write pointers. */
+       {
+               .description = "RAID1: fail: different write pointers",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID1,
+               .num_stripes = 2,
+               .alloc_offsets = {
+                       SZ_1M, SZ_2M,
+               },
+               .expected_result = -EIO,
+       },
+       /*
+        * Partial missing device is not allowed on non-DEGRADED mount never happen
+        * as it is rejected beforehand.
+        */
+       /*
+        * Error case: one sequential and one conventional zone, but having larger
+        * last_alloc than write pointer.
+        */
+       {
+               .description = "RAID1: fail: seq zone and conv zone, larger last_alloc",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID1,
+               .num_stripes = 2,
+               .alloc_offsets = {
+                       SZ_1M, WP_CONVENTIONAL,
+               },
+               .last_alloc = SZ_2M,
+               .expected_result = -EIO,
+       },
+
+       /* RAID0 */
+       /* Normal case */
+       {
+               .description = "RAID0: initial partial write",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID0,
+               .num_stripes = 4,
+               .alloc_offsets = {
+                       HALF_STRIPE_LEN, 0, 0, 0,
+               },
+               .expected_alloc_offset = HALF_STRIPE_LEN,
+       },
+       {
+               .description = "RAID0: while in second stripe",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID0,
+               .num_stripes = 4,
+               .alloc_offsets = {
+                       BTRFS_STRIPE_LEN * 2, BTRFS_STRIPE_LEN + HALF_STRIPE_LEN,
+                       BTRFS_STRIPE_LEN, BTRFS_STRIPE_LEN,
+               },
+               .expected_alloc_offset = BTRFS_STRIPE_LEN * 5 + HALF_STRIPE_LEN,
+       },
+       {
+               .description = "RAID0: one stripe advanced",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID0,
+               .num_stripes = 2,
+               .alloc_offsets = {
+                       SZ_1M + BTRFS_STRIPE_LEN, SZ_1M,
+               },
+               .expected_alloc_offset = SZ_2M + BTRFS_STRIPE_LEN,
+       },
+       /* Error case: having different write pointers. */
+       {
+               .description = "RAID0: fail: disordered stripes",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID0,
+               .num_stripes = 4,
+               .alloc_offsets = {
+                       BTRFS_STRIPE_LEN, BTRFS_STRIPE_LEN * 2,
+                       BTRFS_STRIPE_LEN, BTRFS_STRIPE_LEN,
+               },
+               .expected_result = -EIO,
+       },
+       {
+               .description = "RAID0: fail: far distance",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID0,
+               .num_stripes = 4,
+               .alloc_offsets = {
+                       BTRFS_STRIPE_LEN * 3, BTRFS_STRIPE_LEN,
+                       BTRFS_STRIPE_LEN, BTRFS_STRIPE_LEN,
+               },
+               .expected_result = -EIO,
+       },
+       {
+               .description = "RAID0: fail: too many partial write",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID0,
+               .num_stripes = 4,
+               .alloc_offsets = {
+                       HALF_STRIPE_LEN, HALF_STRIPE_LEN, 0, 0,
+               },
+               .expected_result = -EIO,
+       },
+       /*
+        * Error case: Partial missing device is not allowed even on non-DEGRADED
+        * mount.
+        */
+       {
+               .description = "RAID0: fail: missing device on DEGRADED",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID0,
+               .num_stripes = 2,
+               .alloc_offsets = {
+                       SZ_1M, WP_MISSING_DEV,
+               },
+               .degraded = true,
+               .expected_result = -EIO,
+       },
+
+       /*
+        * One sequential zone and one conventional zone, having matching
+        * last_alloc.
+        */
+       {
+               .description = "RAID0: seq zone and conv zone, partially written stripe",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID0,
+               .num_stripes = 2,
+               .alloc_offsets = {
+                       SZ_1M, WP_CONVENTIONAL,
+               },
+               .last_alloc = SZ_2M - SZ_4K,
+               .expected_alloc_offset = SZ_2M - SZ_4K,
+       },
+       {
+               .description = "RAID0: conv zone and seq zone, partially written stripe",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID0,
+               .num_stripes = 2,
+               .alloc_offsets = {
+                       WP_CONVENTIONAL, SZ_1M,
+               },
+               .last_alloc = SZ_2M + SZ_4K,
+               .expected_alloc_offset = SZ_2M + SZ_4K,
+       },
+       /*
+        * Error case: one sequential and one conventional zone, but having larger
+        * last_alloc than write pointer.
+        */
+       {
+               .description = "RAID0: fail: seq zone and conv zone, larger last_alloc",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID0,
+               .num_stripes = 2,
+               .alloc_offsets = {
+                       SZ_1M, WP_CONVENTIONAL,
+               },
+               .last_alloc = SZ_2M + BTRFS_STRIPE_LEN * 2,
+               .expected_result = -EIO,
+       },
+
+       /* RAID0, 4 stripes with seq zones and conv zones. */
+       {
+               .description = "RAID0: stripes [2, 2, ?, ?] last_alloc = 6",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID0,
+               .num_stripes = 4,
+               .alloc_offsets = {
+                       BTRFS_STRIPE_LEN * 2, BTRFS_STRIPE_LEN * 2,
+                       WP_CONVENTIONAL, WP_CONVENTIONAL,
+               },
+               .last_alloc = BTRFS_STRIPE_LEN * 6,
+               .expected_alloc_offset = BTRFS_STRIPE_LEN * 6,
+       },
+       {
+               .description = "RAID0: stripes [2, 2, ?, ?] last_alloc = 7.5",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID0,
+               .num_stripes = 4,
+               .alloc_offsets = {
+                       BTRFS_STRIPE_LEN * 2, BTRFS_STRIPE_LEN * 2,
+                       WP_CONVENTIONAL, WP_CONVENTIONAL,
+               },
+               .last_alloc = BTRFS_STRIPE_LEN * 7 + HALF_STRIPE_LEN,
+               .expected_alloc_offset = BTRFS_STRIPE_LEN * 7 + HALF_STRIPE_LEN,
+       },
+       {
+               .description = "RAID0: stripes [3, ?, ?, ?] last_alloc = 1",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID0,
+               .num_stripes = 4,
+               .alloc_offsets = {
+                       BTRFS_STRIPE_LEN * 3, WP_CONVENTIONAL,
+                       WP_CONVENTIONAL, WP_CONVENTIONAL,
+               },
+               .last_alloc = BTRFS_STRIPE_LEN,
+               .expected_alloc_offset = BTRFS_STRIPE_LEN * 9,
+       },
+       {
+               .description = "RAID0: stripes [2, ?, 1, ?] last_alloc = 5",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID0,
+               .num_stripes = 4,
+               .alloc_offsets = {
+                       BTRFS_STRIPE_LEN * 2, WP_CONVENTIONAL,
+                       BTRFS_STRIPE_LEN, WP_CONVENTIONAL,
+               },
+               .last_alloc = BTRFS_STRIPE_LEN * 5,
+               .expected_alloc_offset = BTRFS_STRIPE_LEN * 5,
+       },
+       {
+               .description = "RAID0: fail: stripes [2, ?, 1, ?] last_alloc = 7",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID0,
+               .num_stripes = 4,
+               .alloc_offsets = {
+                       BTRFS_STRIPE_LEN * 2, WP_CONVENTIONAL,
+                       BTRFS_STRIPE_LEN, WP_CONVENTIONAL,
+               },
+               .last_alloc = BTRFS_STRIPE_LEN * 7,
+               .expected_result = -EIO,
+       },
+
+       /* RAID10 */
+       /* Normal case */
+       {
+               .description = "RAID10: initial partial write",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID10,
+               .num_stripes = 4,
+               .alloc_offsets = {
+                       HALF_STRIPE_LEN, HALF_STRIPE_LEN, 0, 0,
+               },
+               .expected_alloc_offset = HALF_STRIPE_LEN,
+       },
+       {
+               .description = "RAID10: while in second stripe",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID10,
+               .num_stripes = 8,
+               .alloc_offsets = {
+                       BTRFS_STRIPE_LEN * 2, BTRFS_STRIPE_LEN * 2,
+                       BTRFS_STRIPE_LEN + HALF_STRIPE_LEN,
+                       BTRFS_STRIPE_LEN + HALF_STRIPE_LEN,
+                       BTRFS_STRIPE_LEN, BTRFS_STRIPE_LEN,
+                       BTRFS_STRIPE_LEN, BTRFS_STRIPE_LEN,
+               },
+               .expected_alloc_offset = BTRFS_STRIPE_LEN * 5 + HALF_STRIPE_LEN,
+       },
+       {
+               .description = "RAID10: one stripe advanced",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID10,
+               .num_stripes = 4,
+               .alloc_offsets = {
+                       SZ_1M + BTRFS_STRIPE_LEN, SZ_1M + BTRFS_STRIPE_LEN,
+                       SZ_1M, SZ_1M,
+               },
+               .expected_alloc_offset = SZ_2M + BTRFS_STRIPE_LEN,
+       },
+       {
+               .description = "RAID10: one stripe advanced, with conventional zone",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID10,
+               .num_stripes = 4,
+               .alloc_offsets = {
+                       SZ_1M + BTRFS_STRIPE_LEN, WP_CONVENTIONAL,
+                       WP_CONVENTIONAL, SZ_1M,
+               },
+               .expected_alloc_offset = SZ_2M + BTRFS_STRIPE_LEN,
+       },
+       /* Error case: having different write pointers. */
+       {
+               .description = "RAID10: fail: disordered stripes",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID10,
+               .num_stripes = 8,
+               .alloc_offsets = {
+                       BTRFS_STRIPE_LEN, BTRFS_STRIPE_LEN,
+                       BTRFS_STRIPE_LEN * 2, BTRFS_STRIPE_LEN * 2,
+                       BTRFS_STRIPE_LEN, BTRFS_STRIPE_LEN,
+                       BTRFS_STRIPE_LEN, BTRFS_STRIPE_LEN,
+               },
+               .expected_result = -EIO,
+       },
+       {
+               .description = "RAID10: fail: far distance",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID10,
+               .num_stripes = 8,
+               .alloc_offsets = {
+                       BTRFS_STRIPE_LEN * 3, BTRFS_STRIPE_LEN * 3,
+                       BTRFS_STRIPE_LEN, BTRFS_STRIPE_LEN,
+                       BTRFS_STRIPE_LEN, BTRFS_STRIPE_LEN,
+                       BTRFS_STRIPE_LEN, BTRFS_STRIPE_LEN,
+               },
+               .expected_result = -EIO,
+       },
+       {
+               .description = "RAID10: fail: too many partial write",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID10,
+               .num_stripes = 8,
+               .alloc_offsets = {
+                       HALF_STRIPE_LEN, HALF_STRIPE_LEN,
+                       HALF_STRIPE_LEN, HALF_STRIPE_LEN,
+                       0, 0, 0, 0,
+               },
+               .expected_result = -EIO,
+       },
+       /*
+        * Error case: Partial missing device in RAID0 level is not allowed even on
+        * non-DEGRADED mount.
+        */
+       {
+               .description = "RAID10: fail: missing device on DEGRADED",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID10,
+               .num_stripes = 4,
+               .alloc_offsets = {
+                       SZ_1M, SZ_1M,
+                       WP_MISSING_DEV, WP_MISSING_DEV,
+               },
+               .degraded = true,
+               .expected_result = -EIO,
+       },
+
+       /*
+        * One sequential zone and one conventional zone, having matching
+        * last_alloc.
+        */
+       {
+               .description = "RAID10: seq zone and conv zone, partially written stripe",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID10,
+               .num_stripes = 4,
+               .alloc_offsets = {
+                       SZ_1M, SZ_1M,
+                       WP_CONVENTIONAL, WP_CONVENTIONAL,
+               },
+               .last_alloc = SZ_2M - SZ_4K,
+               .expected_alloc_offset = SZ_2M - SZ_4K,
+       },
+       {
+               .description = "RAID10: conv zone and seq zone, partially written stripe",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID10,
+               .num_stripes = 4,
+               .alloc_offsets = {
+                       WP_CONVENTIONAL, WP_CONVENTIONAL,
+                       SZ_1M, SZ_1M,
+               },
+               .last_alloc = SZ_2M + SZ_4K,
+               .expected_alloc_offset = SZ_2M + SZ_4K,
+       },
+       /*
+        * Error case: one sequential and one conventional zone, but having larger
+        * last_alloc than write pointer.
+        */
+       {
+               .description = "RAID10: fail: seq zone and conv zone, larger last_alloc",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID10,
+               .num_stripes = 4,
+               .alloc_offsets = {
+                       SZ_1M, SZ_1M,
+                       WP_CONVENTIONAL, WP_CONVENTIONAL,
+               },
+               .last_alloc = SZ_2M + BTRFS_STRIPE_LEN * 2,
+               .expected_result = -EIO,
+       },
+
+       /* RAID10, 4 stripes with seq zones and conv zones. */
+       {
+               .description = "RAID10: stripes [2, 2, ?, ?] last_alloc = 6",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID10,
+               .num_stripes = 8,
+               .alloc_offsets = {
+                       BTRFS_STRIPE_LEN * 2, BTRFS_STRIPE_LEN * 2,
+                       BTRFS_STRIPE_LEN * 2, BTRFS_STRIPE_LEN * 2,
+                       WP_CONVENTIONAL, WP_CONVENTIONAL,
+                       WP_CONVENTIONAL, WP_CONVENTIONAL,
+               },
+               .last_alloc = BTRFS_STRIPE_LEN * 6,
+               .expected_alloc_offset = BTRFS_STRIPE_LEN * 6,
+       },
+       {
+               .description = "RAID10: stripes [2, 2, ?, ?] last_alloc = 7.5",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID10,
+               .num_stripes = 8,
+               .alloc_offsets = {
+                       BTRFS_STRIPE_LEN * 2, BTRFS_STRIPE_LEN * 2,
+                       BTRFS_STRIPE_LEN * 2, BTRFS_STRIPE_LEN * 2,
+                       WP_CONVENTIONAL, WP_CONVENTIONAL,
+                       WP_CONVENTIONAL, WP_CONVENTIONAL,
+               },
+               .last_alloc = BTRFS_STRIPE_LEN * 7 + HALF_STRIPE_LEN,
+               .expected_alloc_offset = BTRFS_STRIPE_LEN * 7 + HALF_STRIPE_LEN,
+       },
+       {
+               .description = "RAID10: stripes [3, ?, ?, ?] last_alloc = 1",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID10,
+               .num_stripes = 8,
+               .alloc_offsets = {
+                       BTRFS_STRIPE_LEN * 3, BTRFS_STRIPE_LEN * 3,
+                       WP_CONVENTIONAL, WP_CONVENTIONAL,
+                       WP_CONVENTIONAL, WP_CONVENTIONAL,
+                       WP_CONVENTIONAL, WP_CONVENTIONAL,
+               },
+               .last_alloc = BTRFS_STRIPE_LEN,
+               .expected_alloc_offset = BTRFS_STRIPE_LEN * 9,
+       },
+       {
+               .description = "RAID10: stripes [2, ?, 1, ?] last_alloc = 5",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID10,
+               .num_stripes = 8,
+               .alloc_offsets = {
+                       BTRFS_STRIPE_LEN * 2, BTRFS_STRIPE_LEN * 2,
+                       WP_CONVENTIONAL, WP_CONVENTIONAL,
+                       BTRFS_STRIPE_LEN, BTRFS_STRIPE_LEN,
+                       WP_CONVENTIONAL, WP_CONVENTIONAL,
+               },
+               .last_alloc = BTRFS_STRIPE_LEN * 5,
+               .expected_alloc_offset = BTRFS_STRIPE_LEN * 5,
+       },
+       {
+               .description = "RAID10: fail: stripes [2, ?, 1, ?] last_alloc = 7",
+               .raid_type = BTRFS_BLOCK_GROUP_RAID10,
+               .num_stripes = 8,
+               .alloc_offsets = {
+                       BTRFS_STRIPE_LEN * 2, BTRFS_STRIPE_LEN * 2,
+                       WP_CONVENTIONAL, WP_CONVENTIONAL,
+                       BTRFS_STRIPE_LEN, BTRFS_STRIPE_LEN,
+                       WP_CONVENTIONAL, WP_CONVENTIONAL,
+               },
+               .last_alloc = BTRFS_STRIPE_LEN * 7,
+               .expected_result = -EIO,
+       },
+};
+
+int btrfs_test_zoned(void)
+{
+       struct btrfs_fs_info *fs_info __free(btrfs_free_dummy_fs_info) = NULL;
+       int ret;
+
+       test_msg("running zoned tests (error messages are expected)");
+
+       fs_info = btrfs_alloc_dummy_fs_info(PAGE_SIZE, PAGE_SIZE);
+       if (!fs_info) {
+               test_std_err(TEST_ALLOC_FS_INFO);
+               return -ENOMEM;
+       }
+
+       for (int i = 0; i < ARRAY_SIZE(load_zone_info_tests); i++) {
+               ret = test_load_zone_info(fs_info, &load_zone_info_tests[i]);
+               if (ret) {
+                       test_err("test case \"%s\" failed", load_zone_info_tests[i].description);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
index 0cd7fd3fcfa3afc80969684d222c143ccb4fb419..3f3233c6221b7d55e9e62fd8733b2957f17643fd 100644 (file)
@@ -2386,6 +2386,9 @@ bool btrfs_zone_activate(struct btrfs_block_group *block_group)
        if (!btrfs_is_zoned(block_group->fs_info))
                return true;
 
+       if (unlikely(btrfs_is_testing(fs_info)))
+               return true;
+
        map = block_group->physical_map;
 
        spin_lock(&fs_info->zone_active_bgs_lock);