ioengines: add get_max_open_zones zoned block device operation
authorNiklas Cassel <niklas.cassel@wdc.com>
Fri, 14 May 2021 12:53:14 +0000 (12:53 +0000)
committerJens Axboe <axboe@kernel.dk>
Fri, 14 May 2021 14:57:31 +0000 (08:57 -0600)
Define a new IO engine operation to get the maximum number of open zones.
Like the existing IO engine operations: .get_zoned_model, .report_zones,
and .reset_wp, this new IO engine operation is only valid for zoned block
devices.

Similarly to the other zbd IO engine operations, also provide a default
implementation inside oslib/linux-blkzoned.c that will be used if the
ioengine does not override it.

The default Linux oslib implementation is implemented similarly to
blkzoned_get_zoned_model(), i.e. it will return a successful error code
even when the sysfs attribute does not exist.
This is because the sysfs max_open_zones attribute was introduced first
in Linux v5.9.
All error handling is still there, so an ioengine that provides its own
implementation will still have its error code respected properly.

Signed-off-by: Niklas Cassel <niklas.cassel@wdc.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
engines/skeleton_external.c
ioengines.h
oslib/blkzoned.h
oslib/linux-blkzoned.c
zbd.c

index 7f3e4cb3a13c7d9cdb4fcee9b877f5a9b26e9e5d..c79b6f11146ff5ed7844f54da6ee03c3fbf971ac 100644 (file)
@@ -193,6 +193,18 @@ static int fio_skeleton_reset_wp(struct thread_data *td, struct fio_file *f,
        return 0;
 }
 
+/*
+ * Hook called for getting the maximum number of open zones for a
+ * ZBD_HOST_MANAGED zoned block device.
+ * A @max_open_zones value set to zero means no limit.
+ */
+static int fio_skeleton_get_max_open_zones(struct thread_data *td,
+                                          struct fio_file *f,
+                                          unsigned int *max_open_zones)
+{
+       return 0;
+}
+
 /*
  * Note that the structure is exported, so that fio can get it via
  * dlsym(..., "ioengine"); for (and only for) external engines.
@@ -212,6 +224,7 @@ struct ioengine_ops ioengine = {
        .get_zoned_model = fio_skeleton_get_zoned_model,
        .report_zones   = fio_skeleton_report_zones,
        .reset_wp       = fio_skeleton_reset_wp,
+       .get_max_open_zones = fio_skeleton_get_max_open_zones,
        .options        = options,
        .option_struct_size     = sizeof(struct fio_skeleton_options),
 };
index 1d01ab0a6d1345a71b04b0576f40fefca20062b9..b3f755b477de01f2ece25e93e9ad4d0edfa7bcda 100644 (file)
@@ -8,7 +8,7 @@
 #include "io_u.h"
 #include "zbd_types.h"
 
-#define FIO_IOOPS_VERSION      29
+#define FIO_IOOPS_VERSION      30
 
 #ifndef CONFIG_DYNAMIC_ENGINES
 #define FIO_STATIC     static
@@ -59,6 +59,8 @@ struct ioengine_ops {
                            uint64_t, struct zbd_zone *, unsigned int);
        int (*reset_wp)(struct thread_data *, struct fio_file *,
                        uint64_t, uint64_t);
+       int (*get_max_open_zones)(struct thread_data *, struct fio_file *,
+                                 unsigned int *);
        int option_struct_size;
        struct fio_option *options;
 };
index 4cc071dc6a61be64a257cd749e40cbd536cb9247..719b041d126adc03af9e30b9d2c9815b804a4518 100644 (file)
@@ -16,6 +16,8 @@ extern int blkzoned_report_zones(struct thread_data *td,
                                struct zbd_zone *zones, unsigned int nr_zones);
 extern int blkzoned_reset_wp(struct thread_data *td, struct fio_file *f,
                                uint64_t offset, uint64_t length);
+extern int blkzoned_get_max_open_zones(struct thread_data *td, struct fio_file *f,
+                                      unsigned int *max_open_zones);
 #else
 /*
  * Define stubs for systems that do not have zoned block device support.
@@ -44,6 +46,11 @@ static inline int blkzoned_reset_wp(struct thread_data *td, struct fio_file *f,
 {
        return -EIO;
 }
+static inline int blkzoned_get_max_open_zones(struct thread_data *td, struct fio_file *f,
+                                             unsigned int *max_open_zones)
+{
+       return -EIO;
+}
 #endif
 
 #endif /* FIO_BLKZONED_H */
index 84a64ed3014e04bd7eb188df5d491ad45b78a71f..6f89ec6f414e63c6a9bf6691a10dfc6723a29451 100644 (file)
@@ -162,6 +162,27 @@ int blkzoned_get_zoned_model(struct thread_data *td, struct fio_file *f,
        return 0;
 }
 
+int blkzoned_get_max_open_zones(struct thread_data *td, struct fio_file *f,
+                               unsigned int *max_open_zones)
+{
+       char *max_open_str;
+
+       if (f->filetype != FIO_TYPE_BLOCK)
+               return -EIO;
+
+       max_open_str = blkzoned_get_sysfs_attr(f->file_name, "queue/max_open_zones");
+       if (!max_open_str)
+               return 0;
+
+       dprint(FD_ZBD, "%s: max open zones supported by device: %s\n",
+              f->file_name, max_open_str);
+       *max_open_zones = atoll(max_open_str);
+
+       free(max_open_str);
+
+       return 0;
+}
+
 static uint64_t zone_capacity(struct blk_zone_report *hdr,
                              struct blk_zone *blkz)
 {
diff --git a/zbd.c b/zbd.c
index 46ff9aeb040dafd031b97f6a20e6f9dc4da2d20a..68cd58e1b9db99dfa883dab39bc06daea6b972e8 100644 (file)
--- a/zbd.c
+++ b/zbd.c
@@ -113,6 +113,34 @@ int zbd_reset_wp(struct thread_data *td, struct fio_file *f,
        return ret;
 }
 
+/**
+ * zbd_get_max_open_zones - Get the maximum number of open zones
+ * @td: FIO thread data
+ * @f: FIO file for which to get max open zones
+ * @max_open_zones: Upon success, result will be stored here.
+ *
+ * A @max_open_zones value set to zero means no limit.
+ *
+ * Returns 0 upon success and a negative error code upon failure.
+ */
+int zbd_get_max_open_zones(struct thread_data *td, struct fio_file *f,
+                          unsigned int *max_open_zones)
+{
+       int ret;
+
+       if (td->io_ops && td->io_ops->get_max_open_zones)
+               ret = td->io_ops->get_max_open_zones(td, f, max_open_zones);
+       else
+               ret = blkzoned_get_max_open_zones(td, f, max_open_zones);
+       if (ret < 0) {
+               td_verror(td, errno, "get max open zones failed");
+               log_err("%s: get max open zones failed (%d).\n",
+                       f->file_name, errno);
+       }
+
+       return ret;
+}
+
 /**
  * zbd_zone_idx - convert an offset into a zone number
  * @f: file pointer.
@@ -554,6 +582,51 @@ out:
        return ret;
 }
 
+static int zbd_set_max_open_zones(struct thread_data *td, struct fio_file *f)
+{
+       struct zoned_block_device_info *zbd = f->zbd_info;
+       unsigned int max_open_zones;
+       int ret;
+
+       if (zbd->model != ZBD_HOST_MANAGED) {
+               /* Only host-managed devices have a max open limit */
+               zbd->max_open_zones = td->o.max_open_zones;
+               goto out;
+       }
+
+       /* If host-managed, get the max open limit */
+       ret = zbd_get_max_open_zones(td, f, &max_open_zones);
+       if (ret)
+               return ret;
+
+       if (!max_open_zones) {
+               /* No device limit */
+               zbd->max_open_zones = td->o.max_open_zones;
+       } else if (!td->o.max_open_zones) {
+               /* No user limit. Set limit to device limit */
+               zbd->max_open_zones = max_open_zones;
+       } else if (td->o.max_open_zones <= max_open_zones) {
+               /* Both user limit and dev limit. User limit not too large */
+               zbd->max_open_zones = td->o.max_open_zones;
+       } else {
+               /* Both user limit and dev limit. User limit too large */
+               td_verror(td, EINVAL,
+                         "Specified --max_open_zones is too large");
+               log_err("Specified --max_open_zones (%d) is larger than max (%u)\n",
+                       td->o.max_open_zones, max_open_zones);
+               return -EINVAL;
+       }
+
+out:
+       /* Ensure that the limit is not larger than FIO's internal limit */
+       zbd->max_open_zones = min_not_zero(zbd->max_open_zones,
+                                          (uint32_t) ZBD_MAX_OPEN_ZONES);
+       dprint(FD_ZBD, "%s: using max open zones limit: %"PRIu32"\n",
+              f->file_name, zbd->max_open_zones);
+
+       return 0;
+}
+
 /*
  * Allocate zone information and store it into f->zbd_info if zonemode=zbd.
  *
@@ -576,9 +649,13 @@ static int zbd_create_zone_info(struct thread_data *td, struct fio_file *f)
        case ZBD_HOST_AWARE:
        case ZBD_HOST_MANAGED:
                ret = parse_zone_info(td, f);
+               if (ret)
+                       return ret;
                break;
        case ZBD_NONE:
                ret = init_zone_info(td, f);
+               if (ret)
+                       return ret;
                break;
        default:
                td_verror(td, EINVAL, "Unsupported zoned model");
@@ -586,12 +663,15 @@ static int zbd_create_zone_info(struct thread_data *td, struct fio_file *f)
                return -EINVAL;
        }
 
-       if (ret == 0) {
-               f->zbd_info->model = zbd_model;
-               f->zbd_info->max_open_zones =
-                       min_not_zero(td->o.max_open_zones, ZBD_MAX_OPEN_ZONES);
+       f->zbd_info->model = zbd_model;
+
+       ret = zbd_set_max_open_zones(td, f);
+       if (ret) {
+               zbd_free_zone_info(f);
+               return ret;
        }
-       return ret;
+
+       return 0;
 }
 
 void zbd_free_zone_info(struct fio_file *f)