Fio 3.37
[fio.git] / zbd.c
diff --git a/zbd.c b/zbd.c
index 832868cb45fcf425b56aaf9cea1be2c9273ed772..3741766051547fbc71a46ab42efe72a181826993 100644 (file)
--- a/zbd.c
+++ b/zbd.c
@@ -11,6 +11,7 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include "compiler/compiler.h"
 #include "os/os.h"
 #include "file.h"
 #include "fio.h"
@@ -102,13 +103,12 @@ static bool zbd_zone_full(const struct fio_file *f, struct fio_zone_info *z,
 static void zone_lock(struct thread_data *td, const struct fio_file *f,
                      struct fio_zone_info *z)
 {
-       struct zoned_block_device_info *zbd = f->zbd_info;
-       uint32_t nz = z - zbd->zone_info;
-
+#ifndef NDEBUG
+       unsigned int const nz = zbd_zone_idx(f, z);
        /* A thread should never lock zones outside its working area. */
        assert(f->min_zone <= nz && nz < f->max_zone);
-
        assert(z->has_wp);
+#endif
 
        /*
         * Lock the io_u target zone. The zone will be unlocked if io_u offset
@@ -128,11 +128,8 @@ static void zone_lock(struct thread_data *td, const struct fio_file *f,
 
 static inline void zone_unlock(struct fio_zone_info *z)
 {
-       int ret;
-
        assert(z->has_wp);
-       ret = pthread_mutex_unlock(&z->mutex);
-       assert(!ret);
+       pthread_mutex_unlock(&z->mutex);
 }
 
 static inline struct fio_zone_info *zbd_get_zone(const struct fio_file *f,
@@ -254,7 +251,7 @@ static int zbd_reset_wp(struct thread_data *td, struct fio_file *f,
 }
 
 /**
- * zbd_reset_zone - reset the write pointer of a single zone
+ * __zbd_reset_zone - reset the write pointer of a single zone
  * @td: FIO thread data.
  * @f: FIO file associated with the disk for which to reset a write pointer.
  * @z: Zone to reset.
@@ -263,8 +260,8 @@ static int zbd_reset_wp(struct thread_data *td, struct fio_file *f,
  *
  * The caller must hold z->mutex.
  */
-static int zbd_reset_zone(struct thread_data *td, struct fio_file *f,
-                         struct fio_zone_info *z)
+static int __zbd_reset_zone(struct thread_data *td, struct fio_file *f,
+                           struct fio_zone_info *z)
 {
        uint64_t offset = z->start;
        uint64_t length = (z+1)->start - offset;
@@ -339,6 +336,32 @@ static void zbd_write_zone_put(struct thread_data *td, const struct fio_file *f,
        z->write = 0;
 }
 
+/**
+ * zbd_reset_zone - reset the write pointer of a single zone and remove the zone
+ *                  from the array of write zones.
+ * @td: FIO thread data.
+ * @f: FIO file associated with the disk for which to reset a write pointer.
+ * @z: Zone to reset.
+ *
+ * Returns 0 upon success and a negative error code upon failure.
+ *
+ * The caller must hold z->mutex.
+ */
+static int zbd_reset_zone(struct thread_data *td, struct fio_file *f,
+                         struct fio_zone_info *z)
+{
+       int ret;
+
+       ret = __zbd_reset_zone(td, f, z);
+       if (ret)
+               return ret;
+
+       pthread_mutex_lock(&f->zbd_info->mutex);
+       zbd_write_zone_put(td, f, z);
+       pthread_mutex_unlock(&f->zbd_info->mutex);
+       return 0;
+}
+
 /**
  * zbd_finish_zone - finish the specified zone
  * @td: FIO thread data.
@@ -394,7 +417,8 @@ static int zbd_reset_zones(struct thread_data *td, struct fio_file *f,
        const uint64_t min_bs = td->o.min_bs[DDIR_WRITE];
        int res = 0;
 
-       assert(min_bs);
+       if (fio_unlikely(0 == min_bs))
+               return 1;
 
        dprint(FD_ZBD, "%s: examining zones %u .. %u\n",
               f->file_name, zbd_zone_idx(f, zb), zbd_zone_idx(f, ze));
@@ -404,9 +428,6 @@ static int zbd_reset_zones(struct thread_data *td, struct fio_file *f,
                        continue;
 
                zone_lock(td, f, z);
-               pthread_mutex_lock(&f->zbd_info->mutex);
-               zbd_write_zone_put(td, f, z);
-               pthread_mutex_unlock(&f->zbd_info->mutex);
 
                if (z->wp != z->start) {
                        dprint(FD_ZBD, "%s: resetting zone %u\n",
@@ -450,21 +471,47 @@ static int zbd_get_max_open_zones(struct thread_data *td, struct fio_file *f,
 }
 
 /**
- * zbd_write_zone_get - Add a zone to the array of write zones.
+ * zbd_get_max_active_zones - Get the maximum number of active zones
+ * @td: FIO thread data
+ * @f: FIO file for which to get max active zones
+ *
+ * Returns max_active_zones limit value of the target file if it is available.
+ * Otherwise return zero, which means no limit.
+ */
+static unsigned int zbd_get_max_active_zones(struct thread_data *td,
+                                            struct fio_file *f)
+{
+       unsigned int max_active_zones;
+       int ret;
+
+       if (td->io_ops && td->io_ops->get_max_active_zones)
+               ret = td->io_ops->get_max_active_zones(td, f,
+                                                      &max_active_zones);
+       else
+               ret = blkzoned_get_max_active_zones(td, f, &max_active_zones);
+       if (ret < 0) {
+               dprint(FD_ZBD, "%s: max_active_zones is not available\n",
+                      f->file_name);
+               return 0;
+       }
+
+       return max_active_zones;
+}
+
+/**
+ * __zbd_write_zone_get - Add a zone to the array of write zones.
  * @td: fio thread data.
  * @f: fio file that has the write zones array to add.
  * @zone_idx: Index of the zone to add.
  *
- * Add a ZBD zone to write target zones array, if it is not yet added. Returns
- * true if either the zone was already added or if the zone was successfully
- * added to the array without exceeding the maximum number of write zones.
- * Returns false if the zone was not already added and addition of the zone
- * would cause the zone limit to be exceeded.
+ * Do same operation as @zbd_write_zone_get, except it adds the zone at
+ * @zone_idx to write target zones array even when it does not have remainder
+ * space to write one block.
  */
-static bool zbd_write_zone_get(struct thread_data *td, const struct fio_file *f,
-                              struct fio_zone_info *z)
+static bool __zbd_write_zone_get(struct thread_data *td,
+                                const struct fio_file *f,
+                                struct fio_zone_info *z)
 {
-       const uint64_t min_bs = td->o.min_bs[DDIR_WRITE];
        struct zoned_block_device_info *zbdi = f->zbd_info;
        uint32_t zone_idx = zbd_zone_idx(f, z);
        bool res = true;
@@ -476,7 +523,7 @@ static bool zbd_write_zone_get(struct thread_data *td, const struct fio_file *f,
         * Skip full zones with data verification enabled because resetting a
         * zone causes data loss and hence causes verification to fail.
         */
-       if (td->o.verify != VERIFY_NONE && zbd_zone_full(f, z, min_bs))
+       if (td->o.verify != VERIFY_NONE && zbd_zone_remainder(z) == 0)
                return false;
 
        /*
@@ -521,6 +568,33 @@ out:
        return res;
 }
 
+/**
+ * zbd_write_zone_get - Add a zone to the array of write zones.
+ * @td: fio thread data.
+ * @f: fio file that has the open zones to add.
+ * @zone_idx: Index of the zone to add.
+ *
+ * Add a ZBD zone to write target zones array, if it is not yet added. Returns
+ * true if either the zone was already added or if the zone was successfully
+ * added to the array without exceeding the maximum number of write zones.
+ * Returns false if the zone was not already added and addition of the zone
+ * would cause the zone limit to be exceeded.
+ */
+static bool zbd_write_zone_get(struct thread_data *td, const struct fio_file *f,
+                              struct fio_zone_info *z)
+{
+       const uint64_t min_bs = td->o.min_bs[DDIR_WRITE];
+
+       /*
+        * Skip full zones with data verification enabled because resetting a
+        * zone causes data loss and hence causes verification to fail.
+        */
+       if (td->o.verify != VERIFY_NONE && zbd_zone_full(f, z, min_bs))
+               return false;
+
+       return __zbd_write_zone_get(td, f, z);
+}
+
 /* Verify whether direct I/O is used for all host-managed zoned block drives. */
 static bool zbd_using_direct_io(void)
 {
@@ -599,9 +673,20 @@ static bool zbd_zone_align_file_sizes(struct thread_data *td,
                return false;
        }
 
+       if (td->o.td_ddir == TD_DDIR_READ) {
+               z = zbd_offset_to_zone(f, f->file_offset + f->io_size);
+               new_end = z->start;
+               if (f->file_offset + f->io_size > new_end) {
+                       log_info("%s: rounded io_size from %"PRIu64" to %"PRIu64"\n",
+                                f->file_name, f->io_size,
+                                new_end - f->file_offset);
+                       f->io_size = new_end - f->file_offset;
+               }
+               return true;
+       }
+
        z = zbd_offset_to_zone(f, f->file_offset);
-       if ((f->file_offset != z->start) &&
-           (td->o.td_ddir != TD_DDIR_READ)) {
+       if (f->file_offset != z->start) {
                new_offset = zbd_zone_end(z);
                if (new_offset >= f->file_offset + f->io_size) {
                        log_info("%s: io_size must be at least one zone\n",
@@ -617,8 +702,7 @@ static bool zbd_zone_align_file_sizes(struct thread_data *td,
 
        z = zbd_offset_to_zone(f, f->file_offset + f->io_size);
        new_end = z->start;
-       if ((td->o.td_ddir != TD_DDIR_READ) &&
-           (f->file_offset + f->io_size != new_end)) {
+       if (f->file_offset + f->io_size != new_end) {
                if (new_end <= f->file_offset) {
                        log_info("%s: io_size must be at least one zone\n",
                                 f->file_name);
@@ -880,6 +964,7 @@ static int parse_zone_info(struct thread_data *td, struct fio_file *f)
        f->zbd_info->zone_size_log2 = is_power_of_2(zone_size) ?
                ilog2(zone_size) : 0;
        f->zbd_info->nr_zones = nr_zones;
+       f->zbd_info->max_active_zones = zbd_get_max_active_zones(td, f);
 
        if (same_zone_cap)
                dprint(FD_ZBD, "Zone capacity = %"PRIu64" KB\n",
@@ -1200,9 +1285,13 @@ int zbd_setup_files(struct thread_data *td)
                for (zi = f->min_zone; zi < f->max_zone; zi++) {
                        z = &zbd->zone_info[zi];
                        if (z->cond != ZBD_ZONE_COND_IMP_OPEN &&
-                           z->cond != ZBD_ZONE_COND_EXP_OPEN)
+                           z->cond != ZBD_ZONE_COND_EXP_OPEN &&
+                           z->cond != ZBD_ZONE_COND_CLOSED)
+                               continue;
+                       if (!zbd->max_active_zones &&
+                           z->cond == ZBD_ZONE_COND_CLOSED)
                                continue;
-                       if (zbd_write_zone_get(td, f, z))
+                       if (__zbd_write_zone_get(td, f, z))
                                continue;
                        /*
                         * If the number of open zones exceeds specified limits,
@@ -1499,11 +1588,11 @@ retry:
                dprint(FD_ZBD,
                       "%s(%s): wait zone write and retry write target zone selection\n",
                       __func__, f->file_name);
+               should_retry = in_flight;
                pthread_mutex_unlock(&zbdi->mutex);
                zone_unlock(z);
                io_u_quiesce(td);
                zone_lock(td, f, z);
-               should_retry = in_flight;
                goto retry;
        }
 
@@ -1666,10 +1755,9 @@ unlock:
 static void zbd_put_io(struct thread_data *td, const struct io_u *io_u)
 {
        const struct fio_file *f = io_u->file;
-       struct zoned_block_device_info *zbd_info = f->zbd_info;
        struct fio_zone_info *z;
 
-       assert(zbd_info);
+       assert(f->zbd_info);
 
        z = zbd_offset_to_zone(f, io_u->offset);
        assert(z->has_wp);
@@ -1797,7 +1885,8 @@ enum fio_ddir zbd_adjust_ddir(struct thread_data *td, struct io_u *io_u,
        if (ddir != DDIR_READ || !td_rw(td))
                return ddir;
 
-       if (io_u->file->last_start[DDIR_WRITE] != -1ULL || td->o.read_beyond_wp)
+       if (io_u->file->last_start[DDIR_WRITE] != -1ULL ||
+           td->o.read_beyond_wp || td->o.rwmix[DDIR_WRITE] == 0)
                return DDIR_READ;
 
        return DDIR_WRITE;
@@ -2023,7 +2112,7 @@ retry:
                         */
                        io_u_quiesce(td);
                        zb->reset_zone = 0;
-                       if (zbd_reset_zone(td, f, zb) < 0)
+                       if (__zbd_reset_zone(td, f, zb) < 0)
                                goto eof;
 
                        if (zb->capacity < min_bs) {
@@ -2092,6 +2181,7 @@ retry:
        case DDIR_WAIT:
        case DDIR_LAST:
        case DDIR_INVAL:
+       case DDIR_TIMEOUT:
                goto accept;
        }
 
@@ -2142,7 +2232,7 @@ char *zbd_write_status(const struct thread_stat *ts)
  * Return io_u_completed when reset zone succeeds. Return 0 when the target zone
  * does not have write pointer. On error, return negative errno.
  */
-int zbd_do_io_u_trim(const struct thread_data *td, struct io_u *io_u)
+int zbd_do_io_u_trim(struct thread_data *td, struct io_u *io_u)
 {
        struct fio_file *f = io_u->file;
        struct fio_zone_info *z;
@@ -2164,3 +2254,15 @@ int zbd_do_io_u_trim(const struct thread_data *td, struct io_u *io_u)
 
        return io_u_completed;
 }
+
+void zbd_log_err(const struct thread_data *td, const struct io_u *io_u)
+{
+       const struct fio_file *f = io_u->file;
+
+       if (td->o.zone_mode != ZONE_MODE_ZBD)
+               return;
+
+       if (io_u->error == EOVERFLOW)
+               log_err("%s: Exceeded max_active_zones limit. Check conditions of zones out of I/O ranges.\n",
+                       f->file_name);
+}