1 #!/usr/bin/env bash 2 # 3 # Copyright (C) 2013 eNovance SAS <licensing (at] enovance.com> 4 # Author: Erwan Velu <erwan (at] enovance.com> 5 # 6 # The license below covers all files distributed with fio unless otherwise 7 # noted in the file itself. 8 # 9 # This program is free software; you can redistribute it and/or modify 10 # it under the terms of the GNU General Public License version 2 as 11 # published by the Free Software Foundation. 12 # 13 # This program is distributed in the hope that it will be useful, 14 # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 # GNU General Public License for more details. 17 # 18 # You should have received a copy of the GNU General Public License 19 # along with this program; if not, write to the Free Software 20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 22 BLK_SIZE= 23 BLOCK_SIZE=4k 24 SEQ=-1 25 TEMPLATE=/tmp/template.fio 26 OUTFILE= 27 DISKS= 28 PRINTABLE_DISKS= 29 RUNTIME=300 30 ETA=0 31 MODES="write,randwrite,read,randread" 32 SHORT_HOSTNAME= 33 CACHED_IO="FALSE" 34 PREFIX="" 35 PREFIX_FILENAME="" 36 IODEPTH=1 37 38 show_help() { 39 PROG=$(basename $0) 40 echo "usage of $PROG:" 41 cat << EOF 42 -h : Show this help & exit 43 -c : Enable cached-based IOs 44 Disabled by default 45 -a : Run sequential test then parallel one 46 Disabled by default 47 -s : Run sequential test (default value) 48 one test after another then one disk after another 49 Disabled by default 50 -p : Run parallel test 51 one test after anoter but all disks at the same time 52 Enabled by default 53 -D iodepth : Run with the specified iodepth 54 Default is $IODEPTH 55 -d disk1[,disk2,disk3,..] : Run the tests on the selected disks 56 Separated each disk with a comma 57 -z filesize : Specify the working file size, if you are passing filepaths to -d 58 Disabled by default 59 -r seconds : Time in seconds per benchmark 60 0 means till the end of the device 61 Default is $RUNTIME seconds 62 -b blocksize[,blocksize1, ...] : The blocksizes to test under fio format (4k, 1m, ...) 63 Separated each blocksize with a comma 64 Default is $BLOCK_SIZE 65 -m mode1,[mode2,mode3, ...] : Define the fio IO profile to use like read, write, randread, randwrite 66 Default is "$MODES" 67 -x prefix : Add a prefix to the fio filename 68 Useful to let a context associated with the file 69 If the prefix features a / (slash), prefix will be considered as a directory 70 -A cmd_to_run : System command to run after each job (exec_postrun in fio) 71 -B cmd_to_run : System command to run before each job (exec_prerun in fio) 72 73 Example: 74 75 $PROG -d /dev/sdb,/dev/sdc,/dev/sdd,/dev/sde -a -b 4k,128k,1m -r 100 -a -x dellr720-day2/ 76 77 Will generate an fio file that will run 78 - a sequential bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 4k with write,randwrite,read,randread tests 79 ETA ~ 4 tests * 4 disks * 100 seconds 80 - a sequential bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 128k with write,randwrite,read,randread tests 81 ETA ~ 4 tests * 4 disks * 100 seconds 82 - a sequential bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 1m with write,randwrite,read,randread tests 83 ETA ~ 4 tests * 4 disks * 100 seconds 84 - a parallel bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 4k with write,randwrite,read,randread tests 85 ETA ~ 4 tests * 100 seconds 86 - a parallel bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 128k with write,randwrite,read,randread tests 87 ETA ~ 4 tests * 100 seconds 88 - a parallel bench on /dev/sdb /dev/sdc /dev/sdd /dev/sde for block size = 1m with write,randwrite,read,randread tests 89 ETA ~ 4 tests * 100 seconds 90 91 Generating dellr720-day2/localhost-4k,128k,1m-all-write,randwrite,read,randread-sdb,sdc,sdd,sde.fio 92 Estimated Time = 6000 seconds : 1 hour 40 minutes 93 EOF 94 } 95 96 finish_template() { 97 echo "iodepth=$IODEPTH" >> $TEMPLATE 98 99 if [ "$RUNTIME" != "0" ]; then 100 echo "runtime=$RUNTIME" >> $TEMPLATE 101 echo "time_based" >> $TEMPLATE 102 fi 103 104 if [ "$CACHED_IO" = "FALSE" ]; then 105 echo "direct=1" >> $TEMPLATE 106 fi 107 } 108 109 110 diskname_to_printable() { 111 COUNT=0 112 for disk in $(echo $@ | tr "," " "); do 113 R=$(basename $disk | sed 's|/|_|g') 114 COUNT=$(($COUNT + 1)) 115 if [ $COUNT -eq 1 ]; then 116 P="$R" 117 else 118 P="$P,$R" 119 fi 120 done 121 echo $P 122 } 123 124 gen_template() { 125 cat >$TEMPLATE << EOF 126 [global] 127 ioengine=libaio 128 invalidate=1 129 ramp_time=5 130 EOF 131 } 132 133 gen_seq_suite() { 134 TYPE=$1 135 disk=$2 136 PRINTABLE_DISK=$(diskname_to_printable $disk) 137 cat >> $OUTFILE << EOF 138 [$TYPE-$PRINTABLE_DISK-$BLK_SIZE-seq] 139 stonewall 140 bs=$BLK_SIZE 141 filename=$disk 142 rw=$TYPE 143 write_bw_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-seq.results 144 write_iops_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-seq.results 145 EOF 146 ETA=$(($ETA + $RUNTIME)) 147 } 148 149 gen_seq_fio() { 150 for disk in $(echo $DISKS | tr "," " "); do 151 for mode in $(echo $MODES | tr "," " "); do 152 gen_seq_suite "$mode" "$disk" 153 done 154 done 155 } 156 157 158 gen_para_suite() { 159 TYPE=$1 160 NEED_WALL=$2 161 D=0 162 for disk in $(echo $DISKS | tr "," " "); do 163 PRINTABLE_DISK=$(diskname_to_printable $disk) 164 cat >> $OUTFILE << EOF 165 [$TYPE-$PRINTABLE_DISK-$BLK_SIZE-para] 166 bs=$BLK_SIZE 167 EOF 168 169 if [ "$D" = 0 ]; then 170 echo "stonewall" >> $OUTFILE 171 D=1 172 fi 173 174 cat >> $OUTFILE << EOF 175 filename=$disk 176 rw=$TYPE 177 write_bw_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-para.results 178 write_iops_log=${PREFIX_FILENAME}$SHORT_HOSTNAME-$BLK_SIZE-$PRINTABLE_DISK-$TYPE-para.results 179 EOF 180 done 181 182 ETA=$(($ETA + $RUNTIME)) 183 echo >> $OUTFILE 184 } 185 186 gen_para_fio() { 187 for mode in $(echo $MODES | tr "," " "); do 188 gen_para_suite "$mode" 189 done 190 } 191 192 gen_fio() { 193 case $SEQ in 194 2) 195 gen_seq_fio 196 gen_para_fio 197 ;; 198 1) 199 gen_seq_fio 200 ;; 201 0) 202 gen_para_fio 203 ;; 204 esac 205 } 206 207 parse_cmdline() { 208 while getopts "hacpsd:b:r:m:x:z:D:A:B:" opt; do 209 case $opt in 210 h) 211 show_help 212 exit 0 213 ;; 214 b) 215 BLOCK_SIZE=$OPTARG 216 ;; 217 c) 218 CACHED_IO="TRUE" 219 ;; 220 s) 221 if [ "$SEQ" = "-1" ]; then 222 SEQ=1 223 fi 224 ;; 225 x) 226 PREFIX=$OPTARG 227 echo "$PREFIX" | grep -q "/" 228 if [ "$?" -eq 0 ]; then 229 mkdir -p $PREFIX 230 # No need to keep the prefix for the log files 231 # we do have a directory for that 232 PREFIX_FILENAME="" 233 else 234 # We need to keep the prefix for the log files 235 PREFIX_FILENAME=$PREFIX 236 fi 237 ;; 238 r) 239 RUNTIME=$OPTARG 240 ;; 241 p) 242 if [ "$SEQ" = "-1" ]; then 243 SEQ=0 244 fi 245 ;; 246 m) 247 MODES=$OPTARG; 248 ;; 249 d) 250 DISKS=$OPTARG 251 PRINTABLE_DISKS=$(diskname_to_printable "$DISKS") 252 ;; 253 D) 254 IODEPTH=$OPTARG 255 ;; 256 a) 257 SEQ=2 258 ;; 259 B) 260 echo "exec_prerun=$OPTARG" >> $TEMPLATE 261 ;; 262 A) 263 echo "exec_postrun=$OPTARG" >> $TEMPLATE 264 ;; 265 z) 266 FSIZE=$OPTARG 267 echo "size=$FSIZE" >> $TEMPLATE 268 ;; 269 \?) 270 echo "Invalid option: -$OPTARG" >&2 271 ;; 272 esac 273 done 274 275 if [ "$SEQ" = "-1" ]; then 276 SEQ=0 277 fi 278 279 SHORT_HOSTNAME=$(hostname -s) 280 case $SEQ in 281 2) 282 OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-all-$MODES-$PRINTABLE_DISKS.fio 283 ;; 284 285 1) 286 OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-sequential-$MODES-$PRINTABLE_DISKS.fio 287 ;; 288 0) 289 OUTFILE=${PREFIX}$SHORT_HOSTNAME-$BLOCK_SIZE-parallel-$MODES-$PRINTABLE_DISKS.fio 290 ;; 291 esac 292 293 if [ -z "$DISKS" ]; then 294 echo "Missing DISKS !" 295 echo "Please read the help !" 296 show_help 297 exit 1 298 fi 299 300 } 301 302 check_mode_order() { 303 FOUND_WRITE="NO" 304 CAUSE="You are reading data before writing them " 305 306 # If no write occurs, let's show a different message 307 echo $MODES | grep -q "write" 308 if [ "$?" -ne 0 ]; then 309 CAUSE="You are reading data while never wrote them before" 310 fi 311 312 for mode in $(echo $MODES | tr "," " "); do 313 echo $mode | grep -q write 314 if [ "$?" -eq 0 ]; then 315 FOUND_WRITE="YES" 316 fi 317 echo $mode | grep -q "read" 318 if [ "$?" -eq 0 ]; then 319 if [ "$FOUND_WRITE" = "NO" ]; then 320 echo "###############################################################" 321 echo "# Warning : $CAUSE#" 322 echo "# On some storage devices, this could lead to invalid results #" 323 echo "# #" 324 echo "# Press Ctrl-C to adjust pattern order if you have doubts #" 325 echo "# Or Wait 5 seconds before the file will be created #" 326 echo "###############################################################" 327 sleep 5 328 # No need to try showing the message more than one time 329 return 330 fi 331 fi 332 done 333 } 334 335 336 ########## MAIN 337 gen_template 338 parse_cmdline "$@" 339 finish_template 340 check_mode_order 341 342 echo "Generating $OUTFILE" 343 cp -f $TEMPLATE $OUTFILE 344 echo >> $OUTFILE 345 346 for BLK_SIZE in $(echo $BLOCK_SIZE | tr "," " "); do 347 gen_fio 348 done 349 ETA_H=$(($ETA / 3600)) 350 ETA_M=$((($ETA - ($ETA_H*3600)) / 60)) 351 if [ "$ETA" = "0" ]; then 352 echo "Cannot estimate ETA as RUNTIME=0" 353 else 354 echo "Estimated Time = $ETA seconds : $ETA_H hour $ETA_M minutes" 355 fi 356