• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
act_add_opt(const char * str,...)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 
act_add_rw(const char * dev,int reads)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 
act_add_dev_prep(const char * dev)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 
act_add_dev(const char * dev)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  */
act_prep_cmdline(void)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 
act_io_u_lat(struct thread_data * td,uint64_t usec)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 
get_act_ref(void)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 
show_slice(struct act_slice * slice,unsigned int slice_num)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 
act_show_all_stats(void)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 
put_act_ref(struct thread_data * td)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 
act_td_init(struct thread_data * td)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 
act_td_exit(struct thread_data * td)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 
act_register(void)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 
act_unregister(void)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 }
487