ioengines: implement dircreate, dirstat, dirdelete engines to fileoperations.c
[fio.git] / engines / fileoperations.c
1 /*
2  * file/directory operations engine
3  *
4  * IO engine that doesn't do any IO, just operates files/directories
5  * and tracks the latency of the operation.
6  */
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <fcntl.h>
10 #include <errno.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <unistd.h>
14 #include "../fio.h"
15 #include "../optgroup.h"
16 #include "../oslib/statx.h"
17
18 enum fio_engine {
19         UNKNOWN_OP_ENGINE = 0,
20         FILE_OP_ENGINE = 1,
21         DIR_OP_ENGINE = 2,
22 };
23
24 struct fc_data {
25         enum fio_ddir stat_ddir;
26         enum fio_engine op_engine;
27 };
28
29 struct filestat_options {
30         void *pad;
31         unsigned int stat_type;
32 };
33
34 enum {
35         FIO_FILESTAT_STAT       = 1,
36         FIO_FILESTAT_LSTAT      = 2,
37         FIO_FILESTAT_STATX      = 3,
38 };
39
40 static struct fio_option options[] = {
41         {
42                 .name   = "stat_type",
43                 .lname  = "stat_type",
44                 .type   = FIO_OPT_STR,
45                 .off1   = offsetof(struct filestat_options, stat_type),
46                 .help   = "Specify stat system call type to measure lookup/getattr performance",
47                 .def    = "stat",
48                 .posval = {
49                           { .ival = "stat",
50                             .oval = FIO_FILESTAT_STAT,
51                             .help = "Use stat(2)",
52                           },
53                           { .ival = "lstat",
54                             .oval = FIO_FILESTAT_LSTAT,
55                             .help = "Use lstat(2)",
56                           },
57                           { .ival = "statx",
58                             .oval = FIO_FILESTAT_STATX,
59                             .help = "Use statx(2) if exists",
60                           },
61                 },
62                 .category = FIO_OPT_C_ENGINE,
63                 .group  = FIO_OPT_G_FILESTAT,
64         },
65         {
66                 .name   = NULL,
67         },
68 };
69
70 static int setup_dirs(struct thread_data *td)
71 {
72         int ret = 0;
73         int i;
74         struct fio_file *f;
75
76         for_each_file(td, f, i) {
77                 dprint(FD_FILE, "setup directory %s\n", f->file_name);
78                 ret = fio_mkdir(f->file_name, 0700);
79                 if ((ret && errno != EEXIST)) {
80                         log_err("create directory %s failed with %d\n",
81                                 f->file_name, errno);
82                         break;
83                 }
84                 ret = 0;
85         }
86         return ret;
87 }
88
89
90
91 static int open_file(struct thread_data *td, struct fio_file *f)
92 {
93         struct timespec start;
94         int do_lat = !td->o.disable_lat;
95
96         dprint(FD_FILE, "fd open %s\n", f->file_name);
97
98         if (f->filetype != FIO_TYPE_FILE) {
99                 log_err("fio: only files are supported\n");
100                 return 1;
101         }
102         if (!strcmp(f->file_name, "-")) {
103                 log_err("fio: can't read/write to stdin/out\n");
104                 return 1;
105         }
106
107         if (do_lat)
108                 fio_gettime(&start, NULL);
109
110         if (((struct fc_data *)td->io_ops_data)->op_engine == FILE_OP_ENGINE)
111                 f->fd = open(f->file_name, O_CREAT|O_RDWR, 0600);
112         else if (((struct fc_data *)td->io_ops_data)->op_engine == DIR_OP_ENGINE)
113                 f->fd = fio_mkdir(f->file_name, S_IFDIR);
114         else {
115                 log_err("fio: unknown file/directory operation engine\n");
116                 return 1;
117         }
118
119         if (f->fd == -1) {
120                 char buf[FIO_VERROR_SIZE];
121                 int e = errno;
122
123                 snprintf(buf, sizeof(buf), "open(%s)", f->file_name);
124                 td_verror(td, e, buf);
125                 return 1;
126         }
127
128         if (do_lat) {
129                 struct fc_data *data = td->io_ops_data;
130                 uint64_t nsec;
131
132                 nsec = ntime_since_now(&start);
133                 add_clat_sample(td, data->stat_ddir, nsec, 0, 0, 0, 0);
134         }
135
136         return 0;
137 }
138
139 static int stat_file(struct thread_data *td, struct fio_file *f)
140 {
141         struct filestat_options *o = td->eo;
142         struct timespec start;
143         int do_lat = !td->o.disable_lat;
144         struct stat statbuf;
145 #ifndef WIN32
146         struct statx statxbuf;
147         char *abspath;
148 #endif
149         int ret;
150
151         dprint(FD_FILE, "fd stat %s\n", f->file_name);
152
153         if (f->filetype != FIO_TYPE_FILE) {
154                 log_err("fio: only files are supported\n");
155                 return 1;
156         }
157         if (!strcmp(f->file_name, "-")) {
158                 log_err("fio: can't read/write to stdin/out\n");
159                 return 1;
160         }
161
162         if (do_lat)
163                 fio_gettime(&start, NULL);
164
165         switch (o->stat_type) {
166         case FIO_FILESTAT_STAT:
167                 ret = stat(f->file_name, &statbuf);
168                 break;
169         case FIO_FILESTAT_LSTAT:
170                 ret = lstat(f->file_name, &statbuf);
171                 break;
172         case FIO_FILESTAT_STATX:
173 #ifndef WIN32
174                 abspath = realpath(f->file_name, NULL);
175                 if (abspath) {
176                         ret = statx(-1, abspath, 0, STATX_ALL, &statxbuf);
177                         free(abspath);
178                 } else
179                         ret = -1;
180 #else
181                 ret = -1;
182 #endif
183                 break;
184         default:
185                 ret = -1;
186                 break;
187         }
188
189         if (ret == -1) {
190                 char buf[FIO_VERROR_SIZE];
191                 int e = errno;
192
193                 snprintf(buf, sizeof(buf), "stat(%s) type=%u", f->file_name,
194                         o->stat_type);
195                 td_verror(td, e, buf);
196                 return 1;
197         }
198
199         if (do_lat) {
200                 struct fc_data *data = td->io_ops_data;
201                 uint64_t nsec;
202
203                 nsec = ntime_since_now(&start);
204                 add_clat_sample(td, data->stat_ddir, nsec, 0, 0, 0, 0);
205         }
206
207         return 0;
208 }
209
210
211 static int delete_file(struct thread_data *td, struct fio_file *f)
212 {
213         struct timespec start;
214         int do_lat = !td->o.disable_lat;
215         int ret;
216
217         dprint(FD_FILE, "fd delete %s\n", f->file_name);
218
219         if (f->filetype != FIO_TYPE_FILE) {
220                 log_err("fio: only files are supported\n");
221                 return 1;
222         }
223         if (!strcmp(f->file_name, "-")) {
224                 log_err("fio: can't read/write to stdin/out\n");
225                 return 1;
226         }
227
228         if (do_lat)
229                 fio_gettime(&start, NULL);
230
231         if (((struct fc_data *)td->io_ops_data)->op_engine == FILE_OP_ENGINE)
232                 ret = unlink(f->file_name);
233         else if (((struct fc_data *)td->io_ops_data)->op_engine == DIR_OP_ENGINE)
234                 ret = rmdir(f->file_name);
235         else {
236                 log_err("fio: unknown file/directory operation engine\n");
237                 return 1;
238         }
239
240
241
242         if (ret == -1) {
243                 char buf[FIO_VERROR_SIZE];
244                 int e = errno;
245
246                 snprintf(buf, sizeof(buf), "delete(%s)", f->file_name);
247                 td_verror(td, e, buf);
248                 return 1;
249         }
250
251         if (do_lat) {
252                 struct fc_data *data = td->io_ops_data;
253                 uint64_t nsec;
254
255                 nsec = ntime_since_now(&start);
256                 add_clat_sample(td, data->stat_ddir, nsec, 0, 0, 0, 0);
257         }
258
259         return 0;
260 }
261
262 static int invalidate_do_nothing(struct thread_data *td, struct fio_file *f)
263 {
264         /* do nothing because file not opened */
265         return 0;
266 }
267
268 static enum fio_q_status queue_io(struct thread_data *td, struct io_u *io_u)
269 {
270         return FIO_Q_COMPLETED;
271 }
272
273 /*
274  * Ensure that we at least have a block size worth of IO to do for each
275  * file. If the job file has td->o.size < nr_files * block_size, then
276  * fio won't do anything.
277  */
278 static int get_file_size(struct thread_data *td, struct fio_file *f)
279 {
280         f->real_file_size = td_min_bs(td);
281         return 0;
282 }
283
284 static int init(struct thread_data *td)
285 {
286         struct fc_data *data;
287
288         data = calloc(1, sizeof(*data));
289
290         if (td_read(td))
291                 data->stat_ddir = DDIR_READ;
292         else if (td_write(td))
293                 data->stat_ddir = DDIR_WRITE;
294
295         data->op_engine = UNKNOWN_OP_ENGINE;
296
297         if (!strncmp(td->o.ioengine, "file", 4)) {
298                 data->op_engine = FILE_OP_ENGINE;
299                 dprint(FD_FILE, "Operate engine type: file\n");
300         }
301         if (!strncmp(td->o.ioengine, "dir", 3)) {
302                 data->op_engine = DIR_OP_ENGINE;
303                 dprint(FD_FILE, "Operate engine type: directory\n");
304         }
305
306         td->io_ops_data = data;
307         return 0;
308 }
309
310 static void cleanup(struct thread_data *td)
311 {
312         struct fc_data *data = td->io_ops_data;
313
314         free(data);
315 }
316
317 static int remove_dir(struct thread_data *td, struct fio_file *f)
318 {
319         dprint(FD_FILE, "remove directory %s\n", f->file_name);
320         return rmdir(f->file_name);
321 }
322
323 static struct ioengine_ops ioengine_filecreate = {
324         .name           = "filecreate",
325         .version        = FIO_IOOPS_VERSION,
326         .init           = init,
327         .cleanup        = cleanup,
328         .queue          = queue_io,
329         .get_file_size  = get_file_size,
330         .open_file      = open_file,
331         .close_file     = generic_close_file,
332         .flags          = FIO_DISKLESSIO | FIO_SYNCIO | FIO_FAKEIO |
333                                 FIO_NOSTATS | FIO_NOFILEHASH,
334 };
335
336 static struct ioengine_ops ioengine_filestat = {
337         .name           = "filestat",
338         .version        = FIO_IOOPS_VERSION,
339         .init           = init,
340         .cleanup        = cleanup,
341         .queue          = queue_io,
342         .invalidate     = invalidate_do_nothing,
343         .get_file_size  = generic_get_file_size,
344         .open_file      = stat_file,
345         .flags          =  FIO_SYNCIO | FIO_FAKEIO |
346                                 FIO_NOSTATS | FIO_NOFILEHASH,
347         .options        = options,
348         .option_struct_size = sizeof(struct filestat_options),
349 };
350
351 static struct ioengine_ops ioengine_filedelete = {
352         .name           = "filedelete",
353         .version        = FIO_IOOPS_VERSION,
354         .init           = init,
355         .invalidate     = invalidate_do_nothing,
356         .cleanup        = cleanup,
357         .queue          = queue_io,
358         .get_file_size  = generic_get_file_size,
359         .open_file      = delete_file,
360         .flags          =  FIO_SYNCIO | FIO_FAKEIO |
361                                 FIO_NOSTATS | FIO_NOFILEHASH,
362 };
363
364 static struct ioengine_ops ioengine_dircreate = {
365         .name           = "dircreate",
366         .version        = FIO_IOOPS_VERSION,
367         .init           = init,
368         .cleanup        = cleanup,
369         .queue          = queue_io,
370         .get_file_size  = get_file_size,
371         .open_file      = open_file,
372         .close_file     = generic_close_file,
373         .unlink_file    = remove_dir,
374         .flags          = FIO_DISKLESSIO | FIO_SYNCIO | FIO_FAKEIO |
375                                 FIO_NOSTATS | FIO_NOFILEHASH,
376 };
377
378 static struct ioengine_ops ioengine_dirstat = {
379         .name           = "dirstat",
380         .version        = FIO_IOOPS_VERSION,
381         .setup          = setup_dirs,
382         .init           = init,
383         .cleanup        = cleanup,
384         .queue          = queue_io,
385         .invalidate     = invalidate_do_nothing,
386         .get_file_size  = generic_get_file_size,
387         .open_file      = stat_file,
388         .unlink_file    = remove_dir,
389         .flags          =  FIO_DISKLESSIO | FIO_SYNCIO | FIO_FAKEIO |
390                                 FIO_NOSTATS | FIO_NOFILEHASH,
391         .options        = options,
392         .option_struct_size = sizeof(struct filestat_options),
393 };
394
395 static struct ioengine_ops ioengine_dirdelete = {
396         .name           = "dirdelete",
397         .version        = FIO_IOOPS_VERSION,
398         .setup          = setup_dirs,
399         .init           = init,
400         .invalidate     = invalidate_do_nothing,
401         .cleanup        = cleanup,
402         .queue          = queue_io,
403         .get_file_size  = get_file_size,
404         .open_file      = delete_file,
405         .unlink_file    = remove_dir,
406         .flags          = FIO_DISKLESSIO | FIO_SYNCIO | FIO_FAKEIO |
407                                 FIO_NOSTATS | FIO_NOFILEHASH,
408 };
409
410
411 static void fio_init fio_fileoperations_register(void)
412 {
413         register_ioengine(&ioengine_filecreate);
414         register_ioengine(&ioengine_filestat);
415         register_ioengine(&ioengine_filedelete);
416         register_ioengine(&ioengine_dircreate);
417         register_ioengine(&ioengine_dirstat);
418         register_ioengine(&ioengine_dirdelete);
419 }
420
421 static void fio_exit fio_fileoperations_unregister(void)
422 {
423         unregister_ioengine(&ioengine_filecreate);
424         unregister_ioengine(&ioengine_filestat);
425         unregister_ioengine(&ioengine_filedelete);
426         unregister_ioengine(&ioengine_dircreate);
427         unregister_ioengine(&ioengine_dirstat);
428         unregister_ioengine(&ioengine_dirdelete);
429 }