xattrtest: allow verify with -R and other improvements

- Use a fixed buffer of random bytes when random xattr values are in
  effect.  This eliminates the potential performance bottleneck of
  reading from /dev/urandom for each file. This also allows us to
  verify xattrs in random value mode.

- Show the rate of operations per second in addition to elapsed time
  for each phase of the test. This may be useful for benchmarking.

- Set default xattr size to 6 so that verify doesn't fail if user
  doesn't specify a size. We need at least six bytes to store the
  leading "size=X" string that is used for verification.

- Allow user to execute just one phase of the test. Acceptable
  values for -o and their meanings are:

   1 - run the create phase
   2 - run the setxattr phase
   3 - run the getxattr phase
   4 - run the unlink phase

Signed-off-by: Ned Bass <bass6@llnl.gov>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
This commit is contained in:
Ned Bass 2015-04-10 15:17:30 -07:00 committed by Brian Behlendorf
parent d14fa5dba1
commit 81285582b9
1 changed files with 123 additions and 67 deletions

View File

@ -51,7 +51,7 @@ extern char *program_invocation_short_name;
program_invocation_short_name, __FILE__, __LINE__, \ program_invocation_short_name, __FILE__, __LINE__, \
__func__, ## __VA_ARGS__); __func__, ## __VA_ARGS__);
static const char shortopts[] = "hvycdn:f:x:s:p:t:e:rRk"; static const char shortopts[] = "hvycdn:f:x:s:p:t:e:rRko:";
static const struct option longopts[] = { static const struct option longopts[] = {
{ "help", no_argument, 0, 'h' }, { "help", no_argument, 0, 'h' },
{ "verbose", no_argument, 0, 'v' }, { "verbose", no_argument, 0, 'v' },
@ -68,9 +68,19 @@ static const struct option longopts[] = {
{ "random", no_argument, 0, 'r' }, { "random", no_argument, 0, 'r' },
{ "randomvalue", no_argument, 0, 'R' }, { "randomvalue", no_argument, 0, 'R' },
{ "keep", no_argument, 0, 'k' }, { "keep", no_argument, 0, 'k' },
{ "only", required_argument, 0, 'o' },
{ 0, 0, 0, 0 } { 0, 0, 0, 0 }
}; };
enum phases {
PHASE_ALL = 0,
PHASE_CREATE,
PHASE_SETXATTR,
PHASE_GETXATTR,
PHASE_UNLINK,
PHASE_INVAL
};
static int verbose = 0; static int verbose = 0;
static int verify = 0; static int verify = 0;
static int synccaches = 0; static int synccaches = 0;
@ -78,18 +88,22 @@ static int dropcaches = 0;
static int nth = 0; static int nth = 0;
static int files = 1000; static int files = 1000;
static int xattrs = 1; static int xattrs = 1;
static int size = 1; static int size = 6;
static int size_is_random = 0; static int size_is_random = 0;
static int value_is_random = 0; static int value_is_random = 0;
static int keep_files = 0; static int keep_files = 0;
static int phase = PHASE_ALL;
static char path[PATH_MAX] = "/tmp/xattrtest"; static char path[PATH_MAX] = "/tmp/xattrtest";
static char script[PATH_MAX] = "/bin/true"; static char script[PATH_MAX] = "/bin/true";
static char xattrbytes[XATTR_SIZE_MAX];
static int static int
usage(int argc, char **argv) { usage(int argc, char **argv) {
fprintf(stderr, fprintf(stderr,
"usage: %s [-hvycdrRk] [-n <nth>] [-f <files>] [-x <xattrs>]\n" "usage: %s [-hvycdrRk] [-n <nth>] [-f <files>] [-x <xattrs>]\n"
" [-s <bytes>] [-p <path>] [-t <script> ]\n", argv[0]); " [-s <bytes>] [-p <path>] [-t <script> ] [-o <phase>]\n",
argv[0]);
fprintf(stderr, fprintf(stderr,
" --help -h This help\n" " --help -h This help\n"
" --verbose -v Increase verbosity\n" " --verbose -v Increase verbosity\n"
@ -105,9 +119,12 @@ usage(int argc, char **argv) {
" --seed -e <seed> Random seed value\n" " --seed -e <seed> Random seed value\n"
" --random -r Randomly sized xattrs [16-size]\n" " --random -r Randomly sized xattrs [16-size]\n"
" --randomvalue -R Random xattr values\n" " --randomvalue -R Random xattr values\n"
" --keep -k Don't unlink files\n\n"); " --keep -k Don't unlink files\n"
" --only -o <num> Only run phase N\n"
" 0=all, 1=create, 2=setxattr,\n"
" 3=getxattr, 4=unlink\n\n");
return (0); return (1);
} }
static int static int
@ -115,7 +132,7 @@ parse_args(int argc, char **argv)
{ {
long seed = time(NULL); long seed = time(NULL);
int c; int c;
int rc; int rc = 0;
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
switch (c) { switch (c) {
@ -126,9 +143,9 @@ parse_args(int argc, char **argv)
break; break;
case 'y': case 'y':
verify = 1; verify = 1;
if (value_is_random != 0) { if (phase != PHASE_ALL) {
fprintf(stderr, fprintf(stderr,
"Error: -y and -R are incompatible.\n"); "Error: -y and -o are incompatible.\n");
rc = 1; rc = 1;
} }
break; break;
@ -144,8 +161,8 @@ parse_args(int argc, char **argv)
case 's': case 's':
size = strtol(optarg, NULL, 0); size = strtol(optarg, NULL, 0);
if (size > XATTR_SIZE_MAX) { if (size > XATTR_SIZE_MAX) {
fprintf(stderr, "Error: the size may not be " fprintf(stderr, "Error: the -s value may not "
"greater than %d\n", XATTR_SIZE_MAX); "be greater than %d\n", XATTR_SIZE_MAX);
rc = 1; rc = 1;
} }
break; break;
@ -169,15 +186,24 @@ parse_args(int argc, char **argv)
break; break;
case 'R': case 'R':
value_is_random = 1; value_is_random = 1;
if (verify != 0) {
fprintf(stderr,
"Error: -y and -R are incompatible.\n");
rc = 1;
}
break; break;
case 'k': case 'k':
keep_files = 1; keep_files = 1;
break; break;
case 'o':
phase = strtol(optarg, NULL, 0);
if (phase <= PHASE_ALL || phase >= PHASE_INVAL) {
fprintf(stderr, "Error: the -o value must be "
"greater than %d and less than %d\n",
PHASE_ALL, PHASE_INVAL);
rc = 1;
}
if (verify == 1) {
fprintf(stderr,
"Error: -y and -o are incompatible.\n");
rc = 1;
}
break;
default: default:
rc = 1; rc = 1;
break; break;
@ -204,6 +230,7 @@ parse_args(int argc, char **argv)
fprintf(stdout, "random size: %d\n", size_is_random); fprintf(stdout, "random size: %d\n", size_is_random);
fprintf(stdout, "random value: %d\n", value_is_random); fprintf(stdout, "random value: %d\n", value_is_random);
fprintf(stdout, "keep: %d\n", keep_files); fprintf(stdout, "keep: %d\n", keep_files);
fprintf(stdout, "only: %d\n", phase);
fprintf(stdout, "%s", "\n"); fprintf(stdout, "%s", "\n");
} }
@ -319,12 +346,22 @@ timeval_sub(struct timeval *delta, struct timeval *tv1, struct timeval *tv2)
tv1->tv_usec - tv2->tv_usec); tv1->tv_usec - tv2->tv_usec);
} }
static double
timeval_sub_seconds(struct timeval *tv1, struct timeval *tv2)
{
struct timeval delta;
timeval_sub(&delta, tv1, tv2);
return ((double)delta.tv_usec / USEC_PER_SEC + delta.tv_sec);
}
static int static int
create_files(void) create_files(void)
{ {
int i, rc; int i, rc;
char *file = NULL; char *file = NULL;
struct timeval start, stop, delta; struct timeval start, stop;
double seconds;
file = malloc(PATH_MAX); file = malloc(PATH_MAX);
if (file == NULL) { if (file == NULL) {
@ -366,9 +403,9 @@ create_files(void)
} }
(void) gettimeofday(&stop, NULL); (void) gettimeofday(&stop, NULL);
timeval_sub(&delta, &stop, &start); seconds = timeval_sub_seconds(&stop, &start);
fprintf(stdout, "create: %d.%d seconds\n", fprintf(stdout, "create: %f seconds %f creates/second\n",
(int)delta.tv_sec, (int)delta.tv_usec); seconds, files / seconds);
rc = post_hook("post"); rc = post_hook("post");
out: out:
@ -408,7 +445,8 @@ setxattrs(void)
char name[XATTR_NAME_MAX]; char name[XATTR_NAME_MAX];
char *value = NULL; char *value = NULL;
char *file = NULL; char *file = NULL;
struct timeval start, stop, delta; struct timeval start, stop;
double seconds;
value = malloc(XATTR_SIZE_MAX); value = malloc(XATTR_SIZE_MAX);
if (value == NULL) { if (value == NULL) {
@ -439,19 +477,9 @@ setxattrs(void)
rnd_size = (random() % (size - 16)) + 16; rnd_size = (random() % (size - 16)) + 16;
(void) sprintf(name, "user.%d", j); (void) sprintf(name, "user.%d", j);
if (value_is_random) { shift = sprintf(value, "size=%d ", rnd_size);
rc = get_random_bytes(value, rnd_size); memcpy(value + shift, xattrbytes,
if (rc < rnd_size) { sizeof (xattrbytes) - shift);
ERROR("Error %d: get_random_bytes() "
"wanted %d got %d\n", errno,
rnd_size, rc);
goto out;
}
} else {
shift = sprintf(value, "size=%d ", rnd_size);
memset(value + shift, 'x', XATTR_SIZE_MAX -
shift);
}
rc = lsetxattr(file, name, value, rnd_size, 0); rc = lsetxattr(file, name, value, rnd_size, 0);
if (rc == -1) { if (rc == -1) {
@ -463,9 +491,9 @@ setxattrs(void)
} }
(void) gettimeofday(&stop, NULL); (void) gettimeofday(&stop, NULL);
timeval_sub(&delta, &stop, &start); seconds = timeval_sub_seconds(&stop, &start);
fprintf(stdout, "setxattr: %d.%d seconds\n", fprintf(stdout, "setxattr: %f seconds %f setxattrs/second\n",
(int)delta.tv_sec, (int)delta.tv_usec); seconds, (files * xattrs) / seconds);
rc = post_hook("post"); rc = post_hook("post");
out: out:
@ -484,9 +512,12 @@ getxattrs(void)
int i, j, rnd_size, shift, rc = 0; int i, j, rnd_size, shift, rc = 0;
char name[XATTR_NAME_MAX]; char name[XATTR_NAME_MAX];
char *verify_value = NULL; char *verify_value = NULL;
char *verify_string;
char *value = NULL; char *value = NULL;
char *value_string;
char *file = NULL; char *file = NULL;
struct timeval start, stop, delta; struct timeval start, stop;
double seconds;
verify_value = malloc(XATTR_SIZE_MAX); verify_value = malloc(XATTR_SIZE_MAX);
if (verify_value == NULL) { if (verify_value == NULL) {
@ -504,6 +535,9 @@ getxattrs(void)
goto out; goto out;
} }
verify_string = value_is_random ? "<random>" : verify_value;
value_string = value_is_random ? "<random>" : value;
file = malloc(PATH_MAX); file = malloc(PATH_MAX);
if (file == NULL) { if (file == NULL) {
rc = ENOMEM; rc = ENOMEM;
@ -530,28 +564,30 @@ getxattrs(void)
goto out; goto out;
} }
if (verify) { if (!verify)
sscanf(value, "size=%d [a-z]", &rnd_size); continue;
shift = sprintf(verify_value, "size=%d ",
rnd_size);
memset(verify_value + shift, 'x',
XATTR_SIZE_MAX - shift);
if (rnd_size != rc || sscanf(value, "size=%d [a-z]", &rnd_size);
memcmp(verify_value, value, rnd_size)) { shift = sprintf(verify_value, "size=%d ",
ERROR("Error %d: verify failed\n " rnd_size);
"verify: %s\nvalue: %s\n", memcpy(verify_value + shift, xattrbytes,
EINVAL, verify_value, value); sizeof (xattrbytes) - shift);
goto out;
} if (rnd_size != rc ||
memcmp(verify_value, value, rnd_size)) {
ERROR("Error %d: verify failed\n "
"verify: %s\n value: %s\n", EINVAL,
verify_string, value_string);
rc = 1;
goto out;
} }
} }
} }
(void) gettimeofday(&stop, NULL); (void) gettimeofday(&stop, NULL);
timeval_sub(&delta, &stop, &start); seconds = timeval_sub_seconds(&stop, &start);
fprintf(stdout, "getxattr: %d.%d seconds\n", fprintf(stdout, "getxattr: %f seconds %f getxattrs/second\n",
(int)delta.tv_sec, (int)delta.tv_usec); seconds, (files * xattrs) / seconds);
rc = post_hook("post"); rc = post_hook("post");
out: out:
@ -572,7 +608,8 @@ unlink_files(void)
{ {
int i, rc; int i, rc;
char *file = NULL; char *file = NULL;
struct timeval start, stop, delta; struct timeval start, stop;
double seconds;
file = malloc(PATH_MAX); file = malloc(PATH_MAX);
if (file == NULL) { if (file == NULL) {
@ -598,9 +635,9 @@ unlink_files(void)
} }
(void) gettimeofday(&stop, NULL); (void) gettimeofday(&stop, NULL);
timeval_sub(&delta, &stop, &start); seconds = timeval_sub_seconds(&stop, &start);
fprintf(stdout, "unlink: %d.%d seconds\n", fprintf(stdout, "unlink: %f seconds %f unlinks/second\n",
(int)delta.tv_sec, (int)delta.tv_usec); seconds, files / seconds);
rc = post_hook("post"); rc = post_hook("post");
out: out:
@ -619,19 +656,38 @@ main(int argc, char **argv)
if (rc) if (rc)
return (rc); return (rc);
rc = create_files(); if (value_is_random) {
if (rc) size_t rndsz = sizeof (xattrbytes);
return (rc);
rc = setxattrs(); rc = get_random_bytes(xattrbytes, rndsz);
if (rc) if (rc < rndsz) {
return (rc); ERROR("Error %d: get_random_bytes() wanted %zd "
"got %d\n", errno, rndsz, rc);
return (rc);
}
} else {
memset(xattrbytes, 'x', sizeof (xattrbytes));
}
rc = getxattrs(); if (phase == PHASE_ALL || phase == PHASE_CREATE) {
if (rc) rc = create_files();
return (rc); if (rc)
return (rc);
}
if (!keep_files) { if (phase == PHASE_ALL || phase == PHASE_SETXATTR) {
rc = setxattrs();
if (rc)
return (rc);
}
if (phase == PHASE_ALL || phase == PHASE_GETXATTR) {
rc = getxattrs();
if (rc)
return (rc);
}
if (!keep_files && (phase == PHASE_ALL || phase == PHASE_UNLINK)) {
rc = unlink_files(); rc = unlink_files();
if (rc) if (rc)
return (rc); return (rc);