zbd: Cleanup zbd_init()
[fio.git] / zbd.c
diff --git a/zbd.c b/zbd.c
index 9b603b97b18344b5e17baf31f199371a8c5f11ff..09ddcca42e7c011f1a7f2d30211a4b0beb227d56 100644 (file)
--- a/zbd.c
+++ b/zbd.c
@@ -186,11 +186,14 @@ static bool zbd_verify_bs(void)
  * size of @buf.
  *
  * Returns 0 upon success and a negative error code upon failure.
+ * If the zone report is empty, always assume an error (device problem) and
+ * return -EIO.
  */
 static int read_zone_info(int fd, uint64_t start_sector,
                          void *buf, unsigned int bufsz)
 {
        struct blk_zone_report *hdr = buf;
+       int ret;
 
        if (bufsz < sizeof(*hdr))
                return -EINVAL;
@@ -199,7 +202,12 @@ static int read_zone_info(int fd, uint64_t start_sector,
 
        hdr->nr_zones = (bufsz - sizeof(*hdr)) / sizeof(struct blk_zone);
        hdr->sector = start_sector;
-       return ioctl(fd, BLKREPORTZONE, hdr) >= 0 ? 0 : -errno;
+       ret = ioctl(fd, BLKREPORTZONE, hdr);
+       if (ret)
+               return -errno;
+       if (!hdr->nr_zones)
+               return -EIO;
+       return 0;
 }
 
 /*
@@ -304,13 +312,23 @@ static int init_zone_info(struct thread_data *td, struct fio_file *f)
 {
        uint32_t nr_zones;
        struct fio_zone_info *p;
-       uint64_t zone_size;
+       uint64_t zone_size = td->o.zone_size;
        struct zoned_block_device_info *zbd_info = NULL;
        pthread_mutexattr_t attr;
        int i;
 
-       zone_size = td->o.zone_size;
-       assert(zone_size);
+       if (zone_size == 0) {
+               log_err("%s: Specifying the zone size is mandatory for regular block devices with --zonemode=zbd\n\n",
+                       f->file_name);
+               return 1;
+       }
+
+       if (zone_size < 512) {
+               log_err("%s: zone size must be at least 512 bytes for --zonemode=zbd\n\n",
+                       f->file_name);
+               return 1;
+       }
+
        nr_zones = (f->real_file_size + zone_size - 1) / zone_size;
        zbd_info = scalloc(1, sizeof(*zbd_info) +
                           (nr_zones + 1) * sizeof(zbd_info->zone_info[0]));
@@ -418,8 +436,6 @@ static int parse_zone_info(struct thread_data *td, struct fio_file *f)
                        p->start = z->start << 9;
                        switch (z->cond) {
                        case BLK_ZONE_COND_NOT_WP:
-                               p->wp = p->start;
-                               break;
                        case BLK_ZONE_COND_FULL:
                                p->wp = p->start + zone_size;
                                break;
@@ -475,7 +491,7 @@ out:
  *
  * Returns 0 upon success and a negative error code upon failure.
  */
-int zbd_create_zone_info(struct thread_data *td, struct fio_file *f)
+static int zbd_create_zone_info(struct thread_data *td, struct fio_file *f)
 {
        enum blk_zoned_model zbd_model;
        int ret = 0;
@@ -555,18 +571,8 @@ int zbd_init(struct thread_data *td)
        for_each_file(td, f, i) {
                if (f->filetype != FIO_TYPE_BLOCK)
                        continue;
-               if (td->o.zone_size && td->o.zone_size < 512) {
-                       log_err("%s: zone size must be at least 512 bytes for --zonemode=zbd\n\n",
-                               f->file_name);
-                       return 1;
-               }
-               if (td->o.zone_size == 0 &&
-                   get_zbd_model(f->file_name) == ZBD_DM_NONE) {
-                       log_err("%s: Specifying the zone size is mandatory for regular block devices with --zonemode=zbd\n\n",
-                               f->file_name);
+               if (zbd_init_zone_info(td, f))
                        return 1;
-               }
-               zbd_init_zone_info(td, f);
        }
 
        if (!zbd_using_direct_io()) {
@@ -927,8 +933,8 @@ static void zbd_close_zone(struct thread_data *td, const struct fio_file *f,
  * a multiple of the fio block size. The caller must neither hold z->mutex
  * nor f->zbd_info->mutex. Returns with z->mutex held upon success.
  */
-struct fio_zone_info *zbd_convert_to_open_zone(struct thread_data *td,
-                                              struct io_u *io_u)
+static struct fio_zone_info *zbd_convert_to_open_zone(struct thread_data *td,
+                                                     struct io_u *io_u)
 {
        const uint32_t min_bs = td->o.min_bs[io_u->ddir];
        const struct fio_file *f = io_u->file;
@@ -1111,37 +1117,44 @@ zbd_find_zone(struct thread_data *td, struct io_u *io_u,
        return NULL;
 }
 
-
 /**
- * zbd_post_submit - update the write pointer and unlock the zone lock
+ * zbd_queue_io - update the write pointer of a sequential zone
  * @io_u: I/O unit
- * @success: Whether or not the I/O unit has been executed successfully
+ * @success: Whether or not the I/O unit has been queued successfully
+ * @q: queueing status (busy, completed or queued).
  *
- * For write and trim operations, update the write pointer of all affected
- * zones.
+ * For write and trim operations, update the write pointer of the I/O unit
+ * target zone.
  */
-static void zbd_post_submit(const struct io_u *io_u, bool success)
+static void zbd_queue_io(struct io_u *io_u, int q, bool success)
 {
-       struct zoned_block_device_info *zbd_info;
+       const struct fio_file *f = io_u->file;
+       struct zoned_block_device_info *zbd_info = f->zbd_info;
        struct fio_zone_info *z;
        uint32_t zone_idx;
-       uint64_t end, zone_end;
+       uint64_t zone_end;
 
-       zbd_info = io_u->file->zbd_info;
        if (!zbd_info)
                return;
 
-       zone_idx = zbd_zone_idx(io_u->file, io_u->offset);
-       end = io_u->offset + io_u->buflen;
-       z = &zbd_info->zone_info[zone_idx];
+       zone_idx = zbd_zone_idx(f, io_u->offset);
        assert(zone_idx < zbd_info->nr_zones);
+       z = &zbd_info->zone_info[zone_idx];
+
        if (z->type != BLK_ZONE_TYPE_SEQWRITE_REQ)
                return;
+
        if (!success)
                goto unlock;
+
+       dprint(FD_ZBD,
+              "%s: queued I/O (%lld, %llu) for zone %u\n",
+              f->file_name, io_u->offset, io_u->buflen, zone_idx);
+
        switch (io_u->ddir) {
        case DDIR_WRITE:
-               zone_end = min(end, (z + 1)->start);
+               zone_end = min((uint64_t)(io_u->offset + io_u->buflen),
+                              (z + 1)->start);
                pthread_mutex_lock(&zbd_info->mutex);
                /*
                 * z->wp > zone_end means that one or more I/O errors
@@ -1158,10 +1171,42 @@ static void zbd_post_submit(const struct io_u *io_u, bool success)
        default:
                break;
        }
+
 unlock:
-       pthread_mutex_unlock(&z->mutex);
+       if (!success || q != FIO_Q_QUEUED) {
+               /* BUSY or COMPLETED: unlock the zone */
+               pthread_mutex_unlock(&z->mutex);
+               io_u->zbd_put_io = NULL;
+       }
+}
 
-       zbd_check_swd(io_u->file);
+/**
+ * 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)
+{
+       const struct fio_file *f = io_u->file;
+       struct zoned_block_device_info *zbd_info = f->zbd_info;
+       struct fio_zone_info *z;
+       uint32_t zone_idx;
+
+       if (!zbd_info)
+               return;
+
+       zone_idx = zbd_zone_idx(f, io_u->offset);
+       assert(zone_idx < zbd_info->nr_zones);
+       z = &zbd_info->zone_info[zone_idx];
+
+       if (z->type != BLK_ZONE_TYPE_SEQWRITE_REQ)
+               return;
+
+       dprint(FD_ZBD,
+              "%s: terminate I/O (%lld, %llu) for zone %u\n",
+              f->file_name, io_u->offset, io_u->buflen, zone_idx);
+
+       assert(pthread_mutex_unlock(&z->mutex) == 0);
+       zbd_check_swd(f);
 }
 
 bool zbd_unaligned_write(int error_code)
@@ -1216,7 +1261,21 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u)
 
        zbd_check_swd(f);
 
-       pthread_mutex_lock(&zb->mutex);
+       /*
+        * Lock the io_u target zone. The zone will be unlocked if io_u offset
+        * is changed or when io_u completes and zbd_put_io() executed.
+        * To avoid multiple jobs doing asynchronous I/Os from deadlocking each
+        * other waiting for zone locks when building an io_u batch, first
+        * only trylock the zone. If the zone is already locked by another job,
+        * process the currently queued I/Os so that I/O progress is made and
+        * zones unlocked.
+        */
+       if (pthread_mutex_trylock(&zb->mutex) != 0) {
+               if (!td_ioengine_flagged(td, FIO_SYNCIO))
+                       io_u_quiesce(td);
+               pthread_mutex_lock(&zb->mutex);
+       }
+
        switch (io_u->ddir) {
        case DDIR_READ:
                if (td->runstate == TD_VERIFYING) {
@@ -1354,8 +1413,10 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u)
 accept:
        assert(zb);
        assert(zb->cond != BLK_ZONE_COND_OFFLINE);
-       assert(!io_u->post_submit);
-       io_u->post_submit = zbd_post_submit;
+       assert(!io_u->zbd_queue_io);
+       assert(!io_u->zbd_put_io);
+       io_u->zbd_queue_io = zbd_queue_io;
+       io_u->zbd_put_io = zbd_put_io;
        return io_u_accept;
 
 eof: