#!/bin/sh
GVF=FIO-VERSION-FILE
-DEF_VER=fio-2.1.7
+DEF_VER=fio-2.1.9
LF='
'
is given, fio will use 20% of the full size of the given
files or devices.
+io_limit=int Normally fio operates within the region set by 'size', which
+ means that the 'size' option sets both the region and size of
+ IO to be performed. Sometimes that is not what you want. With
+ this option, it is possible to define just the amount of IO
+ that fio should do. For instance, if 'size' is set to 20G and
+ 'io_limit' is set to 5G, fio will perform IO within the first
+ 20G but exit when 5G have been done.
+
filesize=int Individual file sizes. May be a range, in which case fio
will select sizes for files at random within the given range
and limited to 'size' in total (if that is given). If not
[cpu] cpuchunks=int Split the load into cycles of the given time. In
microseconds.
+[cpu] exit_on_io_done=bool Detect when IO threads are done, then exit.
+
[netsplice] hostname=str
[net] hostname=str The host name or IP address to use for TCP or UDP based IO.
If the job is a TCP listener or UDP reader, the hostname is not
endif
DEBUGFLAGS = -D_FORTIFY_SOURCE=2 -DFIO_INC_DEBUG
-CPPFLAGS= -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 $(DEBUGFLAGS)
+CPPFLAGS= -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DFIO_INTERNAL $(DEBUGFLAGS)
OPTFLAGS= -O3 -g -ffast-math
CFLAGS = -std=gnu99 -Wwrite-strings -Wall -Wdeclaration-after-statement $(OPTFLAGS) $(EXTFLAGS) $(BUILD_CFLAGS)
LIBS += -lm $(EXTLIBS)
ifndef CONFIG_INET_ATON
SOURCE += lib/inet_aton.c
endif
+ifdef CONFIG_GFAPI
+ SOURCE += engines/glusterfs.c
+ SOURCE += engines/glusterfs_sync.c
+ SOURCE += engines/glusterfs_async.c
+ ifdef CONFIG_GF_FADVISE
+ CFLAGS += "-DGFAPI_USE_FADVISE"
+ endif
+endif
ifeq ($(CONFIG_TARGET_OS), Linux)
SOURCE += diskutil.c fifo.c blktrace.c cgroup.c trim.c engines/sg.c \
$(QUIET_LINK)$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(T_LFSR_TEST_OBJS) $(LIBS)
clean: FORCE
- -rm -f .depend $(FIO_OBJS) $(GFIO_OBJS) $(OBJS) $(T_OBJS) $(PROGS) $(T_PROGS) core.* core gfio FIO-VERSION-FILE *.d config-host.mak config-host.h
+ -rm -f .depend $(FIO_OBJS) $(GFIO_OBJS) $(OBJS) $(T_OBJS) $(PROGS) $(T_PROGS) core.* core gfio FIO-VERSION-FILE *.d lib/*.d crc/*.d engines/*.d profiles/*.d t/*.d config-host.mak config-host.h
distclean: clean FORCE
@rm -f cscope.out fio.pdf fio_generate_plots.pdf fio2gnuplot.pdf
#define __NR_sys_vmsplice 309
#endif
-#define nop asm volatile ("diag 0,0,68" : : : "memory")
+#define nop asm volatile("nop" : : : "memory")
#define read_barrier() asm volatile("bcr 15,0" : : : "memory")
#define write_barrier() asm volatile("bcr 15,0" : : : "memory")
{
unsigned long long clk;
+#ifdef CONFIG_S390_Z196_FACILITIES
+ /*
+ * Fio needs monotonic (never lower), but not strict monotonic (never
+ * the same) so store clock fast is enough.
+ */
+ __asm__ __volatile__("stckf %0" : "=Q" (clk) : : "cc");
+#else
__asm__ __volatile__("stck %0" : "=Q" (clk) : : "cc");
- return clk;
+#endif
+ return clk>>12;
}
+#define ARCH_CPU_CLOCK_CYCLES_PER_USEC 1
+#define ARCH_HAVE_CPU_CLOCK
+
#define ARCH_HAVE_INIT
extern int tsc_reliable;
static inline int arch_init(char *envp[])
return 0;
}
-#define ARCH_HAVE_CPU_CLOCK
-
#endif
ratemin);
return 1;
} else {
- rate = ((bytes - td->rate_bytes[ddir]) * 1000) / spent;
+ if (spent)
+ rate = ((bytes - td->rate_bytes[ddir]) * 1000) / spent;
+ else
+ rate = 0;
+
if (rate < ratemin ||
bytes < td->rate_bytes[ddir]) {
log_err("%s: min rate %u not met, got"
td->o.name, rate_iops);
return 1;
} else {
- rate = ((iops - td->rate_blocks[ddir]) * 1000) / spent;
+ if (spent)
+ rate = ((iops - td->rate_blocks[ddir]) * 1000) / spent;
+ else
+ rate = 0;
+
if (rate < rate_iops_min ||
iops < td->rate_blocks[ddir]) {
log_err("%s: min iops rate %u not met,"
static int io_bytes_exceeded(struct thread_data *td)
{
- unsigned long long bytes;
+ unsigned long long bytes, limit;
if (td_rw(td))
bytes = td->this_io_bytes[DDIR_READ] + td->this_io_bytes[DDIR_WRITE];
else
bytes = td->this_io_bytes[DDIR_TRIM];
- return bytes >= td->o.size || exceeds_number_ios(td);
+ if (td->o.io_limit)
+ limit = td->o.io_limit;
+ else
+ limit = td->o.size;
+
+ return bytes >= limit || exceeds_number_ios(td);
}
/*
case FIO_Q_COMPLETED:
if (io_u->error) {
ret = -io_u->error;
+ unlog_io_piece(td, io_u);
clear_io_u(td, io_u);
} else if (io_u->resid) {
int bytes = io_u->xfer_buflen - io_u->resid;
struct fio_file *f = io_u->file;
bytes_issued += bytes;
+
+ trim_io_piece(td, io_u);
+
/*
* zero read, fail
*/
if (!bytes) {
+ unlog_io_piece(td, io_u);
td_verror(td, EIO, "full resid");
put_io_u(td, io_u);
break;
bytes_issued += io_u->xfer_buflen;
break;
case FIO_Q_BUSY:
+ unlog_io_piece(td, io_u);
requeue_io_u(td, &io_u);
ret2 = td_io_commit(td);
if (ret2 < 0)
/*
* Read back and check that the selected scheduler is now the default.
*/
- ret = fread(tmp, 1, sizeof(tmp), f);
+ ret = fread(tmp, sizeof(tmp), 1, f);
if (ferror(f) || ret < 0) {
td_verror(td, errno, "fread");
fclose(f);
return 1;
}
+ tmp[sizeof(tmp) - 1] = '\0';
+
sprintf(tmp2, "[%s]", td->o.ioscheduler);
if (!strstr(tmp, tmp2)) {
static int keep_running(struct thread_data *td)
{
+ unsigned long long limit;
+
if (td->done)
return 0;
if (td->o.time_based)
if (exceeds_number_ios(td))
return 0;
- if (td->o.size != -1ULL && ddir_rw_sum(td->io_bytes) < td->o.size) {
+ if (td->o.io_limit)
+ limit = td->o.io_limit;
+ else
+ limit = td->o.size;
+
+ if (limit != -1ULL && ddir_rw_sum(td->io_bytes) < limit) {
uint64_t diff;
/*
* If the difference is less than the minimum IO size, we
* are done.
*/
- diff = td->o.size - ddir_rw_sum(td->io_bytes);
+ diff = limit - ddir_rw_sum(td->io_bytes);
if (diff < td_max_bs(td))
return 0;
#ifdef CONFIG_LIBNUMA
/* numa node setup */
if (o->numa_cpumask_set || o->numa_memmask_set) {
+ struct bitmask *mask;
int ret;
if (numa_available() < 0) {
}
if (o->numa_cpumask_set) {
- ret = numa_run_on_node_mask(o->numa_cpunodesmask);
+ mask = numa_parse_nodestring(o->numa_cpunodes);
+ ret = numa_run_on_node_mask(mask);
+ numa_free_nodemask(mask);
if (ret == -1) {
td_verror(td, errno, \
"numa_run_on_node_mask failed\n");
if (o->numa_memmask_set) {
+ mask = NULL;
+ if (o->numa_memnodes)
+ mask = numa_parse_nodestring(o->numa_memnodes);
+
switch (o->numa_mem_mode) {
case MPOL_INTERLEAVE:
- numa_set_interleave_mask(o->numa_memnodesmask);
+ numa_set_interleave_mask(mask);
break;
case MPOL_BIND:
- numa_set_membind(o->numa_memnodesmask);
+ numa_set_membind(mask);
break;
case MPOL_LOCAL:
numa_set_localalloc();
break;
}
+ if (mask)
+ numa_free_nodemask(mask);
+
}
}
#endif
*/
if (td->o.replay_redirect) {
dprint(FD_BLKTRACE, "device lookup: %d/%d\n overridden"
- " with: %s", maj, min,
+ " with: %s\n", maj, min,
td->o.replay_redirect);
strcpy(path, td->o.replay_redirect);
found = 1;
dprint(FD_BLKTRACE, "add devices %s\n", dev);
fileno = add_file_exclusive(td, dev);
+ td->o.open_files++;
td->files[fileno]->major = maj;
td->files[fileno]->minor = min;
trace_add_open_close_event(td, fileno, FIO_LOG_OPEN_FILE);
struct fifo *fifo;
int fd, i, old_state;
struct fio_file *f;
+ int this_depth, depth;
fd = open(filename, O_RDONLY);
if (fd < 0) {
ios[0] = ios[1] = 0;
rw_bs[0] = rw_bs[1] = 0;
skipped_writes = 0;
+ this_depth = depth = 0;
do {
int ret = trace_fifo_get(td, fifo, fd, &t, sizeof(t));
goto err;
}
if ((t.action & BLK_TC_ACT(BLK_TC_NOTIFY)) == 0) {
+ if ((t.action & 0xffff) == __BLK_TA_QUEUE)
+ this_depth++;
+ else if ((t.action & 0xffff) == __BLK_TA_COMPLETE) {
+ depth = max(depth, this_depth);
+ this_depth = 0;
+ }
if (!ttime) {
ttime = t.time;
cpu = t.cpu;
return 1;
}
+ /*
+ * For stacked devices, we don't always get a COMPLETE event so
+ * the depth grows to insane values. Limit it to something sane(r).
+ */
+ if (!depth || depth > 1024)
+ depth = 1024;
+
if (skipped_writes)
log_err("fio: %s skips replay of %lu writes due to read-only\n",
td->o.name, skipped_writes);
*/
td->o.odirect = 1;
+ /*
+ * we don't know if this option was set or not. it defaults to 1,
+ * so we'll just guess that we should override it if it's still 1
+ */
+ if (td->o.iodepth != 1)
+ td->o.iodepth = depth;
+
return 0;
err:
close(fd);
*dst = strdup(__src);
}
-static void string_to_net(uint8_t *dst, const char *src)
+static void __string_to_net(uint8_t *dst, const char *src, size_t dst_size)
{
- if (src)
- strcpy((char *) dst, src);
- else
+ if (src) {
+ dst[dst_size - 1] = '\0';
+ strncpy((char *) dst, src, dst_size - 1);
+ } else
dst[0] = '\0';
}
+#define string_to_net(dst, src) __string_to_net((dst), (src), sizeof(dst))
+
static void free_thread_options_to_cpu(struct thread_options *o)
{
free(o->description);
o->iodepth_batch = le32_to_cpu(top->iodepth_batch);
o->iodepth_batch_complete = le32_to_cpu(top->iodepth_batch_complete);
o->size = le64_to_cpu(top->size);
+ o->io_limit = le64_to_cpu(top->io_limit);
o->size_percent = le32_to_cpu(top->size_percent);
o->fill_device = le32_to_cpu(top->fill_device);
o->file_append = le32_to_cpu(top->file_append);
memcpy(top->buffer_pattern, o->buffer_pattern, MAX_PATTERN_SIZE);
top->size = __cpu_to_le64(o->size);
+ top->io_limit = __cpu_to_le64(o->io_limit);
top->verify_backlog = __cpu_to_le64(o->verify_backlog);
top->start_delay = __cpu_to_le64(o->start_delay);
top->start_delay_high = __cpu_to_le64(o->start_delay_high);
memset(addr, 0, sizeof(*addr));
addr->sun_family = AF_UNIX;
- strcpy(addr->sun_path, client->hostname);
+ strncpy(addr->sun_path, client->hostname, sizeof(addr->sun_path) - 1);
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
int i;
for (i = 0; i < 2; i++) {
- dus->ios[i] = le32_to_cpu(dus->ios[i]);
- dus->merges[i] = le32_to_cpu(dus->merges[i]);
- dus->sectors[i] = le64_to_cpu(dus->sectors[i]);
- dus->ticks[i] = le32_to_cpu(dus->ticks[i]);
+ dus->s.ios[i] = le32_to_cpu(dus->s.ios[i]);
+ dus->s.merges[i] = le32_to_cpu(dus->s.merges[i]);
+ dus->s.sectors[i] = le64_to_cpu(dus->s.sectors[i]);
+ dus->s.ticks[i] = le32_to_cpu(dus->s.ticks[i]);
}
- dus->io_ticks = le32_to_cpu(dus->io_ticks);
- dus->time_in_queue = le32_to_cpu(dus->time_in_queue);
- dus->msec = le64_to_cpu(dus->msec);
+ dus->s.io_ticks = le32_to_cpu(dus->s.io_ticks);
+ dus->s.time_in_queue = le32_to_cpu(dus->s.time_in_queue);
+ dus->s.msec = le64_to_cpu(dus->s.msec);
}
static void handle_du(struct fio_client *client, struct fio_net_cmd *cmd)
echo "sched_setaffinity(3 arg) $linux_3arg_affinity"
echo "sched_setaffinity(2 arg) $linux_2arg_affinity"
-##########################################
-# CPU_COUNT test
-cpu_count="no"
-cat > $TMPC << EOF
-#include <sched.h>
-int main(int argc, char **argv)
-{
- cpu_set_t mask;
- return CPU_COUNT(&mask);
-}
-EOF
-if compile_prog "" "" "cpu_count"; then
- cpu_count="yes"
-fi
-echo "CPU_COUNT $cpu_count"
-
##########################################
# clock_gettime probe
clock_gettime="no"
fi
echo "setvbuf $setvbuf"
+# check for gfapi
+gfapi="no"
+cat > $TMPC << EOF
+#include <glusterfs/api/glfs.h>
+
+int main(int argc, char **argv)
+{
+
+ glfs_t *g = glfs_new("foo");
+
+ return 0;
+}
+EOF
+if compile_prog "" "-lgfapi -lglusterfs" "gfapi"; then
+ LIBS="-lgfapi -lglusterfs $LIBS"
+ gfapi="yes"
+fi
+ echo "Gluster API engine $gfapi"
+
+##########################################
+# check for gfapi fadvise support
+gf_fadvise="no"
+cat > $TMPC << EOF
+#include <glusterfs/api/glfs.h>
+
+int main(int argc, char **argv)
+{
+ struct glfs_fd *fd;
+ int ret = glfs_fadvise(fd, 0, 0, 1);
+
+ return 0;
+}
+EOF
+
+if compile_prog "" "-lgfapi -lglusterfs" "gfapi"; then
+ gf_fadvise="yes"
+fi
+echo "Gluster API use fadvise $gf_fadvise"
+
+##########################################
+# Check if we support stckf on s390
+s390_z196_facilities="no"
+cat > $TMPC << EOF
+#define STFLE_BITS_Z196 45 /* various z196 facilities ... */
+int main(int argc, char **argv)
+{
+ /* We want just 1 double word to be returned. */
+ register unsigned long reg0 asm("0") = 0;
+ unsigned long stfle_bits;
+ asm volatile(".machine push" "\n\t"
+ ".machine \"z9-109\"" "\n\t"
+ "stfle %0" "\n\t"
+ ".machine pop" "\n"
+ : "=QS" (stfle_bits), "+d" (reg0)
+ : : "cc");
+
+ if ((stfle_bits & (1UL << (63 - STFLE_BITS_Z196))) != 0)
+ return 0;
+ else
+ return -1;
+}
+EOF
+if compile_prog "" "" "s390_z196_facilities"; then
+ $TMPE
+ if [[ $? -eq 0 ]]; then
+ s390_z196_facilities="yes"
+ fi
+fi
+echo "s390_z196_facilities $s390_z196_facilities"
#############################################################################
if test "$wordsize" = "64" ; then
if test "$rbd" = "yes" ; then
output_sym "CONFIG_RBD"
fi
-if test "$cpu_count" = "yes" ; then
- output_sym "CONFIG_CPU_COUNT"
-fi
if test "$setvbuf" = "yes" ; then
output_sym "CONFIG_SETVBUF"
fi
+if test "$s390_z196_facilities" = "yes" ; then
+ output_sym "CONFIG_S390_Z196_FACILITIES"
+ CFLAGS="$CFLAGS -march=z9-109"
+fi
+if test "$gfapi" = "yes" ; then
+ output_sym "CONFIG_GFAPI"
+fi
+if test "$gf_fadvise" = "yes" ; then
+ output_sym "CONFIG_GF_FADVISE"
+fi
echo "LIBS+=$LIBS" >> $config_host_mak
echo "CFLAGS+=$CFLAGS" >> $config_host_mak
#include "../fio.h"
#include "../gettime.h"
-#include "../time.h"
+#include "../fio_time.h"
#include "../verify.h"
#include "../crc/md5.h"
#include <dirent.h>
#include <libgen.h>
#include <math.h>
+#include <assert.h>
#include "fio.h"
#include "smalloc.h"
dprint(FD_DISKUTIL, "%s: %s", du->path, p);
- ret = sscanf(p, "%u %u %llu %u %u %u %llu %u %u %u %u\n", &dus->ios[0],
- &dus->merges[0], §ors[0],
- &dus->ticks[0], &dus->ios[1],
- &dus->merges[1], §ors[1],
- &dus->ticks[1], &in_flight,
- &dus->io_ticks, &dus->time_in_queue);
+ ret = sscanf(p, "%u %u %llu %u %u %u %llu %u %u %u %u\n",
+ &dus->s.ios[0],
+ &dus->s.merges[0], §ors[0],
+ &dus->s.ticks[0], &dus->s.ios[1],
+ &dus->s.merges[1], §ors[1],
+ &dus->s.ticks[1], &in_flight,
+ &dus->s.io_ticks,
+ &dus->s.time_in_queue);
fclose(f);
dprint(FD_DISKUTIL, "%s: stat read ok? %d\n", du->path, ret == 1);
- dus->sectors[0] = sectors[0];
- dus->sectors[1] = sectors[1];
+ dus->s.sectors[0] = sectors[0];
+ dus->s.sectors[1] = sectors[1];
return ret != 11;
}
dus = &du->dus;
ldus = &du->last_dus;
- dus->sectors[0] += (__dus.sectors[0] - ldus->sectors[0]);
- dus->sectors[1] += (__dus.sectors[1] - ldus->sectors[1]);
- dus->ios[0] += (__dus.ios[0] - ldus->ios[0]);
- dus->ios[1] += (__dus.ios[1] - ldus->ios[1]);
- dus->merges[0] += (__dus.merges[0] - ldus->merges[0]);
- dus->merges[1] += (__dus.merges[1] - ldus->merges[1]);
- dus->ticks[0] += (__dus.ticks[0] - ldus->ticks[0]);
- dus->ticks[1] += (__dus.ticks[1] - ldus->ticks[1]);
- dus->io_ticks += (__dus.io_ticks - ldus->io_ticks);
- dus->time_in_queue += (__dus.time_in_queue - ldus->time_in_queue);
+ dus->s.sectors[0] += (__dus.s.sectors[0] - ldus->s.sectors[0]);
+ dus->s.sectors[1] += (__dus.s.sectors[1] - ldus->s.sectors[1]);
+ dus->s.ios[0] += (__dus.s.ios[0] - ldus->s.ios[0]);
+ dus->s.ios[1] += (__dus.s.ios[1] - ldus->s.ios[1]);
+ dus->s.merges[0] += (__dus.s.merges[0] - ldus->s.merges[0]);
+ dus->s.merges[1] += (__dus.s.merges[1] - ldus->s.merges[1]);
+ dus->s.ticks[0] += (__dus.s.ticks[0] - ldus->s.ticks[0]);
+ dus->s.ticks[1] += (__dus.s.ticks[1] - ldus->s.ticks[1]);
+ dus->s.io_ticks += (__dus.s.io_ticks - ldus->s.io_ticks);
+ dus->s.time_in_queue += (__dus.s.time_in_queue - ldus->s.time_in_queue);
fio_gettime(&t, NULL);
- dus->msec += mtime_since(&du->time, &t);
+ dus->s.msec += mtime_since(&du->time, &t);
memcpy(&du->time, &t, sizeof(t));
- memcpy(ldus, &__dus, sizeof(__dus));
+ memcpy(&ldus->s, &__dus.s, sizeof(__dus.s));
}
int update_io_ticks(void)
* are links to the real directories for the slave
* devices?
*/
- linklen = readlink(temppath, slavepath, PATH_MAX - 0);
+ linklen = readlink(temppath, slavepath, PATH_MAX - 1);
if (linklen < 0) {
perror("readlink() for slave device.");
+ closedir(dirhandle);
return;
}
slavepath[linklen] = '\0';
sprintf(temppath, "%s/%s/dev", slavesdir, slavepath);
if (read_block_dev_entry(temppath, &majdev, &mindev)) {
perror("Error getting slave device numbers.");
+ closedir(dirhandle);
return;
}
sfree(du);
return NULL;
}
- strncpy((char *) du->dus.name, basename(path), FIO_DU_NAME_SZ);
+ strncpy((char *) du->dus.name, basename(path), FIO_DU_NAME_SZ - 1);
du->sysfs_root = path;
du->major = majdev;
du->minor = mindev;
flist_for_each(entry, &masterdu->slaves) {
slavedu = flist_entry(entry, struct disk_util, slavelist);
dus = &slavedu->dus;
- agg->ios[0] += dus->ios[0];
- agg->ios[1] += dus->ios[1];
- agg->merges[0] += dus->merges[0];
- agg->merges[1] += dus->merges[1];
- agg->sectors[0] += dus->sectors[0];
- agg->sectors[1] += dus->sectors[1];
- agg->ticks[0] += dus->ticks[0];
- agg->ticks[1] += dus->ticks[1];
- agg->time_in_queue += dus->time_in_queue;
+ agg->ios[0] += dus->s.ios[0];
+ agg->ios[1] += dus->s.ios[1];
+ agg->merges[0] += dus->s.merges[0];
+ agg->merges[1] += dus->s.merges[1];
+ agg->sectors[0] += dus->s.sectors[0];
+ agg->sectors[1] += dus->s.sectors[1];
+ agg->ticks[0] += dus->s.ticks[0];
+ agg->ticks[1] += dus->s.ticks[1];
+ agg->time_in_queue += dus->s.time_in_queue;
agg->slavecount++;
- util = (double) (100 * dus->io_ticks / (double) slavedu->dus.msec);
+ util = (double) (100 * dus->s.io_ticks / (double) slavedu->dus.s.msec);
/* System utilization is the utilization of the
* component with the highest utilization.
*/
{
double util = 0;
- if (dus->msec)
- util = (double) 100 * dus->io_ticks / (double) dus->msec;
+ if (dus->s.msec)
+ util = (double) 100 * dus->s.io_ticks / (double) dus->s.msec;
if (util > 100.0)
util = 100.0;
log_info(" %s: ios=%u/%u, merge=%u/%u, ticks=%u/%u, "
"in_queue=%u, util=%3.2f%%", dus->name,
- dus->ios[0], dus->ios[1],
- dus->merges[0], dus->merges[1],
- dus->ticks[0], dus->ticks[1],
- dus->time_in_queue, util);
+ dus->s.ios[0], dus->s.ios[1],
+ dus->s.merges[0], dus->s.merges[1],
+ dus->s.ticks[0], dus->s.ticks[1],
+ dus->s.time_in_queue, util);
} else {
log_info(";%s;%u;%u;%u;%u;%u;%u;%u;%3.2f%%",
- dus->name, dus->ios[0], dus->ios[1],
- dus->merges[0], dus->merges[1],
- dus->ticks[0], dus->ticks[1],
- dus->time_in_queue, util);
+ dus->name, dus->s.ios[0],
+ dus->s.ios[1], dus->s.merges[0],
+ dus->s.merges[1], dus->s.ticks[0],
+ dus->s.ticks[1],
+ dus->s.time_in_queue, util);
}
/*
struct json_object *obj;
double util = 0;
- if (dus->msec)
- util = (double) 100 * dus->io_ticks / (double) dus->msec;
+ if (dus->s.msec)
+ util = (double) 100 * dus->s.io_ticks / (double) dus->s.msec;
if (util > 100.0)
util = 100.0;
json_array_add_value_object(array, obj);
json_object_add_value_string(obj, "name", dus->name);
- json_object_add_value_int(obj, "read_ios", dus->ios[0]);
- json_object_add_value_int(obj, "write_ios", dus->ios[1]);
- json_object_add_value_int(obj, "read_merges", dus->merges[0]);
- json_object_add_value_int(obj, "write_merges", dus->merges[1]);
- json_object_add_value_int(obj, "read_ticks", dus->ticks[0]);
- json_object_add_value_int(obj, "write_ticks", dus->ticks[1]);
- json_object_add_value_int(obj, "in_queue", dus->time_in_queue);
+ json_object_add_value_int(obj, "read_ios", dus->s.ios[0]);
+ json_object_add_value_int(obj, "write_ios", dus->s.ios[1]);
+ json_object_add_value_int(obj, "read_merges", dus->s.merges[0]);
+ json_object_add_value_int(obj, "write_merges", dus->s.merges[1]);
+ json_object_add_value_int(obj, "read_ticks", dus->s.ticks[0]);
+ json_object_add_value_int(obj, "write_ticks", dus->s.ticks[1]);
+ json_object_add_value_int(obj, "in_queue", dus->s.time_in_queue);
json_object_add_value_float(obj, "util", util);
/*
return;
}
- if (!terse && !parent)
+ if (output_format == FIO_OUTPUT_JSON)
+ assert(parent);
+
+ if (!terse && output_format != FIO_OUTPUT_JSON)
log_info("\nDisk stats (read/write):\n");
- if (output_format == FIO_OUTPUT_JSON) {
+ if (output_format == FIO_OUTPUT_JSON)
json_object_add_disk_utils(parent, &disk_list);
- } else
+ else
flist_for_each(entry, &disk_list) {
du = flist_entry(entry, struct disk_util, list);
extern volatile int disk_util_exit;
-/*
- * Disk utils as read in /sys/block/<dev>/stat
- */
-struct disk_util_stat {
- uint8_t name[FIO_DU_NAME_SZ];
+struct disk_util_stats {
uint32_t ios[2];
uint32_t merges[2];
uint64_t sectors[2];
uint64_t msec;
};
+/*
+ * Disk utils as read in /sys/block/<dev>/stat
+ */
+struct disk_util_stat {
+ uint8_t name[FIO_DU_NAME_SZ];
+ struct disk_util_stats s;
+};
+
struct disk_util_agg {
uint32_t ios[2];
uint32_t merges[2];
/*
* don't block for min events == 0
*/
- if (!min) {
- bd->fd_flags[i] = fcntl(bf->fd, F_GETFL);
- fcntl(bf->fd, F_SETFL, bd->fd_flags[i] | O_NONBLOCK);
- }
+ if (!min)
+ bd->fd_flags[i] = fio_set_fd_nonblocking(bf->fd, "binject");
+ else
+ bd->fd_flags[i] = -1;
+
bd->pfds[i].fd = bf->fd;
bd->pfds[i].events = POLLIN;
}
if (!min) {
for_each_file(td, f, i) {
bf = (struct binject_file *) (uintptr_t) f->engine_data;
- fcntl(bf->fd, F_SETFL, bd->fd_flags[i]);
+
+ if (bd->fd_flags[i] == -1)
+ continue;
+
+ if (fcntl(bf->fd, F_SETFL, bd->fd_flags[i]) < 0)
+ log_err("fio: binject failed to restore fcntl flags: %s\n", strerror(errno));
}
}
struct thread_data *td;
unsigned int cpuload;
unsigned int cpucycle;
+ unsigned int exit_io_done;
};
static struct fio_option options[] = {
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_INVALID,
},
+ {
+ .name = "exit_on_io_done",
+ .lname = "Exit when IO threads are done",
+ .type = FIO_OPT_BOOL,
+ .off1 = offsetof(struct cpu_options, exit_io_done),
+ .help = "Exit when IO threads finish",
+ .def = "0",
+ .category = FIO_OPT_C_GENERAL,
+ .group = FIO_OPT_G_INVALID,
+ },
{
.name = NULL,
},
{
struct cpu_options *co = td->eo;
+ if (co->exit_io_done && !fio_running_or_pending_io_threads()) {
+ td->done = 1;
+ return FIO_Q_BUSY;
+ }
+
usec_spin(co->cpucycle);
return FIO_Q_COMPLETED;
}
ed = malloc(sizeof(*ed));
if (!ed) {
- td_verror(td, -ENOMEM, "io_queue_init");
+ td_verror(td, ENOMEM, "io_queue_init");
return 1;
}
memset(ed, 0 ,sizeof(*ed));
ed->donor_fd = open(donor_name, O_CREAT|O_WRONLY, 0644);
if (ed->donor_fd < 0) {
- td_verror(td, ed->donor_fd, "io_queue_init");
+ td_verror(td, errno, "io_queue_init");
log_err("Can't open donor file %s err:%d", donor_name, ed->donor_fd);
free(ed);
return 1;
--- /dev/null
+#include <glusterfs/api/glfs.h>
+#include <glusterfs/api/glfs-handles.h>
+#include "../fio.h"
+
+struct gf_options {
+ struct thread_data *td;
+ char *gf_vol;
+ char *gf_brick;
+};
+
+struct gf_data {
+ glfs_t *fs;
+ glfs_fd_t *fd;
+ struct io_u **aio_events;
+};
+
+extern struct fio_option gfapi_options[];
+extern int fio_gf_setup(struct thread_data *td);
+extern void fio_gf_cleanup(struct thread_data *td);
+extern int fio_gf_get_file_size(struct thread_data *td, struct fio_file *f);
+extern int fio_gf_open_file(struct thread_data *td, struct fio_file *f);
+extern int fio_gf_close_file(struct thread_data *td, struct fio_file *f);
--- /dev/null
+/*
+ * glusterfs engine
+ *
+ * common Glusterfs's gfapi interface
+ *
+ */
+
+#include "gfapi.h"
+
+struct fio_option gfapi_options[] = {
+ {
+ .name = "volume",
+ .lname = "Glusterfs volume",
+ .type = FIO_OPT_STR_STORE,
+ .help = "Name of the Glusterfs volume",
+ .off1 = offsetof(struct gf_options, gf_vol),
+ .category = FIO_OPT_C_ENGINE,
+ .group = FIO_OPT_G_GFAPI,
+ },
+ {
+ .name = "brick",
+ .lname = "Glusterfs brick name",
+ .type = FIO_OPT_STR_STORE,
+ .help = "Name of the Glusterfs brick to connect",
+ .off1 = offsetof(struct gf_options, gf_brick),
+ .category = FIO_OPT_C_ENGINE,
+ .group = FIO_OPT_G_GFAPI,
+ },
+ {
+ .name = NULL,
+ },
+};
+
+int fio_gf_setup(struct thread_data *td)
+{
+ int r = 0;
+ struct gf_data *g = NULL;
+ struct gf_options *opt = td->eo;
+ struct stat sb = {0, };
+
+ dprint(FD_IO, "fio setup\n");
+
+ if (td->io_ops->data)
+ return 0;
+
+ g = malloc(sizeof(struct gf_data));
+ if (!g){
+ log_err("malloc failed.\n");
+ return -ENOMEM;
+ }
+ g->fs = NULL; g->fd = NULL; g->aio_events = NULL;
+
+ g->fs = glfs_new (opt->gf_vol);
+ if (!g->fs){
+ log_err("glfs_new failed.\n");
+ goto cleanup;
+ }
+ glfs_set_logging (g->fs, "/tmp/fio_gfapi.log", 7);
+ /* default to tcp */
+ r = glfs_set_volfile_server(g->fs, "tcp", opt->gf_brick, 0);
+ if (r){
+ log_err("glfs_set_volfile_server failed.\n");
+ goto cleanup;
+ }
+ r = glfs_init(g->fs);
+ if (r){
+ log_err("glfs_init failed. Is glusterd running on brick?\n");
+ goto cleanup;
+ }
+ sleep(2);
+ r = glfs_lstat (g->fs, ".", &sb);
+ if (r){
+ log_err("glfs_lstat failed.\n");
+ goto cleanup;
+ }
+ dprint(FD_FILE, "fio setup %p\n", g->fs);
+ td->io_ops->data = g;
+cleanup:
+ if (r){
+ if (g){
+ if (g->fs){
+ glfs_fini(g->fs);
+ }
+ free(g);
+ td->io_ops->data = NULL;
+ }
+ }
+ return r;
+}
+
+void fio_gf_cleanup(struct thread_data *td)
+{
+ struct gf_data *g = td->io_ops->data;
+
+ if (g) {
+ if (g->aio_events)
+ free(g->aio_events);
+ if (g->fd)
+ glfs_close(g->fd);
+ if (g->fs)
+ glfs_fini(g->fs);
+ free(g);
+ td->io_ops->data = NULL;
+ }
+}
+
+int fio_gf_get_file_size(struct thread_data *td, struct fio_file *f)
+{
+ struct stat buf;
+ int ret;
+ struct gf_data *g = td->io_ops->data;
+
+ dprint(FD_FILE, "get file size %s\n", f->file_name);
+
+ if (!g || !g->fs)
+ {
+ return 0;
+ }
+ if (fio_file_size_known(f))
+ return 0;
+
+ ret = glfs_lstat (g->fs, f->file_name, &buf);
+ if (ret < 0){
+ log_err("glfs_lstat failed.\n");
+ return ret;
+ }
+
+ f->real_file_size = buf.st_size;
+ fio_file_set_size_known(f);
+
+ return 0;
+
+}
+
+int fio_gf_open_file(struct thread_data *td, struct fio_file *f)
+{
+
+ int flags = 0;
+ int ret = 0;
+ struct gf_data *g = td->io_ops->data;
+ struct stat sb = {0, };
+
+ if (td_write(td)) {
+ if (!read_only)
+ flags = O_RDWR;
+ } else if (td_read(td)) {
+ if (!read_only)
+ flags = O_RDWR;
+ else
+ flags = O_RDONLY;
+ }
+ dprint(FD_FILE, "fio file %s open mode %s td rw %s\n", f->file_name,
+ flags == O_RDONLY? "ro":"rw", td_read(td)? "read":"write");
+ g->fd = glfs_creat(g->fs, f->file_name, flags, 0644);
+ if (!g->fd){
+ log_err("glfs_creat failed.\n");
+ ret = errno;
+ }
+ /* file for read doesn't exist or shorter than required, create/extend it */
+ if (td_read(td)){
+ if (glfs_lstat (g->fs, f->file_name, &sb) || sb.st_size < f->real_file_size){
+ dprint(FD_FILE, "fio extend file %s from %ld to %ld\n", f->file_name, sb.st_size, f->real_file_size);
+ ret = glfs_ftruncate (g->fd, f->real_file_size);
+ if (ret){
+ log_err("failed fio extend file %s to %ld\n", f->file_name, f->real_file_size);
+ }else{
+ unsigned long long left;
+ unsigned int bs;
+ char *b;
+ int r;
+
+ /* fill the file, copied from extend_file */
+ b = malloc(td->o.max_bs[DDIR_WRITE]);
+
+ left = f->real_file_size;
+ while (left && !td->terminate) {
+ bs = td->o.max_bs[DDIR_WRITE];
+ if (bs > left)
+ bs = left;
+
+ fill_io_buffer(td, b, bs, bs);
+
+ r = glfs_write(g->fd, b, bs, 0);
+ dprint(FD_IO, "fio write %d of %ld file %s\n", r, f->real_file_size, f->file_name);
+
+ if (r > 0) {
+ left -= r;
+ continue;
+ } else {
+ if (r < 0) {
+ int __e = errno;
+
+ if (__e == ENOSPC) {
+ if (td->o.fill_device)
+ break;
+ log_info("fio: ENOSPC on laying out "
+ "file, stopping\n");
+ break;
+ }
+ td_verror(td, errno, "write");
+ } else
+ td_verror(td, EIO, "write");
+
+ break;
+ }
+ }
+
+ if (b) free(b);
+ glfs_lseek(g->fd, 0, SEEK_SET);
+
+ if (td->terminate) {
+ dprint(FD_FILE, "terminate unlink %s\n", f->file_name);
+ unlink(f->file_name);
+ } else if (td->o.create_fsync) {
+ if (glfs_fsync(g->fd) < 0) {
+ dprint(FD_FILE, "failed to sync, close %s\n", f->file_name);
+ td_verror(td, errno, "fsync");
+ glfs_close(g->fd);
+ g->fd = NULL;
+ return 1;
+ }
+ }
+ }
+ }
+ }
+#if defined(GFAPI_USE_FADVISE)
+ {
+ int r = 0;
+ if (td_random(td)){
+ r = glfs_fadvise(g->fd, 0, f->real_file_size, POSIX_FADV_RANDOM);
+ }else{
+ r = glfs_fadvise(g->fd, 0, f->real_file_size, POSIX_FADV_SEQUENTIAL);
+ }
+ if (r){
+ dprint(FD_FILE, "fio %p fadvise %s status %d\n", g->fs, f->file_name, r);
+ }
+ }
+#endif
+ dprint(FD_FILE, "fio %p created %s\n", g->fs, f->file_name);
+ f->fd = -1;
+ f->shadow_fd = -1;
+
+ return ret;
+}
+
+int fio_gf_close_file(struct thread_data *td, struct fio_file *f)
+{
+ int ret = 0;
+ struct gf_data *g = td->io_ops->data;
+
+ dprint(FD_FILE, "fd close %s\n", f->file_name);
+
+ if (g){
+ if (g->fd && glfs_close(g->fd) < 0)
+ ret = errno;
+
+ if (g->fs)
+ glfs_fini(g->fs);
+
+ g->fd = NULL;
+ free(g);
+ }
+ td->io_ops->data = NULL;
+ f->engine_data = 0;
+
+ return ret;
+}
+
--- /dev/null
+/*
+ * glusterfs engine
+ *
+ * IO engine using Glusterfs's gfapi async interface
+ *
+ */
+#include "gfapi.h"
+#define NOT_YET 1
+struct fio_gf_iou {
+ struct io_u *io_u;
+ int io_complete;
+};
+static ulong cb_count = 0, issued = 0;
+
+static struct io_u *fio_gf_event(struct thread_data *td, int event)
+{
+ struct gf_data *gf_data = td->io_ops->data;
+ dprint(FD_IO, "%s\n", __FUNCTION__);
+ return gf_data->aio_events[event];
+}
+
+static int fio_gf_getevents(struct thread_data *td, unsigned int min,
+ unsigned int max, struct timespec *t)
+{
+ struct gf_data *g = td->io_ops->data;
+ unsigned int events = 0;
+ struct io_u *io_u;
+ int i = 0;
+ struct fio_gf_iou *io = NULL;
+
+ dprint(FD_IO, "%s\n", __FUNCTION__);
+ do {
+ io_u_qiter(&td->io_u_all, io_u, i) {
+ if (!(io_u->flags & IO_U_F_FLIGHT))
+ continue;
+
+ io = (struct fio_gf_iou *)io_u->engine_data;
+
+ if (io && io->io_complete) {
+ io->io_complete = 0;
+ g->aio_events[events] = io_u;
+ events++;
+
+ if (events >= max)
+ break;
+ }
+
+ }
+ if (events < min)
+ usleep(100);
+ else
+ break;
+
+ } while (1);
+
+ return events;
+}
+
+static void fio_gf_io_u_free(struct thread_data *td, struct io_u *io_u)
+{
+ struct fio_gf_iou *io = io_u->engine_data;
+
+ if (io) {
+ if (io->io_complete){
+ log_err("incomplete IO found.\n");
+ }
+ io_u->engine_data = NULL;
+ free(io);
+ }
+ fprintf(stderr, "issued %lu finished %lu\n", issued, cb_count);
+}
+
+static int fio_gf_io_u_init(struct thread_data *td, struct io_u *io_u)
+{
+ struct fio_gf_iou *io = NULL;
+
+ dprint(FD_FILE, "%s\n", __FUNCTION__);
+
+ if (!io_u->engine_data){
+ io = malloc(sizeof(struct fio_gf_iou));
+ if (!io){
+ td_verror(td, errno, "malloc");
+ return 1;
+ }
+ io->io_complete = 0;
+ io->io_u = io_u;
+ io_u->engine_data = io;
+ }
+ return 0;
+}
+
+static void gf_async_cb(glfs_fd_t *fd, ssize_t ret, void *data)
+{
+ struct io_u *io_u = (struct io_u *)data;
+ struct fio_gf_iou *iou =
+ (struct fio_gf_iou *)io_u->engine_data;
+
+ dprint(FD_IO, "%s ret %lu\n", __FUNCTION__, ret);
+ iou->io_complete = 1;
+ cb_count ++;
+}
+
+static int fio_gf_async_queue(struct thread_data fio_unused *td, struct io_u *io_u)
+{
+ struct gf_data *g = td->io_ops->data;
+ int r = 0;
+
+ dprint(FD_IO, "%s op %s\n", __FUNCTION__,
+ io_u->ddir == DDIR_READ? "read": io_u->ddir == DDIR_WRITE? "write":io_u->ddir == DDIR_SYNC? "sync":"unknown");
+
+ fio_ro_check(td, io_u);
+
+ if (io_u->ddir == DDIR_READ)
+ r = glfs_pread_async(g->fd, io_u->xfer_buf, io_u->xfer_buflen, io_u->offset,
+ 0, gf_async_cb, (void *)io_u);
+ else if (io_u->ddir == DDIR_WRITE)
+ r = glfs_pwrite_async(g->fd, io_u->xfer_buf, io_u->xfer_buflen, io_u->offset,
+ 0, gf_async_cb, (void *)io_u);
+ else if (io_u->ddir == DDIR_SYNC) {
+ r = glfs_fsync_async(g->fd, gf_async_cb, (void *)io_u);
+ }else {
+ log_err("unsupported operation.\n");
+ io_u->error = -EINVAL;
+ goto failed;
+ }
+ if (r){
+ log_err("glfs failed.\n");
+ io_u->error = r;
+ goto failed;
+ }
+ issued ++;
+ return FIO_Q_QUEUED;
+
+failed:
+ io_u->error = r;
+ td_verror(td, io_u->error, "xfer");
+ return FIO_Q_COMPLETED;
+}
+
+int fio_gf_async_setup(struct thread_data *td)
+{
+ int r = 0;
+ struct gf_data *g = NULL;
+#if defined(NOT_YET)
+ fprintf(stderr, "the async interface is still very experimental...\n");
+#endif
+ r = fio_gf_setup(td);
+ if (r){
+ return r;
+ }
+ td->o.use_thread = 1;
+ g = td->io_ops->data;
+ g->aio_events = malloc(td->o.iodepth * sizeof(struct io_u *));
+ if (!g->aio_events){
+ r = -ENOMEM;
+ fio_gf_cleanup(td);
+ return r;
+ }
+
+ memset(g->aio_events, 0, td->o.iodepth * sizeof(struct io_u *));
+
+ return r;
+
+}
+
+static int fio_gf_async_prep(struct thread_data *td, struct io_u *io_u)
+{
+ dprint(FD_FILE, "%s\n", __FUNCTION__);
+
+ if (!ddir_rw(io_u->ddir))
+ return 0;
+
+ return 0;
+}
+
+static struct ioengine_ops ioengine = {
+ .name = "gfapi_async",
+ .version = FIO_IOOPS_VERSION,
+ .init = fio_gf_async_setup,
+ .cleanup = fio_gf_cleanup,
+ .prep = fio_gf_async_prep,
+ .queue = fio_gf_async_queue,
+ .open_file = fio_gf_open_file,
+ .close_file = fio_gf_close_file,
+ .get_file_size = fio_gf_get_file_size,
+ .getevents = fio_gf_getevents,
+ .event = fio_gf_event,
+ .io_u_init = fio_gf_io_u_init,
+ .io_u_free = fio_gf_io_u_free,
+ .options = gfapi_options,
+ .option_struct_size = sizeof(struct gf_options),
+ .flags = FIO_DISKLESSIO,
+};
+
+static void fio_init fio_gf_register(void)
+{
+ register_ioengine(&ioengine);
+}
+
+static void fio_exit fio_gf_unregister(void)
+{
+ unregister_ioengine(&ioengine);
+}
--- /dev/null
+/*
+ * glusterfs engine
+ *
+ * IO engine using Glusterfs's gfapi sync interface
+ *
+ */
+
+#include "gfapi.h"
+
+#define LAST_POS(f) ((f)->engine_data)
+static int fio_gf_prep(struct thread_data *td, struct io_u *io_u)
+{
+ struct fio_file *f = io_u->file;
+ struct gf_data *g = td->io_ops->data;
+
+ dprint(FD_FILE, "fio prep\n");
+
+ if (!ddir_rw(io_u->ddir))
+ return 0;
+
+ if (LAST_POS(f) != -1ULL && LAST_POS(f) == io_u->offset)
+ return 0;
+
+ if (glfs_lseek(g->fd, io_u->offset, SEEK_SET) < 0) {
+ td_verror(td, errno, "lseek");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int fio_gf_queue(struct thread_data *td, struct io_u *io_u)
+{
+ struct gf_data *g = td->io_ops->data;
+ int ret = 0;
+
+ dprint(FD_FILE, "fio queue len %lu\n", io_u->xfer_buflen);
+ fio_ro_check(td, io_u);
+
+ if (io_u->ddir == DDIR_READ)
+ ret = glfs_read(g->fd, io_u->xfer_buf, io_u->xfer_buflen, 0);
+ else if (io_u->ddir == DDIR_WRITE)
+ ret = glfs_write(g->fd, io_u->xfer_buf, io_u->xfer_buflen, 0);
+ else {
+ log_err("unsupported operation.\n");
+ return -EINVAL;
+ }
+ dprint(FD_FILE, "fio len %lu ret %d\n", io_u->xfer_buflen, ret);
+ if (io_u->file && ret >= 0 && ddir_rw(io_u->ddir))
+ LAST_POS(io_u->file) = io_u->offset + ret;
+
+ if (ret != (int) io_u->xfer_buflen) {
+ if (ret >= 0) {
+ io_u->resid = io_u->xfer_buflen - ret;
+ io_u->error = 0;
+ return FIO_Q_COMPLETED;
+ } else
+ io_u->error = errno;
+ }
+
+ if (io_u->error){
+ log_err("IO failed.\n");
+ td_verror(td, io_u->error, "xfer");
+ }
+
+ return FIO_Q_COMPLETED;
+
+}
+
+static struct ioengine_ops ioengine = {
+ .name = "gfapi",
+ .version = FIO_IOOPS_VERSION,
+ .init = fio_gf_setup,
+ .cleanup = fio_gf_cleanup,
+ .prep = fio_gf_prep,
+ .queue = fio_gf_queue,
+ .open_file = fio_gf_open_file,
+ .close_file = fio_gf_close_file,
+ .get_file_size = fio_gf_get_file_size,
+ .options = gfapi_options,
+ .option_struct_size = sizeof(struct gf_options),
+ .flags = FIO_SYNCIO | FIO_DISKLESSIO,
+};
+
+static void fio_init fio_gf_register(void)
+{
+ register_ioengine(&ioengine);
+}
+
+static void fio_exit fio_gf_unregister(void)
+{
+ unregister_ioengine(&ioengine);
+}
struct sockaddr_un *soun = &nd->addr_un;
soun->sun_family = AF_UNIX;
- strcpy(soun->sun_path, path);
+ memset(soun->sun_path, 0, sizeof(soun->sun_path));
+ strncpy(soun->sun_path, path, sizeof(soun->sun_path) - 1);
return 0;
}
memset(addr, 0, sizeof(*addr));
addr->sun_family = AF_UNIX;
- strcpy(addr->sun_path, path);
+ strncpy(addr->sun_path, path, sizeof(addr->sun_path) - 1);
unlink(path);
len = sizeof(addr->sun_family) + strlen(path) + 1;
struct netio_options *o = td->eo;
struct ip_mreq mr;
struct sockaddr_in sin;
- struct sockaddr_in6 sin6;
struct sockaddr *saddr;
int fd, opt, type, domain;
socklen_t len;
memset(&sin, 0, sizeof(sin));
- memset(&sin6, 0, sizeof(sin6));
if (o->proto == FIO_TYPE_TCP) {
type = SOCK_STREAM;
len = sizeof(nd->addr6);
nd->addr6.sin6_family = AF_INET6;
- nd->addr6.sin6_addr = sin6.sin6_addr.s6_addr ? sin6.sin6_addr : in6addr_any;
+ nd->addr6.sin6_addr = in6addr_any;
nd->addr6.sin6_port = htons(port);
}
if (bind(fd, saddr, len) < 0) {
+ close(fd);
td_verror(td, errno, "bind");
return 1;
}
if (!td->files_index) {
add_file(td, td->o.filename ?: "net", 0, 0);
td->o.nr_files = td->o.nr_files ?: 1;
+ td->o.open_files++;
}
if (!td->io_ops->data) {
failed_open:
rados_ioctx_destroy(rbd_data->io_ctx);
+ rbd_data->io_ctx = NULL;
failed_shutdown:
rados_shutdown(rbd_data->cluster);
+ rbd_data->cluster = NULL;
failed_early:
return 1;
}
if (!td->files_index) {
add_file(td, td->o.filename ? : "rbd", 0, 0);
td->o.nr_files = td->o.nr_files ? : 1;
+ td->o.open_files++;
}
f = td->files[0];
f->real_file_size = info.size;
/*
* don't block for min events == 0
*/
- if (!min) {
- sd->fd_flags[i] = fcntl(f->fd, F_GETFL);
- fcntl(f->fd, F_SETFL, sd->fd_flags[i] | O_NONBLOCK);
- }
+ if (!min)
+ sd->fd_flags[i] = fio_set_fd_nonblocking(f->fd, "sg");
+ else
+ sd->fd_flags[i] = -1;
+
sd->pfds[i].fd = f->fd;
sd->pfds[i].events = POLLIN;
}
}
if (!min) {
- for_each_file(td, f, i)
- fcntl(f->fd, F_SETFL, sd->fd_flags[i]);
+ for_each_file(td, f, i) {
+ if (sd->fd_flags[i] == -1)
+ continue;
+
+ if (fcntl(f->fd, F_SETFL, sd->fd_flags[i]) < 0)
+ log_err("fio: sg failed to restore fcntl flags: %s\n", strerror(errno));
+ }
}
return r;
double perc, perc_t;
bytes_done = ddir_rw_sum(td->io_bytes);
- perc = (double) bytes_done / (double) bytes_total;
- if (perc > 1.0)
- perc = 1.0;
+
+ if (bytes_total) {
+ perc = (double) bytes_done / (double) bytes_total;
+ if (perc > 1.0)
+ perc = 1.0;
+ } else
+ perc = 0.0;
if (td->o.time_based) {
- perc_t = (double) elapsed / (double) timeout;
- if (perc_t < perc)
- perc = perc_t;
+ if (timeout) {
+ perc_t = (double) elapsed / (double) timeout;
+ if (perc_t < perc)
+ perc = perc_t;
+ } else {
+ /*
+ * Will never hit, we can't have time_based
+ * without a timeout set.
+ */
+ perc = 0.0;
+ }
}
eta_sec = (unsigned long) (elapsed * (1.0 / perc)) - elapsed;
int r, new_layout = 0, unlink_file = 0, flags;
unsigned long long left;
unsigned int bs;
- char *b;
+ char *b = NULL;
if (read_only) {
log_err("fio: refusing extend of file due to read-only\n");
if (new_layout)
flags |= O_TRUNC;
+#ifdef WIN32
+ flags |= _O_BINARY;
+#endif
+
dprint(FD_FILE, "open file %s, flags %x\n", f->file_name, flags);
f->fd = open(f->file_name, flags, 0644);
if (f->fd < 0) {
err:
close(f->fd);
f->fd = -1;
+ if (b)
+ free(b);
return 1;
}
dprint(FD_IO, "invalidate cache %s: %llu/%llu\n", f->file_name, off,
len);
- /*
- * FIXME: add blockdev flushing too
- */
if (f->mmap_ptr) {
ret = posix_madvise(f->mmap_ptr, f->mmap_sz, POSIX_MADV_DONTNEED);
#ifdef FIO_MADV_FREE
} else if (f->filetype == FIO_TYPE_CHAR || f->filetype == FIO_TYPE_PIPE)
ret = 0;
- if (ret < 0) {
- td_verror(td, errno, "invalidate_cache");
- return 1;
- } else if (ret > 0) {
- td_verror(td, ret, "invalidate_cache");
- return 1;
+ /*
+ * Cache flushing isn't a fatal condition, and we know it will
+ * happen on some platforms where we don't have the proper
+ * function to flush eg block device caches. So just warn and
+ * continue on our way.
+ */
+ if (ret) {
+ log_info("fio: cache invalidation of %s failed: %s\n", f->file_name, strerror(errno));
+ ret = 0;
}
- return ret;
+ return 0;
}
from_hash = 0;
}
+#ifdef WIN32
+ flags |= _O_BINARY;
+#endif
+
f->fd = open(f->file_name, flags, 0600);
return from_hash;
}
} else if (f->filetype != FIO_TYPE_FILE)
continue;
- strcpy(buf, f->file_name);
+ buf[255] = '\0';
+ strncpy(buf, f->file_name, 255);
if (stat(buf, &sb) < 0) {
if (errno != ENOENT)
if (fm)
continue;
- fm = malloc(sizeof(*fm));
- strcpy(fm->__base, buf);
+ fm = calloc(1, sizeof(*fm));
+ strncpy(fm->__base, buf, sizeof(fm->__base) - 1);
fm->base = basename(fm->__base);
fm->key = sb.st_dev;
flist_add(&fm->list, &list);
* Calculate per-file size and potential extra size for the
* first files, if needed.
*/
- if (!o->file_size_low) {
+ if (!o->file_size_low && o->nr_files) {
uint64_t all_fs;
fs = o->size / o->nr_files;
if (!o->size || o->size > total_size)
o->size = total_size;
+ if (o->size < td_min_bs(td)) {
+ log_err("fio: blocksize too large for data set\n");
+ goto err_out;
+ }
+
/*
* See if we need to extend some files
*/
err = __file_invalidate_cache(td, f, old_len,
extend_len);
- close(f->fd);
+
+ /*
+ * Shut up static checker
+ */
+ if (f->fd != -1)
+ close(f->fd);
+
f->fd = -1;
if (err)
break;
* iolog already set the total io size, if we read back
* stored entries.
*/
- if (!o->read_iolog_file)
- td->total_io_size = o->size * o->loops;
+ if (!o->read_iolog_file) {
+ if (o->io_limit)
+ td->total_io_size = o->io_limit * o->loops;
+ else
+ td->total_io_size = o->size * o->loops;
+ }
done:
if (o->create_only)
unsigned long seed;
seed = td->rand_seeds[FIO_RAND_BLOCK_OFF];
-
+
if (!lfsr_init(&f->lfsr, blocks, seed, 0))
continue;
} else if (!td->o.norandommap) {
set_already_allocated(file_name);
- /*
- * For adding files after the fact - if openfiles= isn't
- * given as an option, ensure we allow at least one file open
- */
- if (!td->o.open_files)
- td->o.open_files = 1;
-
if (inc)
td->o.nr_files++;
if (--f->references)
return 0;
- if (should_fsync(td) && td->o.fsync_on_close)
+ if (should_fsync(td) && td->o.fsync_on_close) {
f_ret = fsync(f->fd);
+ if (f_ret < 0)
+ f_ret = errno;
+ }
if (td->io_ops->close_file)
ret = td->io_ops->close_file(td, f);
if (lstat(full_path, &sb) == -1) {
if (errno != ENOENT) {
td_verror(td, errno, "stat");
- return 1;
+ ret = 1;
+ break;
}
}
divided between the available files for the job. If not set, fio will use the
full size of the given files or devices. If the files do not exist, size
must be given. It is also possible to give size as a percentage between 1 and
-100. If size=20% is given, fio will use 20% of the full size of the given files
-or devices.
+100. If size=20% is given, fio will use 20% of the full size of the given
+files or devices.
+.TP
+.BI io_limit \fR=\fPint
+Normally fio operates within the region set by \fBsize\fR, which means that
+the \fBsize\fR option sets both the region and size of IO to be performed.
+Sometimes that is not what you want. With this option, it is possible to
+define just the amount of IO that fio should do. For instance, if \fBsize\fR
+is set to 20G and \fBio_limit\fR is set to 5G, fio will perform IO within
+the first 20G but exit when 5G have been done.
.TP
.BI fill_device \fR=\fPbool "\fR,\fB fill_fs" \fR=\fPbool
Sets size to something really large and waits for ENOSPC (no space left on
IO engine supporting direct access to Ceph Rados Block Devices (RBD) via librbd
without the need to use the kernel rbd driver. This ioengine defines engine specific
options.
+.TP
+.B gfapi
+Using Glusterfs libgfapi sync interface to direct access to Glusterfs volumes without
+having to go through FUSE. This ioengine defines engine specific
+options.
+.TP
+.B gfapi_async
+Using Glusterfs libgfapi async interface to direct access to Glusterfs volumes without
+having to go through FUSE. This ioengine defines engine specific
+options.
.RE
.P
.RE
.BI ioscheduler \fR=\fPstr
Attempt to switch the device hosting the file to the specified I/O scheduler.
.TP
-.BI cpuload \fR=\fPint
-If the job is a CPU cycle-eater, attempt to use the specified percentage of
-CPU cycles.
-.TP
-.BI cpuchunks \fR=\fPint
-If the job is a CPU cycle-eater, split the load into cycles of the
-given time in milliseconds.
-.TP
.BI disk_util \fR=\fPbool
Generate disk utilization statistics if the platform supports it. Default: true.
.TP
.BI (cpu)cpuchunks \fR=\fPint
Split the load into cycles of the given time. In microseconds.
.TP
+.BI (cpu)exit_on_io_done \fR=\fPbool
+Detect when IO threads are done, then exit.
+.TP
.BI (libaio)userspace_reap
Normally, with the libaio engine in use, fio will use
the io_getevents system call to reap newly returned events.
#include "helpers.h"
#include "options.h"
#include "profile.h"
-#include "time.h"
+#include "fio_time.h"
#include "gettime.h"
#include "lib/getopt.h"
#include "lib/rand.h"
TD_F_VER_NONE = 32,
TD_F_PROFILE_OPS = 64,
TD_F_COMPRESS = 128,
+ TD_F_NOIO = 256,
};
enum {
#define __td_verror(td, err, msg, func) \
do { \
- int ____e = (err); \
+ unsigned int ____e = (err); \
if ((td)->error) \
break; \
(td)->error = ____e; \
extern char *num2str(unsigned long, int, int, int, int);
extern int ioengine_load(struct thread_data *);
extern int parse_dryrun(void);
+extern int fio_running_or_pending_io_threads(void);
+extern int fio_set_fd_nonblocking(int, const char *);
extern uintptr_t page_mask;
extern uintptr_t page_size;
return min(td->o.min_bs[DDIR_TRIM], min_bs);
}
-static inline int is_power_of_2(unsigned int val)
+static inline int is_power_of_2(unsigned long val)
{
return (val != 0 && ((val & (val - 1)) == 0));
}
extern const char *fio_get_arch_string(int);
extern const char *fio_get_os_string(int);
+#ifdef FIO_INTERNAL
#define ARRAY_SIZE(x) (sizeof((x)) / (sizeof((x)[0])))
+#endif
enum {
FIO_OUTPUT_TERSE = 0,
--- /dev/null
+#ifndef FIO_TIME_H
+#define FIO_TIME_H
+
+extern uint64_t utime_since(struct timeval *, struct timeval *);
+extern uint64_t utime_since_now(struct timeval *);
+extern uint64_t mtime_since(struct timeval *, struct timeval *);
+extern uint64_t mtime_since_now(struct timeval *);
+extern uint64_t time_since_now(struct timeval *);
+extern uint64_t mtime_since_genesis(void);
+extern uint64_t utime_since_genesis(void);
+extern void usec_spin(unsigned int);
+extern void usec_sleep(struct thread_data *, unsigned long);
+extern void fill_start_time(struct timeval *);
+extern void set_genesis_time(void);
+extern int ramp_time_over(struct thread_data *);
+extern int in_ramp_time(struct thread_data *);
+extern void fio_time_init(void);
+
+#endif
vbox = gtk_hbox_new(TRUE, 3);
gtk_container_add(GTK_CONTAINER(frame), vbox);
entry = new_info_entry_in_frame(vbox, "IOs");
- entry_set_int_value(entry, p->dus.ios[0]);
+ entry_set_int_value(entry, p->dus.s.ios[0]);
entry = new_info_entry_in_frame(vbox, "Merges");
- entry_set_int_value(entry, p->dus.merges[0]);
+ entry_set_int_value(entry, p->dus.s.merges[0]);
entry = new_info_entry_in_frame(vbox, "Sectors");
- entry_set_int_value(entry, p->dus.sectors[0]);
+ entry_set_int_value(entry, p->dus.s.sectors[0]);
entry = new_info_entry_in_frame(vbox, "Ticks");
- entry_set_int_value(entry, p->dus.ticks[0]);
+ entry_set_int_value(entry, p->dus.s.ticks[0]);
frame = gtk_frame_new("Write");
gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
vbox = gtk_hbox_new(TRUE, 3);
gtk_container_add(GTK_CONTAINER(frame), vbox);
entry = new_info_entry_in_frame(vbox, "IOs");
- entry_set_int_value(entry, p->dus.ios[1]);
+ entry_set_int_value(entry, p->dus.s.ios[1]);
entry = new_info_entry_in_frame(vbox, "Merges");
- entry_set_int_value(entry, p->dus.merges[1]);
+ entry_set_int_value(entry, p->dus.s.merges[1]);
entry = new_info_entry_in_frame(vbox, "Sectors");
- entry_set_int_value(entry, p->dus.sectors[1]);
+ entry_set_int_value(entry, p->dus.s.sectors[1]);
entry = new_info_entry_in_frame(vbox, "Ticks");
- entry_set_int_value(entry, p->dus.ticks[1]);
+ entry_set_int_value(entry, p->dus.s.ticks[1]);
frame = gtk_frame_new("Shared");
gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
vbox = gtk_hbox_new(TRUE, 3);
gtk_container_add(GTK_CONTAINER(frame), vbox);
entry = new_info_entry_in_frame(vbox, "IO ticks");
- entry_set_int_value(entry, p->dus.io_ticks);
+ entry_set_int_value(entry, p->dus.s.io_ticks);
entry = new_info_entry_in_frame(vbox, "Time in queue");
- entry_set_int_value(entry, p->dus.time_in_queue);
+ entry_set_int_value(entry, p->dus.s.time_in_queue);
util = 0.0;
- if (p->dus.msec)
- util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
+ if (p->dus.s.msec)
+ util = (double) 100 * p->dus.s.io_ticks / (double) p->dus.s.msec;
if (util > 100.0)
util = 100.0;
#include "hash.h"
#include "os/os.h"
-#ifdef ARCH_HAVE_CPU_CLOCK
+#if defined(ARCH_HAVE_CPU_CLOCK) && !defined(ARCH_CPU_CLOCK_CYCLES_PER_USEC)
static unsigned long cycles_per_usec;
static unsigned long inv_cycles_per_usec;
#endif
} else if (tv)
tv->last_cycles = t;
+#ifdef ARCH_CPU_CLOCK_CYCLES_PER_USEC
+ usecs = t / ARCH_CPU_CLOCK_CYCLES_PER_USEC;
+#else
usecs = (t * inv_cycles_per_usec) / 16777216UL;
+#endif
tp->tv_sec = usecs / 1000000;
tp->tv_usec = usecs % 1000000;
break;
}
}
-#ifdef ARCH_HAVE_CPU_CLOCK
+#if defined(ARCH_HAVE_CPU_CLOCK) && !defined(ARCH_CPU_CLOCK_CYCLES_PER_USEC)
static unsigned long get_cycles_per_usec(void)
{
struct timeval s, e;
#else
static int calibrate_cpu_clock(void)
{
+#ifdef ARCH_CPU_CLOCK_CYCLES_PER_USEC
+ return 0;
+#else
return 1;
-}
#endif
+}
+#endif // ARCH_HAVE_CPU_CLOCK
#ifndef CONFIG_TLS_THREAD
void fio_local_clock_init(int is_thread)
struct clock_thread *threads;
unsigned int nr_cpus = cpus_online();
struct clock_entry *entries;
- unsigned long tentries, failed;
+ unsigned long tentries, failed = 0;
struct clock_entry *prev, *this;
uint32_t seq = 0;
- int i;
+ unsigned int i;
log_info("cs: reliable_tsc: %s\n", tsc_reliable ? "yes" : "no");
pthread_mutex_init(&t->lock, NULL);
pthread_mutex_init(&t->started, NULL);
pthread_mutex_lock(&t->lock);
- pthread_create(&t->thread, NULL, clock_thread_fn, t);
+ if (pthread_create(&t->thread, NULL, clock_thread_fn, t)) {
+ failed++;
+ nr_cpus = i;
+ break;
+ }
}
for (i = 0; i < nr_cpus; i++) {
pthread_mutex_unlock(&t->lock);
}
- for (failed = i = 0; i < nr_cpus; i++) {
+ for (i = 0; i < nr_cpus; i++) {
struct clock_thread *t = &threads[i];
void *ret;
pthread_mutex_lock(&ipt->init_lock);
/* exit if any other thread failed to start */
- if (ipc.status == IDLE_PROF_STATUS_ABORT)
+ if (ipc.status == IDLE_PROF_STATUS_ABORT) {
+ pthread_mutex_unlock(&ipt->init_lock);
return NULL;
+ }
retval = set_cpu_affinity(ipt);
if (retval == -1) {
pthread_mutex_lock(&ipt->start_lock);
/* exit if other threads failed to initialize */
- if (ipc.status == IDLE_PROF_STATUS_ABORT)
+ if (ipc.status == IDLE_PROF_STATUS_ABORT) {
+ pthread_mutex_unlock(&ipt->start_lock);
return NULL;
+ }
/* exit if we are doing calibration only */
- if (ipc.status == IDLE_PROF_STATUS_CALI_STOP)
+ if (ipc.status == IDLE_PROF_STATUS_CALI_STOP) {
+ pthread_mutex_unlock(&ipt->start_lock);
return NULL;
+ }
fio_gettime(&ipt->tps, NULL);
ipt->state = TD_RUNNING;
/* calculate idleness */
if (ipc.cali_mean != 0.0) {
runt = utime_since(&ipt->tps, &ipt->tpe);
- ipt->idleness = ipt->loops * ipc.cali_mean / runt;
+ if (runt)
+ ipt->idleness = ipt->loops * ipc.cali_mean / runt;
+ else
+ ipt->idleness = 0.0;
} else
ipt->idleness = 0.0;
}
static char **ini_file;
static int max_jobs = FIO_MAX_JOBS;
static int dump_cmdline;
-static int def_timeout;
+static long long def_timeout;
static int parse_only;
static struct thread_data def_thread;
return 0;
}
+static void set_cmd_options(struct thread_data *td)
+{
+ struct thread_options *o = &td->o;
+
+ if (!o->timeout)
+ o->timeout = def_timeout;
+}
+
/*
* Return a free job structure.
*/
{
struct thread_data *td;
- if (global)
+ if (global) {
+ set_cmd_options(&def_thread);
return &def_thread;
+ }
if (setup_thread_area()) {
log_err("error: failed to setup shm segment\n");
return NULL;
td->thread_number = thread_number;
- if (!parent || !parent->o.group_reporting)
+ if (!parent->o.group_reporting)
stat_number++;
+ set_cmd_options(td);
return td;
}
{ .keyword = NULL, },
};
-static char *make_filename(char *buf, struct thread_options *o,
+static char *make_filename(char *buf, size_t buf_size,struct thread_options *o,
const char *jobname, int jobnum, int filenum)
{
struct fpre_keyword *f;
char copy[PATH_MAX];
+ size_t dst_left = PATH_MAX - 1;
if (!o->filename_format || !strlen(o->filename_format)) {
sprintf(buf, "%s.%d.%d", jobname, jobnum, filenum);
for (f = &fpre_keywords[0]; f->keyword; f++)
f->strlen = strlen(f->keyword);
- strcpy(buf, o->filename_format);
+ buf[buf_size - 1] = '\0';
+ strncpy(buf, o->filename_format, buf_size - 1);
+
memset(copy, 0, sizeof(copy));
for (f = &fpre_keywords[0]; f->keyword; f++) {
do {
if (pre_len) {
strncpy(dst, buf, pre_len);
dst += pre_len;
+ dst_left -= pre_len;
}
switch (f->key) {
- case FPRE_JOBNAME:
- dst += sprintf(dst, "%s", jobname);
+ case FPRE_JOBNAME: {
+ int ret;
+
+ ret = snprintf(dst, dst_left, "%s", jobname);
+ if (ret < 0)
+ break;
+ dst += ret;
+ dst_left -= ret;
break;
- case FPRE_JOBNUM:
- dst += sprintf(dst, "%d", jobnum);
+ }
+ case FPRE_JOBNUM: {
+ int ret;
+
+ ret = snprintf(dst, dst_left, "%d", jobnum);
+ if (ret < 0)
+ break;
+ dst += ret;
+ dst_left -= ret;
break;
- case FPRE_FILENUM:
- dst += sprintf(dst, "%d", filenum);
+ }
+ case FPRE_FILENUM: {
+ int ret;
+
+ ret = snprintf(dst, dst_left, "%d", filenum);
+ if (ret < 0)
+ break;
+ dst += ret;
+ dst_left -= ret;
break;
+ }
default:
assert(0);
break;
}
if (post_start)
- strcpy(dst, buf + post_start);
+ strncpy(dst, buf + post_start, dst_left);
- strcpy(buf, copy);
+ strncpy(buf, copy, buf_size - 1);
} while (1);
}
add_file(td, jobname, job_add_num, 0);
else {
for (i = 0; i < o->nr_files; i++)
- add_file(td, make_filename(fname, o, jobname, job_add_num, i), job_add_num, 0);
+ add_file(td, make_filename(fname, sizeof(fname), o, jobname, job_add_num, i), job_add_num, 0);
}
}
i++;
}
- for (i = 0; i < num_opts; i++)
- free(opts[i]);
-
free(string);
free(name);
free(opts);
memset(&def_thread, 0, sizeof(def_thread));
fio_getaffinity(getpid(), &def_thread.o.cpumask);
- def_thread.o.timeout = def_timeout;
def_thread.o.error_dump = 1;
+
/*
* fill default options
*/
optind = 1;
while ((c = getopt_long_only(argc, argv, ostr, l_opts, &lidx)) != -1) {
- did_arg = 1;
-
if ((c & FIO_CLIENT_FLAG) || client_flag_set(c)) {
parse_cmd_client(cur_client, argv[optind - 1]);
c &= ~FIO_CLIENT_FLAG;
smalloc_pool_size = atoi(optarg);
break;
case 't':
- def_timeout = atoi(optarg);
+ if (check_str_time(optarg, &def_timeout, 1)) {
+ log_err("fio: failed parsing time %s\n", optarg);
+ do_exit++;
+ exit_val = 1;
+ }
break;
case 'l':
write_lat_log = 1;
write_bw_log = 1;
break;
case 'o':
+ if (f_out)
+ fclose(f_out);
+
f_out = fopen(optarg, "w+");
if (!f_out) {
perror("fopen output");
append_terse_output = 1;
break;
case 'h':
+ did_arg = 1;
if (!cur_client) {
usage(argv[0]);
do_exit++;
}
break;
case 'c':
+ did_arg = 1;
if (!cur_client) {
fio_show_option_help(optarg);
do_exit++;
}
break;
case 'i':
+ did_arg = 1;
if (!cur_client) {
fio_show_ioengine_help(optarg);
do_exit++;
}
break;
case 's':
+ did_arg = 1;
dump_cmdline = 1;
break;
case 'r':
read_only = 1;
break;
case 'v':
+ did_arg = 1;
if (!cur_client) {
log_info("%s\n", fio_version_string);
do_exit++;
do_exit++;
break;
case 'P':
+ did_arg = 1;
parse_only = 1;
break;
case 'x': {
break;
}
case 'p':
+ did_arg = 1;
+ if (exec_profile)
+ free(exec_profile);
exec_profile = strdup(optarg);
break;
case FIO_GETOPT_JOB: {
if (!strncmp(opt, "name", 4) && td) {
ret = add_job(td, td->o.name ?: "fio", 0, 0, client_type);
if (ret)
- return 0;
+ goto out_free;
td = NULL;
+ did_arg = 1;
}
if (!td) {
int is_section = !strncmp(opt, "name", 4);
td = get_new_job(global, &def_thread, 1);
if (!td || ioengine_load(td))
- return 0;
+ goto out_free;
fio_options_set_ioengine_opts(l_opts, td);
}
if (!ret && !strcmp(opt, "ioengine")) {
free_ioengine(td);
if (ioengine_load(td))
- return 0;
+ goto out_free;
fio_options_set_ioengine_opts(l_opts, td);
}
break;
case FIO_GETOPT_IOENGINE: {
const char *opt = l_opts[lidx].name;
char *val = optarg;
+
+ if (!td)
+ break;
+
ret = fio_cmd_ioengine_option_parse(td, opt, val);
break;
}
}
break;
case 'S':
+ did_arg = 1;
if (nr_clients) {
log_err("fio: can't be both client and server\n");
do_exit++;
backend = 1;
break;
case 'D':
+ if (pid_file)
+ free(pid_file);
pid_file = strdup(optarg);
break;
case 'I':
if ((ret = fio_idle_prof_parse_opt(optarg))) {
/* exit on error and calibration only */
+ did_arg = 1;
do_exit++;
- if (ret == -1)
+ if (ret == -1)
exit_val = 1;
}
break;
case 'C':
+ did_arg = 1;
if (is_backend) {
log_err("fio: can't be both client and server\n");
do_exit++;
}
break;
case 'T':
+ did_arg = 1;
do_exit++;
exit_val = fio_monotonic_clocktest();
break;
case 'G':
+ did_arg = 1;
do_exit++;
exit_val = fio_crctest(optarg);
break;
if (is_backend && backend)
return fio_start_server(pid_file);
+ else if (pid_file)
+ free(pid_file);
if (td) {
- if (!ret)
+ if (!ret) {
ret = add_job(td, td->o.name ?: "fio", 0, 0, client_type);
+ if (ret)
+ did_arg = 1;
+ }
}
while (!ret && optind < argc) {
optind++;
}
+out_free:
+ if (pid_file)
+ free(pid_file);
+
return ini_idx;
}
if (job_files > 0) {
for (i = 0; i < job_files; i++) {
- if (fill_def_thread())
+ if (i && fill_def_thread())
return 1;
if (nr_clients) {
if (fio_clients_send_ini(ini_file[i]))
void put_file_log(struct thread_data *td, struct fio_file *f)
{
- int ret = put_file(td, f);
+ unsigned int ret = put_file(td, f);
if (ret)
td_verror(td, ret, "file close");
* Mark IO ok to verify
*/
if (io_u->ipo) {
- io_u->ipo->flags &= ~IP_F_IN_FLIGHT;
- write_barrier();
+ /*
+ * Remove errored entry from the verification list
+ */
+ if (io_u->error)
+ unlog_io_piece(td, io_u);
+ else {
+ io_u->ipo->flags &= ~IP_F_IN_FLIGHT;
+ write_barrier();
+ }
}
td_io_u_unlock(td);
td->error = ret;
}
+ if (!ret && (td->io_ops->flags & FIO_NOIO))
+ td->flags |= TD_F_NOIO;
+
return ret;
}
static void iolog_delay(struct thread_data *td, unsigned long delay)
{
unsigned long usec = utime_since_now(&td->last_issue);
+ unsigned long this_delay;
if (delay < usec)
return;
if (delay < 100)
return;
- usec_sleep(td, delay);
+ while (delay && !td->terminate) {
+ this_delay = delay;
+ if (this_delay > 500000)
+ this_delay = 500000;
+
+ usec_sleep(td, this_delay);
+ delay -= this_delay;
+ }
}
static int ipo_special(struct thread_data *td, struct io_piece *ipo)
td->io_hist_len++;
}
+void unlog_io_piece(struct thread_data *td, struct io_u *io_u)
+{
+ struct io_piece *ipo = io_u->ipo;
+
+ if (!ipo)
+ return;
+
+ if (ipo->flags & IP_F_ONRB)
+ rb_erase(&ipo->rb_node, &td->io_hist_tree);
+ else if (ipo->flags & IP_F_ONLIST)
+ flist_del(&ipo->list);
+
+ free(ipo);
+ io_u->ipo = NULL;
+ td->io_hist_len--;
+}
+
+void trim_io_piece(struct thread_data *td, struct io_u *io_u)
+{
+ struct io_piece *ipo = io_u->ipo;
+
+ if (!ipo)
+ return;
+
+ ipo->len = io_u->xfer_buflen - io_u->resid;
+}
+
void write_iolog_close(struct thread_data *td)
{
fflush(td->iolog_f);
} else {
ipo->offset = offset;
ipo->len = bytes;
- if (bytes > td->o.max_bs[rw])
+ if (rw != DDIR_INVAL && bytes > td->o.max_bs[rw])
td->o.max_bs[rw] = bytes;
ipo->fileno = fileno;
ipo->file_action = file_action;
extern void log_file(struct thread_data *, struct fio_file *, enum file_log_act);
extern int __must_check init_iolog(struct thread_data *td);
extern void log_io_piece(struct thread_data *, struct io_u *);
+extern void unlog_io_piece(struct thread_data *, struct io_u *);
+extern void trim_io_piece(struct thread_data *, struct io_u *);
extern void queue_io_piece(struct thread_data *, struct io_piece *);
extern void prune_io_piece_log(struct thread_data *);
extern void write_iolog_close(struct thread_data *);
#include "../fio.h"
+#define ARRAY_LENGTH(arr) sizeof(arr) / sizeof((arr)[0])
+
/*
* Cheesy number->string conversion, complete with carry rounding error.
*/
if (modulo == -1U) {
done:
+ if (post_index >= ARRAY_LENGTH(postfix))
+ post_index = 0;
+
sprintf(buf, "%lu%s%s", num, postfix[post_index],
byte_postfix[byte_post_index]);
return buf;
#include <signal.h>
#include <stdint.h>
#include <locale.h>
+#include <fcntl.h>
#include "fio.h"
#include "smalloc.h"
}
}
+int fio_running_or_pending_io_threads(void)
+{
+ struct thread_data *td;
+ int i;
+
+ for_each_td(td, i) {
+ if (td->flags & TD_F_NOIO)
+ continue;
+ if (td->runstate < TD_EXITED)
+ return 1;
+ }
+
+ return 0;
+}
+
+int fio_set_fd_nonblocking(int fd, const char *who)
+{
+ int flags;
+
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0)
+ log_err("fio: %s failed to get file flags: %s\n", who, strerror(errno));
+ else {
+ int new_flags = flags | O_NONBLOCK;
+
+ new_flags = fcntl(fd, F_SETFL, new_flags);
+ if (new_flags < 0)
+ log_err("fio: %s failed to get file flags: %s\n", who, strerror(errno));
+ }
+
+ return flags;
+}
+
static int endian_check(void)
{
union {
if (td->orig_buffer == MAP_FAILED) {
td_verror(td, errno, "mmap");
td->orig_buffer = NULL;
- if (td->mmapfd) {
+ if (td->mmapfd != 1) {
close(td->mmapfd);
- unlink(td->o.mmapfile);
+ if (td->o.mmapfile)
+ unlink(td->o.mmapfile);
}
return 1;
#include "arch/arch.h"
#include "os/os.h"
#include "helpers.h"
-#include "time.h"
+#include "fio_time.h"
#include "gettime.h"
void fio_mutex_remove(struct fio_mutex *mutex)
} else {
error[i] = atoi(fname);
if (error[i] < 0)
- error[i] = error[i];
+ error[i] = -error[i];
}
if (!error[i]) {
log_err("Unknown error %s, please use number value \n",
td->o.continue_on_error |= 1 << etype;
td->o.ignore_error_nr[etype] = i;
td->o.ignore_error[etype] = error;
- }
+ } else
+ free(error);
+
return 0;
}
{
struct thread_data *td = data;
struct thread_options *o = &td->o;
- char *nr = get_opt_postfix(str);
+ char *nr;
if (parse_dryrun())
return 0;
o->ddir_seq_nr = 1;
o->ddir_seq_add = 0;
+ nr = get_opt_postfix(str);
if (!nr)
return 0;
static int str_numa_cpunodes_cb(void *data, char *input)
{
struct thread_data *td = data;
+ struct bitmask *verify_bitmask;
if (parse_dryrun())
return 0;
* numa_allocate_nodemask(), so it should be freed by
* numa_free_nodemask().
*/
- td->o.numa_cpunodesmask = numa_parse_nodestring(input);
- if (td->o.numa_cpunodesmask == NULL) {
+ verify_bitmask = numa_parse_nodestring(input);
+ if (verify_bitmask == NULL) {
log_err("fio: numa_parse_nodestring failed\n");
td_verror(td, 1, "str_numa_cpunodes_cb");
return 1;
}
+ numa_free_nodemask(verify_bitmask);
+ td->o.numa_cpunodes = strdup(input);
td->o.numa_cpumask_set = 1;
return 0;
}
{ "default", "prefer", "bind", "interleave", "local", NULL };
int i;
char *nodelist;
+ struct bitmask *verify_bitmask;
if (parse_dryrun())
return 0;
break;
case MPOL_INTERLEAVE:
case MPOL_BIND:
- td->o.numa_memnodesmask = numa_parse_nodestring(nodelist);
- if (td->o.numa_memnodesmask == NULL) {
+ verify_bitmask = numa_parse_nodestring(nodelist);
+ if (verify_bitmask == NULL) {
log_err("fio: numa_parse_nodestring failed\n");
td_verror(td, 1, "str_numa_memnodes_cb");
return 1;
}
+ td->o.numa_memnodes = strdup(nodelist);
+ numa_free_nodemask(verify_bitmask);
+
break;
case MPOL_LOCAL:
case MPOL_DEFAULT:
.help = "fallocate() file based engine",
},
#endif
+#ifdef CONFIG_GFAPI
+ { .ival = "gfapi",
+ .help = "Glusterfs libgfapi(sync) based engine"
+ },
+ { .ival = "gfapi_async",
+ .help = "Glusterfs libgfapi(async) based engine"
+ },
+#endif
+
{ .ival = "external",
.help = "Load external engine (append name)",
},
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_INVALID,
},
+ {
+ .name = "io_limit",
+ .lname = "IO Limit",
+ .type = FIO_OPT_STR_VAL,
+ .off1 = td_var_offset(io_limit),
+ .interval = 1024 * 1024,
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_INVALID,
+ },
{
.name = "fill_device",
.lname = "Fill device",
return NULL;
ret = fread(&buf[tmp - str], 1, 128 - (tmp - str), f);
- if (ret <= 0)
+ if (ret <= 0) {
+ pclose(f);
return NULL;
+ }
pclose(f);
buf[(tmp - str) + ret - 1] = '\0';
__FIO_OPT_G_ACT,
__FIO_OPT_G_LATPROF,
__FIO_OPT_G_RBD,
+ __FIO_OPT_G_GFAPI,
__FIO_OPT_G_NR,
FIO_OPT_G_RATE = (1U << __FIO_OPT_G_RATE),
FIO_OPT_G_ACT = (1U << __FIO_OPT_G_ACT),
FIO_OPT_G_LATPROF = (1U << __FIO_OPT_G_LATPROF),
FIO_OPT_G_RBD = (1U << __FIO_OPT_G_RBD),
+ FIO_OPT_G_GFAPI = (1U << __FIO_OPT_G_GFAPI),
FIO_OPT_G_INVALID = (1U << __FIO_OPT_G_NR),
};
#endif
#endif /* FIO_HAVE_BYTEORDER_FUNCS */
+#ifdef FIO_INTERNAL
#define le16_to_cpu(val) ({ \
__le16_to_cpu(val); \
})
#define le64_to_cpu(val) ({ \
__le64_to_cpu(val); \
})
+#endif
+
#define cpu_to_le16(val) ({ \
__cpu_to_le16(val); \
})
}
#endif
-#ifndef CONFIG_CPU_COUNT
+#ifndef CPU_COUNT
#ifdef FIO_HAVE_CPU_AFFINITY
static inline int CPU_COUNT(os_cpu_mask_t *mask)
{
<Product Id="*"
Codepage="1252" Language="1033"
Manufacturer="fio" Name="fio"
- UpgradeCode="2338A332-5511-43CF-B9BD-5C60496CCFCC" Version="2.1.7">
+ UpgradeCode="2338A332-5511-43CF-B9BD-5C60496CCFCC" Version="2.1.9">
<Package
Description="Flexible IO Tester"
InstallerVersion="301" Keywords="Installer,MSI,Database"
struct sockaddr_un
{
sa_family_t sun_family; /* Address family */
- char sun_path[]; /* Socket pathname */
+ char sun_path[260]; /* Socket pathname */
};
#endif /* SYS_UN_H */
ret = 1;
for (i = 0; i < PARSE_MAX_VP; i++) {
vp = &posval[i];
- if (!vp->ival || vp->ival[0] == '\0')
+ if (!vp->ival || vp->ival[0] == '\0' || !cp)
continue;
all_skipped = 0;
if (!strncmp(vp->ival, ptr, str_match_len(vp, ptr))) {
#include <unistd.h>
#include <limits.h>
#include <errno.h>
-#include <fcntl.h>
#include <sys/poll.h>
#include <sys/types.h>
#include <sys/wait.h>
"DISK_UTIL",
"SERVER_START",
"ADD_JOB",
- "CMD_RUN"
+ "CMD_RUN",
"CMD_IOLOG",
};
*/
struct fio_net_cmd *fio_net_recv_cmd(int sk)
{
- struct fio_net_cmd cmd, *cmdret = NULL;
+ struct fio_net_cmd cmd, *tmp, *cmdret = NULL;
size_t cmd_size = 0, pdu_offset = 0;
uint16_t crc;
int ret, first = 1;
} else
cmd_size += cmd.pdu_len;
- cmdret = realloc(cmdret, cmd_size);
+ if (cmd_size / 1024 > FIO_SERVER_MAX_CMD_MB * 1024) {
+ log_err("fio: cmd+pdu too large (%llu)\n", (unsigned long long) cmd_size);
+ ret = 1;
+ break;
+ }
+
+ tmp = realloc(cmdret, cmd_size);
+ if (!tmp) {
+ log_err("fio: server failed allocating cmd\n");
+ ret = 1;
+ break;
+ }
+ cmdret = tmp;
if (first)
memcpy(cmdret, &cmd, sizeof(cmd));
struct sockaddr_in6 addr6;
socklen_t len = use_ipv6 ? sizeof(addr6) : sizeof(addr);
struct pollfd pfd;
- int ret = 0, sk, flags, exitval = 0;
+ int ret = 0, sk, exitval = 0;
FLIST_HEAD(conn_list);
dprint(FD_NET, "server enter accept loop\n");
- flags = fcntl(listen_sk, F_GETFL);
- flags |= O_NONBLOCK;
- fcntl(listen_sk, F_SETFL, flags);
+ fio_set_fd_nonblocking(listen_sk, "server");
while (!exit_backend) {
const char *from;
{
int i;
- strcpy((char *) dst->name, (char *) src->name);
+ dst->name[FIO_DU_NAME_SZ - 1] = '\0';
+ strncpy((char *) dst->name, (char *) src->name, FIO_DU_NAME_SZ - 1);
for (i = 0; i < 2; i++) {
- dst->ios[i] = cpu_to_le32(src->ios[i]);
- dst->merges[i] = cpu_to_le32(src->merges[i]);
- dst->sectors[i] = cpu_to_le64(src->sectors[i]);
- dst->ticks[i] = cpu_to_le32(src->ticks[i]);
+ dst->s.ios[i] = cpu_to_le32(src->s.ios[i]);
+ dst->s.merges[i] = cpu_to_le32(src->s.merges[i]);
+ dst->s.sectors[i] = cpu_to_le64(src->s.sectors[i]);
+ dst->s.ticks[i] = cpu_to_le32(src->s.ticks[i]);
}
- dst->io_ticks = cpu_to_le32(src->io_ticks);
- dst->time_in_queue = cpu_to_le32(src->time_in_queue);
- dst->msec = cpu_to_le64(src->msec);
+ dst->s.io_ticks = cpu_to_le32(src->s.io_ticks);
+ dst->s.time_in_queue = cpu_to_le32(src->s.time_in_queue);
+ dst->s.msec = cpu_to_le64(src->s.msec);
}
void fio_server_send_du(void)
pdu.nr_samples = __cpu_to_le32(log->nr_samples);
pdu.log_type = cpu_to_le32(log->log_type);
pdu.compressed = cpu_to_le32(use_zlib);
- strcpy((char *) pdu.name, name);
+
+ strncpy((char *) pdu.name, name, FIO_NET_NAME_MAX);
+ pdu.name[FIO_NET_NAME_MAX - 1] = '\0';
for (i = 0; i < log->nr_samples; i++) {
struct io_sample *s = &log->log[i];
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
- strcpy(addr.sun_path, bind_sock);
+ strncpy(addr.sun_path, bind_sock, sizeof(addr.sun_path) - 1);
len = sizeof(addr.sun_family) + strlen(bind_sock) + 1;
if (sk < 0)
return sk;
+ memset(bind_str, 0, sizeof(bind_str));
+
if (!bind_sock) {
char *p, port[16];
const void *src;
if (p)
strcat(p, port);
else
- strcpy(bind_str, port);
+ strncpy(bind_str, port, sizeof(bind_str) - 1);
} else
- strcpy(bind_str, bind_sock);
+ strncpy(bind_str, bind_sock, sizeof(bind_str) - 1);
log_info("fio: server listening on %s\n", bind_str);
if (listen(sk, 0) < 0) {
log_err("fio: listen: %s\n", strerror(errno));
+ close(sk);
return -1;
}
if (check_existing_pidfile(pidfile)) {
log_err("fio: pidfile %s exists and server appears alive\n",
pidfile);
+ free(pidfile);
return -1;
}
} else if (pid) {
int ret = write_pid(pid, pidfile);
+ free(pidfile);
exit(ret);
}
};
enum {
- FIO_SERVER_VER = 33,
+ FIO_SERVER_VER = 34,
FIO_SERVER_MAX_FRAGMENT_PDU = 1024,
+ FIO_SERVER_MAX_CMD_MB = 2048,
FIO_NET_CMD_QUIT = 1,
FIO_NET_CMD_EXIT = 2,
log_info("\n");
/* Additional output if description is set */
- if (ts->description)
+ if (strlen(ts->description))
log_info(";%s", ts->description);
log_info("\n");
if (!td->error && td->o.continue_on_error &&
td->first_error) {
ts->error = td->first_error;
- strcpy(ts->verror, td->verror);
+ ts->verror[sizeof(ts->verror) - 1] = '\0';
+ strncpy(ts->verror, td->verror, sizeof(ts->verror) - 1);
} else if (td->error) {
ts->error = td->error;
- strcpy(ts->verror, td->verror);
+ ts->verror[sizeof(ts->verror) - 1] = '\0';
+ strncpy(ts->verror, td->verror, sizeof(ts->verror) - 1);
}
}
fio_mutex_down(stat_mutex);
if (!pthread_create(&thread, NULL, __show_running_run_stats, NULL)) {
- pthread_detach(thread);
+ int err;
+
+ err = pthread_detach(thread);
+ if (err)
+ log_err("fio: DU thread detach failed: %s\n", strerror(err));
+
return;
}
return 0;
temp_dir = getenv("TMPDIR");
- if (temp_dir == NULL)
+ if (temp_dir == NULL) {
temp_dir = getenv("TEMP");
+ if (temp_dir && strlen(temp_dir) >= PATH_MAX)
+ temp_dir = NULL;
+ }
if (temp_dir == NULL)
temp_dir = "/tmp";
if (!delta)
continue; /* No entries for interval */
- rate = delta * 1000 / spent / 1024;
+ if (spent)
+ rate = delta * 1000 / spent / 1024;
+ else
+ rate = 0;
+
add_stat_sample(&ts->bw_stat[ddir], rate);
if (td->bw_log)
if (!delta)
continue; /* No entries for interval */
- iops = (delta * 1000) / spent;
+ if (spent)
+ iops = (delta * 1000) / spent;
+ else
+ iops = 0;
+
add_stat_sample(&ts->iops_stat[ddir], iops);
if (td->iops_log)
unsigned int iodepth_batch_complete;
unsigned long long size;
+ unsigned long long io_limit;
unsigned int size_percent;
unsigned int fill_device;
unsigned int file_append;
os_cpu_mask_t verify_cpumask;
unsigned int verify_cpumask_set;
unsigned int cpus_allowed_policy;
-#ifdef CONFIG_LIBNUMA
- struct bitmask *numa_cpunodesmask;
+ char *numa_cpunodes;
unsigned int numa_cpumask_set;
unsigned short numa_mem_mode;
unsigned int numa_mem_prefer_node;
- struct bitmask *numa_memnodesmask;
+ char *numa_memnodes;
unsigned int numa_memmask_set;
-#endif
unsigned int iolog;
unsigned int rwmixcycle;
unsigned int rwmix[DDIR_RWDIR_CNT];
uint32_t iodepth_batch_complete;
uint64_t size;
+ uint64_t io_limit;
uint32_t size_percent;
uint32_t fill_device;
uint32_t file_append;
+++ /dev/null
-#ifndef FIO_TIME_H
-#define FIO_TIME_H
-
-extern uint64_t utime_since(struct timeval *, struct timeval *);
-extern uint64_t utime_since_now(struct timeval *);
-extern uint64_t mtime_since(struct timeval *, struct timeval *);
-extern uint64_t mtime_since_now(struct timeval *);
-extern uint64_t time_since_now(struct timeval *);
-extern uint64_t mtime_since_genesis(void);
-extern uint64_t utime_since_genesis(void);
-extern void usec_spin(unsigned int);
-extern void usec_sleep(struct thread_data *, unsigned long);
-extern void fill_start_time(struct timeval *);
-extern void set_genesis_time(void);
-extern int ramp_time_over(struct thread_data *);
-extern int in_ramp_time(struct thread_data *);
-extern void fio_time_init(void);
-
-#endif
unsigned int crc_len;
};
+#define DUMP_BUF_SZ 255
+static int dump_buf_warned;
+
static void dump_buf(char *buf, unsigned int len, unsigned long long offset,
const char *type, struct fio_file *f)
{
- char *ptr, fname[256];
+ char *ptr, fname[DUMP_BUF_SZ];
+ size_t buf_left = DUMP_BUF_SZ;
int ret, fd;
ptr = strdup(f->file_name);
- strcpy(fname, basename(ptr));
- sprintf(fname + strlen(fname), ".%llu.%s", offset, type);
+ fname[DUMP_BUF_SZ - 1] = '\0';
+ strncpy(fname, basename(ptr), DUMP_BUF_SZ - 1);
+
+ buf_left -= strlen(fname);
+ if (buf_left <= 0) {
+ if (!dump_buf_warned) {
+ log_err("fio: verify failure dump buffer too small\n");
+ dump_buf_warned = 1;
+ }
+ free(ptr);
+ return;
+ }
+
+ snprintf(fname + strlen(fname), buf_left, ".%llu.%s", offset, type);
fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY, 0644);
if (fd < 0) {