os: define EDQUOT to EIO if the OS doesn't provide it
[fio.git] / zbd.c
diff --git a/zbd.c b/zbd.c
index 634ee8f6289ba260232ce1d738357cab92551365..eed796b3217d297eb94942b28b4cfbf0ab5113ab 100644 (file)
--- a/zbd.c
+++ b/zbd.c
@@ -285,9 +285,7 @@ static bool zbd_verify_sizes(void)
                                return false;
                        }
 
-                       if (td->o.zone_skip &&
-                           (td->o.zone_skip < td->o.zone_size ||
-                            td->o.zone_skip % td->o.zone_size)) {
+                       if (td->o.zone_skip % td->o.zone_size) {
                                log_err("%s: zoneskip %llu is not a multiple of the device zone size %llu.\n",
                                        f->file_name, (unsigned long long) td->o.zone_skip,
                                        (unsigned long long) td->o.zone_size);
@@ -335,20 +333,21 @@ static bool zbd_verify_bs(void)
 {
        struct thread_data *td;
        struct fio_file *f;
-       uint32_t zone_size;
        int i, j, k;
 
        for_each_td(td, i) {
                for_each_file(td, f, j) {
+                       uint64_t zone_size;
+
                        if (!f->zbd_info)
                                continue;
                        zone_size = f->zbd_info->zone_size;
                        for (k = 0; k < FIO_ARRAY_SIZE(td->o.bs); k++) {
                                if (td->o.verify != VERIFY_NONE &&
                                    zone_size % td->o.bs[k] != 0) {
-                                       log_info("%s: block size %llu is not a divisor of the zone size %d\n",
+                                       log_info("%s: block size %llu is not a divisor of the zone size %llu\n",
                                                 f->file_name, td->o.bs[k],
-                                                zone_size);
+                                                (unsigned long long)zone_size);
                                        return false;
                                }
                        }
@@ -648,7 +647,7 @@ static bool zbd_open_zone(struct thread_data *td, const struct fio_file *f,
 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)
+int zbd_init_files(struct thread_data *td)
 {
        struct fio_file *f;
        int i;
@@ -657,6 +656,44 @@ int zbd_setup_files(struct thread_data *td)
                if (zbd_init_zone_info(td, f))
                        return 1;
        }
+       return 0;
+}
+
+void zbd_recalc_options_with_zone_granularity(struct thread_data *td)
+{
+       struct fio_file *f;
+       int i;
+
+       for_each_file(td, f, i) {
+               struct zoned_block_device_info *zbd = f->zbd_info;
+               // zonemode=strided doesn't get per-file zone size.
+               uint64_t zone_size = zbd ? zbd->zone_size : td->o.zone_size;
+
+               if (zone_size == 0)
+                       continue;
+
+               if (td->o.size_nz > 0) {
+                       td->o.size = td->o.size_nz * zone_size;
+               }
+               if (td->o.io_size_nz > 0) {
+                       td->o.io_size = td->o.io_size_nz * zone_size;
+               }
+               if (td->o.start_offset_nz > 0) {
+                       td->o.start_offset = td->o.start_offset_nz * zone_size;
+               }
+               if (td->o.offset_increment_nz > 0) {
+                       td->o.offset_increment = td->o.offset_increment_nz * zone_size;
+               }
+               if (td->o.zone_skip_nz > 0) {
+                       td->o.zone_skip = td->o.zone_skip_nz * zone_size;
+               }
+       }
+}
+
+int zbd_setup_files(struct thread_data *td)
+{
+       struct fio_file *f;
+       int i;
 
        if (!zbd_using_direct_io()) {
                log_err("Using direct I/O is mandatory for writing to ZBD drives\n\n");
@@ -805,16 +842,13 @@ static void zbd_close_zone(struct thread_data *td, const struct fio_file *f,
  * @f: fio file for which to reset zones
  * @zb: first zone to reset.
  * @ze: first zone not to reset.
- * @all_zones: whether to reset all zones or only those zones for which the
- *     write pointer is not a multiple of td->o.min_bs[DDIR_WRITE].
  */
 static int zbd_reset_zones(struct thread_data *td, struct fio_file *f,
                           struct fio_zone_info *const zb,
-                          struct fio_zone_info *const ze, bool all_zones)
+                          struct fio_zone_info *const ze)
 {
        struct fio_zone_info *z;
        const uint32_t min_bs = td->o.min_bs[DDIR_WRITE];
-       bool reset_wp;
        int res = 0;
 
        assert(min_bs);
@@ -827,16 +861,10 @@ static int zbd_reset_zones(struct thread_data *td, struct fio_file *f,
                if (!z->has_wp)
                        continue;
                zone_lock(td, f, z);
-               if (all_zones) {
-                       pthread_mutex_lock(&f->zbd_info->mutex);
-                       zbd_close_zone(td, f, nz);
-                       pthread_mutex_unlock(&f->zbd_info->mutex);
-
-                       reset_wp = z->wp != z->start;
-               } else {
-                       reset_wp = z->wp % min_bs != 0;
-               }
-               if (reset_wp) {
+               pthread_mutex_lock(&f->zbd_info->mutex);
+               zbd_close_zone(td, f, nz);
+               pthread_mutex_unlock(&f->zbd_info->mutex);
+               if (z->wp != z->start) {
                        dprint(FD_ZBD, "%s: resetting zone %u\n",
                               f->file_name, zbd_zone_nr(f, z));
                        if (zbd_reset_zone(td, f, z) < 0)
@@ -959,8 +987,8 @@ void zbd_file_reset(struct thread_data *td, struct fio_file *f)
         * writing any data to avoid that a zone reset has to be issued while
         * writing data, which causes data loss.
         */
-       zbd_reset_zones(td, f, zb, ze, td->o.verify != VERIFY_NONE &&
-                       td->runstate != TD_VERIFYING);
+       if (td->o.verify != VERIFY_NONE && td->runstate != TD_VERIFYING)
+               zbd_reset_zones(td, f, zb, ze);
        zbd_reset_write_cnt(td, f);
 }
 
@@ -1087,16 +1115,18 @@ static struct fio_zone_info *zbd_convert_to_open_zone(struct thread_data *td,
                uint32_t tmp_idx;
 
                z = get_zone(f, zone_idx);
-
-               zone_lock(td, f, z);
+               if (z->has_wp)
+                       zone_lock(td, f, z);
                pthread_mutex_lock(&f->zbd_info->mutex);
-               if (z->cond != ZBD_ZONE_COND_OFFLINE &&
-                   td->o.max_open_zones == 0 && td->o.job_max_open_zones == 0)
-                       goto examine_zone;
-               if (f->zbd_info->num_open_zones == 0) {
-                       dprint(FD_ZBD, "%s(%s): no zones are open\n",
-                              __func__, f->file_name);
-                       goto open_other_zone;
+               if (z->has_wp) {
+                       if (z->cond != ZBD_ZONE_COND_OFFLINE &&
+                           td->o.max_open_zones == 0 && td->o.job_max_open_zones == 0)
+                               goto examine_zone;
+                       if (f->zbd_info->num_open_zones == 0) {
+                               dprint(FD_ZBD, "%s(%s): no zones are open\n",
+                                      __func__, f->file_name);
+                               goto open_other_zone;
+                       }
                }
 
                /*
@@ -1105,7 +1135,8 @@ static struct fio_zone_info *zbd_convert_to_open_zone(struct thread_data *td,
                 * Ignore zones which don't belong to thread's offset/size area.
                 */
                open_zone_idx = pick_random_zone_idx(f, io_u);
-               assert(open_zone_idx < f->zbd_info->num_open_zones);
+               assert(!open_zone_idx ||
+                      open_zone_idx < f->zbd_info->num_open_zones);
                tmp_idx = open_zone_idx;
                for (i = 0; i < f->zbd_info->num_open_zones; i++) {
                        uint32_t tmpz;
@@ -1124,7 +1155,8 @@ static struct fio_zone_info *zbd_convert_to_open_zone(struct thread_data *td,
                dprint(FD_ZBD, "%s(%s): no candidate zone\n",
                        __func__, f->file_name);
                pthread_mutex_unlock(&f->zbd_info->mutex);
-               zone_unlock(z);
+               if (z->has_wp)
+                       zone_unlock(z);
                return NULL;
 
 found_candidate_zone:
@@ -1133,7 +1165,8 @@ found_candidate_zone:
                        break;
                zone_idx = new_zone_idx;
                pthread_mutex_unlock(&f->zbd_info->mutex);
-               zone_unlock(z);
+               if (z->has_wp)
+                       zone_unlock(z);
        }
 
        /* Both z->mutex and f->zbd_info->mutex are held. */
@@ -1235,10 +1268,23 @@ static struct fio_zone_info *zbd_replay_write_order(struct thread_data *td,
                assert(z);
        }
 
-       if (z->verify_block * min_bs >= z->capacity)
+       if (z->verify_block * min_bs >= z->capacity) {
                log_err("%s: %d * %d >= %llu\n", f->file_name, z->verify_block,
                        min_bs, (unsigned long long)z->capacity);
-       io_u->offset = z->start + z->verify_block++ * min_bs;
+               /*
+                * If the assertion below fails during a test run, adding
+                * "--experimental_verify=1" to the command line may help.
+                */
+               assert(false);
+       }
+       io_u->offset = z->start + z->verify_block * min_bs;
+       if (io_u->offset + io_u->buflen >= zbd_zone_capacity_end(z)) {
+               log_err("%s: %llu + %llu >= %llu\n", f->file_name, io_u->offset,
+                       io_u->buflen, (unsigned long long) zbd_zone_capacity_end(z));
+               assert(false);
+       }
+       z->verify_block += io_u->buflen / min_bs;
+
        return z;
 }
 
@@ -1574,9 +1620,9 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u)
 
                if (io_u->offset + min_bs > (zb + 1)->start) {
                        dprint(FD_IO,
-                              "%s: off=%llu + min_bs=%u > next zone %lu\n",
-                              f->file_name, io_u->offset, min_bs,
-                              (zb + 1)->start);
+                              "%s: off=%llu + min_bs=%u > next zone %llu\n",
+                              f->file_name, io_u->offset,
+                              min_bs, (unsigned long long) (zb + 1)->start);
                        io_u->offset = zb->start + (zb + 1)->start - io_u->offset;
                        new_len = min(io_u->buflen, (zb + 1)->start - io_u->offset);
                } else {
@@ -1602,12 +1648,6 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u)
        case DDIR_READ:
                if (td->runstate == TD_VERIFYING && td_write(td)) {
                        zb = zbd_replay_write_order(td, io_u, zb);
-                       /*
-                        * Since we return with the zone lock still held,
-                        * add an annotation to let Coverity know that it
-                        * is intentional.
-                        */
-                       /* coverity[missing_unlock] */
                        goto accept;
                }
                /*
@@ -1670,13 +1710,22 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u)
                assert(io_u->offset + io_u->buflen <= zb->wp);
                goto accept;
        case DDIR_WRITE:
-               if (io_u->buflen > f->zbd_info->zone_size)
+               if (io_u->buflen > f->zbd_info->zone_size) {
+                       td_verror(td, EINVAL, "I/O buflen exceeds zone size");
+                       dprint(FD_IO,
+                              "%s: I/O buflen %llu exceeds zone size %llu\n",
+                              f->file_name, io_u->buflen,
+                              (unsigned long long) f->zbd_info->zone_size);
                        goto eof;
+               }
                if (!zbd_open_zone(td, f, zone_idx_b)) {
                        zone_unlock(zb);
                        zb = zbd_convert_to_open_zone(td, io_u);
-                       if (!zb)
+                       if (!zb) {
+                               dprint(FD_IO, "%s: can't convert to open zone",
+                                      f->file_name);
                                goto eof;
+                       }
                        zone_idx_b = zbd_zone_nr(f, zb);
                }
                /* Check whether the zone reset threshold has been exceeded */
@@ -1703,6 +1752,7 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u)
                                goto eof;
 
                        if (zb->capacity < min_bs) {
+                               td_verror(td, EINVAL, "ZCAP is less min_bs");
                                log_err("zone capacity %llu smaller than minimum block size %d\n",
                                        (unsigned long long)zb->capacity,
                                        min_bs);
@@ -1713,8 +1763,9 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u)
                assert(!zbd_zone_full(f, zb, min_bs));
                io_u->offset = zb->wp;
                if (!is_valid_offset(f, io_u->offset)) {
-                       dprint(FD_ZBD, "Dropped request with offset %llu\n",
-                              io_u->offset);
+                       td_verror(td, EINVAL, "invalid WP value");
+                       dprint(FD_ZBD, "%s: dropped request with offset %llu\n",
+                              f->file_name, io_u->offset);
                        goto eof;
                }
                /*
@@ -1733,9 +1784,9 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u)
                               orig_len, io_u->buflen);
                        goto accept;
                }
-               log_err("Zone remainder %lld smaller than minimum block size %d\n",
-                       (zbd_zone_capacity_end(zb) - io_u->offset),
-                       min_bs);
+               td_verror(td, EIO, "zone remainder too small");
+               log_err("zone remainder %lld smaller than min block size %d\n",
+                       (zbd_zone_capacity_end(zb) - io_u->offset), min_bs);
                goto eof;
        case DDIR_TRIM:
                /* fall-through */
@@ -1757,6 +1808,12 @@ accept:
        assert(!io_u->zbd_put_io);
        io_u->zbd_queue_io = zbd_queue_io;
        io_u->zbd_put_io = zbd_put_io;
+       /*
+        * Since we return with the zone lock still held,
+        * add an annotation to let Coverity know that it
+        * is intentional.
+        */
+       /* coverity[missing_unlock] */
        return io_u_accept;
 
 eof: