#include <asm/types.h>
#include <asm/bitops.h>
+#include "arch.h"
#include "list.h"
#include "md5.h"
#define MAX_JOBS (1024)
-/*
- * assume we don't have _get either, if _set isn't defined
- */
-#ifndef __NR_ioprio_set
-#if defined(__i386__)
-#define __NR_ioprio_set 289
-#define __NR_ioprio_get 290
-#elif defined(__powerpc__) || defined(__powerpc64__)
-#define __NR_ioprio_set 273
-#define __NR_ioprio_get 274
-#elif defined(__x86_64__)
-#define __NR_ioprio_set 251
-#define __NR_ioprio_get 252
-#elif defined(__ia64__)
-#define __NR_ioprio_set 1274
-#define __NR_ioprio_get 1275
-#elif defined(__alpha__)
-#define __NR_ioprio_set 442
-#define __NR_ioprio_get 443
-#elif defined(__s390x__) || defined(__s390__)
-#define __NR_ioprio_set 282
-#define __NR_ioprio_get 283
-#else
-#error "Unsupported arch"
-#endif
-#endif
-
-#ifndef __NR_fadvise64
-#if defined(__i386__)
-#define __NR_fadvise64 250
-#elif defined(__powerpc__) || defined(__powerpc64__)
-#define __NR_fadvise64 233
-#elif defined(__x86_64__)
-#define __NR_fadvise64 221
-#elif defined(__ia64__)
-#define __NR_fadvise64 1234
-#elif defined(__alpha__)
-#define __NR_fadvise64 413
-#elif defined(__s390x__) || defined(__s390__)
-#define __NR_fadvise64 253
-#else
-#error "Unsupported arch"
-#endif
-#endif
-
static int ioprio_set(int which, int who, int ioprio)
{
return syscall(__NR_ioprio_set, which, who, ioprio);
#define DEF_CREATE_FSYNC (1)
#define DEF_LOOPS (1)
#define DEF_VERIFY (0)
+#define DEF_STONEWALL (0)
+#define DEF_NUMJOBS (1)
#define ALIGN(buf) (char *) (((unsigned long) (buf) + MASK) & ~(MASK))
#define should_fsync(td) (td_write(td) && !(td)->odirect)
#define BLOCKS_PER_MAP (8 * sizeof(long))
-#define RAND_MAP_IDX(sector) ((sector) / BLOCKS_PER_MAP)
-#define RAND_MAP_BIT(sector) ((sector) & (BLOCKS_PER_MAP - 1))
+#define TO_MAP_BLOCK(td, b) ((b) - ((td)->file_offset / (td)->min_bs))
+#define RAND_MAP_IDX(td, b) (TO_MAP_BLOCK(td, b) / BLOCKS_PER_MAP)
+#define RAND_MAP_BIT(td, b) (TO_MAP_BLOCK(td, b) & (BLOCKS_PER_MAP - 1))
struct thread_data {
char file_name[256];
+ char directory[256];
int thread_number;
int error;
int fd;
unsigned int sync_io;
unsigned int mem_type;
unsigned int verify;
+ unsigned int stonewall;
+ unsigned int numjobs;
cpu_set_t cpumask;
struct drand48_data bsrange_state;
struct io_log *bw_log;
struct timeval start;
+ struct rusage ru_start;
+ struct rusage ru_end;
struct list_head io_hist_list;
};
return sec + usec;
}
+static unsigned long utime_since_now(struct timeval *s)
+{
+ struct timeval t;
+
+ gettimeofday(&t, NULL);
+ return utime_since(s, &t);
+}
+
static unsigned long mtime_since(struct timeval *s, struct timeval *e)
{
double sec, usec;
static int random_map_free(struct thread_data *td, unsigned long long block)
{
- unsigned int idx = RAND_MAP_IDX(block);
- unsigned int bit = RAND_MAP_BIT(block);
+ unsigned int idx = RAND_MAP_IDX(td, block);
+ unsigned int bit = RAND_MAP_BIT(td, block);
return (td->file_map[idx] & (1UL << bit)) == 0;
}
if (!random_map_free(td, block))
break;
- idx = RAND_MAP_IDX(block);
- bit = RAND_MAP_BIT(block);
+ idx = RAND_MAP_IDX(td, block);
+ bit = RAND_MAP_BIT(td, block);
assert(idx < td->num_maps);
static int get_next_offset(struct thread_data *td, unsigned long long *offset)
{
- unsigned long long b;
+ unsigned long long b, rb;
long r;
if (!td->sequential) {
do {
lrand48_r(&td->random_state, &r);
b = ((max_blocks - 1) * r / (RAND_MAX+1.0));
+ rb = b + (td->file_offset / td->min_bs);
loops--;
- } while (!random_map_free(td, b) && loops);
+ } while (!random_map_free(td, rb) && loops);
if (!loops) {
if (get_next_free_block(td, &b))
b = td->last_bytes / td->min_bs;
*offset = (b * td->min_bs) + td->file_offset;
+ if (*offset > td->file_size)
+ return 1;
+
return 0;
}
td->stat_io_bytes = td->this_io_bytes;
}
+/*
+ * busy looping version for the last few usec
+ */
+static void __usec_sleep(int usec)
+{
+ struct timeval start;
+
+ gettimeofday(&start, NULL);
+ while (utime_since_now(&start) < usec)
+ __asm__ __volatile__("rep;nop": : :"memory");
+}
+
static void usec_sleep(int usec)
{
struct timespec req = { .tv_sec = 0, .tv_nsec = usec * 1000 };
struct timespec rem;
do {
+ if (usec < 5000) {
+ __usec_sleep(usec);
+ break;
+ }
rem.tv_sec = rem.tv_nsec = 0;
nanosleep(&req, &rem);
if (!rem.tv_nsec)
break;
req.tv_nsec = rem.tv_nsec;
+ usec = rem.tv_nsec * 1000;
} while (1);
}
return NULL;
}
- if (io_u->buflen + io_u->offset > td->io_size)
- io_u->buflen = td->io_size - io_u->offset;
+ if (io_u->buflen + io_u->offset > td->file_size)
+ io_u->buflen = td->file_size - io_u->offset;
if (!td->sequential)
mark_random_map(td, io_u);
gettimeofday(&td->start, NULL);
+ getrusage(RUSAGE_SELF, &td->ru_start);
+
while (td->loops--) {
gettimeofday(&td->stat_sample_time, NULL);
}
td->runtime = mtime_since_now(&td->start);
+ getrusage(RUSAGE_SELF, &td->ru_end);
ret = 0;
if (td->bw_log)
static void show_thread_status(struct thread_data *td)
{
int prio, prio_class;
- unsigned long min, max, bw = 0;
- double mean, dev;
+ unsigned long min, max, bw = 0, ctx;
+ double mean, dev, usr_cpu, sys_cpu;
if (!td->io_bytes && !td->error)
return;
printf(" clat (msec): min=%5lu, max=%5lu, avg=%5.02f, dev=%5.02f\n", min, max, mean, dev);
if (calc_lat(&td->bw_stat, &min, &max, &mean, &dev))
printf(" bw (KiB/s) : min=%5lu, max=%5lu, avg=%5.02f, dev=%5.02f\n", min, max, mean, dev);
+
+ if (td->runtime) {
+ unsigned long t;
+
+ t = mtime_since(&td->ru_start.ru_utime, &td->ru_end.ru_utime);
+ usr_cpu = (double) t * 100 / (double) td->runtime;
+
+ t = mtime_since(&td->ru_start.ru_stime, &td->ru_end.ru_stime);
+ sys_cpu = (double) t * 100 / (double) td->runtime;
+ } else {
+ usr_cpu = 0;
+ sys_cpu = 0;
+ }
+
+ ctx = td->ru_end.ru_nvcsw + td->ru_end.ru_nivcsw - (td->ru_start.ru_nvcsw + td->ru_start.ru_nivcsw);
+
+ printf(" cpu : usr=%3.2f%%, sys=%3.2f%%, ctx=%lu\n", usr_cpu, sys_cpu, ctx);
}
static int setup_rate(struct thread_data *td)
return 0;
}
-static struct thread_data *get_new_job(int global)
+static struct thread_data *get_new_job(int global, struct thread_data *parent)
{
struct thread_data *td;
td = &threads[thread_number++];
memset(td, 0, sizeof(*td));
+ sprintf(td->directory, ".");
+
td->fd = -1;
td->thread_number = thread_number;
- td->ddir = def_thread.ddir;
- td->ioprio = def_thread.ioprio;
- td->sequential = def_thread.sequential;
- td->bs = def_thread.bs;
- td->min_bs = def_thread.min_bs;
- td->max_bs = def_thread.max_bs;
- td->odirect = def_thread.odirect;
- td->thinktime = def_thread.thinktime;
- td->fsync_blocks = def_thread.fsync_blocks;
- td->start_delay = def_thread.start_delay;
- td->timeout = def_thread.timeout;
- td->use_aio = def_thread.use_aio;
- td->create_file = def_thread.create_file;
- td->overwrite = def_thread.overwrite;
- td->invalidate_cache = def_thread.invalidate_cache;
- td->file_size = def_thread.file_size;
- td->file_offset = def_thread.file_offset;
- td->rate = def_thread.rate;
- td->ratemin = def_thread.ratemin;
- td->ratecycle = def_thread.ratecycle;
- td->aio_depth = def_thread.aio_depth;
- td->sync_io = def_thread.sync_io;
- td->mem_type = def_thread.mem_type;
- td->bw_avg_time = def_thread.bw_avg_time;
- td->create_serialize = def_thread.create_serialize;
- td->create_fsync = def_thread.create_fsync;
- td->loops = def_thread.loops;
- td->verify = def_thread.verify;
- memcpy(&td->cpumask, &def_thread.cpumask, sizeof(td->cpumask));
+ td->ddir = parent->ddir;
+ td->ioprio = parent->ioprio;
+ td->sequential = parent->sequential;
+ td->bs = parent->bs;
+ td->min_bs = parent->min_bs;
+ td->max_bs = parent->max_bs;
+ td->odirect = parent->odirect;
+ td->thinktime = parent->thinktime;
+ td->fsync_blocks = parent->fsync_blocks;
+ td->start_delay = parent->start_delay;
+ td->timeout = parent->timeout;
+ td->use_aio = parent->use_aio;
+ td->create_file = parent->create_file;
+ td->overwrite = parent->overwrite;
+ td->invalidate_cache = parent->invalidate_cache;
+ td->file_size = parent->file_size;
+ td->file_offset = parent->file_offset;
+ td->rate = parent->rate;
+ td->ratemin = parent->ratemin;
+ td->ratecycle = parent->ratecycle;
+ td->aio_depth = parent->aio_depth;
+ td->sync_io = parent->sync_io;
+ td->mem_type = parent->mem_type;
+ td->bw_avg_time = parent->bw_avg_time;
+ td->create_serialize = parent->create_serialize;
+ td->create_fsync = parent->create_fsync;
+ td->loops = parent->loops;
+ td->verify = parent->verify;
+ td->stonewall = parent->stonewall;
+ td->numjobs = parent->numjobs;
+ memcpy(&td->cpumask, &parent->cpumask, sizeof(td->cpumask));
return td;
}
thread_number--;
}
-static int add_job(struct thread_data *td, const char *filename, int prioclass,
+static int add_job(struct thread_data *td, const char *jobname, int prioclass,
int prio)
{
+ int numjobs;
+
if (td == &def_thread)
return 0;
- strcpy(td->file_name, filename);
+ sprintf(td->file_name, "%s/%s.%d", td->directory, jobname, td->thread_number);
sem_init(&td->mutex, 1, 0);
td->ioprio = (prioclass << IOPRIO_CLASS_SHIFT) | prio;
td->verify = 0;
if (setup_rate(td))
- return -1;
+ goto err;
if (write_lat_log)
setup_log(&td->lat_log);
if (write_bw_log)
setup_log(&td->bw_log);
- printf("Client%d: file=%s, rw=%d, prio=%d/%d, seq=%d, odir=%d, bs=%d-%d, rate=%d, aio=%d, aio_depth=%d\n", td->thread_number, filename, td->ddir, prioclass, prio, td->sequential, td->odirect, td->min_bs, td->max_bs, td->rate, td->use_aio, td->aio_depth);
+ printf("Client%d: rw=%d, prio=%d/%d, seq=%d, odir=%d, bs=%d-%d, rate=%d, aio=%d, aio_depth=%d\n", td->thread_number, td->ddir, prioclass, prio, td->sequential, td->odirect, td->min_bs, td->max_bs, td->rate, td->use_aio, td->aio_depth);
+
+ /*
+ * recurse add identical jobs, clear numjobs and stonewall options
+ * as they don't apply to sub-jobs
+ */
+ numjobs = td->numjobs;
+ while (--numjobs) {
+ struct thread_data *td_new = get_new_job(0, td);
+
+ if (!td_new)
+ break;
+
+ td_new->numjobs = 1;
+ td_new->stonewall = 0;
+
+ if (add_job(td_new, jobname, prioclass, prio))
+ break;
+ }
return 0;
+err:
+ put_job(td);
+ return -1;
}
static void fill_cpu_mask(cpu_set_t cpumask, int cpu)
return 1;
}
+static int check_strstore(char *p, char *name, char *dest)
+{
+ char *s = strstr(p, name);
+
+ if (!s)
+ return 1;
+
+ s = strstr(p, "=");
+ if (!s)
+ return 1;
+
+ s++;
+ while (isblank(*s))
+ s++;
+
+ strcpy(dest, s);
+ return 0;
+}
+
static int check_range(char *p, char *name, unsigned long *s, unsigned long *e)
{
char str[128];
name[strlen(name) - 1] = '\0';
- td = get_new_job(global);
+ td = get_new_job(global, &def_thread);
if (!td)
break;
fgetpos(f, &off);
continue;
}
+ if (!check_int(p, "numjobs", &td->numjobs)) {
+ fgetpos(f, &off);
+ continue;
+ }
if (!check_range(p, "bsrange", &ul1, &ul2)) {
if (ul1 & 511)
printf("bad min block size, must be a multiple of 512\n");
fgetpos(f, &off);
continue;
}
+ if (!check_strstore(p, "directory", td->directory)) {
+ printf("got dir %s\n", td->directory);
+ fgetpos(f, &off);
+ continue;
+ }
if (!check_str(p, "mem", "malloc")) {
td->mem_type = MEM_MALLOC;
fgetpos(f, &off);
fgetpos(f, &off);
continue;
}
+ if (!strncmp(p, "stonewall", 9)) {
+ td->stonewall = 1;
+ fgetpos(f, &off);
+ continue;
+ }
printf("Client%d: bad option %s\n",td->thread_number,p);
}
fsetpos(f, &off);
if (add_job(td, name, prioclass, prio))
- put_job(td);
+ break;
}
free(string);
struct timeval genesis;
struct thread_data *td;
unsigned long spent;
- int i, todo, nr_running, m_rate, t_rate;
+ int i, todo, nr_running, m_rate, t_rate, nr_started;
printf("Starting %d threads\n", thread_number);
fflush(stdout);
todo = thread_number;
nr_running = 0;
+ nr_started = 0;
m_rate = t_rate = 0;
for (i = 0; i < thread_number; i++) {
continue;
}
+ if (td->stonewall && (nr_started || nr_running))
+ break;
+
td_set_runstate(td, TD_CREATED);
check_str_update(td, nr_running, t_rate, m_rate);
sem_init(&startup_sem, 1, 1);
todo--;
+ nr_started++;
if (fork())
sem_wait(&startup_sem);
td_set_runstate(td, TD_RUNNING);
nr_running++;
+ nr_started--;
m_rate += td->ratemin;
t_rate += td->rate;
check_str_update(td, nr_running, t_rate, m_rate);
for (i = 0; i < thread_number; i++) {
struct thread_data *td = &threads[i];
- if (td->runstate == TD_RUNNING)
- run_str[td->thread_number - 1] = '+';
- else if (td->runstate == TD_VERIFYING)
- run_str[td->thread_number - 1] = 'V';
- else
+ if (td->runstate != TD_RUNNING &&
+ td->runstate != TD_VERIFYING)
continue;
check_str_update(td, nr_running, t_rate, m_rate);
def_thread.create_fsync = DEF_CREATE_FSYNC;
def_thread.loops = DEF_LOOPS;
def_thread.verify = DEF_VERIFY;
+ def_thread.stonewall = DEF_STONEWALL;
+ def_thread.numjobs = DEF_NUMJOBS;
i = parse_options(argc, argv);