--- /dev/null
+#!/usr/bin/python2.7
+#
+# sgunmap-test.py
+#
+# Basic performance testing using fio's sg ioengine
+#
+# USAGE
+# sgunmap-perf.py char-device block-device fio-executable
+#
+# EXAMPLE
+# t/sgunmap-perf.py /dev/sg1 /dev/sdb ./fio
+#
+# REQUIREMENTS
+# Python 2.6+
+#
+#
+
+from __future__ import absolute_import
+from __future__ import print_function
+import sys
+import json
+import argparse
+import subprocess
+from six.moves import range
+
+
+def parse_args():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('cdev',
+ help='character device target (e.g., /dev/sg0)')
+ parser.add_argument('bdev',
+ help='block device target (e.g., /dev/sda)')
+ parser.add_argument('fioc',
+ help='path to candidate fio executable (e.g., ./fio)')
+ parser.add_argument('fior',
+ help='path to reference fio executable (e.g., ./fio)')
+ args = parser.parse_args()
+
+ return args
+
+
+def fulldevice(fio, dev, ioengine='psync', rw='trim', bs='1M'):
+ parameters = ["--name=test",
+ "--output-format=json",
+ "--random_generator=lfsr",
+ "--bs={0}".format(bs),
+ "--rw={0}".format(rw),
+ "--ioengine={0}".format(ioengine),
+ "--filename={0}".format(dev)]
+
+ output = subprocess.check_output([fio] + parameters)
+ jsondata = json.loads(output)
+ jobdata = jsondata['jobs'][0]
+ return jobdata
+
+
+def runtest(fio, dev, rw, qd, batch, bs='512', runtime='30s'):
+ parameters = ["--name=test",
+ "--random_generator=tausworthe64",
+ "--time_based",
+ "--runtime={0}".format(runtime),
+ "--output-format=json",
+ "--ioengine=sg",
+ "--blocksize={0}".format(bs),
+ "--rw={0}".format(rw),
+ "--filename={0}".format(dev),
+ "--iodepth={0}".format(qd),
+ "--iodepth_batch={0}".format(batch)]
+
+ output = subprocess.check_output([fio] + parameters)
+ jsondata = json.loads(output)
+ jobdata = jsondata['jobs'][0]
+# print(parameters)
+
+ return jobdata
+
+
+def runtests(fio, dev, qd, batch, rw, bs='512', trials=5):
+ iops = []
+ for x in range(trials):
+ jd = runtest(fio, dev, rw, qd, batch, bs=bs)
+ total = jd['read']['iops'] + jd['write']['iops'] + jd['trim']['iops']
+# print(total)
+ iops.extend([total])
+ return iops, (sum(iops) / trials)
+
+if __name__ == '__main__':
+ args = parse_args()
+
+ print("Trimming full device {0}".format(args.cdev))
+ fulldevice(args.fior, args.cdev, ioengine='sg')
+
+ print("Running rand read tests on {0}"
+ " with fio candidate build {1}".format(args.cdev, args.fioc))
+ randread, rrmean = runtests(args.fioc, args.cdev, 16, 1, 'randread',
+ trials=5)
+ print("IOPS mean {0}, trials {1}".format(rrmean, randread))
+
+ print("Running rand read tests on {0}"
+ " with fio reference build {1}".format(args.cdev, args.fior))
+ randread, rrmean = runtests(args.fior, args.cdev, 16, 1, 'randread',
+ trials=5)
+ print("IOPS mean {0}, trials {1}".format(rrmean, randread))
+
+ print("Running rand write tests on {0}"
+ " with fio candidate build {1}".format(args.cdev, args.fioc))
+ randwrite, rwmean = runtests(args.fioc, args.cdev, 16, 1, 'randwrite',
+ trials=5)
+ print("IOPS mean {0}, trials {1}".format(rwmean, randwrite))
+
+ print("Running rand write tests on {0}"
+ " with fio reference build {1}".format(args.cdev, args.fior))
+ randwrite, rwmean = runtests(args.fior, args.cdev, 16, 1, 'randwrite',
+ trials=5)
+ print("IOPS mean {0}, trials {1}".format(rwmean, randwrite))
--- /dev/null
+#!/usr/bin/python2.7
+# Note: this script is python2 and python 3 compatible.
+#
+# sgunmap-test.py
+#
+# Limited functonality test for trim workloads using fio's sg ioengine
+# This checks only the three sets of reported iodepths
+#
+# !!!WARNING!!!
+# This script carries out destructive tests. Be sure that
+# there is no data you want to keep on the supplied devices.
+#
+# USAGE
+# sgunmap-test.py char-device block-device fio-executable
+#
+# EXAMPLE
+# t/sgunmap-test.py /dev/sg1 /dev/sdb ./fio
+#
+# REQUIREMENTS
+# Python 2.6+
+#
+# TEST MATRIX
+# For both char-dev and block-dev these are the expected
+# submit/complete IO depths
+#
+# blockdev chardev
+# iodepth iodepth
+# R QD1 sub/comp: 1-4=100% sub/comp: 1-4=100%
+# W QD1 sub/comp: 1-4=100% sub/comp: 1-4=100%
+# T QD1 sub/comp: 1-4=100% sub/comp: 1-4=100%
+#
+# R QD16, batch8 sub/comp: 1-4=100% sub/comp: 1-4=100%
+# W QD16, batch8 sub/comp: 1-4=100% sub/comp: 1-4=100%
+# T QD16, batch8 sub/comp: 1-4=100% sub/comp: 5-8=100%
+#
+# R QD16, batch16 sub/comp: 1-4=100% sub/comp: 1-4=100%
+# W QD16, batch16 sub/comp: 1-4=100% sub/comp: 1-4=100%
+# T QD16, batch16 sub/comp: 1-4=100% sub/comp: 9-16=100%
+#
+
+from __future__ import absolute_import
+from __future__ import print_function
+import sys
+import json
+import argparse
+import traceback
+import subprocess
+from six.moves import range
+
+
+def parse_args():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('chardev',
+ help='character device target (e.g., /dev/sg0)')
+ parser.add_argument('blockdev',
+ help='block device target (e.g., /dev/sda)')
+ parser.add_argument('fio',
+ help='path to fio executable (e.g., ./fio)')
+ args = parser.parse_args()
+
+ return args
+
+#
+# With block devices,
+# iodepth = 1 always
+# submit = complete = 1-4 always
+# With character devices,
+# RW
+# iodepth = qd
+# submit = 1-4
+# complete = 1-4 except for the IOs in flight
+# when the job is ending
+# T
+# iodepth = qd
+# submit = qdbatch
+# complete = qdbatch except for the IOs in flight
+# when the job is ending
+#
+
+
+def check(jsondata, parameters, block, qd, qdbatch, rw):
+ iodepth = jsondata['iodepth_level']
+ submit = jsondata['iodepth_submit']
+ complete = jsondata['iodepth_complete']
+
+ try:
+ if block:
+ assert iodepth['1'] == 100.0
+ assert submit['4'] == 100.0
+ assert complete['4'] == 100.0
+ elif 'read' in rw or 'write' in rw:
+ assert iodepth[str(qd)] > 99.9
+ assert submit['4'] == 100.0
+ assert complete['4'] > 99.9
+ else:
+ if qdbatch <= 4:
+ batchkey = '4'
+ elif qdbatch > 64:
+ batchkey = '>=64'
+ else:
+ batchkey = str(qdbatch)
+ if qd >= 64:
+ qdkey = ">=64"
+ else:
+ qdkey = str(qd)
+ assert iodepth[qdkey] > 99
+ assert submit[batchkey] == 100.0
+ assert complete[batchkey] > 99
+ except AssertionError:
+ print("Assertion failed")
+ traceback.print_exc()
+ print(jsondata)
+ return
+
+ print("**********passed*********")
+
+
+def runalltests(args, qd, batch):
+ block = False
+ for dev in [args.chardev, args.blockdev]:
+ for rw in ["randread", "randwrite", "randtrim"]:
+ parameters = ["--name=test",
+ "--time_based",
+ "--runtime=30s",
+ "--output-format=json",
+ "--ioengine=sg",
+ "--rw={0}".format(rw),
+ "--filename={0}".format(dev),
+ "--iodepth={0}".format(qd),
+ "--iodepth_batch={0}".format(batch)]
+
+ print(parameters)
+ output = subprocess.check_output([args.fio] + parameters)
+ jsondata = json.loads(output)
+ jobdata = jsondata['jobs'][0]
+ check(jobdata, parameters, block, qd, batch, rw)
+ block = True
+
+
+def runcdevtrimtest(args, qd, batch):
+ parameters = ["--name=test",
+ "--time_based",
+ "--runtime=30s",
+ "--output-format=json",
+ "--ioengine=sg",
+ "--rw=randtrim",
+ "--filename={0}".format(args.chardev),
+ "--iodepth={0}".format(qd),
+ "--iodepth_batch={0}".format(batch)]
+
+ print(parameters)
+ output = subprocess.check_output([args.fio] + parameters)
+ jsondata = json.loads(output)
+ jobdata = jsondata['jobs'][0]
+ check(jobdata, parameters, False, qd, batch, "randtrim")
+
+
+if __name__ == '__main__':
+ args = parse_args()
+
+ runcdevtrimtest(args, 32, 2)
+ runcdevtrimtest(args, 32, 4)
+ runcdevtrimtest(args, 32, 8)
+ runcdevtrimtest(args, 64, 4)
+ runcdevtrimtest(args, 64, 8)
+ runcdevtrimtest(args, 64, 16)
+ runcdevtrimtest(args, 128, 8)
+ runcdevtrimtest(args, 128, 16)
+ runcdevtrimtest(args, 128, 32)
+
+ runalltests(args, 1, 1)
+ runalltests(args, 16, 2)
+ runalltests(args, 16, 16)