get_engine_name() function added to support external ioengine option
via "external:" prefix in below commits in 2007 has been broken.
7b395ca5('Prefix external io engine loading with 'external'')
8a7bd877('Document loading external io engines')
It seems to have been broken since below commit in 2011 which made
__handle_option() strip ':' and after while parsing the option.
c44b1ff5('Add sub-option support (sort-of) and convert libaio_userspace_reap')
Above change made fio dlopen "external" instead of the path located
after "external:", since the path had already been stripped by
__handle_option() by the time get_engine_name() was called. This commit
fixes it by adding ->ioengine_so_path pointer to keep the path while
parsing using sub-option callback.
Note that removing get_engine_name() doesn't affect non "external:"
ioengine options, as the option parser also strips while parsing.
-- before this commit
# ./fio --name=xxx --ioengine=external:./engines/skeleton_external.so
fio: engine external not loadable
fio: failed to load engine external
fio: file:ioengines.c:91, func=dlopen, error=external: cannot open shared object file: No such file or directory
-- with this commit
# ./fio --name=xxx --ioengine=external:./engines/skeleton_external.so
xxx: (g=0): rw=read, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=engine_name, iodepth=1
fio-3.0-32-g68f6f
Starting 1 process
xxx: you need to specify size=
fio: pid=0, err=22/file:filesetup.c:973, func=total_file_size, error=Invalid argument
Run status group 0 (all jobs):
Signed-off-by: Tomohiro Kusumi <tkusumi@tuxera.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
return ret;
}
-/* External engines are specified by "external:name.o") */
-static const char *get_engine_name(const char *str)
-{
- char *p = strstr(str, ":");
-
- if (!p)
- return str;
-
- p++;
- strip_blank_front(&p);
- strip_blank_end(p);
- return p;
-}
-
static void init_rand_file_service(struct thread_data *td)
{
unsigned long nranges = td->o.nr_files << FIO_FSERVICE_SHIFT;
free_ioengine(td);
}
- engine = get_engine_name(td->o.ioengine);
+ /*
+ * Use ->ioengine_so_path if an external ioengine is specified.
+ */
+ engine = td->o.ioengine_so_path ?: td->o.ioengine;
td->io_ops = load_ioengine(td, engine);
if (!td->io_ops) {
log_err("fio: failed to load engine %s\n", engine);
return 0;
}
+/*
+ * str is supposed to be a substring of the strdup'd original string,
+ * and is valid only if it's a regular file path.
+ * This function keeps the pointer to the path as needed later.
+ *
+ * "external:/path/to/so\0" <- original pointer updated with strdup'd
+ * "external\0" <- above pointer after parsed, i.e. ->ioengine
+ * "/path/to/so\0" <- str argument, i.e. ->ioengine_so_path
+ */
+static int str_ioengine_external_cb(void *data, const char *str)
+{
+ struct thread_data *td = cb_data_to_td(data);
+ struct stat sb;
+ char *p;
+
+ if (!str) {
+ log_err("fio: null external ioengine path\n");
+ return 1;
+ }
+
+ p = (char *)str; /* str is mutable */
+ strip_blank_front(&p);
+ strip_blank_end(p);
+
+ if (stat(p, &sb) || !S_ISREG(sb.st_mode)) {
+ log_err("fio: invalid external ioengine path \"%s\"\n", p);
+ return 1;
+ }
+
+ td->o.ioengine_so_path = p;
+ return 0;
+}
+
static int rw_verify(struct fio_option *o, void *data)
{
struct thread_data *td = cb_data_to_td(data);
#endif
{ .ival = "external",
.help = "Load external engine (append name)",
+ .cb = str_ioengine_external_cb,
},
},
},
char *filename_format;
char *opendir;
char *ioengine;
+ char *ioengine_so_path;
char *mmapfile;
enum td_ddir td_ddir;
unsigned int rw_seq;