4d2ec5c33c2a4d9a99b34b686be0e1649b9731da
[fio.git] / profiles / act.c
1 #include "../fio.h"
2 #include "../profile.h"
3 #include "../parse.h"
4
5 /*
6  * 1x loads
7  */
8 #define R_LOAD          2000
9 #define W_LOAD          1000
10
11 #define SAMPLE_SEC      3600            /* 1h checks */
12
13 struct act_pass_criteria {
14         unsigned int max_usec;
15         unsigned int max_perm;
16 };
17 #define ACT_MAX_CRIT    3
18
19 static struct act_pass_criteria act_pass[ACT_MAX_CRIT] = {
20         {
21                 .max_usec =     1000,
22                 .max_perm =     50,
23         },
24         {
25                 .max_usec =     8000,
26                 .max_perm =     10,
27         },
28         {
29                 .max_usec =     64000,
30                 .max_perm =     1,
31         },
32 };
33
34 struct act_slice {
35         uint64_t lat_buckets[ACT_MAX_CRIT];
36         uint64_t total_ios;
37 };
38
39 struct act_run_data {
40         struct fio_mutex *mutex;
41         unsigned int pending;
42
43         struct act_slice *slices;
44         unsigned int nr_slices;
45 };
46 static struct act_run_data *act_run_data;
47
48 struct act_prof_data {
49         struct timeval sample_tv;
50         struct act_slice *slices;
51         unsigned int cur_slice;
52         unsigned int nr_slices;
53 };
54
55 static char *device_names;
56 static unsigned int load;
57 static unsigned int prep;
58 static unsigned int threads_per_queue;
59 static unsigned int num_read_blocks;
60 static unsigned int write_size;
61 static unsigned long long test_duration;
62
63 #define ACT_MAX_OPTS    128
64 static const char *act_opts[ACT_MAX_OPTS] = {
65         "direct=1",
66         "ioengine=sync",
67         "random_generator=lfsr",
68         "group_reporting=1",
69         "thread",
70         NULL,
71 };
72 static unsigned int opt_idx = 5;
73 static unsigned int org_idx;
74
75 static int act_add_opt(const char *format, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
76
77 struct act_options {
78         unsigned int pad;
79         char *device_names;
80         unsigned int load;
81         unsigned int prep;
82         unsigned int threads_per_queue;
83         unsigned int num_read_blocks;
84         unsigned int write_size;
85         unsigned long long test_duration;
86 };
87
88 static struct act_options act_options;
89
90 static struct fio_option options[] = {
91         {
92                 .name   = "device-names",
93                 .lname  = "device-names",
94                 .type   = FIO_OPT_STR_STORE,
95                 .off1   = offsetof(struct act_options, device_names),
96                 .help   = "Devices to use",
97                 .category = FIO_OPT_C_PROFILE,
98                 .group  = FIO_OPT_G_ACT,
99         },
100         {
101                 .name   = "load",
102                 .lname  = "Load multiplier",
103                 .type   = FIO_OPT_INT,
104                 .off1   = offsetof(struct act_options, load),
105                 .help   = "ACT load multipler (default 1x)",
106                 .def    = "1",
107                 .category = FIO_OPT_C_PROFILE,
108                 .group  = FIO_OPT_G_ACT,
109         },
110         {
111                 .name   = "test-duration",
112                 .lname  = "Test duration",
113                 .type   = FIO_OPT_STR_VAL_TIME,
114                 .off1   = offsetof(struct act_options, test_duration),
115                 .help   = "How long the entire test takes to run",
116                 .def    = "24h",
117                 .category = FIO_OPT_C_PROFILE,
118                 .group  = FIO_OPT_G_ACT,
119         },
120         {
121                 .name   = "threads-per-queue",
122                 .lname  = "Number of read IO threads per device",
123                 .type   = FIO_OPT_INT,
124                 .off1   = offsetof(struct act_options, threads_per_queue),
125                 .help   = "Number of read IO threads per device",
126                 .def    = "8",
127                 .category = FIO_OPT_C_PROFILE,
128                 .group  = FIO_OPT_G_ACT,
129         },
130         {
131                 .name   = "read-req-num-512-blocks",
132                 .lname  = "Number of 512b blocks to read",
133                 .type   = FIO_OPT_INT,
134                 .off1   = offsetof(struct act_options, num_read_blocks),
135                 .help   = "Number of 512b blocks to read at the time",
136                 .def    = "3",
137                 .category = FIO_OPT_C_PROFILE,
138                 .group  = FIO_OPT_G_ACT,
139         },
140         {
141                 .name   = "large-block-op-kbytes",
142                 .lname  = "Size of large block ops (writes)",
143                 .type   = FIO_OPT_INT,
144                 .off1   = offsetof(struct act_options, write_size),
145                 .help   = "Size of large block ops (writes)",
146                 .def    = "128k",
147                 .category = FIO_OPT_C_PROFILE,
148                 .group  = FIO_OPT_G_ACT,
149         },
150         {
151                 .name   = "prep",
152                 .lname  = "Run ACT prep phase",
153                 .type   = FIO_OPT_STR_SET,
154                 .off1   = offsetof(struct act_options, prep),
155                 .help   = "Set to run ACT prep phase",
156                 .category = FIO_OPT_C_PROFILE,
157                 .group  = FIO_OPT_G_ACT,
158         },
159         {
160                 .name   = NULL,
161         },
162 };
163
164 static int act_add_opt(const char *str, ...)
165 {
166         char buffer[512];
167         va_list args;
168         size_t len;
169
170         if (opt_idx == ACT_MAX_OPTS) {
171                 log_err("act: ACT_MAX_OPTS is too small\n");
172                 return 1;
173         }
174
175         va_start(args, str);
176         len = vsnprintf(buffer, sizeof(buffer), str, args);
177         va_end(args);
178
179         if (len)
180                 act_opts[opt_idx++] = strdup(buffer);
181
182         return 0;
183 }
184
185 static int act_add_rw(const char *dev, int reads)
186 {
187         if (act_add_opt("name=act-%s-%s", reads ? "read" : "write", dev))
188                 return 1;
189         if (act_add_opt("filename=%s", dev))
190                 return 1;
191         if (act_add_opt("rw=%s", reads ? "randread" : "randwrite"))
192                 return 1;
193         if (reads) {
194                 int rload = load * R_LOAD / threads_per_queue;
195
196                 if (act_add_opt("numjobs=%u", threads_per_queue))
197                         return 1;
198                 if (act_add_opt("rate_iops=%u", rload))
199                         return 1;
200                 if (act_add_opt("bs=%u", num_read_blocks * 512))
201                         return 1;
202         } else {
203                 const int rsize = write_size / (num_read_blocks * 512);
204                 int wload = (load * W_LOAD + rsize - 1) / rsize;
205
206                 if (act_add_opt("rate_iops=%u", wload))
207                         return 1;
208                 if (act_add_opt("bs=%u", write_size))
209                         return 1;
210         }
211
212         return 0;
213 }
214
215 static int act_add_dev_prep(const char *dev)
216 {
217         /* Add sequential zero phase */
218         if (act_add_opt("name=act-prep-zeroes-%s", dev))
219                 return 1;
220         if (act_add_opt("filename=%s", dev))
221                 return 1;
222         if (act_add_opt("bs=1M"))
223                 return 1;
224         if (act_add_opt("zero_buffers"))
225                 return 1;
226         if (act_add_opt("rw=write"))
227                 return 1;
228
229         /* Randomly overwrite device */
230         if (act_add_opt("name=act-prep-salt-%s", dev))
231                 return 1;
232         if (act_add_opt("stonewall"))
233                 return 1;
234         if (act_add_opt("filename=%s", dev))
235                 return 1;
236         if (act_add_opt("bs=4k"))
237                 return 1;
238         if (act_add_opt("ioengine=libaio"))
239                 return 1;
240         if (act_add_opt("iodepth=64"))
241                 return 1;
242         if (act_add_opt("rw=randwrite"))
243                 return 1;
244
245         return 0;
246 }
247
248 static int act_add_dev(const char *dev)
249 {
250         if (prep)
251                 return act_add_dev_prep(dev);
252
253         if (act_add_opt("runtime=%llus", test_duration))
254                 return 1;
255         if (act_add_opt("time_based=1"))
256                 return 1;
257
258         if (act_add_rw(dev, 1))
259                 return 1;
260         if (act_add_rw(dev, 0))
261                 return 1;
262
263         return 0;
264 }
265
266 /*
267  * Fill our private options into the command line
268  */
269 static int act_prep_cmdline(void)
270 {
271         if (!device_names) {
272                 log_err("act: you need to set IO target(s) with the "
273                         "device-names option.\n");
274                 return 1;
275         }
276
277         org_idx = opt_idx;
278
279         do {
280                 char *dev;
281
282                 dev = strsep(&device_names, ",");
283                 if (!dev)
284                         break;
285
286                 if (act_add_dev(dev)) {
287                         log_err("act: failed adding device to the mix\n");
288                         break;
289                 }
290         } while (1);
291
292         return 0;
293 }
294
295 static int act_io_u_lat(struct thread_data *td, uint64_t usec)
296 {
297         struct act_prof_data *apd = td->prof_data;
298         struct act_slice *slice;
299         int i, ret = 0;
300         double perm;
301
302         if (prep)
303                 return 0;
304
305         /*
306          * Really should not happen, but lets not let jitter at the end
307          * ruin our day.
308          */
309         if (apd->cur_slice >= apd->nr_slices)
310                 return 0;
311
312         slice = &apd->slices[apd->cur_slice];
313         slice->total_ios++;
314
315         for (i = ACT_MAX_CRIT - 1; i >= 0; i--) {
316                 if (usec > act_pass[i].max_usec) {
317                         slice->lat_buckets[i]++;
318                         break;
319                 }
320         }
321
322         if (time_since_now(&apd->sample_tv) < SAMPLE_SEC)
323                 return 0;
324
325         /* SAMPLE_SEC has passed, check criteria for pass */
326         for (i = 0; i < ACT_MAX_CRIT; i++) {
327                 perm = (1000.0 * slice->lat_buckets[i]) / slice->total_ios;
328                 if (perm < act_pass[i].max_perm)
329                         continue;
330
331                 log_err("act: %f%% exceeds pass criteria of %f%%\n", perm / 10.0, (double) act_pass[i].max_perm / 10.0);
332                 ret = 1;
333                 break;
334         }
335
336         fio_gettime(&apd->sample_tv, NULL);
337         apd->cur_slice++;
338         return ret;
339 }
340
341 static void get_act_ref(void)
342 {
343         fio_mutex_down(act_run_data->mutex);
344         act_run_data->pending++;
345         fio_mutex_up(act_run_data->mutex);
346 }
347
348 static int show_slice(struct act_slice *slice, unsigned int slice_num)
349 {
350         unsigned int i, failed = 0;
351
352         log_info("   %2u", slice_num);
353
354         for (i = 0; i < ACT_MAX_CRIT; i++) {
355                 double perc = 0.0;
356
357                 if (slice->total_ios)
358                         perc = 100.0 * (double) slice->lat_buckets[i] / (double) slice->total_ios;
359                 if ((perc * 10.0) >= act_pass[i].max_perm)
360                         failed++;
361                 log_info("\t%2.2f", perc);
362         }
363         for (i = 0; i < ACT_MAX_CRIT; i++) {
364                 double perc = 0.0;
365
366                 if (slice->total_ios)
367                         perc = 100.0 * (double) slice->lat_buckets[i] / (double) slice->total_ios;
368                 log_info("\t%2.2f", perc);
369         }
370         log_info("\n");
371
372         return failed;
373 }
374
375 static void act_show_all_stats(void)
376 {
377         unsigned int i, fails = 0;
378
379         log_info("        trans                   device\n");
380         log_info("        %%>(ms)                  %%>(ms)\n");
381         log_info(" slice");
382
383         for (i = 0; i < ACT_MAX_CRIT; i++)
384                 log_info("\t %2u", act_pass[i].max_usec / 1000);
385         for (i = 0; i < ACT_MAX_CRIT; i++)
386                 log_info("\t %2u", act_pass[i].max_usec / 1000);
387
388         log_info("\n");
389         log_info(" -----  -----   -----  ------   -----   -----  ------\n");
390
391         for (i = 0; i < act_run_data->nr_slices; i++)
392                 fails += show_slice(&act_run_data->slices[i], i + 1);
393
394         log_info("\nact: test complete, device(s): %s\n", fails ? "FAILED" : "PASSED");
395 }
396
397 static void put_act_ref(struct thread_data *td)
398 {
399         struct act_prof_data *apd = td->prof_data;
400         unsigned int i, slice;
401
402         fio_mutex_down(act_run_data->mutex);
403
404         if (!act_run_data->slices) {
405                 act_run_data->slices = calloc(apd->nr_slices, sizeof(struct act_slice));
406                 act_run_data->nr_slices = apd->nr_slices;
407         }
408
409         for (slice = 0; slice < apd->nr_slices; slice++) {
410                 struct act_slice *dst = &act_run_data->slices[slice];
411                 struct act_slice *src = &apd->slices[slice];
412
413                 dst->total_ios += src->total_ios;
414
415                 for (i = 0; i < ACT_MAX_CRIT; i++)
416                         dst->lat_buckets[i] += src->lat_buckets[i];
417         }
418
419         if (!--act_run_data->pending)
420                 act_show_all_stats();
421
422         fio_mutex_up(act_run_data->mutex);
423 }
424
425 static int act_td_init(struct thread_data *td)
426 {
427         struct act_prof_data *apd;
428         unsigned int nr_slices;
429
430         get_act_ref();
431
432         apd = calloc(1, sizeof(*apd));
433         nr_slices = (test_duration + SAMPLE_SEC - 1) / SAMPLE_SEC;
434         apd->slices = calloc(nr_slices, sizeof(struct act_slice));
435         apd->nr_slices = nr_slices;
436         fio_gettime(&apd->sample_tv, NULL);
437         td->prof_data = apd;
438         return 0;
439 }
440
441 static void act_td_exit(struct thread_data *td)
442 {
443         struct act_prof_data *apd = td->prof_data;
444
445         put_act_ref(td);
446         free(apd->slices);
447         free(apd);
448         td->prof_data = NULL;
449 }
450
451 static struct prof_io_ops act_io_ops = {
452         .td_init        = act_td_init,
453         .td_exit        = act_td_exit,
454         .io_u_lat       = act_io_u_lat,
455 };
456
457 static struct profile_ops act_profile = {
458         .name           = "act",
459         .desc           = "ACT Aerospike like benchmark",
460         .options        = options,
461         .opt_data       = &act_options,
462         .prep_cmd       = act_prep_cmdline,
463         .cmdline        = act_opts,
464         .io_ops         = &act_io_ops,
465 };
466
467 static void fio_init act_register(void)
468 {
469         act_run_data = calloc(1, sizeof(*act_run_data));
470         act_run_data->mutex = fio_mutex_init(FIO_MUTEX_UNLOCKED);
471
472         if (register_profile(&act_profile))
473                 log_err("fio: failed to register profile 'act'\n");
474 }
475
476 static void fio_exit act_unregister(void)
477 {
478         while (org_idx && org_idx < opt_idx)
479                 free((void *) act_opts[++org_idx]);
480
481         unregister_profile(&act_profile);
482         fio_mutex_remove(act_run_data->mutex);
483         free(act_run_data->slices);
484         free(act_run_data);
485         act_run_data = NULL;
486 }