RE: [PATCH v2] fio: Append "filecreate/filestat/filedelete" to 'readwrite=' field

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



How about Patch v2?

I plan to create more patches to correct IOPS, BW fields in log. With several steps of "not-big" modification, makes FIO fit for file operation measurement.

-----Original Message-----
From: Su, Friendy 
Sent: Friday, December 2, 2022 11:29 AM
To: 'fio' <fio@xxxxxxxxxxxxxxx>
Cc: Aoyama, Wataru (SGC) <Wataru.Aoyama@xxxxxxxx>; Su, Friendy <Friendy.Su@xxxxxxxx>
Subject: RE: [PATCH v2] fio: Append "filecreate/filestat/filedelete" to 'readwrite=' field

Diff to v1:

1). Add option check into source code, 'readwrite' field and 'ioengine' field must be paired if anyone is set to file operation string. User must set 'ioengine=filecreate' and 'rw=filecreate' both, otherwise fio exit with warning message.

2). Add explanation into document.

-----Original Message-----
From: Su, Friendy 
Sent: Friday, December 2, 2022 11:25 AM
To: fio <fio@xxxxxxxxxxxxxxx>
Cc: Su, Friendy <Friendy.Su@xxxxxxxx>; Aoyama, Wataru (SGC) <Wataru.Aoyama@xxxxxxxx>
Subject: [PATCH v2] fio: Append "filecreate/filestat/filedelete" to 'readwrite=' field

Specify exact file operation strings for 'readwrite' field in job file,
Show correct file operation strings in log.

For file operation measurement, 'rw' field is set to 'read' in job file.
The corresponding 'rw' in log is also 'read'. This makes user confused
since file operation and IO read are totally different things.
In this commit, add 3 new file operations to 'rw' field.

For implementation, TD_DDIR_FILECREATE/TD_DDIR_FILESTAT/TD_DDIR_FILEDELETE
are added. Meanwhile, DDIR_FILE_OP is added to identify the file
operation and IO operation. DDIR_FILE_OP is same value as
DDIR_READ, then clat_stat[DDIR_FILE_READ] can be 'borrowed' for file
operation statistics. Just set DDIR_FILE_OP_MASK when get io_ddir_name().
This avoid big code change if add a new index to enum fio_ddir.

With this commit, user can explicitly specify the exact file operation
in job file 'rw' field. 'rw' and 'ioengine' must set to same value if
anyone is file operation string. 'rw' and 'ddir' string in log are also
file operation's.

Example:

$ fio   filestat-ioengine.fio
t0: (g=0): rw=filestat, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=filestat, iodepth=1
t1: (g=0): rw=filestat, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=filestat, iodepth=1
fio-3.33
Starting 2 threads

t0: (groupid=0, jobs=1): err= 0: pid=4744: Thu Nov 10 18:24:00 2022
  file_operation: IOPS=20.0k, BW=78.1MiB/s (81.9MB/s)(80.0KiB/1msec)
    clat (nsec): min=12645, max=29394, avg=13751.35, stdev=3690.55
    clat percentiles (nsec):
     |  1.00th=[12608],  5.00th=[12608], 10.00th=[12736], 20.00th=[12736],
     | 30.00th=[12736], 40.00th=[12736], 50.00th=[12864], 60.00th=[12992],
     | 70.00th=[12992], 80.00th=[13120], 90.00th=[13120], 95.00th=[13760],
     | 99.00th=[29312], 99.50th=[29312], 99.90th=[29312], 99.95th=[29312],
     | 99.99th=[29312]

Signed-off-by: friendy-su <friendy.su@xxxxxxxx>
---
 HOWTO.rst                        | 23 +++++++++++++++-------
 backend.c                        |  3 +++
 engines/fileoperations.c         |  7 +++----
 examples/filecreate-ioengine.fio |  1 +
 examples/filedelete-ioengine.fio |  1 +
 examples/filestat-ioengine.fio   |  1 +
 fio.1                            | 24 ++++++++++++++++-------
 fio.h                            |  1 +
 init.c                           | 33 ++++++++++++++++++++++++++++++++
 io_ddir.h                        | 26 +++++++++++++++++++++++--
 io_u.c                           |  7 ++++---
 iolog.c                          |  2 +-
 options.c                        | 12 ++++++++++++
 stat.c                           | 10 ++++++----
 stat.h                           |  1 +
 15 files changed, 124 insertions(+), 28 deletions(-)

diff --git a/HOWTO.rst b/HOWTO.rst
index e796f961..30c49eb8 100644
--- a/HOWTO.rst
+++ b/HOWTO.rst
@@ -1137,6 +1137,12 @@ I/O type
 		**randtrimwrite**
 				Like trimwrite, but uses random offsets rather
 				than sequential writes.
+		**filecreate**
+				Create a batch of files. Must also set ``ioengine=filecreate``.
+		**filestat**
+				Stat a batch of files. Must also set ``ioengine=filestat``.
+		**filedelete**
+				Delete a batch of files. Must also set ``ioengine=filedelete``.
 
 	Fio defaults to read if the option is not specified.  For the mixed I/O
 	types, the default is to split them 50/50.  For certain types of I/O the
@@ -2124,18 +2130,21 @@ I/O engine
 			details of writing an external I/O engine.
 
 		**filecreate**
-			Simply create the files and do no I/O to them.  You still need to
-			set  `filesize` so that all the accounting still occurs, but no
-			actual I/O will be done other than creating the file.
+			Simply create the files and do no I/O to them. Must also set
+			``readwrite=filecreate``. You still need to set  `filesize` so
+			that all the accounting still occurs, but no actual I/O will
+			be done other than creating the file.
 
 		**filestat**
-			Simply do stat() and do no I/O to the file. You need to set 'filesize'
-			and 'nrfiles', so that files will be created.
+			Simply do stat() and do no I/O to the file. Must also set
+			``readwrite=filestat``. You need to set `filesize` and `nrfiles`,
+			so that files will be created.
 			This engine is to measure file lookup and meta data access.
 
 		**filedelete**
-			Simply delete the files by unlink() and do no I/O to them. You need to set 'filesize'
-			and 'nrfiles', so that the files will be created.
+			Simply delete the files by unlink() and do no I/O to them.
+			Must also set ``readwrite=filedelete``. You need to set `filesize`
+			and `nrfiles`, so that the files will be created.
 			This engine is to measure file delete.
 
 		**libpmem**
diff --git a/backend.c b/backend.c
index ba954a6b..7024003a 100644
--- a/backend.c
+++ b/backend.c
@@ -1924,6 +1924,9 @@ static void *thread_main(void *data)
 			update_runtime(td, elapsed_us, DDIR_WRITE);
 		if (td_trim(td) && td->io_bytes[DDIR_TRIM])
 			update_runtime(td, elapsed_us, DDIR_TRIM);
+		if (td_fileoperate(td) && td->io_bytes[DDIR_FILE_OP])
+			update_runtime(td, elapsed_us, DDIR_FILE_OP);
+
 		fio_gettime(&td->start, NULL);
 		fio_sem_up(stat_sem);
 
diff --git a/engines/fileoperations.c b/engines/fileoperations.c
index 1db60da1..3b44d429 100644
--- a/engines/fileoperations.c
+++ b/engines/fileoperations.c
@@ -245,12 +245,11 @@ static int init(struct thread_data *td)
 
 	data = calloc(1, sizeof(*data));
 
-	if (td_read(td))
-		data->stat_ddir = DDIR_READ;
-	else if (td_write(td))
-		data->stat_ddir = DDIR_WRITE;
+	if (td_fileoperate(td))
+		data->stat_ddir = DDIR_FILE_OP;
 
 	td->io_ops_data = data;
+	td->file_op_flag = DDIR_FILE_OP_MASK;
 	return 0;
 }
 
diff --git a/examples/filecreate-ioengine.fio b/examples/filecreate-ioengine.fio
index ec7caad5..86226b19 100644
--- a/examples/filecreate-ioengine.fio
+++ b/examples/filecreate-ioengine.fio
@@ -10,6 +10,7 @@
 # IO will not really be done and the write latency numbers will only reflect the
 # open times.
 [global]
+rw=filecreate
 create_on_open=1
 nrfiles=31250
 ioengine=filecreate
diff --git a/examples/filedelete-ioengine.fio b/examples/filedelete-ioengine.fio
index 3c0028f9..6fa2b6a7 100644
--- a/examples/filedelete-ioengine.fio
+++ b/examples/filedelete-ioengine.fio
@@ -5,6 +5,7 @@
 # 'unlink' is better set to 0, since the file is deleted in measurement.
 # the options disabled completion latency output such as 'disable_clat' and 'gtod_reduce' must not set.
 [global]
+rw=filedelete
 ioengine=filedelete
 filesize=4k
 nrfiles=200
diff --git a/examples/filestat-ioengine.fio b/examples/filestat-ioengine.fio
index 932fced8..d225795b 100644
--- a/examples/filestat-ioengine.fio
+++ b/examples/filestat-ioengine.fio
@@ -4,6 +4,7 @@
 # 'filesize' must be set, then files will be created at setup stage.
 
 [global]
+rw=filestat
 ioengine=filestat
 numjobs=1
 filesize=4k
diff --git a/fio.1 b/fio.1
index 9e33c9e1..d2a65a5c 100644
--- a/fio.1
+++ b/fio.1
@@ -909,6 +909,16 @@ other Fio options limiting the total bytes or number of I/O's.
 Like
 .B trimwrite ,
 but uses random offsets rather than sequential writes.
+.TP
+.B filecreate
+Create a batch of files. Must also set `ioengine=filecreate`.
+.TP
+.B filestat
+Stat a batch of files. Must also set `ioengine=filestat`.
+.TP
+.B filedelete
+Delete a batch of files. Must also set `ioengine=filedelete`.
+.TP
 .RE
 .P
 Fio defaults to read if the option is not specified. For the mixed I/O
@@ -1925,18 +1935,18 @@ absolute or relative. See `engines/skeleton_external.c' in the fio source for
 details of writing an external I/O engine.
 .TP
 .B filecreate
-Simply create the files and do no I/O to them.  You still need to set
-\fBfilesize\fR so that all the accounting still occurs, but no actual I/O will be
-done other than creating the file.
+Simply create the files and do no I/O to them. Must also set `readwrite=filecreate`.
+You still need to set \fBfilesize\fR so that all the accounting still occurs,
+but no actual I/O will be done other than creating the file.
 .TP
 .B filestat
-Simply do stat() and do no I/O to the file. You need to set 'filesize'
-and 'nrfiles', so that files will be created.
+Simply do stat() and do no I/O to the file. Must also set `readwrite=filestat`.
+You need to set \fBfilesize\fR and \fBnrfiles\fR, so that files will be created.
 This engine is to measure file lookup and meta data access.
 .TP
 .B filedelete
-Simply delete files by unlink() and do no I/O to the file. You need to set 'filesize'
-and 'nrfiles', so that files will be created.
+Simply delete files by unlink() and do no I/O to the file. Must also set `readwrite=filedelete`.
+You need to set \fBfilesize\fR and \fBnrfiles\fR, so that files will be created.
 This engine is to measure file delete.
 .TP
 .B libpmem
diff --git a/fio.h b/fio.h
index 8da77640..6e6ed780 100644
--- a/fio.h
+++ b/fio.h
@@ -191,6 +191,7 @@ struct thread_data {
 	unsigned int thread_number;
 	unsigned int subjob_number;
 	unsigned int groupid;
+	uint32_t file_op_flag;
 	struct thread_stat ts __attribute__ ((aligned(8)));
 
 	int client_type;
diff --git a/init.c b/init.c
index f6a8056a..ac316f85 100644
--- a/init.c
+++ b/init.c
@@ -603,6 +603,30 @@ static unsigned int gcd(unsigned int m, unsigned int n)
 	return gcd(n, m % n);
 }
 
+static int is_engine(struct thread_data *td, const char *engine)
+{
+	return !strcmp(td->o.ioengine, engine);
+}
+
+/*
+ * Check whether td->o.td_ddir and td->o.ioengine are paired
+ * td_ddir and ioengine must set to same if anyone is file
+ * operation.
+ */
+static int is_file_operation_options_correct(struct thread_data *td)
+{
+	if (((td_filecreate(td) && !is_engine(td, "filecreate")) ||
+	     (!td_filecreate(td) && is_engine(td, "filecreate"))) ||
+		 ((td_filestat(td) && !is_engine(td, "filestat")) ||
+		 (!td_filestat(td) && is_engine(td, "filestat"))) ||
+		 ((td_filedelete(td) && !is_engine(td, "filedelete")) ||
+		 (!td_filedelete(td) && is_engine(td, "filedelete")))) {
+		return 0;
+	}
+
+	return 1;
+}
+
 /*
  * Lazy way of fixing up options that depend on each other. We could also
  * define option callback handlers, but this is easier.
@@ -618,6 +642,15 @@ static int fixup_options(struct thread_data *td)
 		ret |= 1;
 	}
 
+    /*
+     * return error if readwrite and ioengine are not paired
+     */
+	if (!is_file_operation_options_correct(td)) {
+		log_err("ioengine and readwrite must set to same value if run file operation measurement.\n"
+				"current ioengine=%s, readwrite = %s\n", td->o.ioengine, ddir_str(td->o.td_ddir));
+		ret |=1;
+	}
+
 #ifndef CONFIG_PSHARED
 	if (!o->use_thread) {
 		log_info("fio: this platform does not support process shared"
diff --git a/io_ddir.h b/io_ddir.h
index 217eb628..3c1ff4cb 100644
--- a/io_ddir.h
+++ b/io_ddir.h
@@ -14,8 +14,17 @@ enum fio_ddir {
 
 	DDIR_RWDIR_CNT = 3,
 	DDIR_RWDIR_SYNC_CNT = 4,
+
+	/*
+	 * file operation ddir 'borrows' DDIR_READ, then ts->clat_stat[0] is also borrowed
+	 * only when print by io_ddir_name(), DDIR_FILE_OP_MASK is set to print "file_operation"
+	 * string
+	 */
+	DDIR_FILE_OP = 0,
 };
 
+#define DDIR_FILE_OP_MASK	(1 << 4)
+
 #define for_each_rw_ddir(ddir)	for (enum fio_ddir ddir = 0; ddir < DDIR_RWDIR_CNT; ddir++)
 
 static inline const char *io_ddir_name(enum fio_ddir ddir)
@@ -24,7 +33,10 @@ static inline const char *io_ddir_name(enum fio_ddir ddir)
 					"datasync", "sync_file_range",
 					"wait", };
 
-	if (ddir >= 0 && ddir < DDIR_LAST)
+
+	if (ddir & DDIR_FILE_OP_MASK)
+		return "file_operation";
+	else if (ddir >= 0 && ddir < DDIR_LAST)
 		return name[ddir];
 
 	return "invalid";
@@ -42,6 +54,12 @@ enum td_ddir {
 	TD_DDIR_RANDTRIM	= TD_DDIR_TRIM | TD_DDIR_RAND,
 	TD_DDIR_TRIMWRITE	= TD_DDIR_TRIM | TD_DDIR_WRITE,
 	TD_DDIR_RANDTRIMWRITE	= TD_DDIR_RANDTRIM | TD_DDIR_WRITE,
+
+	TD_DDIR_FILEOP		= 1 << 4,
+	TD_DDIR_FILEOP_MASK	= TD_DDIR_FILEOP | (TD_DDIR_FILEOP - 1),
+	TD_DDIR_FILECREATE	= TD_DDIR_FILEOP | 0,
+	TD_DDIR_FILESTAT	= TD_DDIR_FILEOP | 1,
+	TD_DDIR_FILEDELETE	= TD_DDIR_FILEOP | 2,
 };
 
 #define td_read(td)		((td)->o.td_ddir & TD_DDIR_READ)
@@ -54,6 +72,10 @@ enum td_ddir {
 					== TD_DDIR_TRIMWRITE)
 #define td_randtrimwrite(td)	(((td)->o.td_ddir & TD_DDIR_RANDTRIMWRITE) \
 					== TD_DDIR_RANDTRIMWRITE)
+#define td_filecreate(td)	(((td)->o.td_ddir & TD_DDIR_FILEOP_MASK) == TD_DDIR_FILECREATE)
+#define td_filestat(td)		(((td)->o.td_ddir & TD_DDIR_FILEOP_MASK) == TD_DDIR_FILESTAT)
+#define td_filedelete(td)	(((td)->o.td_ddir & TD_DDIR_FILEOP_MASK) == TD_DDIR_FILEDELETE)
+#define td_fileoperate(td)	((td)->o.td_ddir & TD_DDIR_FILEOP)
 
 static inline int ddir_sync(enum fio_ddir ddir)
 {
@@ -71,7 +93,7 @@ static inline const char *ddir_str(enum td_ddir ddir)
 	static const char *__str[] = { NULL, "read", "write", "rw", "rand",
 				"randread", "randwrite", "randrw",
 				"trim", NULL, "trimwrite", NULL, "randtrim",
-				NULL, "randtrimwrite" };
+				NULL, "randtrimwrite", NULL, "filecreate", "filestat", "filedelete" };
 
 	return __str[ddir];
 }
diff --git a/io_u.c b/io_u.c
index 8035f4b7..aaea3fdd 100644
--- a/io_u.c
+++ b/io_u.c
@@ -674,7 +674,6 @@ static enum fio_ddir rate_ddir(struct thread_data *td, enum fio_ddir ddir)
 	enum fio_ddir odir = ddir ^ 1;
 	uint64_t usec;
 	uint64_t now;
-
 	assert(ddir_rw(ddir));
 	now = utime_since_now(&td->epoch);
 
@@ -782,6 +781,8 @@ static enum fio_ddir get_rw_ddir(struct thread_data *td)
 		ddir = DDIR_WRITE;
 	else if (td_trim(td))
 		ddir = DDIR_TRIM;
+	else if (td_fileoperate(td))
+		ddir = DDIR_FILE_OP;
 	else
 		ddir = DDIR_INVAL;
 
@@ -1426,7 +1427,7 @@ static void lat_fatal(struct thread_data *td, struct io_u *io_u, struct io_compl
 		log_err("fio: latency of %llu nsec exceeds specified max (%llu nsec): %s %s %llu %llu\n",
 					tnsec, max_nsec,
 					io_u->file->file_name,
-					io_ddir_name(io_u->ddir),
+					io_ddir_name(io_u->ddir | td->file_op_flag),
 					io_u->offset, io_u->buflen);
 	}
 	td_verror(td, ETIMEDOUT, "max latency exceeded");
@@ -1862,7 +1863,7 @@ static void __io_u_log_error(struct thread_data *td, struct io_u *io_u)
 		io_u->file ? " on file " : "",
 		io_u->file ? io_u->file->file_name : "",
 		strerror(io_u->error),
-		io_ddir_name(io_u->ddir),
+		io_ddir_name(io_u->ddir | td->file_op_flag),
 		io_u->offset, io_u->xfer_buflen);
 
 	if (td->io_ops->errdetails) {
diff --git a/iolog.c b/iolog.c
index aa9c3bb1..7b9d7088 100644
--- a/iolog.c
+++ b/iolog.c
@@ -49,7 +49,7 @@ void log_io_u(const struct thread_data *td, const struct io_u *io_u)
 	fio_gettime(&now, NULL);
 	fprintf(td->iolog_f, "%llu %s %s %llu %llu\n",
 		(unsigned long long) utime_since_now(&td->io_log_start_time),
-		io_u->file->file_name, io_ddir_name(io_u->ddir), io_u->offset,
+		io_u->file->file_name, io_ddir_name(io_u->ddir | td->file_op_flag), io_u->offset,
 		io_u->buflen);
 
 }
diff --git a/options.c b/options.c
index 9e4d8cd1..bbe0eada 100644
--- a/options.c
+++ b/options.c
@@ -1951,6 +1951,18 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
 			    .oval = TD_DDIR_RANDTRIMWRITE,
 			    .help = "Randomly trim and write mix, trims preceding writes"
 			  },
+			  { .ival = "filecreate",
+			    .oval = TD_DDIR_FILECREATE,
+			    .help = "Create file with file size zero"
+			  },
+			  { .ival = "filestat",
+			    .oval = TD_DDIR_FILESTAT,
+			    .help = "Stat files"
+			  },
+			  { .ival = "filedelete",
+			    .oval = TD_DDIR_FILEDELETE,
+			    .help = "Delete files"
+			  },
 		},
 	},
 	{
diff --git a/stat.c b/stat.c
index b963973a..cad18817 100644
--- a/stat.c
+++ b/stat.c
@@ -530,12 +530,12 @@ static void show_ddir_status(struct group_run_stats *rs, struct thread_stat *ts,
 	if (ddir_sync(ddir)) {
 		if (calc_lat(&ts->sync_stat, &min, &max, &mean, &dev)) {
 			log_buf(out, "  %s:\n", "fsync/fdatasync/sync_file_range");
-			display_lat(io_ddir_name(ddir), min, max, mean, dev, out);
+			display_lat(io_ddir_name(ddir | ts->file_op_flag), min, max, mean, dev, out);
 			show_clat_percentiles(ts->io_u_sync_plat,
 						ts->sync_stat.samples,
 						ts->percentile_list,
 						ts->percentile_precision,
-						io_ddir_name(ddir), out);
+						io_ddir_name(ddir | ts->file_op_flag), out);
 		}
 		return;
 	}
@@ -569,7 +569,7 @@ static void show_ddir_status(struct group_run_stats *rs, struct thread_stat *ts,
 	}
 
 	log_buf(out, "  %s: IOPS=%s, BW=%s (%s)(%s/%llumsec)%s\n",
-			(ts->unified_rw_rep == UNIFIED_MIXED) ? "mixed" : io_ddir_name(ddir),
+			(ts->unified_rw_rep == UNIFIED_MIXED) ? "mixed" : io_ddir_name(ddir | ts->file_op_flag),
 			iops_p, bw_p, bw_p_alt, io_p,
 			(unsigned long long) ts->runtime[ddir],
 			post_st ? : "");
@@ -1449,7 +1449,7 @@ static void add_ddir_status_json(struct thread_stat *ts,
 
 	dir_object = json_create_object();
 	json_object_add_value_object(parent,
-		(ts->unified_rw_rep == UNIFIED_MIXED) ? "mixed" : io_ddir_name(ddir), dir_object);
+		(ts->unified_rw_rep == UNIFIED_MIXED) ? "mixed" : io_ddir_name(ddir | ts->file_op_flag), dir_object);
 
 	if (ddir_rw(ddir)) {
 		bw_bytes = 0;
@@ -2487,6 +2487,8 @@ void __show_run_stats(void)
 		memcpy(ts->percentile_list, td->o.percentile_list, sizeof(td->o.percentile_list));
 		opt_lists[j] = &td->opt_list;
 
+		ts->file_op_flag = td->file_op_flag;
+
 		idx++;
 
 		if (ts->groupid == -1) {
diff --git a/stat.h b/stat.h
index 4c3bf71f..f67965f9 100644
--- a/stat.h
+++ b/stat.h
@@ -175,6 +175,7 @@ struct thread_stat {
 	uint32_t members;
 	uint32_t unified_rw_rep;
 	uint32_t disable_prio_stat;
+	uint32_t file_op_flag;
 
 	/*
 	 * bandwidth and latency stats
-- 
2.17.1





[Index of Archives]     [Linux Kernel]     [Linux SCSI]     [Linux IDE]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux