Wrap thread_data in thread_segment
[fio.git] / zbd.c
diff --git a/zbd.c b/zbd.c
index e4a480b7e7967216aa5ba9f5b7c21ac03b33d4c5..9327816aaf8f8eb68f7f971eb8958b9fbcf0e616 100644 (file)
--- a/zbd.c
+++ b/zbd.c
@@ -319,6 +319,7 @@ static bool zbd_verify_sizes(void)
 
                        f->min_zone = zbd_zone_idx(f, f->file_offset);
                        f->max_zone = zbd_zone_idx(f, f->file_offset + f->io_size);
+                       assert(f->min_zone < f->max_zone);
                }
        }
 
@@ -627,6 +628,11 @@ static int zbd_init_zone_info(struct thread_data *td, struct fio_file *file)
        return ret;
 }
 
+static bool zbd_open_zone(struct thread_data *td, const struct fio_file *f,
+                         uint32_t zone_idx);
+static int zbd_reset_zone(struct thread_data *td, struct fio_file *f,
+                         struct fio_zone_info *z);
+
 int zbd_setup_files(struct thread_data *td)
 {
        struct fio_file *f;
@@ -650,6 +656,8 @@ int zbd_setup_files(struct thread_data *td)
 
        for_each_file(td, f, i) {
                struct zoned_block_device_info *zbd = f->zbd_info;
+               struct fio_zone_info *z;
+               int zi;
 
                if (!zbd)
                        continue;
@@ -665,6 +673,23 @@ int zbd_setup_files(struct thread_data *td)
                        log_err("'max_open_zones' value is limited by %u\n", ZBD_MAX_OPEN_ZONES);
                        return 1;
                }
+
+               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)
+                               continue;
+                       if (zbd_open_zone(td, f, zi))
+                               continue;
+                       /*
+                        * If the number of open zones exceeds specified limits,
+                        * reset all extra open zones.
+                        */
+                       if (zbd_reset_zone(td, f, z) < 0) {
+                               log_err("Failed to reest zone %d\n", zi);
+                               return 1;
+                       }
+               }
        }
 
        return 0;
@@ -693,6 +718,9 @@ static int zbd_reset_zone(struct thread_data *td, struct fio_file *f,
        uint64_t length = (z+1)->start - offset;
        int ret = 0;
 
+       if (z->wp == z->start)
+               return 0;
+
        assert(is_valid_offset(f, offset + length - 1));
 
        dprint(FD_ZBD, "%s: resetting wp of zone %u.\n", f->file_name,
@@ -721,12 +749,21 @@ static int zbd_reset_zone(struct thread_data *td, struct fio_file *f,
 
 /* The caller must hold f->zbd_info->mutex */
 static void zbd_close_zone(struct thread_data *td, const struct fio_file *f,
-                          unsigned int open_zone_idx)
+                          unsigned int zone_idx)
 {
-       uint32_t zone_idx;
+       uint32_t open_zone_idx = 0;
 
-       assert(open_zone_idx < f->zbd_info->num_open_zones);
-       zone_idx = f->zbd_info->open_zones[open_zone_idx];
+       for (; open_zone_idx < f->zbd_info->num_open_zones; open_zone_idx++) {
+               if (f->zbd_info->open_zones[open_zone_idx] == zone_idx)
+                       break;
+       }
+       if (open_zone_idx == f->zbd_info->num_open_zones) {
+               dprint(FD_ZBD, "%s: zone %d is not open\n",
+                      f->file_name, zone_idx);
+               return;
+       }
+
+       dprint(FD_ZBD, "%s: closing zone %d\n", f->file_name, zone_idx);
        memmove(f->zbd_info->open_zones + open_zone_idx,
                f->zbd_info->open_zones + open_zone_idx + 1,
                (ZBD_MAX_OPEN_ZONES - (open_zone_idx + 1)) *
@@ -765,13 +802,8 @@ static int zbd_reset_zones(struct thread_data *td, struct fio_file *f,
                        continue;
                zone_lock(td, f, z);
                if (all_zones) {
-                       unsigned int i;
-
                        pthread_mutex_lock(&f->zbd_info->mutex);
-                       for (i = 0; i < f->zbd_info->num_open_zones; i++) {
-                               if (f->zbd_info->open_zones[i] == nz)
-                                       zbd_close_zone(td, f, i);
-                       }
+                       zbd_close_zone(td, f, nz);
                        pthread_mutex_unlock(&f->zbd_info->mutex);
 
                        reset_wp = z->wp != z->start;
@@ -839,9 +871,8 @@ static uint64_t zbd_process_swd(const struct fio_file *f, enum swd_action a)
        struct fio_zone_info *zb, *ze, *z;
        uint64_t swd = 0;
 
-       zb = &f->zbd_info->zone_info[zbd_zone_idx(f, f->file_offset)];
-       ze = &f->zbd_info->zone_info[zbd_zone_idx(f, f->file_offset +
-                                                 f->io_size)];
+       zb = &f->zbd_info->zone_info[f->min_zone];
+       ze = &f->zbd_info->zone_info[f->max_zone];
        for (z = zb; z < ze; z++) {
                pthread_mutex_lock(&z->mutex);
                swd += z->wp - z->start;
@@ -933,11 +964,10 @@ static bool is_zone_open(const struct thread_data *td, const struct fio_file *f,
  * was not yet open and opening a new zone would cause the zone limit to be
  * exceeded.
  */
-static bool zbd_open_zone(struct thread_data *td, const struct io_u *io_u,
+static bool zbd_open_zone(struct thread_data *td, const struct fio_file *f,
                          uint32_t zone_idx)
 {
        const uint32_t min_bs = td->o.min_bs[DDIR_WRITE];
-       const struct fio_file *f = io_u->file;
        struct fio_zone_info *z = &f->zbd_info->zone_info[zone_idx];
        bool res = true;
 
@@ -952,8 +982,15 @@ static bool zbd_open_zone(struct thread_data *td, const struct io_u *io_u,
                return false;
 
        pthread_mutex_lock(&f->zbd_info->mutex);
-       if (is_zone_open(td, f, zone_idx))
+       if (is_zone_open(td, f, zone_idx)) {
+               /*
+                * If the zone is already open and going to be full by writes
+                * in-flight, handle it as a full zone instead of an open zone.
+                */
+               if (z->wp >= zbd_zone_capacity_end(z))
+                       res = false;
                goto out;
+       }
        res = false;
        /* Zero means no limit */
        if (td->o.job_max_open_zones > 0 &&
@@ -995,6 +1032,7 @@ static struct fio_zone_info *zbd_convert_to_open_zone(struct thread_data *td,
        unsigned int open_zone_idx = -1;
        uint32_t zone_idx, new_zone_idx;
        int i;
+       bool wait_zone_close;
 
        assert(is_valid_offset(f, io_u->offset));
 
@@ -1030,11 +1068,9 @@ static struct fio_zone_info *zbd_convert_to_open_zone(struct thread_data *td,
                if (td->o.max_open_zones == 0 && td->o.job_max_open_zones == 0)
                        goto examine_zone;
                if (f->zbd_info->num_open_zones == 0) {
-                       pthread_mutex_unlock(&f->zbd_info->mutex);
-                       pthread_mutex_unlock(&z->mutex);
                        dprint(FD_ZBD, "%s(%s): no zones are open\n",
                               __func__, f->file_name);
-                       return NULL;
+                       goto open_other_zone;
                }
 
                /*
@@ -1081,14 +1117,30 @@ examine_zone:
                pthread_mutex_unlock(&f->zbd_info->mutex);
                goto out;
        }
-       dprint(FD_ZBD, "%s(%s): closing zone %d\n", __func__, f->file_name,
-              zone_idx);
-       if (td->o.max_open_zones || td->o.job_max_open_zones)
-               zbd_close_zone(td, f, open_zone_idx);
+
+open_other_zone:
+       /* Check if number of open zones reaches one of limits. */
+       wait_zone_close =
+               f->zbd_info->num_open_zones == f->max_zone - f->min_zone ||
+               (td->o.max_open_zones &&
+                f->zbd_info->num_open_zones == td->o.max_open_zones) ||
+               (td->o.job_max_open_zones &&
+                td->num_open_zones == td->o.job_max_open_zones);
+
        pthread_mutex_unlock(&f->zbd_info->mutex);
 
        /* Only z->mutex is held. */
 
+       /*
+        * When number of open zones reaches to one of limits, wait for
+        * zone close before opening a new zone.
+        */
+       if (wait_zone_close) {
+               dprint(FD_ZBD, "%s(%s): quiesce to allow open zones to close\n",
+                      __func__, f->file_name);
+               io_u_quiesce(td);
+       }
+
        /* Zone 'z' is full, so try to open a new zone. */
        for (i = f->io_size / f->zbd_info->zone_size; i > 0; i--) {
                zone_idx++;
@@ -1103,7 +1155,7 @@ examine_zone:
                zone_lock(td, f, z);
                if (z->open)
                        continue;
-               if (zbd_open_zone(td, io_u, zone_idx))
+               if (zbd_open_zone(td, f, zone_idx))
                        goto out;
        }
 
@@ -1146,7 +1198,7 @@ static struct fio_zone_info *zbd_replay_write_order(struct thread_data *td,
        const struct fio_file *f = io_u->file;
        const uint32_t min_bs = td->o.min_bs[DDIR_WRITE];
 
-       if (!zbd_open_zone(td, io_u, z - f->zbd_info->zone_info)) {
+       if (!zbd_open_zone(td, f, z - f->zbd_info->zone_info)) {
                pthread_mutex_unlock(&z->mutex);
                z = zbd_convert_to_open_zone(td, io_u);
                assert(z);
@@ -1175,7 +1227,7 @@ zbd_find_zone(struct thread_data *td, struct io_u *io_u,
        struct fio_file *f = io_u->file;
        struct fio_zone_info *z1, *z2;
        const struct fio_zone_info *const zf =
-               &f->zbd_info->zone_info[zbd_zone_idx(f, f->file_offset)];
+               &f->zbd_info->zone_info[f->min_zone];
 
        /*
         * Skip to the next non-empty zone in case of sequential I/O and to
@@ -1203,6 +1255,28 @@ zbd_find_zone(struct thread_data *td, struct io_u *io_u,
        return NULL;
 }
 
+/**
+ * zbd_end_zone_io - update zone status at command completion
+ * @io_u: I/O unit
+ * @z: zone info pointer
+ *
+ * If the write command made the zone full, close it.
+ *
+ * The caller must hold z->mutex.
+ */
+static void zbd_end_zone_io(struct thread_data *td, const struct io_u *io_u,
+                           struct fio_zone_info *z)
+{
+       const struct fio_file *f = io_u->file;
+
+       if (io_u->ddir == DDIR_WRITE &&
+           io_u->offset + io_u->buflen >= zbd_zone_capacity_end(z)) {
+               pthread_mutex_lock(&f->zbd_info->mutex);
+               zbd_close_zone(td, f, z - f->zbd_info->zone_info);
+               pthread_mutex_unlock(&f->zbd_info->mutex);
+       }
+}
+
 /**
  * zbd_queue_io - update the write pointer of a sequential zone
  * @io_u: I/O unit
@@ -1212,7 +1286,8 @@ zbd_find_zone(struct thread_data *td, struct io_u *io_u,
  * For write and trim operations, update the write pointer of the I/O unit
  * target zone.
  */
-static void zbd_queue_io(struct io_u *io_u, int q, bool success)
+static void zbd_queue_io(struct thread_data *td, struct io_u *io_u, int q,
+                        bool success)
 {
        const struct fio_file *f = io_u->file;
        struct zoned_block_device_info *zbd_info = f->zbd_info;
@@ -1258,6 +1333,9 @@ static void zbd_queue_io(struct io_u *io_u, int q, bool success)
                break;
        }
 
+       if (q == FIO_Q_COMPLETED && !io_u->error)
+               zbd_end_zone_io(td, io_u, z);
+
 unlock:
        if (!success || q != FIO_Q_QUEUED) {
                /* BUSY or COMPLETED: unlock the zone */
@@ -1270,7 +1348,7 @@ unlock:
  * zbd_put_io - Unlock an I/O unit target zone lock
  * @io_u: I/O unit
  */
-static void zbd_put_io(const struct io_u *io_u)
+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;
@@ -1292,6 +1370,8 @@ static void zbd_put_io(const struct io_u *io_u)
               "%s: terminate I/O (%lld, %llu) for zone %u\n",
               f->file_name, io_u->offset, io_u->buflen, zone_idx);
 
+       zbd_end_zone_io(td, io_u, z);
+
        ret = pthread_mutex_unlock(&z->mutex);
        assert(ret == 0);
        zbd_check_swd(f);
@@ -1467,9 +1547,9 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u)
 
        switch (io_u->ddir) {
        case DDIR_READ:
-               if (td->runstate == TD_VERIFYING) {
-                       if (td_write(td))
-                               zb = zbd_replay_write_order(td, io_u, zb);
+               if (td->runstate == TD_VERIFYING && td_write(td)) {
+                       zb = zbd_replay_write_order(td, io_u, zb);
+                       pthread_mutex_unlock(&zb->mutex);
                        goto accept;
                }
                /*
@@ -1482,8 +1562,7 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u)
                if (range < min_bs ||
                    ((!td_random(td)) && (io_u->offset + min_bs > zb->wp))) {
                        pthread_mutex_unlock(&zb->mutex);
-                       zl = &f->zbd_info->zone_info[zbd_zone_idx(f,
-                                               f->file_offset + f->io_size)];
+                       zl = &f->zbd_info->zone_info[f->max_zone];
                        zb = zbd_find_zone(td, io_u, zb, zl);
                        if (!zb) {
                                dprint(FD_ZBD,
@@ -1529,7 +1608,7 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u)
        case DDIR_WRITE:
                if (io_u->buflen > f->zbd_info->zone_size)
                        goto eof;
-               if (!zbd_open_zone(td, io_u, zone_idx_b)) {
+               if (!zbd_open_zone(td, f, zone_idx_b)) {
                        pthread_mutex_unlock(&zb->mutex);
                        zb = zbd_convert_to_open_zone(td, io_u);
                        if (!zb)