Merge branch 'xnvme_includes' of https://github.com/safl/fio
[fio.git] / t / strided.py
CommitLineData
8c00c383 1#!/usr/bin/env python3
5a36d0e4
VF
2
3"""
6d5af490
VF
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
03317fbb 9# a minimum size of 64MiB is recommended.
6d5af490
VF
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
03317fbb 17# dd if=/dev/zero of=temp bs=1M count=64
6d5af490
VF
18# python t/strided.py ./fio -f temp
19#
6d5af490
VF
20# ===TEST MATRIX===
21#
fa9fd914 22# --zonemode=strided, zoneskip unset
6d5af490
VF
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
5a36d0e4 29"""
6d5af490 30
6d5af490
VF
31import os
32import sys
5a36d0e4 33import time
6d5af490 34import argparse
5a36d0e4
VF
35from pathlib import Path
36from fiotestlib import FioJobCmdTest, run_fio_tests
6d5af490
VF
37
38
5a36d0e4
VF
39class StridedTest(FioJobCmdTest):
40 """Test zonemode=strided."""
6d5af490 41
5a36d0e4
VF
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 ]
6d5af490 55
5a36d0e4
VF
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)
6d5af490 60
5a36d0e4
VF
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 zonestart = 0 if 'offset' not in self.fio_opts else self.fio_opts['offset']
75 iospersize = self.fio_opts['zonesize'] / self.fio_opts['bs']
76 iosperrange = self.fio_opts['zonerange'] / self.fio_opts['bs']
77 iosperzone = 0
78 lines = self.iops_log_lines.split('\n')
79 zoneset = set()
80
81 for line in lines:
82 if len(line) == 0:
6d5af490
VF
83 continue
84
5a36d0e4
VF
85 if iosperzone == iospersize:
86 # time to move to a new zone
87 iosperzone = 0
88 zoneset = set()
89 zonestart += self.fio_opts['zonerange']
90 if zonestart >= self.fio_opts['filesize']:
91 zonestart = 0 if 'offset' not in self.fio_opts else self.fio_opts['offset']
92
93 iosperzone = iosperzone + 1
94 tokens = line.split(',')
95 offset = int(tokens[4])
96 if offset < zonestart or offset >= zonestart + self.fio_opts['zonerange']:
97 print(f"Offset {offset} outside of zone starting at {zonestart}")
6d5af490 98 return False
6d5af490 99
5a36d0e4
VF
100 # skip next section if norandommap is enabled with no
101 # random_generator or with a random_generator != lfsr
102 if 'norandommap' in self.fio_opts:
103 if 'random_generator' in self.fio_opts:
104 if self.fio_opts['random_generator'] != 'lfsr':
105 continue
106 else:
107 continue
6d5af490 108
5a36d0e4
VF
109 # we either have a random map enabled or we
110 # are using an LFSR
111 # so all blocks should be unique and we should have
112 # covered the entire zone when iosperzone % iosperrange == 0
113 block = (offset - zonestart) / self.fio_opts['bs']
114 if block in zoneset:
115 print(f"Offset {offset} in zone already touched")
116 return False
117
118 zoneset.add(block)
119 if iosperzone % iosperrange == 0:
120 if len(zoneset) != iosperrange:
121 print(f"Expected {iosperrange} blocks in zone but only saw {len(zoneset)}")
122 return False
123 zoneset = set()
124
125 return True
126
127
128TEST_LIST = [ # randommap enabled
129 {
130 "test_id": 1,
131 "fio_opts": {
132 "zonerange": 4096,
133 "zonesize": 4096,
134 "bs": 4096,
135 "offset": 8*4096,
136 "size": 16*4096,
137 "io_size": 16*4096,
138 },
139 "test_class": StridedTest,
140 },
141 {
142 "test_id": 2,
143 "fio_opts": {
144 "zonerange": 4096,
145 "zonesize": 4096,
146 "bs": 4096,
147 "size": 16*4096,
148 "io_size": 16*4096,
149 },
150 "test_class": StridedTest,
151 },
152 {
153 "test_id": 3,
154 "fio_opts": {
155 "zonerange": 16*1024*1024,
156 "zonesize": 16*1024*1024,
157 "bs": 4096,
158 "size": 256*1024*1024,
159 "io_size": 256*1024*204,
160 },
161 "test_class": StridedTest,
162 },
163 {
164 "test_id": 4,
165 "fio_opts": {
166 "zonerange": 4096,
167 "zonesize": 4*4096,
168 "bs": 4096,
169 "size": 16*4096,
170 "io_size": 16*4096,
171 },
172 "test_class": StridedTest,
173 },
174 {
175 "test_id": 5,
176 "fio_opts": {
177 "zonerange": 16*1024*1024,
178 "zonesize": 32*1024*1024,
179 "bs": 4096,
180 "size": 256*1024*1024,
181 "io_size": 256*1024*204,
182 },
183 "test_class": StridedTest,
184 },
185 {
186 "test_id": 6,
187 "fio_opts": {
188 "zonerange": 8192,
189 "zonesize": 4096,
190 "bs": 4096,
191 "size": 16*4096,
192 "io_size": 16*4096,
193 },
194 "test_class": StridedTest,
195 },
196 {
197 "test_id": 7,
198 "fio_opts": {
199 "zonerange": 16*1024*1024,
200 "zonesize": 8*1024*1024,
201 "bs": 4096,
202 "size": 256*1024*1024,
203 "io_size": 256*1024*204,
204 },
205 "test_class": StridedTest,
206 },
207 # lfsr
208 {
209 "test_id": 8,
210 "fio_opts": {
211 "random_generator": "lfsr",
212 "zonerange": 4096*1024,
213 "zonesize": 4096*1024,
214 "bs": 4096,
215 "offset": 8*4096*1024,
216 "size": 16*4096*1024,
217 "io_size": 16*4096*1024,
218 },
219 "test_class": StridedTest,
220 },
221 {
222 "test_id": 9,
223 "fio_opts": {
224 "random_generator": "lfsr",
225 "zonerange": 4096*1024,
226 "zonesize": 4096*1024,
227 "bs": 4096,
228 "size": 16*4096*1024,
229 "io_size": 16*4096*1024,
230 },
231 "test_class": StridedTest,
232 },
233 {
234 "test_id": 10,
235 "fio_opts": {
236 "random_generator": "lfsr",
237 "zonerange": 16*1024*1024,
238 "zonesize": 16*1024*1024,
239 "bs": 4096,
240 "size": 256*1024*1024,
241 "io_size": 256*1024*204,
242 },
243 "test_class": StridedTest,
244 },
245 {
246 "test_id": 11,
247 "fio_opts": {
248 "random_generator": "lfsr",
249 "zonerange": 4096*1024,
250 "zonesize": 4*4096*1024,
251 "bs": 4096,
252 "size": 16*4096*1024,
253 "io_size": 16*4096*1024,
254 },
255 "test_class": StridedTest,
256 },
257 {
258 "test_id": 12,
259 "fio_opts": {
260 "random_generator": "lfsr",
261 "zonerange": 16*1024*1024,
262 "zonesize": 32*1024*1024,
263 "bs": 4096,
264 "size": 256*1024*1024,
265 "io_size": 256*1024*204,
266 },
267 "test_class": StridedTest,
268 },
269 {
270 "test_id": 13,
271 "fio_opts": {
272 "random_generator": "lfsr",
273 "zonerange": 8192*1024,
274 "zonesize": 4096*1024,
275 "bs": 4096,
276 "size": 16*4096*1024,
277 "io_size": 16*4096*1024,
278 },
279 "test_class": StridedTest,
280 },
281 {
282 "test_id": 14,
283 "fio_opts": {
284 "random_generator": "lfsr",
285 "zonerange": 16*1024*1024,
286 "zonesize": 8*1024*1024,
287 "bs": 4096,
288 "size": 256*1024*1024,
289 "io_size": 256*1024*204,
290 },
291 "test_class": StridedTest,
292 },
293 # norandommap
294 {
295 "test_id": 15,
296 "fio_opts": {
297 "norandommap": 1,
298 "zonerange": 4096,
299 "zonesize": 4096,
300 "bs": 4096,
301 "offset": 8*4096,
302 "size": 16*4096,
303 "io_size": 16*4096,
304 },
305 "test_class": StridedTest,
306 },
307 {
308 "test_id": 16,
309 "fio_opts": {
310 "norandommap": 1,
311 "zonerange": 4096,
312 "zonesize": 4096,
313 "bs": 4096,
314 "size": 16*4096,
315 "io_size": 16*4096,
316 },
317 "test_class": StridedTest,
318 },
319 {
320 "test_id": 17,
321 "fio_opts": {
322 "norandommap": 1,
323 "zonerange": 16*1024*1024,
324 "zonesize": 16*1024*1024,
325 "bs": 4096,
326 "size": 256*1024*1024,
327 "io_size": 256*1024*204,
328 },
329 "test_class": StridedTest,
330 },
331 {
332 "test_id": 18,
333 "fio_opts": {
334 "norandommap": 1,
335 "zonerange": 4096,
336 "zonesize": 8192,
337 "bs": 4096,
338 "size": 16*4096,
339 "io_size": 16*4096,
340 },
341 "test_class": StridedTest,
342 },
343 {
344 "test_id": 19,
345 "fio_opts": {
346 "norandommap": 1,
347 "zonerange": 16*1024*1024,
348 "zonesize": 32*1024*1024,
349 "bs": 4096,
350 "size": 256*1024*1024,
351 "io_size": 256*1024*204,
352 },
353 "test_class": StridedTest,
354 },
355 {
356 "test_id": 20,
357 "fio_opts": {
358 "norandommap": 1,
359 "zonerange": 8192,
360 "zonesize": 4096,
361 "bs": 4096,
362 "size": 16*4096,
363 "io_size": 16*4096,
364 },
365 "test_class": StridedTest,
366 },
367 {
368 "test_id": 21,
369 "fio_opts": {
370 "norandommap": 1,
371 "zonerange": 16*1024*1024,
372 "zonesize": 8*1024*1024,
373 "bs": 4096,
374 "size": 256*1024*1024,
375 "io_size": 256*1024*1024,
376 },
377 "test_class": StridedTest,
378 },
379]
380
381
382def parse_args():
383 """Parse command-line arguments."""
384
385 parser = argparse.ArgumentParser()
386 parser.add_argument('-f', '--fio', help='path to file executable (e.g., ./fio)')
387 parser.add_argument('-a', '--artifact-root', help='artifact root directory')
388 parser.add_argument('-s', '--skip', nargs='+', type=int,
389 help='list of test(s) to skip')
390 parser.add_argument('-o', '--run-only', nargs='+', type=int,
391 help='list of test(s) to run, skipping all others')
392 parser.add_argument('--dut',
393 help='target file/device to test.')
394 args = parser.parse_args()
395
396 return args
397
398
399def main():
400 """Run zonemode=strided tests."""
6d5af490 401
6d5af490
VF
402 args = parse_args()
403
5a36d0e4
VF
404 artifact_root = args.artifact_root if args.artifact_root else \
405 f"strided-test-{time.strftime('%Y%m%d-%H%M%S')}"
406 os.mkdir(artifact_root)
407 print(f"Artifact directory is {artifact_root}")
408
409 if args.fio:
410 fio_path = str(Path(args.fio).absolute())
411 else:
412 fio_path = 'fio'
413 print(f"fio path is {fio_path}")
414
415 if args.dut:
416 statinfo = os.stat(args.dut)
6d5af490
VF
417 filesize = statinfo.st_size
418 if filesize == 0:
5a36d0e4 419 f = os.open(args.dut, os.O_RDONLY)
6d5af490
VF
420 filesize = os.lseek(f, 0, os.SEEK_END)
421 os.close(f)
422
5a36d0e4
VF
423 for test in TEST_LIST:
424 if args.dut:
425 test['fio_opts']['filename'] = os.path.abspath(args.dut)
426 test['fio_opts']['filesize'] = filesize
6d5af490 427 else:
5a36d0e4 428 test['fio_opts']['filesize'] = test['fio_opts']['size']
6d5af490 429
5a36d0e4
VF
430 test_env = {
431 'fio_path': fio_path,
432 'fio_root': str(Path(__file__).absolute().parent.parent),
433 'artifact_root': artifact_root,
434 'basename': 'strided',
435 }
6d5af490 436
5a36d0e4 437 _, failed, _ = run_fio_tests(TEST_LIST, test_env, args)
6d5af490 438 sys.exit(failed)
5a36d0e4
VF
439
440
441if __name__ == '__main__':
442 main()