6 # Test zonemode=strided. This uses the null ioengine when no file is
7 # specified. If a file is specified, use it for randdom read testing.
8 # Some of the zoneranges in the tests are 16MiB. So when using a file
9 # a minimum size of 64MiB is recommended.
12 # python strided.py fio-executable [-f file/device]
15 # python t/strided.py ./fio
16 # python t/strided.py ./fio -f /dev/sda
17 # dd if=/dev/zero of=temp bs=1M count=64
18 # python t/strided.py ./fio -f temp
22 # --zonemode=strided, zoneskip unset
23 # w/ randommap and LFSR
24 # zonesize=zonerange all blocks in zonerange touched
25 # zonesize>zonerange all blocks touched and roll-over back into zone
26 # zonesize<zonerange all blocks inside zone
28 # w/o randommap all blocks inside zone
35 from pathlib import Path
36 from fiotestlib import FioJobCmdTest, run_fio_tests
39 class StridedTest(FioJobCmdTest):
40 """Test zonemode=strided."""
42 def setup(self, parameters):
49 f"--write_iops_log={self.filenames['iopslog']}",
50 f"--output={self.filenames['output']}",
51 f"--zonerange={self.fio_opts['zonerange']}",
52 f"--zonesize={self.fio_opts['zonesize']}",
53 f"--bs={self.fio_opts['bs']}",
56 for opt in ['norandommap', 'random_generator', 'offset']:
57 if opt in self.fio_opts:
58 option = f"--{opt}={self.fio_opts[opt]}"
59 fio_args.append(option)
61 if 'filename' in self.fio_opts:
62 for opt in ['filename', 'filesize']:
63 option = f"--{opt}={self.fio_opts[opt]}"
64 fio_args.append(option)
66 fio_args.append('--ioengine=null')
67 for opt in ['size', 'io_size', 'filesize']:
68 option = f"--{opt}={self.fio_opts[opt]}"
69 fio_args.append(option)
71 super().setup(fio_args)
73 def check_result(self):
74 super().check_result()
78 zonestart = 0 if 'offset' not in self.fio_opts else self.fio_opts['offset']
79 iospersize = self.fio_opts['zonesize'] / self.fio_opts['bs']
80 iosperrange = self.fio_opts['zonerange'] / self.fio_opts['bs']
82 lines = self.iops_log_lines.split('\n')
89 if iosperzone == iospersize:
90 # time to move to a new zone
93 zonestart += self.fio_opts['zonerange']
94 if zonestart >= self.fio_opts['filesize']:
95 zonestart = 0 if 'offset' not in self.fio_opts else self.fio_opts['offset']
97 iosperzone = iosperzone + 1
98 tokens = line.split(',')
99 offset = int(tokens[4])
100 if offset < zonestart or offset >= zonestart + self.fio_opts['zonerange']:
101 print(f"Offset {offset} outside of zone starting at {zonestart}")
104 # skip next section if norandommap is enabled with no
105 # random_generator or with a random_generator != lfsr
106 if 'norandommap' in self.fio_opts:
107 if 'random_generator' in self.fio_opts:
108 if self.fio_opts['random_generator'] != 'lfsr':
113 # we either have a random map enabled or we
115 # so all blocks should be unique and we should have
116 # covered the entire zone when iosperzone % iosperrange == 0
117 block = (offset - zonestart) / self.fio_opts['bs']
119 print(f"Offset {offset} in zone already touched")
123 if iosperzone % iosperrange == 0:
124 if len(zoneset) != iosperrange:
125 print(f"Expected {iosperrange} blocks in zone but only saw {len(zoneset)}")
130 TEST_LIST = [ # randommap enabled
141 "test_class": StridedTest,
152 "test_class": StridedTest,
157 "zonerange": 16*1024*1024,
158 "zonesize": 16*1024*1024,
160 "size": 256*1024*1024,
161 "io_size": 256*1024*204,
163 "test_class": StridedTest,
174 "test_class": StridedTest,
179 "zonerange": 16*1024*1024,
180 "zonesize": 32*1024*1024,
182 "size": 256*1024*1024,
183 "io_size": 256*1024*204,
185 "test_class": StridedTest,
196 "test_class": StridedTest,
201 "zonerange": 16*1024*1024,
202 "zonesize": 8*1024*1024,
204 "size": 256*1024*1024,
205 "io_size": 256*1024*204,
207 "test_class": StridedTest,
213 "random_generator": "lfsr",
214 "zonerange": 4096*1024,
215 "zonesize": 4096*1024,
217 "offset": 8*4096*1024,
218 "size": 16*4096*1024,
219 "io_size": 16*4096*1024,
221 "test_class": StridedTest,
226 "random_generator": "lfsr",
227 "zonerange": 4096*1024,
228 "zonesize": 4096*1024,
230 "size": 16*4096*1024,
231 "io_size": 16*4096*1024,
233 "test_class": StridedTest,
238 "random_generator": "lfsr",
239 "zonerange": 16*1024*1024,
240 "zonesize": 16*1024*1024,
242 "size": 256*1024*1024,
243 "io_size": 256*1024*204,
245 "test_class": StridedTest,
250 "random_generator": "lfsr",
251 "zonerange": 4096*1024,
252 "zonesize": 4*4096*1024,
254 "size": 16*4096*1024,
255 "io_size": 16*4096*1024,
257 "test_class": StridedTest,
262 "random_generator": "lfsr",
263 "zonerange": 16*1024*1024,
264 "zonesize": 32*1024*1024,
266 "size": 256*1024*1024,
267 "io_size": 256*1024*204,
269 "test_class": StridedTest,
274 "random_generator": "lfsr",
275 "zonerange": 8192*1024,
276 "zonesize": 4096*1024,
278 "size": 16*4096*1024,
279 "io_size": 16*4096*1024,
281 "test_class": StridedTest,
286 "random_generator": "lfsr",
287 "zonerange": 16*1024*1024,
288 "zonesize": 8*1024*1024,
290 "size": 256*1024*1024,
291 "io_size": 256*1024*204,
293 "test_class": StridedTest,
307 "test_class": StridedTest,
319 "test_class": StridedTest,
325 "zonerange": 16*1024*1024,
326 "zonesize": 16*1024*1024,
328 "size": 256*1024*1024,
329 "io_size": 256*1024*204,
331 "test_class": StridedTest,
343 "test_class": StridedTest,
349 "zonerange": 16*1024*1024,
350 "zonesize": 32*1024*1024,
352 "size": 256*1024*1024,
353 "io_size": 256*1024*204,
355 "test_class": StridedTest,
367 "test_class": StridedTest,
373 "zonerange": 16*1024*1024,
374 "zonesize": 8*1024*1024,
376 "size": 256*1024*1024,
377 "io_size": 256*1024*1024,
379 "test_class": StridedTest,
385 """Parse command-line arguments."""
387 parser = argparse.ArgumentParser()
388 parser.add_argument('-f', '--fio', help='path to file executable (e.g., ./fio)')
389 parser.add_argument('-a', '--artifact-root', help='artifact root directory')
390 parser.add_argument('-s', '--skip', nargs='+', type=int,
391 help='list of test(s) to skip')
392 parser.add_argument('-o', '--run-only', nargs='+', type=int,
393 help='list of test(s) to run, skipping all others')
394 parser.add_argument('--dut',
395 help='target file/device to test.')
396 args = parser.parse_args()
402 """Run zonemode=strided tests."""
406 artifact_root = args.artifact_root if args.artifact_root else \
407 f"strided-test-{time.strftime('%Y%m%d-%H%M%S')}"
408 os.mkdir(artifact_root)
409 print(f"Artifact directory is {artifact_root}")
412 fio_path = str(Path(args.fio).absolute())
415 print(f"fio path is {fio_path}")
418 statinfo = os.stat(args.dut)
419 filesize = statinfo.st_size
421 f = os.open(args.dut, os.O_RDONLY)
422 filesize = os.lseek(f, 0, os.SEEK_END)
425 for test in TEST_LIST:
427 test['fio_opts']['filename'] = os.path.abspath(args.dut)
428 test['fio_opts']['filesize'] = filesize
430 test['fio_opts']['filesize'] = test['fio_opts']['size']
433 'fio_path': fio_path,
434 'fio_root': str(Path(__file__).absolute().parent.parent),
435 'artifact_root': artifact_root,
436 'basename': 'strided',
439 _, failed, _ = run_fio_tests(TEST_LIST, test_env, args)
443 if __name__ == '__main__':