X-Git-Url: https://git.kernel.dk/?a=blobdiff_plain;f=t%2Frun-fio-tests.py;h=d7baa139977f386bbf71b7d076418bc6bceac298;hb=114eadb2d1df6a86afdcc42a21310d8c5559e72d;hp=2bd02a2a31f84aec122bc81af48110570878d35d;hpb=31a58cbacdbbac5783ee0255de3532ff5294480b;p=fio.git diff --git a/t/run-fio-tests.py b/t/run-fio-tests.py index 2bd02a2a..d7baa139 100755 --- a/t/run-fio-tests.py +++ b/t/run-fio-tests.py @@ -55,7 +55,7 @@ import multiprocessing from pathlib import Path -class FioTest(object): +class FioTest(): """Base for all fio tests.""" def __init__(self, exe_path, parameters, success): @@ -427,8 +427,10 @@ class FioJobTest_t0012(FioJobTest): return iops_files = [] - for i in range(1,4): - file_data, success = self.get_file(os.path.join(self.test_dir, "{0}_iops.{1}.log".format(os.path.basename(self.fio_job), i))) + for i in range(1, 4): + filename = os.path.join(self.test_dir, "{0}_iops.{1}.log".format(os.path.basename( + self.fio_job), i)) + file_data, success = self.get_file(filename) if not success: self.failure_reason = "{0} unable to open output file,".format(self.failure_reason) @@ -448,17 +450,15 @@ class FioJobTest_t0012(FioJobTest): ratio1 = iops3/iops2 ratio2 = iops3/iops1 - logging.debug( - "sample {0}: job1 iops={1} job2 iops={2} job3 iops={3} job3/job2={4:.3f} job3/job1={5:.3f}".format( - i, iops1, iops2, iops3, ratio1, ratio2 - ) - ) + logging.debug("sample {0}: job1 iops={1} job2 iops={2} job3 iops={3} " \ + "job3/job2={4:.3f} job3/job1={5:.3f}".format(i, iops1, iops2, iops3, ratio1, + ratio2)) # test job1 and job2 succeeded to recalibrate if ratio1 < 1 or ratio1 > 3 or ratio2 < 7 or ratio2 > 13: - self.failure_reason = "{0} iops ratio mismatch iops1={1} iops2={2} iops3={3} expected r1~2 r2~10 got r1={4:.3f} r2={5:.3f},".format( - self.failure_reason, iops1, iops2, iops3, ratio1, ratio2 - ) + self.failure_reason += " iops ratio mismatch iops1={0} iops2={1} iops3={2} " \ + "expected r1~2 r2~10 got r1={3:.3f} r2={4:.3f},".format(iops1, iops2, iops3, + ratio1, ratio2) self.passed = False return @@ -478,8 +478,10 @@ class FioJobTest_t0014(FioJobTest): return iops_files = [] - for i in range(1,4): - file_data, success = self.get_file(os.path.join(self.test_dir, "{0}_iops.{1}.log".format(os.path.basename(self.fio_job), i))) + for i in range(1, 4): + filename = os.path.join(self.test_dir, "{0}_iops.{1}.log".format(os.path.basename( + self.fio_job), i)) + file_data, success = self.get_file(filename) if not success: self.failure_reason = "{0} unable to open output file,".format(self.failure_reason) @@ -501,10 +503,9 @@ class FioJobTest_t0014(FioJobTest): if ratio1 < 0.43 or ratio1 > 0.57 or ratio2 < 0.21 or ratio2 > 0.45: - self.failure_reason = "{0} iops ratio mismatch iops1={1} iops2={2} iops3={3}\ - expected r1~0.5 r2~0.33 got r1={4:.3f} r2={5:.3f},".format( - self.failure_reason, iops1, iops2, iops3, ratio1, ratio2 - ) + self.failure_reason += " iops ratio mismatch iops1={0} iops2={1} iops3={2} " \ + "expected r1~0.5 r2~0.33 got r1={3:.3f} r2={4:.3f},".format( + iops1, iops2, iops3, ratio1, ratio2) self.passed = False iops1 = iops1 + float(iops_files[0][i].split(',')[1]) @@ -512,17 +513,14 @@ class FioJobTest_t0014(FioJobTest): ratio1 = iops1/iops2 ratio2 = iops1/iops3 - logging.debug( - "sample {0}: job1 iops={1} job2 iops={2} job3 iops={3} job1/job2={4:.3f} job1/job3={5:.3f}".format( - i, iops1, iops2, iops3, ratio1, ratio2 - ) - ) + logging.debug("sample {0}: job1 iops={1} job2 iops={2} job3 iops={3} " \ + "job1/job2={4:.3f} job1/job3={5:.3f}".format(i, iops1, iops2, iops3, + ratio1, ratio2)) # test job1 and job2 succeeded to recalibrate if ratio1 < 0.43 or ratio1 > 0.57: - self.failure_reason = "{0} iops ratio mismatch iops1={1} iops2={2} expected ratio~0.5 got ratio={3:.3f},".format( - self.failure_reason, iops1, iops2, ratio1 - ) + self.failure_reason += " iops ratio mismatch iops1={0} iops2={1} expected ratio~0.5 " \ + "got ratio={2:.3f},".format(iops1, iops2, ratio1) self.passed = False return @@ -548,6 +546,253 @@ class FioJobTest_t0015(FioJobTest): self.passed = False +class FioJobTest_t0019(FioJobTest): + """Test consists of fio test job t0019 + Confirm that all offsets were touched sequentially""" + + def check_result(self): + super(FioJobTest_t0019, self).check_result() + + bw_log_filename = os.path.join(self.test_dir, "test_bw.log") + file_data, success = self.get_file(bw_log_filename) + if not success: + self.failure_reason += " unable to open output file {0}".format(bw_log_filename) + self.passed = False + return + + log_lines = file_data.split('\n') + + prev = -4096 + for line in log_lines: + if len(line.strip()) == 0: + continue + cur = int(line.split(',')[4]) + if cur - prev != 4096: + self.passed = False + self.failure_reason = "offsets {0}, {1} not sequential".format(prev, cur) + return + prev = cur + + if cur/4096 != 255: + self.passed = False + self.failure_reason = "unexpected last offset {0}".format(cur) + + +class FioJobTest_t0020(FioJobTest): + """Test consists of fio test jobs t0020 and t0021 + Confirm that almost all offsets were touched non-sequentially""" + + def check_result(self): + super(FioJobTest_t0020, self).check_result() + + bw_log_filename = os.path.join(self.test_dir, "test_bw.log") + file_data, success = self.get_file(bw_log_filename) + if not success: + self.failure_reason += " unable to open output file {0}".format(bw_log_filename) + self.passed = False + return + + log_lines = file_data.split('\n') + + seq_count = 0 + offsets = set() + + prev = int(log_lines[0].split(',')[4]) + for line in log_lines[1:]: + offsets.add(prev/4096) + if len(line.strip()) == 0: + continue + cur = int(line.split(',')[4]) + if cur - prev == 4096: + seq_count += 1 + prev = cur + + # 10 is an arbitrary threshold + if seq_count > 10: + self.passed = False + self.failure_reason = "too many ({0}) consecutive offsets".format(seq_count) + + if len(offsets) != 256: + self.passed = False + self.failure_reason += " number of offsets is {0} instead of 256".format(len(offsets)) + + for i in range(256): + if not i in offsets: + self.passed = False + self.failure_reason += " missing offset {0}".format(i*4096) + + +class FioJobTest_t0022(FioJobTest): + """Test consists of fio test job t0022""" + + def check_result(self): + super(FioJobTest_t0022, self).check_result() + + bw_log_filename = os.path.join(self.test_dir, "test_bw.log") + file_data, success = self.get_file(bw_log_filename) + if not success: + self.failure_reason += " unable to open output file {0}".format(bw_log_filename) + self.passed = False + return + + log_lines = file_data.split('\n') + + filesize = 1024*1024 + bs = 4096 + seq_count = 0 + offsets = set() + + prev = int(log_lines[0].split(',')[4]) + for line in log_lines[1:]: + offsets.add(prev/bs) + if len(line.strip()) == 0: + continue + cur = int(line.split(',')[4]) + if cur - prev == bs: + seq_count += 1 + prev = cur + + # 10 is an arbitrary threshold + if seq_count > 10: + self.passed = False + self.failure_reason = "too many ({0}) consecutive offsets".format(seq_count) + + if len(offsets) == filesize/bs: + self.passed = False + self.failure_reason += " no duplicate offsets found with norandommap=1" + + +class FioJobTest_t0023(FioJobTest): + """Test consists of fio test job t0023 randtrimwrite test.""" + + def check_trimwrite(self, filename): + """Make sure that trims are followed by writes of the same size at the same offset.""" + + bw_log_filename = os.path.join(self.test_dir, filename) + file_data, success = self.get_file(bw_log_filename) + if not success: + self.failure_reason += " unable to open output file {0}".format(bw_log_filename) + self.passed = False + return + + log_lines = file_data.split('\n') + + prev_ddir = 1 + for line in log_lines: + if len(line.strip()) == 0: + continue + vals = line.split(',') + ddir = int(vals[2]) + bs = int(vals[3]) + offset = int(vals[4]) + if prev_ddir == 1: + if ddir != 2: + self.passed = False + self.failure_reason += " {0}: write not preceeded by trim: {1}".format( + bw_log_filename, line) + break + else: + if ddir != 1: + self.passed = False + self.failure_reason += " {0}: trim not preceeded by write: {1}".format( + bw_log_filename, line) + break + else: + if prev_bs != bs: + self.passed = False + self.failure_reason += " {0}: block size does not match: {1}".format( + bw_log_filename, line) + break + if prev_offset != offset: + self.passed = False + self.failure_reason += " {0}: offset does not match: {1}".format( + bw_log_filename, line) + break + prev_ddir = ddir + prev_bs = bs + prev_offset = offset + + + def check_all_offsets(self, filename, sectorsize, filesize): + """Make sure all offsets were touched.""" + + file_data, success = self.get_file(os.path.join(self.test_dir, filename)) + if not success: + self.passed = False + self.failure_reason = " could not open {0}".format(filename) + return + + log_lines = file_data.split('\n') + + offsets = set() + + for line in log_lines: + if len(line.strip()) == 0: + continue + vals = line.split(',') + bs = int(vals[3]) + offset = int(vals[4]) + if offset % sectorsize != 0: + self.passed = False + self.failure_reason += " {0}: offset {1} not a multiple of sector size {2}".format( + filename, offset, sectorsize) + break + if bs % sectorsize != 0: + self.passed = False + self.failure_reason += " {0}: block size {1} not a multiple of sector size " \ + "{2}".format(filename, bs, sectorsize) + break + for i in range(int(bs/sectorsize)): + offsets.add(offset/sectorsize + i) + + if len(offsets) != filesize/sectorsize: + self.passed = False + self.failure_reason += " {0}: only {1} offsets touched; expected {2}".format( + filename, len(offsets), filesize/sectorsize) + else: + logging.debug("%s: %d sectors touched", filename, len(offsets)) + + + def check_result(self): + super(FioJobTest_t0023, self).check_result() + + filesize = 1024*1024 + + self.check_trimwrite("basic_bw.log") + self.check_trimwrite("bs_bw.log") + self.check_trimwrite("bsrange_bw.log") + self.check_trimwrite("bssplit_bw.log") + self.check_trimwrite("basic_no_rm_bw.log") + self.check_trimwrite("bs_no_rm_bw.log") + self.check_trimwrite("bsrange_no_rm_bw.log") + self.check_trimwrite("bssplit_no_rm_bw.log") + + self.check_all_offsets("basic_bw.log", 4096, filesize) + self.check_all_offsets("bs_bw.log", 8192, filesize) + self.check_all_offsets("bsrange_bw.log", 512, filesize) + self.check_all_offsets("bssplit_bw.log", 512, filesize) + + +class FioJobTest_t0024(FioJobTest_t0023): + """Test consists of fio test job t0024 trimwrite test.""" + + def check_result(self): + # call FioJobTest_t0023's parent to skip checks done by t0023 + super(FioJobTest_t0023, self).check_result() + + filesize = 1024*1024 + + self.check_trimwrite("basic_bw.log") + self.check_trimwrite("bs_bw.log") + self.check_trimwrite("bsrange_bw.log") + self.check_trimwrite("bssplit_bw.log") + + self.check_all_offsets("basic_bw.log", 4096, filesize) + self.check_all_offsets("bs_bw.log", 8192, filesize) + self.check_all_offsets("bsrange_bw.log", 512, filesize) + self.check_all_offsets("bssplit_bw.log", 512, filesize) + + class FioJobTest_iops_rate(FioJobTest): """Test consists of fio test job t0009 Confirm that job0 iops == 1000 @@ -576,12 +821,13 @@ class FioJobTest_iops_rate(FioJobTest): self.passed = False -class Requirements(object): +class Requirements(): """Requirements consists of multiple run environment characteristics. These are to determine if a particular test can be run""" _linux = False _libaio = False + _io_uring = False _zbd = False _root = False _zoned_nullb = False @@ -605,6 +851,12 @@ class Requirements(object): Requirements._zbd = "CONFIG_HAS_BLKZONED" in contents Requirements._libaio = "CONFIG_LIBAIO" in contents + contents, success = FioJobTest.get_file("/proc/kallsyms") + if not success: + print("Unable to open '/proc/kallsyms' to probe for io_uring support") + else: + Requirements._io_uring = "io_uring_setup" in contents + Requirements._root = (os.geteuid() == 0) if Requirements._zbd and Requirements._root: try: @@ -627,6 +879,7 @@ class Requirements(object): req_list = [Requirements.linux, Requirements.libaio, + Requirements.io_uring, Requirements.zbd, Requirements.root, Requirements.zoned_nullb, @@ -648,6 +901,11 @@ class Requirements(object): """Is libaio available?""" return Requirements._libaio, "libaio required" + @classmethod + def io_uring(cls): + """Is io_uring available?""" + return Requirements._io_uring, "io_uring required" + @classmethod def zbd(cls): """Is ZBD support available?""" @@ -850,7 +1108,7 @@ TEST_LIST = [ { 'test_id': 16, 'test_class': FioJobTest_t0015, - 'job': 't0016-259ebc00.fio', + 'job': 't0016-d54ae22.fio', 'success': SUCCESS_DEFAULT, 'pre_job': None, 'pre_success': None, @@ -867,6 +1125,69 @@ TEST_LIST = [ 'output_format': 'json', 'requirements': [Requirements.not_windows], }, + { + 'test_id': 18, + 'test_class': FioJobTest, + 'job': 't0018.fio', + 'success': SUCCESS_DEFAULT, + 'pre_job': None, + 'pre_success': None, + 'requirements': [Requirements.linux, Requirements.io_uring], + }, + { + 'test_id': 19, + 'test_class': FioJobTest_t0019, + 'job': 't0019.fio', + 'success': SUCCESS_DEFAULT, + 'pre_job': None, + 'pre_success': None, + 'requirements': [], + }, + { + 'test_id': 20, + 'test_class': FioJobTest_t0020, + 'job': 't0020.fio', + 'success': SUCCESS_DEFAULT, + 'pre_job': None, + 'pre_success': None, + 'requirements': [], + }, + { + 'test_id': 21, + 'test_class': FioJobTest_t0020, + 'job': 't0021.fio', + 'success': SUCCESS_DEFAULT, + 'pre_job': None, + 'pre_success': None, + 'requirements': [], + }, + { + 'test_id': 22, + 'test_class': FioJobTest_t0022, + 'job': 't0022.fio', + 'success': SUCCESS_DEFAULT, + 'pre_job': None, + 'pre_success': None, + 'requirements': [], + }, + { + 'test_id': 23, + 'test_class': FioJobTest_t0023, + 'job': 't0023.fio', + 'success': SUCCESS_DEFAULT, + 'pre_job': None, + 'pre_success': None, + 'requirements': [], + }, + { + 'test_id': 24, + 'test_class': FioJobTest_t0024, + 'job': 't0024.fio', + 'success': SUCCESS_DEFAULT, + 'pre_job': None, + 'pre_success': None, + 'requirements': [], + }, { 'test_id': 1000, 'test_class': FioExeTest, @@ -965,6 +1286,14 @@ TEST_LIST = [ 'success': SUCCESS_DEFAULT, 'requirements': [], }, + { + 'test_id': 1012, + 'test_class': FioExeTest, + 'exe': 't/log_compression.py', + 'parameters': ['-f', '{fio_path}'], + 'success': SUCCESS_DEFAULT, + 'requirements': [], + }, ]