Add support for resetting zones periodically
authorBart Van Assche <bart.vanassche@wdc.com>
Fri, 24 Aug 2018 18:31:30 +0000 (11:31 -0700)
committerJens Axboe <axboe@kernel.dk>
Fri, 24 Aug 2018 18:54:41 +0000 (12:54 -0600)
Filesystems that support zoned block devices typically perform garbage
collection if device usage exceeds a certain threshold. Add two command
line options that allow to simulate this behavior.

Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
HOWTO
fio.1
options.c
thread_options.h
zbd.c
zbd.h

diff --git a/HOWTO b/HOWTO
index 25ce5c49782193d3fba1a597a21b0d1feb7b1dc5..7bbd589838ed5dc3c4f9a6cb27e1e620c75a8e3d 100644 (file)
--- a/HOWTO
+++ b/HOWTO
@@ -1025,6 +1025,20 @@ Target file/device
        number of open zones is defined as the number of zones to which write
        commands are issued.
 
        number of open zones is defined as the number of zones to which write
        commands are issued.
 
+.. option:: zone_reset_threshold=float
+
+       A number between zero and one that indicates the ratio of logical
+       blocks with data to the total number of logical blocks in the test
+       above which zones should be reset periodically.
+
+.. option:: zone_reset_frequency=float
+
+       A number between zero and one that indicates how often a zone reset
+       should be issued if the zone reset threshold has been exceeded. A zone
+       reset is submitted after each (1 / zone_reset_frequency) write
+       requests. This and the previous parameter can be used to simulate
+       garbage collection activity.
+
 
 I/O type
 ~~~~~~~~
 
 I/O type
 ~~~~~~~~
diff --git a/fio.1 b/fio.1
index 7172d951ca2fcaa5fbc443bb263dcb62c6992b3d..b555b208b3820b1aa0f624401ad0ca4a95c9a195 100644 (file)
--- a/fio.1
+++ b/fio.1
@@ -786,6 +786,17 @@ When running a random write test across an entire drive many more zones will be
 open than in a typical application workload. Hence this command line option
 that allows to limit the number of open zones. The number of open zones is
 defined as the number of zones to which write commands are issued.
 open than in a typical application workload. Hence this command line option
 that allows to limit the number of open zones. The number of open zones is
 defined as the number of zones to which write commands are issued.
+.TP
+.BI zone_reset_threshold \fR=\fPfloat
+A number between zero and one that indicates the ratio of logical blocks with
+data to the total number of logical blocks in the test above which zones
+should be reset periodically.
+.TP
+.BI zone_reset_frequency \fR=\fPfloat
+A number between zero and one that indicates how often a zone reset should be
+issued if the zone reset threshold has been exceeded. A zone reset is
+submitted after each (1 / zone_reset_frequency) write requests. This and the
+previous parameter can be used to simulate garbage collection activity.
 
 .SS "I/O type"
 .TP
 
 .SS "I/O type"
 .TP
index 20b64648004e3cf54d0c28b0939d3722ba305981..534233bdbc297251d040f605a84b37d21d1b51be 100644 (file)
--- a/options.c
+++ b/options.c
@@ -3318,6 +3318,30 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_INVALID,
        },
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_INVALID,
        },
+       {
+               .name   = "zone_reset_threshold",
+               .lname  = "Zone reset threshold",
+               .help   = "Zoned block device reset threshold",
+               .type   = FIO_OPT_FLOAT_LIST,
+               .maxlen = 1,
+               .off1   = offsetof(struct thread_options, zrt),
+               .minfp  = 0,
+               .maxfp  = 1,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_ZONE,
+       },
+       {
+               .name   = "zone_reset_frequency",
+               .lname  = "Zone reset frequency",
+               .help   = "Zoned block device zone reset frequency in HZ",
+               .type   = FIO_OPT_FLOAT_LIST,
+               .maxlen = 1,
+               .off1   = offsetof(struct thread_options, zrf),
+               .minfp  = 0,
+               .maxfp  = 1,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_ZONE,
+       },
        {
                .name   = "lockmem",
                .lname  = "Lock memory",
        {
                .name   = "lockmem",
                .lname  = "Lock memory",
index 597c4221f1996c1a1e6b8dbb82ea65a1a1dc01bb..393158340e96a52a8068cd39d0d8c3c6c4cc4da3 100644 (file)
@@ -338,6 +338,8 @@ struct thread_options {
        /* Parameters that affect zonemode=zbd */
        unsigned int read_beyond_wp;
        int max_open_zones;
        /* Parameters that affect zonemode=zbd */
        unsigned int read_beyond_wp;
        int max_open_zones;
+       fio_fp64_t zrt;
+       fio_fp64_t zrf;
 };
 
 #define FIO_TOP_STR_MAX                256
 };
 
 #define FIO_TOP_STR_MAX                256
diff --git a/zbd.c b/zbd.c
index 2bf352122bf1dfae08c9cbb8a97f7f22ca2ace4a..561976939298f9ad98991e9c5c3098a31049baf3 100644 (file)
--- a/zbd.c
+++ b/zbd.c
@@ -589,6 +589,9 @@ static int zbd_reset_range(struct thread_data *td, const struct fio_file *f,
        ze = &f->zbd_info->zone_info[zone_idx_e];
        for (z = zb; z < ze; z++) {
                pthread_mutex_lock(&z->mutex);
        ze = &f->zbd_info->zone_info[zone_idx_e];
        for (z = zb; z < ze; z++) {
                pthread_mutex_lock(&z->mutex);
+               pthread_mutex_lock(&f->zbd_info->mutex);
+               f->zbd_info->sectors_with_data -= z->wp - z->start;
+               pthread_mutex_unlock(&f->zbd_info->mutex);
                z->wp = z->start;
                z->verify_block = 0;
                pthread_mutex_unlock(&z->mutex);
                z->wp = z->start;
                z->verify_block = 0;
                pthread_mutex_unlock(&z->mutex);
@@ -687,10 +690,65 @@ static int zbd_reset_zones(struct thread_data *td, struct fio_file *f,
        return res;
 }
 
        return res;
 }
 
+/*
+ * Reset zbd_info.write_cnt, the counter that counts down towards the next
+ * zone reset.
+ */
+static void zbd_reset_write_cnt(const struct thread_data *td,
+                               const struct fio_file *f)
+{
+       assert(0 <= td->o.zrf.u.f && td->o.zrf.u.f <= 1);
+
+       pthread_mutex_lock(&f->zbd_info->mutex);
+       f->zbd_info->write_cnt = td->o.zrf.u.f ?
+               min(1.0 / td->o.zrf.u.f, 0.0 + UINT_MAX) : UINT_MAX;
+       pthread_mutex_unlock(&f->zbd_info->mutex);
+}
+
+static bool zbd_dec_and_reset_write_cnt(const struct thread_data *td,
+                                       const struct fio_file *f)
+{
+       uint32_t write_cnt = 0;
+
+       pthread_mutex_lock(&f->zbd_info->mutex);
+       assert(f->zbd_info->write_cnt);
+       if (f->zbd_info->write_cnt)
+               write_cnt = --f->zbd_info->write_cnt;
+       if (write_cnt == 0)
+               zbd_reset_write_cnt(td, f);
+       pthread_mutex_unlock(&f->zbd_info->mutex);
+
+       return write_cnt == 0;
+}
+
+/* Check whether the value of zbd_info.sectors_with_data is correct. */
+static void check_swd(const struct thread_data *td, const struct fio_file *f)
+{
+#if 0
+       struct fio_zone_info *zb, *ze, *z;
+       uint64_t swd;
+
+       zb = &f->zbd_info->zone_info[zbd_zone_idx(f, f->file_offset)];
+       ze = &f->zbd_info->zone_info[zbd_zone_idx(f, f->file_offset +
+                                                 f->io_size)];
+       swd = 0;
+       for (z = zb; z < ze; z++) {
+               pthread_mutex_lock(&z->mutex);
+               swd += z->wp - z->start;
+       }
+       pthread_mutex_lock(&f->zbd_info->mutex);
+       assert(f->zbd_info->sectors_with_data == swd);
+       pthread_mutex_unlock(&f->zbd_info->mutex);
+       for (z = zb; z < ze; z++)
+               pthread_mutex_unlock(&z->mutex);
+#endif
+}
+
 void zbd_file_reset(struct thread_data *td, struct fio_file *f)
 {
 void zbd_file_reset(struct thread_data *td, struct fio_file *f)
 {
-       struct fio_zone_info *zb, *ze;
+       struct fio_zone_info *zb, *ze, *z;
        uint32_t zone_idx_e;
        uint32_t zone_idx_e;
+       uint64_t swd = 0;
 
        if (!f->zbd_info)
                return;
 
        if (!f->zbd_info)
                return;
@@ -698,6 +756,16 @@ void zbd_file_reset(struct thread_data *td, struct fio_file *f)
        zb = &f->zbd_info->zone_info[zbd_zone_idx(f, f->file_offset)];
        zone_idx_e = zbd_zone_idx(f, f->file_offset + f->io_size);
        ze = &f->zbd_info->zone_info[zone_idx_e];
        zb = &f->zbd_info->zone_info[zbd_zone_idx(f, f->file_offset)];
        zone_idx_e = zbd_zone_idx(f, f->file_offset + f->io_size);
        ze = &f->zbd_info->zone_info[zone_idx_e];
+       for (z = zb ; z < ze; z++) {
+               pthread_mutex_lock(&z->mutex);
+               swd += z->wp - z->start;
+       }
+       pthread_mutex_lock(&f->zbd_info->mutex);
+       f->zbd_info->sectors_with_data = swd;
+       pthread_mutex_unlock(&f->zbd_info->mutex);
+       for (z = zb ; z < ze; z++)
+               pthread_mutex_unlock(&z->mutex);
+       dprint(FD_ZBD, "%s(%s): swd = %ld\n", __func__, f->file_name, swd);
        /*
         * If data verification is enabled reset the affected zones before
         * writing any data to avoid that a zone reset has to be issued while
        /*
         * If data verification is enabled reset the affected zones before
         * writing any data to avoid that a zone reset has to be issued while
@@ -706,6 +774,7 @@ void zbd_file_reset(struct thread_data *td, struct fio_file *f)
        zbd_reset_zones(td, f, zb, ze, td->o.verify != VERIFY_NONE &&
                        (td->o.td_ddir & TD_DDIR_WRITE) &&
                        td->runstate != TD_VERIFYING);
        zbd_reset_zones(td, f, zb, ze, td->o.verify != VERIFY_NONE &&
                        (td->o.td_ddir & TD_DDIR_WRITE) &&
                        td->runstate != TD_VERIFYING);
+       zbd_reset_write_cnt(td, f);
 }
 
 /* The caller must hold f->zbd_info->mutex. */
 }
 
 /* The caller must hold f->zbd_info->mutex. */
@@ -1007,6 +1076,14 @@ static void zbd_post_submit(const struct io_u *io_u, bool success)
        switch (io_u->ddir) {
        case DDIR_WRITE:
                zone_end = min(end, (z + 1)->start);
        switch (io_u->ddir) {
        case DDIR_WRITE:
                zone_end = min(end, (z + 1)->start);
+               pthread_mutex_lock(&zbd_info->mutex);
+               /*
+                * z->wp > zone_end means that one or more I/O errors
+                * have occurred.
+                */
+               if (z->wp <= zone_end)
+                       zbd_info->sectors_with_data += zone_end - z->wp;
+               pthread_mutex_unlock(&zbd_info->mutex);
                z->wp = zone_end;
                break;
        case DDIR_TRIM:
                z->wp = zone_end;
                break;
        case DDIR_TRIM:
@@ -1121,6 +1198,15 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u)
                                goto eof;
                        zone_idx_b = zb - f->zbd_info->zone_info;
                }
                                goto eof;
                        zone_idx_b = zb - f->zbd_info->zone_info;
                }
+               /* Check whether the zone reset threshold has been exceeded */
+               if (td->o.zrf.u.f) {
+                       check_swd(td, f);
+                       if ((f->zbd_info->sectors_with_data << 9) >=
+                           f->io_size * td->o.zrt.u.f &&
+                           zbd_dec_and_reset_write_cnt(td, f)) {
+                               zb->reset_zone = 1;
+                       }
+               }
                /* Reset the zone pointer if necessary */
                if (zb->reset_zone || zbd_zone_full(f, zb, min_bs)) {
                        assert(td->o.verify == VERIFY_NONE);
                /* Reset the zone pointer if necessary */
                if (zb->reset_zone || zbd_zone_full(f, zb, min_bs)) {
                        assert(td->o.verify == VERIFY_NONE);
@@ -1135,6 +1221,7 @@ enum io_u_action zbd_adjust_block(struct thread_data *td, struct io_u *io_u)
                        zb->reset_zone = 0;
                        if (zbd_reset_zone(td, f, zb) < 0)
                                goto eof;
                        zb->reset_zone = 0;
                        if (zbd_reset_zone(td, f, zb) < 0)
                                goto eof;
+                       check_swd(td, f);
                }
                /* Make writes occur at the write pointer */
                assert(!zbd_zone_full(f, zb, min_bs));
                }
                /* Make writes occur at the write pointer */
                assert(!zbd_zone_full(f, zb, min_bs));
diff --git a/zbd.h b/zbd.h
index 215a96388096e9588cfa962add66d4fddc5c4a79..08751fd5bf348d43e0efc35f0b891f158eb06299 100644 (file)
--- a/zbd.h
+++ b/zbd.h
@@ -60,11 +60,14 @@ struct fio_zone_info {
  * @mutex: Protects the modifiable members in this structure (refcount and
  *             num_open_zones).
  * @zone_size: size of a single zone in units of 512 bytes
  * @mutex: Protects the modifiable members in this structure (refcount and
  *             num_open_zones).
  * @zone_size: size of a single zone in units of 512 bytes
+ * @sectors_with_data: total size of data in all zones in units of 512 bytes
  * @zone_size_log2: log2 of the zone size in bytes if it is a power of 2 or 0
  *             if the zone size is not a power of 2.
  * @nr_zones: number of zones
  * @refcount: number of fio files that share this structure
  * @num_open_zones: number of open zones
  * @zone_size_log2: log2 of the zone size in bytes if it is a power of 2 or 0
  *             if the zone size is not a power of 2.
  * @nr_zones: number of zones
  * @refcount: number of fio files that share this structure
  * @num_open_zones: number of open zones
+ * @write_cnt: Number of writes since the latest zone reset triggered by
+ *            the zone_reset_frequency fio job parameter.
  * @open_zones: zone numbers of open zones
  * @zone_info: description of the individual zones
  *
  * @open_zones: zone numbers of open zones
  * @zone_info: description of the individual zones
  *
@@ -76,10 +79,12 @@ struct zoned_block_device_info {
        enum blk_zoned_model    model;
        pthread_mutex_t         mutex;
        uint64_t                zone_size;
        enum blk_zoned_model    model;
        pthread_mutex_t         mutex;
        uint64_t                zone_size;
+       uint64_t                sectors_with_data;
        uint32_t                zone_size_log2;
        uint32_t                nr_zones;
        uint32_t                refcount;
        uint32_t                num_open_zones;
        uint32_t                zone_size_log2;
        uint32_t                nr_zones;
        uint32_t                refcount;
        uint32_t                num_open_zones;
+       uint32_t                write_cnt;
        uint32_t                open_zones[FIO_MAX_OPEN_ZBD_ZONES];
        struct fio_zone_info    zone_info[0];
 };
        uint32_t                open_zones[FIO_MAX_OPEN_ZBD_ZONES];
        struct fio_zone_info    zone_info[0];
 };