summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xFIO-VERSION-GEN2
-rw-r--r--HOWTO13
-rw-r--r--Makefile11
-rw-r--r--cconv.c4
-rwxr-xr-xci/appveyor-install.sh2
-rwxr-xr-xconfigure29
-rw-r--r--engines/libpmem.c64
-rw-r--r--engines/libzbc.c21
-rw-r--r--engines/nfs.c314
-rw-r--r--engines/skeleton_external.c13
-rw-r--r--examples/libpmem.fio35
-rw-r--r--examples/nfs.fio22
-rw-r--r--fio.115
-rw-r--r--ioengines.h4
-rw-r--r--optgroup.c4
-rw-r--r--optgroup.h2
-rw-r--r--options.c15
-rw-r--r--os/os-android.h19
-rw-r--r--oslib/blkzoned.h7
-rw-r--r--oslib/linux-blkzoned.c83
-rw-r--r--server.h2
-rw-r--r--stat.c2
-rw-r--r--thread_options.h3
-rw-r--r--zbd.c91
24 files changed, 682 insertions, 95 deletions
diff --git a/FIO-VERSION-GEN b/FIO-VERSION-GEN
index 29486071..47af94e9 100755
--- a/FIO-VERSION-GEN
+++ b/FIO-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=FIO-VERSION-FILE
-DEF_VER=fio-3.26
+DEF_VER=fio-3.27
LF='
'
diff --git a/HOWTO b/HOWTO
index f5681c0d..86fb2964 100644
--- a/HOWTO
+++ b/HOWTO
@@ -1171,7 +1171,7 @@ I/O type
**1**
Backward-compatible alias for **mixed**.
-
+
**2**
Alias for **both**.
@@ -2103,6 +2103,12 @@ I/O engine
I/O engine supporting asynchronous read and write operations to the
DAOS File System (DFS) via libdfs.
+ **nfs**
+ I/O engine supporting asynchronous read and write operations to
+ NFS filesystems from userspace via libnfs. This is useful for
+ achieving higher concurrency and thus throughput than is possible
+ via kernel NFS.
+
I/O engine specific parameters
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2525,6 +2531,11 @@ with the caveat that when used on the command line, they must come after the
Specificy a different object class for the dfs file.
Use DAOS container's object class by default.
+.. option:: nfs_url=str : [nfs]
+
+ URL in libnfs format, eg nfs://<server|ipv4|ipv6>/path[?arg=val[&arg=val]*]
+ Refer to the libnfs README for more details.
+
I/O depth
~~~~~~~~~
diff --git a/Makefile b/Makefile
index ba027b2e..f57569d5 100644
--- a/Makefile
+++ b/Makefile
@@ -40,6 +40,11 @@ ifdef CONFIG_PDB
LDFLAGS += -fuse-ld=lld $(LINK_PDBFILE)
endif
+# If clang, do not use builtin stpcpy as it breaks the build
+ifeq ($(CC),clang)
+ FIO_CFLAGS += -fno-builtin-stpcpy
+endif
+
ifdef CONFIG_GFIO
PROGS += gfio
endif
@@ -79,6 +84,12 @@ ifdef CONFIG_LIBNBD
ENGINES += nbd
endif
+ifdef CONFIG_LIBNFS
+ CFLAGS += $(LIBNFS_CFLAGS)
+ LIBS += $(LIBNFS_LIBS)
+ SOURCE += engines/nfs.c
+endif
+
ifdef CONFIG_64BIT
CPPFLAGS += -DBITS_PER_LONG=64
else ifdef CONFIG_32BIT
diff --git a/cconv.c b/cconv.c
index aa06e3ea..74c24106 100644
--- a/cconv.c
+++ b/cconv.c
@@ -231,6 +231,8 @@ void convert_thread_options_to_cpu(struct thread_options *o,
o->zone_capacity = le64_to_cpu(top->zone_capacity);
o->zone_skip = le64_to_cpu(top->zone_skip);
o->zone_mode = le32_to_cpu(top->zone_mode);
+ o->max_open_zones = __le32_to_cpu(top->max_open_zones);
+ o->ignore_zone_limits = le32_to_cpu(top->ignore_zone_limits);
o->lockmem = le64_to_cpu(top->lockmem);
o->offset_increment_percent = le32_to_cpu(top->offset_increment_percent);
o->offset_increment = le64_to_cpu(top->offset_increment);
@@ -573,6 +575,8 @@ void convert_thread_options_to_net(struct thread_options_pack *top,
top->zone_capacity = __cpu_to_le64(o->zone_capacity);
top->zone_skip = __cpu_to_le64(o->zone_skip);
top->zone_mode = __cpu_to_le32(o->zone_mode);
+ top->max_open_zones = __cpu_to_le32(o->max_open_zones);
+ top->ignore_zone_limits = cpu_to_le32(o->ignore_zone_limits);
top->lockmem = __cpu_to_le64(o->lockmem);
top->ddir_seq_add = __cpu_to_le64(o->ddir_seq_add);
top->file_size_low = __cpu_to_le64(o->file_size_low);
diff --git a/ci/appveyor-install.sh b/ci/appveyor-install.sh
index c73e4cb5..3137f39e 100755
--- a/ci/appveyor-install.sh
+++ b/ci/appveyor-install.sh
@@ -31,7 +31,9 @@ case "${DISTRO}" in
pacman.exe --noconfirm -S \
mingw-w64-${PACKAGE_ARCH}-clang \
mingw-w64-${PACKAGE_ARCH}-cunit \
+ mingw-w64-${PACKAGE_ARCH}-toolchain \
mingw-w64-${PACKAGE_ARCH}-lld
+ pacman.exe -Q # List installed packages
;;
esac
diff --git a/configure b/configure
index e886bdc8..8b763700 100755
--- a/configure
+++ b/configure
@@ -170,6 +170,7 @@ disable_native="no"
march_set="no"
libiscsi="no"
libnbd="no"
+libnfs="no"
libzbc=""
dfs=""
dynamic_engines="no"
@@ -241,6 +242,8 @@ for opt do
;;
--disable-tcmalloc) disable_tcmalloc="yes"
;;
+ --disable-nfs) disable_nfs="yes"
+ ;;
--dynamic-libengines) dynamic_engines="yes"
;;
--disable-dfs) dfs="no"
@@ -271,8 +274,10 @@ if test "$show_help" = "yes" ; then
echo "--disable-rados Disable Rados support even if found"
echo "--disable-rbd Disable Rados Block Device even if found"
echo "--disable-http Disable HTTP support even if found"
+ echo "--disable-nfs Disable userspace NFS support even if found"
echo "--disable-gfapi Disable gfapi"
echo "--enable-libhdfs Enable hdfs support"
+ echo "--enable-libnfs Enable nfs support"
echo "--disable-lex Disable use of lex/yacc for math"
echo "--disable-pmem Disable pmem based engines even if found"
echo "--enable-lex Enable use of lex/yacc for math"
@@ -2278,6 +2283,21 @@ fi
print_config "DAOS File System (dfs) Engine" "$dfs"
##########################################
+# Check if we have libnfs (for userspace nfs support).
+if test "$disable_nfs" != "yes"; then
+ if $(pkg-config libnfs); then
+ libnfs="yes"
+ libnfs_cflags=$(pkg-config --cflags libnfs)
+ libnfs_libs=$(pkg-config --libs libnfs)
+ else
+ if test "$libnfs" = "yes" ; then
+ echo "libnfs" "Install libnfs"
+ fi
+ fi
+fi
+print_config "NFS engine" "$libnfs"
+
+##########################################
# Check if we have lex/yacc available
yacc="no"
yacc_is_bison="no"
@@ -3101,6 +3121,9 @@ fi
if test "$dfs" = "yes" ; then
output_sym "CONFIG_DFS"
fi
+if test "$libnfs" = "yes" ; then
+ output_sym "CONFIG_NFS"
+fi
if test "$march_set" = "no" && test "$build_native" = "yes" ; then
output_sym "CONFIG_BUILD_NATIVE"
fi
@@ -3140,6 +3163,12 @@ if test "$libnbd" = "yes" ; then
echo "LIBNBD_CFLAGS=$libnbd_cflags" >> $config_host_mak
echo "LIBNBD_LIBS=$libnbd_libs" >> $config_host_mak
fi
+if test "$libnfs" = "yes" ; then
+ output_sym "CONFIG_LIBNFS"
+ echo "CONFIG_LIBNFS=m" >> $config_host_mak
+ echo "LIBNFS_CFLAGS=$libnfs_cflags" >> $config_host_mak
+ echo "LIBNFS_LIBS=$libnfs_libs" >> $config_host_mak
+fi
if test "$dynamic_engines" = "yes" ; then
output_sym "CONFIG_DYNAMIC_ENGINES"
fi
diff --git a/engines/libpmem.c b/engines/libpmem.c
index 2338f0fa..ab29a453 100644
--- a/engines/libpmem.c
+++ b/engines/libpmem.c
@@ -2,7 +2,7 @@
* libpmem: IO engine that uses PMDK libpmem to read and write data
*
* Copyright (C) 2017 Nippon Telegraph and Telephone Corporation.
- * Copyright 2018-2020, Intel Corporation
+ * Copyright 2018-2021, Intel Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License,
@@ -18,7 +18,8 @@
/*
* libpmem engine
*
- * IO engine that uses libpmem to write data (and memcpy to read)
+ * IO engine that uses libpmem (part of PMDK collection) to write data
+ * and libc's memcpy to read. It requires PMDK >= 1.5.
*
* To use:
* ioengine=libpmem
@@ -43,25 +44,13 @@
* mkdir /mnt/pmem0
* mount -o dax /dev/pmem0 /mnt/pmem0
*
- * See examples/libpmem.fio for more.
- *
- *
- * libpmem.so
- * By default, the libpmem engine will let the system find the libpmem.so
- * that it uses. You can use an alternative libpmem by setting the
- * FIO_PMEM_LIB environment variable to the full path to the desired
- * libpmem.so. This engine requires PMDK >= 1.5.
+ * See examples/libpmem.fio for complete usage example.
*/
#include <stdio.h>
-#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/sysmacros.h>
-#include <libgen.h>
#include <libpmem.h>
#include "../fio.h"
@@ -77,8 +66,8 @@ static int fio_libpmem_init(struct thread_data *td)
{
struct thread_options *o = &td->o;
- dprint(FD_IO,"o->rw_min_bs %llu \n o->fsync_blocks %u \n o->fdatasync_blocks %u \n",
- o->rw_min_bs,o->fsync_blocks,o->fdatasync_blocks);
+ dprint(FD_IO, "o->rw_min_bs %llu\n o->fsync_blocks %u\n o->fdatasync_blocks %u\n",
+ o->rw_min_bs, o->fsync_blocks, o->fdatasync_blocks);
dprint(FD_IO, "DEBUG fio_libpmem_init\n");
if ((o->rw_min_bs & page_mask) &&
@@ -91,23 +80,17 @@ static int fio_libpmem_init(struct thread_data *td)
}
/*
- * This is the pmem_map_file execution function
+ * This is the pmem_map_file execution function, a helper to
+ * fio_libpmem_open_file function.
*/
static int fio_libpmem_file(struct thread_data *td, struct fio_file *f,
size_t length, off_t off)
{
struct fio_libpmem_data *fdd = FILE_ENG_DATA(f);
- mode_t mode = 0;
+ mode_t mode = S_IWUSR | S_IRUSR;
size_t mapped_len;
int is_pmem;
- if(td_rw(td))
- mode = S_IWUSR | S_IRUSR;
- else if (td_write(td))
- mode = S_IWUSR;
- else
- mode = S_IRUSR;
-
dprint(FD_IO, "DEBUG fio_libpmem_file\n");
dprint(FD_IO, "f->file_name = %s td->o.verify = %d \n", f->file_name,
td->o.verify);
@@ -142,11 +125,11 @@ static int fio_libpmem_open_file(struct thread_data *td, struct fio_file *f)
{
struct fio_libpmem_data *fdd;
- dprint(FD_IO,"DEBUG fio_libpmem_open_file\n");
- dprint(FD_IO,"f->io_size=%ld \n",f->io_size);
- dprint(FD_IO,"td->o.size=%lld \n",td->o.size);
- dprint(FD_IO,"td->o.iodepth=%d\n",td->o.iodepth);
- dprint(FD_IO,"td->o.iodepth_batch=%d \n",td->o.iodepth_batch);
+ dprint(FD_IO, "DEBUG fio_libpmem_open_file\n");
+ dprint(FD_IO, "f->io_size=%ld\n", f->io_size);
+ dprint(FD_IO, "td->o.size=%lld\n", td->o.size);
+ dprint(FD_IO, "td->o.iodepth=%d\n", td->o.iodepth);
+ dprint(FD_IO, "td->o.iodepth_batch=%d\n", td->o.iodepth_batch);
if (fio_file_open(f))
td_io_close_file(td, f);
@@ -167,8 +150,8 @@ static int fio_libpmem_prep(struct thread_data *td, struct io_u *io_u)
struct fio_file *f = io_u->file;
struct fio_libpmem_data *fdd = FILE_ENG_DATA(f);
- dprint(FD_IO, "DEBUG fio_libpmem_prep\n" );
- dprint(FD_IO," io_u->offset %llu : fdd->libpmem_off %ld : "
+ dprint(FD_IO, "DEBUG fio_libpmem_prep\n");
+ dprint(FD_IO, "io_u->offset %llu : fdd->libpmem_off %ld : "
"io_u->buflen %llu : fdd->libpmem_sz %ld\n",
io_u->offset, fdd->libpmem_off,
io_u->buflen, fdd->libpmem_sz);
@@ -192,8 +175,9 @@ static enum fio_q_status fio_libpmem_queue(struct thread_data *td,
io_u->error = 0;
dprint(FD_IO, "DEBUG fio_libpmem_queue\n");
- dprint(FD_IO,"td->o.odirect %d td->o.sync_io %d \n",td->o.odirect, td->o.sync_io);
- /* map both O_SYNC / DSYNC to not using NODRAIN */
+ dprint(FD_IO, "td->o.odirect %d td->o.sync_io %d\n",
+ td->o.odirect, td->o.sync_io);
+ /* map both O_SYNC / DSYNC to not use NODRAIN */
flags = td->o.sync_io ? 0 : PMEM_F_MEM_NODRAIN;
flags |= td->o.odirect ? PMEM_F_MEM_NONTEMPORAL : PMEM_F_MEM_TEMPORAL;
@@ -203,7 +187,7 @@ static enum fio_q_status fio_libpmem_queue(struct thread_data *td,
break;
case DDIR_WRITE:
dprint(FD_IO, "DEBUG mmap_data=%p, xfer_buf=%p\n",
- io_u->mmap_data, io_u->xfer_buf );
+ io_u->mmap_data, io_u->xfer_buf);
pmem_memcpy(io_u->mmap_data,
io_u->xfer_buf,
io_u->xfer_buflen,
@@ -227,13 +211,7 @@ static int fio_libpmem_close_file(struct thread_data *td, struct fio_file *f)
struct fio_libpmem_data *fdd = FILE_ENG_DATA(f);
int ret = 0;
- dprint(FD_IO,"DEBUG fio_libpmem_close_file\n");
- dprint(FD_IO,"td->o.odirect %d \n",td->o.odirect);
-
- if (!td->o.odirect) {
- dprint(FD_IO,"pmem_drain\n");
- pmem_drain();
- }
+ dprint(FD_IO, "DEBUG fio_libpmem_close_file\n");
if (fdd->libpmem_ptr)
ret = pmem_unmap(fdd->libpmem_ptr, fdd->libpmem_sz);
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/nfs.c b/engines/nfs.c
new file mode 100644
index 00000000..21be8833
--- /dev/null
+++ b/engines/nfs.c
@@ -0,0 +1,314 @@
+#include <stdlib.h>
+#include <poll.h>
+#include <nfsc/libnfs.h>
+#include <nfsc/libnfs-raw.h>
+#include <nfsc/libnfs-raw-mount.h>
+
+#include "../fio.h"
+#include "../optgroup.h"
+
+enum nfs_op_type {
+ NFS_READ_WRITE = 0,
+ NFS_STAT_MKDIR_RMDIR,
+ NFS_STAT_TOUCH_RM,
+};
+
+struct fio_libnfs_options {
+ struct nfs_context *context;
+ char *nfs_url;
+ unsigned int queue_depth; /* nfs_callback needs this info, but doesn't have fio td structure to pull it from */
+ /* the following implement a circular queue of outstanding IOs */
+ int outstanding_events; /* IOs issued to libnfs, that have not returned yet */
+ int prev_requested_event_index; /* event last returned via fio_libnfs_event */
+ int next_buffered_event; /* round robin-pointer within events[] */
+ int buffered_event_count; /* IOs completed by libnfs, waiting for FIO */
+ int free_event_buffer_index; /* next free buffer */
+ struct io_u**events;
+};
+
+struct nfs_data {
+ struct nfsfh *nfsfh;
+ struct fio_libnfs_options *options;
+};
+
+static struct fio_option options[] = {
+ {
+ .name = "nfs_url",
+ .lname = "nfs_url",
+ .type = FIO_OPT_STR_STORE,
+ .help = "URL in libnfs format, eg nfs://<server|ipv4|ipv6>/path[?arg=val[&arg=val]*]",
+ .off1 = offsetof(struct fio_libnfs_options, nfs_url),
+ .category = FIO_OPT_C_ENGINE,
+ .group = __FIO_OPT_G_NFS,
+ },
+ {
+ .name = NULL,
+ },
+};
+
+static struct io_u *fio_libnfs_event(struct thread_data *td, int event)
+{
+ struct fio_libnfs_options *o = td->eo;
+ struct io_u *io_u = o->events[o->next_buffered_event];
+ assert(o->events[o->next_buffered_event]);
+ o->events[o->next_buffered_event] = NULL;
+ o->next_buffered_event = (o->next_buffered_event + 1) % td->o.iodepth;
+ /* validate our state machine */
+ assert(o->buffered_event_count);
+ o->buffered_event_count--;
+ assert(io_u);
+ /* assert that fio_libnfs_event is being called in sequential fashion */
+ assert(event == 0 || o->prev_requested_event_index + 1 == event);
+ if (o->buffered_event_count == 0) {
+ o->prev_requested_event_index = -1;
+ } else {
+ o->prev_requested_event_index = event;
+ }
+ return io_u;
+}
+
+static int nfs_event_loop(struct thread_data *td, bool flush) {
+ struct fio_libnfs_options *o = td->eo;
+ struct pollfd pfds[1]; /* nfs:0 */
+ /* we already have stuff queued for fio, no need to waste cpu on poll() */
+ if (o->buffered_event_count)
+ return o->buffered_event_count;
+ /* fio core logic seems to stop calling this event-loop if we ever return with 0 events */
+ #define SHOULD_WAIT() (o->outstanding_events == td->o.iodepth || (flush && o->outstanding_events))
+
+ do {
+ int timeout = SHOULD_WAIT() ? -1 : 0;
+ int ret = 0;
+ pfds[0].fd = nfs_get_fd(o->context);
+ pfds[0].events = nfs_which_events(o->context);
+ ret = poll(&pfds[0], 1, timeout);
+ if (ret < 0) {
+ if (errno == EINTR || errno == EAGAIN) {
+ continue;
+ }
+ log_err("nfs: failed to poll events: %s.\n",
+ strerror(errno));
+ break;
+ }
+
+ ret = nfs_service(o->context, pfds[0].revents);
+ if (ret < 0) {
+ log_err("nfs: socket is in an unrecoverable error state.\n");
+ break;
+ }
+ } while (SHOULD_WAIT());
+ return o->buffered_event_count;
+#undef SHOULD_WAIT
+}
+
+static int fio_libnfs_getevents(struct thread_data *td, unsigned int min,
+ unsigned int max, const struct timespec *t)
+{
+ return nfs_event_loop(td, false);
+}
+
+static void nfs_callback(int res, struct nfs_context *nfs, void *data,
+ void *private_data)
+{
+ struct io_u *io_u = private_data;
+ struct nfs_data *nfs_data = io_u->file->engine_data;
+ struct fio_libnfs_options *o = nfs_data->options;
+ if (res < 0) {
+ log_err("Failed NFS operation(code:%d): %s\n", res, nfs_get_error(o->context));
+ io_u->error = -res;
+ /* res is used for read math below, don't wanna pass negative there */
+ res = 0;
+ } else if (io_u->ddir == DDIR_READ) {
+ memcpy(io_u->buf, data, res);
+ if (res == 0)
+ log_err("Got NFS EOF, this is probably not expected\n");
+ }
+ /* fio uses resid to track remaining data */
+ io_u->resid = io_u->xfer_buflen - res;
+
+ assert(!o->events[o->free_event_buffer_index]);
+ o->events[o->free_event_buffer_index] = io_u;
+ o->free_event_buffer_index = (o->free_event_buffer_index + 1) % o->queue_depth;
+ o->outstanding_events--;
+ o->buffered_event_count++;
+}
+
+static int queue_write(struct fio_libnfs_options *o, struct io_u *io_u) {
+ struct nfs_data *nfs_data = io_u->engine_data;
+ return nfs_pwrite_async(o->context, nfs_data->nfsfh,
+ io_u->offset, io_u->buflen, io_u->buf, nfs_callback,
+ io_u);
+}
+
+static int queue_read(struct fio_libnfs_options *o, struct io_u *io_u) {
+ struct nfs_data *nfs_data = io_u->engine_data;
+ return nfs_pread_async(o->context, nfs_data->nfsfh, io_u->offset, io_u->buflen, nfs_callback, io_u);
+}
+
+static enum fio_q_status fio_libnfs_queue(struct thread_data *td,
+ struct io_u *io_u)
+{
+ struct nfs_data *nfs_data = io_u->file->engine_data;
+ struct fio_libnfs_options *o = nfs_data->options;
+ struct nfs_context *nfs = o->context;
+ int err;
+ enum fio_q_status ret = FIO_Q_QUEUED;
+
+ io_u->engine_data = nfs_data;
+ switch(io_u->ddir) {
+ case DDIR_WRITE:
+ err = queue_write(o, io_u);
+ break;
+ case DDIR_READ:
+ err = queue_read(o, io_u);
+ break;
+ case DDIR_TRIM:
+ log_err("nfs: trim is not supported");
+ err = -1;
+ break;
+ default:
+ log_err("nfs: unhandled io %d\n", io_u->ddir);
+ err = -1;
+ }
+ if (err) {
+ log_err("nfs: Failed to queue nfs op: %s\n", nfs_get_error(nfs));
+ td->error = 1;
+ return FIO_Q_COMPLETED;
+ }
+ o->outstanding_events++;
+ return ret;
+}
+
+/*
+ * Do a mount if one has not been done before
+ */
+static int do_mount(struct thread_data *td, const char *url)
+{
+ size_t event_size = sizeof(struct io_u **) * td->o.iodepth;
+ struct fio_libnfs_options *options = td->eo;
+ struct nfs_url *nfs_url = NULL;
+ int ret = 0;
+ int path_len = 0;
+ char *mnt_dir = NULL;
+
+ if (options->context)
+ return 0;
+
+ options->context = nfs_init_context();
+ if (options->context == NULL) {
+ log_err("nfs: failed to init nfs context\n");
+ return -1;
+ }
+
+ options->events = malloc(event_size);
+ memset(options->events, 0, event_size);
+
+ options->prev_requested_event_index = -1;
+ options->queue_depth = td->o.iodepth;
+
+ nfs_url = nfs_parse_url_full(options->context, url);
+ path_len = strlen(nfs_url->path);
+ mnt_dir = malloc(path_len + strlen(nfs_url->file) + 1);
+ strcpy(mnt_dir, nfs_url->path);
+ strcpy(mnt_dir + strlen(nfs_url->path), nfs_url->file);
+ ret = nfs_mount(options->context, nfs_url->server, mnt_dir);
+ free(mnt_dir);
+ nfs_destroy_url(nfs_url);
+ return ret;
+}
+
+static int fio_libnfs_setup(struct thread_data *td)
+{
+ /* Using threads with libnfs causes fio to hang on exit, lower performance */
+ td->o.use_thread = 0;
+ return 0;
+}
+
+static void fio_libnfs_cleanup(struct thread_data *td)
+{
+ struct fio_libnfs_options *o = td->eo;
+ nfs_umount(o->context);
+ nfs_destroy_context(o->context);
+ free(o->events);
+}
+
+static int fio_libnfs_open(struct thread_data *td, struct fio_file *f)
+{
+ int ret;
+ struct fio_libnfs_options *options = td->eo;
+ struct nfs_data *nfs_data = NULL;
+ int flags = 0;
+
+ if (!options->nfs_url) {
+ log_err("nfs: nfs_url is a required parameter\n");
+ return -1;
+ }
+
+ ret = do_mount(td, options->nfs_url);
+
+ if (ret != 0) {
+ log_err("nfs: Failed to mount %s with code %d: %s\n", options->nfs_url, ret, nfs_get_error(options->context));
+ return ret;
+ }
+ nfs_data = malloc(sizeof(struct nfs_data));
+ memset(nfs_data, 0, sizeof(struct nfs_data));
+ nfs_data->options = options;
+
+ if (td->o.td_ddir == TD_DDIR_WRITE) {
+ flags |= O_CREAT | O_RDWR;
+ } else {
+ flags |= O_RDWR;
+ }
+ ret = nfs_open(options->context, f->file_name, flags, &nfs_data->nfsfh);
+
+ if (ret != 0)
+ log_err("Failed to open %s: %s\n", f->file_name, nfs_get_error(options->context));
+ f->engine_data = nfs_data;
+ return ret;
+}
+
+static int fio_libnfs_close(struct thread_data *td, struct fio_file *f)
+{
+ struct nfs_data *nfs_data = f->engine_data;
+ struct fio_libnfs_options *o = nfs_data->options;
+ int ret = 0;
+ if (nfs_data->nfsfh)
+ ret = nfs_close(o->context, nfs_data->nfsfh);
+ free(nfs_data);
+ f->engine_data = NULL;
+ return ret;
+}
+
+/*
+ * Hook for writing out outstanding data.
+ */
+static int fio_libnfs_commit(struct thread_data *td) {
+ nfs_event_loop(td, true);
+ return 0;
+}
+
+struct ioengine_ops ioengine = {
+ .name = "nfs",
+ .version = FIO_IOOPS_VERSION,
+ .setup = fio_libnfs_setup,
+ .queue = fio_libnfs_queue,
+ .getevents = fio_libnfs_getevents,
+ .event = fio_libnfs_event,
+ .cleanup = fio_libnfs_cleanup,
+ .open_file = fio_libnfs_open,
+ .close_file = fio_libnfs_close,
+ .commit = fio_libnfs_commit,
+ .flags = FIO_DISKLESSIO | FIO_NOEXTEND | FIO_NODISKUTIL,
+ .options = options,
+ .option_struct_size = sizeof(struct fio_libnfs_options),
+};
+
+static void fio_init fio_nfs_register(void)
+{
+ register_ioengine(&ioengine);
+}
+
+static void fio_exit fio_nfs_unregister(void)
+{
+ unregister_ioengine(&ioengine);
+}
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
@@ -194,6 +194,18 @@ static int fio_skeleton_reset_wp(struct thread_data *td, struct fio_file *f,
}
/*
+ * 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/examples/libpmem.fio b/examples/libpmem.fio
index 0ff681f0..3b854a32 100644
--- a/examples/libpmem.fio
+++ b/examples/libpmem.fio
@@ -1,6 +1,6 @@
[global]
bs=4k
-size=8g
+size=10g
ioengine=libpmem
norandommap
time_based
@@ -18,16 +18,6 @@ numjobs=1
runtime=300
#
-# In case of 'scramble_buffers=1', the source buffer
-# is rewritten with a random value every write operations.
-#
-# But when 'scramble_buffers=0' is set, the source buffer isn't
-# rewritten. So it will be likely that the source buffer is in CPU
-# cache and it seems to be high performance.
-#
-scramble_buffers=0
-
-#
# depends on direct option, flags are set for pmem_memcpy() call:
# direct=1 - PMEM_F_MEM_NONTEMPORAL,
# direct=0 - PMEM_F_MEM_TEMPORAL.
@@ -39,9 +29,19 @@ direct=1
#
sync=1
+#
+# In case of 'scramble_buffers=1', the source buffer
+# is rewritten with a random value every write operation.
+#
+# But when 'scramble_buffers=0' is set, the source buffer isn't
+# rewritten. So it will be likely that the source buffer is in CPU
+# cache and it seems to be high write performance.
+#
+scramble_buffers=1
#
-# Setting for fio process's CPU Node and Memory Node
+# Setting for fio process's CPU Node and Memory Node.
+# Set proper node below or use `numactl` command along with FIO.
#
numa_cpu_nodes=0
numa_mem_policy=bind:0
@@ -53,21 +53,22 @@ cpus_allowed_policy=split
#
# The libpmem engine does IO to files in a DAX-mounted filesystem.
-# The filesystem should be created on an NVDIMM (e.g /dev/pmem0)
+# The filesystem should be created on a Non-Volatile DIMM (e.g /dev/pmem0)
# and then mounted with the '-o dax' option. Note that the engine
# accesses the underlying NVDIMM directly, bypassing the kernel block
# layer, so the usual filesystem/disk performance monitoring tools such
# as iostat will not provide useful data.
#
-directory=/mnt/pmem0
+#filename=/mnt/pmem/somefile
+directory=/mnt/pmem
[libpmem-seqwrite]
rw=write
stonewall
-#[libpmem-seqread]
-#rw=read
-#stonewall
+[libpmem-seqread]
+rw=read
+stonewall
#[libpmem-randwrite]
#rw=randwrite
diff --git a/examples/nfs.fio b/examples/nfs.fio
new file mode 100644
index 00000000..f856cebf
--- /dev/null
+++ b/examples/nfs.fio
@@ -0,0 +1,22 @@
+[global]
+nfs_url=nfs://127.0.0.1/nfs
+blocksize=524288
+iodepth=10
+ioengine=nfs
+size=104857600
+lat_percentiles=1
+group_reporting
+numjobs=10
+ramp_time=5s
+filename_format=myfiles.$clientuid.$jobnum.$filenum
+time_based=1
+
+[write]
+rw=write
+runtime=10s
+stonewall
+
+[read]
+wait_for=write
+rw=randread
+runtime=10s
diff --git a/fio.1 b/fio.1
index 533bcf6a..5aa54a4d 100644
--- a/fio.1
+++ b/fio.1
@@ -835,6 +835,11 @@ threads/processes.
.BI job_max_open_zones \fR=\fPint
Limit on the number of simultaneously opened zones per single thread/process.
.TP
+.BI ignore_zone_limits \fR=\fPbool
+If this isn't set, fio will query the max open zones limit from the zoned block
+device, and exit if the specified \fBmax_open_zones\fR value is larger than the
+limit reported by the device. Default: false.
+.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
@@ -1901,6 +1906,12 @@ not be \fBcudamalloc\fR. This ioengine defines engine specific options.
.B dfs
I/O engine supporting asynchronous read and write operations to the DAOS File
System (DFS) via libdfs.
+.TP
+.B nfs
+I/O engine supporting asynchronous read and write operations to
+NFS filesystems from userspace via libnfs. This is useful for
+achieving higher concurrency and thus throughput than is possible
+via kernel NFS.
.SS "I/O engine specific parameters"
In addition, there are some parameters which are only valid when a specific
\fBioengine\fR is in use. These are used identically to normal parameters,
@@ -2283,6 +2294,10 @@ Use DAOS container's chunk size by default.
.BI (dfs)object_class
Specificy a different object class for the dfs file.
Use DAOS container's object class by default.
+.TP
+.BI (nfs)nfs_url
+URL in libnfs format, eg nfs://<server|ipv4|ipv6>/path[?arg=val[&arg=val]*]
+Refer to the libnfs README for more details.
.SS "I/O depth"
.TP
.BI iodepth \fR=\fPint
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/optgroup.c b/optgroup.c
index 15a16229..bebb4a51 100644
--- a/optgroup.c
+++ b/optgroup.c
@@ -186,6 +186,10 @@ static const struct opt_group fio_opt_cat_groups[] = {
.mask = FIO_OPT_G_DFS,
},
{
+ .name = "NFS I/O engine", /* nfs */
+ .mask = FIO_OPT_G_NFS,
+ },
+ {
.name = NULL,
},
};
diff --git a/optgroup.h b/optgroup.h
index ff748629..1fb84a29 100644
--- a/optgroup.h
+++ b/optgroup.h
@@ -70,6 +70,7 @@ enum opt_category_group {
__FIO_OPT_G_NR,
__FIO_OPT_G_LIBCUFILE,
__FIO_OPT_G_DFS,
+ __FIO_OPT_G_NFS,
FIO_OPT_G_RATE = (1ULL << __FIO_OPT_G_RATE),
FIO_OPT_G_ZONE = (1ULL << __FIO_OPT_G_ZONE),
@@ -110,6 +111,7 @@ enum opt_category_group {
FIO_OPT_G_INVALID = (1ULL << __FIO_OPT_G_NR),
FIO_OPT_G_ISCSI = (1ULL << __FIO_OPT_G_ISCSI),
FIO_OPT_G_NBD = (1ULL << __FIO_OPT_G_NBD),
+ FIO_OPT_G_NFS = (1ULL << __FIO_OPT_G_NFS),
FIO_OPT_G_IOURING = (1ULL << __FIO_OPT_G_IOURING),
FIO_OPT_G_FILESTAT = (1ULL << __FIO_OPT_G_FILESTAT),
FIO_OPT_G_LIBCUFILE = (1ULL << __FIO_OPT_G_LIBCUFILE),
diff --git a/options.c b/options.c
index ddabaa82..a8986d11 100644
--- a/options.c
+++ b/options.c
@@ -2026,6 +2026,11 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
.help = "DAOS File System (dfs) IO engine",
},
#endif
+#ifdef CONFIG_NFS
+ { .ival = "nfs",
+ .help = "NFS IO engine",
+ },
+#endif
},
},
{
@@ -3488,6 +3493,16 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
.group = FIO_OPT_G_INVALID,
},
{
+ .name = "ignore_zone_limits",
+ .lname = "Ignore zone resource limits",
+ .type = FIO_OPT_BOOL,
+ .off1 = offsetof(struct thread_options, ignore_zone_limits),
+ .def = "0",
+ .help = "Ignore the zone resource limits (max open/active zones) reported by the device",
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_INVALID,
+ },
+ {
.name = "zone_reset_threshold",
.lname = "Zone reset threshold",
.help = "Zoned block device reset threshold",
diff --git a/os/os-android.h b/os/os-android.h
index 3f1aa9d3..a81cd815 100644
--- a/os/os-android.h
+++ b/os/os-android.h
@@ -71,11 +71,15 @@
#include <stdio.h>
#include <linux/ashmem.h>
#include <linux/shm.h>
+#include <android/api-level.h>
+#if __ANDROID_API__ >= __ANDROID_API_O__
+#include <android/sharedmem.h>
+#else
+#define ASHMEM_DEVICE "/dev/ashmem"
+#endif
#define shmid_ds shmid64_ds
#define SHM_HUGETLB 04000
-#define ASHMEM_DEVICE "/dev/ashmem"
-
static inline int shmctl(int __shmid, int __cmd, struct shmid_ds *__buf)
{
int ret=0;
@@ -89,6 +93,16 @@ static inline int shmctl(int __shmid, int __cmd, struct shmid_ds *__buf)
return ret;
}
+#if __ANDROID_API__ >= __ANDROID_API_O__
+static inline int shmget(key_t __key, size_t __size, int __shmflg)
+{
+ char keybuf[11];
+
+ sprintf(keybuf, "%d", __key);
+
+ return ASharedMemory_create(keybuf, __size + sizeof(uint64_t));
+}
+#else
static inline int shmget(key_t __key, size_t __size, int __shmflg)
{
int fd,ret;
@@ -114,6 +128,7 @@ error:
close(fd);
return ret;
}
+#endif
static inline void *shmat(int __shmid, const void *__shmaddr, int __shmflg)
{
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/server.h b/server.h
index b45b319b..c128df28 100644
--- a/server.h
+++ b/server.h
@@ -48,7 +48,7 @@ struct fio_net_cmd_reply {
};
enum {
- FIO_SERVER_VER = 89,
+ FIO_SERVER_VER = 91,
FIO_SERVER_MAX_FRAGMENT_PDU = 1024,
FIO_SERVER_MAX_CMD_MB = 2048,
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/thread_options.h b/thread_options.h
index 5ecc72d7..05c2d138 100644
--- a/thread_options.h
+++ b/thread_options.h
@@ -355,6 +355,7 @@ struct thread_options {
unsigned int read_beyond_wp;
int max_open_zones;
unsigned int job_max_open_zones;
+ unsigned int ignore_zone_limits;
fio_fp64_t zrt;
fio_fp64_t zrf;
};
@@ -656,6 +657,8 @@ struct thread_options_pack {
uint32_t allow_mounted_write;
uint32_t zone_mode;
+ int32_t max_open_zones;
+ uint32_t ignore_zone_limits;
} __attribute__((packed));
extern void convert_thread_options_to_cpu(struct thread_options *o, struct thread_options_pack *top);
diff --git a/zbd.c b/zbd.c
index eed796b3..5d9e331a 100644
--- a/zbd.c
+++ b/zbd.c
@@ -114,6 +114,34 @@ int zbd_reset_wp(struct thread_data *td, struct fio_file *f,
}
/**
+ * 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.
* @offset: offset in bytes. If this offset is in the first zone_size bytes
@@ -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 || td->o.ignore_zone_limits) {
+ /* 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");