diff --git a/HOWTO.rst b/HOWTO.rst index 9f55a73bde..c9c42d34f9 100644 --- a/HOWTO.rst +++ b/HOWTO.rst @@ -2332,6 +2332,18 @@ I/O engine This engine is to measure file lookup and meta data access. Example job file: filestat-ioengine.fio. + **filesetxattr** + Simply set files extended attributes via setxattr(). You need to set 'filesize' + and 'nrfiles', so that files will be created. + This engine is to measure extended attributes access. + Example job file: filexattr-ioengine.fio. + + **filelistxattr** + Simply list and then get all files extended attributes via listxattr and getxattr. + You need to set 'filesize' and 'nrfiles', so that files will be created. + This engine is to measure extended attributes access. + Example job file: filexattr-ioengine.fio. + **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. @@ -4854,17 +4866,21 @@ writes in the example above). In the order listed, they denote: For file and directory operation engines, **clat** denotes the time to complete one file or directory operation. - **filecreate engine**:the time cost to create a new file + **filecreate engine**: the time cost to create a new file - **filestat engine**: the time cost to look up an existing file + **filestat engine**: the time cost to look up an existing file - **filedelete engine**:the time cost to delete a file + **filesetxattr engine**:the time cost to set a bunch of file extended attributes - **dircreate engine**: the time cost to create a new directory + **filestat engine**: the time cost to look up all file extended attributes - **dirstat engine**: the time cost to look up an existing directory + **filedelete engine**: the time cost to delete a file - **dirdelete engine**: the time cost to delete a directory + **dircreate engine**: the time cost to create a new directory + + **dirstat engine**: the time cost to look up an existing directory + + **dirdelete engine**: the time cost to delete a directory **lat** Total latency. Same names as slat and clat, this denotes the time from @@ -4896,17 +4912,19 @@ writes in the example above). In the order listed, they denote: fundamental index to denote the performance. It means how many files or directories can be operated per second. - **filecreate engine**:number of files can be created per second + **filecreate engine**: number of files can be created per second + + **filestat engine**: number of files can be looked up per second - **filestat engine**: number of files can be looked up per second + **filesetxattr engine**: number of files for which extended attributes can been set per second - **filedelete engine**:number of files can be deleted per second + **filelistxattr engine**:number of all file extended attributes fetches per second - **dircreate engine**: number of directories can be created per second + **dircreate engine**: number of directories can be created per second - **dirstat engine**: number of directories can be looked up per second + **dirstat engine**: number of directories can be looked up per second - **dirdelete engine**: number of directories can be deleted per second + **dirdelete engine**: number of directories can be deleted per second **lat (nsec/usec/msec)** The distribution of I/O completion latencies. This is the time from when diff --git a/engines/fileoperations.c b/engines/fileoperations.c index e530335958..3e3e17a978 100644 --- a/engines/fileoperations.c +++ b/engines/fileoperations.c @@ -10,6 +10,9 @@ #include #include #include +#ifndef _WIN32 +#include +#endif #include #include "../fio.h" #include "../optgroup.h" @@ -31,13 +34,47 @@ struct filestat_options { unsigned int stat_type; }; +struct xattr_options { + void* pad; + int size; + int count; +}; + enum { FIO_FILESTAT_STAT = 1, FIO_FILESTAT_LSTAT = 2, FIO_FILESTAT_STATX = 3, }; -static struct fio_option options[] = { +#ifndef _WIN32 +static struct fio_option xattr_options[] = { + { + .name = "xattr_size", + .lname = "xattr_size", + .type = FIO_OPT_INT, + .off1 = offsetof(struct xattr_options, size), + .help = "Specify the size of the extended attribute to be set.", + .def = "512", + .category = FIO_OPT_C_ENGINE, + .group = FIO_OPT_G_FILEXATTR, + }, + { + .name = "xattr_count", + .lname = "xattr_count", + .type = FIO_OPT_INT, + .off1 = offsetof(struct xattr_options, count), + .help = "Specify the number of the extended attributes to be set.", + .def = "1", + .category = FIO_OPT_C_ENGINE, + .group = FIO_OPT_G_FILEXATTR, + }, + { + .name = NULL, + } +}; +#endif + +static struct fio_option stat_options[] = { { .name = "stat_type", .lname = "stat_type", @@ -206,6 +243,178 @@ static int stat_file(struct thread_data *td, struct fio_file *f) return 0; } +#ifndef _WIN32 +static int file_setxattr(struct thread_data *td, struct fio_file *f) +{ + struct xattr_options *o = td->eo; + struct timespec start; + int attrcount; + char attrname[256]; + char *attrval; + int ret; + + dprint(FD_FILE, "fd setxattr %s, count = %i, size = %i\n", f->file_name, + o->count, o->size); + + if (f->filetype != FIO_TYPE_FILE) { + log_err("fio: only files are supported\n"); + return 1; + } + if (!strcmp(f->file_name, "-")) { + log_err("fio: can't read/write to stdin/out\n"); + return 1; + } + + if (!td->o.disable_lat) + fio_gettime(&start, NULL); + + attrcount = o->count; + while (attrcount > 0) { + attrval = malloc(o->size); + snprintf(attrname, 256, "user.fio_xattr_%i", attrcount--); +#ifdef __linux__ + ret = setxattr(f->file_name, attrname, attrval, o->size, 0); +#elif defined (__APPLE__) + ret = setxattr(f->file_name, attrname, attrval, o->size, 0, 0); +#else + ret = -1; +#endif + free(attrval); + + if (ret == -1) { + char buf[FIO_VERROR_SIZE]; + int e = errno; + + snprintf(buf, sizeof(buf), "setxattr(%s)", f->file_name); + td_verror(td, e, buf); + return 1; + } + } + + if (!td->o.disable_lat) { + struct fc_data *data = td->io_ops_data; + uint64_t nsec; + + nsec = ntime_since_now(&start); + add_clat_sample(td, data->stat_ddir, nsec, 0, NULL); + } + + return 0; +} + +static int file_listxattr(struct thread_data *td, struct fio_file *f) +{ + struct timespec start; + + ssize_t buflen; + ssize_t vallen; + size_t namelen; + char *attrname; + char *attrbuf; + char *attrval; + + const char *errfn; + char buf[FIO_VERROR_SIZE]; + int e; + + dprint(FD_FILE, "fd listxattr %s\n", f->file_name); + + if (f->filetype != FIO_TYPE_FILE) { + log_err("fio: only files are supported\n"); + return 1; + } + if (!strcmp(f->file_name, "-")) { + log_err("fio: can't read/write to stdin/out\n"); + return 1; + } + + if (!td->o.disable_lat) + fio_gettime(&start, NULL); + +#ifdef __linux__ + buflen = listxattr(f->file_name, NULL, 0); +#elif defined (__APPLE__) + buflen = listxattr(f->file_name, NULL, 0, 0); +#else + buflen = -1; +#endif + if (buflen == -1) { + errfn = "listxattr"; + goto err; + } else if (buflen == 0) { + return 0; + } + + attrbuf = malloc(buflen); +#ifdef __linux__ + buflen = listxattr(f->file_name, attrbuf, buflen); +#elif defined (__APPLE__) + buflen = listxattr(f->file_name, attrbuf, buflen, 0); +#else + buflen = -1; +#endif + if (buflen == -1) { + errfn = "listxattr"; + goto err_cleanup; + } + + attrname = attrbuf; + while (buflen > 0) { +#ifdef __linux__ + vallen = getxattr(f->file_name, attrname, NULL, 0); +#elif defined (__APPLE__) + vallen = getxattr(f->file_name, attrname, NULL, 0, 0, 0); +#else + vallen = -1; +#endif + if (vallen == -1) { + errfn = "getxattr"; + goto err_cleanup; + } + + if (vallen > 0) { + attrval = malloc(vallen); +#ifdef __linux__ + vallen = getxattr(f->file_name, attrname, attrval, vallen); +#elif defined (__APPLE__) + vallen = getxattr(f->file_name, attrname, attrval, vallen, 0, 0); +#else + vallen = -1; +#endif + free(attrval); + if (vallen == -1) { + errfn = "getxattr"; + goto err_cleanup; + } + } + + namelen = strlen(attrname) + 1; + buflen -= namelen; + attrname += namelen; + } + + free(attrbuf); + + if (!td->o.disable_lat) { + struct fc_data *data = td->io_ops_data; + uint64_t nsec; + + nsec = ntime_since_now(&start); + add_clat_sample(td, data->stat_ddir, nsec, 0, NULL); + } + + return 0; + +err_cleanup: + free(attrbuf); +err: + e = errno; + snprintf(buf, sizeof(buf), "%s(%s)", errfn, f->file_name); + td_verror(td, e, buf); + return 1; +} +#endif + static int delete_file(struct thread_data *td, struct fio_file *f) { struct timespec start; @@ -341,7 +550,7 @@ static struct ioengine_ops ioengine_filestat = { .open_file = stat_file, .flags = FIO_SYNCIO | FIO_FAKEIO | FIO_NOSTATS | FIO_NOFILEHASH, - .options = options, + .options = stat_options, .option_struct_size = sizeof(struct filestat_options), }; @@ -385,7 +594,7 @@ static struct ioengine_ops ioengine_dirstat = { .unlink_file = remove_dir, .flags = FIO_DISKLESSIO | FIO_SYNCIO | FIO_FAKEIO | FIO_NOSTATS | FIO_NOFILEHASH, - .options = options, + .options = stat_options, .option_struct_size = sizeof(struct filestat_options), }; @@ -404,6 +613,36 @@ static struct ioengine_ops ioengine_dirdelete = { FIO_NOSTATS | FIO_NOFILEHASH, }; +#ifndef _WIN32 +static struct ioengine_ops ioengine_filesetxattr = { + .name = "filesetxattr", + .version = FIO_IOOPS_VERSION, + .init = init, + .cleanup = cleanup, + .queue = queue_io, + .invalidate = invalidate_do_nothing, + .get_file_size = generic_get_file_size, + .open_file = file_setxattr, + .flags = FIO_SYNCIO | FIO_FAKEIO | + FIO_NOSTATS | FIO_NOFILEHASH, + .options = xattr_options, + .option_struct_size = sizeof(struct xattr_options), +}; + +static struct ioengine_ops ioengine_filelistxattr = { + .name = "filelistxattr", + .version = FIO_IOOPS_VERSION, + .init = init, + .invalidate = invalidate_do_nothing, + .cleanup = cleanup, + .queue = queue_io, + .get_file_size = generic_get_file_size, + .open_file = file_listxattr, + .flags = FIO_SYNCIO | FIO_FAKEIO | + FIO_NOSTATS | FIO_NOFILEHASH, +}; +#endif + static void fio_init fio_fileoperations_register(void) { register_ioengine(&ioengine_filecreate); @@ -412,6 +651,10 @@ static void fio_init fio_fileoperations_register(void) register_ioengine(&ioengine_dircreate); register_ioengine(&ioengine_dirstat); register_ioengine(&ioengine_dirdelete); +#ifndef _WIN32 + register_ioengine(&ioengine_filesetxattr); + register_ioengine(&ioengine_filelistxattr); +#endif } static void fio_exit fio_fileoperations_unregister(void) @@ -422,4 +665,8 @@ static void fio_exit fio_fileoperations_unregister(void) unregister_ioengine(&ioengine_dircreate); unregister_ioengine(&ioengine_dirstat); unregister_ioengine(&ioengine_dirdelete); +#ifndef _WIN32 + unregister_ioengine(&ioengine_filesetxattr); + unregister_ioengine(&ioengine_filelistxattr); +#endif } diff --git a/examples/filexattr-ioengine.fio b/examples/filexattr-ioengine.fio new file mode 100644 index 0000000000..bbf2c7fe63 --- /dev/null +++ b/examples/filexattr-ioengine.fio @@ -0,0 +1,18 @@ +# Example filesetxattr and filelistattr job + +[global] +nrfiles=100 +filesize=4k +openfiles=1 +directory=./small_files +filename_format=small_file.$jobnum.$filenum + +[setxattr] +ioengine=filesetxattr +xattr_count=2 +xattr_size=128 + +[listxattr] +stonewall=1 +ioengine=filelistxattr + diff --git a/examples/filexattr-ioengine.png b/examples/filexattr-ioengine.png new file mode 100644 index 0000000000..aff245926c Binary files /dev/null and b/examples/filexattr-ioengine.png differ diff --git a/fio.1 b/fio.1 index 5036b4f99e..5fb9bc88d0 100644 --- a/fio.1 +++ b/fio.1 @@ -2131,6 +2131,18 @@ and \fBnrfiles\fP, so that files will be created. This engine is to measure file lookup and meta data access. Example job file: filestat-ioengine.fio. .TP +.B filesetxattr +Simply set files extended attributes via setxattr(). You need to set \fBfilesize\fP +and \fBnrfiles\fP, so that files will be created. +This engine is to measure extended attributes access. +Example job file: filexattr-ioengine.fio. +.TP +.B filelistxattr +Simply list and then get all files extended attributes via listxattr and getxattr. +You need to set \fBfilesize\fP and \fBnrfiles\fP, so that files will be created. +This engine is to measure extended attributes access. +Example job file: filexattr-ioengine.fio. +.TP .B filedelete Simply delete the files by unlink() and do no I/O to them. You need to set \fBfilesize\fP and \fBnrfiles\fP, so that the files will be created. @@ -4502,6 +4514,10 @@ to complete one file or directory operation. .TP \fBfilestat engine\fP:\tthe time cost to look up an existing file .TP +\fBfilesetxattr engine\fP:\tnumber of files for which extended attributes can been set per second +.TP +\fBfilelistxattr engine\fP:\tnumber of all file extended attributes fetches per second +.TP \fBfiledelete engine\fP:\tthe time cost to delete a file .TP \fBdircreate engine\fP:\tthe time cost to create a new directory diff --git a/optgroup.h b/optgroup.h index eb5e6f35eb..4bb609ea4c 100644 --- a/optgroup.h +++ b/optgroup.h @@ -73,6 +73,7 @@ enum opt_category_group { __FIO_OPT_G_WINDOWSAIO, __FIO_OPT_G_XNVME, __FIO_OPT_G_LIBBLKIO, + __FIO_OPT_G_FILEXATTR, FIO_OPT_G_RATE = (1ULL << __FIO_OPT_G_RATE), FIO_OPT_G_ZONE = (1ULL << __FIO_OPT_G_ZONE), @@ -120,6 +121,7 @@ enum opt_category_group { FIO_OPT_G_WINDOWSAIO = (1ULL << __FIO_OPT_G_WINDOWSAIO), FIO_OPT_G_XNVME = (1ULL << __FIO_OPT_G_XNVME), FIO_OPT_G_LIBBLKIO = (1ULL << __FIO_OPT_G_LIBBLKIO), + FIO_OPT_G_FILEXATTR = (1ULL << __FIO_OPT_G_FILEXATTR), }; extern const struct opt_group *opt_group_from_mask(uint64_t *mask);