Recent changes (master)

Jens Axboe
2021-05-15T12:01:21Z

The following changes since commit 30bec59eab3908b681cbc2866179f7166a849c83:

os: define EDQUOT to EIO if the OS doesn't provide it (2021-05-11 07:58:03 -0600)

are available in the Git repository at:

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

for you to fetch changes up to dfecde6a4b49bd299b2a7192c10533b9beb4820d:

Merge branch '2021-05-13/stat-fix-integer-overflow' of https://github.com/flx42/fio (2021-05-14 09:36:59 -0600)

----------------------------------------------------------------
Felix Abecassis (1):
stat: fix integer overflow in convert_agg_kbytes_percent

Jens Axboe (1):
Merge branch '2021-05-13/stat-fix-integer-overflow' of https://github.com/flx42/fio

Niklas Cassel (4):
zbd: only put an upper limit on max open zones once
oslib/linux-blkzoned: move sysfs reading into its own function
ioengines: add get_max_open_zones zoned block device operation
engines/libzbc: add support for the get_max_open_zones io op

engines/libzbc.c | 21 +++++++++++
engines/skeleton_external.c | 13 +++++++
ioengines.h | 4 +-
oslib/blkzoned.h | 7 ++++
oslib/linux-blkzoned.c | 83 ++++++++++++++++++++++++++++++-----------
stat.c | 2 +-
zbd.c | 91 ++++++++++++++++++++++++++++++++++++++++++---
7 files changed, 191 insertions(+), 30 deletions(-)

---

Diff of recent changes:

diff --git a/engines/libzbc.c b/engines/libzbc.c
index 2aacf7bb..3dde93db 100644
--- a/engines/libzbc.c
+++ b/engines/libzbc.c
@@ -19,6 +19,7 @@ struct libzbc_data {
struct zbc_device *zdev;
enum zbc_dev_model model;
uint64_t nr_sectors;
+ uint32_t max_open_seq_req;
};

static int libzbc_get_dev_info(struct libzbc_data *ld, struct fio_file *f)
@@ -32,6 +33,7 @@ static int libzbc_get_dev_info(struct libzbc_data *ld, struct fio_file *f)
zbc_get_device_info(ld->zdev, zinfo);
ld->model = zinfo->zbd_model;
ld->nr_sectors = zinfo->zbd_sectors;
+ ld->max_open_seq_req = zinfo->zbd_max_nr_open_seq_req;

dprint(FD_ZBD, "%s: vendor_id:%s, type: %s, model: %s\n",
f->file_name, zinfo->zbd_vendor_id,
@@ -335,6 +337,24 @@ err:
return -ret;
}

+static int libzbc_get_max_open_zones(struct thread_data *td, struct fio_file *f,
+ unsigned int *max_open_zones)
+{
+ struct libzbc_data *ld;
+ int ret;
+
+ ret = libzbc_open_dev(td, f, &ld);
+ if (ret)
+ return ret;
+
+ if (ld->max_open_seq_req == ZBC_NO_LIMIT)
+ *max_open_zones = 0;
+ else
+ *max_open_zones = ld->max_open_seq_req;
+
+ return 0;
+}
+
ssize_t libzbc_rw(struct thread_data *td, struct io_u *io_u)
{
struct libzbc_data *ld = td->io_ops_data;
@@ -414,6 +434,7 @@ FIO_STATIC struct ioengine_ops ioengine = {
.get_zoned_model = libzbc_get_zoned_model,
.report_zones = libzbc_report_zones,
.reset_wp = libzbc_reset_wp,
+ .get_max_open_zones = libzbc_get_max_open_zones,
.queue = libzbc_queue,
.flags = FIO_SYNCIO | FIO_NOEXTEND | FIO_RAWIO,
};
diff --git a/engines/skeleton_external.c b/engines/skeleton_external.c
index 7f3e4cb3..c79b6f11 100644
--- a/engines/skeleton_external.c
+++ b/engines/skeleton_external.c
@@ -193,6 +193,18 @@ static int fio_skeleton_reset_wp(struct thread_data *td, struct fio_file *f,
return 0;
}

+/*
+ * Hook called for getting the maximum number of open zones for a
+ * ZBD_HOST_MANAGED zoned block device.
+ * A @max_open_zones value set to zero means no limit.
+ */
+static int fio_skeleton_get_max_open_zones(struct thread_data *td,
+ struct fio_file *f,
+ unsigned int *max_open_zones)
+{
+ return 0;
+}
+
/*
* Note that the structure is exported, so that fio can get it via
* dlsym(..., "ioengine"); for (and only for) external engines.
@@ -212,6 +224,7 @@ struct ioengine_ops ioengine = {
.get_zoned_model = fio_skeleton_get_zoned_model,
.report_zones = fio_skeleton_report_zones,
.reset_wp = fio_skeleton_reset_wp,
+ .get_max_open_zones = fio_skeleton_get_max_open_zones,
.options = options,
.option_struct_size = sizeof(struct fio_skeleton_options),
};
diff --git a/ioengines.h b/ioengines.h
index 1d01ab0a..b3f755b4 100644
--- a/ioengines.h
+++ b/ioengines.h
@@ -8,7 +8,7 @@
#include "io_u.h"
#include "zbd_types.h"

-#define FIO_IOOPS_VERSION 29
+#define FIO_IOOPS_VERSION 30

#ifndef CONFIG_DYNAMIC_ENGINES
#define FIO_STATIC static
@@ -59,6 +59,8 @@ struct ioengine_ops {
uint64_t, struct zbd_zone *, unsigned int);
int (*reset_wp)(struct thread_data *, struct fio_file *,
uint64_t, uint64_t);
+ int (*get_max_open_zones)(struct thread_data *, struct fio_file *,
+ unsigned int *);
int option_struct_size;
struct fio_option *options;
};
diff --git a/oslib/blkzoned.h b/oslib/blkzoned.h
index 4cc071dc..719b041d 100644
--- a/oslib/blkzoned.h
+++ b/oslib/blkzoned.h
@@ -16,6 +16,8 @@ extern int blkzoned_report_zones(struct thread_data *td,
struct zbd_zone *zones, unsigned int nr_zones);
extern int blkzoned_reset_wp(struct thread_data *td, struct fio_file *f,
uint64_t offset, uint64_t length);
+extern int blkzoned_get_max_open_zones(struct thread_data *td, struct fio_file *f,
+ unsigned int *max_open_zones);
#else
/*
* Define stubs for systems that do not have zoned block device support.
@@ -44,6 +46,11 @@ static inline int blkzoned_reset_wp(struct thread_data *td, struct fio_file *f,
{
return -EIO;
}
+static inline int blkzoned_get_max_open_zones(struct thread_data *td, struct fio_file *f,
+ unsigned int *max_open_zones)
+{
+ return -EIO;
+}
#endif

#endif /* FIO_BLKZONED_H */
diff --git a/oslib/linux-blkzoned.c b/oslib/linux-blkzoned.c
index 81e4e7f0..6f89ec6f 100644
--- a/oslib/linux-blkzoned.c
+++ b/oslib/linux-blkzoned.c
@@ -74,12 +74,16 @@ static char *read_file(const char *path)
return strdup(line);
}

-int blkzoned_get_zoned_model(struct thread_data *td, struct fio_file *f,
- enum zbd_zoned_model *model)
+/*
+ * Get the value of a sysfs attribute for a block device.
+ *
+ * Returns NULL on failure.
+ * Returns a pointer to a string on success.
+ * The caller is responsible for freeing the memory.
+ */
+static char *blkzoned_get_sysfs_attr(const char *file_name, const char *attr)
{
- const char *file_name = f->file_name;
- char *zoned_attr_path = NULL;
- char *model_str = NULL;
+ char *attr_path = NULL;
struct stat statbuf;
char *sys_devno_path = NULL;
char *part_attr_path = NULL;
@@ -87,13 +91,7 @@ int blkzoned_get_zoned_model(struct thread_data *td, struct fio_file *f,
char sys_path[PATH_MAX];
ssize_t sz;
char *delim = NULL;
-
- if (f->filetype != FIO_TYPE_BLOCK) {
- *model = ZBD_IGNORE;
- return 0;
- }
-
- *model = ZBD_NONE;
+ char *attr_str = NULL;

if (stat(file_name, &statbuf) < 0)
goto out;
@@ -123,24 +121,65 @@ int blkzoned_get_zoned_model(struct thread_data *td, struct fio_file *f,
*delim = '\0';
}

- if (asprintf(&zoned_attr_path,
- "/sys/dev/block/%s/queue/zoned", sys_path) < 0)
+ if (asprintf(&attr_path,
+ "/sys/dev/block/%s/%s", sys_path, attr) < 0)
goto out;

- model_str = read_file(zoned_attr_path);
+ attr_str = read_file(attr_path);
+out:
+ free(attr_path);
+ free(part_str);
+ free(part_attr_path);
+ free(sys_devno_path);
+
+ return attr_str;
+}
+
+int blkzoned_get_zoned_model(struct thread_data *td, struct fio_file *f,
+ enum zbd_zoned_model *model)
+{
+ char *model_str = NULL;
+
+ if (f->filetype != FIO_TYPE_BLOCK) {
+ *model = ZBD_IGNORE;
+ return 0;
+ }
+
+ *model = ZBD_NONE;
+
+ model_str = blkzoned_get_sysfs_attr(f->file_name, "queue/zoned");
if (!model_str)
- goto out;
- dprint(FD_ZBD, "%s: zbd model string: %s\n", file_name, model_str);
+ return 0;
+
+ dprint(FD_ZBD, "%s: zbd model string: %s\n", f->file_name, model_str);
if (strcmp(model_str, "host-aware") == 0)
*model = ZBD_HOST_AWARE;
else if (strcmp(model_str, "host-managed") == 0)
*model = ZBD_HOST_MANAGED;
-out:
+
free(model_str);
- free(zoned_attr_path);
- free(part_str);
- free(part_attr_path);
- free(sys_devno_path);
+
+ return 0;
+}
+
+int blkzoned_get_max_open_zones(struct thread_data *td, struct fio_file *f,
+ unsigned int *max_open_zones)
+{
+ char *max_open_str;
+
+ if (f->filetype != FIO_TYPE_BLOCK)
+ return -EIO;
+
+ max_open_str = blkzoned_get_sysfs_attr(f->file_name, "queue/max_open_zones");
+ if (!max_open_str)
+ return 0;
+
+ dprint(FD_ZBD, "%s: max open zones supported by device: %s\n",
+ f->file_name, max_open_str);
+ *max_open_zones = atoll(max_open_str);
+
+ free(max_open_str);
+
return 0;
}

diff --git a/stat.c b/stat.c
index b7222f46..a8a96c85 100644
--- a/stat.c
+++ b/stat.c
@@ -462,7 +462,7 @@ static double convert_agg_kbytes_percent(struct group_run_stats *rs, int ddir, i
{
double p_of_agg = 100.0;
if (rs && rs->agg[ddir] > 1024) {
- p_of_agg = mean * 100 / (double) (rs->agg[ddir] / 1024.0);
+ p_of_agg = mean * 100.0 / (double) (rs->agg[ddir] / 1024.0);

if (p_of_agg > 100.0)
p_of_agg = 100.0;
diff --git a/zbd.c b/zbd.c
index eed796b3..68cd58e1 100644
--- a/zbd.c
+++ b/zbd.c
@@ -113,6 +113,34 @@ int zbd_reset_wp(struct thread_data *td, struct fio_file *f,
return ret;
}

+/**
+ * zbd_get_max_open_zones - Get the maximum number of open zones
+ * @td: FIO thread data
+ * @f: FIO file for which to get max open zones
+ * @max_open_zones: Upon success, result will be stored here.
+ *
+ * A @max_open_zones value set to zero means no limit.
+ *
+ * Returns 0 upon success and a negative error code upon failure.
+ */
+int zbd_get_max_open_zones(struct thread_data *td, struct fio_file *f,
+ unsigned int *max_open_zones)
+{
+ int ret;
+
+ if (td->io_ops && td->io_ops->get_max_open_zones)
+ ret = td->io_ops->get_max_open_zones(td, f, max_open_zones);
+ else
+ ret = blkzoned_get_max_open_zones(td, f, max_open_zones);
+ if (ret < 0) {
+ td_verror(td, errno, "get max open zones failed");
+ log_err("%s: get max open zones failed (%d).\n",
+ f->file_name, errno);
+ }
+
+ return ret;
+}
+
/**
* zbd_zone_idx - convert an offset into a zone number
* @f: file pointer.
@@ -554,6 +582,51 @@ out:
return ret;
}

+static int zbd_set_max_open_zones(struct thread_data *td, struct fio_file *f)
+{
+ struct zoned_block_device_info *zbd = f->zbd_info;
+ unsigned int max_open_zones;
+ int ret;
+
+ if (zbd->model != ZBD_HOST_MANAGED) {
+ /* Only host-managed devices have a max open limit */
+ zbd->max_open_zones = td->o.max_open_zones;
+ goto out;
+ }
+
+ /* If host-managed, get the max open limit */
+ ret = zbd_get_max_open_zones(td, f, &max_open_zones);
+ if (ret)
+ return ret;
+
+ if (!max_open_zones) {
+ /* No device limit */
+ zbd->max_open_zones = td->o.max_open_zones;
+ } else if (!td->o.max_open_zones) {
+ /* No user limit. Set limit to device limit */
+ zbd->max_open_zones = max_open_zones;
+ } else if (td->o.max_open_zones <= max_open_zones) {
+ /* Both user limit and dev limit. User limit not too large */
+ zbd->max_open_zones = td->o.max_open_zones;
+ } else {
+ /* Both user limit and dev limit. User limit too large */
+ td_verror(td, EINVAL,
+ "Specified --max_open_zones is too large");
+ log_err("Specified --max_open_zones (%d) is larger than max (%u)\n",
+ td->o.max_open_zones, max_open_zones);
+ return -EINVAL;
+ }
+
+out:
+ /* Ensure that the limit is not larger than FIO's internal limit */
+ zbd->max_open_zones = min_not_zero(zbd->max_open_zones,
+ (uint32_t) ZBD_MAX_OPEN_ZONES);
+ dprint(FD_ZBD, "%s: using max open zones limit: %"PRIu32"\n",
+ f->file_name, zbd->max_open_zones);
+
+ return 0;
+}
+
/*
* Allocate zone information and store it into f->zbd_info if zonemode=zbd.
*
@@ -576,9 +649,13 @@ static int zbd_create_zone_info(struct thread_data *td, struct fio_file *f)
case ZBD_HOST_AWARE:
case ZBD_HOST_MANAGED:
ret = parse_zone_info(td, f);
+ if (ret)
+ return ret;
break;
case ZBD_NONE:
ret = init_zone_info(td, f);
+ if (ret)
+ return ret;
break;
default:
td_verror(td, EINVAL, "Unsupported zoned model");
@@ -586,11 +663,15 @@ static int zbd_create_zone_info(struct thread_data *td, struct fio_file *f)
return -EINVAL;
}

- if (ret == 0) {
- f->zbd_info->model = zbd_model;
- f->zbd_info->max_open_zones = td->o.max_open_zones;
+ f->zbd_info->model = zbd_model;
+
+ ret = zbd_set_max_open_zones(td, f);
+ if (ret) {
+ zbd_free_zone_info(f);
+ return ret;
}
- return ret;
+
+ return 0;
}

void zbd_free_zone_info(struct fio_file *f)
@@ -726,8 +807,6 @@ int zbd_setup_files(struct thread_data *td)
if (zbd_is_seq_job(f))
assert(f->min_zone < f->max_zone);

- zbd->max_open_zones = zbd->max_open_zones ?: ZBD_MAX_OPEN_ZONES;
-
if (td->o.max_open_zones > 0 &&
zbd->max_open_zones != td->o.max_open_zones) {
log_err("Different 'max_open_zones' values\n");