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;
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;
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;
/* 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)) *
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;
* 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;
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 &&
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));
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;
}
/*
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++;
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;
}
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);
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
* 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;
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 */
* 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;
"%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);
case DDIR_READ:
if (td->runstate == TD_VERIFYING && td_write(td)) {
zb = zbd_replay_write_order(td, io_u, zb);
+ pthread_mutex_unlock(&zb->mutex);
goto accept;
}
/*
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)