btrfs: zoned: load active zone information from devices
authorNaohiro Aota <naohiro.aota@wdc.com>
Thu, 19 Aug 2021 12:19:15 +0000 (21:19 +0900)
committerDavid Sterba <dsterba@suse.com>
Tue, 26 Oct 2021 17:07:59 +0000 (19:07 +0200)
The ZNS specification defines a limit on the number of zones that can be in
the implicit open, explicit open or closed conditions. Any zone with such
condition is defined as an active zone and correspond to any zone that is
being written or that has been only partially written. If the maximum
number of active zones is reached, we must either reset or finish some
active zones before being able to chose other zones for storing data.

Load queue_max_active_zones() and track the number of active zones left on
the device.

Signed-off-by: Naohiro Aota <naohiro.aota@wdc.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/zoned.c
fs/btrfs/zoned.h

index 17554caf8b86bb561ee897dd4c0d016e19e4d43d..2ccdc6e1f7937fac7544acf7d576cfd87937e229 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/slab.h>
 #include <linux/blkdev.h>
 #include <linux/sched/mm.h>
+#include <linux/atomic.h>
 #include "ctree.h"
 #include "volumes.h"
 #include "zoned.h"
 /* Number of superblock log zones */
 #define BTRFS_NR_SB_LOG_ZONES 2
 
+/*
+ * Minimum of active zones we need:
+ *
+ * - BTRFS_SUPER_MIRROR_MAX zones for superblock mirrors
+ * - 3 zones to ensure at least one zone per SYSTEM, META and DATA block group
+ * - 1 zone for tree-log dedicated block group
+ * - 1 zone for relocation
+ */
+#define BTRFS_MIN_ACTIVE_ZONES         (BTRFS_SUPER_MIRROR_MAX + 5)
+
 /*
  * Maximum supported zone size. Currently, SMR disks have a zone size of
  * 256MiB, and we are expecting ZNS drives to be in the 1-4GiB range. We do not
@@ -303,6 +314,9 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device)
        struct btrfs_fs_info *fs_info = device->fs_info;
        struct btrfs_zoned_device_info *zone_info = NULL;
        struct block_device *bdev = device->bdev;
+       struct request_queue *queue = bdev_get_queue(bdev);
+       unsigned int max_active_zones;
+       unsigned int nactive;
        sector_t nr_sectors;
        sector_t sector = 0;
        struct blk_zone *zones = NULL;
@@ -358,6 +372,17 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device)
        if (!IS_ALIGNED(nr_sectors, zone_sectors))
                zone_info->nr_zones++;
 
+       max_active_zones = queue_max_active_zones(queue);
+       if (max_active_zones && max_active_zones < BTRFS_MIN_ACTIVE_ZONES) {
+               btrfs_err_in_rcu(fs_info,
+"zoned: %s: max active zones %u is too small, need at least %u active zones",
+                                rcu_str_deref(device->name), max_active_zones,
+                                BTRFS_MIN_ACTIVE_ZONES);
+               ret = -EINVAL;
+               goto out;
+       }
+       zone_info->max_active_zones = max_active_zones;
+
        zone_info->seq_zones = bitmap_zalloc(zone_info->nr_zones, GFP_KERNEL);
        if (!zone_info->seq_zones) {
                ret = -ENOMEM;
@@ -370,6 +395,12 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device)
                goto out;
        }
 
+       zone_info->active_zones = bitmap_zalloc(zone_info->nr_zones, GFP_KERNEL);
+       if (!zone_info->active_zones) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
        zones = kcalloc(BTRFS_REPORT_NR_ZONES, sizeof(struct blk_zone), GFP_KERNEL);
        if (!zones) {
                ret = -ENOMEM;
@@ -377,6 +408,7 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device)
        }
 
        /* Get zones type */
+       nactive = 0;
        while (sector < nr_sectors) {
                nr_zones = BTRFS_REPORT_NR_ZONES;
                ret = btrfs_get_dev_zones(device, sector << SECTOR_SHIFT, zones,
@@ -387,8 +419,17 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device)
                for (i = 0; i < nr_zones; i++) {
                        if (zones[i].type == BLK_ZONE_TYPE_SEQWRITE_REQ)
                                __set_bit(nreported, zone_info->seq_zones);
-                       if (zones[i].cond == BLK_ZONE_COND_EMPTY)
+                       switch (zones[i].cond) {
+                       case BLK_ZONE_COND_EMPTY:
                                __set_bit(nreported, zone_info->empty_zones);
+                               break;
+                       case BLK_ZONE_COND_IMP_OPEN:
+                       case BLK_ZONE_COND_EXP_OPEN:
+                       case BLK_ZONE_COND_CLOSED:
+                               __set_bit(nreported, zone_info->active_zones);
+                               nactive++;
+                               break;
+                       }
                        nreported++;
                }
                sector = zones[nr_zones - 1].start + zones[nr_zones - 1].len;
@@ -403,6 +444,19 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device)
                goto out;
        }
 
+       if (max_active_zones) {
+               if (nactive > max_active_zones) {
+                       btrfs_err_in_rcu(device->fs_info,
+                       "zoned: %u active zones on %s exceeds max_active_zones %u",
+                                        nactive, rcu_str_deref(device->name),
+                                        max_active_zones);
+                       ret = -EIO;
+                       goto out;
+               }
+               atomic_set(&zone_info->active_zones_left,
+                          max_active_zones - nactive);
+       }
+
        /* Validate superblock log */
        nr_zones = BTRFS_NR_SB_LOG_ZONES;
        for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
@@ -485,6 +539,7 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device)
 out:
        kfree(zones);
 out_free_zone_info:
+       bitmap_free(zone_info->active_zones);
        bitmap_free(zone_info->empty_zones);
        bitmap_free(zone_info->seq_zones);
        kfree(zone_info);
@@ -500,6 +555,7 @@ void btrfs_destroy_dev_zone_info(struct btrfs_device *device)
        if (!zone_info)
                return;
 
+       bitmap_free(zone_info->active_zones);
        bitmap_free(zone_info->seq_zones);
        bitmap_free(zone_info->empty_zones);
        kfree(zone_info);
index 4f30f3bf18864da8234d8c7f0d31f270faad2291..48628782e4b8757dec18290983da176aa21ddd0e 100644 (file)
@@ -23,8 +23,11 @@ struct btrfs_zoned_device_info {
        u64 zone_size;
        u8  zone_size_shift;
        u32 nr_zones;
+       unsigned int max_active_zones;
+       atomic_t active_zones_left;
        unsigned long *seq_zones;
        unsigned long *empty_zones;
+       unsigned long *active_zones;
        struct blk_zone sb_zones[2 * BTRFS_SUPER_MIRROR_MAX];
 };