Merge commit 'refs/remotes/origin/linux-zpios' into HEAD
This commit is contained in:
commit
b7ce372906
17
.topmsg
17
.topmsg
|
@ -1,19 +1,6 @@
|
|||
From: Brian Behlendorf <behlendorf1@llnl.gov>
|
||||
Subject: [PATCH] zfs branch
|
||||
Subject: [PATCH] linux zpios
|
||||
|
||||
Merged result of all changes which are relevant to both Solaris
|
||||
and Linux builds of the ZFS code. These are changes where there
|
||||
is a reasonable chance they will be accepted upstream.
|
||||
|
||||
Additionally, since this is effectively the root of the linux
|
||||
ZFS tree the core linux build system is added here. This
|
||||
includes autogen.sh, configure.ac, m4 macros, some scripts/*,
|
||||
and makefiles for all the core ZFS components. Linux-only
|
||||
features which require tweaks to the build system should appear
|
||||
on the relevant topic branches. All autotools products which
|
||||
result from autogen.sh are commited to the linux-configure-branch.
|
||||
|
||||
This branch also contains the META, ChangeLog, AUTHORS, TODO,
|
||||
and README, files.
|
||||
Linux kernel implementation of PIOS test app.
|
||||
|
||||
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
|
||||
|
|
|
@ -120,7 +120,7 @@
|
|||
- Minor build system improvements
|
||||
- Minor script improvements
|
||||
- Create a full copy and not a link tree with quilt
|
||||
- KPIOS_MAJOR changed from 231 to 232
|
||||
- ZPIOS_MAJOR changed from 231 to 232
|
||||
- BIO_RW_BARRIER flag removed from IO request
|
||||
|
||||
2008-06-30 Brian Behlendorf <behlendorf1@llnl.gov>
|
||||
|
|
|
@ -1 +1 @@
|
|||
SUBDIRS = zfs zpool zdb zinject ztest
|
||||
SUBDIRS = zfs zpool zdb zinject ztest zpios
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
include $(top_srcdir)/config/Rules.am
|
||||
|
||||
DEFAULT_INCLUDES += \
|
||||
-I${top_srcdir}/module/zpios/include
|
||||
|
||||
sbin_PROGRAMS = zpios
|
||||
|
||||
zpios_SOURCES = \
|
||||
$(top_srcdir)/cmd/zpios/zpios_main.c \
|
||||
$(top_srcdir)/cmd/zpios/zpios_util.c \
|
||||
$(top_srcdir)/cmd/zpios/zpios.h
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* This file is part of the ZFS Linux port.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* LLNL-CODE-403049
|
||||
*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
#ifndef _ZPIOS_H
|
||||
#define _ZPIOS_H
|
||||
|
||||
#include <zpios-ctl.h>
|
||||
|
||||
#define VERSION_SIZE 64
|
||||
|
||||
/* Regular expressions */
|
||||
#define REGEX_NUMBERS "^[0-9]*[0-9]$"
|
||||
#define REGEX_NUMBERS_COMMA "^([0-9]+,)*[0-9]+$"
|
||||
#define REGEX_SIZE "^[0-9][0-9]*[kmgt]$"
|
||||
#define REGEX_SIZE_COMMA "^([0-9][0-9]*[kmgt]+,)*[0-9][0-9]*[kmgt]$"
|
||||
|
||||
/* Flags for low, high, incr */
|
||||
#define FLAG_SET 0x01
|
||||
#define FLAG_LOW 0x02
|
||||
#define FLAG_HIGH 0x04
|
||||
#define FLAG_INCR 0x08
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
#define KB (1024)
|
||||
#define MB (KB * 1024)
|
||||
#define GB (MB * 1024)
|
||||
#define TB (GB * 1024)
|
||||
|
||||
#define KMGT_SIZE 16
|
||||
|
||||
/* All offsets, sizes and counts can be passed to the application in
|
||||
* multiple ways.
|
||||
* 1. a value (stored in val[0], val_count will be 1)
|
||||
* 2. a comma separated list of values (stored in val[], using val_count)
|
||||
* 3. a range and block sizes, low, high, factor (val_count must be 0)
|
||||
*/
|
||||
typedef struct pios_range_repeat {
|
||||
uint64_t val[32]; /* Comma sep array, or low, high, inc */
|
||||
uint64_t val_count; /* Num of values */
|
||||
uint64_t val_low;
|
||||
uint64_t val_high;
|
||||
uint64_t val_inc_perc;
|
||||
uint64_t next_val; /* Used for multiple runs in get_next() */
|
||||
} range_repeat_t;
|
||||
|
||||
typedef struct cmd_args {
|
||||
range_repeat_t T; /* Thread count */
|
||||
range_repeat_t N; /* Region count */
|
||||
range_repeat_t O; /* Offset count */
|
||||
range_repeat_t C; /* Chunksize */
|
||||
range_repeat_t S; /* Regionsize */
|
||||
|
||||
const char *pool; /* Pool */
|
||||
uint32_t flags; /* Flags */
|
||||
uint32_t io_type; /* DMUIO only */
|
||||
uint32_t verbose; /* Verbose */
|
||||
uint32_t human_readable; /* Human readable output */
|
||||
|
||||
uint64_t regionnoise; /* Region noise */
|
||||
uint64_t chunknoise; /* Chunk noise */
|
||||
uint64_t thread_delay; /* Thread delay */
|
||||
|
||||
char pre[ZPIOS_PATH_SIZE]; /* Pre-exec hook */
|
||||
char post[ZPIOS_PATH_SIZE]; /* Post-exec hook */
|
||||
char log[ZPIOS_PATH_SIZE]; /* Requested log dir */
|
||||
|
||||
/* Control */
|
||||
int current_id;
|
||||
uint64_t current_T;
|
||||
uint64_t current_N;
|
||||
uint64_t current_C;
|
||||
uint64_t current_S;
|
||||
uint64_t current_O;
|
||||
|
||||
uint32_t rc;
|
||||
} cmd_args_t;
|
||||
|
||||
int set_count(char *pattern1, char *pattern2, range_repeat_t *range,
|
||||
char *optarg, uint32_t *flags, char *arg);
|
||||
int set_lhi(char *pattern, range_repeat_t *range, char *optarg,
|
||||
int flag, uint32_t *flag_thread, char *arg);
|
||||
int set_noise(uint64_t *noise, char *optarg, char *arg);
|
||||
int set_load_params(cmd_args_t *args, char *optarg);
|
||||
int check_mutual_exclusive_command_lines(uint32_t flag, char *arg);
|
||||
void print_stats_header(void);
|
||||
void print_stats(cmd_args_t *args, zpios_cmd_t *cmd);
|
||||
|
||||
#endif /* _ZPIOS_H */
|
|
@ -0,0 +1,621 @@
|
|||
/*
|
||||
* This file is part of the ZFS Linux port.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* LLNL-CODE-403049
|
||||
*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*
|
||||
* Kernel PIOS DMU implemenation originally derived from PIOS test code.
|
||||
* Character control interface derived from SPL code.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include "zpios.h"
|
||||
|
||||
static const char short_opt[] = "t:l:h:e:n:i:j:k:o:m:q:r:c:a:b:g:s:A:B:C:"
|
||||
"L:p:xP:R:G:I:N:T:VzOHv?";
|
||||
static const struct option long_opt[] = {
|
||||
{"threadcount", required_argument, 0, 't' },
|
||||
{"threadcount_low", required_argument, 0, 'l' },
|
||||
{"threadcount_high", required_argument, 0, 'h' },
|
||||
{"threadcount_incr", required_argument, 0, 'e' },
|
||||
{"regioncount", required_argument, 0, 'n' },
|
||||
{"regioncount_low", required_argument, 0, 'i' },
|
||||
{"regioncount_high", required_argument, 0, 'j' },
|
||||
{"regioncount_incr", required_argument, 0, 'k' },
|
||||
{"offset", required_argument, 0, 'o' },
|
||||
{"offset_low", required_argument, 0, 'm' },
|
||||
{"offset_high", required_argument, 0, 'q' },
|
||||
{"offset_incr", required_argument, 0, 'r' },
|
||||
{"chunksize", required_argument, 0, 'c' },
|
||||
{"chunksize_low", required_argument, 0, 'a' },
|
||||
{"chunksize_high", required_argument, 0, 'b' },
|
||||
{"chunksize_incr", required_argument, 0, 'g' },
|
||||
{"regionsize", required_argument, 0, 's' },
|
||||
{"regionsize_low", required_argument, 0, 'A' },
|
||||
{"regionsize_high", required_argument, 0, 'B' },
|
||||
{"regionsize_incr", required_argument, 0, 'C' },
|
||||
{"load", required_argument, 0, 'L' },
|
||||
{"pool", required_argument, 0, 'p' },
|
||||
{"cleanup", no_argument, 0, 'x' },
|
||||
{"prerun", required_argument, 0, 'P' },
|
||||
{"postrun", required_argument, 0, 'R' },
|
||||
{"log", required_argument, 0, 'G' },
|
||||
{"regionnoise", required_argument, 0, 'I' },
|
||||
{"chunknoise", required_argument, 0, 'N' },
|
||||
{"threaddelay", required_argument, 0, 'T' },
|
||||
{"verify", no_argument, 0, 'V' },
|
||||
{"zerocopy", no_argument, 0, 'z' },
|
||||
{"nowait", no_argument, 0, 'O' },
|
||||
{"human-readable", no_argument, 0, 'H' },
|
||||
{"verbose", no_argument, 0, 'v' },
|
||||
{"help", no_argument, 0, '?' },
|
||||
{ 0, 0, 0, 0 },
|
||||
};
|
||||
|
||||
static int zpiosctl_fd; /* Control file descriptor */
|
||||
static char zpios_version[VERSION_SIZE]; /* Kernel version string */
|
||||
static char *zpios_buffer = NULL; /* Scratch space area */
|
||||
static int zpios_buffer_size = 0; /* Scratch space size */
|
||||
|
||||
static int
|
||||
usage(void)
|
||||
{
|
||||
fprintf(stderr, "Usage: zpios\n");
|
||||
fprintf(stderr,
|
||||
" --threadcount -t =values\n"
|
||||
" --threadcount_low -l =value\n"
|
||||
" --threadcount_high -h =value\n"
|
||||
" --threadcount_incr -e =value\n"
|
||||
" --regioncount -n =values\n"
|
||||
" --regioncount_low -i =value\n"
|
||||
" --regioncount_high -j =value\n"
|
||||
" --regioncount_incr -k =value\n"
|
||||
" --offset -o =values\n"
|
||||
" --offset_low -m =value\n"
|
||||
" --offset_high -q =value\n"
|
||||
" --offset_incr -r =value\n"
|
||||
" --chunksize -c =values\n"
|
||||
" --chunksize_low -a =value\n"
|
||||
" --chunksize_high -b =value\n"
|
||||
" --chunksize_incr -g =value\n"
|
||||
" --regionsize -s =values\n"
|
||||
" --regionsize_low -A =value\n"
|
||||
" --regionsize_high -B =value\n"
|
||||
" --regionsize_incr -C =value\n"
|
||||
" --load -L =dmuio|ssf|fpp\n"
|
||||
" --pool -p =pool name\n"
|
||||
" --cleanup -x\n"
|
||||
" --prerun -P =pre-command\n"
|
||||
" --postrun -R =post-command\n"
|
||||
" --log -G =log directory\n"
|
||||
" --regionnoise -I =shift\n"
|
||||
" --chunknoise -N =bytes\n"
|
||||
" --threaddelay -T =jiffies\n"
|
||||
" --verify -V\n"
|
||||
" --zerocopy -z\n"
|
||||
" --nowait -O\n"
|
||||
" --human-readable -H\n"
|
||||
" --verbose -v =increase verbosity\n"
|
||||
" --help -? =this help\n\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void args_fini(cmd_args_t *args)
|
||||
{
|
||||
assert(args != NULL);
|
||||
free(args);
|
||||
}
|
||||
|
||||
static cmd_args_t *
|
||||
args_init(int argc, char **argv)
|
||||
{
|
||||
cmd_args_t *args;
|
||||
uint32_t fl_th = 0;
|
||||
uint32_t fl_rc = 0;
|
||||
uint32_t fl_of = 0;
|
||||
uint32_t fl_rs = 0;
|
||||
uint32_t fl_cs = 0;
|
||||
int c, rc;
|
||||
|
||||
if (argc == 1) {
|
||||
usage();
|
||||
return (cmd_args_t *)NULL;
|
||||
}
|
||||
|
||||
/* Configure and populate the args structures */
|
||||
args = malloc(sizeof(*args));
|
||||
if (args == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(args, 0, sizeof(*args));
|
||||
|
||||
while ((c=getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
|
||||
rc = 0;
|
||||
|
||||
switch (c) {
|
||||
case 't': /* --thread count */
|
||||
rc = set_count(REGEX_NUMBERS, REGEX_NUMBERS_COMMA,
|
||||
&args->T, optarg, &fl_th, "threadcount");
|
||||
break;
|
||||
case 'l': /* --threadcount_low */
|
||||
rc = set_lhi(REGEX_NUMBERS, &args->T, optarg,
|
||||
FLAG_LOW, &fl_th, "threadcount_low");
|
||||
break;
|
||||
case 'h': /* --threadcount_high */
|
||||
rc = set_lhi(REGEX_NUMBERS, &args->T, optarg,
|
||||
FLAG_HIGH, &fl_th, "threadcount_high");
|
||||
break;
|
||||
case 'e': /* --threadcount_inc */
|
||||
rc = set_lhi(REGEX_NUMBERS, &args->T, optarg,
|
||||
FLAG_INCR, &fl_th, "threadcount_incr");
|
||||
break;
|
||||
case 'n': /* --regioncount */
|
||||
rc = set_count(REGEX_NUMBERS, REGEX_NUMBERS_COMMA,
|
||||
&args->N, optarg, &fl_rc, "regioncount");
|
||||
break;
|
||||
case 'i': /* --regioncount_low */
|
||||
rc = set_lhi(REGEX_NUMBERS, &args->N, optarg,
|
||||
FLAG_LOW, &fl_rc, "regioncount_low");
|
||||
break;
|
||||
case 'j': /* --regioncount_high */
|
||||
rc = set_lhi(REGEX_NUMBERS, &args->N, optarg,
|
||||
FLAG_HIGH, &fl_rc, "regioncount_high");
|
||||
break;
|
||||
case 'k': /* --regioncount_inc */
|
||||
rc = set_lhi(REGEX_NUMBERS, &args->N, optarg,
|
||||
FLAG_INCR, &fl_rc, "regioncount_incr");
|
||||
break;
|
||||
case 'o': /* --offset */
|
||||
rc = set_count(REGEX_SIZE, REGEX_SIZE_COMMA,
|
||||
&args->O, optarg, &fl_of, "offset");
|
||||
break;
|
||||
case 'm': /* --offset_low */
|
||||
rc = set_lhi(REGEX_SIZE, &args->O, optarg,
|
||||
FLAG_LOW, &fl_of, "offset_low");
|
||||
break;
|
||||
case 'q': /* --offset_high */
|
||||
rc = set_lhi(REGEX_SIZE, &args->O, optarg,
|
||||
FLAG_HIGH, &fl_of, "offset_high");
|
||||
break;
|
||||
case 'r': /* --offset_inc */
|
||||
rc = set_lhi(REGEX_NUMBERS, &args->O, optarg,
|
||||
FLAG_INCR, &fl_of, "offset_incr");
|
||||
break;
|
||||
case 'c': /* --chunksize */
|
||||
rc = set_count(REGEX_SIZE, REGEX_SIZE_COMMA,
|
||||
&args->C, optarg, &fl_cs, "chunksize");
|
||||
break;
|
||||
case 'a': /* --chunksize_low */
|
||||
rc = set_lhi(REGEX_SIZE, &args->C, optarg,
|
||||
FLAG_LOW, &fl_cs, "chunksize_low");
|
||||
break;
|
||||
case 'b': /* --chunksize_high */
|
||||
rc = set_lhi(REGEX_SIZE, &args->C, optarg,
|
||||
FLAG_HIGH, &fl_cs, "chunksize_high");
|
||||
break;
|
||||
case 'g': /* --chunksize_inc */
|
||||
rc = set_lhi(REGEX_NUMBERS, &args->C, optarg,
|
||||
FLAG_INCR, &fl_cs, "chunksize_incr");
|
||||
break;
|
||||
case 's': /* --regionsize */
|
||||
rc = set_count(REGEX_SIZE, REGEX_SIZE_COMMA,
|
||||
&args->S, optarg, &fl_rs, "regionsize");
|
||||
break;
|
||||
case 'A': /* --regionsize_low */
|
||||
rc = set_lhi(REGEX_SIZE, &args->S, optarg,
|
||||
FLAG_LOW, &fl_rs, "regionsize_low");
|
||||
break;
|
||||
case 'B': /* --regionsize_high */
|
||||
rc = set_lhi(REGEX_SIZE, &args->S, optarg,
|
||||
FLAG_HIGH, &fl_rs, "regionsize_high");
|
||||
break;
|
||||
case 'C': /* --regionsize_inc */
|
||||
rc = set_lhi(REGEX_NUMBERS, &args->S, optarg,
|
||||
FLAG_INCR, &fl_rs, "regionsize_incr");
|
||||
break;
|
||||
case 'L': /* --load */
|
||||
rc = set_load_params(args, optarg);
|
||||
break;
|
||||
case 'p': /* --pool */
|
||||
args->pool = optarg;
|
||||
break;
|
||||
case 'x': /* --cleanup */
|
||||
args->flags |= DMU_REMOVE;
|
||||
break;
|
||||
case 'P': /* --prerun */
|
||||
strncpy(args->pre, optarg, ZPIOS_PATH_SIZE - 1);
|
||||
break;
|
||||
case 'R': /* --postrun */
|
||||
strncpy(args->post, optarg, ZPIOS_PATH_SIZE - 1);
|
||||
break;
|
||||
case 'G': /* --log */
|
||||
strncpy(args->log, optarg, ZPIOS_PATH_SIZE - 1);
|
||||
break;
|
||||
case 'I': /* --regionnoise */
|
||||
rc = set_noise(&args->regionnoise, optarg, "regionnoise");
|
||||
break;
|
||||
case 'N': /* --chunknoise */
|
||||
rc = set_noise(&args->chunknoise, optarg, "chunknoise");
|
||||
break;
|
||||
case 'T': /* --threaddelay */
|
||||
rc = set_noise(&args->thread_delay, optarg, "threaddelay");
|
||||
break;
|
||||
case 'V': /* --verify */
|
||||
args->flags |= DMU_VERIFY;
|
||||
break;
|
||||
case 'z': /* --zerocopy */
|
||||
args->flags |= (DMU_WRITE_ZC | DMU_READ_ZC);
|
||||
break;
|
||||
case 'O': /* --nowait */
|
||||
args->flags |= DMU_WRITE_NOWAIT;
|
||||
break;
|
||||
case 'H': /* --human-readable */
|
||||
args->human_readable = 1;
|
||||
break;
|
||||
case 'v': /* --verbose */
|
||||
args->verbose++;
|
||||
break;
|
||||
case '?':
|
||||
rc = 1;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr,"Unknown option '%s'\n",argv[optind-1]);
|
||||
rc = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
usage();
|
||||
args_fini(args);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
check_mutual_exclusive_command_lines(fl_th, "threadcount");
|
||||
check_mutual_exclusive_command_lines(fl_rc, "regioncount");
|
||||
check_mutual_exclusive_command_lines(fl_of, "offset");
|
||||
check_mutual_exclusive_command_lines(fl_rs, "regionsize");
|
||||
check_mutual_exclusive_command_lines(fl_cs, "chunksize");
|
||||
|
||||
if (args->pool == NULL) {
|
||||
fprintf(stderr, "Error: Pool not specificed\n");
|
||||
usage();
|
||||
args_fini(args);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((args->flags & (DMU_WRITE_ZC | DMU_READ_ZC)) &&
|
||||
(args->flags & DMU_VERIFY)) {
|
||||
fprintf(stderr, "Error, --zerocopy incompatible --verify, "
|
||||
"used for performance analysis only\n");
|
||||
usage();
|
||||
args_fini(args);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
static int
|
||||
dev_clear(void)
|
||||
{
|
||||
zpios_cfg_t cfg;
|
||||
int rc;
|
||||
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.cfg_magic = ZPIOS_CFG_MAGIC;
|
||||
cfg.cfg_cmd = ZPIOS_CFG_BUFFER_CLEAR;
|
||||
cfg.cfg_arg1 = 0;
|
||||
|
||||
rc = ioctl(zpiosctl_fd, ZPIOS_CFG, &cfg);
|
||||
if (rc)
|
||||
fprintf(stderr, "Ioctl() error %lu / %d: %d\n",
|
||||
(unsigned long) ZPIOS_CFG, cfg.cfg_cmd, errno);
|
||||
|
||||
lseek(zpiosctl_fd, 0, SEEK_SET);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Passing a size of zero simply results in querying the current size */
|
||||
static int
|
||||
dev_size(int size)
|
||||
{
|
||||
zpios_cfg_t cfg;
|
||||
int rc;
|
||||
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.cfg_magic = ZPIOS_CFG_MAGIC;
|
||||
cfg.cfg_cmd = ZPIOS_CFG_BUFFER_SIZE;
|
||||
cfg.cfg_arg1 = size;
|
||||
|
||||
rc = ioctl(zpiosctl_fd, ZPIOS_CFG, &cfg);
|
||||
if (rc) {
|
||||
fprintf(stderr, "Ioctl() error %lu / %d: %d\n",
|
||||
(unsigned long) ZPIOS_CFG, cfg.cfg_cmd, errno);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return cfg.cfg_rc1;
|
||||
}
|
||||
|
||||
static void
|
||||
dev_fini(void)
|
||||
{
|
||||
if (zpios_buffer)
|
||||
free(zpios_buffer);
|
||||
|
||||
if (zpiosctl_fd != -1) {
|
||||
if (close(zpiosctl_fd) == -1) {
|
||||
fprintf(stderr, "Unable to close %s: %d\n",
|
||||
ZPIOS_DEV, errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
dev_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
zpiosctl_fd = open(ZPIOS_DEV, O_RDONLY);
|
||||
if (zpiosctl_fd == -1) {
|
||||
fprintf(stderr, "Unable to open %s: %d\n"
|
||||
"Is the zpios module loaded?\n", ZPIOS_DEV, errno);
|
||||
rc = errno;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((rc = dev_clear()))
|
||||
goto error;
|
||||
|
||||
if ((rc = dev_size(0)) < 0)
|
||||
goto error;
|
||||
|
||||
zpios_buffer_size = rc;
|
||||
zpios_buffer = (char *)malloc(zpios_buffer_size);
|
||||
if (zpios_buffer == NULL) {
|
||||
rc = ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
memset(zpios_buffer, 0, zpios_buffer_size);
|
||||
return 0;
|
||||
error:
|
||||
if (zpiosctl_fd != -1) {
|
||||
if (close(zpiosctl_fd) == -1) {
|
||||
fprintf(stderr, "Unable to close %s: %d\n",
|
||||
ZPIOS_DEV, errno);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
get_next(uint64_t *val, range_repeat_t *range)
|
||||
{
|
||||
/* if low, incr, high is given */
|
||||
if (range->val_count == 0) {
|
||||
*val = (range->val_low) +
|
||||
(range->val_low * range->next_val / 100);
|
||||
|
||||
if (*val > range->val_high)
|
||||
return 0; /* No more values, limit exceeded */
|
||||
|
||||
if (!range->next_val)
|
||||
range->next_val = range->val_inc_perc;
|
||||
else
|
||||
range->next_val = range->next_val+range->val_inc_perc;
|
||||
|
||||
return 1; /* more values to come */
|
||||
|
||||
/* if only one val is given */
|
||||
} else if (range->val_count == 1) {
|
||||
if (range->next_val)
|
||||
return 0; /* No more values, we only have one */
|
||||
|
||||
*val = range->val[0];
|
||||
range->next_val = 1;
|
||||
return 1; /* more values to come */
|
||||
|
||||
/* if comma separated values are given */
|
||||
} else if (range->val_count > 1) {
|
||||
if (range->next_val > range->val_count - 1)
|
||||
return 0; /* No more values, limit exceeded */
|
||||
|
||||
*val = range->val[range->next_val];
|
||||
range->next_val++;
|
||||
return 1; /* more values to come */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
run_one(cmd_args_t *args, uint32_t id, uint32_t T, uint32_t N,
|
||||
uint64_t C, uint64_t S, uint64_t O)
|
||||
{
|
||||
zpios_cmd_t *cmd;
|
||||
int rc, rc2, cmd_size;
|
||||
|
||||
dev_clear();
|
||||
|
||||
cmd_size = sizeof(zpios_cmd_t) + ((T + N + 1) * sizeof(zpios_stats_t));
|
||||
cmd = (zpios_cmd_t *)malloc(cmd_size);
|
||||
if (cmd == NULL)
|
||||
return ENOMEM;
|
||||
|
||||
memset(cmd, 0, cmd_size);
|
||||
cmd->cmd_magic = ZPIOS_CMD_MAGIC;
|
||||
strncpy(cmd->cmd_pool, args->pool, ZPIOS_NAME_SIZE - 1);
|
||||
strncpy(cmd->cmd_pre, args->pre, ZPIOS_PATH_SIZE - 1);
|
||||
strncpy(cmd->cmd_post, args->post, ZPIOS_PATH_SIZE - 1);
|
||||
strncpy(cmd->cmd_log, args->log, ZPIOS_PATH_SIZE - 1);
|
||||
cmd->cmd_id = id;
|
||||
cmd->cmd_chunk_size = C;
|
||||
cmd->cmd_thread_count = T;
|
||||
cmd->cmd_region_count = N;
|
||||
cmd->cmd_region_size = S;
|
||||
cmd->cmd_offset = O;
|
||||
cmd->cmd_region_noise = args->regionnoise;
|
||||
cmd->cmd_chunk_noise = args->chunknoise;
|
||||
cmd->cmd_thread_delay = args->thread_delay;
|
||||
cmd->cmd_flags = args->flags;
|
||||
cmd->cmd_data_size = (T + N + 1) * sizeof(zpios_stats_t);
|
||||
|
||||
rc = ioctl(zpiosctl_fd, ZPIOS_CMD, cmd);
|
||||
if (rc)
|
||||
args->rc = errno;
|
||||
|
||||
print_stats(args, cmd);
|
||||
|
||||
if (args->verbose) {
|
||||
rc2 = read(zpiosctl_fd, zpios_buffer, zpios_buffer_size - 1);
|
||||
if (rc2 < 0) {
|
||||
fprintf(stdout, "Error reading results: %d\n", rc2);
|
||||
} else if ((rc2 > 0) && (strlen(zpios_buffer) > 0)) {
|
||||
fprintf(stdout, "\n%s\n", zpios_buffer);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
free(cmd);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
run_offsets(cmd_args_t *args)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
while (rc == 0 && get_next(&args->current_O, &args->O)) {
|
||||
rc = run_one(args, args->current_id,
|
||||
args->current_T, args->current_N, args->current_C,
|
||||
args->current_S, args->current_O);
|
||||
args->current_id++;
|
||||
}
|
||||
|
||||
args->O.next_val = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
run_region_counts(cmd_args_t *args)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
while (rc == 0 && get_next((uint64_t *)&args->current_N, &args->N))
|
||||
rc = run_offsets(args);
|
||||
|
||||
args->N.next_val = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
run_region_sizes(cmd_args_t *args)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
while (rc == 0 && get_next(&args->current_S, &args->S)) {
|
||||
if (args->current_S < args->current_C) {
|
||||
fprintf(stderr, "Error: in any run chunksize can "
|
||||
"not be smaller than regionsize.\n");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
rc = run_region_counts(args);
|
||||
}
|
||||
|
||||
args->S.next_val = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
run_chunk_sizes(cmd_args_t *args)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
while (rc == 0 && get_next(&args->current_C, &args->C)) {
|
||||
rc = run_region_sizes(args);
|
||||
}
|
||||
|
||||
args->C.next_val = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
run_thread_counts(cmd_args_t *args)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
while (rc == 0 && get_next((uint64_t *)&args->current_T, &args->T))
|
||||
rc = run_chunk_sizes(args);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
cmd_args_t *args;
|
||||
int rc = 0;
|
||||
|
||||
/* Argument init and parsing */
|
||||
if ((args = args_init(argc, argv)) == NULL) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Device specific init */
|
||||
if ((rc = dev_init()))
|
||||
goto out;
|
||||
|
||||
/* Generic kernel version string */
|
||||
if (args->verbose)
|
||||
fprintf(stdout, "%s", zpios_version);
|
||||
|
||||
print_stats_header();
|
||||
rc = run_thread_counts(args);
|
||||
out:
|
||||
if (args != NULL)
|
||||
args_fini(args);
|
||||
|
||||
dev_fini();
|
||||
return rc;
|
||||
}
|
|
@ -0,0 +1,441 @@
|
|||
/*
|
||||
* This file is part of the ZFS Linux port.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* LLNL-CODE-403049
|
||||
*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*
|
||||
* Kernel PIOS DMU implemenation originally derived from PIOS test code.
|
||||
* Character control interface derived from SPL code.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <regex.h>
|
||||
#include "zpios.h"
|
||||
|
||||
/* extracts an unsigned int (64) and K,M,G,T from the string */
|
||||
/* and returns a 64 bit value converted to the proper units */
|
||||
static int
|
||||
kmgt_to_uint64(const char *str, uint64_t *val)
|
||||
{
|
||||
char *endptr;
|
||||
int rc = 0;
|
||||
|
||||
*val = strtoll(str, &endptr, 0);
|
||||
if ((str == endptr) && (*val == 0))
|
||||
return EINVAL;
|
||||
|
||||
switch (endptr[0]) {
|
||||
case 'k': case 'K':
|
||||
*val = (*val) << 10;
|
||||
break;
|
||||
case 'm': case 'M':
|
||||
*val = (*val) << 20;
|
||||
break;
|
||||
case 'g': case 'G':
|
||||
*val = (*val) << 30;
|
||||
break;
|
||||
case 't': case 'T':
|
||||
*val = (*val) << 40;
|
||||
break;
|
||||
case '\0':
|
||||
break;
|
||||
default:
|
||||
rc = EINVAL;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static char *
|
||||
uint64_to_kmgt(char *str, uint64_t val)
|
||||
{
|
||||
char postfix[] = "kmgt";
|
||||
int i = -1;
|
||||
|
||||
while ((val >= KB) && (i < 4)) {
|
||||
val = (val >> 10);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i >= 4)
|
||||
(void)snprintf(str, KMGT_SIZE-1, "inf");
|
||||
else
|
||||
(void)snprintf(str, KMGT_SIZE-1, "%lu%c", (unsigned long)val,
|
||||
(i == -1) ? '\0' : postfix[i]);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *
|
||||
kmgt_per_sec(char *str, uint64_t v, double t)
|
||||
{
|
||||
char postfix[] = "kmgt";
|
||||
double val = ((double)v) / t;
|
||||
int i = -1;
|
||||
|
||||
while ((val >= (double)KB) && (i < 4)) {
|
||||
val /= (double)KB;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i >= 4)
|
||||
(void)snprintf(str, KMGT_SIZE-1, "inf");
|
||||
else
|
||||
(void)snprintf(str, KMGT_SIZE-1, "%.2f%c", val,
|
||||
(i == -1) ? '\0' : postfix[i]);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *
|
||||
print_flags(char *str, uint32_t flags)
|
||||
{
|
||||
str[0] = (flags & DMU_WRITE) ? 'w' : '-';
|
||||
str[1] = (flags & DMU_READ) ? 'r' : '-';
|
||||
str[2] = (flags & DMU_VERIFY) ? 'v' : '-';
|
||||
str[3] = (flags & DMU_REMOVE) ? 'c' : '-';
|
||||
str[4] = (flags & DMU_FPP) ? 'p' : 's';
|
||||
str[5] = (flags & (DMU_WRITE_ZC | DMU_READ_ZC)) ? 'z' : '-';
|
||||
str[6] = (flags & DMU_WRITE_NOWAIT) ? 'O' : '-';
|
||||
str[7] = '\0';
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static double
|
||||
timespec_to_double(struct timespec t)
|
||||
{
|
||||
return ((double)(t.tv_sec) +
|
||||
((double)(t.tv_nsec) / (double)(1000*1000*1000)));
|
||||
}
|
||||
|
||||
static int
|
||||
regex_match(const char *string, char *pattern)
|
||||
{
|
||||
regex_t re = { 0 };
|
||||
int rc;
|
||||
|
||||
rc = regcomp(&re, pattern, REG_EXTENDED | REG_NOSUB | REG_ICASE);
|
||||
if (rc) {
|
||||
fprintf(stderr, "Error: Couldn't do regcomp, %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = regexec(&re, string, (size_t) 0, NULL, 0);
|
||||
regfree(&re);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* fills the pios_range_repeat structure of comma separated values */
|
||||
static int
|
||||
split_string(const char *optarg, char *pattern, range_repeat_t *range)
|
||||
{
|
||||
const char comma[] = ",";
|
||||
char *cp, *token[32];
|
||||
int rc, i = 0;
|
||||
|
||||
if ((rc = regex_match(optarg, pattern)))
|
||||
return rc;
|
||||
|
||||
cp = strdup(optarg);
|
||||
if (cp == NULL)
|
||||
return ENOMEM;
|
||||
|
||||
do {
|
||||
/* STRTOK(3) Each subsequent call, with a null pointer as the
|
||||
* value of the * first argument, starts searching from the
|
||||
* saved pointer and behaves as described above.
|
||||
*/
|
||||
token[i] = strtok(cp, comma);
|
||||
cp = NULL;
|
||||
} while ((token[i++] != NULL) && (i < 32));
|
||||
|
||||
range->val_count = i - 1;
|
||||
|
||||
for (i = 0; i < range->val_count; i++)
|
||||
kmgt_to_uint64(token[i], &range->val[i]);
|
||||
|
||||
free(cp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
set_count(char *pattern1, char *pattern2, range_repeat_t *range,
|
||||
char *optarg, uint32_t *flags, char *arg)
|
||||
{
|
||||
if (flags)
|
||||
*flags |= FLAG_SET;
|
||||
|
||||
range->next_val = 0;
|
||||
|
||||
if (regex_match(optarg, pattern1) == 0) {
|
||||
kmgt_to_uint64(optarg, &range->val[0]);
|
||||
range->val_count = 1;
|
||||
} else if (split_string(optarg, pattern2, range) < 0) {
|
||||
fprintf(stderr, "Error: Incorrect pattern for %s, '%s'\n",
|
||||
arg, optarg);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* validates the value with regular expression and sets low, high, incr
|
||||
* according to value at which flag will be set. Sets the flag after. */
|
||||
int
|
||||
set_lhi(char *pattern, range_repeat_t *range, char *optarg,
|
||||
int flag, uint32_t *flag_thread, char *arg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if ((rc = regex_match(optarg, pattern))) {
|
||||
fprintf(stderr, "Error: Wrong pattern in %s, '%s'\n",
|
||||
arg, optarg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
switch (flag) {
|
||||
case FLAG_LOW:
|
||||
kmgt_to_uint64(optarg, &range->val_low);
|
||||
break;
|
||||
case FLAG_HIGH:
|
||||
kmgt_to_uint64(optarg, &range->val_high);
|
||||
break;
|
||||
case FLAG_INCR:
|
||||
kmgt_to_uint64(optarg, &range->val_inc_perc);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
*flag_thread |= flag;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
set_noise(uint64_t *noise, char *optarg, char *arg)
|
||||
{
|
||||
if (regex_match(optarg, REGEX_NUMBERS) == 0) {
|
||||
kmgt_to_uint64(optarg, noise);
|
||||
} else {
|
||||
fprintf(stderr, "Error: Incorrect pattern for %s\n", arg);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
set_load_params(cmd_args_t *args, char *optarg)
|
||||
{
|
||||
char *param, *search, comma[] = ",";
|
||||
int rc = 0;
|
||||
|
||||
search = strdup(optarg);
|
||||
if (search == NULL)
|
||||
return ENOMEM;
|
||||
|
||||
while ((param = strtok(search, comma)) != NULL) {
|
||||
search = NULL;
|
||||
|
||||
if (strcmp("fpp", param) == 0) {
|
||||
args->flags |= DMU_FPP; /* File Per Process/Thread */
|
||||
} else if (strcmp("ssf", param) == 0) {
|
||||
args->flags &= ~DMU_FPP; /* Single Shared File */
|
||||
} else if (strcmp("dmuio", param) == 0) {
|
||||
args->io_type |= DMU_IO;
|
||||
args->flags |= (DMU_WRITE | DMU_READ);
|
||||
} else {
|
||||
fprintf(stderr, "Invalid load: %s\n", param);
|
||||
rc = EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
free(search);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* checks the low, high, increment values against the single value for
|
||||
* mutual exclusion, for e.g threadcount is mutually exclusive to
|
||||
* threadcount_low, ..._high, ..._incr */
|
||||
int
|
||||
check_mutual_exclusive_command_lines(uint32_t flag, char *arg)
|
||||
{
|
||||
if ((flag & FLAG_SET) && (flag & (FLAG_LOW | FLAG_HIGH | FLAG_INCR))) {
|
||||
fprintf(stderr, "Error: --%s can not be given with --%s_low, "
|
||||
"--%s_high or --%s_incr.\n", arg, arg, arg, arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((flag & (FLAG_LOW | FLAG_HIGH | FLAG_INCR)) && !(flag & FLAG_SET)) {
|
||||
if (flag != (FLAG_LOW | FLAG_HIGH | FLAG_INCR)) {
|
||||
fprintf(stderr, "Error: One or more values missing "
|
||||
"from --%s_low, --%s_high, --%s_incr.\n",
|
||||
arg, arg, arg);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
print_stats_header(void)
|
||||
{
|
||||
printf("ret-code id\tth-cnt\trg-cnt\trg-sz\tch-sz\toffset\trg-no\tch-no\t"
|
||||
"th-dly\tflags\ttime\tcr-time\trm-time\twr-time\t"
|
||||
"rd-time\twr-data\twr-ch\twr-bw\trd-data\trd-ch\trd-bw\n");
|
||||
printf("------------------------------------------------------------"
|
||||
"------------------------------------------------------------"
|
||||
"-----------------------------------------------------------\n");
|
||||
}
|
||||
|
||||
static void
|
||||
print_stats_human_readable(cmd_args_t *args, zpios_cmd_t *cmd)
|
||||
{
|
||||
zpios_stats_t *summary_stats;
|
||||
double t_time, wr_time, rd_time, cr_time, rm_time;
|
||||
char str[KMGT_SIZE];
|
||||
|
||||
if (args->rc)
|
||||
printf("FAILED: %3d ", args->rc);
|
||||
else
|
||||
printf("PASSED: %3d ", args->rc);
|
||||
|
||||
printf("%u\t", cmd->cmd_id);
|
||||
printf("%u\t", cmd->cmd_thread_count);
|
||||
printf("%u\t", cmd->cmd_region_count);
|
||||
printf("%s\t", uint64_to_kmgt(str, cmd->cmd_region_size));
|
||||
printf("%s\t", uint64_to_kmgt(str, cmd->cmd_chunk_size));
|
||||
printf("%s\t", uint64_to_kmgt(str, cmd->cmd_offset));
|
||||
printf("%s\t", uint64_to_kmgt(str, cmd->cmd_region_noise));
|
||||
printf("%s\t", uint64_to_kmgt(str, cmd->cmd_chunk_noise));
|
||||
printf("%s\t", uint64_to_kmgt(str, cmd->cmd_thread_delay));
|
||||
printf("%s\t", print_flags(str, cmd->cmd_flags));
|
||||
|
||||
if (args->rc) {
|
||||
printf("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
summary_stats = (zpios_stats_t *)cmd->cmd_data_str;
|
||||
t_time = timespec_to_double(summary_stats->total_time.delta);
|
||||
wr_time = timespec_to_double(summary_stats->wr_time.delta);
|
||||
rd_time = timespec_to_double(summary_stats->rd_time.delta);
|
||||
cr_time = timespec_to_double(summary_stats->cr_time.delta);
|
||||
rm_time = timespec_to_double(summary_stats->rm_time.delta);
|
||||
|
||||
printf("%.2f\t", t_time);
|
||||
printf("%.3f\t", cr_time);
|
||||
printf("%.3f\t", rm_time);
|
||||
printf("%.2f\t", wr_time);
|
||||
printf("%.2f\t", rd_time);
|
||||
|
||||
printf("%s\t", uint64_to_kmgt(str, summary_stats->wr_data));
|
||||
printf("%s\t", uint64_to_kmgt(str, summary_stats->wr_chunks));
|
||||
printf("%s\t", kmgt_per_sec(str, summary_stats->wr_data, wr_time));
|
||||
|
||||
printf("%s\t", uint64_to_kmgt(str, summary_stats->rd_data));
|
||||
printf("%s\t", uint64_to_kmgt(str, summary_stats->rd_chunks));
|
||||
printf("%s\n", kmgt_per_sec(str, summary_stats->rd_data, rd_time));
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static void
|
||||
print_stats_table(cmd_args_t *args, zpios_cmd_t *cmd)
|
||||
{
|
||||
zpios_stats_t *summary_stats;
|
||||
double wr_time, rd_time;
|
||||
|
||||
if (args->rc)
|
||||
printf("FAILED: %3d ", args->rc);
|
||||
else
|
||||
printf("PASSED: %3d ", args->rc);
|
||||
|
||||
printf("%u\t", cmd->cmd_id);
|
||||
printf("%u\t", cmd->cmd_thread_count);
|
||||
printf("%u\t", cmd->cmd_region_count);
|
||||
printf("%llu\t", (long long unsigned)cmd->cmd_region_size);
|
||||
printf("%llu\t", (long long unsigned)cmd->cmd_chunk_size);
|
||||
printf("%llu\t", (long long unsigned)cmd->cmd_offset);
|
||||
printf("%u\t", cmd->cmd_region_noise);
|
||||
printf("%u\t", cmd->cmd_chunk_noise);
|
||||
printf("%u\t", cmd->cmd_thread_delay);
|
||||
printf("0x%x\t", cmd->cmd_flags);
|
||||
|
||||
if (args->rc) {
|
||||
printf("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
summary_stats = (zpios_stats_t *)cmd->cmd_data_str;
|
||||
wr_time = timespec_to_double(summary_stats->wr_time.delta);
|
||||
rd_time = timespec_to_double(summary_stats->rd_time.delta);
|
||||
|
||||
printf("%ld.%02ld\t",
|
||||
summary_stats->total_time.delta.tv_sec,
|
||||
summary_stats->total_time.delta.tv_nsec);
|
||||
printf("%ld.%02ld\t",
|
||||
summary_stats->cr_time.delta.tv_sec,
|
||||
summary_stats->cr_time.delta.tv_nsec);
|
||||
printf("%ld.%02ld\t",
|
||||
summary_stats->rm_time.delta.tv_sec,
|
||||
summary_stats->rm_time.delta.tv_nsec);
|
||||
printf("%ld.%02ld\t",
|
||||
summary_stats->wr_time.delta.tv_sec,
|
||||
summary_stats->wr_time.delta.tv_nsec);
|
||||
printf("%ld.%02ld\t",
|
||||
summary_stats->rd_time.delta.tv_sec,
|
||||
summary_stats->rd_time.delta.tv_nsec);
|
||||
|
||||
printf("%lld\t", (long long unsigned)summary_stats->wr_data);
|
||||
printf("%lld\t", (long long unsigned)summary_stats->wr_chunks);
|
||||
printf("%.4f\t", (double)summary_stats->wr_data / wr_time);
|
||||
|
||||
printf("%lld\t", (long long unsigned)summary_stats->rd_data);
|
||||
printf("%lld\t", (long long unsigned)summary_stats->rd_chunks);
|
||||
printf("%.4f\n", (double)summary_stats->rd_data / rd_time);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void
|
||||
print_stats(cmd_args_t *args, zpios_cmd_t *cmd)
|
||||
{
|
||||
if (args->human_readable)
|
||||
print_stats_human_readable(args, cmd);
|
||||
else
|
||||
print_stats_table(args, cmd);
|
||||
}
|
|
@ -68,14 +68,18 @@ AC_CONFIG_FILES([
|
|||
cmd/zinject/Makefile
|
||||
cmd/zpool/Makefile
|
||||
cmd/ztest/Makefile
|
||||
cmd/zpios/Makefile
|
||||
module/Makefile
|
||||
module/avl/Makefile
|
||||
module/nvpair/Makefile
|
||||
module/unicode/Makefile
|
||||
module/zcommon/Makefile
|
||||
module/zfs/Makefile
|
||||
module/zpios/Makefile
|
||||
scripts/Makefile
|
||||
scripts/zpool-config/Makefile
|
||||
scripts/zpios-test/Makefile
|
||||
scripts/zpios-profile/Makefile
|
||||
zfs.spec
|
||||
])
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ subdir-m += nvpair
|
|||
subdir-m += unicode
|
||||
subdir-m += zcommon
|
||||
subdir-m += zfs
|
||||
subdir-m += zpios
|
||||
|
||||
modules clean:
|
||||
# Make the exported SPL symbols available to these modules.
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
MODULE := zpios
|
||||
|
||||
EXTRA_CFLAGS = -I@MODDIR@/zfs/include
|
||||
EXTRA_CFLAGS += -I@MODDIR@/zcommon/include
|
||||
EXTRA_CFLAGS += -I@MODDIR@/avl/include
|
||||
EXTRA_CFLAGS += -I@MODDIR@/nvpair/include
|
||||
EXTRA_CFLAGS += -I@MODDIR@/unicode/include
|
||||
EXTRA_CFLAGS += -I@MODDIR@/zpios/include
|
||||
EXTRA_CFLAGS += @KERNELCPPFLAGS@
|
||||
|
||||
obj-m := ${MODULE}.o
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* This file is part of the ZFS Linux port.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* LLNL-CODE-403049
|
||||
*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
#ifndef _ZPIOS_CTL_H
|
||||
#define _ZPIOS_CTL_H
|
||||
|
||||
/* Contains shared definitions which both the userspace
|
||||
* and kernelspace portions of zpios must agree on.
|
||||
*/
|
||||
#ifndef _KERNEL
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#define ZPIOS_MAJOR 232 /* XXX - Arbitrary */
|
||||
#define ZPIOS_MINORS 1
|
||||
#define ZPIOS_DEV "/dev/zpios"
|
||||
|
||||
#define DMU_IO 0x01
|
||||
|
||||
#define DMU_WRITE 0x01
|
||||
#define DMU_READ 0x02
|
||||
#define DMU_VERIFY 0x04
|
||||
#define DMU_REMOVE 0x08
|
||||
#define DMU_FPP 0x10
|
||||
#define DMU_WRITE_ZC 0x20 /* Incompatible with DMU_VERIFY */
|
||||
#define DMU_READ_ZC 0x40 /* Incompatible with DMU_VERIFY */
|
||||
#define DMU_WRITE_NOWAIT 0x80
|
||||
|
||||
#define ZPIOS_NAME_SIZE 16
|
||||
#define ZPIOS_PATH_SIZE 128
|
||||
|
||||
#define PHASE_PRE_RUN "pre-run"
|
||||
#define PHASE_PRE_CREATE "pre-create"
|
||||
#define PHASE_PRE_WRITE "pre-write"
|
||||
#define PHASE_PRE_READ "pre-read"
|
||||
#define PHASE_PRE_REMOVE "pre-remove"
|
||||
#define PHASE_POST_RUN "post-run"
|
||||
#define PHASE_POST_CREATE "post-create"
|
||||
#define PHASE_POST_WRITE "post-write"
|
||||
#define PHASE_POST_READ "post-read"
|
||||
#define PHASE_POST_REMOVE "post-remove"
|
||||
|
||||
#define ZPIOS_CFG_MAGIC 0x87237190U
|
||||
typedef struct zpios_cfg {
|
||||
uint32_t cfg_magic; /* Unique magic */
|
||||
int32_t cfg_cmd; /* Config command */
|
||||
int32_t cfg_arg1; /* Config command arg 1 */
|
||||
int32_t cfg_rc1; /* Config response 1 */
|
||||
} zpios_cfg_t;
|
||||
|
||||
typedef struct zpios_time {
|
||||
struct timespec start;
|
||||
struct timespec stop;
|
||||
struct timespec delta;
|
||||
} zpios_time_t;
|
||||
|
||||
typedef struct zpios_stats {
|
||||
zpios_time_t total_time;
|
||||
zpios_time_t cr_time;
|
||||
zpios_time_t rm_time;
|
||||
zpios_time_t wr_time;
|
||||
zpios_time_t rd_time;
|
||||
uint64_t wr_data;
|
||||
uint64_t wr_chunks;
|
||||
uint64_t rd_data;
|
||||
uint64_t rd_chunks;
|
||||
} zpios_stats_t;
|
||||
|
||||
#define ZPIOS_CMD_MAGIC 0x49715385U
|
||||
typedef struct zpios_cmd {
|
||||
uint32_t cmd_magic; /* Unique magic */
|
||||
uint32_t cmd_id; /* Run ID */
|
||||
char cmd_pool[ZPIOS_NAME_SIZE]; /* Pool name */
|
||||
uint64_t cmd_chunk_size; /* Chunk size */
|
||||
uint32_t cmd_thread_count; /* Thread count */
|
||||
uint32_t cmd_region_count; /* Region count */
|
||||
uint64_t cmd_region_size; /* Region size */
|
||||
uint64_t cmd_offset; /* Region offset */
|
||||
uint32_t cmd_region_noise; /* Region noise */
|
||||
uint32_t cmd_chunk_noise; /* Chunk noise */
|
||||
uint32_t cmd_thread_delay; /* Thread delay */
|
||||
uint32_t cmd_flags; /* Test flags */
|
||||
char cmd_pre[ZPIOS_PATH_SIZE]; /* Pre-exec hook */
|
||||
char cmd_post[ZPIOS_PATH_SIZE]; /* Post-exec hook */
|
||||
char cmd_log[ZPIOS_PATH_SIZE]; /* Requested log dir */
|
||||
uint64_t cmd_data_size; /* Opaque data size */
|
||||
char cmd_data_str[0]; /* Opaque data region */
|
||||
} zpios_cmd_t;
|
||||
|
||||
/* Valid ioctls */
|
||||
#define ZPIOS_CFG _IOWR('f', 101, long)
|
||||
#define ZPIOS_CMD _IOWR('f', 102, long)
|
||||
|
||||
/* Valid configuration commands */
|
||||
#define ZPIOS_CFG_BUFFER_CLEAR 0x001 /* Clear text buffer */
|
||||
#define ZPIOS_CFG_BUFFER_SIZE 0x002 /* Resize text buffer */
|
||||
|
||||
#endif /* _ZPIOS_CTL_H */
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* This file is part of the ZFS Linux port.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* LLNL-CODE-403049
|
||||
*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
#ifndef _ZPIOS_INTERNAL_H
|
||||
#define _ZPIOS_INTERNAL_H
|
||||
|
||||
#include "zpios-ctl.h"
|
||||
|
||||
#define OBJ_SIZE 64
|
||||
|
||||
struct run_args;
|
||||
|
||||
typedef struct dmu_obj {
|
||||
objset_t *os;
|
||||
uint64_t obj;
|
||||
} dmu_obj_t;
|
||||
|
||||
/* thread doing the IO data */
|
||||
typedef struct thread_data {
|
||||
struct run_args *run_args;
|
||||
int thread_no;
|
||||
int rc;
|
||||
zpios_stats_t stats;
|
||||
kmutex_t lock;
|
||||
} thread_data_t;
|
||||
|
||||
/* region for IO data */
|
||||
typedef struct zpios_region {
|
||||
__u64 wr_offset;
|
||||
__u64 rd_offset;
|
||||
__u64 init_offset;
|
||||
__u64 max_offset;
|
||||
dmu_obj_t obj;
|
||||
zpios_stats_t stats;
|
||||
kmutex_t lock;
|
||||
} zpios_region_t;
|
||||
|
||||
/* arguments for one run */
|
||||
typedef struct run_args {
|
||||
/* Config args */
|
||||
int id;
|
||||
char pool[ZPIOS_NAME_SIZE];
|
||||
__u64 chunk_size;
|
||||
__u32 thread_count;
|
||||
__u32 region_count;
|
||||
__u64 region_size;
|
||||
__u64 offset;
|
||||
__u32 region_noise;
|
||||
__u32 chunk_noise;
|
||||
__u32 thread_delay;
|
||||
__u32 flags;
|
||||
char pre[ZPIOS_PATH_SIZE];
|
||||
char post[ZPIOS_PATH_SIZE];
|
||||
char log[ZPIOS_PATH_SIZE];
|
||||
|
||||
/* Control data */
|
||||
objset_t *os;
|
||||
wait_queue_head_t waitq;
|
||||
volatile uint64_t threads_done;
|
||||
kmutex_t lock_work;
|
||||
kmutex_t lock_ctl;
|
||||
__u32 region_next;
|
||||
|
||||
/* Results data */
|
||||
struct file *file;
|
||||
zpios_stats_t stats;
|
||||
|
||||
thread_data_t **threads;
|
||||
zpios_region_t regions[0]; /* Must be last element */
|
||||
} run_args_t;
|
||||
|
||||
#define ZPIOS_INFO_BUFFER_SIZE 65536
|
||||
#define ZPIOS_INFO_BUFFER_REDZONE 1024
|
||||
|
||||
typedef struct zpios_info {
|
||||
spinlock_t info_lock;
|
||||
int info_size;
|
||||
char *info_buffer;
|
||||
char *info_head; /* Internal kernel use only */
|
||||
} zpios_info_t;
|
||||
|
||||
#define zpios_print(file, format, args...) \
|
||||
({ zpios_info_t *_info_ = (zpios_info_t *)file->private_data; \
|
||||
int _rc_; \
|
||||
\
|
||||
ASSERT(_info_); \
|
||||
ASSERT(_info_->info_buffer); \
|
||||
\
|
||||
spin_lock(&_info_->info_lock); \
|
||||
\
|
||||
/* Don't allow the kernel to start a write in the red zone */ \
|
||||
if ((int)(_info_->info_head - _info_->info_buffer) > \
|
||||
(_info_->info_size - ZPIOS_INFO_BUFFER_REDZONE)) { \
|
||||
_rc_ = -EOVERFLOW; \
|
||||
} else { \
|
||||
_rc_ = sprintf(_info_->info_head, format, args); \
|
||||
if (_rc_ >= 0) \
|
||||
_info_->info_head += _rc_; \
|
||||
} \
|
||||
\
|
||||
spin_unlock(&_info_->info_lock); \
|
||||
_rc_; \
|
||||
})
|
||||
|
||||
#define zpios_vprint(file, test, format, args...) \
|
||||
zpios_print(file, "%*s: " format, ZPIOS_NAME_SIZE, test, args)
|
||||
|
||||
#endif /* _ZPIOS_INTERNAL_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,6 @@
|
|||
SUBDIRS = zpool-config
|
||||
SUBDIRS = zpool-config zpios-test zpios-profile
|
||||
EXTRA_DIST = common.sh zfs-update.sh zfs.sh zpool-create.sh
|
||||
EXTRA_DIST += zpios.sh zpios-survey.sh
|
||||
|
||||
ZFS=${top_srcdir}/scripts/zfs.sh
|
||||
ZTEST=${top_builddir}/cmd/ztest/ztest
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
EXTRA_DIST = *.sh
|
|
@ -0,0 +1,129 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# /proc/diskinfo <after skipping major/minor>
|
||||
# Field 1 -- device name
|
||||
# Field 2 -- # of reads issued
|
||||
# Field 3 -- # of reads merged
|
||||
# Field 4 -- # of sectors read
|
||||
# Field 5 -- # of milliseconds spent reading
|
||||
# Field 6 -- # of writes completed
|
||||
# Field 7 -- # of writes merged
|
||||
# Field 8 -- # of sectors written
|
||||
# Field 9 -- # of milliseconds spent writing
|
||||
# Field 10 -- # of I/Os currently in progress
|
||||
# Field 11 -- # of milliseconds spent doing I/Os
|
||||
# Field 12 -- weighted # of milliseconds spent doing I/Os
|
||||
|
||||
PROG=zpios-profile-disk.sh
|
||||
|
||||
RUN_PIDS=${0}
|
||||
RUN_LOG_DIR=${1}
|
||||
RUN_ID=${2}
|
||||
|
||||
create_table() {
|
||||
local FIELD=$1
|
||||
local ROW_M=()
|
||||
local ROW_N=()
|
||||
local HEADER=1
|
||||
local STEP=1
|
||||
|
||||
for DISK_FILE in `ls -r --sort=time --time=ctime ${RUN_LOG_DIR}/${RUN_ID}/disk-[0-9]*`; do
|
||||
ROW_M=( ${ROW_N[@]} )
|
||||
ROW_N=( `cat ${DISK_FILE} | grep sd | cut -c11- | cut -f${FIELD} -d' ' | tr "\n" "\t"` )
|
||||
|
||||
if [ $HEADER -eq 1 ]; then
|
||||
echo -n "step, "
|
||||
cat ${DISK_FILE} | grep sd | cut -c11- | cut -f1 -d' ' | tr "\n" ", "
|
||||
echo "total"
|
||||
HEADER=0
|
||||
fi
|
||||
|
||||
if [ ${#ROW_M[@]} -eq 0 ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ ${#ROW_M[@]} -ne ${#ROW_N[@]} ]; then
|
||||
echo "Badly formatted profile data in ${DISK_FILE}"
|
||||
break
|
||||
fi
|
||||
|
||||
TOTAL=0
|
||||
echo -n "${STEP}, "
|
||||
for (( i=0; i<${#ROW_N[@]}; i++ )); do
|
||||
DELTA=`echo "${ROW_N[${i}]}-${ROW_M[${i}]}" | bc`
|
||||
let TOTAL=${TOTAL}+${DELTA}
|
||||
echo -n "${DELTA}, "
|
||||
done
|
||||
echo "${TOTAL}, "
|
||||
|
||||
let STEP=${STEP}+1
|
||||
done
|
||||
}
|
||||
|
||||
create_table_mbs() {
|
||||
local FIELD=$1
|
||||
local TIME=$2
|
||||
local ROW_M=()
|
||||
local ROW_N=()
|
||||
local HEADER=1
|
||||
local STEP=1
|
||||
|
||||
for DISK_FILE in `ls -r --sort=time --time=ctime ${RUN_LOG_DIR}/${RUN_ID}/disk-[0-9]*`; do
|
||||
ROW_M=( ${ROW_N[@]} )
|
||||
ROW_N=( `cat ${DISK_FILE} | grep sd | cut -c11- | cut -f${FIELD} -d' ' | tr "\n" "\t"` )
|
||||
|
||||
if [ $HEADER -eq 1 ]; then
|
||||
echo -n "step, "
|
||||
cat ${DISK_FILE} | grep sd | cut -c11- | cut -f1 -d' ' | tr "\n" ", "
|
||||
echo "total"
|
||||
HEADER=0
|
||||
fi
|
||||
|
||||
if [ ${#ROW_M[@]} -eq 0 ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ ${#ROW_M[@]} -ne ${#ROW_N[@]} ]; then
|
||||
echo "Badly formatted profile data in ${DISK_FILE}"
|
||||
break
|
||||
fi
|
||||
|
||||
TOTAL=0
|
||||
echo -n "${STEP}, "
|
||||
for (( i=0; i<${#ROW_N[@]}; i++ )); do
|
||||
DELTA=`echo "${ROW_N[${i}]}-${ROW_M[${i}]}" | bc`
|
||||
MBS=`echo "scale=2; ((${DELTA}*512)/${TIME})/(1024*1024)" | bc`
|
||||
TOTAL=`echo "scale=2; ${TOTAL}+${MBS}" | bc`
|
||||
echo -n "${MBS}, "
|
||||
done
|
||||
echo "${TOTAL}, "
|
||||
|
||||
let STEP=${STEP}+1
|
||||
done
|
||||
}
|
||||
|
||||
echo
|
||||
echo "Reads issued per device"
|
||||
create_table 2
|
||||
echo
|
||||
echo "Reads merged per device"
|
||||
create_table 3
|
||||
echo
|
||||
echo "Sectors read per device"
|
||||
create_table 4
|
||||
echo "MB/s per device"
|
||||
create_table_mbs 4 3
|
||||
|
||||
echo
|
||||
echo "Writes issued per device"
|
||||
create_table 6
|
||||
echo
|
||||
echo "Writes merged per device"
|
||||
create_table 7
|
||||
echo
|
||||
echo "Sectors written per device"
|
||||
create_table 8
|
||||
echo "MB/s per device"
|
||||
create_table_mbs 8 3
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,131 @@
|
|||
#!/bin/bash
|
||||
|
||||
PROG=zpios-profile-pids.sh
|
||||
|
||||
RUN_PIDS=${0}
|
||||
RUN_LOG_DIR=${1}
|
||||
RUN_ID=${2}
|
||||
|
||||
ROW_M=()
|
||||
ROW_N=()
|
||||
ROW_N_SCHED=()
|
||||
ROW_N_WAIT=()
|
||||
|
||||
HEADER=1
|
||||
STEP=1
|
||||
|
||||
for PID_FILE in `ls -r --sort=time --time=ctime ${RUN_LOG_DIR}/${RUN_ID}/pids-[0-9]*`; do
|
||||
ROW_M=( ${ROW_N[@]} )
|
||||
ROW_N=( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 )
|
||||
ROW_N_SCHED=( `cat ${PID_FILE} | cut -f15 -d' ' | tr "\n" "\t"` )
|
||||
ROW_N_WAIT=( `cat ${PID_FILE} | cut -f17 -d' ' | tr "\n" "\t"` )
|
||||
ROW_N_NAMES=( `cat ${PID_FILE} | cut -f2 -d' ' | cut -f2 -d'(' |
|
||||
cut -f1 -d')' | cut -f1 -d'/' | tr "\n" "\t"` )
|
||||
|
||||
for (( i=0; i<${#ROW_N_SCHED[@]}; i++ )); do
|
||||
SUM=`echo "${ROW_N_WAIT[${i}]}+${ROW_N_SCHED[${i}]}" | bc`
|
||||
|
||||
case ${ROW_N_NAMES[${i}]} in
|
||||
zio_taskq) IDX=0;;
|
||||
zio_req_nul) IDX=1;;
|
||||
zio_irq_nul) IDX=2;;
|
||||
zio_req_rd) IDX=3;;
|
||||
zio_irq_rd) IDX=4;;
|
||||
zio_req_wr) IDX=5;;
|
||||
zio_irq_wr) IDX=6;;
|
||||
zio_req_fr) IDX=7;;
|
||||
zio_irq_fr) IDX=8;;
|
||||
zio_req_cm) IDX=9;;
|
||||
zio_irq_cm) IDX=10;;
|
||||
zio_req_ctl) IDX=11;;
|
||||
zio_irq_ctl) IDX=12;;
|
||||
txg_quiesce) IDX=13;;
|
||||
txg_sync) IDX=14;;
|
||||
txg_timelimit) IDX=15;;
|
||||
arc_reclaim) IDX=16;;
|
||||
l2arc_feed) IDX=17;;
|
||||
zpios_io) IDX=18;;
|
||||
*) continue;;
|
||||
esac
|
||||
|
||||
let ROW_N[${IDX}]=${ROW_N[${IDX}]}+${SUM}
|
||||
done
|
||||
|
||||
if [ $HEADER -eq 1 ]; then
|
||||
echo "step, zio_taskq, zio_req_nul, zio_irq_nul, " \
|
||||
"zio_req_rd, zio_irq_rd, zio_req_wr, zio_irq_wr, " \
|
||||
"zio_req_fr, zio_irq_fr, zio_req_cm, zio_irq_cm, " \
|
||||
"zio_req_ctl, zio_irq_ctl, txg_quiesce, txg_sync, " \
|
||||
"txg_timelimit, arc_reclaim, l2arc_feed, zpios_io, " \
|
||||
"idle"
|
||||
HEADER=0
|
||||
fi
|
||||
|
||||
if [ ${#ROW_M[@]} -eq 0 ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ ${#ROW_M[@]} -ne ${#ROW_N[@]} ]; then
|
||||
echo "Badly formatted profile data in ${PID_FILE}"
|
||||
break
|
||||
fi
|
||||
|
||||
# Original values are in jiffies and we expect HZ to be 1000
|
||||
# on most 2.6 systems thus we divide by 10 to get a percentage.
|
||||
IDLE=1000
|
||||
echo -n "${STEP}, "
|
||||
for (( i=0; i<${#ROW_N[@]}; i++ )); do
|
||||
DELTA=`echo "${ROW_N[${i}]}-${ROW_M[${i}]}" | bc`
|
||||
DELTA_PERCENT=`echo "scale=1; ${DELTA}/10" | bc`
|
||||
let IDLE=${IDLE}-${DELTA}
|
||||
echo -n "${DELTA_PERCENT}, "
|
||||
done
|
||||
ILDE_PERCENT=`echo "scale=1; ${IDLE}/10" | bc`
|
||||
echo "${ILDE_PERCENT}"
|
||||
|
||||
let STEP=${STEP}+1
|
||||
done
|
||||
|
||||
exit
|
||||
|
||||
echo
|
||||
echo "Percent of total system time per pid"
|
||||
for PID_FILE in `ls -r --sort=time --time=ctime ${RUN_LOG_DIR}/${RUN_ID}/pids-[0-9]*`; do
|
||||
ROW_M=( ${ROW_N[@]} )
|
||||
ROW_N_SCHED=( `cat ${PID_FILE} | cut -f15 -d' ' | tr "\n" "\t"` )
|
||||
ROW_N_WAIT=( `cat ${PID_FILE} | cut -f17 -d' ' | tr "\n" "\t"` )
|
||||
|
||||
for (( i=0; i<${#ROW_N_SCHED[@]}; i++ )); do
|
||||
ROW_N[${i}]=`echo "${ROW_N_WAIT[${i}]}+${ROW_N_SCHED[${i}]}" | bc`
|
||||
done
|
||||
|
||||
if [ $HEADER -eq 1 ]; then
|
||||
echo -n "step, "
|
||||
cat ${PID_FILE} | cut -f2 -d' ' | tr "\n" ", "
|
||||
echo
|
||||
HEADER=0
|
||||
fi
|
||||
|
||||
if [ ${#ROW_M[@]} -eq 0 ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ ${#ROW_M[@]} -ne ${#ROW_N[@]} ]; then
|
||||
echo "Badly formatted profile data in ${PID_FILE}"
|
||||
break
|
||||
fi
|
||||
|
||||
# Original values are in jiffies and we expect HZ to be 1000
|
||||
# on most 2.6 systems thus we divide by 10 to get a percentage.
|
||||
echo -n "${STEP}, "
|
||||
for (( i=0; i<${#ROW_N[@]}; i++ )); do
|
||||
DELTA=`echo "scale=1; (${ROW_N[${i}]}-${ROW_M[${i}]})/10" | bc`
|
||||
echo -n "${DELTA}, "
|
||||
done
|
||||
|
||||
echo
|
||||
let STEP=${STEP}+1
|
||||
done
|
||||
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,129 @@
|
|||
#!/bin/bash
|
||||
|
||||
PROG=zpios-profile-post.sh
|
||||
|
||||
RUN_POST=${0}
|
||||
RUN_PHASE=${1}
|
||||
RUN_DIR=${2}
|
||||
RUN_ID=${3}
|
||||
RUN_POOL=${4}
|
||||
RUN_CHUNK_SIZE=${5}
|
||||
RUN_REGION_SIZE=${6}
|
||||
RUN_THRD_COUNT=${7}
|
||||
RUN_REGION_COUNT=${8}
|
||||
RUN_OFFSET=${9}
|
||||
RUN_REGION_NOISE=${10}
|
||||
RUN_CHUNK_NOISE=${11}
|
||||
RUN_THRD_DELAY=${12}
|
||||
RUN_FLAGS=${13}
|
||||
RUN_RESULT=${14}
|
||||
|
||||
# Summarize system time per process
|
||||
zpios_profile_post_pids() {
|
||||
${PROFILE_PIDS} ${PROFILE_RUN_CR_PIDS_LOG} >${PROFILE_RUN_CR_PIDS_CSV}
|
||||
${PROFILE_PIDS} ${PROFILE_RUN_WR_PIDS_LOG} >${PROFILE_RUN_WR_PIDS_CSV}
|
||||
${PROFILE_PIDS} ${PROFILE_RUN_RD_PIDS_LOG} >${PROFILE_RUN_RD_PIDS_CSV}
|
||||
${PROFILE_PIDS} ${PROFILE_RUN_RM_PIDS_LOG} >${PROFILE_RUN_RM_PIDS_CSV}
|
||||
}
|
||||
|
||||
zpios_profile_post_disk() {
|
||||
${PROFILE_DISK} ${PROFILE_RUN_CR_DISK_LOG} >${PROFILE_RUN_CR_DISK_CSV}
|
||||
${PROFILE_DISK} ${PROFILE_RUN_WR_DISK_LOG} >${PROFILE_RUN_WR_DISK_CSV}
|
||||
${PROFILE_DISK} ${PROFILE_RUN_RD_DISK_LOG} >${PROFILE_RUN_RD_DISK_CSV}
|
||||
${PROFILE_DISK} ${PROFILE_RUN_RM_DISK_LOG} >${PROFILE_RUN_RM_DISK_CSV}
|
||||
}
|
||||
|
||||
# Summarize per device performance
|
||||
|
||||
# Stop a user defined profiling script which is gathering additional data
|
||||
zpios_profile_post_stop() {
|
||||
local PROFILE_PID=$1
|
||||
|
||||
kill -s SIGHUP `cat ${PROFILE_PID}`
|
||||
|
||||
|
||||
# Sleep waiting for profile script to exit
|
||||
while [ -f ${PROFILE_PID} ]; do
|
||||
sleep 0.01
|
||||
done
|
||||
}
|
||||
|
||||
zpios_profile_post_proc_stop() {
|
||||
local PROC_DIR=$1
|
||||
|
||||
if [ -f ${PROFILE_ARC_PROC} ]; then
|
||||
cat ${PROFILE_ARC_PROC} >${PROC_DIR}/arcstats.txt
|
||||
fi
|
||||
|
||||
if [ -f ${PROFILE_VDEV_CACHE_PROC} ]; then
|
||||
cat ${PROFILE_VDEV_CACHE_PROC} >${PROC_DIR}/vdev_cache_stats.txt
|
||||
fi
|
||||
}
|
||||
|
||||
zpios_profile_post_oprofile_stop() {
|
||||
local OPROFILE_LOG=$1
|
||||
local OPROFILE_ARGS="-a -g -l -p ${OPROFILE_KERNEL_DIR},${OPROFILE_SPL_DIR},${OPROFILE_ZFS_DIR}"
|
||||
|
||||
/usr/bin/opcontrol --stop >>${OPROFILE_LOG} 2>&1
|
||||
/usr/bin/opcontrol --dump >>${OPROFILE_LOG} 2>&1
|
||||
/usr/bin/opreport ${OPROFILE_ARGS} >${OPROFILE_LOG} 2>&1
|
||||
/usr/bin/oparchive
|
||||
}
|
||||
|
||||
zpios_profile_post_create() {
|
||||
zpios_profile_post_oprofile_stop ${PROFILE_RUN_CR_OPROFILE_LOG}
|
||||
zpios_profile_post_proc_stop ${PROFILE_RUN_CR_DIR}
|
||||
zpios_profile_post_stop ${PROFILE_RUN_CR_PID}
|
||||
}
|
||||
|
||||
zpios_profile_post_write() {
|
||||
zpios_profile_post_oprofile_stop ${PROFILE_RUN_WR_OPROFILE_LOG}
|
||||
zpios_profile_post_proc_stop ${PROFILE_RUN_WR_DIR}
|
||||
zpios_profile_post_stop ${PROFILE_RUN_WR_PID}
|
||||
}
|
||||
|
||||
zpios_profile_post_read() {
|
||||
zpios_profile_post_oprofile_stop ${PROFILE_RUN_CR_RD_LOG}
|
||||
zpios_profile_post_proc_stop ${PROFILE_RUN_RD_DIR}
|
||||
zpios_profile_post_stop ${PROFILE_RUN_RD_PID}
|
||||
}
|
||||
|
||||
zpios_profile_post_remove() {
|
||||
zpios_profile_post_oprofile_stop ${PROFILE_RUN_RM_OPROFILE_LOG}
|
||||
zpios_profile_post_proc_stop ${PROFILE_RUN_RM_DIR}
|
||||
zpios_profile_post_stop ${PROFILE_RUN_RM_PID}
|
||||
}
|
||||
|
||||
# Source global zpios test configuration
|
||||
if [ -f ${RUN_DIR}/zpios-config.sh ]; then
|
||||
. ${RUN_DIR}/zpios-config.sh
|
||||
fi
|
||||
|
||||
# Source global per-run test configuration
|
||||
if [ -f ${RUN_DIR}/${RUN_ID}/zpios-config-run.sh ]; then
|
||||
. ${RUN_DIR}/${RUN_ID}/zpios-config-run.sh
|
||||
fi
|
||||
|
||||
case "${RUN_PHASE}" in
|
||||
post-run)
|
||||
zpios_profile_post_pids
|
||||
zpios_profile_post_disk
|
||||
;;
|
||||
post-create)
|
||||
zpios_profile_post_create
|
||||
;;
|
||||
post-write)
|
||||
zpios_profile_post_write
|
||||
;;
|
||||
post-read)
|
||||
zpios_profile_post_read
|
||||
;;
|
||||
post-remove)
|
||||
zpios_profile_post_remove
|
||||
;;
|
||||
*)
|
||||
echo "Usage: ${PROG} {post-run|post-create|post-write|post-read|post-remove}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,184 @@
|
|||
#!/bin/bash
|
||||
|
||||
PROG=zpios-profile-pre.sh
|
||||
|
||||
PROFILE_RDY=0
|
||||
trap "PROFILE_RDY=1" SIGHUP
|
||||
|
||||
RUN_PRE=${0}
|
||||
RUN_PHASE=${1}
|
||||
RUN_DIR=${2}
|
||||
RUN_ID=${3}
|
||||
RUN_POOL=${4}
|
||||
RUN_CHUNK_SIZE=${5}
|
||||
RUN_REGION_SIZE=${6}
|
||||
RUN_THRD_COUNT=${7}
|
||||
RUN_REGION_COUNT=${8}
|
||||
RUN_OFFSET=${9}
|
||||
RUN_REGION_NOISE=${10}
|
||||
RUN_CHUNK_NOISE=${11}
|
||||
RUN_THRD_DELAY=${12}
|
||||
RUN_FLAGS=${13}
|
||||
RUN_RESULT=${14}
|
||||
|
||||
zpios_profile_pre_run_cfg() {
|
||||
cat > ${RUN_DIR}/${RUN_ID}/zpios-config-run.sh << EOF
|
||||
#
|
||||
# Zpios Profiling Configuration for Run ${RUN_ID}
|
||||
#
|
||||
|
||||
PROFILE_RUN_DIR=${RUN_DIR}/${RUN_ID}
|
||||
|
||||
PROFILE_RUN_CR_DIR=${RUN_DIR}/${RUN_ID}/create
|
||||
PROFILE_RUN_CR_PID=${RUN_DIR}/${RUN_ID}/create/profile.pid
|
||||
PROFILE_RUN_CR_OPROFILE_LOG=${RUN_DIR}/${RUN_ID}/create/oprofile.txt
|
||||
PROFILE_RUN_CR_PIDS_LOG=${RUN_DIR}/${RUN_ID}/create/pids.txt
|
||||
PROFILE_RUN_CR_PIDS_CSV=${RUN_DIR}/${RUN_ID}/create/pids.csv
|
||||
PROFILE_RUN_CR_DISK_LOG=${RUN_DIR}/${RUN_ID}/create/disk.txt
|
||||
PROFILE_RUN_CR_DISK_CSV=${RUN_DIR}/${RUN_ID}/create/disk.csv
|
||||
|
||||
PROFILE_RUN_WR_DIR=${RUN_DIR}/${RUN_ID}/write
|
||||
PROFILE_RUN_WR_PID=${RUN_DIR}/${RUN_ID}/write/profile.pid
|
||||
PROFILE_RUN_WR_OPROFILE_LOG=${RUN_DIR}/${RUN_ID}/write/oprofile.txt
|
||||
PROFILE_RUN_WR_PIDS_LOG=${RUN_DIR}/${RUN_ID}/write/pids.txt
|
||||
PROFILE_RUN_WR_PIDS_CSV=${RUN_DIR}/${RUN_ID}/write/pids.csv
|
||||
PROFILE_RUN_WR_DISK_LOG=${RUN_DIR}/${RUN_ID}/write/disk.txt
|
||||
PROFILE_RUN_WR_DISK_CSV=${RUN_DIR}/${RUN_ID}/write/disk.csv
|
||||
|
||||
PROFILE_RUN_RD_DIR=${RUN_DIR}/${RUN_ID}/read
|
||||
PROFILE_RUN_RD_PID=${RUN_DIR}/${RUN_ID}/read/profile.pid
|
||||
PROFILE_RUN_RD_OPROFILE_LOG=${RUN_DIR}/${RUN_ID}/read/oprofile.txt
|
||||
PROFILE_RUN_RD_PIDS_LOG=${RUN_DIR}/${RUN_ID}/read/pids.txt
|
||||
PROFILE_RUN_RD_PIDS_CSV=${RUN_DIR}/${RUN_ID}/read/pids.csv
|
||||
PROFILE_RUN_RD_DISK_LOG=${RUN_DIR}/${RUN_ID}/read/disk.txt
|
||||
PROFILE_RUN_RD_DISK_CSV=${RUN_DIR}/${RUN_ID}/read/disk.csv
|
||||
|
||||
PROFILE_RUN_RM_DIR=${RUN_DIR}/${RUN_ID}/remove
|
||||
PROFILE_RUN_RM_PID=${RUN_DIR}/${RUN_ID}/remove/profile.pid
|
||||
PROFILE_RUN_RM_OPROFILE_LOG=${RUN_DIR}/${RUN_ID}/remove/oprofile.txt
|
||||
PROFILE_RUN_RM_PIDS_LOG=${RUN_DIR}/${RUN_ID}/remove/pids.txt
|
||||
PROFILE_RUN_RM_PIDS_CSV=${RUN_DIR}/${RUN_ID}/remove/pids.csv
|
||||
PROFILE_RUN_RM_DISK_LOG=${RUN_DIR}/${RUN_ID}/remove/disk.txt
|
||||
PROFILE_RUN_RM_DISK_CSV=${RUN_DIR}/${RUN_ID}/remove/disk.csv
|
||||
|
||||
# PROFILE_PIDS_LOG=${RUN_DIR}/${RUN_ID}/pids-summary.csv
|
||||
# PROFILE_DISK_LOG=${RUN_DIR}/${RUN_ID}/disk-summary.csv
|
||||
EOF
|
||||
}
|
||||
|
||||
zpios_profile_pre_run_args() {
|
||||
cat > ${RUN_DIR}/${RUN_ID}/zpios-args.txt << EOF
|
||||
#
|
||||
# Zpios Arguments for Run ${RUN_ID}
|
||||
#
|
||||
|
||||
DIR=${RUN_DIR}
|
||||
ID=${RUN_ID}
|
||||
POOL=${RUN_POOL}
|
||||
CHUNK_SIZE=${RUN_CHUNK_SIZE}
|
||||
REGION_SIZE=${RUN_REGION_SIZE}
|
||||
THRD_COUNT=${RUN_THRD_COUNT}
|
||||
REGION_COUNT=${RUN_REGION_COUNT}
|
||||
OFFSET=${RUN_OFFSET}
|
||||
REGION_NOISE=${RUN_REGION_NOISE}
|
||||
CHUNK_NOISE=${RUN_CHUNK_NOISE}
|
||||
THRD_DELAY=${RUN_THRD_DELAY}
|
||||
FLAGS=${RUN_FLAGS}
|
||||
RESULT=${RUN_RESULT}
|
||||
EOF
|
||||
}
|
||||
|
||||
# Spawn a user defined profiling script to gather additional data
|
||||
zpios_profile_pre_start() {
|
||||
local PROFILE_PID=$1
|
||||
|
||||
${PROFILE_USER} ${RUN_PHASE} ${RUN_DIR} ${RUN_ID} &
|
||||
echo "$!" >${PROFILE_PID}
|
||||
|
||||
# Sleep waiting for profile script to be ready, it will
|
||||
# signal us via SIGHUP when it is ready to start profiling.
|
||||
while [ ${PROFILE_RDY} -eq 0 ]; do
|
||||
sleep 0.01
|
||||
done
|
||||
}
|
||||
|
||||
zpios_profile_post_proc_start() {
|
||||
|
||||
if [ -f ${PROFILE_ARC_PROC} ]; then
|
||||
echo 0 >${PROFILE_ARC_PROC}
|
||||
fi
|
||||
|
||||
if [ -f ${PROFILE_VDEV_CACHE_PROC} ]; then
|
||||
echo 0 >${PROFILE_VDEV_CACHE_PROC}
|
||||
fi
|
||||
}
|
||||
|
||||
zpios_profile_pre_oprofile_start() {
|
||||
local OPROFILE_LOG=$1
|
||||
|
||||
/usr/bin/opcontrol --reset >>${OPROFILE_LOG} 2>&1
|
||||
/usr/bin/opcontrol --start >>${OPROFILE_LOG} 2>&1
|
||||
}
|
||||
|
||||
zpios_profile_pre_create() {
|
||||
mkdir ${PROFILE_RUN_CR_DIR}
|
||||
zpios_profile_pre_start ${PROFILE_RUN_CR_PID}
|
||||
zpios_profile_post_proc_start
|
||||
zpios_profile_pre_oprofile_start ${PROFILE_RUN_CR_OPROFILE_LOG}
|
||||
}
|
||||
|
||||
zpios_profile_pre_write() {
|
||||
mkdir ${PROFILE_RUN_WR_DIR}
|
||||
zpios_profile_pre_start ${PROFILE_RUN_WR_PID}
|
||||
zpios_profile_post_proc_start
|
||||
zpios_profile_pre_oprofile_start ${PROFILE_RUN_WR_OPROFILE_LOG}
|
||||
}
|
||||
|
||||
zpios_profile_pre_read() {
|
||||
mkdir ${PROFILE_RUN_RD_DIR}
|
||||
zpios_profile_pre_start ${PROFILE_RUN_RD_PID}
|
||||
zpios_profile_post_proc_start
|
||||
zpios_profile_pre_oprofile_start ${PROFILE_RUN_CR_RD_LOG}
|
||||
}
|
||||
|
||||
zpios_profile_pre_remove() {
|
||||
mkdir ${PROFILE_RUN_RM_DIR}
|
||||
zpios_profile_pre_start ${PROFILE_RUN_RM_PID}
|
||||
zpios_profile_post_proc_start
|
||||
zpios_profile_pre_oprofile_start ${PROFILE_RUN_RM_OPROFILE_LOG}
|
||||
}
|
||||
|
||||
# Source global zpios test configuration
|
||||
if [ -f ${RUN_DIR}/zpios-config.sh ]; then
|
||||
. ${RUN_DIR}/zpios-config.sh
|
||||
fi
|
||||
|
||||
# Source global per-run test configuration
|
||||
if [ -f ${RUN_DIR}/${RUN_ID}/zpios-config-run.sh ]; then
|
||||
. ${RUN_DIR}/${RUN_ID}/zpios-config-run.sh
|
||||
fi
|
||||
|
||||
case "${RUN_PHASE}" in
|
||||
pre-run)
|
||||
mkdir -p ${RUN_DIR}/${RUN_ID}/
|
||||
zpios_profile_pre_run_cfg
|
||||
zpios_profile_pre_run_args
|
||||
;;
|
||||
pre-create)
|
||||
zpios_profile_pre_create
|
||||
;;
|
||||
pre-write)
|
||||
zpios_profile_pre_write
|
||||
;;
|
||||
pre-read)
|
||||
zpios_profile_pre_read
|
||||
;;
|
||||
pre-remove)
|
||||
zpios_profile_pre_remove
|
||||
;;
|
||||
*)
|
||||
echo "Usage: ${PROG} {pre-run|pre-create|pre-write|pre-read|pre-remove}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,226 @@
|
|||
#!/bin/bash
|
||||
|
||||
|
||||
PROG=zpios-profile.sh
|
||||
|
||||
trap "RUN_DONE=1" SIGHUP
|
||||
|
||||
RUN_PHASE=${1}
|
||||
RUN_LOG_DIR=${2}
|
||||
RUN_ID=${3}
|
||||
RUN_DONE=0
|
||||
|
||||
POLL_INTERVAL=2.99
|
||||
|
||||
# Log these pids, the exact pid numbers will vary from system to system
|
||||
# so I harvest pid for all the following type of processes from /proc/<pid>/
|
||||
#
|
||||
# zio_taskq/#
|
||||
# spa_zio_issue/#
|
||||
# spa_zio_intr/#
|
||||
# txg_quiesce_thr
|
||||
# txg_sync_thread
|
||||
# txg_timelimit_t
|
||||
# arc_reclaim_thr
|
||||
# l2arc_feed_thre
|
||||
# zpios_io/#
|
||||
|
||||
ZIO_TASKQ_PIDS=()
|
||||
ZIO_REQ_NUL_PIDS=()
|
||||
ZIO_IRQ_NUL_PIDS=()
|
||||
ZIO_REQ_RD_PIDS=()
|
||||
ZIO_IRQ_RD_PIDS=()
|
||||
ZIO_REQ_WR_PIDS=()
|
||||
ZIO_IRQ_WR_PIDS=()
|
||||
ZIO_REQ_FR_PIDS=()
|
||||
ZIO_IRQ_FR_PIDS=()
|
||||
ZIO_REQ_CM_PIDS=()
|
||||
ZIO_IRQ_CM_PIDS=()
|
||||
ZIO_REQ_CTL_PIDS=()
|
||||
ZIO_IRQ_CTL_PIDS=()
|
||||
|
||||
TXG_QUIESCE_PIDS=()
|
||||
TXG_SYNC_PIDS=()
|
||||
TXG_TIMELIMIT_PIDS=()
|
||||
|
||||
ARC_RECLAIM_PIDS=()
|
||||
L2ARC_FEED_PIDS=()
|
||||
|
||||
ZPIOS_IO_PIDS=()
|
||||
|
||||
show_pids() {
|
||||
echo "* zio_taskq: { ${ZIO_TASKQ_PIDS[@]} } = ${#ZIO_TASKQ_PIDS[@]}"
|
||||
echo "* zio_req_nul: { ${ZIO_REQ_NUL_PIDS[@]} } = ${#ZIO_REQ_NUL_PIDS[@]}"
|
||||
echo "* zio_irq_nul: { ${ZIO_IRQ_NUL_PIDS[@]} } = ${#ZIO_IRQ_NUL_PIDS[@]}"
|
||||
echo "* zio_req_rd: { ${ZIO_REQ_RD_PIDS[@]} } = ${#ZIO_REQ_RD_PIDS[@]}"
|
||||
echo "* zio_irq_rd: { ${ZIO_IRQ_RD_PIDS[@]} } = ${#ZIO_IRQ_RD_PIDS[@]}"
|
||||
echo "* zio_req_wr: { ${ZIO_REQ_WR_PIDS[@]} } = ${#ZIO_REQ_WR_PIDS[@]}"
|
||||
echo "* zio_irq_wr: { ${ZIO_IRQ_WR_PIDS[@]} } = ${#ZIO_IRQ_WR_PIDS[@]}"
|
||||
echo "* zio_req_fr: { ${ZIO_REQ_FR_PIDS[@]} } = ${#ZIO_REQ_FR_PIDS[@]}"
|
||||
echo "* zio_irq_fr: { ${ZIO_IRQ_FR_PIDS[@]} } = ${#ZIO_IRQ_FR_PIDS[@]}"
|
||||
echo "* zio_req_cm: { ${ZIO_REQ_CM_PIDS[@]} } = ${#ZIO_REQ_CM_PIDS[@]}"
|
||||
echo "* zio_irq_cm: { ${ZIO_IRQ_CM_PIDS[@]} } = ${#ZIO_IRQ_CM_PIDS[@]}"
|
||||
echo "* zio_req_ctl: { ${ZIO_REQ_CTL_PIDS[@]} } = ${#ZIO_REQ_CTL_PIDS[@]}"
|
||||
echo "* zio_irq_ctl: { ${ZIO_IRQ_CTL_PIDS[@]} } = ${#ZIO_IRQ_CTL_PIDS[@]}"
|
||||
echo "* txg_quiesce: { ${TXG_QUIESCE_PIDS[@]} } = ${#TXG_QUIESCE_PIDS[@]}"
|
||||
echo "* txg_sync: { ${TXG_SYNC_PIDS[@]} } = ${#TXG_SYNC_PIDS[@]}"
|
||||
echo "* txg_timelimit: { ${TXG_TIMELIMIT_PIDS[@]} } = ${#TXG_TIMELIMIT_PIDS[@]}"
|
||||
echo "* arc_reclaim: { ${ARC_RECLAIM_PIDS[@]} } = ${#ARC_RECLAIM_PIDS[@]}"
|
||||
echo "* l2arc_feed: { ${L2ARC_FEED_PIDS[@]} } = ${#L2ARC_FEED_PIDS[@]}"
|
||||
echo "* zpios_io: { ${ZPIOS_IO_PIDS[@]} } = ${#ZPIOS_IO_PIDS[@]}"
|
||||
}
|
||||
|
||||
check_pid() {
|
||||
local PID=$1
|
||||
local NAME=$2
|
||||
local TYPE=$3
|
||||
local PIDS=( "$4" )
|
||||
local NAME_STRING=`echo ${NAME} | cut -f1 -d'/'`
|
||||
local NAME_NUMBER=`echo ${NAME} | cut -f2 -d'/'`
|
||||
|
||||
if [ "${NAME_STRING}" == "${TYPE}" ]; then
|
||||
if [ -n "${NAME_NUMBER}" ]; then
|
||||
PIDS[${NAME_NUMBER}]=${PID}
|
||||
else
|
||||
PIDS[${#PIDS[@]}]=${PID}
|
||||
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "${PIDS[@]}"
|
||||
}
|
||||
|
||||
# NOTE: This whole process is crazy slow but it will do for now
|
||||
aquire_pids() {
|
||||
echo "--- Aquiring ZFS pids ---"
|
||||
|
||||
for PID in `ls /proc/ | grep [0-9] | sort -n -u`; do
|
||||
if [ ! -e /proc/${PID}/status ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
NAME=`cat /proc/${PID}/status | head -n1 | cut -f2`
|
||||
|
||||
ZIO_TASKQ_PIDS=( `check_pid ${PID} ${NAME} "zio_taskq" \
|
||||
"$(echo "${ZIO_TASKQ_PIDS[@]}")"` )
|
||||
|
||||
ZIO_REQ_NUL_PIDS=( `check_pid ${PID} ${NAME} "zio_req_nul" \
|
||||
"$(echo "${ZIO_REQ_NUL_PIDS[@]}")"` )
|
||||
|
||||
ZIO_IRQ_NUL_PIDS=( `check_pid ${PID} ${NAME} "zio_irq_nul" \
|
||||
"$(echo "${ZIO_IRQ_NUL_PIDS[@]}")"` )
|
||||
|
||||
ZIO_REQ_RD_PIDS=( `check_pid ${PID} ${NAME} "zio_req_rd" \
|
||||
"$(echo "${ZIO_REQ_RD_PIDS[@]}")"` )
|
||||
|
||||
ZIO_IRQ_RD_PIDS=( `check_pid ${PID} ${NAME} "zio_irq_rd" \
|
||||
"$(echo "${ZIO_IRQ_RD_PIDS[@]}")"` )
|
||||
|
||||
ZIO_REQ_WR_PIDS=( `check_pid ${PID} ${NAME} "zio_req_wr" \
|
||||
"$(echo "${ZIO_REQ_WR_PIDS[@]}")"` )
|
||||
|
||||
ZIO_IRQ_WR_PIDS=( `check_pid ${PID} ${NAME} "zio_irq_wr" \
|
||||
"$(echo "${ZIO_IRQ_WR_PIDS[@]}")"` )
|
||||
|
||||
ZIO_REQ_FR_PIDS=( `check_pid ${PID} ${NAME} "zio_req_fr" \
|
||||
"$(echo "${ZIO_REQ_FR_PIDS[@]}")"` )
|
||||
|
||||
ZIO_IRQ_FR_PIDS=( `check_pid ${PID} ${NAME} "zio_irq_fr" \
|
||||
"$(echo "${ZIO_IRQ_FR_PIDS[@]}")"` )
|
||||
|
||||
ZIO_REQ_CM_PIDS=( `check_pid ${PID} ${NAME} "zio_req_cm" \
|
||||
"$(echo "${ZIO_REQ_CM_PIDS[@]}")"` )
|
||||
|
||||
ZIO_IRQ_CM_PIDS=( `check_pid ${PID} ${NAME} "zio_irq_cm" \
|
||||
"$(echo "${ZIO_IRQ_CM_PIDS[@]}")"` )
|
||||
|
||||
ZIO_REQ_CTL_PIDS=( `check_pid ${PID} ${NAME} "zio_req_ctl" \
|
||||
"$(echo "${ZIO_REQ_CTL_PIDS[@]}")"` )
|
||||
|
||||
ZIO_IRQ_CTL_PIDS=( `check_pid ${PID} ${NAME} "zio_irq_ctl" \
|
||||
"$(echo "${ZIO_IRQ_CTL_PIDS[@]}")"` )
|
||||
|
||||
TXG_QUIESCE_PIDS=( `check_pid ${PID} ${NAME} "txg_quiesce" \
|
||||
"$(echo "${TXG_QUIESCE_PIDS[@]}")"` )
|
||||
|
||||
TXG_SYNC_PIDS=( `check_pid ${PID} ${NAME} "txg_sync" \
|
||||
"$(echo "${TXG_SYNC_PIDS[@]}")"` )
|
||||
|
||||
TXG_TIMELIMIT_PIDS=( `check_pid ${PID} ${NAME} "txg_timelimit" \
|
||||
"$(echo "${TXG_TIMELIMIT_PIDS[@]}")"` )
|
||||
|
||||
ARC_RECLAIM_PIDS=( `check_pid ${PID} ${NAME} "arc_reclaim" \
|
||||
"$(echo "${ARC_RECLAIM_PIDS[@]}")"` )
|
||||
|
||||
L2ARC_FEED_PIDS=( `check_pid ${PID} ${NAME} "l2arc_feed" \
|
||||
"$(echo "${L2ARC_FEED_PIDS[@]}")"` )
|
||||
done
|
||||
|
||||
# Wait for zpios_io threads to start
|
||||
kill -s SIGHUP ${PPID}
|
||||
echo "* Waiting for zpios_io threads to start"
|
||||
while [ ${RUN_DONE} -eq 0 ]; do
|
||||
ZPIOS_IO_PIDS=( `ps ax | grep zpios_io | grep -v grep | \
|
||||
sed 's/^ *//g' | cut -f1 -d' '` )
|
||||
if [ ${#ZPIOS_IO_PIDS[@]} -gt 0 ]; then
|
||||
break;
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
echo "`show_pids`" >${RUN_LOG_DIR}/${RUN_ID}/pids.txt
|
||||
}
|
||||
|
||||
log_pids() {
|
||||
echo "--- Logging ZFS profile to ${RUN_LOG_DIR}/${RUN_ID}/ ---"
|
||||
ALL_PIDS=( ${ZIO_TASKQ_PIDS[@]} \
|
||||
${ZIO_REQ_NUL_PIDS[@]} \
|
||||
${ZIO_IRQ_NUL_PIDS[@]} \
|
||||
${ZIO_REQ_RD_PID[@]} \
|
||||
${ZIO_IRQ_RD_PIDS[@]} \
|
||||
${ZIO_REQ_WR_PIDS[@]} \
|
||||
${ZIO_IRQ_WR_PIDS[@]} \
|
||||
${ZIO_REQ_FR_PIDS[@]} \
|
||||
${ZIO_IRQ_FR_PIDS[@]} \
|
||||
${ZIO_REQ_CM_PIDS[@]} \
|
||||
${ZIO_IRQ_CM_PIDS[@]} \
|
||||
${ZIO_REQ_CTL_PIDS[@]} \
|
||||
${ZIO_IRQ_CTL_PIDS[@]} \
|
||||
${TXG_QUIESCE_PIDS[@]} \
|
||||
${TXG_SYNC_PIDS[@]} \
|
||||
${TXG_TIMELIMIT_PIDS[@]} \
|
||||
${ARC_RECLAIM_PIDS[@]} \
|
||||
${L2ARC_FEED_PIDS[@]} \
|
||||
${ZPIOS_IO_PIDS[@]} )
|
||||
|
||||
while [ ${RUN_DONE} -eq 0 ]; do
|
||||
NOW=`date +%s.%N`
|
||||
LOG_PIDS="${RUN_LOG_DIR}/${RUN_ID}/pids-${NOW}"
|
||||
LOG_DISK="${RUN_LOG_DIR}/${RUN_ID}/disk-${NOW}"
|
||||
|
||||
for PID in "${ALL_PIDS[@]}"; do
|
||||
if [ -z ${PID} ]; then
|
||||
continue;
|
||||
fi
|
||||
|
||||
if [ -e /proc/${PID}/stat ]; then
|
||||
cat /proc/${PID}/stat | head -n1 >>${LOG_PIDS}
|
||||
else
|
||||
echo "<${PID} exited>" >>${LOG_PIDS}
|
||||
fi
|
||||
done
|
||||
|
||||
cat /proc/diskstats >${LOG_DISK}
|
||||
|
||||
NOW2=`date +%s.%N`
|
||||
DELTA=`echo "${POLL_INTERVAL}-(${NOW2}-${NOW})" | bc`
|
||||
sleep ${DELTA}
|
||||
done
|
||||
}
|
||||
|
||||
aquire_pids
|
||||
log_pids
|
||||
|
||||
# rm ${PROFILE_PID}
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,208 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Wrapper script for easily running a survey of zpios based tests
|
||||
#
|
||||
|
||||
. ./common.sh
|
||||
PROG=zpios-survey.sh
|
||||
|
||||
usage() {
|
||||
cat << EOF
|
||||
USAGE:
|
||||
$0 [hvp] <-c config> <-t test>
|
||||
|
||||
DESCRIPTION:
|
||||
Helper script for easy zpios survey benchmarking.
|
||||
|
||||
OPTIONS:
|
||||
-h Show this message
|
||||
-v Verbose
|
||||
-p Enable profiling
|
||||
-c Zpool configuration
|
||||
-t Zpios test
|
||||
-l Zpios survey log
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
print_header() {
|
||||
tee -a ${ZPIOS_SURVEY_LOG} << EOF
|
||||
|
||||
================================================================
|
||||
Test: $1
|
||||
EOF
|
||||
}
|
||||
|
||||
# Baseline performance for an out of the box config with no manual tuning.
|
||||
# Ideally, we want everything to be automatically tuned for your system and
|
||||
# for this to perform reasonably well.
|
||||
zpios_survey_base() {
|
||||
TEST_NAME="${ZPOOL_CONFIG}+${ZPIOS_TEST}+baseline"
|
||||
print_header ${TEST_NAME}
|
||||
|
||||
./zfs.sh ${VERBOSE_FLAG} | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
./zpios.sh ${VERBOSE_FLAG} -c ${ZPOOL_CONFIG} -t ${ZPIOS_TEST} | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
./zfs.sh -u ${VERBOSE_FLAG} | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
}
|
||||
|
||||
# Disable ZFS's prefetching. For some reason still not clear to me
|
||||
# current prefetching policy is quite bad for a random workload.
|
||||
# Allowint the algorithm to detect a random workload and not do
|
||||
# anything may be the way to address this issue.
|
||||
zpios_survey_prefetch() {
|
||||
TEST_NAME="${ZPOOL_CONFIG}+${ZPIOS_TEST}+prefetch"
|
||||
print_header ${TEST_NAME}
|
||||
|
||||
./zfs.sh ${VERBOSE_FLAG} \
|
||||
zfs="zfs_prefetch_disable=1" | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
./zpios.sh ${VERBOSE_FLAG} -c ${ZPOOL_CONFIG} -t ${ZPIOS_TEST} | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
./zfs.sh -u ${VERBOSE_FLAG} | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
}
|
||||
|
||||
# Simulating a zerocopy IO path should improve performance by freeing up
|
||||
# lots of CPU which is wasted move data between buffers.
|
||||
zpios_survey_zerocopy() {
|
||||
TEST_NAME="${ZPOOL_CONFIG}+${ZPIOS_TEST}+zerocopy"
|
||||
print_header ${TEST_NAME}
|
||||
|
||||
./zfs.sh ${VERBOSE_FLAG} | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
./zpios.sh ${VERBOSE_FLAG} -c ${ZPOOL_CONFIG} -t ${ZPIOS_TEST} \
|
||||
-o "--zerocopy" | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
./zfs.sh -u ${VERBOSE_FLAG} | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
}
|
||||
|
||||
# Disabling checksumming should show some (if small) improvement
|
||||
# simply due to freeing up a modest amount of CPU.
|
||||
zpios_survey_checksum() {
|
||||
TEST_NAME="${ZPOOL_CONFIG}+${ZPIOS_TEST}+checksum"
|
||||
print_header ${TEST_NAME}
|
||||
|
||||
./zfs.sh ${VERBOSE_FLAG} | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
./zpios.sh ${VERBOSE_FLAG} -c ${ZPOOL_CONFIG} -t ${ZPIOS_TEST} \
|
||||
-s "set checksum=off" | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
./zfs.sh -u ${VERBOSE_FLAG} | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
}
|
||||
|
||||
# Increasing the pending IO depth also seems to improve things likely
|
||||
# at the expense of latency. This should be explored more because I'm
|
||||
# seeing a much bigger impact there that I would have expected. There
|
||||
# may be some low hanging fruit to be found here.
|
||||
zpios_survey_pending() {
|
||||
TEST_NAME="${ZPOOL_CONFIG}+${ZPIOS_TEST}+pending"
|
||||
print_header ${TEST_NAME}
|
||||
|
||||
./zfs.sh ${VERBOSE_FLAG} \
|
||||
zfs="zfs_vdev_max_pending=1024" | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
./zpios.sh ${VERBOSE_FLAG} -c ${ZPOOL_CONFIG} -t ${ZPIOS_TEST} | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
./zfs.sh -u ${VERBOSE_FLAG} | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
}
|
||||
|
||||
# To avoid memory fragmentation issues our slab implementation can be
|
||||
# based on a virtual address space. Interestingly, we take a pretty
|
||||
# substantial performance penalty for this somewhere in the low level
|
||||
# IO drivers. If we back the slab with kmem pages we see far better
|
||||
# read performance numbers at the cost of memory fragmention and general
|
||||
# system instability due to large allocations. This may be because of
|
||||
# an optimization in the low level drivers due to the contigeous kmem
|
||||
# based memory. This needs to be explained. The good news here is that
|
||||
# with zerocopy interfaces added at the DMU layer we could gaurentee
|
||||
# kmem based memory for a pool of pages.
|
||||
#
|
||||
# 0x100 = KMC_KMEM - Force kmem_* based slab
|
||||
# 0x200 = KMC_VMEM - Force vmem_* based slab
|
||||
zpios_survey_kmem() {
|
||||
TEST_NAME="${ZPOOL_CONFIG}+${ZPIOS_TEST}+kmem"
|
||||
print_header ${TEST_NAME}
|
||||
|
||||
./zfs.sh ${VERBOSE_FLAG} \
|
||||
zfs="zio_bulk_flags=0x100" | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
./zpios.sh ${VERBOSE_FLAG} -c ${ZPOOL_CONFIG} -t ${ZPIOS_TEST} | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
./zfs.sh -u ${VERBOSE_FLAG} | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
}
|
||||
|
||||
# Apply all possible turning concurrently to get a best case number
|
||||
zpios_survey_all() {
|
||||
TEST_NAME="${ZPOOL_CONFIG}+${ZPIOS_TEST}+all"
|
||||
print_header ${TEST_NAME}
|
||||
|
||||
./zfs.sh ${VERBOSE_FLAG} \
|
||||
zfs="zfs_prefetch_disable=1" \
|
||||
zfs="zfs_vdev_max_pending=1024" \
|
||||
zfs="zio_bulk_flags=0x100" | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
./zpios.sh ${VERBOSE_FLAG} -c ${ZPOOL_CONFIG} -t ${ZPIOS_TEST} \
|
||||
-o "--zerocopy" \
|
||||
-s "set checksum=off" | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
./zfs.sh -u ${VERBOSE_FLAG} | \
|
||||
tee -a ${ZPIOS_SURVEY_LOG}
|
||||
}
|
||||
|
||||
|
||||
PROFILE=
|
||||
ZPOOL_NAME=zpios-survey
|
||||
ZPOOL_CONFIG=zpool-config.sh
|
||||
ZPIOS_TEST=zpios-test.sh
|
||||
ZPIOS_SURVEY_LOG=/dev/null
|
||||
|
||||
while getopts 'hvpc:t:l:' OPTION; do
|
||||
case $OPTION in
|
||||
h)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
v)
|
||||
VERBOSE=1
|
||||
VERBOSE_FLAG="-v"
|
||||
;;
|
||||
p)
|
||||
PROFILE=1
|
||||
PROFILE_FLAG="-p"
|
||||
;;
|
||||
c)
|
||||
ZPOOL_CONFIG=${OPTARG}
|
||||
;;
|
||||
t)
|
||||
ZPIOS_TEST=${OPTARG}
|
||||
;;
|
||||
l)
|
||||
ZPIOS_SURVEY_LOG=${OPTARG}
|
||||
;;
|
||||
?)
|
||||
usage
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ $(id -u) != 0 ]; then
|
||||
die "Must run as root"
|
||||
fi
|
||||
|
||||
zpios_survey_base
|
||||
zpios_survey_prefetch
|
||||
zpios_survey_zerocopy
|
||||
zpios_survey_checksum
|
||||
zpios_survey_pending
|
||||
zpios_survey_kmem
|
||||
zpios_survey_all
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,60 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Usage: zpios
|
||||
# --threadcount -t =values
|
||||
# --threadcount_low -l =value
|
||||
# --threadcount_high -h =value
|
||||
# --threadcount_incr -e =value
|
||||
# --regioncount -n =values
|
||||
# --regioncount_low -i =value
|
||||
# --regioncount_high -j =value
|
||||
# --regioncount_incr -k =value
|
||||
# --offset -o =values
|
||||
# --offset_low -m =value
|
||||
# --offset_high -q =value
|
||||
# --offset_incr -r =value
|
||||
# --chunksize -c =values
|
||||
# --chunksize_low -a =value
|
||||
# --chunksize_high -b =value
|
||||
# --chunksize_incr -g =value
|
||||
# --regionsize -s =values
|
||||
# --regionsize_low -A =value
|
||||
# --regionsize_high -B =value
|
||||
# --regionsize_incr -C =value
|
||||
# --load -L =dmuio|ssf|fpp
|
||||
# --pool -p =pool name
|
||||
# --cleanup -x
|
||||
# --prerun -P =pre-command
|
||||
# --postrun -R =post-command
|
||||
# --log -G =log directory
|
||||
# --regionnoise -I =shift
|
||||
# --chunknoise -N =bytes
|
||||
# --threaddelay -T =jiffies
|
||||
# --verify -V
|
||||
# --zerocopy -z
|
||||
# --nowait -O
|
||||
# --human-readable -H
|
||||
# --verbose -v =increase verbosity
|
||||
# --help -? =this help
|
||||
|
||||
ZPIOS_CMD="${CMDDIR}/zpios/zpios \
|
||||
--load=dmuio \
|
||||
--pool=${ZPOOL_NAME} \
|
||||
--threadcount=16 \
|
||||
--regioncount=8192 \
|
||||
--regionsize=4M \
|
||||
--chunksize=1M \
|
||||
--offset=4M \
|
||||
--cleanup \
|
||||
--verbose \
|
||||
--human-readable \
|
||||
${ZPIOS_OPTIONS}"
|
||||
|
||||
zpios_start() {
|
||||
echo ${ZPIOS_CMD}
|
||||
${ZPIOS_CMD} || exit 1
|
||||
}
|
||||
|
||||
zpios_stop() {
|
||||
echo
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Usage: zpios
|
||||
# --threadcount -t =values
|
||||
# --threadcount_low -l =value
|
||||
# --threadcount_high -h =value
|
||||
# --threadcount_incr -e =value
|
||||
# --regioncount -n =values
|
||||
# --regioncount_low -i =value
|
||||
# --regioncount_high -j =value
|
||||
# --regioncount_incr -k =value
|
||||
# --offset -o =values
|
||||
# --offset_low -m =value
|
||||
# --offset_high -q =value
|
||||
# --offset_incr -r =value
|
||||
# --chunksize -c =values
|
||||
# --chunksize_low -a =value
|
||||
# --chunksize_high -b =value
|
||||
# --chunksize_incr -g =value
|
||||
# --regionsize -s =values
|
||||
# --regionsize_low -A =value
|
||||
# --regionsize_high -B =value
|
||||
# --regionsize_incr -C =value
|
||||
# --load -L =dmuio|ssf|fpp
|
||||
# --pool -p =pool name
|
||||
# --cleanup -x
|
||||
# --prerun -P =pre-command
|
||||
# --postrun -R =post-command
|
||||
# --log -G =log directory
|
||||
# --regionnoise -I =shift
|
||||
# --chunknoise -N =bytes
|
||||
# --threaddelay -T =jiffies
|
||||
# --verify -V
|
||||
# --zerocopy -z
|
||||
# --nowait -O
|
||||
# --human-readable -H
|
||||
# --verbose -v =increase verbosity
|
||||
# --help -? =this help
|
||||
|
||||
|
||||
ZPIOS_CMD="${CMDDIR}/zpios/zpios \
|
||||
--load=dmuio \
|
||||
--pool=${ZPOOL_NAME} \
|
||||
--threadcount=1 \
|
||||
--regioncount=16 \
|
||||
--regionsize=4M \
|
||||
--chunksize=1M \
|
||||
--offset=4M \
|
||||
--cleanup \
|
||||
--verbose \
|
||||
--human-readable \
|
||||
${ZPIOS_OPTIONS}"
|
||||
|
||||
zpios_start() {
|
||||
echo ${ZPIOS_CMD}
|
||||
${ZPIOS_CMD} || exit 1
|
||||
}
|
||||
|
||||
zpios_stop() {
|
||||
echo
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Usage: zpios
|
||||
# --threadcount -t =values
|
||||
# --threadcount_low -l =value
|
||||
# --threadcount_high -h =value
|
||||
# --threadcount_incr -e =value
|
||||
# --regioncount -n =values
|
||||
# --regioncount_low -i =value
|
||||
# --regioncount_high -j =value
|
||||
# --regioncount_incr -k =value
|
||||
# --offset -o =values
|
||||
# --offset_low -m =value
|
||||
# --offset_high -q =value
|
||||
# --offset_incr -r =value
|
||||
# --chunksize -c =values
|
||||
# --chunksize_low -a =value
|
||||
# --chunksize_high -b =value
|
||||
# --chunksize_incr -g =value
|
||||
# --regionsize -s =values
|
||||
# --regionsize_low -A =value
|
||||
# --regionsize_high -B =value
|
||||
# --regionsize_incr -C =value
|
||||
# --load -L =dmuio|ssf|fpp
|
||||
# --pool -p =pool name
|
||||
# --cleanup -x
|
||||
# --prerun -P =pre-command
|
||||
# --postrun -R =post-command
|
||||
# --log -G =log directory
|
||||
# --regionnoise -I =shift
|
||||
# --chunknoise -N =bytes
|
||||
# --threaddelay -T =jiffies
|
||||
# --verify -V
|
||||
# --zerocopy -z
|
||||
# --nowait -O
|
||||
# --human-readable -H
|
||||
# --verbose -v =increase verbosity
|
||||
# --help -? =this help
|
||||
|
||||
ZPIOS_CMD="${CMDDIR}/zpios/zpios \
|
||||
--load=dmuio \
|
||||
--pool=${ZPOOL_NAME} \
|
||||
--threadcount=1,2,4,8,16,32,64,128,256 \
|
||||
--regioncount=65536 \
|
||||
--regionsize=4M \
|
||||
--chunksize=1M \
|
||||
--offset=4M \
|
||||
--cleanup \
|
||||
--verbose \
|
||||
--human-readable \
|
||||
${ZPIOS_OPTIONS}"
|
||||
|
||||
zpios_start() {
|
||||
echo ${ZPIOS_CMD}
|
||||
${ZPIOS_CMD} || exit 1
|
||||
}
|
||||
|
||||
zpios_stop() {
|
||||
echo
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Usage: zpios
|
||||
# --threadcount -t =values
|
||||
# --threadcount_low -l =value
|
||||
# --threadcount_high -h =value
|
||||
# --threadcount_incr -e =value
|
||||
# --regioncount -n =values
|
||||
# --regioncount_low -i =value
|
||||
# --regioncount_high -j =value
|
||||
# --regioncount_incr -k =value
|
||||
# --offset -o =values
|
||||
# --offset_low -m =value
|
||||
# --offset_high -q =value
|
||||
# --offset_incr -r =value
|
||||
# --chunksize -c =values
|
||||
# --chunksize_low -a =value
|
||||
# --chunksize_high -b =value
|
||||
# --chunksize_incr -g =value
|
||||
# --regionsize -s =values
|
||||
# --regionsize_low -A =value
|
||||
# --regionsize_high -B =value
|
||||
# --regionsize_incr -C =value
|
||||
# --load -L =dmuio|ssf|fpp
|
||||
# --pool -p =pool name
|
||||
# --cleanup -x
|
||||
# --prerun -P =pre-command
|
||||
# --postrun -R =post-command
|
||||
# --log -G =log directory
|
||||
# --regionnoise -I =shift
|
||||
# --chunknoise -N =bytes
|
||||
# --threaddelay -T =jiffies
|
||||
# --verify -V
|
||||
# --zerocopy -z
|
||||
# --nowait -O
|
||||
# --human-readable -H
|
||||
# --verbose -v =increase verbosity
|
||||
# --help -? =this help
|
||||
|
||||
ZPIOS_CMD="${CMDDIR}/zpios/zpios \
|
||||
--load=dmuio \
|
||||
--pool=${ZPOOL_NAME} \
|
||||
--threadcount=256 \
|
||||
--regioncount=65536 \
|
||||
--regionsize=4M \
|
||||
--chunksize=1M \
|
||||
--offset=4M \
|
||||
--cleanup \
|
||||
--verbose \
|
||||
--human-readable \
|
||||
${ZPIOS_OPTIONS}"
|
||||
|
||||
zpios_start() {
|
||||
echo ${ZPIOS_CMD}
|
||||
${ZPIOS_CMD} || exit 1
|
||||
}
|
||||
|
||||
zpios_stop() {
|
||||
echo
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Usage: zpios
|
||||
# --threadcount -t =values
|
||||
# --threadcount_low -l =value
|
||||
# --threadcount_high -h =value
|
||||
# --threadcount_incr -e =value
|
||||
# --regioncount -n =values
|
||||
# --regioncount_low -i =value
|
||||
# --regioncount_high -j =value
|
||||
# --regioncount_incr -k =value
|
||||
# --offset -o =values
|
||||
# --offset_low -m =value
|
||||
# --offset_high -q =value
|
||||
# --offset_incr -r =value
|
||||
# --chunksize -c =values
|
||||
# --chunksize_low -a =value
|
||||
# --chunksize_high -b =value
|
||||
# --chunksize_incr -g =value
|
||||
# --regionsize -s =values
|
||||
# --regionsize_low -A =value
|
||||
# --regionsize_high -B =value
|
||||
# --regionsize_incr -C =value
|
||||
# --load -L =dmuio|ssf|fpp
|
||||
# --pool -p =pool name
|
||||
# --cleanup -x
|
||||
# --prerun -P =pre-command
|
||||
# --postrun -R =post-command
|
||||
# --log -G =log directory
|
||||
# --regionnoise -I =shift
|
||||
# --chunknoise -N =bytes
|
||||
# --threaddelay -T =jiffies
|
||||
# --verify -V
|
||||
# --zerocopy -z
|
||||
# --nowait -O
|
||||
# --human-readable -H
|
||||
# --verbose -v =increase verbosity
|
||||
# --help -? =this help
|
||||
|
||||
ZPIOS_CMD="${CMDDIR}/zpios/zpios \
|
||||
--load=dmuio \
|
||||
--pool=${ZPOOL_NAME} \
|
||||
--threadcount=4 \
|
||||
--regioncount=1024 \
|
||||
--regionsize=4M \
|
||||
--chunksize=1M \
|
||||
--offset=4M \
|
||||
--cleanup \
|
||||
--verbose \
|
||||
--human-readable \
|
||||
${ZPIOS_OPTIONS}"
|
||||
|
||||
zpios_start() {
|
||||
echo ${ZPIOS_CMD}
|
||||
${ZPIOS_CMD} || exit 1
|
||||
}
|
||||
|
||||
zpios_stop() {
|
||||
echo
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
EXTRA_DIST = *.sh
|
|
@ -0,0 +1 @@
|
|||
1x256th-65536rc-4rs-1cs-4off.sh
|
|
@ -0,0 +1 @@
|
|||
256th-65536rc-4rs-1cs-4off.sh
|
|
@ -0,0 +1 @@
|
|||
16th-8192rc-4rs-1cs-4off.sh
|
|
@ -0,0 +1 @@
|
|||
4th-1024rc-4rs-1cs-4off.sh
|
|
@ -0,0 +1 @@
|
|||
1th-16rc-4rs-1cs-4off.sh
|
|
@ -0,0 +1,258 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Wrapper script for easily running zpios based tests
|
||||
#
|
||||
|
||||
. ./common.sh
|
||||
PROG=zpios.sh
|
||||
DATE=`date +%Y%m%d-%H%M%S`
|
||||
|
||||
MODULES=( \
|
||||
${MODDIR}/zpios/zpios.ko \
|
||||
)
|
||||
|
||||
usage() {
|
||||
cat << EOF
|
||||
USAGE:
|
||||
$0 [hvp] <-c config> <-t test>
|
||||
|
||||
DESCRIPTION:
|
||||
Helper script for easy zpios benchmarking.
|
||||
|
||||
OPTIONS:
|
||||
-h Show this message
|
||||
-v Verbose
|
||||
-p Enable profiling
|
||||
-c Zpool configuration
|
||||
-t Zpios test
|
||||
-o Additional zpios options
|
||||
-l Additional zpool options
|
||||
-s Additional zfs options
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
print_header() {
|
||||
echo --------------------- ZPIOS RESULTS ----------------------------
|
||||
echo -n "Date: "; date
|
||||
echo -n "Kernel: "; uname -r
|
||||
dmesg | grep "Loaded Solaris Porting Layer" | tail -n1
|
||||
dmesg | grep "Loaded ZFS Filesystem" | tail -n1
|
||||
echo
|
||||
}
|
||||
|
||||
print_spl_info() {
|
||||
echo --------------------- SPL Tunings ------------------------------
|
||||
sysctl -A | grep spl
|
||||
|
||||
if [ -d /sys/module/spl/parameters ]; then
|
||||
grep [0-9] /sys/module/spl/parameters/*
|
||||
else
|
||||
grep [0-9] /sys/module/spl/*
|
||||
fi
|
||||
|
||||
echo
|
||||
}
|
||||
|
||||
print_zfs_info() {
|
||||
echo --------------------- ZFS Tunings ------------------------------
|
||||
sysctl -A | grep zfs
|
||||
|
||||
if [ -d /sys/module/zfs/parameters ]; then
|
||||
grep [0-9] /sys/module/zfs/parameters/*
|
||||
else
|
||||
grep [0-9] /sys/module/zfs/*
|
||||
fi
|
||||
|
||||
echo
|
||||
}
|
||||
|
||||
print_stats() {
|
||||
echo ---------------------- Statistics -------------------------------
|
||||
sysctl -A | grep spl | grep stack_max
|
||||
|
||||
if [ -d /proc/spl/kstat/ ]; then
|
||||
if [ -f /proc/spl/kstat/zfs/arcstats ]; then
|
||||
echo "* ARC"
|
||||
cat /proc/spl/kstat/zfs/arcstats
|
||||
echo
|
||||
fi
|
||||
|
||||
if [ -f /proc/spl/kstat/zfs/vdev_cache_stats ]; then
|
||||
echo "* VDEV Cache"
|
||||
cat /proc/spl/kstat/zfs/vdev_cache_stats
|
||||
echo
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -f /proc/spl/kmem/slab ]; then
|
||||
echo "* SPL SLAB"
|
||||
cat /proc/spl/kmem/slab
|
||||
echo
|
||||
fi
|
||||
|
||||
echo
|
||||
}
|
||||
|
||||
check_test() {
|
||||
|
||||
if [ ! -f ${ZPIOS_TEST} ]; then
|
||||
local NAME=`basename ${ZPIOS_TEST} .sh`
|
||||
ERROR="Unknown test '${NAME}', available tests are:\n"
|
||||
|
||||
for TST in `ls ${TOPDIR}/scripts/zpios-test/`; do
|
||||
local NAME=`basename ${TST} .sh`
|
||||
ERROR="${ERROR}${NAME}\n"
|
||||
done
|
||||
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
zpios_profile_config() {
|
||||
cat > ${PROFILE_DIR}/zpios-config.sh << EOF
|
||||
#
|
||||
# Zpios Profiling Configuration
|
||||
#
|
||||
|
||||
PROFILE_DIR=/tmp/zpios/${ZPOOL_CONFIG}+${ZPIOS_TEST_ARG}+${DATE}
|
||||
PROFILE_PRE=${TOPDIR}/scripts/zpios-profile/zpios-profile-pre.sh
|
||||
PROFILE_POST=${TOPDIR}/scripts/zpios-profile/zpios-profile-post.sh
|
||||
PROFILE_USER=${TOPDIR}/scripts/zpios-profile/zpios-profile.sh
|
||||
PROFILE_PIDS=${TOPDIR}/scripts/zpios-profile/zpios-profile-pids.sh
|
||||
PROFILE_DISK=${TOPDIR}/scripts/zpios-/profile/zpios-profile-disk.sh
|
||||
PROFILE_ARC_PROC=/proc/spl/kstat/zfs/arcstats
|
||||
PROFILE_VDEV_CACHE_PROC=/proc/spl/kstat/zfs/vdev_cache_stats
|
||||
|
||||
OPROFILE_KERNEL="/boot/vmlinux-`uname -r`"
|
||||
OPROFILE_KERNEL_DIR="/lib/modules/`uname -r`/kernel/"
|
||||
OPROFILE_SPL_DIR="${SPLBUILD}/module/"
|
||||
OPROFILE_ZFS_DIR="${TOPDIR}/module/"
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
zpios_profile_start() {
|
||||
PROFILE_DIR=/tmp/zpios/${ZPOOL_CONFIG}+${ZPIOS_TEST_ARG}+${DATE}
|
||||
|
||||
mkdir -p ${PROFILE_DIR}
|
||||
zpios_profile_config
|
||||
. ${PROFILE_DIR}/zpios-config.sh
|
||||
|
||||
ZPIOS_OPTIONS="${ZPIOS_OPTIONS} --log=${PROFILE_DIR}"
|
||||
ZPIOS_OPTIONS="${ZPIOS_OPTIONS} --prerun=${PROFILE_PRE}"
|
||||
ZPIOS_OPTIONS="${ZPIOS_OPTIONS} --postrun=${PROFILE_POST}"
|
||||
|
||||
/usr/bin/opcontrol --init
|
||||
/usr/bin/opcontrol --setup --vmlinux=${OPROFILE_KERNEL}
|
||||
}
|
||||
|
||||
zpios_profile_stop() {
|
||||
/usr/bin/opcontrol --shutdown
|
||||
/usr/bin/opcontrol --deinit
|
||||
}
|
||||
|
||||
PROFILE=
|
||||
ZPOOL_CONFIG=zpool-config.sh
|
||||
ZPIOS_TEST=zpios-test.sh
|
||||
ZPOOL_NAME=zpios
|
||||
ZPIOS_OPTIONS=
|
||||
ZPOOL_OPTIONS=""
|
||||
ZFS_OPTIONS=""
|
||||
|
||||
while getopts 'hvpc:t:o:l:s:' OPTION; do
|
||||
case $OPTION in
|
||||
h)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
v)
|
||||
VERBOSE=1
|
||||
VERBOSE_FLAG="-v"
|
||||
;;
|
||||
p)
|
||||
PROFILE=1
|
||||
;;
|
||||
c)
|
||||
ZPOOL_CONFIG=${OPTARG}
|
||||
;;
|
||||
t)
|
||||
ZPIOS_TEST_ARG=${OPTARG}
|
||||
ZPIOS_TEST=${TOPDIR}/scripts/zpios-test/${OPTARG}.sh
|
||||
;;
|
||||
o)
|
||||
ZPIOS_OPTIONS=${OPTARG}
|
||||
;;
|
||||
l) # Passed through to zpool-create.sh
|
||||
ZPOOL_OPTIONS=${OPTARG}
|
||||
;;
|
||||
s) # Passed through to zpool-create.sh
|
||||
ZFS_OPTIONS=${OPTARG}
|
||||
;;
|
||||
?)
|
||||
usage
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ $(id -u) != 0 ]; then
|
||||
die "Must run as root"
|
||||
fi
|
||||
|
||||
# Validate and source your test config
|
||||
check_test || die "${ERROR}"
|
||||
. ${ZPIOS_TEST}
|
||||
|
||||
# Pull in the zpios test module is not loaded. If this fails it is
|
||||
# likely because the full module stack was not yet loaded with zfs.sh
|
||||
if check_modules; then
|
||||
if ! load_modules; then
|
||||
die "Run 'zfs.sh' to ensure the full module stack is loaded"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Wait for device creation
|
||||
while [ ! -c /dev/zpios ]; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
if [ ${VERBOSE} ]; then
|
||||
print_header
|
||||
print_spl_info
|
||||
print_zfs_info
|
||||
fi
|
||||
|
||||
# Create the zpool configuration
|
||||
./zpool-create.sh ${VERBOSE_FLAG} -p ${ZPOOL_NAME} -c ${ZPOOL_CONFIG} \
|
||||
-l "${ZPOOL_OPTIONS}" -s "${ZFS_OPTIONS}" || exit 1
|
||||
|
||||
if [ ${PROFILE} ]; then
|
||||
zpios_profile_start
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "${DATE}"
|
||||
zpios_start
|
||||
zpios_stop
|
||||
|
||||
if [ ${PROFILE} ]; then
|
||||
zpios_profile_stop
|
||||
fi
|
||||
|
||||
if [ ${VERBOSE} ]; then
|
||||
print_stats
|
||||
fi
|
||||
|
||||
# Destroy the zpool configuration
|
||||
./zpool-create.sh ${VERBOSE_FLAG} -p ${ZPOOL_NAME} -c ${ZPOOL_CONFIG} -d || exit 1
|
||||
|
||||
# Unload the test module stack and wait for device removal
|
||||
unload_modules
|
||||
while [ -c /dev/zpios ]; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
exit 0
|
Loading…
Reference in New Issue