1 #
   2 # This file and its contents are supplied under the terms of the
   3 # Common Development and Distribution License ("CDDL"), version 1.0.
   4 # You may only use this file in accordance with the terms of version
   5 # 1.0 of the CDDL.
   6 #
   7 # A full copy of the text of the CDDL should have accompanied this
   8 # source.  A copy of the CDDL is also available via the Internet at
   9 # http://www.illumos.org/license/CDDL.
  10 #
  11 
  12 #
  13 # Copyright (c) 2015 by Delphix. All rights reserved.
  14 #
  15 
  16 . $STF_SUITE/include/libtest.shlib
  17 
  18 # If neither is specified, do a nightly run.
  19 [[ -z $PERF_REGRESSION_WEEKLY ]] && export PERF_REGRESSION_NIGHTLY=1
  20 
  21 # Default runtime for each type of test run.
  22 export PERF_RUNTIME_WEEKLY=$((30 * 60))
  23 export PERF_RUNTIME_NIGHTLY=$((10 * 60))
  24 
  25 # Default fs creation options
  26 export PERF_FS_OPTS=${PERF_FS_OPTS:-'-o recsize=8k -o compress=lz4' \
  27     ' -o checksum=sha256 -o redundant_metadata=most'}
  28 
  29 function get_sync_str
  30 {
  31         typeset sync=$1
  32         typeset sync_str=''
  33 
  34         [[ $sync -eq 0 ]] && sync_str='async'
  35         [[ $sync -eq 1 ]] && sync_str='sync'
  36         echo $sync_str
  37 }
  38 
  39 #
  40 # This function will run fio in a loop, according to the .fio file passed
  41 # in and a number of environment variables. The following variables can be
  42 # set before launching zfstest to override the defaults.
  43 #
  44 # PERF_RUNTIME: The time in seconds each fio invocation should run.
  45 # PERF_RUNTYPE: A human readable tag that appears in logs. The defaults are
  46 #    nightly and weekly.
  47 # PERF_NTHREADS: A list of how many threads each fio invocation will use.
  48 # PERF_SYNC_TYPES: Whether to use (O_SYNC) or not. 1 is sync IO, 0 is async IO.
  49 # PERF_IOSIZES: A list of blocksizes in which each fio invocation will do IO.
  50 # PERF_COLLECT_SCRIPTS: A comma delimited list of 'command args, logfile_tag'
  51 #    pairs that will be added to the scripts specified in each test.
  52 #
  53 function do_fio_run
  54 {
  55         typeset script=$1
  56         typeset do_recreate=$2
  57         typeset clear_cache=$3
  58         typeset threads sync iosize
  59 
  60         for threads in $PERF_NTHREADS; do
  61                 for sync in $PERF_SYNC_TYPES; do
  62                         for iosize in $PERF_IOSIZES; do
  63                                 log_note "Running with $threads" \
  64                                     "$(get_sync_str $sync) threads, $iosize ios"
  65 
  66                                 if $do_recreate; then
  67                                         recreate_perfpool
  68                                         log_must $ZFS create $PERF_FS_OPTS \
  69                                             $TESTFS
  70                                 fi
  71 
  72                                 if $clear_cache; then
  73                                         # Clear the ARC
  74                                         $ZPOOL export $PERFPOOL
  75                                         $ZPOOL import $PERFPOOL
  76                                 fi
  77 
  78                                 export RUNTIME=$PERF_RUNTIME
  79                                 export FILESIZE=$((TOTAL_SIZE / threads))
  80                                 export NUMJOBS=$threads
  81                                 export SYNC_TYPE=$sync
  82                                 export BLOCKSIZE=$iosize
  83                                 $SYNC
  84 
  85                                 # Start the data collection
  86                                 do_collect_scripts $threads $sync $iosize
  87 
  88                                 # Start the load
  89                                 log_must $FIO $FIO_SCRIPTS/$script
  90                         done
  91                 done
  92         done
  93 }
  94 
  95 #
  96 # This function iterates through the value pairs in $PERF_COLLECT_SCRIPTS.
  97 # The script at index N is launched in the background, with its output
  98 # redirected to a logfile containing the tag specified at index N + 1.
  99 #
 100 function do_collect_scripts
 101 {
 102         typeset threads=$1
 103         typeset sync=$2
 104         typeset iosize=$3
 105 
 106         [[ -n $collect_scripts ]] || log_fail "No data collection scripts."
 107         [[ -n $PERF_RUNTIME ]] || log_fail "No runtime specified."
 108 
 109         # This will be part of the output filename.
 110         typeset sync_str=$(get_sync_str $sync)
 111         typeset suffix="$sync_str.$iosize-ios.$threads-threads"
 112 
 113         # Add in user supplied scripts and logfiles, if any.
 114         typeset oIFS=$IFS
 115         IFS=','
 116         for item in $PERF_COLLECT_SCRIPTS; do
 117                 collect_scripts+=($($ECHO $item | $SED 's/^ *//g'))
 118         done
 119         IFS=$oIFS
 120 
 121         typeset idx=0
 122         while [[ $idx -lt "${#collect_scripts[@]}" ]]; do
 123                 typeset logbase="$(get_perf_output_dir)/$($BASENAME \
 124                     $SUDO_COMMAND)"
 125                 typeset outfile="$logbase.${collect_scripts[$idx + 1]}.$suffix"
 126 
 127                 $TIMEOUT $PERF_RUNTIME ${collect_scripts[$idx]} >$outfile 2>&1 &
 128                 ((idx += 2))
 129         done
 130 
 131         # Need to explicitly return 0 because timeout(1) will kill
 132         # a child process and cause us to return non-zero.
 133         return 0
 134 }
 135 
 136 # Find a place to deposit performance data collected while under load.
 137 function get_perf_output_dir
 138 {
 139         typeset dir="$(pwd)/perf_data"
 140         [[ -d $dir ]] || $MKDIR -p $dir
 141 
 142         $ECHO $dir
 143 }
 144 
 145 #
 146 # Destroy and create the pool used for performance tests. The
 147 # PERFPOOL_CREATE_CMD variable allows users to test with a custom pool
 148 # configuration by specifying the pool creation command in their environment.
 149 # If PERFPOOL_CREATE_CMD is empty, a pool using all available disks is created.
 150 #
 151 function recreate_perfpool
 152 {
 153         [[ -n $PERFPOOL ]] || log_fail "The \$PERFPOOL variable isn't set."
 154 
 155         poolexists $PERFPOOL && destroy_pool $PERFPOOL
 156 
 157         if [[ -n $PERFPOOL_CREATE_CMD ]]; then
 158                 log_must $PERFPOOL_CREATE_CMD
 159         else
 160                 log_must eval "$ZPOOL create -f $PERFPOOL $DISKS"
 161         fi
 162 }
 163 
 164 function get_max_arc_size
 165 {
 166         typeset -l max_arc_size=$(dtrace -qn 'BEGIN {
 167             printf("%u\n", `arc_stats.arcstat_c_max.value.ui64);
 168             exit(0);
 169         }')
 170 
 171         [[ $? -eq 0 ]] || log_fail "get_max_arc_size failed"
 172 
 173         echo $max_arc_size
 174 }
 175 
 176 # Create a file with some information about how this system is configured.
 177 function get_system_config
 178 {
 179         typeset config=$PERF_DATA_DIR/$1
 180 
 181         echo "{" >>$config
 182         $DTRACE -qn 'BEGIN{
 183             printf("  \"ncpus\": %d,\n", `ncpus);
 184             printf("  \"physmem\": %u,\n", `physmem * `_pagesize);
 185             printf("  \"c_max\": %u,\n", `arc_stats.arcstat_c_max.value.ui64);
 186             printf("  \"kmem_flags\": \"0x%x\",", `kmem_flags);
 187             exit(0)}' >>$config
 188         $ECHO "  \"hostname\": \"$($UNAME -n)\"," >>$config
 189         $ECHO "  \"kernel version\": \"$($UNAME -v)\"," >>$config
 190         $IOSTAT -En | $AWK 'BEGIN {
 191             printf("  \"disks\": {\n"); first = 1}
 192             /^c/ {disk = $1}
 193             /^Size: [^0]/ {size = $2;
 194             if (first != 1) {printf(",\n")} else {first = 0}
 195             printf("    \"%s\": \"%s\"", disk, size)}
 196             END {printf("\n  },\n")}' >>$config
 197         $SED -n 's/^set \(.*\)[ ]=[ ]\(.*\)/\1=\2/p' /etc/system | \
 198             $AWK -F= 'BEGIN {printf("  \"system\": {\n"); first = 1}
 199             {if (first != 1) {printf(",\n")} else {first = 0};
 200             printf("    \"%s\": %s", $1, $2)}
 201             END {printf("\n  }\n")}' >>$config
 202         echo "}" >>$config
 203 }
 204 
 205 function num_jobs_by_cpu
 206 {
 207         typeset ncpu=$($PSRINFO | $WC -l)
 208         typeset num_jobs=$ncpu
 209 
 210         [[ $ncpu -gt 8 ]] && num_jobs=$($ECHO "$ncpu * 3 / 4" | $BC)
 211 
 212         $ECHO $num_jobs
 213 }
 214 
 215 function pool_to_lun_list
 216 {
 217         typeset pool=$1
 218         typeset ctd ctds devname lun
 219         typeset lun_list=':'
 220 
 221         ctds=$($ZPOOL list -v $pool | $AWK '/c[0-9]*t[0-9a-fA-F]*d[0-9]*/ \
 222             {print $1}')
 223 
 224         for ctd in $ctds; do
 225                 # Get the device name as it appears in /etc/path_to_inst
 226                 devname=$($READLINK -f /dev/dsk/${ctd}s0 | $SED -n \
 227                     's/\/devices\([^:]*\):.*/\1/p')
 228                 # Add a string composed of the driver name and instance
 229                 # number to the list for comparison with dev_statname.
 230                 lun=$($SED 's/"//g' /etc/path_to_inst | $GREP $devname | $AWK \
 231                     '{print $3$2}')
 232                 lun_list="$lun_list$lun:"
 233         done
 234         echo $lun_list
 235 }
 236 
 237 # Create a perf_data directory to hold performance statistics and
 238 # configuration information.
 239 export PERF_DATA_DIR=$(get_perf_output_dir)
 240 [[ -f $PERF_DATA_DIR/config.json ]] || get_system_config config.json