Messages in this thread |  | | Date | Fri, 4 May 2001 14:29:15 -0700 | From | Larry McVoy <> | Subject | Re: 3ware 6410 RAID 10 performance? |
| |
On Fri, May 04, 2001 at 02:03:35PM -0700, Adam Radford wrote: > Larry, > > If there's anything to fix in the driver for this problem I'd be interested, > however I have not seen this problem before. > > What benchmark (and options) are you running? bonnie++ ? > > BTW... I am the author of the Linux driver.
First let me say I really like this card. It's definitely the right idea and when it works it screams. I used to work on really large I/O systems at SGI so I have a fair amount of experience in this area (the last system I worked on images the entire earth down to about 32 point fonts, pretty wacky, our tax dollars at work).
Anyway, the benchmark I use is a program called lmdd, it's part of lmbench but I've pulled all the files together into one and included it below. It's really a handy tool, lots of different file system and disk drive people use it. For years, I've known I need to wrap a shell script around it so that we could toss stuff like bonnie and iozone.
The typical ways I run it
# allocate and touch a buffer to flush memory lmdd opat=1 bs=512m count=1
# write a file, watching for variation in write times lmdd of=XXX bs=100m wt=1 move=2g
# read a file, watching for variation in read times lmdd if=XXX bs=100m rt=1 move=2g
lmdd is a lot like dd(1) but has the following key differences
a) the default if is an interal form of if=/dev/zero, it doesn't bother with the read from /dev/zero. b) the default of is an interal form of of=/dev/null, it doesn't bother with the write to /dev/null. c) default blocksize is 8KB
On top of that, it has a number of options not found in dd
fork=1 fork to do write I/O fsync=1 fsync output before exit ipat=1 check input for a pattern (see opat) mismatch=1 use with ipat, stop at first mismatch move=<amt> move <amt> bytes; takes k, m, and g suffices k/m/g are powers of ten, not 2, the disk people want powers of ten. Yeah, it sucks. opat=1 put a pattern in the output stream rand=<sz> do randoms over this size print=<p> different output formats, you want print=1 with rand rt=1 time and report results for each read wt=1 time and report results for each write srand=seed seed the random number generator, used with rand.
There are others, read the source for them, those are the useful ones.
Another tidbit of data on the 3ware card issues - it is definitely associated with that card or the drives on that card; if I do the same tests on a file system which is not going through the 3ware card, I get repeatable 27MB/sec performance. Yes, I did try it after the 3ware card was wedged, the non 3ware performance is still good.
Adam, if you want to ssh in and play with the machine directly, call me at 415-401-8808 x101 and I'll set you up immediately.
Thanks,
--lm
/* * $Id$ */ #ifndef _BENCH_H #define _BENCH_H
#ifdef WIN32 #include <windows.h> typedef unsigned char bool_t; #endif
#include <assert.h> #include <ctype.h> #include <stdio.h> #ifndef WIN32 #include <unistd.h> #endif #include <stdlib.h> #include <fcntl.h> #include <signal.h> #include <errno.h> #ifndef WIN32 #include <strings.h> #endif #include <sys/types.h> #ifndef WIN32 #include <sys/mman.h> #endif #include <sys/stat.h> #ifndef WIN32 #include <sys/wait.h> #include <time.h> #include <sys/time.h> #include <sys/socket.h> #include <sys/un.h> #include <sys/resource.h> #define PORTMAP #include <rpc/rpc.h> #endif
typedef unsigned int u32;
#ifdef HAVE_u64_t typedef u64_t u64; #else typedef unsigned long long u64; #endif
#define NO_PORTMAPPER /* needs to be up here, lib_*.h look at it */ #include "stats.h" #include "timing.h"
#ifdef DEBUG # define debug(x) fprintf x #else # define debug(x) #endif #ifdef NO_PORTMAPPER #define TCP_SELECT -31233 #define TCP_XACT -31234 #define TCP_CONTROL -31235 #define TCP_DATA -31236 #define TCP_CONNECT -31237 #define UDP_XACT -31238 #define UDP_DATA -31239 #else #define TCP_SELECT (u_long)404038 /* XXX - unregistered */ #define TCP_XACT (u_long)404039 /* XXX - unregistered */ #define TCP_CONTROL (u_long)404040 /* XXX - unregistered */ #define TCP_DATA (u_long)404041 /* XXX - unregistered */ #define TCP_CONNECT (u_long)404042 /* XXX - unregistered */ #define UDP_XACT (u_long)404032 /* XXX - unregistered */ #define UDP_DATA (u_long)404033 /* XXX - unregistered */ #define VERS (u_long)1 #endif
#define UNIX_CONTROL "/tmp/lmbench.ctl" #define UNIX_DATA "/tmp/lmbench.data" #define UNIX_LAT "/tmp/lmbench.lat"
/* * socket send/recv buffer optimizations */ #define SOCKOPT_READ 0x0001 #define SOCKOPT_WRITE 0x0002 #define SOCKOPT_RDWR 0x0003 #define SOCKOPT_PID 0x0004 #define SOCKOPT_REUSE 0x0008 #define SOCKOPT_NONE 0
#ifndef SOCKBUF #define SOCKBUF (1024*1024) #endif
#ifndef XFERSIZE #define XFERSIZE (64*1024) /* all bandwidth I/O should use this */ #endif
#if defined(SYS5) || defined(WIN32) #define bzero(b, len) memset(b, 0, len) #define bcopy(s, d, l) memcpy(d, s, l) #define rindex(s, c) strrchr(s, c) #endif #define gettime usecs_spent #define streq !strcmp #define ulong unsigned long
#ifdef WIN32 #include <process.h> #define getpid _getpid #endif
#define SMALLEST_LINE 32 /* smallest cache line size */ #define TIME_OPEN2CLOSE
#define GO_AWAY signal(SIGALRM, exit); alarm(60 * 60); #define REAL_SHORT 50000 #define SHORT 1000000 #define MEDIUM 2000000 #define LONGER 7500000 /* for networking data transfers */ #define ENOUGH REAL_SHORT
#define TRIES 11
typedef struct { int N; u64 u[TRIES]; u64 n[TRIES]; } result_t; void insertinit(result_t *r); void insertsort(u64, u64, result_t *); void save_median(); void save_minimum(); void save_results(result_t *r); void get_results(result_t *r);
#define BENCHO(loop_body, overhead_body, enough) { \ int __i, __N; \ double __oh; \ result_t __overhead, __r; \ insertinit(&__overhead); insertinit(&__r); \ __N = (enough == 0 || get_enough(enough) <= 100000) ? TRIES : 1;\ if (enough < LONGER) {loop_body;} /* warm the cache */ \ for (__i = 0; __i < __N; ++__i) { \ BENCH1(overhead_body, enough); \ if (gettime() > 0) \ insertsort(gettime(), get_n(), &__overhead); \ BENCH1(loop_body, enough); \ if (gettime() > 0) \ insertsort(gettime(), get_n(), &__r); \ } \ for (__i = 0; __i < __r.N; ++__i) { \ __oh = __overhead.u[__i] / (double)__overhead.n[__i]; \ __r.u[__i] -= (u64)((double)__r.n[__i] * __oh); \ } \ save_results(&__r); \ }
#define BENCH(loop_body, enough) { \ long __i, __N; \ result_t __r; \ insertinit(&__r); \ __N = (enough == 0 || get_enough(enough) <= 100000) ? TRIES : 1;\ if (enough < LONGER) {loop_body;} /* warm the cache */ \ for (__i = 0; __i < __N; ++__i) { \ BENCH1(loop_body, enough); \ if (gettime() > 0) \ insertsort(gettime(), get_n(), &__r); \ } \ save_results(&__r); \ }
#define BENCH1(loop_body, enough) { \ double __usecs; \ BENCH_INNER(loop_body, enough); \ __usecs = gettime(); \ __usecs -= t_overhead() + get_n() * l_overhead(); \ settime(__usecs >= 0. ? (u64)__usecs : 0.); \ } #define BENCH_INNER(loop_body, enough) { \ static u_long __iterations = 1; \ int __enough = get_enough(enough); \ u_long __n; \ double __result = 0.; \ \ while(__result < 0.95 * __enough) { \ start(0); \ for (__n = __iterations; __n > 0; __n--) { \ loop_body; \ } \ __result = stop(0,0); \ if (__result < 0.99 * __enough \ || __result > 1.2 * __enough) { \ if (__result > 150.) { \ double tmp = __iterations / __result; \ tmp *= 1.1 * __enough; \ __iterations = (u_long)(tmp + 1); \ } else { \ if (__iterations > (u_long)1<<27) { \ __result = 0.; \ break; \ } \ __iterations <<= 3; \ } \ } \ } /* while */ \ save_n((u64)__iterations); settime((u64)__result); \ }
/* * Generated from msg.x which is included here:
program XACT_PROG { version XACT_VERS { char RPC_XACT(char) = 1; } = 1; } = 3970;
* Please do not edit this file. * It was generated using rpcgen. */
#include <rpc/types.h>
#define XACT_PROG ((u_long)404040) #define XACT_VERS ((u_long)1) #define RPC_XACT ((u_long)1) #define RPC_EXIT ((u_long)2) extern char *rpc_xact_1(); extern char *client_rpc_xact_1();
#endif /* _BENCH_H */ #ifndef _STATS_H #define _STATS_H
#include "bench.h" #include "timing.h"
#define ABS(x) ((x) < 0 ? -(x) : (x))
int int_compare(const void *a, const void *b); int u64_compare(const void *a, const void *b); int double_compare(const void *a, const void *b);
typedef int (*int_stat)(int *values, int size); typedef u64 (*u64_stat)(u64 *values, int size); typedef double (*double_stat)(double *values, int size);
int int_median(int *values, int size); u64 u64_median(u64 *values, int size); double double_median(double *values, int size);
int int_mean(int *values, int size); u64 u64_mean(u64 *values, int size); double double_mean(double *values, int size);
int int_min(int *values, int size); u64 u64_min(u64 *values, int size); double double_min(double *values, int size);
int int_max(int *values, int size); u64 u64_max(u64 *values, int size); double double_max(double *values, int size);
double int_stderr(int *values, int size); double u64_stderr(u64 *values, int size); double double_stderr(double *values, int size);
double int_bootstrap_stderr(int *values, int size, int_stat f); double u64_bootstrap_stderr(u64 *values, int size, u64_stat f); double double_bootstrap_stderr(double *values, int size, double_stat f);
void regression(double *x, double *y, double *sig, int n, double *a, double *b, double *sig_a, double *sig_b, double *chi2);
#endif /* _STATS_H */ /* * $Id$ */ #ifndef _TIMING_H #define _TIMING_H
char *p64(u64 big); char *p64sz(u64 big); double Delta(void); double Now(void); void adjust(int usec); void bandwidth(u64 bytes, u64 times, int verbose); int bytes(char *s); void context(u64 xfers); u64 delta(void); int get_enough(int); u64 get_n(void); void kb(u64 bytes); double l_overhead(void); char last(char *s); void latency(u64 xfers, u64 size); void mb(u64 bytes); void micro(char *s, u64 n); void micromb(u64 mb, u64 n); void milli(char *s, u64 n); void morefds(void); void nano(char *s, u64 n); u64 now(void); void ptime(u64 n); void rusage(void); void save_n(u64); void settime(u64 usecs); void start(struct timeval *tv); u64 stop(struct timeval *begin, struct timeval *end); u64 t_overhead(void); double timespent(void); void timing(FILE *out); u64 tvdelta(struct timeval *, struct timeval *); void tvsub(struct timeval *tdiff, struct timeval *t1, struct timeval *t0); void use_int(int result); void use_pointer(void *result); u64 usecs_spent(void); void touch(char *buf, int size);
#if defined(hpux) || defined(__hpux) int getpagesize(); #endif
#endif /* _TIMING_H */ /* * a timing utilities library * * Requires 64bit integers to work. * * %W% %@% * * Copyright (c) 1994-1998 Larry McVoy. */ #define _LIB /* bench.h needs this */ #include "bench.h"
#define nz(x) ((x) == 0 ? 1 : (x))
/* * I know you think these should be 2^10 and 2^20, but people are quoting * disk sizes in powers of 10, and bandwidths are all power of ten. * Deal with it. */ #define MB (1000*1000.0) #define KB (1000.0)
static struct timeval start_tv, stop_tv; FILE *ftiming; volatile u64 use_result_dummy; /* !static for optimizers. */ static u64 iterations; static void init_timing(void);
#if defined(hpux) || defined(__hpux) #include <sys/mman.h> #endif
#ifdef RUSAGE #include <sys/resource.h> #define SECS(tv) (tv.tv_sec + tv.tv_usec / 1000000.0) #define mine(f) (int)(ru_stop.f - ru_start.f)
static struct rusage ru_start, ru_stop;
void rusage(void) { double sys, user, idle; double per;
sys = SECS(ru_stop.ru_stime) - SECS(ru_start.ru_stime); user = SECS(ru_stop.ru_utime) - SECS(ru_start.ru_utime); idle = timespent() - (sys + user); per = idle / timespent() * 100; if (!ftiming) ftiming = stderr; fprintf(ftiming, "real=%.2f sys=%.2f user=%.2f idle=%.2f stall=%.0f%% ", timespent(), sys, user, idle, per); fprintf(ftiming, "rd=%d wr=%d min=%d maj=%d ctx=%d\n", mine(ru_inblock), mine(ru_oublock), mine(ru_minflt), mine(ru_majflt), mine(ru_nvcsw) + mine(ru_nivcsw)); }
#endif /* RUSAGE */ /* * Redirect output someplace else. */ void timing(FILE *out) { ftiming = out; }
/* * Start timing now. */ void start(struct timeval *tv) { if (tv == NULL) { tv = &start_tv; } #ifdef RUSAGE getrusage(RUSAGE_SELF, &ru_start); #endif (void) gettimeofday(tv, (struct timezone *) 0); }
/* * Stop timing and return real time in microseconds. */ u64 stop(struct timeval *begin, struct timeval *end) { if (end == NULL) { end = &stop_tv; } (void) gettimeofday(end, (struct timezone *) 0); #ifdef RUSAGE getrusage(RUSAGE_SELF, &ru_stop); #endif
if (begin == NULL) { begin = &start_tv; } return tvdelta(begin, end); }
u64 now(void) { struct timeval t; u64 m;
(void) gettimeofday(&t, (struct timezone *) 0); m = t.tv_sec; m *= 1000000; m += t.tv_usec; return (m); }
double Now(void) { struct timeval t;
(void) gettimeofday(&t, (struct timezone *) 0); return (t.tv_sec * 1000000.0 + t.tv_usec); }
u64 delta(void) { static struct timeval last; struct timeval t; struct timeval diff; u64 m;
(void) gettimeofday(&t, (struct timezone *) 0); if (last.tv_usec) { tvsub(&diff, &t, &last); last = t; m = diff.tv_sec; m *= 1000000; m += diff.tv_usec; return (m); } else { last = t; return (0); } }
double Delta(void) { struct timeval t; struct timeval diff;
(void) gettimeofday(&t, (struct timezone *) 0); tvsub(&diff, &t, &start_tv); return (diff.tv_sec + diff.tv_usec / 1000000.0); }
void save_n(u64 n) { iterations = n; }
u64 get_n(void) { return (iterations); }
/* * Make the time spend be usecs. */ void settime(u64 usecs) { bzero((void*)&start_tv, sizeof(start_tv)); stop_tv.tv_sec = usecs / 1000000; stop_tv.tv_usec = usecs % 1000000; }
void bandwidth(u64 bytes, u64 times, int verbose) { struct timeval tdiff; double mb, secs;
tvsub(&tdiff, &stop_tv, &start_tv); secs = tdiff.tv_sec; secs *= 1000000; secs += tdiff.tv_usec; secs /= 1000000; secs /= times; mb = bytes / MB; if (!ftiming) ftiming = stderr; if (verbose) { (void) fprintf(ftiming, "%.4f MB in %.4f secs, %.4f MB/sec\n", mb, secs, mb/secs); } else { if (mb < 1) { (void) fprintf(ftiming, "%.6f ", mb); } else { (void) fprintf(ftiming, "%.2f ", mb); } if (mb / secs < 1) { (void) fprintf(ftiming, "%.6f\n", mb/secs); } else { (void) fprintf(ftiming, "%.2f\n", mb/secs); } } }
void kb(u64 bytes) { struct timeval td; double s, bs;
tvsub(&td, &stop_tv, &start_tv); s = td.tv_sec + td.tv_usec / 1000000.0; bs = bytes / nz(s); if (!ftiming) ftiming = stderr; (void) fprintf(ftiming, "%.0f KB/sec\n", bs / KB); }
void mb(u64 bytes) { struct timeval td; double s, bs;
tvsub(&td, &stop_tv, &start_tv); s = td.tv_sec + td.tv_usec / 1000000.0; bs = bytes / nz(s); if (!ftiming) ftiming = stderr; (void) fprintf(ftiming, "%.2f MB/sec\n", bs / MB); }
void latency(u64 xfers, u64 size) { struct timeval td; double s;
if (!ftiming) ftiming = stderr; tvsub(&td, &stop_tv, &start_tv); s = td.tv_sec + td.tv_usec / 1000000.0; if (xfers > 1) { fprintf(ftiming, "%d %dKB xfers in %.2f secs, ", (int) xfers, (int) (size / KB), s); } else { fprintf(ftiming, "%.1fKB in ", size / KB); } if ((s * 1000 / xfers) > 100) { fprintf(ftiming, "%.0f millisec%s, ", s * 1000 / xfers, xfers > 1 ? "/xfer" : "s"); } else { fprintf(ftiming, "%.4f millisec%s, ", s * 1000 / xfers, xfers > 1 ? "/xfer" : "s"); } if (((xfers * size) / (MB * s)) > 1) { fprintf(ftiming, "%.2f MB/sec\n", (xfers * size) / (MB * s)); } else { fprintf(ftiming, "%.2f KB/sec\n", (xfers * size) / (KB * s)); } }
void context(u64 xfers) { struct timeval td; double s;
tvsub(&td, &stop_tv, &start_tv); s = td.tv_sec + td.tv_usec / 1000000.0; if (!ftiming) ftiming = stderr; fprintf(ftiming, "%d context switches in %.2f secs, %.0f microsec/switch\n", (int)xfers, s, s * 1000000 / xfers); }
void nano(char *s, u64 n) { struct timeval td; double micro;
tvsub(&td, &stop_tv, &start_tv); micro = td.tv_sec * 1000000 + td.tv_usec; micro *= 1000; if (!ftiming) ftiming = stderr; fprintf(ftiming, "%s: %.0f nanoseconds\n", s, micro / n); }
void micro(char *s, u64 n) { struct timeval td; double micro;
tvsub(&td, &stop_tv, &start_tv); micro = td.tv_sec * 1000000 + td.tv_usec; micro /= n; if (!ftiming) ftiming = stderr; fprintf(ftiming, "%s: %.4f microseconds\n", s, micro); #if 0 if (micro >= 100) { fprintf(ftiming, "%s: %.1f microseconds\n", s, micro); } else if (micro >= 10) { fprintf(ftiming, "%s: %.3f microseconds\n", s, micro); } else { fprintf(ftiming, "%s: %.4f microseconds\n", s, micro); } #endif }
void micromb(u64 sz, u64 n) { struct timeval td; double mb, micro;
tvsub(&td, &stop_tv, &start_tv); micro = td.tv_sec * 1000000 + td.tv_usec; micro /= n; mb = sz; mb /= MB; if (!ftiming) ftiming = stderr; if (micro >= 10) { fprintf(ftiming, "%.6f %.0f\n", mb, micro); } else { fprintf(ftiming, "%.6f %.3f\n", mb, micro); } }
void milli(char *s, u64 n) { struct timeval td; u64 milli;
tvsub(&td, &stop_tv, &start_tv); milli = td.tv_sec * 1000 + td.tv_usec / 1000; milli /= n; if (!ftiming) ftiming = stderr; fprintf(ftiming, "%s: %d milliseconds\n", s, (int)milli); }
void ptime(u64 n) { struct timeval td; double s;
tvsub(&td, &stop_tv, &start_tv); s = td.tv_sec + td.tv_usec / 1000000.0; if (!ftiming) ftiming = stderr; fprintf(ftiming, "%d in %.2f secs, %.0f microseconds each\n", (int)n, s, s * 1000000 / n); }
u64 tvdelta(struct timeval *start, struct timeval *stop) { struct timeval td; u64 usecs;
tvsub(&td, stop, start); usecs = td.tv_sec; usecs *= 1000000; usecs += td.tv_usec; return (usecs); }
void tvsub(struct timeval * tdiff, struct timeval * t1, struct timeval * t0) { tdiff->tv_sec = t1->tv_sec - t0->tv_sec; tdiff->tv_usec = t1->tv_usec - t0->tv_usec; if (tdiff->tv_usec < 0 && tdiff->tv_sec > 0) { tdiff->tv_sec--; tdiff->tv_usec += 1000000; assert(tdiff->tv_usec >= 0); }
/* time shouldn't go backwards!!! */ if (tdiff->tv_usec < 0 || t1->tv_sec < t0->tv_sec) { tdiff->tv_sec = 0; tdiff->tv_usec = 0; } }
u64 gettime(void) { return (tvdelta(&start_tv, &stop_tv)); }
double timespent(void) { struct timeval td;
tvsub(&td, &stop_tv, &start_tv); return (td.tv_sec + td.tv_usec / 1000000.0); }
static char p64buf[10][20]; static int n;
char * p64(u64 big) { char *s = p64buf[n++];
if (n == 10) n = 0; #ifdef linux { int *a = (int*)&big;
if (a[1]) { sprintf(s, "0x%x%08x", a[1], a[0]); } else { sprintf(s, "0x%x", a[0]); } } #endif #ifdef __sgi sprintf(s, "0x%llx", big); #endif return (s); }
char * p64sz(u64 big) { double d = big; char *tags = " KMGTPE"; int t = 0; char *s = p64buf[n++];
if (n == 10) n = 0; while (d > 512) t++, d /= 1024; if (d == 0) { return ("0"); } if (d < 100) { sprintf(s, "%.4f%c", d, tags[t]); } else { sprintf(s, "%.2f%c", d, tags[t]); } return (s); }
char last(char *s) { while (*s++) ; return (s[-2]); }
int bytes(char *s) { int n = atoi(s);
if ((last(s) == 'k') || (last(s) == 'K')) n *= 1024; if ((last(s) == 'm') || (last(s) == 'M')) n *= (1024 * 1024); return (n); }
void use_int(int result) { use_result_dummy += result; }
void use_pointer(void *result) { use_result_dummy += (int)result; }
void insertinit(result_t *r) { int i;
r->N = 0; for (i = 0; i < TRIES; i++) { r->u[i] = 0; r->n[i] = 1; } }
/* biggest to smallest */ void insertsort(u64 u, u64 n, result_t *r) { int i, j;
if (u == 0) return;
for (i = 0; i < r->N; ++i) { if (u/(double)n > r->u[i]/(double)r->n[i]) { for (j = r->N; j > i; --j) { r->u[j] = r->u[j-1]; r->n[j] = r->n[j-1]; } break; } } r->u[i] = u; r->n[i] = n; r->N++; }
static result_t results;
void print_results(void) { int i;
for (i = 0; i < results.N; ++i) { fprintf(stderr, "%.2f ", (double)results.u[i]/results.n[i]); } }
void get_results(result_t *r) { *r = results; }
void save_results(result_t *r) { results = *r; save_median(); }
void save_minimum() { if (results.N == 0) { save_n(1); settime(0); } else { save_n(results.n[results.N - 1]); settime(results.u[results.N - 1]); } }
void save_median() { int i = results.N / 2; u64 u, n;
if (results.N == 0) { n = 1; u = 0; } else if (results.N % 2) { n = results.n[i]; u = results.u[i]; } else { n = (results.n[i] + results.n[i-1]) / 2; u = (results.u[i] + results.u[i-1]) / 2; } save_n(n); settime(u); }
/* * The inner loop tracks bench.h but uses a different results array. */ static long * one_op(register long *p) { BENCH_INNER(p = (long *)*p, 0); return (p); }
static long * two_op(register long *p, register long *q) { BENCH_INNER(p = (long *)*q; q = (long*)*p, 0); return (p); }
static long *p = (long *)&p; static long *q = (long *)&q;
double l_overhead(void) { int i; u64 N_save, u_save; static double overhead; static int initialized = 0; result_t one, two, r_save;
init_timing(); if (initialized) return (overhead);
initialized = 1; if (getenv("LOOP_O")) { overhead = atof(getenv("LOOP_O")); } else { get_results(&r_save); N_save = get_n(); u_save = gettime(); insertinit(&one); insertinit(&two); for (i = 0; i < TRIES; ++i) { use_pointer((void*)one_op(p)); if (gettime() > t_overhead()) insertsort(gettime() - t_overhead(), get_n(), &one); use_pointer((void *)two_op(p, q)); if (gettime() > t_overhead()) insertsort(gettime() - t_overhead(), get_n(), &two); } /* * u1 = (n1 * (overhead + work)) * u2 = (n2 * (overhead + 2 * work)) * ==> overhead = 2. * u1 / n1 - u2 / n2 */ save_results(&one); save_minimum(); overhead = 2. * gettime() / (double)get_n(); save_results(&two); save_minimum(); overhead -= gettime() / (double)get_n(); if (overhead < 0.) overhead = 0.; /* Gag */
save_results(&r_save); save_n(N_save); settime(u_save); } return (overhead); }
/* * Figure out the timing overhead. This has to track bench.h */ u64 t_overhead(void) { u64 N_save, u_save; static int initialized = 0; static u64 overhead = 0; struct timeval tv; result_t r_save;
init_timing(); if (initialized) return (overhead);
initialized = 1; if (getenv("TIMING_O")) { overhead = atof(getenv("TIMING_O")); } else if (get_enough(0) <= 50000) { /* it is not in the noise, so compute it */ int i; result_t r;
get_results(&r_save); N_save = get_n(); u_save = gettime(); insertinit(&r); for (i = 0; i < TRIES; ++i) { BENCH_INNER(gettimeofday(&tv, 0), 0); insertsort(gettime(), get_n(), &r); } save_results(&r); save_minimum(); overhead = gettime() / get_n();
save_results(&r_save); save_n(N_save); settime(u_save); } return (overhead); }
/* * Figure out how long to run it. * If enough == 0, then they want us to figure it out. * If enough is !0 then return it unless we think it is too short. */ static int long_enough; static int compute_enough();
int get_enough(int e) { init_timing(); return (long_enough > e ? long_enough : e); }
static void init_timing(void) { static int done = 0;
if (done) return; done = 1; long_enough = compute_enough(); t_overhead(); l_overhead(); }
typedef long TYPE;
static TYPE ** enough_duration(register long N, register TYPE ** p) { #define ENOUGH_DURATION_TEN(one) one one one one one one one one one one while (N-- > 0) { ENOUGH_DURATION_TEN(p = (TYPE **) *p;); } return (p); }
static u64 duration(long N) { u64 usecs; TYPE *x = (TYPE *)&x; TYPE **p = (TYPE **)&x;
start(0); p = enough_duration(N, p); usecs = stop(0, 0); use_pointer((void *)p); return (usecs); }
/* * find the minimum time that work "N" takes in "tries" tests */ static u64 time_N(long N) { int i; u64 usecs; result_t r;
insertinit(&r); for (i = 1; i < TRIES; ++i) { usecs = duration(N); insertsort(usecs, N, &r); } save_results(&r); save_minimum(); return (gettime()); }
/* * return the amount of work needed to run "enough" microseconds */ static long find_N(int enough) { int tries; static long N = 10000; static u64 usecs = 0;
if (!usecs) usecs = time_N(N);
for (tries = 0; tries < 10; ++tries) { if (0.98 * enough < usecs && usecs < 1.02 * enough) return (N); if (usecs < 1000) N *= 10; else { double n = N;
n /= usecs; n *= enough; N = n + 1; } usecs = time_N(N); } return (-1); }
/* * We want to verify that small modifications proportionally affect the runtime */ static double test_points[] = {1.015, 1.02, 1.035}; static int test_time(int enough) { int i; long N; u64 usecs, expected, baseline, diff;
if ((N = find_N(enough)) <= 0) return (0);
baseline = time_N(N);
for (i = 0; i < sizeof(test_points) / sizeof(double); ++i) { usecs = time_N((int)((double) N * test_points[i])); expected = (u64)((double)baseline * test_points[i]); diff = expected > usecs ? expected - usecs : usecs - expected; if (diff / (double)expected > 0.0025) return (0); } return (1); }
/* * We want to find the smallest timing interval that has accurate timing */ static int possibilities[] = { 5000, 10000, 50000, 100000 }; static int compute_enough() { int i;
if (getenv("ENOUGH")) { return (atoi(getenv("ENOUGH"))); } for (i = 0; i < sizeof(possibilities) / sizeof(int); ++i) { if (test_time(possibilities[i])) return (possibilities[i]); }
/* * if we can't find a timing interval that is sufficient, * then use SHORT as a default. */ return (SHORT); }
/* * This stuff isn't really lib_timing, but ... */ void morefds(void) { #ifdef RLIMIT_NOFILE struct rlimit r;
getrlimit(RLIMIT_NOFILE, &r); r.rlim_cur = r.rlim_max; setrlimit(RLIMIT_NOFILE, &r); #endif }
void touch(char *buf, int nbytes) { static psize;
if (!psize) { psize = getpagesize(); } while (nbytes > 0) { *buf = 1; buf += psize; nbytes -= psize; } }
#if defined(hpux) || defined(__hpux) int getpagesize() { return (sysconf(_SC_PAGE_SIZE)); } #endif
#ifdef WIN32 int getpagesize() { SYSTEM_INFO s;
GetSystemInfo(&s); return ((int)s.dwPageSize); }
LARGE_INTEGER getFILETIMEoffset() { SYSTEMTIME s; FILETIME f; LARGE_INTEGER t;
s.wYear = 1970; s.wMonth = 1; s.wDay = 1; s.wHour = 0; s.wMinute = 0; s.wSecond = 0; s.wMilliseconds = 0; SystemTimeToFileTime(&s, &f); t.QuadPart = f.dwHighDateTime; t.QuadPart <<= 32; t.QuadPart |= f.dwLowDateTime; return (t); }
int gettimeofday(struct timeval *tv, struct timezone *tz) { LARGE_INTEGER t; FILETIME f; double microseconds; static LARGE_INTEGER offset; static double frequencyToMicroseconds; static int initialized = 0; static BOOL usePerformanceCounter = 0;
if (!initialized) { LARGE_INTEGER performanceFrequency; initialized = 1; usePerformanceCounter = QueryPerformanceFrequency(&performanceFrequency); if (usePerformanceCounter) { QueryPerformanceCounter(&offset); frequencyToMicroseconds = (double)performanceFrequency.QuadPart / 1000000.; } else { offset = getFILETIMEoffset(); frequencyToMicroseconds = 10.; } } if (usePerformanceCounter) QueryPerformanceCounter(&t); else { GetSystemTimeAsFileTime(&f); t.QuadPart = f.dwHighDateTime; t.QuadPart <<= 32; t.QuadPart |= f.dwLowDateTime; }
t.QuadPart -= offset.QuadPart; microseconds = (double)t.QuadPart / frequencyToMicroseconds; t.QuadPart = microseconds; tv->tv_sec = t.QuadPart / 1000000; tv->tv_usec = t.QuadPart % 1000000; return (0); } #endif char *id = "$Id: lmdd.c,v 1.23 1997/12/01 23:47:59 lm Exp $\n"; /* * defaults: * bs=8k * count=forever * if=internal * of=internal * ipat=0 * opat=0 * mismatch=0 * rusage=0 * flush=0 * rand=0 * print=0 * direct=0 * rt=0 * rtmax=0 * wtmax=0 * rtmin=0 * wtmin=0 * label="" * shorthands: * k, m, g are 2^10, 2^20, 2^30 multipliers. * K, M, G are 10^3, 10^6, 10^9 multipliers. * recognizes "internal" as an internal /dev/zero /dev/null file. * * Copyright (c) 1994-1998 by Larry McVoy. All rights reserved. * See the file COPYING for the licensing terms. * * TODO - rewrite this entire thing from scratch. This is disgusting code. */
#ifndef __Lynx__ #define FLUSH #endif
#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <string.h> #include <malloc.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #ifdef USE_BDS #include "bds.h" #endif #include <sys/time.h> #include "bench.h"
#define ALIGN(x, bs) ((x + (bs - 1)) & ~(bs - 1))
#ifdef FLUSH #include <sys/mman.h> #include <sys/stat.h> void flush(void); #endif
#define USE_VALLOC #ifdef USE_VALLOC #define VALLOC valloc #else #define VALLOC malloc #endif
extern double drand48();
#ifdef __sgi # define LSEEK(a,b,c) (u64)lseek64(a, (off64_t)b, c) # define ATOL(s) atoll(s) #else # define LSEEK(a,b,c) (u64)lseek(a, b, c) # define ATOL(s) atol(s) #endif
int awrite, poff, out, Print, Fsync, Sync, Flush, Bsize, ru; u64 Start, End, Rand, int_count; int hash; int Realtime, Notrunc; int Rt, Rtmax, Rtmin, Wt, Wtmax, Wtmin; int rthist[12]; /* histogram of read times */ int wthist[12]; /* histogram of write times */ char *Label; u64 *norepeat; int norepeats = -1; #ifdef USE_BDS bds_msg *m1, *m2; #endif
u64 getarg(); int been_there(u64 off); int getfile(char *s, int ac, char **av);
char *cmds[] = { "bs", /* block size */ "bufs", /* use this many buffers round robin */ "count", /* number of blocks */ #ifdef DBG "debug", /* set external variable "dbg" */ #endif #ifdef O_DIRECT "direct", /* direct I/O on input and output */ "idirect", /* direct I/O on input */ "odirect", /* direct I/O on output */ #endif #ifdef FLUSH "flush", /* map in out and invalidate (flush) */ #endif "fork", /* fork to do write I/O */ "fsync", /* fsync output before exit */ "if", /* input file */ "ipat", /* check input for pattern */ "label", /* prefix print out with this */ "mismatch", /* stop at first mismatch */ "move", /* instead of count, limit transfer to this */ "of", /* output file */ "opat", /* generate pattern on output */ "print", /* report type */ "rand", /* do randoms over the specified size */ /* must be power of two, not checked */ "poff", /* Print the offsets as we do the io. */ #ifdef RUSAGE "rusage", /* dump rusage stats */ #endif "skip", /* skip this number of blocks */ "sync", /* sync output before exit */ "touch", /* touch each buffer after the I/O */ #if !defined(hpux) "usleep", /* sleep this many usecs between I/O */ #endif "hash", /* hash marks like FTP */ "append", /* O_APPEND */ "rt", /* time reads */ "wt", /* time writes */ "rtmax", /* read latency histogram max in mills */ "wtmax", /* write latency histogram max in mills */ "rtmin", /* read latency histogram max in mills */ "wtmin", /* write latency histogram max in mills */ "realtime", /* create files as XFS realtime files */ "notrunc", /* overwrite rather than truncing out file */ "end", /* limit randoms to this size near the * Rand endpoints. */ "start", /* Add this to Rand */ "time", /* Run for this many seconds only. */ "srand", /* Seed the random number generator */ "padin", /* Pad an extra untimed block_size read */ #ifdef USE_BDS "awrite", /* use async writes and pipeline them. */ #endif "norepeat", /* don't ever do the same I/O twice */ #ifdef sgi "mpin", /* pin the buffer */ #endif "timeopen", /* include open time in results */ "nocreate", /* just open for writing, don't create/trunc it */ #ifdef O_SYNC "osync", /* O_SYNC */ #endif 0, };
void error(char *); void done(); #ifdef DBG extern int dbg; #endif
int main(int ac, char **av) { u32 *buf; u32 *bufs[10]; int nbufs, nextbuf = 0; int Fork, misses, mismatch, outpat, inpat, in, timeopen, gotcnt; int slp; u64 skip, size, count; void chkarg(); int i; u64 off = 0; int touch; int time; int mills; int pad_in; int pid = 0; struct timeval start_tv; struct timeval stop_tv;
if (sizeof(int) != 4) { fprintf(stderr, "sizeof(int) != 4\n"); exit(1); } for (i = 1; i < ac; ++i) { chkarg(av[i]); } signal(SIGINT, done); signal(SIGALRM, done); misses = mismatch = getarg("mismatch=", ac, av); inpat = getarg("ipat=", ac, av); outpat = getarg("opat=", ac, av); Bsize = getarg("bs=", ac, av); if (Bsize < 0) Bsize = 8192; #if !defined(hpux) slp = getarg("usleep=", ac, av); #endif Fork = getarg("fork=", ac, av); Fsync = getarg("fsync=", ac, av); Sync = getarg("sync=", ac, av); Rand = getarg("rand=", ac, av); Start = getarg("start=", ac, av); End = getarg("end=", ac, av); time = getarg("time=", ac, av); if ((End != -1) && (Rand != -1) && (End > Rand)) { End = Rand; } if (getarg("srand=", ac, av) != -1) { srand48((long)getarg("srand=", ac, av)); } poff = getarg("poff=", ac, av) != -1; Print = getarg("print=", ac, av); nbufs = getarg("bufs=", ac, av); Realtime = getarg("realtime=", ac, av); Rtmax = getarg("rtmax=", ac, av); if ((Rtmax != -1) && (Rtmax < 10)) Rtmax = 10; Rtmin = getarg("rtmin=", ac, av); if ((Rtmax != -1) && (Rtmin == -1)) { Rtmin = 0; } Rt = getarg("rt=", ac, av); Wtmax = getarg("wtmax=", ac, av); if ((Wtmax != -1) && (Wtmax < 10)) Wtmax = 10; Wtmin = getarg("wtmin=", ac, av); if ((Wtmax != -1) && (Wtmin == -1)) { Wtmin = 0; } Wt = getarg("wt=", ac, av); if ((Rtmin && !Rtmax) || (Wtmin && !Wtmax)) { fprintf(stderr, "Need a max to go with that min.\n"); exit(1); } if ((Rtmin > Rtmax) || (Wtmin > Wtmax)) { fprintf(stderr, "min has to be less than max, R=%d,%d W=%d,%d\n", Rtmax, Rtmin, Wtmax, Wtmin); exit(1); } timeopen = getarg("timeopen=", ac, av); pad_in = getarg("padin=", ac, av); if (pad_in == -1) pad_in = 0; if (nbufs == -1) nbufs = 1; if (nbufs > 10) { printf("Too many bufs\n"); exit(1); } #ifdef DBG dbg = getarg("debug=", ac, av) != -1; #endif #ifdef RUSAGE ru = getarg("rusage=", ac, av); #endif touch = getarg("touch=", ac, av) != -1; hash = getarg("hash=", ac, av) != (u64)-1; Label = (char *)(u32)getarg("label=", ac, av); count = getarg("count=", ac, av); size = getarg("move=", ac, av); if (size != (u64)-1) count = size / Bsize; if (Rand != -1) { size = Rand - Bsize; size = ALIGN(size, Bsize); }
#ifdef FLUSH Flush = getarg("flush=", ac, av); #endif if (count == (u64)-1) gotcnt = 0; else gotcnt = 1; int_count = 0; skip = getarg("skip=", ac, av); if (getarg("norepeat=", ac, av) != -1) { if (gotcnt) { norepeat = (u64*)calloc(count, sizeof(u64)); } else { norepeat = (u64*)calloc(10<<10, sizeof(u64)); } }
if ((inpat != -1 || outpat != -1) && (Bsize & 3)) { fprintf(stderr, "Block size 0x%x must be word aligned\n", Bsize); exit(1); } if ((Bsize >> 2) == 0) { fprintf(stderr, "Block size must be at least 4.\n"); exit(1); } for (i = 0; i < nbufs; i++) { if (!(bufs[i] = (u32 *) VALLOC((unsigned) Bsize))) { perror("VALLOC"); exit(1); } bzero((char *) bufs[i], Bsize); #ifdef sgi if (getarg("mpin=", ac, av) != -1) { if (mpin((void *)bufs[i], (size_t)Bsize)) { perror("mpin for adam"); } } #endif }
if (time != -1) { alarm(time); } if (timeopen != -1) { start(NULL); } in = getfile("if=", ac, av); out = getfile("of=", ac, av); if (timeopen == -1) { start(NULL); } if ((Rtmax != -1) && in < 0) { fprintf(stderr, "I think you wanted wtmax, not rtmax\n"); exit(1); } if ((Wtmax != -1) && out < 0) { fprintf(stderr, "I think you wanted rtmax, not wtmax\n"); exit(1); } if (skip != (u64)-1) { off = skip; off *= Bsize; if (in >= 0) { LSEEK(in, off, 0); } if (out >= 0) { LSEEK(out, off, 0); } if (poff) { fprintf(stderr, "%s ", p64sz(off)); } } for (;;) { register int moved;
if (gotcnt && count-- <= 0) { done(); }
/* * If End is set, it means alternate back and forth * between the end points of Rand, doing randoms within * the area 0..End and Rand-End..Rand */ if (End != -1) { static u64 start = 0;
start = start ? 0 : Rand - End; do { off = drand48() * End; off = ALIGN(off, Bsize); off += start; if (Start != -1) { off += Start; } } while (norepeat && been_there(off)); if (norepeat) { norepeat[norepeats++] = off; if (!gotcnt && (norepeats == 10<<10)) { norepeats = 0; } } if (in >= 0) { LSEEK(in, off, 0); } if (out >= 0) { LSEEK(out, off, 0); } } /* * Set the seek pointer if doing randoms */ else if (Rand != -1) { do { off = drand48() * (size - Bsize); if (Start != -1) { off += Start; } off = ALIGN(off, Bsize); } while (norepeat && been_there(off)); if (norepeat) { norepeat[norepeats++] = off; } if (!gotcnt && (norepeats == 10<<10)) { norepeats = 0; } if (in >= 0) { LSEEK(in, off, 0); } if (out >= 0) { LSEEK(out, off, 0); } } if (poff) { fprintf(stderr, "%s ", p64sz(off)); }
buf = bufs[nextbuf]; if (++nextbuf == nbufs) nextbuf = 0; if (in >= 0) { if ((Rt != -1) || (Rtmax != -1) || (Rtmin != -1)) { start(&start_tv); } moved = read(in, buf, Bsize); if (pad_in) { /* ignore this run, restart clock */ pad_in = 0; count++; start(NULL); continue; } if (Rt) { int mics = stop(&start_tv, &stop_tv); mills = mics / 1000; fprintf(stderr, "READ: %.02f milliseconds offset %s, ", ((float)mics) / 1000, p64sz(LSEEK(in, 0, SEEK_CUR))); fprintf(stderr, "%.02f MB/sec\n", (double)moved/mics); } else if ((Rtmax != -1) || (Rtmin != -1)) { int mics = stop(&start_tv, &stop_tv); mills = mics / 1000; if ((mills > Rtmax) || (mills < Rtmin)) { fprintf(stderr, "READ: %.02f milliseconds offset %s\n", ((float)mics) / 1000, p64sz(LSEEK(in, 0, SEEK_CUR))); } /* * Put this read time in the histogram. * The buckets are each 1/10th of Rtmax. */ if (mills >= Rtmax) { rthist[11]++; } else if (mills < Rtmin) { rthist[0]++; } else { int step = (Rtmax - Rtmin) / 10; int i;
for (i = 1; i <= 10; ++i) { if (mills < i * step + Rtmin) { rthist[i]++; break; } } } } } else { moved = Bsize; } if (moved == -1) { perror("read"); } if (moved <= 0) { done(); } if (inpat != -1) { register int foo, cnt;
for (foo = 0, cnt = moved/sizeof(int); cnt--; foo++) { if (buf[foo] != (u32) (off + foo*sizeof(int))) { fprintf(stderr, "off=%u want=%x got=%x\n", (u32)off, (u32)(off + foo*sizeof(int)), buf[foo]); if (mismatch != -1 && --misses == 0) { done(); } } } } if ((in >= 0) && touch) { int i;
for (i = 0; i < moved; i += 4096) { ((char *)buf)[i] = 0; } } if (out >= 0) { int moved2;
if (Fork != -1) { if (pid) { waitpid(pid, 0, 0); } if ((pid = fork())) { off += moved; int_count += (moved >> 2); continue; } } if (outpat != -1) { register int foo, cnt;
for (foo = 0, cnt = moved/sizeof(int); cnt--; foo++) { buf[foo] = (u32)(off + foo*sizeof(int)); } } if ((Wt != -1) || (Wtmax != -1) || (Wtmin != -1)) { start(&start_tv); } #ifdef USE_BDS /* * The first time through, m1 & m2 are null. * The Nth time through, we start the I/O into * m2, and wait on m1, then switch. */ if (awrite) { if (m1) { m2 = bds_awrite(out, buf, moved); moved2 = bds_adone(out, m1); m1 = m2; } else { m1 = bds_awrite(out, buf, moved); goto writedone; } } else { moved2 = write(out, buf, moved); } #else moved2 = write(out, buf, moved); #endif
if (moved2 == -1) { perror("write"); } if (moved2 != moved) { fprintf(stderr, "write: wanted=%d got=%d\n", moved, moved2); done(); } if (Wt) { int mics = stop(&start_tv, &stop_tv); mills = mics / 1000; fprintf(stderr, "WRITE: %.02f milliseconds offset %s, ", ((float)mics) / 1000, p64sz(LSEEK(out, 0, SEEK_CUR))); fprintf(stderr, "%.02f MB/sec\n", (double)moved/mics); } else if ((Wtmax != -1) || (Wtmin != -1)) { int mics = stop(&start_tv, &stop_tv);
mills = mics / 1000; if ((mills > Wtmax) || (mills < Wtmin)) { fprintf(stderr, "WRITE: %.02f milliseconds offset %s\n", ((float)mics) / 1000, p64sz(LSEEK(out, 0, SEEK_CUR))); } /* * Put this write time in the histogram. * The buckets are each 1/10th of Wtmax. */ if (mills >= Wtmax) { wthist[11]++; } else if (mills < Wtmin) { wthist[0]++; } else { int step = (Wtmax - Wtmin) / 10; int i;
for (i = 1; i <= 10; ++i) { if (mills < i * step + Wtmin) { wthist[i]++; break; } } } }
if (moved2 == -1) { perror("write"); } if (moved2 != moved) { done(); }
if (touch) { int i;
for (i = 0; i < moved; i += 4096) { ((char *)buf)[i] = 0; } } } #ifdef USE_BDS writedone: /* for the first async write */ #endif off += moved; int_count += (moved >> 2); #if !defined(hpux) if (slp != -1) { usleep(slp); } #endif if (hash) { fprintf(stderr, "#"); } if (Fork != -1) { exit(0); } } }
int been_there(u64 off) { register int i;
for (i = 0; i <= norepeats; ++i) { if (off == norepeat[i]) { fprintf(stderr, "norepeat on %u\n", (u32)off); return (1); } } return (0); }
void chkarg(char *arg) { int i; char *a, *b;
for (i = 0; cmds[i]; ++i) { for (a = arg, b = cmds[i]; *a && *b && *a == *b; a++, b++) ; if (*a == '=') return; } fprintf(stderr, "Bad arg: %s, possible arguments are: ", arg); for (i = 0; cmds[i]; ++i) { fprintf(stderr, "%s ", cmds[i]); } fprintf(stderr, "\n"); exit(1); /*NOTREACHED*/ }
void done(void) { int i; int step; int size;
#ifdef USE_BDS if (awrite && m1) { bds_adone(out, m1); } #endif if (Sync > 0) sync(); if (Fsync > 0) fsync(out); #ifdef FLUSH if (Flush > 0) flush(); #endif stop(NULL, NULL); #ifdef RUSAGE if (ru != -1) rusage(); #endif if (hash || poff) { fprintf(stderr, "\n"); } if ((long)Label != -1) { fprintf(stderr, "%s", Label); } int_count <<= 2; switch (Print) { case 0: /* no print out */ break;
case 1: /* latency type print out */ latency((u64)(int_count / Bsize), (u64)Bsize); break;
case 2: /* microsecond per op print out */ micro("", (u64)(int_count / Bsize)); break;
case 3: /* kb / sec print out */ kb(int_count); break;
case 4: /* mb / sec print out */ mb(int_count); break;
case 5: /* Xgraph output */ bandwidth(int_count, 1, 0); break;
default: /* bandwidth print out */ bandwidth(int_count, 1, 1); break; } if (Rtmax != -1) { printf("READ operation latencies\n"); step = (Rtmax - Rtmin) / 10; if (rthist[0]) { printf("%d- ms: %d\n", Rtmin, rthist[0]); } for (i = 1, size = Rtmin; i <= 10; i++, size += step) { if (!rthist[i]) continue; printf("%d to %d ms: %d\n", size, size + step - 1, rthist[i]); } if (rthist[11]) { printf("%d+ ms: %d\n", Rtmax, rthist[11]); } } if (Wtmax != -1) { printf("WRITE operation latencies\n"); step = (Wtmax - Wtmin) / 10; if (wthist[0]) { printf("%d- ms: %d\n", Wtmin, wthist[0]); } for (i = 1, size = Wtmin; i <= 10; i++, size += step) { if (!wthist[i]) continue; printf("%d to %d ms: %d\n", size, size + step - 1, wthist[i]); } if (wthist[11]) { printf("%d+ ms: %d\n", Wtmax, wthist[11]); } } exit(0); }
u64 getarg(char *s, int ac, char **av) { register u64 len, i;
len = strlen(s);
for (i = 1; i < ac; ++i) { if (!strncmp(av[i], s, len)) { register u64 bs = ATOL(&av[i][len]);
switch (av[i][strlen(av[i]) - 1]) { case 'K': bs *= 1000; break; case 'k': bs <<= 10; break; case 'M': bs *= 1000000; break; case 'm': bs <<= 20; break; case 'G': bs *= 1000000000L; break; case 'g': bs <<= 30; break; }
if (!strncmp(av[i], "label", 5)) { return (u64)(u32)(&av[i][len]); /* HACK */ } if (!strncmp(av[i], "bs=", 3)) { return (u64)(bs); } return (bs); } } return ((u64)-1); }
char *output;
int getfile(char *s, int ac, char **av) { register int ret, len, i; int append = getarg("append=", ac, av) != -1; int notrunc = getarg("notrunc=", ac, av) != -1; int nocreate = getarg("nocreate=", ac, av) != -1; #ifdef O_SYNC int osync = getarg("osync=", ac, av) != -1; #endif int oflags;
len = strlen(s);
for (i = 1; i < ac; ++i) { if (!strncmp(av[i], s, len)) { if (av[i][0] == 'o') { if (!strcmp("of=internal", av[i])) return (-2); if (!strcmp("of=stdout", av[i])) return (1); if (!strcmp("of=1", av[i])) return (1); if (!strcmp("of=-", av[i])) return (1); if (!strcmp("of=stderr", av[i])) return (2); if (!strcmp("of=2", av[i])) return (2); oflags = O_WRONLY; oflags |= (notrunc || append) ? 0 : O_TRUNC; oflags |= nocreate ? 0 : O_CREAT; oflags |= append ? O_APPEND : 0; #ifdef O_SYNC oflags |= osync ? O_SYNC : 0; #endif ret = open(&av[i][len], oflags,0644); #ifdef O_DIRECT if ((getarg("odirect=", ac, av) != -1) || (getarg("direct=", ac, av) != -1)) { close(ret); ret = open(&av[i][len], oflags|O_DIRECT); awrite = getarg("awrite=", ac, av) != -1; } #endif if (ret == -1) error(&av[i][len]); #ifdef F_FSSETXATTR if (Realtime == 1) { struct fsxattr fsxattr; bzero(&fsxattr,sizeof(struct fsxattr)); fsxattr.fsx_xflags = 0x1; if (fcntl(ret,F_FSSETXATTR,&fsxattr)){ printf("WARNING: Could not make %s a real time file\n", &av[i][len]); } } #endif output = &av[i][len]; return (ret); } else { if (!strcmp("if=internal", av[i])) return (-2); if (!strcmp("if=stdin", av[i])) return (0); if (!strcmp("if=0", av[i])) return (0); if (!strcmp("if=-", av[i])) return (0); ret = open(&av[i][len], 0); #ifdef O_DIRECT if ((getarg("idirect=", ac, av) != -1) || (getarg("direct=", ac, av) != -1)) { close(ret); ret = open(&av[i][len], O_RDONLY|O_DIRECT); } #endif if (ret == -1) error(&av[i][len]); return (ret); } } } return (-2); }
#ifdef FLUSH int warning(char *s) { if ((long)Label != -1) { fprintf(stderr, "%s: ", Label); } perror(s); return (-1); }
void flush(void) { int fd; struct stat sb; caddr_t where;
if (output == NULL || (fd = open(output, 2)) == -1) { warning("No output file"); return; } if (fstat(fd, &sb) == -1 || sb.st_size == 0) { warning(output); return; } where = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); msync(where, sb.st_size, MS_INVALIDATE); munmap(where, sb.st_size); } #endif
void error(char *s) { if ((long)Label != -1) { fprintf(stderr, "%s: ", Label); } perror(s); exit(1); } - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
|  |