act: bring closer to real ACT
[fio.git] / profiles / act.c
CommitLineData
d4afedfd
JA
1#include "../fio.h"
2#include "../profile.h"
3#include "../parse.h"
4
00f81c37
JA
5/*
6 * 1x loads
7 */
d4afedfd
JA
8#define R_LOAD 2000
9#define W_LOAD 1000
10
11#define SAMPLE_SEC 3600 /* 1h checks */
12
13struct act_pass_criteria {
14 unsigned int max_usec;
15 unsigned int max_perm;
16};
17#define ACT_MAX_CRIT 3
18
19static struct act_pass_criteria act_pass[ACT_MAX_CRIT] = {
20 {
21 .max_usec = 1000,
22 .max_perm = 50,
23 },
24 {
78a6aad7 25 .max_usec = 8000,
d4afedfd
JA
26 .max_perm = 10,
27 },
28 {
29 .max_usec = 64000,
30 .max_perm = 1,
31 },
32};
33
34struct act_prof_data {
35 struct timeval sample_tv;
36 uint64_t lat_buckets[ACT_MAX_CRIT];
37 uint64_t total_ios;
38};
39
40static char *device_names;
41static unsigned int load = 1;
00f81c37
JA
42static unsigned int prep;
43static unsigned int threads_per_queue;
44static unsigned int num_read_blocks;
45static unsigned int write_size;
d4afedfd 46
00f81c37
JA
47#define ACT_MAX_OPTS 128
48static const char *act_opts[ACT_MAX_OPTS] = {
d4afedfd 49 "direct=1",
00f81c37 50 "ioengine=sync",
d4afedfd 51 "random_generator=lfsr",
00f81c37 52 "group_reporting=1",
d4afedfd
JA
53 NULL,
54};
00f81c37 55static unsigned int opt_idx = 4;
d4afedfd
JA
56static unsigned int org_idx;
57
00f81c37 58static int act_add_opt(const char *format, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
d4afedfd
JA
59
60static struct fio_option options[] = {
61 {
62 .name = "device-names",
63 .lname = "device-names",
64 .type = FIO_OPT_STR_STORE,
65 .roff1 = &device_names,
66 .help = "Devices to use",
67 .category = FIO_OPT_C_PROFILE,
68 .group = FIO_OPT_G_ACT,
69 },
70 {
71 .name = "load",
72 .lname = "Load multiplier",
73 .type = FIO_OPT_INT,
74 .roff1 = &load,
75 .help = "ACT load multipler (default 1x)",
00f81c37
JA
76 .def = "1",
77 .category = FIO_OPT_C_PROFILE,
78 .group = FIO_OPT_G_ACT,
79 },
80 {
81 .name = "threads-per-queue",
82 .lname = "Number of read IO threads per device",
83 .type = FIO_OPT_INT,
84 .roff1 = &threads_per_queue,
85 .help = "Number of read IO threads per device",
86 .def = "8",
87 .category = FIO_OPT_C_PROFILE,
88 .group = FIO_OPT_G_ACT,
89 },
90 {
91 .name = "read-req-num-512-blocks",
92 .lname = "Number of 512b blocks to read",
93 .type = FIO_OPT_INT,
94 .roff1 = &num_read_blocks,
95 .help = "Number of 512b blocks to read at the time",
96 .def = "3",
97 .category = FIO_OPT_C_PROFILE,
98 .group = FIO_OPT_G_ACT,
99 },
100 {
101 .name = "large-block-op-kbytes",
102 .lname = "Size of large block ops (writes)",
103 .type = FIO_OPT_INT,
104 .roff1 = &write_size,
105 .help = "Size of large block ops (writes)",
106 .def = "128k",
107 .category = FIO_OPT_C_PROFILE,
108 .group = FIO_OPT_G_ACT,
109 },
110 {
111 .name = "prep",
112 .lname = "Run ACT prep phase",
113 .type = FIO_OPT_STR_SET,
114 .roff1 = &prep,
115 .help = "Set to run ACT prep phase",
d4afedfd
JA
116 .category = FIO_OPT_C_PROFILE,
117 .group = FIO_OPT_G_ACT,
118 },
119 {
120 .name = NULL,
121 },
122};
123
00f81c37 124static int act_add_opt(const char *str, ...)
d4afedfd
JA
125{
126 char buffer[512];
127 va_list args;
128 size_t len;
129
00f81c37
JA
130 if (opt_idx == ACT_MAX_OPTS) {
131 log_err("act: ACT_MAX_OPTS is too small\n");
132 return 1;
133 }
134
d4afedfd
JA
135 va_start(args, str);
136 len = vsnprintf(buffer, sizeof(buffer), str, args);
137 va_end(args);
138
139 if (len)
140 act_opts[opt_idx++] = strdup(buffer);
00f81c37
JA
141
142 return 0;
143}
144
145static int act_add_rw(const char *dev, int reads)
146{
147 if (act_add_opt("name=act-%s-%s", reads ? "read" : "write", dev))
148 return 1;
149 if (act_add_opt("filename=%s", dev))
150 return 1;
151 if (act_add_opt("rw=%s", reads ? "randread" : "randwrite"))
152 return 1;
153 if (reads) {
154 int rload = load * R_LOAD / threads_per_queue;
155
156 if (act_add_opt("numjobs=%u", threads_per_queue))
157 return 1;
158 if (act_add_opt("rate_iops=%u", rload))
159 return 1;
160 if (act_add_opt("bs=%u", num_read_blocks * 512))
161 return 1;
162 } else {
163 const int rsize = write_size / (num_read_blocks * 512);
164 int wload = (load * W_LOAD + rsize - 1) / rsize;
165
166 if (act_add_opt("rate_iops=%u", wload))
167 return 1;
168 if (act_add_opt("bs=%u", write_size))
169 return 1;
170 }
171
172 return 0;
173}
174
175static int act_add_dev_prep(const char *dev)
176{
177 /* Add sequential zero phase */
178 if (act_add_opt("name=act-prep-zeroes-%s", dev))
179 return 1;
180 if (act_add_opt("filename=%s", dev))
181 return 1;
182 if (act_add_opt("bs=1M"))
183 return 1;
184 if (act_add_opt("zero_buffers"))
185 return 1;
186 if (act_add_opt("rw=write"))
187 return 1;
188
189 /* Randomly overwrite device */
190 if (act_add_opt("name=act-prep-salt-%s", dev))
191 return 1;
192 if (act_add_opt("stonewall"))
193 return 1;
194 if (act_add_opt("filename=%s", dev))
195 return 1;
196 if (act_add_opt("bs=4k"))
197 return 1;
198 if (act_add_opt("ioengine=libaio"))
199 return 1;
200 if (act_add_opt("iodepth=64"))
201 return 1;
202 if (act_add_opt("rw=randwrite"))
203 return 1;
204
205 return 0;
d4afedfd
JA
206}
207
00f81c37 208static int act_add_dev(const char *dev)
d4afedfd 209{
00f81c37
JA
210 if (prep)
211 return act_add_dev_prep(dev);
212
213 if (act_add_opt("runtime=24h"))
214 return 1;
215 if (act_add_opt("time_based=1"))
216 return 1;
217
218 if (act_add_rw(dev, 1))
219 return 1;
220 if (act_add_rw(dev, 0))
221 return 1;
222
223 return 0;
d4afedfd
JA
224}
225
226/*
227 * Fill our private options into the command line
228 */
229static int act_prep_cmdline(void)
230{
231 if (!device_names) {
232 log_err("act: need device-names\n");
233 return 1;
234 }
235
236 org_idx = opt_idx;
d4afedfd
JA
237
238 do {
239 char *dev;
240
241 dev = strsep(&device_names, ",");
242 if (!dev)
243 break;
244
00f81c37
JA
245 if (act_add_dev(dev)) {
246 log_err("act: failed adding device to the mix\n");
247 break;
248 }
d4afedfd
JA
249 } while (1);
250
251 return 0;
252}
253
254static int act_io_u_lat(struct thread_data *td, uint64_t usec)
255{
256 struct act_prof_data *apd = td->prof_data;
257 int i, ret = 0;
258 double perm;
259
00f81c37
JA
260 if (prep)
261 return 0;
262
d4afedfd
JA
263 apd->total_ios++;
264
78a6aad7
JA
265 for (i = ACT_MAX_CRIT - 1; i >= 0; i--) {
266 if (usec > act_pass[i].max_usec) {
d4afedfd
JA
267 apd->lat_buckets[i]++;
268 break;
269 }
270 }
271
d4afedfd
JA
272 if (time_since_now(&apd->sample_tv) < SAMPLE_SEC)
273 return 0;
274
275 /* SAMPLE_SEC has passed, check criteria for pass */
276 for (i = 0; i < ACT_MAX_CRIT; i++) {
277 perm = (1000.0 * apd->lat_buckets[i]) / apd->total_ios;
78a6aad7 278 if (perm < act_pass[i].max_perm)
d4afedfd
JA
279 continue;
280
281 log_err("act: %f%% exceeds pass criteria of %f%%\n", perm / 10.0, (double) act_pass[i].max_perm / 10.0);
282 ret = 1;
283 break;
284 }
285
78a6aad7
JA
286 memset(apd->lat_buckets, 0, sizeof(apd->lat_buckets));
287 apd->total_ios = 0;
288
d4afedfd
JA
289 fio_gettime(&apd->sample_tv, NULL);
290 return ret;
291}
292
293static int act_td_init(struct thread_data *td)
294{
295 struct act_prof_data *apd;
296
297 apd = calloc(sizeof(*apd), 1);
298 fio_gettime(&apd->sample_tv, NULL);
299 td->prof_data = apd;
300 return 0;
301}
302
303static void act_td_exit(struct thread_data *td)
304{
305 free(td->prof_data);
306 td->prof_data = NULL;
307}
308
309static struct prof_io_ops act_io_ops = {
310 .td_init = act_td_init,
311 .td_exit = act_td_exit,
312 .io_u_lat = act_io_u_lat,
313};
314
315static struct profile_ops act_profile = {
316 .name = "act",
317 .desc = "ACT Aerospike like benchmark",
318 .options = options,
319 .prep_cmd = act_prep_cmdline,
320 .cmdline = act_opts,
321 .io_ops = &act_io_ops,
322};
323
324static void fio_init act_register(void)
325{
326 if (register_profile(&act_profile))
327 log_err("fio: failed to register profile 'act'\n");
328}
329
330static void fio_exit act_unregister(void)
331{
332 while (org_idx && org_idx < opt_idx)
333 free((void *) act_opts[++org_idx]);
334
335 unregister_profile(&act_profile);
336}