#include <sys/stat.h>
#include <unistd.h>
+#include "compiler/compiler.h"
#include "os/os.h"
#include "file.h"
#include "fio.h"
static void zone_lock(struct thread_data *td, const struct fio_file *f,
struct fio_zone_info *z)
{
+#ifndef NDEBUG
struct zoned_block_device_info *zbd = f->zbd_info;
- uint32_t nz = z - zbd->zone_info;
-
+ uint32_t const nz = z - zbd->zone_info;
/* 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
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,
}
/**
- * 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.
*
* 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;
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.
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));
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",
}
/**
- * 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;
* 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;
/*
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)
{
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",
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,
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;
}
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);
*/
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) {
* 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;
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);
+}