X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=profiles%2Fact.c;h=5d3bd2544ed9b714b5b769c2bbe7cf6bcb7c4a09;hp=e5b31d7d2e1dceb3181a8f90e4e1ca730b07d293;hb=ce4d13ca162df4127ec3b5911553802c53396705;hpb=78a6aad70b54fbf6eb63f14c60b292bcf6a5605e diff --git a/profiles/act.c b/profiles/act.c index e5b31d7d..5d3bd254 100644 --- a/profiles/act.c +++ b/profiles/act.c @@ -1,10 +1,11 @@ #include "../fio.h" #include "../profile.h" #include "../parse.h" +#include "../optgroup.h" -#define OBJ_SIZE 1536 /* each object */ -#define W_BUF_SIZE 128 * 1024 /* write coalescing */ - +/* + * 1x loads + */ #define R_LOAD 2000 #define W_LOAD 1000 @@ -31,45 +32,121 @@ static struct act_pass_criteria act_pass[ACT_MAX_CRIT] = { }, }; -struct act_prof_data { - struct timeval sample_tv; +struct act_slice { uint64_t lat_buckets[ACT_MAX_CRIT]; uint64_t total_ios; }; -static char *device_names; -static unsigned int load = 1; +struct act_run_data { + struct fio_sem *sem; + unsigned int pending; + + struct act_slice *slices; + unsigned int nr_slices; +}; +static struct act_run_data *act_run_data; + +struct act_prof_data { + struct timespec sample_tv; + struct act_slice *slices; + unsigned int cur_slice; + unsigned int nr_slices; +}; -static const char *act_opts[128] = { +#define ACT_MAX_OPTS 128 +static const char *act_opts[ACT_MAX_OPTS] = { "direct=1", - "ioengine=libaio", - "iodepth=32", + "ioengine=sync", "random_generator=lfsr", - "runtime=24h", - "time_based=1", + "group_reporting=1", + "thread", NULL, }; -static unsigned int opt_idx = 6; +static unsigned int opt_idx = 5; static unsigned int org_idx; -static void act_add_opt(const char *format, ...) __attribute__ ((__format__ (__printf__, 1, 2))); +static int act_add_opt(const char *format, ...) __attribute__ ((__format__ (__printf__, 1, 2))); + +struct act_options { + unsigned int pad; + char *device_names; + unsigned int load; + unsigned int prep; + unsigned int threads_per_queue; + unsigned int num_read_blocks; + unsigned int write_size; + unsigned long long test_duration; +}; + +static struct act_options act_options; static struct fio_option options[] = { { .name = "device-names", .lname = "device-names", .type = FIO_OPT_STR_STORE, - .roff1 = &device_names, + .off1 = offsetof(struct act_options, device_names), .help = "Devices to use", .category = FIO_OPT_C_PROFILE, .group = FIO_OPT_G_ACT, + .no_free = true, }, { .name = "load", .lname = "Load multiplier", .type = FIO_OPT_INT, - .roff1 = &load, + .off1 = offsetof(struct act_options, load), .help = "ACT load multipler (default 1x)", + .def = "1", + .category = FIO_OPT_C_PROFILE, + .group = FIO_OPT_G_ACT, + }, + { + .name = "test-duration", + .lname = "Test duration", + .type = FIO_OPT_STR_VAL_TIME, + .off1 = offsetof(struct act_options, test_duration), + .help = "How long the entire test takes to run", + .def = "24h", + .category = FIO_OPT_C_PROFILE, + .group = FIO_OPT_G_ACT, + }, + { + .name = "threads-per-queue", + .lname = "Number of read IO threads per device", + .type = FIO_OPT_INT, + .off1 = offsetof(struct act_options, threads_per_queue), + .help = "Number of read IO threads per device", + .def = "8", + .category = FIO_OPT_C_PROFILE, + .group = FIO_OPT_G_ACT, + }, + { + .name = "read-req-num-512-blocks", + .lname = "Number of 512B blocks to read", + .type = FIO_OPT_INT, + .off1 = offsetof(struct act_options, num_read_blocks), + .help = "Number of 512B blocks to read at the time", + .def = "3", + .category = FIO_OPT_C_PROFILE, + .group = FIO_OPT_G_ACT, + }, + { + .name = "large-block-op-kbytes", + .lname = "Size of large block ops in KiB (writes)", + .type = FIO_OPT_INT, + .off1 = offsetof(struct act_options, write_size), + .help = "Size of large block ops in KiB (writes)", + .def = "131072", + .category = FIO_OPT_C_PROFILE, + .group = FIO_OPT_G_ACT, + }, + { + .name = "prep", + .lname = "Run ACT prep phase", + .type = FIO_OPT_STR_SET, + .off1 = offsetof(struct act_options, prep), + .help = "Set to run ACT prep phase", .category = FIO_OPT_C_PROFILE, .group = FIO_OPT_G_ACT, }, @@ -78,31 +155,108 @@ static struct fio_option options[] = { }, }; -static void act_add_opt(const char *str, ...) +static int act_add_opt(const char *str, ...) { char buffer[512]; va_list args; size_t len; + if (opt_idx == ACT_MAX_OPTS) { + log_err("act: ACT_MAX_OPTS is too small\n"); + return 1; + } + va_start(args, str); len = vsnprintf(buffer, sizeof(buffer), str, args); va_end(args); if (len) act_opts[opt_idx++] = strdup(buffer); + + return 0; +} + +static int act_add_rw(const char *dev, int reads) +{ + struct act_options *ao = &act_options; + + if (act_add_opt("name=act-%s-%s", reads ? "read" : "write", dev)) + return 1; + if (act_add_opt("filename=%s", dev)) + return 1; + if (act_add_opt("rw=%s", reads ? "randread" : "randwrite")) + return 1; + if (reads) { + int rload = ao->load * R_LOAD / ao->threads_per_queue; + + if (act_add_opt("numjobs=%u", ao->threads_per_queue)) + return 1; + if (act_add_opt("rate_iops=%u", rload)) + return 1; + if (act_add_opt("bs=%u", ao->num_read_blocks * 512)) + return 1; + } else { + const int rsize = ao->write_size / (ao->num_read_blocks * 512); + int wload = (ao->load * W_LOAD + rsize - 1) / rsize; + + if (act_add_opt("rate_iops=%u", wload)) + return 1; + if (act_add_opt("bs=%u", ao->write_size)) + return 1; + } + + return 0; +} + +static int act_add_dev_prep(const char *dev) +{ + /* Add sequential zero phase */ + if (act_add_opt("name=act-prep-zeroes-%s", dev)) + return 1; + if (act_add_opt("filename=%s", dev)) + return 1; + if (act_add_opt("bs=1048576")) + return 1; + if (act_add_opt("zero_buffers")) + return 1; + if (act_add_opt("rw=write")) + return 1; + + /* Randomly overwrite device */ + if (act_add_opt("name=act-prep-salt-%s", dev)) + return 1; + if (act_add_opt("stonewall")) + return 1; + if (act_add_opt("filename=%s", dev)) + return 1; + if (act_add_opt("bs=4096")) + return 1; + if (act_add_opt("ioengine=libaio")) + return 1; + if (act_add_opt("iodepth=64")) + return 1; + if (act_add_opt("rw=randwrite")) + return 1; + + return 0; } -static void act_add_dev(const char *dev) +static int act_add_dev(const char *dev) { - act_add_opt("name=act-read-%s", dev); - act_add_opt("filename=%s", dev); - act_add_opt("rw=randread"); - act_add_opt("rate_iops=%u", load * R_LOAD); - - act_add_opt("name=act-write-%s", dev); - act_add_opt("filename=%s", dev); - act_add_opt("rw=randwrite"); - act_add_opt("rate_iops=%u", load * W_LOAD); + if (act_options.prep) + return act_add_dev_prep(dev); + + if (act_add_opt("runtime=%llus", act_options.test_duration)) + return 1; + if (act_add_opt("time_based=1")) + return 1; + + if (act_add_rw(dev, 1)) + return 1; + if (act_add_rw(dev, 0)) + return 1; + + return 0; } /* @@ -110,38 +264,54 @@ static void act_add_dev(const char *dev) */ static int act_prep_cmdline(void) { - if (!device_names) { - log_err("act: need device-names\n"); + if (!act_options.device_names) { + log_err("act: you need to set IO target(s) with the " + "device-names option.\n"); return 1; } org_idx = opt_idx; - act_add_opt("bs=%u", OBJ_SIZE); do { char *dev; - dev = strsep(&device_names, ","); + dev = strsep(&act_options.device_names, ","); if (!dev) break; - act_add_dev(dev); + if (act_add_dev(dev)) { + log_err("act: failed adding device to the mix\n"); + break; + } } while (1); return 0; } -static int act_io_u_lat(struct thread_data *td, uint64_t usec) +static int act_io_u_lat(struct thread_data *td, uint64_t nsec) { struct act_prof_data *apd = td->prof_data; + struct act_slice *slice; + uint64_t usec = nsec / 1000ULL; int i, ret = 0; double perm; - apd->total_ios++; + if (act_options.prep) + return 0; + + /* + * Really should not happen, but lets not let jitter at the end + * ruin our day. + */ + if (apd->cur_slice >= apd->nr_slices) + return 0; + + slice = &apd->slices[apd->cur_slice]; + slice->total_ios++; for (i = ACT_MAX_CRIT - 1; i >= 0; i--) { if (usec > act_pass[i].max_usec) { - apd->lat_buckets[i]++; + slice->lat_buckets[i]++; break; } } @@ -151,7 +321,7 @@ static int act_io_u_lat(struct thread_data *td, uint64_t usec) /* SAMPLE_SEC has passed, check criteria for pass */ for (i = 0; i < ACT_MAX_CRIT; i++) { - perm = (1000.0 * apd->lat_buckets[i]) / apd->total_ios; + perm = (1000.0 * slice->lat_buckets[i]) / slice->total_ios; if (perm < act_pass[i].max_perm) continue; @@ -160,18 +330,106 @@ static int act_io_u_lat(struct thread_data *td, uint64_t usec) break; } - memset(apd->lat_buckets, 0, sizeof(apd->lat_buckets)); - apd->total_ios = 0; - fio_gettime(&apd->sample_tv, NULL); + apd->cur_slice++; return ret; } +static void get_act_ref(void) +{ + fio_sem_down(act_run_data->sem); + act_run_data->pending++; + fio_sem_up(act_run_data->sem); +} + +static int show_slice(struct act_slice *slice, unsigned int slice_num) +{ + unsigned int i, failed = 0; + + log_info(" %2u", slice_num); + + for (i = 0; i < ACT_MAX_CRIT; i++) { + double perc = 0.0; + + if (slice->total_ios) + perc = 100.0 * (double) slice->lat_buckets[i] / (double) slice->total_ios; + if ((perc * 10.0) >= act_pass[i].max_perm) + failed++; + log_info("\t%2.2f", perc); + } + for (i = 0; i < ACT_MAX_CRIT; i++) { + double perc = 0.0; + + if (slice->total_ios) + perc = 100.0 * (double) slice->lat_buckets[i] / (double) slice->total_ios; + log_info("\t%2.2f", perc); + } + log_info("\n"); + + return failed; +} + +static void act_show_all_stats(void) +{ + unsigned int i, fails = 0; + + log_info(" trans device\n"); + log_info(" %%>(ms) %%>(ms)\n"); + log_info(" slice"); + + for (i = 0; i < ACT_MAX_CRIT; i++) + log_info("\t %2u", act_pass[i].max_usec / 1000); + for (i = 0; i < ACT_MAX_CRIT; i++) + log_info("\t %2u", act_pass[i].max_usec / 1000); + + log_info("\n"); + log_info(" ----- ----- ----- ------ ----- ----- ------\n"); + + for (i = 0; i < act_run_data->nr_slices; i++) + fails += show_slice(&act_run_data->slices[i], i + 1); + + log_info("\nact: test complete, device(s): %s\n", fails ? "FAILED" : "PASSED"); +} + +static void put_act_ref(struct thread_data *td) +{ + struct act_prof_data *apd = td->prof_data; + unsigned int i, slice; + + fio_sem_down(act_run_data->sem); + + if (!act_run_data->slices) { + act_run_data->slices = calloc(apd->nr_slices, sizeof(struct act_slice)); + act_run_data->nr_slices = apd->nr_slices; + } + + for (slice = 0; slice < apd->nr_slices; slice++) { + struct act_slice *dst = &act_run_data->slices[slice]; + struct act_slice *src = &apd->slices[slice]; + + dst->total_ios += src->total_ios; + + for (i = 0; i < ACT_MAX_CRIT; i++) + dst->lat_buckets[i] += src->lat_buckets[i]; + } + + if (!--act_run_data->pending) + act_show_all_stats(); + + fio_sem_up(act_run_data->sem); +} + static int act_td_init(struct thread_data *td) { struct act_prof_data *apd; + unsigned int nr_slices; + + get_act_ref(); - apd = calloc(sizeof(*apd), 1); + apd = calloc(1, sizeof(*apd)); + nr_slices = (act_options.test_duration + SAMPLE_SEC - 1) / SAMPLE_SEC; + apd->slices = calloc(nr_slices, sizeof(struct act_slice)); + apd->nr_slices = nr_slices; fio_gettime(&apd->sample_tv, NULL); td->prof_data = apd; return 0; @@ -179,7 +437,11 @@ static int act_td_init(struct thread_data *td) static void act_td_exit(struct thread_data *td) { - free(td->prof_data); + struct act_prof_data *apd = td->prof_data; + + put_act_ref(td); + free(apd->slices); + free(apd); td->prof_data = NULL; } @@ -193,6 +455,7 @@ static struct profile_ops act_profile = { .name = "act", .desc = "ACT Aerospike like benchmark", .options = options, + .opt_data = &act_options, .prep_cmd = act_prep_cmdline, .cmdline = act_opts, .io_ops = &act_io_ops, @@ -200,6 +463,9 @@ static struct profile_ops act_profile = { static void fio_init act_register(void) { + act_run_data = calloc(1, sizeof(*act_run_data)); + act_run_data->sem = fio_sem_init(FIO_SEM_UNLOCKED); + if (register_profile(&act_profile)) log_err("fio: failed to register profile 'act'\n"); } @@ -210,4 +476,8 @@ static void fio_exit act_unregister(void) free((void *) act_opts[++org_idx]); unregister_profile(&act_profile); + fio_sem_remove(act_run_data->sem); + free(act_run_data->slices); + free(act_run_data); + act_run_data = NULL; }