Recent changes (master)

Jens Axboe
2020-03-18T12:00:08Z

The following changes since commit 5e8865c0e08861558c1253c521dc9098d0c773ee:

t/io_uring: don't use *rand48_r() variants (2020-03-16 08:30:36 -0600)

are available in the Git repository at:

git://git.kernel.dk/fio.git master

for you to fetch changes up to 3bd2078bdd1c173f9d02bc20e2d630302555a8a0:

zbd: add test for stressing zone locking (2020-03-17 20:05:54 -0600)

----------------------------------------------------------------
Feng Tang (1):
gauss.c: correct the stddev initializtion

Naohiro Aota (6):
zbd: avoid initializing swd when unnecessary
zbd: reset one zone at a time
zbd: use zone_lock to lock a zone
backend: always clean up pending aios
io_u: ensure io_u_quiesce() to process all the IOs
zbd: add test for stressing zone locking

backend.c | 5 ---
io_u.c | 6 ++--
lib/gauss.c | 2 +-
t/zbd/test-zbd-support | 30 ++++++++++++++++++
zbd.c | 84 +++++++++++++++++++-------------------------------
5 files changed, 66 insertions(+), 61 deletions(-)

---

Diff of recent changes:

diff --git a/backend.c b/backend.c
index 936203dc..feb34e51 100644
--- a/backend.c
+++ b/backend.c
@@ -237,15 +237,10 @@ static void cleanup_pending_aio(struct thread_data *td)
{
int r;

- if (td->error)
- return;
-
/*
* get immediately available events, if any
*/
r = io_u_queued_complete(td, 0);
- if (r < 0)
- return;

/*
* now cancel remaining active events
diff --git a/io_u.c b/io_u.c
index bcb893c5..5d62a76c 100644
--- a/io_u.c
+++ b/io_u.c
@@ -606,7 +606,7 @@ static inline enum fio_ddir get_rand_ddir(struct thread_data *td)

int io_u_quiesce(struct thread_data *td)
{
- int ret = 0, completed = 0;
+ int ret = 0, completed = 0, err = 0;

/*
* We are going to sleep, ensure that we flush anything pending as
@@ -625,7 +625,7 @@ int io_u_quiesce(struct thread_data *td)
if (ret > 0)
completed += ret;
else if (ret < 0)
- break;
+ err = ret;
}

if (td->flags & TD_F_REGROW_LOGS)
@@ -634,7 +634,7 @@ int io_u_quiesce(struct thread_data *td)
if (completed)
return completed;

- return ret;
+ return err;
}

static enum fio_ddir rate_ddir(struct thread_data *td, enum fio_ddir ddir)
diff --git a/lib/gauss.c b/lib/gauss.c
index 1d24e187..3f84dbc6 100644
--- a/lib/gauss.c
+++ b/lib/gauss.c
@@ -51,7 +51,7 @@ void gauss_init(struct gauss_state *gs, unsigned long nranges, double dev,
gs->nranges = nranges;

if (dev != 0.0) {
- gs->stddev = ceil((double) (nranges * 100.0) / dev);
+ gs->stddev = ceil((double)(nranges * dev) / 100.0);
if (gs->stddev > nranges / 2)
gs->stddev = nranges / 2;
}
diff --git a/t/zbd/test-zbd-support b/t/zbd/test-zbd-support
index 5d079a8b..bd41fffb 100755
--- a/t/zbd/test-zbd-support
+++ b/t/zbd/test-zbd-support
@@ -755,6 +755,36 @@ test47() {
grep -q 'zoneskip 1 is not a multiple of the device zone size' "${logfile}.${test_number}"
}

+# Multiple overlapping random write jobs for the same drive and with a
+# limited number of open zones. This is similar to test29, but uses libaio
+# to stress test zone locking.
+test48() {
+ local i jobs=16 off opts=()
+
+ off=$((first_sequential_zone_sector * 512 + 64 * zone_size))
+ size=$((16*zone_size))
+ [ -n "$is_zbd" ] && reset_zone "$dev" $((off / 512))
+ opts=("--aux-path=/tmp" "--allow_file_create=0" "--significant_figures=10")
+ opts+=("--debug=zbd")
+ opts+=("--ioengine=libaio" "--rw=randwrite" "--direct=1")
+ opts+=("--time_based" "--runtime=30")
+ opts+=("--zonemode=zbd" "--zonesize=${zone_size}")
+ opts+=("--max_open_zones=4")
+ for ((i=0;i<jobs;i++)); do
+ opts+=("--name=job$i" "--filename=$dev" "--offset=$off" "--bs=16K")
+ opts+=("--io_size=$zone_size" "--iodepth=256" "--thread=1")
+ opts+=("--group_reporting=1")
+ done
+
+ fio=$(dirname "$0")/../../fio
+
+ { echo; echo "fio ${opts[*]}"; echo; } >>"${logfile}.${test_number}"
+
+ timeout -v -s KILL 45s \
+ "${dynamic_analyzer[@]}" "$fio" "${opts[@]}" \
+ >> "${logfile}.${test_number}" 2>&1 || return $?
+}
+
tests=()
dynamic_analyzer=()
reset_all_zones=
diff --git a/zbd.c b/zbd.c
index ee8bcb30..b2d94249 100644
--- a/zbd.c
+++ b/zbd.c
@@ -707,7 +707,7 @@ 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 *z, *start_z = ze;
+ struct fio_zone_info *z;
const uint32_t min_bs = td->o.min_bs[DDIR_WRITE];
bool reset_wp;
int res = 0;
@@ -717,48 +717,20 @@ static int zbd_reset_zones(struct thread_data *td, struct fio_file *f,
assert(f->fd != -1);
for (z = zb; z < ze; z++) {
pthread_mutex_lock(&z->mutex);
- switch (z->type) {
- case BLK_ZONE_TYPE_SEQWRITE_REQ:
+ if (z->type == BLK_ZONE_TYPE_SEQWRITE_REQ) {
reset_wp = all_zones ? z->wp != z->start :
(td->o.td_ddir & TD_DDIR_WRITE) &&
z->wp % min_bs != 0;
- if (start_z == ze && reset_wp) {
- start_z = z;
- } else if (start_z < ze && !reset_wp) {
- dprint(FD_ZBD,
- "%s: resetting zones %u .. %u\n",
+ if (reset_wp) {
+ dprint(FD_ZBD, "%s: resetting zone %u\n",
f->file_name,
- zbd_zone_nr(f->zbd_info, start_z),
- zbd_zone_nr(f->zbd_info, z));
- if (zbd_reset_range(td, f, start_z->start,
- z->start - start_z->start) < 0)
+ zbd_zone_nr(f->zbd_info, z));
+ if (zbd_reset_zone(td, f, z) < 0)
res = 1;
- start_z = ze;
}
- break;
- default:
- if (start_z == ze)
- break;
- dprint(FD_ZBD, "%s: resetting zones %u .. %u\n",
- f->file_name, zbd_zone_nr(f->zbd_info, start_z),
- zbd_zone_nr(f->zbd_info, z));
- if (zbd_reset_range(td, f, start_z->start,
- z->start - start_z->start) < 0)
- res = 1;
- start_z = ze;
- break;
}
- }
- if (start_z < ze) {
- dprint(FD_ZBD, "%s: resetting zones %u .. %u\n", f->file_name,
- zbd_zone_nr(f->zbd_info, start_z),
- zbd_zone_nr(f->zbd_info, z));
- if (zbd_reset_range(td, f, start_z->start,
- z->start - start_z->start) < 0)
- res = 1;
- }
- for (z = zb; z < ze; z++)
pthread_mutex_unlock(&z->mutex);
+ }

return res;
}
@@ -847,6 +819,9 @@ static void zbd_init_swd(struct fio_file *f)
{
uint64_t swd;

+ if (!enable_check_swd)
+ return;
+
swd = zbd_process_swd(f, SET_SWD);
dprint(FD_ZBD, "%s(%s): swd = %" PRIu64 "\n", __func__, f->file_name,
swd);
@@ -952,6 +927,24 @@ static void zbd_close_zone(struct thread_data *td, const struct fio_file *f,
f->zbd_info->zone_info[zone_idx].open = 0;
}

+static void zone_lock(struct thread_data *td, struct fio_zone_info *z)
+{
+ /*
+ * 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(&z->mutex) != 0) {
+ if (!td_ioengine_flagged(td, FIO_SYNCIO))
+ io_u_quiesce(td);
+ pthread_mutex_lock(&z->mutex);
+ }
+}
+
/*
* Modify the offset of an I/O unit that does not refer to an open zone such
* that it refers to an open zone. Close an open zone and open a new zone if
@@ -994,7 +987,7 @@ static struct fio_zone_info *zbd_convert_to_open_zone(struct thread_data *td,
for (;;) {
z = &f->zbd_info->zone_info[zone_idx];

- pthread_mutex_lock(&z->mutex);
+ zone_lock(td, z);
pthread_mutex_lock(&f->zbd_info->mutex);
if (td->o.max_open_zones == 0)
goto examine_zone;
@@ -1042,7 +1035,7 @@ examine_zone:
z = &f->zbd_info->zone_info[zone_idx];
}
assert(is_valid_offset(f, z->start));
- pthread_mutex_lock(&z->mutex);
+ zone_lock(td, z);
if (z->open)
continue;
if (zbd_open_zone(td, io_u, zone_idx))
@@ -1060,7 +1053,7 @@ examine_zone:

z = &f->zbd_info->zone_info[zone_idx];

- pthread_mutex_lock(&z->mutex);
+ zone_lock(td, z);
if (z->wp + min_bs <= (z+1)->start)
goto out;
pthread_mutex_lock(&f->zbd_info->mutex);
@@ -1346,20 +1339,7 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u)

zbd_check_swd(f);

- /*
- * 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);
- }
+ zone_lock(td, zb);

switch (io_u->ddir) {