+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2004 SuSE, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it would be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * Further, this software is distributed without any warranty that it is
- * free of the rightful claim of any third person regarding infringement
- * or the like. Any license provided herein, whether implied or
- * otherwise, applies only to this software file. Patent licenses, if
- * any, provided herein do not apply to combinations of this program with
- * other software, or any other product whatsoever.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write the Free Software Foundation, Inc., 59
- * Temple Place - Suite 330, Boston MA 02111-1307, USA.
- *
- * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
- * Mountain View, CA 94043, or:
- *
- *
+ */
+
+/*
* aio-stress
*
* will open or create each file on the command line, and start a series
* Please mail Chris Mason (mason@suse.com) with bug reports or patches
*/
#define _FILE_OFFSET_BITS 64
-#define PROG_VERSION "0.18"
+#define PROG_VERSION "0.21"
#define NEW_GETEVENTS
#include <stdio.h>
int o_direct = 0;
int o_sync = 0;
int latency_stats = 0;
+int completion_latency_stats = 0;
int io_iter = 8;
int iterations = RUN_FOREVER;
int max_io_submit = 0;
int stonewall = 1;
int verify = 0;
char *verify_buf = NULL;
+int unlink_files = 0;
struct io_unit;
struct thread_info;
long res;
struct io_unit *next;
+
+ struct timeval io_start_time; /* time of io_submit */
};
struct thread_info {
/* how much io this thread did in the last stage */
double stage_mb_trans;
+
+ /* latency completion stats i/o time from io_submit until io_getevents */
+ struct io_latency io_completion_latency;
};
-static double time_since(struct timeval *tv) {
+/*
+ * return seconds between start_tv and stop_tv in double precision
+ */
+static double time_since(struct timeval *start_tv, struct timeval *stop_tv)
+{
double sec, usec;
double ret;
- struct timeval stop;
- gettimeofday(&stop, NULL);
- sec = stop.tv_sec - tv->tv_sec;
- usec = stop.tv_usec - tv->tv_usec;
+ sec = stop_tv->tv_sec - start_tv->tv_sec;
+ usec = stop_tv->tv_usec - start_tv->tv_usec;
if (sec > 0 && usec < 0) {
sec--;
usec += 1000000;
return ret;
}
-static void calc_latency(struct timeval *tv, struct io_latency *lat)
+/*
+ * return seconds between start_tv and now in double precision
+ */
+static double time_since_now(struct timeval *start_tv)
+{
+ struct timeval stop_time;
+ gettimeofday(&stop_time, NULL);
+ return time_since(start_tv, &stop_time);
+}
+
+/*
+ * Add latency info to latency struct
+ */
+static void calc_latency(struct timeval *start_tv, struct timeval *stop_tv,
+ struct io_latency *lat)
{
double delta;
int i;
- delta = time_since(tv);
+ delta = time_since(start_tv, stop_tv);
delta = delta * 1000;
if (delta > lat->max)
double tput;
double mb;
- runtime = time_since(&oper->start_time);
+ runtime = time_since_now(&oper->start_time);
mb = oper_mb_trans(oper);
tput = mb / runtime;
fprintf(stderr, "%s on %s (%.2f MB/s) %.2f MB in %.2fs\n",
stage_name(oper->rw), oper->file_name, tput, mb, runtime);
}
-static void print_latency(struct thread_info *t) {
- struct io_latency *lat = &t->io_submit_latency;
+static void print_lat(char *str, struct io_latency *lat) {
double avg = lat->total_lat / lat->total_io;
int i;
double total_counted = 0;
- fprintf(stderr, "latency min %.2f avg %.2f max %.2f\n\t",
- lat->min, avg, lat->max);
+ fprintf(stderr, "%s min %.2f avg %.2f max %.2f\n\t",
+ str, lat->min, avg, lat->max);
for (i = 0 ; i < DEVIATIONS ; i++) {
fprintf(stderr, " %.0f < %d", lat->deviations[i], deviations[i]);
if (total_counted && lat->total_io - total_counted)
fprintf(stderr, " < %.0f", lat->total_io - total_counted);
fprintf(stderr, "\n");
- memset(&t->io_submit_latency, 0, sizeof(t->io_submit_latency));
+ memset(lat, 0, sizeof(*lat));
+}
+
+static void print_latency(struct thread_info *t)
+{
+ struct io_latency *lat = &t->io_submit_latency;
+ print_lat("latency", lat);
+}
+
+static void print_completion_latency(struct thread_info *t)
+{
+ struct io_latency *lat = &t->io_completion_latency;
+ print_lat("completion latency", lat);
}
/*
* updates the fields in the io operation struct that belongs to this
* io unit, and make the io unit reusable again
*/
-void finish_io(struct thread_info *t, struct io_unit *io, long result) {
+void finish_io(struct thread_info *t, struct io_unit *io, long result,
+ struct timeval *tv_now) {
struct io_oper *oper = io->io_oper;
+ calc_latency(&io->io_start_time, tv_now, &t->io_completion_latency);
io->res = result;
io->busy = IO_FREE;
io->next = t->free_ious;
int nr;
int i;
int min_nr = io_iter;
+ struct timeval stop_time;
if (t->num_global_pending < io_iter)
min_nr = t->num_global_pending;
if (nr <= 0)
return nr;
+ gettimeofday(&stop_time, NULL);
for (i = 0 ; i < nr ; i++) {
event = t->events + i;
event_io = (struct io_unit *)((unsigned long)event->obj);
- finish_io(t, event_io, event->res);
+ finish_io(t, event_io, event->res, &stop_time);
}
return nr;
}
#else
while(io_getevents(t->io_ctx, 1, &event, NULL) > 0) {
#endif
+ struct timeval tv_now;
event_io = (struct io_unit *)((unsigned long)event.obj);
- finish_io(t, event_io, event.res);
+ gettimeofday(&tv_now, NULL);
+ finish_io(t, event_io, event.res, &tv_now);
if (oper->num_pending == 0)
break;
* runs through the iocbs in the array provided and updates
* counters in the associated oper struct
*/
-static void update_iou_counters(struct iocb **my_iocbs, int nr)
+static void update_iou_counters(struct iocb **my_iocbs, int nr,
+ struct timeval *tv_now)
{
struct io_unit *io;
int i;
io = (struct io_unit *)(my_iocbs[i]);
io->io_oper->num_pending++;
io->io_oper->started_ios++;
+ io->io_start_time = *tv_now; /* set time of io_submit */
}
}
{
int ret;
struct timeval start_time;
+ struct timeval stop_time;
resubmit:
gettimeofday(&start_time, NULL);
ret = io_submit(t->io_ctx, num_ios, my_iocbs);
- calc_latency(&start_time, &t->io_submit_latency);
+ gettimeofday(&stop_time, NULL);
+ calc_latency(&start_time, &stop_time, &t->io_submit_latency);
+
if (ret != num_ios) {
/* some ios got through */
if (ret > 0) {
- update_iou_counters(my_iocbs, ret);
+ update_iou_counters(my_iocbs, ret, &stop_time);
my_iocbs += ret;
t->num_global_pending += ret;
num_ios -= ret;
* retry
*/
if (ret > 0 || ret == -EAGAIN) {
+ int old_ret = ret;
if ((ret = read_some_events(t) > 0)) {
goto resubmit;
+ } else {
+ fprintf(stderr, "ret was %d and now is %d\n", ret, old_ret);
+ abort();
}
}
fprintf(stderr, "ret %d (%s) on io_submit\n", ret, strerror(-ret));
return -1;
}
- update_iou_counters(my_iocbs, ret);
+ update_iou_counters(my_iocbs, ret, &stop_time);
t->num_global_pending += ret;
return 0;
}
*/
void global_thread_throughput(struct thread_info *t, char *this_stage) {
int i;
- double runtime = time_since(&global_stage_start_time);
+ double runtime = time_since_now(&global_stage_start_time);
double total_mb = 0;
double min_trans = 0;
aio_setup(&t->io_ctx, 512);
restart:
- printf("Starting %s iter:%d \n", __FUNCTION__,iteration);
if (num_threads > 1) {
- printf("num_threads %d \n", num_threads);
pthread_mutex_lock(&stage_mutex);
- threads_starting++;
- if (threads_starting == num_threads) {
- threads_ending = 0;
- gettimeofday(&global_stage_start_time, NULL);
- pthread_cond_broadcast(&stage_cond);
- }
- while (threads_starting != num_threads)
- pthread_cond_wait(&stage_cond, &stage_mutex);
+ threads_starting++;
+ if (threads_starting == num_threads) {
+ threads_ending = 0;
+ gettimeofday(&global_stage_start_time, NULL);
+ pthread_cond_broadcast(&stage_cond);
+ }
+ while (threads_starting != num_threads)
+ pthread_cond_wait(&stage_cond, &stage_mutex);
pthread_mutex_unlock(&stage_mutex);
}
if (t->active_opers) {
-// printf("active_opers %p line:%d\n", t->active_opers, __LINE__);
this_stage = stage_name(t->active_opers->rw);
- gettimeofday(&stage_time, NULL);
- t->stage_mb_trans = 0;
+ gettimeofday(&stage_time, NULL);
+ t->stage_mb_trans = 0;
}
+
cnt = 0;
/* first we send everything through aio */
-// printf("cnt:%d max_iterations:%d oper:%p\n",cnt, iterations,oper);
-
- while (t->active_opers && (cnt < iterations || iterations == RUN_FOREVER)) {
-// printf("active_opers %p line:%d cnt:%d ", t->active_opers,__LINE__,cnt);
- if (stonewall && threads_ending) {
- oper = t->active_opers;
- oper->stonewalled = 1;
- oper_list_del(oper, &t->active_opers);
- oper_list_add(oper, &t->finished_opers);
-// printf(" if branch\n");
- } else {
- run_active_list(t, io_iter, max_io_submit);
-// printf(" else branch\n");
+ while(t->active_opers && (cnt < iterations || iterations == RUN_FOREVER)) {
+ if (stonewall && threads_ending) {
+ oper = t->active_opers;
+ oper->stonewalled = 1;
+ oper_list_del(oper, &t->active_opers);
+ oper_list_add(oper, &t->finished_opers);
+ } else {
+ run_active_list(t, io_iter, max_io_submit);
}
- cnt++;
+ cnt++;
}
-
if (latency_stats)
print_latency(t);
+ if (completion_latency_stats)
+ print_completion_latency(t);
+
/* then we wait for all the operations to finish */
oper = t->finished_opers;
-// printf("line:%d oper:%p\n", __LINE__, oper);
do {
- io_oper_wait(t, oper);
- if (oper != NULL) {
- oper = oper->next;
- }
- } while (oper != t->finished_opers);
-// printf("finished_opers %p line:%d\n", t->finished_opers,__LINE__);
+ if (!oper)
+ break;
+ io_oper_wait(t, oper);
+ oper = oper->next;
+ } while(oper != t->finished_opers);
/* then we do an fsync to get the timing for any future operations
* right, and check to see if any of these need to get restarted
*/
oper = t->finished_opers;
-// printf("oper %p line:%d\n", oper,__LINE__);
- while (oper) {
- if (fsync_stages)
+ while(oper) {
+ if (fsync_stages)
fsync(oper->fd);
- t->stage_mb_trans += oper_mb_trans(oper);
- if (restart_oper(oper)) {
- oper_list_del(oper, &t->finished_opers);
- oper_list_add(oper, &t->active_opers);
- oper = t->finished_opers;
- continue;
- }
- oper = oper->next;
- if (oper == t->finished_opers)
- break;
+ t->stage_mb_trans += oper_mb_trans(oper);
+ if (restart_oper(oper)) {
+ oper_list_del(oper, &t->finished_opers);
+ oper_list_add(oper, &t->active_opers);
+ oper = t->finished_opers;
+ continue;
+ }
+ oper = oper->next;
+ if (oper == t->finished_opers)
+ break;
}
if (t->stage_mb_trans && t->num_files > 0) {
-// printf("num_files %d line:%d\n", t->num_files,__LINE__);
- double seconds = time_since(&stage_time);
- fprintf(stderr, "thread %d %s totals (%.2f MB/s) %.2f MB in %.2fs\n",
- t - global_thread_info, this_stage, t->stage_mb_trans/seconds,
- t->stage_mb_trans, seconds);
+ double seconds = time_since_now(&stage_time);
+ fprintf(stderr, "thread %llu %s totals (%.2f MB/s) %.2f MB in %.2fs\n",
+ (unsigned long long)(t - global_thread_info), this_stage,
+ t->stage_mb_trans/seconds, t->stage_mb_trans, seconds);
}
if (num_threads > 1) {
-// printf("num_threads %d line:%d\n", num_threads,__LINE__);
- pthread_mutex_lock(&stage_mutex);
- threads_ending++;
- if (threads_ending == num_threads) {
- threads_starting = 0;
- pthread_cond_broadcast(&stage_cond);
- global_thread_throughput(t, this_stage);
- }
-// printf("threads_ending %d line:%d\n", threads_ending,__LINE__);
- while (threads_ending != num_threads)
- pthread_cond_wait(&stage_cond, &stage_mutex);
- pthread_mutex_unlock(&stage_mutex);
+ pthread_mutex_lock(&stage_mutex);
+ threads_ending++;
+ if (threads_ending == num_threads) {
+ threads_starting = 0;
+ pthread_cond_broadcast(&stage_cond);
+ global_thread_throughput(t, this_stage);
+ }
+ while(threads_ending != num_threads)
+ pthread_cond_wait(&stage_cond, &stage_mutex);
+ pthread_mutex_unlock(&stage_mutex);
}
-
+
/* someone got restarted, go back to the beginning */
if (t->active_opers && (cnt < iterations || iterations == RUN_FOREVER)) {
- iteration++;
+ iteration++;
goto restart;
}
/* finally, free all the ram */
-// printf("finished_opers %p line:%d\n", t->finished_opers,__LINE__);
- while (t->finished_opers) {
- oper = t->finished_opers;
- oper_list_del(oper, &t->finished_opers);
- status = finish_oper(t, oper);
+ while(t->finished_opers) {
+ oper = t->finished_opers;
+ oper_list_del(oper, &t->finished_opers);
+ status = finish_oper(t, oper);
}
if (t->num_global_pending) {
fprintf(stderr, "global num pending is %d\n", t->num_global_pending);
}
io_queue_release(t->io_ctx);
-
+
return status;
}
int run_workers(struct thread_info *t, int num_threads)
{
int ret;
- int thread_ret;
int i;
-// printf("%s num_threads %d line:%d\n", __FUNCTION__,num_threads,__LINE__);
for(i = 0 ; i < num_threads ; i++) {
ret = pthread_create(&t[i].tid, NULL, (start_routine)worker, t + i);
if (ret) {
}
}
for(i = 0 ; i < num_threads ; i++) {
- ret = pthread_join(t[i].tid, (void *)&thread_ret);
+ ret = pthread_join(t[i].tid, NULL);
if (ret) {
perror("pthread_join");
exit(1);
case 'K':
mult = 1024;
break;
+ case 'b':
+ case 'B':
+ mult = 1;
+ break;
}
ret = mult * num;
return ret;
printf("\t-m shmfs mmap a file in /dev/shm for io buffers\n");
printf("\t-n no fsyncs between write stage and read stage\n");
printf("\t-l print io_submit latencies after each stage\n");
+ printf("\t-L print io completion latencies after each stage\n");
printf("\t-t number of threads to run\n");
+ printf("\t-u unlink files after completion\n");
printf("\t-v verification of bytes written\n");
printf("\t-x turn off thread stonewalling\n");
printf("\t-h this message\n");
page_size_mask = getpagesize() - 1;
while(1) {
- c = getopt(ac, av, "a:b:c:C:m:s:r:d:i:I:o:t:lnhOSxv");
+ c = getopt(ac, av, "a:b:c:C:m:s:r:d:i:I:o:t:lLnhOSxvu");
if (c < 0)
break;
case 'l':
latency_stats = 1;
break;
+ case 'L':
+ completion_latency_stats = 1;
+ break;
case 'm':
if (!strcmp(optarg, "shm")) {
fprintf(stderr, "using ipc shm\n");
case 'x':
stonewall = 0;
break;
+ case 'u':
+ unlink_files = 1;
+ break;
case 'v':
verify = 1;
break;
num_threads);
}
- t = malloc(num_threads * sizeof(*t));
+ t = calloc(num_threads, sizeof(*t));
if (!t) {
- perror("malloc");
+ perror("calloc");
exit(1);
}
global_thread_info = t;
if (file_size < num_contexts * context_offset) {
fprintf(stderr, "file size %Lu too small for %d contexts\n",
- file_size, num_contexts);
+ (unsigned long long)file_size, num_contexts);
exit(1);
}
- fprintf(stderr, "file size %LuMB, record size %luKB, depth %d, ios per iteration %d\n", file_size / (1024 * 1024), rec_len / 1024, depth, io_iter);
+ fprintf(stderr, "file size %LuMB, record size %luKB, depth %d, ios per iteration %d\n",
+ (unsigned long long)file_size / (1024 * 1024),
+ rec_len / 1024, depth, io_iter);
fprintf(stderr, "max io_submit %d, buffer alignment set to %luKB\n",
max_io_submit, (page_size_mask + 1)/1024);
fprintf(stderr, "threads %d files %d contexts %d context offset %LuMB verification %s\n",
num_threads, num_files, num_contexts,
- context_offset / (1024 * 1024), verify ? "on" : "off");
+ (unsigned long long)context_offset / (1024 * 1024),
+ verify ? "on" : "off");
/* open all the files and do any required setup for them */
for (i = optind ; i < ac ; i++) {
int thread_index;
for (j = 0 ; j < num_contexts ; j++) {
thread_index = open_fds % num_threads;
open_fds++;
-// fprintf(stderr, "adding file %s thread %d\n", av[i], thread_index);
rwfd = open(av[i], O_CREAT | O_RDWR | o_direct | o_sync, 0600);
assert(rwfd != -1);
if (num_threads > 1){
printf("Running multi thread version num_threads:%d\n", num_threads);
run_workers(t, num_threads);
- }
- else {
+ } else {
printf("Running single thread version \n");
- status = worker(t);
+ status = worker(t);
}
-
-
- for (i = optind ; i < ac ; i++) {
- printf("Cleaning up file %s \n", av[i]);
- unlink(av[i]);
+ if (unlink_files) {
+ for (i = optind ; i < ac ; i++) {
+ printf("Cleaning up file %s \n", av[i]);
+ unlink(av[i]);
+ }
}
if (status) {
- printf("non zero return %d \n", status);
- }
- else{
- printf("aio-stress Completed successfully %d \n", status);
+ exit(1);
}
-
- exit(0);
+ return status;
}
+