Skip to content

Commit

Permalink
fio: Generalize zonemode=zbd
Browse files Browse the repository at this point in the history
Generalize the implementation of the zbd zonemode for non-linux systems
and Linux systems without the blkzoned.h header file (that is, linux
systems with a kernel predating v4.10 or kernels compiled without zoned
block device support).

The configuration option CONFIG_HAS_BLKZONED determines if the system
supports or not zoned block devices. This option can be set for Linux
only for now. If it is set, the file oslib/linux-blkzoned.c is compiled
and the 3 functions defined are used by the zbd.c code to determine a
block device zoned model, get zone information and reset zones.
For systems that do not set the CONFIG_HAS_BLKZONED option,
zonemode=zbd will be useable with regular block devices with the
zbd code emulating zones as is already done currently.

Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
  • Loading branch information
damien-lemoal authored and axboe committed Apr 7, 2020
1 parent ebc403f commit b769496
Show file tree
Hide file tree
Showing 11 changed files with 517 additions and 282 deletions.
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ SOURCE := $(sort $(patsubst $(SRCDIR)/%,%,$(wildcard $(SRCDIR)/crc/*.c)) \
gettime-thread.c helpers.c json.c idletime.c td_error.c \
profiles/tiobench.c profiles/act.c io_u_queue.c filelock.c \
workqueue.c rate-submit.c optgroup.c helper_thread.c \
steadystate.c zone-dist.c
steadystate.c zone-dist.c zbd.c

ifdef CONFIG_LIBHDFS
HDFSFLAGS= -I $(JAVA_HOME)/include -I $(JAVA_HOME)/include/linux -I $(FIO_LIBHDFS_INCLUDE)
Expand Down Expand Up @@ -160,13 +160,13 @@ endif
ifdef CONFIG_IME
SOURCE += engines/ime.c
endif
ifdef CONFIG_LINUX_BLKZONED
SOURCE += zbd.c
endif

ifeq ($(CONFIG_TARGET_OS), Linux)
SOURCE += diskutil.c fifo.c blktrace.c cgroup.c trim.c engines/sg.c \
oslib/linux-dev-lookup.c engines/io_uring.c
ifdef CONFIG_HAS_BLKZONED
SOURCE += oslib/linux-blkzoned.c
endif
LIBS += -lpthread -ldl
LDFLAGS += -rdynamic
endif
Expand Down
2 changes: 1 addition & 1 deletion configure
Original file line number Diff line number Diff line change
Expand Up @@ -2862,7 +2862,7 @@ if test "$valgrind_dev" = "yes"; then
output_sym "CONFIG_VALGRIND_DEV"
fi
if test "$linux_blkzoned" = "yes" ; then
output_sym "CONFIG_LINUX_BLKZONED"
output_sym "CONFIG_HAS_BLKZONED"
fi
if test "$zlib" = "no" ; then
echo "Consider installing zlib-dev (zlib-devel, some fio features depend on it."
Expand Down
2 changes: 0 additions & 2 deletions fio.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,6 @@ struct zone_split_index {
uint64_t size_prev;
};

#define FIO_MAX_OPEN_ZBD_ZONES 128

/*
* This describes a single thread/process executing a fio job.
*/
Expand Down
2 changes: 0 additions & 2 deletions io_u.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ struct io_u {
struct workqueue_work work;
};

#ifdef CONFIG_LINUX_BLKZONED
/*
* ZBD mode zbd_queue_io callback: called after engine->queue operation
* to advance a zone write pointer and eventually unlock the I/O zone.
Expand All @@ -108,7 +107,6 @@ struct io_u {
* or commit of an async I/O to unlock the I/O target zone.
*/
void (*zbd_put_io)(const struct io_u *);
#endif

/*
* Callback for io completion
Expand Down
3 changes: 2 additions & 1 deletion options.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "lib/pattern.h"
#include "options.h"
#include "optgroup.h"
#include "zbd.h"

char client_sockaddr_str[INET6_ADDRSTRLEN] = { 0 };

Expand Down Expand Up @@ -3362,7 +3363,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
.lname = "Maximum number of open zones",
.type = FIO_OPT_INT,
.off1 = offsetof(struct thread_options, max_open_zones),
.maxval = FIO_MAX_OPEN_ZBD_ZONES,
.maxval = ZBD_MAX_OPEN_ZONES,
.help = "Limit random writes to SMR drives to the specified"
" number of sequential zones",
.def = "0",
Expand Down
49 changes: 49 additions & 0 deletions oslib/blkzoned.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2020 Western Digital Corporation or its affiliates.
*
* This file is released under the GPL.
*/
#ifndef FIO_BLKZONED_H
#define FIO_BLKZONED_H

#include "zbd_types.h"

#ifdef CONFIG_HAS_BLKZONED
extern int blkzoned_get_zoned_model(struct thread_data *td,
struct fio_file *f, enum zbd_zoned_model *model);
extern int blkzoned_report_zones(struct thread_data *td,
struct fio_file *f, uint64_t offset,
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);
#else
/*
* Define stubs for systems that do not have zoned block device support.
*/
static inline int blkzoned_get_zoned_model(struct thread_data *td,
struct fio_file *f, enum zbd_zoned_model *model)
{
/*
* If this is a block device file, allow zbd emulation.
*/
if (f->filetype == FIO_TYPE_BLOCK) {
*model = ZBD_NONE;
return 0;
}

return -ENODEV;
}
static inline int blkzoned_report_zones(struct thread_data *td,
struct fio_file *f, uint64_t offset,
struct zbd_zone *zones, unsigned int nr_zones)
{
return -EIO;
}
static inline int blkzoned_reset_wp(struct thread_data *td, struct fio_file *f,
uint64_t offset, uint64_t length)
{
return -EIO;
}
#endif

#endif /* FIO_BLKZONED_H */
219 changes: 219 additions & 0 deletions oslib/linux-blkzoned.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/*
* Copyright (C) 2020 Western Digital Corporation or its affiliates.
*
* This file is released under the GPL.
*/
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>

#include "file.h"
#include "fio.h"
#include "lib/pow2.h"
#include "log.h"
#include "oslib/asprintf.h"
#include "smalloc.h"
#include "verify.h"
#include "zbd_types.h"

#include <linux/blkzoned.h>

/*
* Read up to 255 characters from the first line of a file. Strip the trailing
* newline.
*/
static char *read_file(const char *path)
{
char line[256], *p = line;
FILE *f;

f = fopen(path, "rb");
if (!f)
return NULL;
if (!fgets(line, sizeof(line), f))
line[0] = '\0';
strsep(&p, "\n");
fclose(f);

return strdup(line);
}

int blkzoned_get_zoned_model(struct thread_data *td, struct fio_file *f,
enum zbd_zoned_model *model)
{
const char *file_name = f->file_name;
char *zoned_attr_path = NULL;
char *model_str = NULL;
struct stat statbuf;
char *sys_devno_path = NULL;
char *part_attr_path = NULL;
char *part_str = NULL;
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;

if (stat(file_name, &statbuf) < 0)
goto out;

if (asprintf(&sys_devno_path, "/sys/dev/block/%d:%d",
major(statbuf.st_rdev), minor(statbuf.st_rdev)) < 0)
goto out;

sz = readlink(sys_devno_path, sys_path, sizeof(sys_path) - 1);
if (sz < 0)
goto out;
sys_path[sz] = '\0';

/*
* If the device is a partition device, cut the device name in the
* canonical sysfs path to obtain the sysfs path of the holder device.
* e.g.: /sys/devices/.../sda/sda1 -> /sys/devices/.../sda
*/
if (asprintf(&part_attr_path, "/sys/dev/block/%s/partition",
sys_path) < 0)
goto out;
part_str = read_file(part_attr_path);
if (part_str && *part_str == '1') {
delim = strrchr(sys_path, '/');
if (!delim)
goto out;
*delim = '\0';
}

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

model_str = read_file(zoned_attr_path);
if (!model_str)
goto out;
dprint(FD_ZBD, "%s: zbd model string: %s\n", 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_report_zones(struct thread_data *td, struct fio_file *f,
uint64_t offset, struct zbd_zone *zones,
unsigned int nr_zones)
{
struct blk_zone_report *hdr = NULL;
struct blk_zone *blkz;
struct zbd_zone *z;
unsigned int i;
int fd = -1, ret;

fd = open(f->file_name, O_RDONLY | O_LARGEFILE);
if (fd < 0)
return -errno;

hdr = calloc(1, sizeof(struct blk_zone_report) +
nr_zones * sizeof(struct blk_zone));
if (!hdr) {
ret = -ENOMEM;
goto out;
}

hdr->nr_zones = nr_zones;
hdr->sector = offset >> 9;
ret = ioctl(fd, BLKREPORTZONE, hdr);
if (ret) {
ret = -errno;
goto out;
}

nr_zones = hdr->nr_zones;
blkz = &hdr->zones[0];
z = &zones[0];
for (i = 0; i < nr_zones; i++, z++, blkz++) {
z->start = blkz->start << 9;
z->wp = blkz->wp << 9;
z->len = blkz->len << 9;

switch (blkz->type) {
case BLK_ZONE_TYPE_CONVENTIONAL:
z->type = ZBD_ZONE_TYPE_CNV;
break;
case BLK_ZONE_TYPE_SEQWRITE_REQ:
z->type = ZBD_ZONE_TYPE_SWR;
break;
case BLK_ZONE_TYPE_SEQWRITE_PREF:
z->type = ZBD_ZONE_TYPE_SWP;
break;
default:
td_verror(td, errno, "invalid zone type");
log_err("%s: invalid type for zone at sector %llu.\n",
f->file_name, (unsigned long long)offset >> 9);
ret = -EIO;
goto out;
}

switch (blkz->cond) {
case BLK_ZONE_COND_NOT_WP:
z->cond = ZBD_ZONE_COND_NOT_WP;
break;
case BLK_ZONE_COND_EMPTY:
z->cond = ZBD_ZONE_COND_EMPTY;
break;
case BLK_ZONE_COND_IMP_OPEN:
z->cond = ZBD_ZONE_COND_IMP_OPEN;
break;
case BLK_ZONE_COND_EXP_OPEN:
z->cond = ZBD_ZONE_COND_EXP_OPEN;
break;
case BLK_ZONE_COND_CLOSED:
z->cond = ZBD_ZONE_COND_CLOSED;
break;
case BLK_ZONE_COND_FULL:
z->cond = ZBD_ZONE_COND_FULL;
break;
case BLK_ZONE_COND_READONLY:
case BLK_ZONE_COND_OFFLINE:
default:
/* Treat all these conditions as offline (don't use!) */
z->cond = ZBD_ZONE_COND_OFFLINE;
break;
}
}

ret = nr_zones;
out:
free(hdr);
close(fd);

return ret;
}

int blkzoned_reset_wp(struct thread_data *td, struct fio_file *f,
uint64_t offset, uint64_t length)
{
struct blk_zone_range zr = {
.sector = offset >> 9,
.nr_sectors = length >> 9,
};

if (ioctl(f->fd, BLKRESETZONE, &zr) < 0)
return -errno;

return 0;
}
2 changes: 1 addition & 1 deletion t/run-fio-tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ def __init__(self, fio_root):
print("Unable to open {0} to check requirements".format(config_file))
Requirements._zbd = True
else:
Requirements._zbd = "CONFIG_LINUX_BLKZONED" in contents
Requirements._zbd = "CONFIG_HAS_BLKZONED" in contents
Requirements._libaio = "CONFIG_LIBAIO" in contents

Requirements._root = (os.geteuid() == 0)
Expand Down
Loading

0 comments on commit b769496

Please sign in to comment.