blob: 45e6f148e19c242c7dc986f997ed64febdcbf105 [file] [log] [blame]
#!/usr/bin/env python3
#
# strided.py
#
# Test zonemode=strided. This uses the null ioengine when no file is
# specified. If a file is specified, use it for randdom read testing.
# Some of the zoneranges in the tests are 16MiB. So when using a file
# a minimum size of 32MiB is recommended.
#
# USAGE
# python strided.py fio-executable [-f file/device]
#
# EXAMPLES
# python t/strided.py ./fio
# python t/strided.py ./fio -f /dev/sda
# dd if=/dev/zero of=temp bs=1M count=32
# python t/strided.py ./fio -f temp
#
# REQUIREMENTS
# Python 2.6+
#
# ===TEST MATRIX===
#
# --zonemode=strided, zoneskip unset
# w/ randommap and LFSR
# zonesize=zonerange all blocks in zonerange touched
# zonesize>zonerange all blocks touched and roll-over back into zone
# zonesize<zonerange all blocks inside zone
#
# w/o randommap all blocks inside zone
#
from __future__ import absolute_import
from __future__ import print_function
import os
import sys
import argparse
import subprocess
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('fio',
help='path to fio executable (e.g., ./fio)')
parser.add_argument('-f', '--filename', help="file/device to test")
args = parser.parse_args()
return args
def run_fio(fio, test, index):
filename = "strided"
fio_args = [
"--max-jobs=16",
"--name=strided",
"--zonemode=strided",
"--log_offset=1",
"--randrepeat=0",
"--rw=randread",
"--write_iops_log={0}{1:03d}".format(filename, index),
"--output={0}{1:03d}.out".format(filename, index),
"--zonerange={zonerange}".format(**test),
"--zonesize={zonesize}".format(**test),
"--bs={bs}".format(**test),
]
if 'norandommap' in test:
fio_args.append('--norandommap')
if 'random_generator' in test:
fio_args.append('--random_generator={random_generator}'.format(**test))
if 'offset' in test:
fio_args.append('--offset={offset}'.format(**test))
if 'filename' in test:
fio_args.append('--filename={filename}'.format(**test))
fio_args.append('--filesize={filesize})'.format(**test))
else:
fio_args.append('--ioengine=null')
fio_args.append('--size={size}'.format(**test))
fio_args.append('--io_size={io_size}'.format(**test))
fio_args.append('--filesize={size})'.format(**test))
output = subprocess.check_output([fio] + fio_args, universal_newlines=True)
f = open("{0}{1:03d}_iops.1.log".format(filename, index), "r")
log = f.read()
f.close()
return log
def check_output(iops_log, test):
zonestart = 0 if 'offset' not in test else test['offset']
iospersize = test['zonesize'] / test['bs']
iosperrange = test['zonerange'] / test['bs']
iosperzone = 0
lines = iops_log.split('\n')
zoneset = set()
for line in lines:
if len(line) == 0:
continue
if iosperzone == iospersize:
# time to move to a new zone
iosperzone = 0
zoneset = set()
zonestart += test['zonerange']
if zonestart >= test['filesize']:
zonestart = 0 if 'offset' not in test else test['offset']
iosperzone = iosperzone + 1
tokens = line.split(',')
offset = int(tokens[4])
if offset < zonestart or offset >= zonestart + test['zonerange']:
print("Offset {0} outside of zone starting at {1}".format(
offset, zonestart))
return False
# skip next section if norandommap is enabled with no
# random_generator or with a random_generator != lfsr
if 'norandommap' in test:
if 'random_generator' in test:
if test['random_generator'] != 'lfsr':
continue
else:
continue
# we either have a random map enabled or we
# are using an LFSR
# so all blocks should be unique and we should have
# covered the entire zone when iosperzone % iosperrange == 0
block = (offset - zonestart) / test['bs']
if block in zoneset:
print("Offset {0} in zone already touched".format(offset))
return False
zoneset.add(block)
if iosperzone % iosperrange == 0:
if len(zoneset) != iosperrange:
print("Expected {0} blocks in zone but only saw {1}".format(
iosperrange, len(zoneset)))
return False
zoneset = set()
return True
if __name__ == '__main__':
args = parse_args()
tests = [ # randommap enabled
{
"zonerange": 4096,
"zonesize": 4096,
"bs": 4096,
"offset": 8*4096,
"size": 16*4096,
"io_size": 16*4096,
},
{
"zonerange": 4096,
"zonesize": 4096,
"bs": 4096,
"size": 16*4096,
"io_size": 16*4096,
},
{
"zonerange": 16*1024*1024,
"zonesize": 16*1024*1024,
"bs": 4096,
"size": 256*1024*1024,
"io_size": 256*1024*204,
},
{
"zonerange": 4096,
"zonesize": 4*4096,
"bs": 4096,
"size": 16*4096,
"io_size": 16*4096,
},
{
"zonerange": 16*1024*1024,
"zonesize": 32*1024*1024,
"bs": 4096,
"size": 256*1024*1024,
"io_size": 256*1024*204,
},
{
"zonerange": 8192,
"zonesize": 4096,
"bs": 4096,
"size": 16*4096,
"io_size": 16*4096,
},
{
"zonerange": 16*1024*1024,
"zonesize": 8*1024*1024,
"bs": 4096,
"size": 256*1024*1024,
"io_size": 256*1024*204,
},
# lfsr
{
"random_generator": "lfsr",
"zonerange": 4096*1024,
"zonesize": 4096*1024,
"bs": 4096,
"offset": 8*4096*1024,
"size": 16*4096*1024,
"io_size": 16*4096*1024,
},
{
"random_generator": "lfsr",
"zonerange": 4096*1024,
"zonesize": 4096*1024,
"bs": 4096,
"size": 16*4096*1024,
"io_size": 16*4096*1024,
},
{
"random_generator": "lfsr",
"zonerange": 16*1024*1024,
"zonesize": 16*1024*1024,
"bs": 4096,
"size": 256*1024*1024,
"io_size": 256*1024*204,
},
{
"random_generator": "lfsr",
"zonerange": 4096*1024,
"zonesize": 4*4096*1024,
"bs": 4096,
"size": 16*4096*1024,
"io_size": 16*4096*1024,
},
{
"random_generator": "lfsr",
"zonerange": 16*1024*1024,
"zonesize": 32*1024*1024,
"bs": 4096,
"size": 256*1024*1024,
"io_size": 256*1024*204,
},
{
"random_generator": "lfsr",
"zonerange": 8192*1024,
"zonesize": 4096*1024,
"bs": 4096,
"size": 16*4096*1024,
"io_size": 16*4096*1024,
},
{
"random_generator": "lfsr",
"zonerange": 16*1024*1024,
"zonesize": 8*1024*1024,
"bs": 4096,
"size": 256*1024*1024,
"io_size": 256*1024*204,
},
# norandommap
{
"norandommap": 1,
"zonerange": 4096,
"zonesize": 4096,
"bs": 4096,
"offset": 8*4096,
"size": 16*4096,
"io_size": 16*4096,
},
{
"norandommap": 1,
"zonerange": 4096,
"zonesize": 4096,
"bs": 4096,
"size": 16*4096,
"io_size": 16*4096,
},
{
"norandommap": 1,
"zonerange": 16*1024*1024,
"zonesize": 16*1024*1024,
"bs": 4096,
"size": 256*1024*1024,
"io_size": 256*1024*204,
},
{
"norandommap": 1,
"zonerange": 4096,
"zonesize": 8192,
"bs": 4096,
"size": 16*4096,
"io_size": 16*4096,
},
{
"norandommap": 1,
"zonerange": 16*1024*1024,
"zonesize": 32*1024*1024,
"bs": 4096,
"size": 256*1024*1024,
"io_size": 256*1024*204,
},
{
"norandommap": 1,
"zonerange": 8192,
"zonesize": 4096,
"bs": 4096,
"size": 16*4096,
"io_size": 16*4096,
},
{
"norandommap": 1,
"zonerange": 16*1024*1024,
"zonesize": 8*1024*1024,
"bs": 4096,
"size": 256*1024*1024,
"io_size": 256*1024*1024,
},
]
index = 1
passed = 0
failed = 0
if args.filename:
statinfo = os.stat(args.filename)
filesize = statinfo.st_size
if filesize == 0:
f = os.open(args.filename, os.O_RDONLY)
filesize = os.lseek(f, 0, os.SEEK_END)
os.close(f)
for test in tests:
if args.filename:
test['filename'] = args.filename
test['filesize'] = filesize
else:
test['filesize'] = test['size']
iops_log = run_fio(args.fio, test, index)
status = check_output(iops_log, test)
print("Test {0} {1}".format(index, ("PASSED" if status else "FAILED")))
if status:
passed = passed + 1
else:
failed = failed + 1
index = index + 1
print("{0} tests passed, {1} failed".format(passed, failed))
sys.exit(failed)