Commit | Line | Data |
---|---|---|
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 |
31 | import os |
32 | import sys | |
5a36d0e4 | 33 | import time |
6d5af490 | 34 | import argparse |
5a36d0e4 VF |
35 | from pathlib import Path |
36 | from fiotestlib import FioJobCmdTest, run_fio_tests | |
6d5af490 VF |
37 | |
38 | ||
5a36d0e4 VF |
39 | class 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 | ||
128 | TEST_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 | ||
382 | def 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 | ||
399 | def 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 | ||
441 | if __name__ == '__main__': | |
442 | main() |