struct netio_data *nd;
if (!td->files_index) {
- add_file(td, td->o.filename ?: "net");
+ add_file(td, td->o.filename ?: "net", 0);
td->o.nr_files = td->o.nr_files ?: 1;
}
* The size of the RBD is set instead of a artificial file.
*/
if (!td->files_index) {
- add_file(td, td->o.filename ? : "rbd");
+ add_file(td, td->o.filename ? : "rbd", 0);
td->o.nr_files = td->o.nr_files ? : 1;
}
f = td->files[0];
struct disk_util *du;
};
+struct file_name {
+ struct flist_head list;
+ char *filename;
+};
+
#define FILE_FLAG_FNS(name) \
static inline void fio_file_set_##name(struct fio_file *f) \
{ \
extern int __must_check generic_get_file_size(struct thread_data *, struct fio_file *);
extern int __must_check file_lookup_open(struct fio_file *f, int flags);
extern int __must_check pre_read_files(struct thread_data *);
-extern int add_file(struct thread_data *, const char *);
+extern int add_file(struct thread_data *, const char *, int);
extern int add_file_exclusive(struct thread_data *, const char *);
extern void get_file(struct fio_file *);
extern int __must_check put_file(struct thread_data *, struct fio_file *);
extern void dup_files(struct thread_data *, struct thread_data *);
extern int get_fileno(struct thread_data *, const char *);
extern void free_release_files(struct thread_data *);
+extern void filesetup_mem_free(void);
void fio_file_reset(struct thread_data *, struct fio_file *);
int fio_files_done(struct thread_data *);
#include "fio.h"
#include "smalloc.h"
#include "filehash.h"
+#include "options.h"
#include "os/os.h"
#include "hash.h"
#include "lib/axmap.h"
static int root_warn;
+static FLIST_HEAD(filename_list);
+
static inline void clear_error(struct thread_data *td)
{
td->error = 0;
}
}
-int add_file(struct thread_data *td, const char *fname)
+static void set_already_allocated(const char *fname) {
+ struct file_name *fn;
+
+ fn = malloc(sizeof(struct file_name));
+ fn->filename = strdup(fname);
+ flist_add_tail(&fn->list, &filename_list);
+}
+
+static int is_already_allocated(const char *fname)
+{
+ struct flist_head *entry;
+ char *filename;
+
+ if (!flist_empty(&filename_list))
+ {
+ flist_for_each(entry, &filename_list) {
+ filename = flist_entry(entry, struct file_name, list)->filename;
+
+ if (strcmp(filename, fname) == 0)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void free_already_allocated() {
+ struct flist_head *entry, *tmp;
+ struct file_name *fn;
+
+ if (!flist_empty(&filename_list))
+ {
+ flist_for_each_safe(entry, tmp, &filename_list) {
+ fn = flist_entry(entry, struct file_name, list);
+ free(fn->filename);
+ flist_del(&fn->list);
+ free(fn);
+ }
+ }
+}
+
+int add_file(struct thread_data *td, const char *fname, int numjob)
{
int cur_files = td->files_index;
char file_name[PATH_MAX];
dprint(FD_FILE, "add file %s\n", fname);
+ if (td->o.directory)
+ len = set_name_idx(file_name, td->o.directory, numjob);
+
+ sprintf(file_name + len, "%s", fname);
+
+ /* clean cloned siblings using existing files */
+ if (numjob && is_already_allocated(file_name))
+ return 0;
+
f = smalloc(sizeof(*f));
if (!f) {
log_err("fio: smalloc OOM\n");
if (td->io_ops && (td->io_ops->flags & FIO_DISKLESSIO))
f->real_file_size = -1ULL;
- if (td->o.directory)
- len = sprintf(file_name, "%s/", td->o.directory);
-
- sprintf(file_name + len, "%s", fname);
f->file_name = smalloc_strdup(file_name);
if (!f->file_name) {
log_err("fio: smalloc OOM\n");
if (f->filetype == FIO_TYPE_FILE)
td->nr_normal_files++;
+ set_already_allocated(file_name);
+
dprint(FD_FILE, "file %p \"%s\" added at %d\n", f, f->file_name,
cur_files);
return i;
}
- return add_file(td, fname);
+ return add_file(td, fname, 0);
}
void get_file(struct fio_file *f)
}
if (S_ISREG(sb.st_mode)) {
- add_file(td, full_path);
+ add_file(td, full_path, 0);
td->o.nr_files++;
continue;
}
return 1;
}
+
+/* free memory used in initialization phase only */
+void filesetup_mem_free() {
+ free_already_allocated();
+}
.BI directory \fR=\fPstr
Prefix filenames with this directory. Used to place files in a location other
than `./'.
+You can specify a number of directories by separating the names with a ':'
+character. These directories will be assigned equally distributed to job clones
+creates with \fInumjobs\fR as long as they are using generated filenames.
+If specific \fIfilename(s)\fR are set fio will use the first listed directory,
+and thereby matching the \fIfilename\fR semantic which generates a file each
+clone if not specified, but let all clones use the same if set.
.TP
.BI filename \fR=\fPstr
.B fio
file_alloced = 1;
if (o->nr_files == 1 && exists_and_not_file(jobname))
- add_file(td, jobname);
+ add_file(td, jobname, job_add_num);
else {
for (i = 0; i < o->nr_files; i++)
- add_file(td, make_filename(fname, o, jobname, td->thread_number, i));
+ add_file(td, make_filename(fname, o, jobname, job_add_num, i), job_add_num);
}
}
}
}
- job_add_num = numjobs - 1;
-
- if (add_job(td_new, jobname, job_add_num, 1, client_type))
+ if (add_job(td_new, jobname, numjobs, 1, client_type))
goto err;
}
free(ini_file);
fio_options_free(&def_thread);
+ filesetup_mem_free();
if (!thread_number) {
if (parse_dryrun())
rw = DDIR_INVAL;
if (!strcmp(act, "add")) {
td->o.nr_files++;
- fileno = add_file(td, fname);
+ fileno = add_file(td, fname, 0);
file_action = FIO_LOG_ADD_FILE;
continue;
} else if (!strcmp(act, "open")) {
}
/*
- * Return next file in the string. Files are separated with ':'. If the ':'
+ * Return next name in the string. Files are separated with ':'. If the ':'
* is escaped with a '\', then that ':' is part of the filename and does not
* indicate a new file.
*/
-static char *get_next_file_name(char **ptr)
+static char *get_next_name(char **ptr)
{
char *str = *ptr;
char *p, *start;
return start;
}
+
+static int get_max_name_idx(char *input)
+{
+ unsigned int cur_idx;
+ char *str, *p;
+
+ p = str = strdup(input);
+ for (cur_idx = 0; ; cur_idx++)
+ if (get_next_name(&str) == NULL)
+ break;
+
+ free(p);
+ return cur_idx;
+}
+
+/*
+ * Returns the directory at the index, indexes > entires will be
+ * assigned via modulo division of the index
+ */
+int set_name_idx(char *target, char *input, int index)
+{
+ unsigned int cur_idx;
+ int len;
+ char *fname, *str, *p;
+
+ p = str = strdup(input);
+
+ index %= get_max_name_idx(input);
+ for (cur_idx = 0; cur_idx <= index; cur_idx++)
+ fname = get_next_name(&str);
+
+ len = sprintf(target, "%s/", fname);
+ free(p);
+
+ return len;
+}
+
static int str_filename_cb(void *data, const char *input)
{
struct thread_data *td = data;
if (!td->files_index)
td->o.nr_files = 0;
- while ((fname = get_next_file_name(&str)) != NULL) {
+ while ((fname = get_next_name(&str)) != NULL) {
if (!strlen(fname))
break;
- add_file(td, fname);
+ add_file(td, fname, 0);
td->o.nr_files++;
}
return 0;
}
-static int str_directory_cb(void *data, const char fio_unused *str)
+static int str_directory_cb(void *data, const char fio_unused *unused)
{
struct thread_data *td = data;
struct stat sb;
+ char *dirname, *str, *p;
+ int ret = 0;
if (parse_dryrun())
return 0;
- if (lstat(td->o.directory, &sb) < 0) {
- int ret = errno;
+ p = str = strdup(td->o.directory);
+ while ((dirname = get_next_name(&str)) != NULL) {
+ if (lstat(dirname, &sb) < 0) {
+ ret = errno;
- log_err("fio: %s is not a directory\n", td->o.directory);
- td_verror(td, ret, "lstat");
- return 1;
- }
- if (!S_ISDIR(sb.st_mode)) {
- log_err("fio: %s is not a directory\n", td->o.directory);
- return 1;
+ log_err("fio: %s is not a directory\n", dirname);
+ td_verror(td, ret, "lstat");
+ goto out;
+ }
+ if (!S_ISDIR(sb.st_mode)) {
+ log_err("fio: %s is not a directory\n", dirname);
+ ret = 1;
+ goto out;
+ }
}
- return 0;
+out:
+ free(p);
+ return ret;
}
static int str_lockfile_cb(void *data, const char fio_unused *str)
void del_opt_posval(const char *, const char *);
struct thread_data;
void fio_options_free(struct thread_data *);
+char *get_name_idx(char *, int);
+int set_name_idx(char *, char *, int);
extern struct fio_option fio_options[FIO_MAX_OPTS];