Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package ioping for openSUSE:Factory checked in at 2022-09-13 15:10:12 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ioping (Old) and /work/SRC/openSUSE:Factory/.ioping.new.2083 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ioping" Tue Sep 13 15:10:12 2022 rev:5 rq:1003304 version:1.3 Changes: -------- --- /work/SRC/openSUSE:Factory/ioping/ioping.changes 2020-03-16 10:19:42.847620859 +0100 +++ /work/SRC/openSUSE:Factory/.ioping.new.2083/ioping.changes 2022-09-13 15:11:34.805012708 +0200 @@ -1,0 +2,28 @@ +Tue Sep 13 09:27:12 UTC 2022 - Dirk M??ller <dmuel...@suse.com> + +- update to 1.3: + * Workaround for glibc 2.36 sys/mount.h + * ioping: add RWF_HIPRI + * ioping: add "make static" + * ioping: print notice "slow"/"fast" when twice slower/faster than average + * ioping: cleanup timing notice + * ioping: fix mingw build + * ioping: add option -burst + * ioping: include stdbool + * ioping: restore CPPFLAGS in makefile + * ioping: use 64-bit printf format + * ioping: switch mingw build to 64-bit and ucrt + * ioping: retrive device size and name for windows + * ioping: use F_FULLFSYNC for OSX + * ioping: move fdatasync after write out of make_request() + * ioping: implement O_SYNC for windows by FILE_FLAG_WRITE_THROUGH + * ioping: fallback from non-cached to direct only for reads + * ioping: rename global target fd variable + * ioping: add option -e, -entropy for deterministic randomization + * ioping: add support nowait I/O (RWF_NOWAIT) + * ioping: print help and version into stdout + * ioping: add option -I|-time to print current time + * ioping: describe json format in manpage + * ioping: add human-readable localtime into json output + +------------------------------------------------------------------- Old: ---- ioping-1.2.tar.gz New: ---- ioping-1.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ioping.spec ++++++ --- /var/tmp/diff_new_pack.hHemps/_old 2022-09-13 15:11:35.157013698 +0200 +++ /var/tmp/diff_new_pack.hHemps/_new 2022-09-13 15:11:35.165013721 +0200 @@ -1,7 +1,7 @@ # # spec file for package ioping # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: ioping -Version: 1.2 +Version: 1.3 Release: 0 Summary: A tool to monitor I/O latency in real time License: GPL-3.0-or-later ++++++ ioping-1.2.tar.gz -> ioping-1.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ioping-1.2/Makefile new/ioping-1.3/Makefile --- old/ioping-1.2/Makefile 2020-02-02 14:37:55.000000000 +0100 +++ new/ioping-1.3/Makefile 2022-08-24 10:30:48.000000000 +0200 @@ -1,7 +1,5 @@ -CFLAGS ?= -g -O2 -funroll-loops -ftree-vectorize -CFLAGS += -std=gnu99 -Wall -Wextra -pedantic -LIBS=-lm -lrt -PREFIX=/usr/local +prefix=/usr/local +PREFIX=$(prefix) BINDIR=$(PREFIX)/bin MAN1DIR=$(PREFIX)/share/man/man1 @@ -22,25 +20,41 @@ DISTDIR=$(PACKAGE)-$(VERSION) DISTFILES=$(SRCS) $(MANS) $(DOCS) $(SPEC) Makefile PACKFILES=$(BINARY) $(MANS) $(MANS_F) $(DOCS) -CPPFLAGS+=-DEXTRA_VERSION=\"${EXTRA_VERSION}\" -STRIP=strip -TARGET=$(shell ${CC} -dumpmachine) +CFLAGS ?= -g -O2 -funroll-loops -ftree-vectorize +CFLAGS += -std=gnu99 -Wall -Wextra -pedantic +CPPFLAGS = -DEXTRA_VERSION=\"${EXTRA_VERSION}\" + +ifneq (,$(STATIC)) +CFLAGS += -static +endif + +LIBS = -lm -lrt + +MINGW = x86_64-w64-mingw32- +MINGW_CFLAGS = -specs=ucrt-spec -Wno-format +MINGW_LIBS = -lm + +CC = $(CROSS_COMPILE)gcc +STRIP = $(CROSS_COMPILE)strip +TARGET = $(shell ${CC} -dumpmachine) ifneq (,$(findstring -apple-,${TARGET})) LIBS=-lm endif -ifdef MINGW -CC=i686-w64-mingw32-gcc -STRIP=i686-w64-mingw32-strip -TARGET=win32 -BINARY:=$(BINARY:=.exe) -LIBS=-lm +ifneq (,$(findstring -mingw,${TARGET})) +BINARY := $(BINARY:=.exe) +CFLAGS += ${MINGW_CFLAGS} +LIBS := ${MINGW_LIBS} +${BINARY}: ucrt-spec endif all: checkver $(BINARY) +static: + $(MAKE) STATIC=1 + version: checkver @echo ${VERSION} @@ -79,7 +93,13 @@ MANWIDTH=80 man ./$< | col -b > $@ $(BINARY): $(SRCS) - $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(LIBS) + $(CC) -o $@ $(SRCS) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(LIBS) + +ucrt-spec: + ${MINGW}gcc -dumpspecs | sed 's/-lmsvcrt/-lucrt/' > $@ + +mingw: + $(MAKE) CROSS_COMPILE=${MINGW} dist: checkver $(DISTFILES) tar -cz --transform='s,^,$(DISTDIR)/,S' $^ -f $(DISTDIR).tar.gz diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ioping-1.2/changelog new/ioping-1.3/changelog --- old/ioping-1.2/changelog 2020-02-02 14:37:55.000000000 +0100 +++ new/ioping-1.3/changelog 2022-08-24 10:30:48.000000000 +0200 @@ -1,4 +1,31 @@ +v1.3 / 2022-08-24 +================== + + * Workaround for glibc 2.36 sys/mount.h + * ioping: add RWF_HIPRI + * ioping: add "make static" + * ioping: print notice "slow"/"fast" when twice slower/faster than average + * ioping: cleanup timing notice + * ioping: fix mingw build + * ioping: add option -burst + * ioping: include stdbool + * ioping: restore CPPFLAGS in makefile + * ioping: use 64-bit printf format + * ioping: switch mingw build to 64-bit and ucrt + * ioping: retrive device size and name for windows + * ioping: use F_FULLFSYNC for OSX + * ioping: move fdatasync after write out of make_request() + * ioping: implement O_SYNC for windows by FILE_FLAG_WRITE_THROUGH + * ioping: fallback from non-cached to direct only for reads + * ioping: rename global target fd variable + * ioping: add option -e, -entropy for deterministic randomization + * ioping: add support nowait I/O (RWF_NOWAIT) + * ioping: print help and version into stdout + * ioping: add option -I|-time to print current time + * ioping: describe json format in manpage + * ioping: add human-readable localtime into json output + v1.2 / 2020-02-02 ================== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ioping-1.2/ioping.1 new/ioping-1.3/ioping.1 --- old/ioping-1.2/ioping.1 2020-02-02 14:37:55.000000000 +0100 +++ new/ioping-1.3/ioping.1 2022-08-24 10:30:48.000000000 +0200 @@ -3,9 +3,11 @@ ioping \- simple disk I/O latency monitoring tool .SH SYNOPSYS .SY ioping -.OP \-ABCDJLRWGYykq +.OP \-ABCDJLNRWGYykq .OP \-a count +.OP \-b count .OP \-c count +.OP \-e seed .OP \-i interval .OP \-l speed .OP \-r rate @@ -17,6 +19,7 @@ .OP \-w deadline .OP \-p period .OP \-P period +.OP \-I [format] .IR directory | file | device .br .SY ioping @@ -31,10 +34,18 @@ .TP \fB\-a\fR, \fB\-warmup\fR \fIcount\fR Ignore in statistics first \fIcount\fR requests, default 1. +First request usually is much slower due to power-saving features. +.TP +\fB\-b\fR, \fB\-burst\fR \fIcount\fR +Make series of \fIcount\fR requests without delay, default \fB0\fR. +Aggressive power-saving features slow down requests even after short delay. .TP \fB\-c\fR, \fB\-count\fR \fIcount\fR Stop after \fIcount\fR requests, default \fB0\fR (infinite). .TP +\fB\-e\fR, \fB\-entropy\fR \fIseed\fR +Set seed for random number generator, default \fB0\fR (random). +.TP \fB\-i\fR, \fB\-interval\fR \fItime\fR Set \fItime\fR between requests, default \fB1s\fR. .TP @@ -83,6 +94,11 @@ \fB\-D\fR, \fB\-direct\fR Use direct I/O (see \fBO_DIRECT\fR in \fBopen\fR(2)). .TP +\fB\-I\fR, \fB\-time\fR \fI[format]\fR +Print current time for each request. +Optional argument defines time format in \fBstrftime\fR(3) notation, +default is "%b %d %T" (Jan 01 00:00:00). +.TP \fB\-J\fR, \fB\-json\fR Print output in JSON format. .TP @@ -90,6 +106,10 @@ Use sequential operations rather than random. This also sets default request size to \fB256k\fR (as in \fB-size 256k\fR). .TP +\fB\-N\fR, \fB\-nowait\fR +Set RWF_NOWAIT on I/O, indicating to the kernel to do not wait if request +cannot be executed immediately. (see \fBRWF_NOWAIT\fR in \fBpreadv2\fR(2)) +.TP \fB\-R\fR, \fB\-rapid\fR Disk seek rate test, or bandwidth test if used together with \fB-linear\fR. @@ -230,6 +250,64 @@ (9) total requests (including warmup, too slow or too fast) .br (10) total running time (nanoseconds) + +.SH JSON OUTPUT +With option -J|--json ioping prints json array of objects: +.br +\fB[\fR +.br +\fB...\fR +.br +{ + // timestamps + "timestamp": (unix time in seconds as float), + "localtime": (local time ISO 8601), + + // io target + "target": { + "path": (target path), + "fstype": (filesystem name), + "device": (device name), + "device_size": (device size in bytes) + }, + + // io request + "io": { + "request": (request index), + "operation": (request type: "read" | "write"), + "size": (request size in bytes), + "time": (io time in ns), + "ignored": (ignored in statistics: true | false) + }, + + // statistics + "stat": { + "count": (nr reqeusts), + "size": (total io size in bytes), + "time": (total io time in ns), + "iops": (avg iops), + "bps": (avg rate), + "min": (min io time in ns), + "avg": (avg io time in ns), + "max": (max io time in ns), + "mdev": (standard deviation in ns) + }, + + // load statistics + "load": { + "count": (nr requests), + "size": (total io size in bytes), + "time": (total real time in ns), + "iops": (avg iops), + "bps": (avg rate) + }, +.br +}, +.br +\fB...\fR +.br +\fB]\fR + .SH EXAMPLES .TP .B ioping . @@ -248,6 +326,9 @@ .TP .B ioping -RLB . | awk '{print $4}' Get disk sequential speed in bytes per second. +.TP +.B ioping -J . | jq -r --stream 'fromstream(1|truncate_stream(inputs)) | [.localtime, .io.time/1000000] | @tsv' +Select localtime and io time in milliseconds from json outout. .SH SEE ALSO .BR iostat (1), .BR dd (1), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ioping-1.2/ioping.c new/ioping-1.3/ioping.c --- old/ioping-1.2/ioping.c 2020-02-02 14:37:55.000000000 +0100 +++ new/ioping-1.3/ioping.c 2022-08-24 10:30:48.000000000 +0200 @@ -19,7 +19,7 @@ */ #ifndef VERSION -# define VERSION "1.2" +# define VERSION "1.3" #endif #ifndef EXTRA_VERSION @@ -29,9 +29,15 @@ #define _GNU_SOURCE #define _FILE_OFFSET_BITS 64 +#ifdef __MINGW32__ +# define _WIN32_WINNT 0x0600 +# define _POSIX_C_SOURCE 200809L +#endif + #include <stdio.h> #include <stdlib.h> #include <stdarg.h> +#include <stdbool.h> #include <getopt.h> #include <string.h> #include <unistd.h> @@ -49,8 +55,8 @@ #ifdef __linux__ # include <sys/ioctl.h> -# include <sys/mount.h> # include <sys/sysmacros.h> +# include <sys/syscall.h> # define HAVE_CLOCK_GETTIME # define HAVE_POSIX_FADVICE # define HAVE_POSIX_MEMALIGN @@ -60,7 +66,39 @@ # define HAVE_ERR_INCLUDE # define HAVE_STATVFS # define MAX_RW_COUNT 0x7ffff000 /* 2G - 4K */ -#endif + +# undef RWF_NOWAIT +# include <linux/fs.h> +# include <linux/aio_abi.h> +# ifndef RWF_NOWAIT +# define aio_rw_flags aio_reserved1 +# endif + +# undef RWF_NOWAIT +# include <sys/uio.h> +# ifdef RWF_NOWAIT +# define HAVE_LINUX_PREADV2 +# endif + +# ifndef RWF_NOWAIT +# define RWF_NOWAIT 0x00000008 +# endif + +# ifndef RWF_HIPRI +# define RWF_HIPRI 0x00000001 +# endif + +#else /* __linux__ */ + +# ifndef RWF_NOWAIT +# define RWF_NOWAIT 0 +# endif + +# ifndef RWF_HIPRI +# define RWF_HIPRI 0 +# endif + +#endif /* __linux__ */ #ifdef __gnu_hurd__ # include <sys/ioctl.h> @@ -108,6 +146,7 @@ # include <sys/mount.h> # include <sys/disk.h> # include <sys/uio.h> +# define HAVE_FULLFSYNC # define HAVE_NOCACHE_IO # define HAVE_ERR_INCLUDE # define HAVE_STATVFS @@ -127,6 +166,7 @@ # include <stdarg.h> # include <windows.h> # define HAVE_DIRECT_IO +# define HAVE_SYNC_IO # define HAVE_MKOSTEMP /* not required */ #endif @@ -134,6 +174,14 @@ # define HAVE_POSIX_FDATASYNC #endif +#ifdef O_SYNC +# define HAVE_SYNC_IO +#endif + +#ifdef O_DSYNC +# define HAVE_DATA_SYNC_IO +#endif + #ifdef HAVE_STATVFS # include <sys/statvfs.h> #endif @@ -181,6 +229,11 @@ #define NSEC_PER_SEC 1000000000ll #define USEC_PER_SEC 1000000L +int timestamp_uptodate; +char timestamp_str[64]; +char localtime_str[64]; +const char *localtime_fmt = "%b %d %T"; + #ifdef HAVE_CLOCK_GETTIME static inline long long now(void) @@ -193,14 +246,24 @@ return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; } -static inline double timestamp(void) +static inline void update_timestamp(void) { struct timespec ts; + struct tm tm; + + if (timestamp_uptodate) + return; + + timestamp_uptodate = 1; if (clock_gettime(CLOCK_REALTIME, &ts)) err(3, "clock_gettime failed"); - return ts.tv_sec + (double)ts.tv_nsec / NSEC_PER_SEC; + snprintf(timestamp_str, sizeof(timestamp_str), "%f", + ts.tv_sec + (double)ts.tv_nsec / NSEC_PER_SEC); + + localtime_r(&ts.tv_sec, &tm); + strftime(localtime_str, sizeof(localtime_str), localtime_fmt, &tm); } #else @@ -215,14 +278,25 @@ return tv.tv_sec * NSEC_PER_SEC + tv.tv_usec * 1000ll; } -static inline double timestamp(void) +static inline void update_timestamp(void) { struct timeval tv; + struct tm tm; + + if (timestamp_uptodate) + return; + + timestamp_uptodate = 1; if (gettimeofday(&tv, NULL)) err(3, "gettimeofday failed"); - return tv.tv_sec + (double)tv.tv_usec / USEC_PER_SEC; + snprintf(timestamp_str, sizeof(timestamp_str), "%f", + tv.tv_sec + (double)tv.tv_usec / USEC_PER_SEC); + + time_t t = tv.tv_sec; /* windows bug */ + localtime_r(&t, &tm); + strftime(localtime_str, sizeof(localtime_str), localtime_fmt, &tm); } #endif /* HAVE_CLOCK_GETTIME */ @@ -291,16 +365,6 @@ return FlushFileBuffers(h) ? 0 : -1; } -void srandom(unsigned int seed) -{ - srand(seed); -} - -long int random(void) -{ - return rand() * (RAND_MAX + 1) + rand(); -} - int nanosleep(const struct timespec *req, struct timespec *rem) { (void)rem; @@ -323,16 +387,9 @@ } #endif -#ifndef HAVE_POSIX_FDATASYNC -int fdatasync(int fd) -{ - return fsync(fd); -} -#endif - void version(void) { - fprintf(stderr, "ioping %s\n", VERSION EXTRA_VERSION); + fprintf(stdout, "ioping %s\n", VERSION EXTRA_VERSION); } struct suffix { @@ -480,19 +537,25 @@ char *device = ""; long long device_size = 0; -int fd; +int target_fd = -1; void *buf; +const char *notice = NULL; + int quiet = 0; +int time_info = 0; int batch_mode = 0; int direct = 0; int cached = 0; +int rw_flags = 0; int syncio = 0; int data_syncio = 0; int randomize = 1; int write_test = 0; int write_read_test = 0; +unsigned long long random_entropy = 0; + long long period_request = 0; long long period_time = 0; @@ -518,6 +581,8 @@ long long request = 0; long long warmup_request = 1; +long long burst = 0; +long long burst_request = 0; long long stop_at_request = 0; int json = 0; @@ -525,7 +590,7 @@ int exiting = 0; -const char *options = "hvkALRDCWGYBqyi:t:T:w:s:S:c:o:p:P:l:r:a:J"; +const char *options = "hvkALRDNHCWGYBqyi:t:T:w:s:S:c:o:p:P:l:r:a:I::Je:b:"; #ifdef HAVE_GETOPT_LONG_ONLY @@ -537,12 +602,15 @@ {"quiet", no_argument, NULL, 'q'}, {"batch", no_argument, NULL, 'B'}, + {"time", optional_argument, NULL, 'I'}, {"json", no_argument, NULL, 'J'}, {"rapid", no_argument, NULL, 'R'}, {"linear", no_argument, NULL, 'L'}, {"direct", no_argument, NULL, 'D'}, {"cached", no_argument, NULL, 'C'}, + {"nowait", no_argument, NULL, 'N'}, + {"hipri", no_argument, NULL, 'H'}, {"sync", no_argument, NULL, 'Y'}, {"dsync", no_argument, NULL, 'y'}, {"async", no_argument, NULL, 'A'}, @@ -557,6 +625,7 @@ {"work-time", required_argument, NULL, 'w'}, {"interval", required_argument, NULL, 'i'}, + {"burst", required_argument, NULL, 'b'}, {"speed-limit", required_argument, NULL, 'l'}, {"rate-limit", required_argument, NULL, 'r'}, @@ -567,16 +636,17 @@ {"print-count", required_argument, NULL, 'p'}, {"print-interval", required_argument, NULL, 'P'}, + {"entropy", required_argument, NULL, 'e'}, + {0, 0, NULL, 0}, }; #endif /* HAVE_GETOPT_LONG */ -void usage(void) +void usage(FILE *output) { - fprintf(stderr, - " Usage: ioping [-ABCDRLWYykq] [-c count] [-i interval] [-s size] [-S wsize]\n" - " [-o offset] [-w deadline] [-pP period] directory|file|device\n" + fprintf(output, + " Usage: ioping [options...] directory|file|device\n" " ioping -h | -v\n" "\n" " options:\n" @@ -585,6 +655,8 @@ " -D, -direct use direct I/O (O_DIRECT)\n" " -G, -read-write read-write ping-pong mode\n" " -L, -linear use sequential operations\n" + " -N, -nowait use nowait I/O (RWF_NOWAIT)\n" + " -H, -hipri use high priority I/O (RWF_HIPRI)\n" " -W, -write use write I/O (please read manpage)\n" " -Y, -sync use sync I/O (O_SYNC)\n" " -y, -dsync use data sync I/O (O_DSYNC)\n" @@ -593,7 +665,9 @@ "\n" " parameters:\n" " -a, -warmup <count> ignore <count> first requests (1)\n" + " -b, -burst <count> make <count> requsts without delay (0)\n" " -c, -count <count> stop after <count> requests\n" + " -e, -entropy <seed> seed for random number generator (0)\n" " -i, -interval <time> interval between requests (1s)\n" " -s, -size <size> request size (4k)\n" " -S, -work-size <size> working set size (1m)\n" @@ -601,14 +675,15 @@ " -w, -work-time <time> stop after <time> passed\n" " -l, -speed-limit <size> limit speed with <size> per second\n" " -r, -rate-limit <count> limit rate with <count> per second\n" - "\n" - " output:\n" - " -p, -print-count <count> print raw statistics for every <count> requests\n" - " -P, -print-interval <time> print raw statistics for every <time>\n" " -t, -min-time <time> minimal valid request time (0us)\n" " -T, -max-time <time> maximum valid request time\n" + "\n" + " output:\n" " -B, -batch print final statistics in raw format\n" + " -I, -time [format] print current time for every request\n" " -J, -json print output in JSON format\n" + " -p, -print-count <count> print statistics for every <count> requests\n" + " -P, -print-interval <time> print statistics for every <time>\n" " -q, -quiet suppress human-readable output\n" " -h, -help display this message and exit\n" " -v, -version display version and exit\n" @@ -621,7 +696,7 @@ int opt; if (argc < 2) { - usage(); + usage(stdout); exit(1); } @@ -635,7 +710,7 @@ switch (opt) { case 'h': - usage(); + usage(stdout); exit(0); case 'v': version(); @@ -644,6 +719,9 @@ randomize = 0; default_size = 1<<18; break; + case 'e': + random_entropy = strtoull(optarg, NULL, 0); + break; case 'l': if (!custom_interval) interval = 0; @@ -668,6 +746,12 @@ case 'C': cached = 1; break; + case 'N': + rw_flags |= RWF_NOWAIT; + break; + case 'H': + rw_flags |= RWF_HIPRI; + break; case 'A': async = 1; break; @@ -720,8 +804,14 @@ quiet = 1; batch_mode = 1; break; + case 'I': + time_info = 1; + if (optarg) + localtime_fmt = optarg; + break; case 'J': json = 1; + localtime_fmt = "%FT%T%z"; break; case 'c': stop_at_request = parse_int(optarg); @@ -729,11 +819,15 @@ case 'a': warmup_request = parse_int(optarg); break; + case 'b': + burst = parse_int(optarg); + break; case 'k': keep_file = 1; break; case '?': - usage(); + fprintf(stderr, "\n"); + usage(stderr); exit(1); } } @@ -751,11 +845,14 @@ { char *buf = NULL, *ptr; unsigned major, minor; - struct stat st; + struct statvfs vfs; size_t len; FILE *file; char *real; + if (!fstatvfs(target_fd, &vfs)) + device_size = (long long)vfs.f_frsize * vfs.f_blocks; + /* since v2.6.26 */ file = fopen("/proc/self/mountinfo", "r"); if (!file) @@ -775,6 +872,8 @@ if (!file) return; while (getline(&buf, &len, file) > 0) { + struct stat st; + ptr = buf; strsep(&ptr, " "); if (*buf != '/' || stat(buf, &st) || st.st_rdev != dev) @@ -807,12 +906,44 @@ fstype = strdup(fs.f_fstypename); device = strdup(fs.f_mntfromname); + device_size = (long long)fs.f_bsize * fs.f_blocks; +} + +#elif defined(__MINGW32__) + +void parse_device(dev_t dev) +{ + HANDLE h = (HANDLE)_get_osfhandle(target_fd); + ULARGE_INTEGER total; + DWORD flags; + wchar_t wname[MAX_PATH + 1]; + wchar_t wtype[MAX_PATH + 1]; + + (void)dev; + + if (GetDiskFreeSpaceExA(path, NULL ,&total, NULL)) + device_size = total.QuadPart; + + if (GetVolumeInformationByHandleW(h, wname, MAX_PATH, + NULL, NULL, &flags, + wtype, MAX_PATH)) { + size_t len; + + len = wcstombs(NULL, wname, 0) + 1; + device = malloc(len); + wcstombs(device, wname, len); + + len = wcstombs(NULL, wtype, 0) + 1; + fstype = malloc(len); + wcstombs(fstype, wtype, len); + } } #else void parse_device(dev_t dev) { +# warning no method to get filesystem name, device and size (void)dev; } @@ -869,26 +1000,61 @@ ssize_t do_pwrite(int fd, void *buf, size_t nbytes, off_t offset) { - ssize_t ret; + return pwrite(fd, buf, nbytes, offset); +} - ret = pwrite(fd, buf, nbytes, offset); - if (ret < 0) - return ret; +void sync_file(int fd) +{ +#ifdef HAVE_FULLFSYNC + static bool have_fullfsync = true; - if (!cached && fdatasync(fd) < 0) - err(3, "fdatasync failed, please retry with option -C"); + if (have_fullfsync) { + if (fcntl(fd, F_FULLFSYNC, 0) < 0) { + warn("fcntl(F_FULLFSYNC) failed, fallback to fsync"); + have_fullfsync = false; + } else + return; + } +#endif - return ret; +#ifdef HAVE_POSIX_FDATASYNC + if (fdatasync(fd) < 0) + err(3, "fdatasync failed, please retry with option -C"); +#else + if (fsync(fd) < 0) + err(3, "fsync failed, please retry with option -C"); +#endif } ssize_t (*make_pread) (int fd, void *buf, size_t nbytes, off_t offset) = pread; ssize_t (*make_pwrite) (int fd, void *buf, size_t nbytes, off_t offset) = do_pwrite; ssize_t (*make_request) (int fd, void *buf, size_t nbytes, off_t offset) = pread; -#ifdef HAVE_LINUX_ASYNC_IO +#ifdef HAVE_LINUX_PREADV2 -#include <sys/syscall.h> -#include <linux/aio_abi.h> +ssize_t do_preadv2(int fd, void *buf, size_t nbytes, off_t offset) +{ + struct iovec iov = { + .iov_base = buf, + .iov_len = nbytes, + }; + + return preadv2(fd, &iov, 1, offset, rw_flags); +} + +ssize_t do_pwritev2(int fd, void *buf, size_t nbytes, off_t offset) +{ + struct iovec iov = { + .iov_base = buf, + .iov_len = nbytes, + }; + + return pwritev2(fd, &iov, 1, offset, rw_flags); +} + +#endif /* HAVE_LINUX_PREADV2 */ + +#ifdef HAVE_LINUX_ASYNC_IO static long io_setup(unsigned nr_reqs, aio_context_t *ctx) { return syscall(__NR_io_setup, nr_reqs, ctx); @@ -923,9 +1089,10 @@ { aio_cb.aio_lio_opcode = IOCB_CMD_PREAD; aio_cb.aio_fildes = fd; - aio_cb.aio_buf = (unsigned long) buf; + aio_cb.aio_buf = (intptr_t)buf; aio_cb.aio_nbytes = nbytes; aio_cb.aio_offset = offset; + aio_cb.aio_rw_flags = rw_flags; if (io_submit(aio_ctx, 1, &aio_cbp) != 1) err(1, "aio submit failed"); @@ -945,9 +1112,10 @@ { aio_cb.aio_lio_opcode = IOCB_CMD_PWRITE; aio_cb.aio_fildes = fd; - aio_cb.aio_buf = (unsigned long) buf; + aio_cb.aio_buf = (intptr_t)buf; aio_cb.aio_nbytes = nbytes; aio_cb.aio_offset = offset; + aio_cb.aio_rw_flags = rw_flags; if (io_submit(aio_ctx, 1, &aio_cbp) != 1) err(1, "aio submit failed"); @@ -960,22 +1128,7 @@ return -1; } - if (!cached && fdatasync(fd) < 0) - err(3, "fdatasync failed, please retry with option -C"); - return aio_ev.res; - -#if 0 - aio_cb.aio_lio_opcode = IOCB_CMD_FDSYNC; - if (io_submit(aio_ctx, 1, &aio_cbp) != 1) - err(1, "aio fdsync submit failed"); - - if (io_getevents(aio_ctx, 1, 1, &aio_ev, NULL) != 1) - err(1, "aio getevents failed"); - - if (aio_ev.res < 0) - return aio_ev.res; -#endif } static void aio_setup(void) @@ -990,7 +1143,7 @@ make_pwrite = aio_pwrite; } -#else +#else /* HAVE_LINUX_ASYNC_IO */ static void aio_setup(void) { @@ -999,7 +1152,7 @@ #endif } -#endif +#endif /* HAVE_LINUX_ASYNC_IO */ #ifdef __MINGW32__ @@ -1028,7 +1181,9 @@ } if (direct) - attr |= FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH; + attr |= FILE_FLAG_NO_BUFFERING; + if (syncio) + attr |= FILE_FLAG_WRITE_THROUGH; if (randomize) attr |= FILE_FLAG_RANDOM_ACCESS; else @@ -1045,7 +1200,8 @@ if (h == INVALID_HANDLE_VALUE) return -1; - return _open_osfhandle((long)h, 0); + + return _open_osfhandle((intptr_t)h, 0); } BOOL WINAPI sig_exit(DWORD type) @@ -1161,11 +1317,23 @@ return random_state[1] + s0; } +/* splitmix64 */ +static unsigned long long random64_seed(void) +{ + unsigned long long result = random_entropy; + + random_entropy = result + 0x9E3779B97f4A7C15; + result = (result ^ (result >> 30)) * 0xBF58476D1CE4E5B9; + result = (result ^ (result >> 27)) * 0x94D049BB133111EB; + return result ^ (result >> 31); +} + static void random_init(void) { - srandom(now()); - random_state[0] = random(); - random_state[1] = random(); + if (!random_entropy) + random_entropy = now(); + random_state[0] = random64_seed(); + random_state[1] = random64_seed(); (void)random64(); (void)random64(); } @@ -1203,10 +1371,12 @@ static int add_statistics(struct statistics *s, long long val) { s->count++; if (request <= warmup_request) { - /* warmup */ + notice = "warmup"; } else if (val < min_valid_time) { + notice = "too fast"; s->too_fast++; } else if (val > max_valid_time) { + notice = "too slow"; s->too_slow++; } else { s->valid++; @@ -1216,6 +1386,16 @@ s->min = val; if (val > s->max) s->max = val; + + notice = NULL; + if (s->valid > 5) { + long long avg = s->sum / s->valid; + if (val * 2 < avg) + notice = "fast"; + else if (val > avg * 2) + notice = "slow"; + } + return 1; } @@ -1262,97 +1442,100 @@ } static void dump_statistics(struct statistics *s) { - printf("%lu %.0f %.0f %.0f %lu %.0f %lu %.0f %lu %lu\n", - (unsigned long)s->valid, s->sum, - s->iops, s->speed, - (unsigned long)s->min, s->avg, - (unsigned long)s->max, s->mdev, - (unsigned long)s->count, - (unsigned long)s->load_time); + printf("%llu %.0f %.0f %.0f %llu %.0f %llu %.0f %llu %llu\n", + s->valid, s->sum, s->iops, s->speed, + s->min, s->avg, s->max, s->mdev, + s->count, s->load_time); } -static void json_request(size_t io_size, long long io_time, int valid) +static void json_request(long long io_size, long long io_time, int valid) { + update_timestamp(); + printf("%s{\n" - " \"timestamp\": %f,\n" + " \"timestamp\": %s,\n" + " \"localtime\": \"%s\",\n" " \"target\": {\n" " \"path\": \"%s\",\n" " \"fstype\": \"%s\",\n" " \"device\": \"%s\",\n" - " \"device_size\": %ld\n" + " \"device_size\": %lld\n" " },\n" " \"io\": {\n" - " \"request\": %ld,\n" + " \"request\": %lld,\n" " \"operation\": \"%s\",\n" - " \"size\": %ld,\n" - " \"time\": %lu,\n" + " \"size\": %lld,\n" + " \"time\": %llu,\n" " \"ignored\": %s\n" " }\n" "}", json_line++ ? "," : "", - timestamp(), + timestamp_str, + localtime_str, path, fstype, device, - (long)device_size, - (long)request, + device_size, + request, write_test ? "write" : "read", - (unsigned long)io_size, - (unsigned long)io_time, + io_size, + io_time, valid ? "false" : "true"); - fflush(stdout); } static void json_statistics(struct statistics *s) { + update_timestamp(); + printf("%s{\n" - " \"timestamp\": %f,\n" + " \"timestamp\": %s,\n" + " \"localtime\": \"%s\",\n" " \"target\": {\n" " \"path\": \"%s\",\n" " \"fstype\": \"%s\",\n" " \"device\": \"%s\",\n" - " \"device_size\": %ld\n" + " \"device_size\": %lld\n" " },\n" " \"stat\": {\n" - " \"count\": %lu,\n" - " \"size\": %lu,\n" + " \"count\": %llu,\n" + " \"size\": %llu,\n" " \"time\": %.0f,\n" " \"iops\": %f,\n" " \"bps\": %.0f,\n" - " \"min\": %lu,\n" + " \"min\": %llu,\n" " \"avg\": %.0f,\n" - " \"max\": %lu,\n" + " \"max\": %llu,\n" " \"mdev\": %.0f\n" " },\n" " \"load\": {\n" - " \"count\": %lu,\n" - " \"size\": %lu,\n" - " \"time\": %lu,\n" + " \"count\": %llu,\n" + " \"size\": %llu,\n" + " \"time\": %llu,\n" " \"iops\": %f,\n" " \"bps\": %.0f\n" " }\n" "}", json_line++ ? "," : "", - timestamp(), + timestamp_str, + localtime_str, path, fstype, device, - (long)device_size, - (unsigned long)s->valid, - (unsigned long)s->size, + device_size, + s->valid, + s->size, s->sum, s->iops, s->speed, - (unsigned long)s->min, + s->min, s->avg, - (unsigned long)s->max, + s->max, s->mdev, - (unsigned long)s->count, - (unsigned long)s->load_size, - (unsigned long)s->load_time, + s->count, + s->load_size, + s->load_time, s->load_iops, s->load_speed); - fflush(stdout); } int main (int argc, char **argv) @@ -1369,8 +1552,7 @@ parse_options(argc, argv); - if (!json) - setvbuf(stdout, NULL, _IOLBF, 0); + setvbuf(stdout, NULL, _IOFBF, BUFSIZ); if (!size) size = default_size; @@ -1404,22 +1586,33 @@ temp_wsize = size; #if !defined(HAVE_POSIX_FADVICE) && !defined(HAVE_NOCACHE_IO) + if (!cached && !write_test) { + cached = 1; # if defined(HAVE_DIRECT_IO) - if (!direct && !cached) { - warnx("non-cached I/O not supported, will use direct I/O"); - direct = cached = 1; - } + if (!direct) { + warnx("non-cached I/O not supported, will use direct I/O"); + direct = 1; + } # else - if (!cached && !write_test) { warnx("non-cached I/O not supported by this platform"); warnx("you can use write I/O to get reliable results"); - cached = 1; - } # endif + } #endif - if (async) + if (async) { aio_setup(); + } else if (rw_flags) { +#ifdef HAVE_LINUX_PREADV2 + make_pread = do_preadv2; + make_pwrite = do_pwritev2; +#else + warnx("nowait/hipri I/O is not supported"); +#endif + } + + if ((rw_flags & RWF_NOWAIT) && !cached && !direct) + warnx("nowait without cached or direct I/O is supposed to fail"); make_request = write_test ? make_pwrite : make_pread; @@ -1428,12 +1621,12 @@ errx(1, "direct I/O not supported by this platform"); #endif -#ifndef O_SYNC +#ifndef HAVE_SYNC_IO if (syncio) errx(1, "sync I/O not supported by this platform"); #endif -#ifndef O_DSYNC +#ifndef HAVE_DATA_SYNC_IO if (data_syncio) errx(1, "data sync I/O not supported by this platform"); #endif @@ -1447,13 +1640,12 @@ if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode)) { if (S_ISDIR(st.st_mode)) st.st_size = offset + temp_wsize; - parse_device(st.st_dev); } else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) { - fd = open_file(path, NULL); - if (fd < 0) + target_fd = open_file(path, NULL); + if (target_fd < 0) err(2, "failed to open \"%s\"", path); - if (get_device_size(fd, &st)) { + if (get_device_size(target_fd, &st)) { if (!S_ISCHR(st.st_mode)) err(2, "block get size ioctl failed"); st.st_size = offset + temp_wsize; @@ -1486,11 +1678,11 @@ random_memory(buf, size); if (S_ISDIR(st.st_mode)) { - fd = open_file(path, "ioping.tmp"); - if (fd < 0) + target_fd = open_file(path, "ioping.tmp"); + if (target_fd < 0) err(2, "failed to create temporary file at \"%s\"", path); if (keep_file) { - if (fstat(fd, &st)) + if (fstat(target_fd, &st)) err(2, "fstat at \"%s\" failed", path); if (st.st_size >= offset + wsize) #ifndef __MINGW32__ @@ -1504,32 +1696,26 @@ ret_size = wsize - woffset; if (woffset) random_memory(buf, ret_size); - ret_size = pwrite(fd, buf, ret_size, offset + woffset); + ret_size = pwrite(target_fd, buf, ret_size, offset + woffset); if (ret_size <= 0) err(2, "preparation write failed"); } skip_preparation: - if (fsync(fd)) + if (fsync(target_fd)) err(2, "fsync failed"); } else if (S_ISREG(st.st_mode)) { - fd = open_file(path, NULL); - if (fd < 0) + target_fd = open_file(path, NULL); + if (target_fd < 0) err(2, "failed to open \"%s\"", path); } -#ifdef HAVE_STATVFS - if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode)) { - struct statvfs vfs; - - if (!fstatvfs(fd, &vfs)) - device_size = (long long)vfs.f_frsize * vfs.f_blocks; - } -#endif + if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode)) + parse_device(st.st_dev); /* No readahead for non-cached I/O, we'll invalidate it anyway */ if (randomize || !cached) { #ifdef HAVE_POSIX_FADVICE - ret = posix_fadvise(fd, offset, wsize, POSIX_FADV_RANDOM); + ret = posix_fadvise(target_fd, offset, wsize, POSIX_FADV_RANDOM); if (ret) warn("fadvise(RANDOM) failed, " "operations might perform unneeded readahead"); @@ -1538,7 +1724,7 @@ if (!cached) { #ifdef HAVE_NOCACHE_IO - ret = fcntl(fd, F_NOCACHE, 1); + ret = fcntl(target_fd, F_NOCACHE, 1); if (ret) err(2, "fcntl(F_NOCACHE) failed, " "please retry with option -C"); @@ -1572,7 +1758,7 @@ #ifdef HAVE_POSIX_FADVICE if (!cached) { - ret = posix_fadvise(fd, offset + woffset, size, + ret = posix_fadvise(target_fd, offset + woffset, size, POSIX_FADV_DONTNEED); if (ret) err(3, "fadvise(DONTNEED) failed, " @@ -1590,51 +1776,59 @@ this_time = now(); - ret_size = make_request(fd, buf, size, offset + woffset); + ret_size = make_request(target_fd, buf, size, offset + woffset); if (ret_size < 0) { - if (errno != EINTR) + if ((rw_flags & RWF_NOWAIT) && errno == EAGAIN) { + notice = "EAGAIN"; + ret_size = 0; + } else if (errno != EINTR) err(3, "request failed"); - } else if (ret_size < size) - warnx("request returned less than expected: %zu", ret_size); - else if (ret_size > size) - errx(3, "request returned more than expected: %zu", ret_size); + } else { + if (ret_size < size) + warnx("request returned less than expected: %zu", ret_size); + else if (ret_size > size) + errx(3, "request returned more than expected: %zu", ret_size); + + if (write_test && !cached) + sync_file(target_fd); + } time_now = now(); - time_next += interval; + if (!burst || ++burst_request == burst) { + burst_request = 0; + time_next += interval; + } + if ((time_now - time_next) > 0) time_next = time_now; this_time = time_now - this_time; - valid = add_statistics(&part, this_time); + timestamp_uptodate = 0; + + valid = ret_size ? add_statistics(&part, this_time) : false; if (quiet) { /* silence */ } else if (json) { json_request(ret_size, this_time, valid); } else { + if (time_info) { + update_timestamp(); + printf("%s ", localtime_str); + } print_size(ret_size); printf(" %s %s (%s %s ", write_test ? ">>>" : "<<<", path, fstype, device); print_size(device_size); - printf("): request=%lu time=", (long unsigned)request); + printf("): request=%llu time=", request); print_time(this_time); - if (request <= warmup_request) { - printf(" (warmup)"); - } else if (this_time < min_valid_time) { - printf(" (too fast)"); - } else if (this_time > max_valid_time) { - printf(" (too slow)"); - } else if (part.valid > 5 && part.min < part.max) { - int percent = (this_time - part.min) * 100 / - (part.max - part.min); - if (percent < 5) - printf(" (fast)"); - else if (percent > 95) - printf(" (slow)"); - } + if (notice) + printf(" (%s)", notice); + if (burst && !burst_request) + printf("\n"); printf("\n"); } @@ -1645,6 +1839,7 @@ json_statistics(&part); else dump_statistics(&part); + fflush(stdout); merge_statistics(&total, &part); start_statistics(&part, time_now); period_deadline = time_now + period_time; @@ -1670,6 +1865,10 @@ interval_ts.tv_sec = delta / NSEC_PER_SEC; interval_ts.tv_nsec = delta % NSEC_PER_SEC; + + if (!quiet) + fflush(stdout); + nanosleep(&interval_ts, NULL); } }