Merge branch 'enable-dataplacement-while-replaying-io' of https://github.com/parkvibe...
[fio.git] / t / strided.py
1 #!/usr/bin/env python3
2
3 """
4 # strided.py
5 #
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.
10 #
11 # USAGE
12 # python strided.py fio-executable [-f file/device]
13 #
14 # EXAMPLES
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
19 #
20 # ===TEST MATRIX===
21 #
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
27 #
28 #   w/o randommap       all blocks inside zone
29 """
30
31 import os
32 import sys
33 import time
34 import argparse
35 from pathlib import Path
36 from fiotestlib import FioJobCmdTest, run_fio_tests
37
38
39 class StridedTest(FioJobCmdTest):
40     """Test zonemode=strided."""
41
42     def setup(self, parameters):
43         fio_args = [
44                     "--name=strided",
45                     "--zonemode=strided",
46                     "--log_offset=1",
47                     "--randrepeat=0",
48                     "--rw=randread",
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']}",
54                    ]
55
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)
60
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)
65         else:
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)
70
71         super().setup(fio_args)
72
73     def check_result(self):
74         super().check_result()
75         if not self.passed:
76             return
77
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']
81         iosperzone = 0
82         lines = self.iops_log_lines.split('\n')
83         zoneset = set()
84
85         for line in lines:
86             if len(line) == 0:
87                 continue
88
89             if iosperzone == iospersize:
90                 # time to move to a new zone
91                 iosperzone = 0
92                 zoneset = set()
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']
96
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}")
102                 return
103
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':
109                         continue
110                 else:
111                     continue
112
113             # we either have a random map enabled or we
114             # are using an LFSR
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']
118             if block in zoneset:
119                 print(f"Offset {offset} in zone already touched")
120                 return
121
122             zoneset.add(block)
123             if iosperzone % iosperrange == 0:
124                 if len(zoneset) != iosperrange:
125                     print(f"Expected {iosperrange} blocks in zone but only saw {len(zoneset)}")
126                     return
127                 zoneset = set()
128
129
130 TEST_LIST = [   # randommap enabled
131     {
132         "test_id": 1,
133         "fio_opts": {
134             "zonerange": 4096,
135             "zonesize": 4096,
136             "bs": 4096,
137             "offset": 8*4096,
138             "size": 16*4096,
139             "io_size": 16*4096,
140             },
141         "test_class": StridedTest,
142     },
143     {
144         "test_id": 2,
145         "fio_opts": {
146             "zonerange": 4096,
147             "zonesize": 4096,
148             "bs": 4096,
149             "size": 16*4096,
150             "io_size": 16*4096,
151             },
152         "test_class": StridedTest,
153     },
154     {
155         "test_id": 3,
156         "fio_opts": {
157             "zonerange": 16*1024*1024,
158             "zonesize": 16*1024*1024,
159             "bs": 4096,
160             "size": 256*1024*1024,
161             "io_size": 256*1024*204,
162             },
163         "test_class": StridedTest,
164     },
165     {
166         "test_id": 4,
167         "fio_opts": {
168             "zonerange": 4096,
169             "zonesize": 4*4096,
170             "bs": 4096,
171             "size": 16*4096,
172             "io_size": 16*4096,
173             },
174         "test_class": StridedTest,
175     },
176     {
177         "test_id": 5,
178         "fio_opts": {
179             "zonerange": 16*1024*1024,
180             "zonesize": 32*1024*1024,
181             "bs": 4096,
182             "size": 256*1024*1024,
183             "io_size": 256*1024*204,
184             },
185         "test_class": StridedTest,
186     },
187     {
188         "test_id": 6,
189         "fio_opts": {
190             "zonerange": 8192,
191             "zonesize": 4096,
192             "bs": 4096,
193             "size": 16*4096,
194             "io_size": 16*4096,
195             },
196         "test_class": StridedTest,
197     },
198     {
199         "test_id": 7,
200         "fio_opts": {
201             "zonerange": 16*1024*1024,
202             "zonesize": 8*1024*1024,
203             "bs": 4096,
204             "size": 256*1024*1024,
205             "io_size": 256*1024*204,
206             },
207         "test_class": StridedTest,
208     },
209             # lfsr
210     {
211         "test_id": 8,
212         "fio_opts": {
213             "random_generator": "lfsr",
214             "zonerange": 4096*1024,
215             "zonesize": 4096*1024,
216             "bs": 4096,
217             "offset": 8*4096*1024,
218             "size": 16*4096*1024,
219             "io_size": 16*4096*1024,
220             },
221         "test_class": StridedTest,
222     },
223     {
224         "test_id": 9,
225         "fio_opts": {
226             "random_generator": "lfsr",
227             "zonerange": 4096*1024,
228             "zonesize": 4096*1024,
229             "bs": 4096,
230             "size": 16*4096*1024,
231             "io_size": 16*4096*1024,
232             },
233         "test_class": StridedTest,
234     },
235     {
236         "test_id": 10,
237         "fio_opts": {
238             "random_generator": "lfsr",
239             "zonerange": 16*1024*1024,
240             "zonesize": 16*1024*1024,
241             "bs": 4096,
242             "size": 256*1024*1024,
243             "io_size": 256*1024*204,
244             },
245         "test_class": StridedTest,
246     },
247     {
248         "test_id": 11,
249         "fio_opts": {
250             "random_generator": "lfsr",
251             "zonerange": 4096*1024,
252             "zonesize": 4*4096*1024,
253             "bs": 4096,
254             "size": 16*4096*1024,
255             "io_size": 16*4096*1024,
256             },
257         "test_class": StridedTest,
258     },
259     {
260         "test_id": 12,
261         "fio_opts": {
262             "random_generator": "lfsr",
263             "zonerange": 16*1024*1024,
264             "zonesize": 32*1024*1024,
265             "bs": 4096,
266             "size": 256*1024*1024,
267             "io_size": 256*1024*204,
268             },
269         "test_class": StridedTest,
270     },
271     {
272         "test_id": 13,
273         "fio_opts": {
274             "random_generator": "lfsr",
275             "zonerange": 8192*1024,
276             "zonesize": 4096*1024,
277             "bs": 4096,
278             "size": 16*4096*1024,
279             "io_size": 16*4096*1024,
280             },
281         "test_class": StridedTest,
282     },
283     {
284         "test_id": 14,
285         "fio_opts": {
286             "random_generator": "lfsr",
287             "zonerange": 16*1024*1024,
288             "zonesize": 8*1024*1024,
289             "bs": 4096,
290             "size": 256*1024*1024,
291             "io_size": 256*1024*204,
292             },
293         "test_class": StridedTest,
294     },
295     # norandommap
296     {
297         "test_id": 15,
298         "fio_opts": {
299             "norandommap": 1,
300             "zonerange": 4096,
301             "zonesize": 4096,
302             "bs": 4096,
303             "offset": 8*4096,
304             "size": 16*4096,
305             "io_size": 16*4096,
306             },
307         "test_class": StridedTest,
308     },
309     {
310         "test_id": 16,
311         "fio_opts": {
312             "norandommap": 1,
313             "zonerange": 4096,
314             "zonesize": 4096,
315             "bs": 4096,
316             "size": 16*4096,
317             "io_size": 16*4096,
318             },
319         "test_class": StridedTest,
320     },
321     {
322         "test_id": 17,
323         "fio_opts": {
324             "norandommap": 1,
325             "zonerange": 16*1024*1024,
326             "zonesize": 16*1024*1024,
327             "bs": 4096,
328             "size": 256*1024*1024,
329             "io_size": 256*1024*204,
330             },
331         "test_class": StridedTest,
332     },
333     {
334         "test_id": 18,
335         "fio_opts": {
336             "norandommap": 1,
337             "zonerange": 4096,
338             "zonesize": 8192,
339             "bs": 4096,
340             "size": 16*4096,
341             "io_size": 16*4096,
342             },
343         "test_class": StridedTest,
344     },
345     {
346         "test_id": 19,
347         "fio_opts": {
348             "norandommap": 1,
349             "zonerange": 16*1024*1024,
350             "zonesize": 32*1024*1024,
351             "bs": 4096,
352             "size": 256*1024*1024,
353             "io_size": 256*1024*204,
354             },
355         "test_class": StridedTest,
356     },
357     {
358         "test_id": 20,
359         "fio_opts": {
360             "norandommap": 1,
361             "zonerange": 8192,
362             "zonesize": 4096,
363             "bs": 4096,
364             "size": 16*4096,
365             "io_size": 16*4096,
366             },
367         "test_class": StridedTest,
368     },
369     {
370         "test_id": 21,
371         "fio_opts": {
372             "norandommap": 1,
373             "zonerange": 16*1024*1024,
374             "zonesize": 8*1024*1024,
375             "bs": 4096,
376             "size": 256*1024*1024,
377             "io_size": 256*1024*1024,
378             },
379         "test_class": StridedTest,
380     },
381 ]
382
383
384 def parse_args():
385     """Parse command-line arguments."""
386
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()
397
398     return args
399
400
401 def main():
402     """Run zonemode=strided tests."""
403
404     args = parse_args()
405
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}")
410
411     if args.fio:
412         fio_path = str(Path(args.fio).absolute())
413     else:
414         fio_path = 'fio'
415     print(f"fio path is {fio_path}")
416
417     if args.dut:
418         statinfo = os.stat(args.dut)
419         filesize = statinfo.st_size
420         if filesize == 0:
421             f = os.open(args.dut, os.O_RDONLY)
422             filesize = os.lseek(f, 0, os.SEEK_END)
423             os.close(f)
424
425     for test in TEST_LIST:
426         if args.dut:
427             test['fio_opts']['filename'] = os.path.abspath(args.dut)
428             test['fio_opts']['filesize'] = filesize
429         else:
430             test['fio_opts']['filesize'] = test['fio_opts']['size']
431
432     test_env = {
433               'fio_path': fio_path,
434               'fio_root': str(Path(__file__).absolute().parent.parent),
435               'artifact_root': artifact_root,
436               'basename': 'strided',
437               }
438
439     _, failed, _ = run_fio_tests(TEST_LIST, test_env, args)
440     sys.exit(failed)
441
442
443 if __name__ == '__main__':
444     main()