/* Copyright (c) 2008-2012 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #define _GNU_SOURCE #define __USE_FILE_OFFSET64 #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include struct state { char need_op_write : 1; char need_op_read : 1; char need_iface_fileio : 1; char need_iface_xattr : 1; char need_mode_posix : 1; char prefix[512]; long int count; size_t block_size; char *specfile; long int io_size; }; #define MEASURE(func, arg) measure(func, #func, arg) void tv_difference(struct timeval *tv_stop, struct timeval *tv_start, struct timeval *tv_diff) { if (tv_stop->tv_usec < tv_start->tv_usec) { tv_diff->tv_usec = (tv_stop->tv_usec + 1000000) - tv_start->tv_usec; tv_diff->tv_sec = (tv_stop->tv_sec - 1 - tv_start->tv_sec); } else { tv_diff->tv_usec = tv_stop->tv_usec - tv_start->tv_usec; tv_diff->tv_sec = tv_stop->tv_sec - tv_start->tv_sec; } } void measure(int (*func)(struct state *state), char *func_name, struct state *state) { struct timeval tv_start, tv_stop, tv_diff; state->io_size = 0; long int count; gettimeofday(&tv_start, NULL); count = func(state); gettimeofday(&tv_stop, NULL); tv_difference(&tv_stop, &tv_start, &tv_diff); fprintf(stdout, "%s: count=%ld, size=%ld, time=%ld:%ld\n", func_name, count, state->io_size, tv_diff.tv_sec, tv_diff.tv_usec); } static error_t parse_opts(int key, char *arg, struct argp_state *_state) { struct state *state = _state->input; switch (key) { case 'o': if (strcasecmp(arg, "read") == 0) { state->need_op_write = 0; state->need_op_read = 1; } else if (strcasecmp(arg, "write") == 0) { state->need_op_write = 1; state->need_op_read = 0; } else if (strcasecmp(arg, "both") == 0) { state->need_op_write = 1; state->need_op_read = 1; } else { fprintf(stderr, "unknown op: %s\n", arg); return -1; } break; case 'i': if (strcasecmp(arg, "fileio") == 0) { state->need_iface_fileio = 1; state->need_iface_xattr = 0; } else if (strcasecmp(arg, "xattr") == 0) { state->need_iface_fileio = 0; state->need_iface_xattr = 1; } else if (strcasecmp(arg, "both") == 0) { state->need_iface_fileio = 1; state->need_iface_xattr = 1; } else { fprintf(stderr, "unknown interface: %s\n", arg); return -1; } break; case 'b': { size_t block_size = atoi(arg); if (!block_size) { fprintf(stderr, "incorrect size: %s\n", arg); return -1; } state->block_size = block_size; } break; case 's': state->specfile = strdup(arg); break; case 'p': fprintf(stderr, "using prefix: %s\n", arg); strncpy(state->prefix, arg, 512); break; case 'c': { long count = atol(arg); if (!count) { fprintf(stderr, "incorrect count: %s\n", arg); return -1; } state->count = count; } break; case ARGP_KEY_NO_ARGS: break; case ARGP_KEY_ARG: break; } return 0; } int do_mode_posix_iface_fileio_write(struct state *state) { long int i; int ret = -1; char block[state->block_size]; for (i = 0; i < state->count; i++) { int fd = -1; char filename[512]; sprintf(filename, "%s.%06ld", state->prefix, i); fd = open(filename, O_CREAT | O_WRONLY, 00600); if (fd == -1) { fprintf(stderr, "open(%s) => %s\n", filename, strerror(errno)); break; } ret = write(fd, block, state->block_size); if (ret != state->block_size) { fprintf(stderr, "write (%s) => %d/%s\n", filename, ret, strerror(errno)); close(fd); break; } close(fd); state->io_size += ret; } return i; } int do_mode_posix_iface_fileio_read(struct state *state) { long int i; int ret = -1; char block[state->block_size]; for (i = 0; i < state->count; i++) { int fd = -1; char filename[512]; sprintf(filename, "%s.%06ld", state->prefix, i); fd = open(filename, O_RDONLY); if (fd == -1) { fprintf(stderr, "open(%s) => %s\n", filename, strerror(errno)); break; } ret = read(fd, block, state->block_size); if (ret == -1) { fprintf(stderr, "read(%s) => %d/%s\n", filename, ret, strerror(errno)); close(fd); break; } close(fd); state->io_size += ret; } return i; } int do_mode_posix_iface_fileio(struct state *state) { if (state->need_op_write) MEASURE(do_mode_posix_iface_fileio_write, state); if (state->need_op_read) MEASURE(do_mode_posix_iface_fileio_read, state); return 0; } int do_mode_posix_iface_xattr_write(struct state *state) { long int i; int ret = -1; char block[state->block_size]; char *dname = NULL, *dirc = NULL; char *bname = NULL, *basec = NULL; dirc = strdup(state->prefix); basec = strdup(state->prefix); dname = dirname(dirc); bname = basename(basec); for (i = 0; i < state->count; i++) { char key[512]; sprintf(key, "glusterfs.file.%s.%06ld", bname, i); ret = lsetxattr(dname, key, block, state->block_size, 0); if (ret != 0) { fprintf(stderr, "lsetxattr (%s, %s, %p) => %s\n", dname, key, block, strerror(errno)); break; } state->io_size += state->block_size; } free(dirc); free(basec); return i; } int do_mode_posix_iface_xattr_read(struct state *state) { long int i; int ret = -1; char block[state->block_size]; char *dname = NULL, *dirc = NULL; char *bname = NULL, *basec = NULL; dirc = strdup(state->prefix); basec = strdup(state->prefix); dname = dirname(dirc); bname = basename(basec); for (i = 0; i < state->count; i++) { char key[512]; sprintf(key, "glusterfs.file.%s.%06ld", bname, i); ret = lgetxattr(dname, key, block, state->block_size); if (ret < 0) { fprintf(stderr, "lgetxattr (%s, %s, %p) => %s\n", dname, key, block, strerror(errno)); break; } state->io_size += ret; } return i; } int do_mode_posix_iface_xattr(struct state *state) { if (state->need_op_write) MEASURE(do_mode_posix_iface_xattr_write, state); if (state->need_op_read) MEASURE(do_mode_posix_iface_xattr_read, state); return 0; } int do_mode_posix(struct state *state) { if (state->need_iface_fileio) do_mode_posix_iface_fileio(state); if (state->need_iface_xattr) do_mode_posix_iface_xattr(state); return 0; } int do_actions(struct state *state) { if (state->need_mode_posix) do_mode_posix(state); return 0; } static struct argp_option options[] = { {"op", 'o', "OPERATIONS", 0, "WRITE|READ|BOTH - defaults to BOTH"}, {"iface", 'i', "INTERFACE", 0, "FILEIO|XATTR|BOTH - defaults to FILEIO"}, {"block", 'b', "BLOCKSIZE", 0, " - defaults to 4096"}, {"specfile", 's', "SPECFILE", 0, "absolute path to specfile"}, {"prefix", 'p', "PREFIX", 0, "filename prefix"}, {"count", 'c', "COUNT", 0, "number of files"}, {0, 0, 0, 0, 0}}; static struct argp argp = {options, parse_opts, "tool", "tool to benchmark small file performance"}; int main(int argc, char *argv[]) { struct state state = { 0, }; state.need_op_write = 1; state.need_op_read = 1; state.need_iface_fileio = 1; state.need_iface_xattr = 0; state.need_mode_posix = 1; state.block_size = 4096; strcpy(state.prefix, "tmpfile"); state.count = 1048576; if (argp_parse(&argp, argc, argv, 0, 0, &state) != 0) { fprintf(stderr, "argp_parse() failed\n"); return 1; } do_actions(&state); return 0; }