zbd: finish zone when all random write target zones have small remainder
authorShin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Mon, 14 Apr 2025 06:27:20 +0000 (15:27 +0900)
committerJens Axboe <axboe@kernel.dk>
Mon, 14 Apr 2025 14:27:31 +0000 (08:27 -0600)
When a random write target offset points to a zone that is not writable,
zbd_convert_to_write_zone() attempts to convert the write offset to a
different, writable zone. However, the conversion fails when all of the
following conditions are met:

1) the workload has the max_open_zones limit
2) every write target zones, up to the max_open_zones limit, has
   remainder sectors smaller than the block size
3) the next random write request targets a zone not in the write target
   zone list

In this case, zbd_convert_to_write_zone() can not open another zone
without exceeding the max_open_zones constraint. Therefore, It does not
convert the write to a different zone printing with the debug message
"did not choose another write zone". This leads to an unexpected stop of
the random write workload.

To prevent the unexpected write stop, finish one of the write target
zones with small remainder sectors. Check if all write target zones have
small remainder, and store the result in the new local boolean variable
all_write_zones_have_small_remainder. When this condition is true,
choose one of the write target zones and finish it. Then return the zone
from zbd_convert_to_write_zone() enabling the write process to continue.

Signed-off-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Link: https://lore.kernel.org/r/20250414062721.87641-4-shinichiro.kawasaki@wdc.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
zbd.c

diff --git a/zbd.c b/zbd.c
index d547bf3b3cf6be168f4c5856297531c5200cabc7..895192342cc2443f55560be4e783091eb93c82d5 100644 (file)
--- a/zbd.c
+++ b/zbd.c
@@ -1460,6 +1460,7 @@ static struct fio_zone_info *zbd_convert_to_write_zone(struct thread_data *td,
        bool wait_zone_write;
        bool in_flight;
        bool should_retry = true;
+       bool need_zone_finish;
 
        assert(is_valid_offset(f, io_u->offset));
 
@@ -1611,6 +1612,7 @@ retry:
 
        /* Check whether the write fits in any of the write target zones. */
        pthread_mutex_lock(&zbdi->mutex);
+       need_zone_finish = true;
        for (i = 0; i < zbdi->num_write_zones; i++) {
                zone_idx = zbdi->write_zones[i];
                if (zone_idx < f->min_zone || zone_idx >= f->max_zone)
@@ -1621,8 +1623,10 @@ retry:
                z = zbd_get_zone(f, zone_idx);
 
                zone_lock(td, f, z);
-               if (zbd_zone_remainder(z) >= min_bs)
+               if (zbd_zone_remainder(z) >= min_bs) {
+                       need_zone_finish = false;
                        goto out;
+               }
                pthread_mutex_lock(&zbdi->mutex);
        }
 
@@ -1645,6 +1649,26 @@ retry:
                goto retry;
        }
 
+       if (td_random(td) && td->o.verify == VERIFY_NONE && need_zone_finish)
+               /*
+                * If all open zones have remainder smaller than the block size
+                * for random write jobs, choose one of the write target zones
+                * and finish it. When verify is enabled, skip this zone finish
+                * operation to avoid verify data corruption by overwrite to the
+                * zone.
+                */
+               if (zbd_pick_write_zone(f, io_u, &zone_idx)) {
+                       pthread_mutex_unlock(&zbdi->mutex);
+                       zone_unlock(z);
+                       z = zbd_get_zone(f, zone_idx);
+                       zone_lock(td, f, z);
+                       io_u_quiesce(td);
+                       dprint(FD_ZBD, "%s(%s): All write target zones have remainder smaller than block size. Choose zone %d and finish.\n",
+                              __func__, f->file_name, zone_idx);
+                       zbd_finish_zone(td, f, z);
+                       goto out;
+               }
+
        pthread_mutex_unlock(&zbdi->mutex);
 
        zone_unlock(z);