Commit | Line | Data |
---|---|---|
6d5af490 VF |
1 | #!/usr/bin/python |
2 | # Note: this script is python2 and python3 compatible. | |
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 32MiB 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=32 | |
18 | # python t/strided.py ./fio -f temp | |
19 | # | |
20 | # REQUIREMENTS | |
21 | # Python 2.6+ | |
22 | # | |
23 | # ===TEST MATRIX=== | |
24 | # | |
fa9fd914 | 25 | # --zonemode=strided, zoneskip unset |
6d5af490 VF |
26 | # w/ randommap and LFSR |
27 | # zonesize=zonerange all blocks in zonerange touched | |
28 | # zonesize>zonerange all blocks touched and roll-over back into zone | |
29 | # zonesize<zonerange all blocks inside zone | |
30 | # | |
31 | # w/o randommap all blocks inside zone | |
32 | # | |
33 | ||
34 | from __future__ import absolute_import | |
35 | from __future__ import print_function | |
36 | import os | |
37 | import sys | |
38 | import argparse | |
39 | import subprocess | |
40 | ||
41 | ||
42 | def parse_args(): | |
43 | parser = argparse.ArgumentParser() | |
44 | parser.add_argument('fio', | |
45 | help='path to fio executable (e.g., ./fio)') | |
46 | parser.add_argument('-f', '--filename', help="file/device to test") | |
47 | args = parser.parse_args() | |
48 | ||
49 | return args | |
50 | ||
51 | ||
52 | def run_fio(fio, test, index): | |
53 | filename = "strided" | |
54 | fio_args = [ | |
771dbb52 | 55 | "--max-jobs=16", |
6d5af490 VF |
56 | "--name=strided", |
57 | "--zonemode=strided", | |
58 | "--log_offset=1", | |
59 | "--randrepeat=0", | |
60 | "--rw=randread", | |
6d5af490 VF |
61 | "--write_iops_log={0}{1:03d}".format(filename, index), |
62 | "--output={0}{1:03d}.out".format(filename, index), | |
63 | "--zonerange={zonerange}".format(**test), | |
64 | "--zonesize={zonesize}".format(**test), | |
65 | "--bs={bs}".format(**test), | |
66 | ] | |
67 | if 'norandommap' in test: | |
68 | fio_args.append('--norandommap') | |
69 | if 'random_generator' in test: | |
70 | fio_args.append('--random_generator={random_generator}'.format(**test)) | |
71 | if 'offset' in test: | |
72 | fio_args.append('--offset={offset}'.format(**test)) | |
73 | if 'filename' in test: | |
74 | fio_args.append('--filename={filename}'.format(**test)) | |
75 | fio_args.append('--filesize={filesize})'.format(**test)) | |
76 | else: | |
77 | fio_args.append('--ioengine=null') | |
78 | fio_args.append('--size={size}'.format(**test)) | |
79 | fio_args.append('--io_size={io_size}'.format(**test)) | |
80 | fio_args.append('--filesize={size})'.format(**test)) | |
81 | ||
82 | output = subprocess.check_output([fio] + fio_args, universal_newlines=True) | |
83 | ||
84 | f = open("{0}{1:03d}_iops.1.log".format(filename, index), "r") | |
85 | log = f.read() | |
86 | f.close() | |
87 | ||
88 | return log | |
89 | ||
90 | ||
91 | def check_output(iops_log, test): | |
92 | zonestart = 0 if 'offset' not in test else test['offset'] | |
93 | iospersize = test['zonesize'] / test['bs'] | |
94 | iosperrange = test['zonerange'] / test['bs'] | |
95 | iosperzone = 0 | |
96 | lines = iops_log.split('\n') | |
97 | zoneset = set() | |
98 | ||
99 | for line in lines: | |
100 | if len(line) == 0: | |
101 | continue | |
102 | ||
103 | if iosperzone == iospersize: | |
104 | # time to move to a new zone | |
105 | iosperzone = 0 | |
106 | zoneset = set() | |
107 | zonestart += test['zonerange'] | |
108 | if zonestart >= test['filesize']: | |
109 | zonestart = 0 if 'offset' not in test else test['offset'] | |
110 | ||
111 | iosperzone = iosperzone + 1 | |
112 | tokens = line.split(',') | |
113 | offset = int(tokens[4]) | |
114 | if offset < zonestart or offset >= zonestart + test['zonerange']: | |
115 | print("Offset {0} outside of zone starting at {1}".format( | |
116 | offset, zonestart)) | |
117 | return False | |
118 | ||
119 | # skip next section if norandommap is enabled with no | |
120 | # random_generator or with a random_generator != lfsr | |
121 | if 'norandommap' in test: | |
122 | if 'random_generator' in test: | |
123 | if test['random_generator'] != 'lfsr': | |
124 | continue | |
125 | else: | |
126 | continue | |
127 | ||
128 | # we either have a random map enabled or we | |
129 | # are using an LFSR | |
130 | # so all blocks should be unique and we should have | |
131 | # covered the entire zone when iosperzone % iosperrange == 0 | |
132 | block = (offset - zonestart) / test['bs'] | |
133 | if block in zoneset: | |
134 | print("Offset {0} in zone already touched".format(offset)) | |
135 | return False | |
136 | ||
137 | zoneset.add(block) | |
138 | if iosperzone % iosperrange == 0: | |
139 | if len(zoneset) != iosperrange: | |
140 | print("Expected {0} blocks in zone but only saw {1}".format( | |
141 | iosperrange, len(zoneset))) | |
142 | return False | |
143 | zoneset = set() | |
144 | ||
145 | return True | |
146 | ||
147 | ||
148 | if __name__ == '__main__': | |
149 | args = parse_args() | |
150 | ||
151 | tests = [ # randommap enabled | |
152 | { | |
153 | "zonerange": 4096, | |
154 | "zonesize": 4096, | |
155 | "bs": 4096, | |
156 | "offset": 8*4096, | |
157 | "size": 16*4096, | |
158 | "io_size": 16*4096, | |
159 | }, | |
160 | { | |
161 | "zonerange": 4096, | |
162 | "zonesize": 4096, | |
163 | "bs": 4096, | |
164 | "size": 16*4096, | |
165 | "io_size": 16*4096, | |
166 | }, | |
167 | { | |
168 | "zonerange": 16*1024*1024, | |
169 | "zonesize": 16*1024*1024, | |
170 | "bs": 4096, | |
171 | "size": 256*1024*1024, | |
172 | "io_size": 256*1024*204, | |
173 | }, | |
174 | { | |
175 | "zonerange": 4096, | |
176 | "zonesize": 4*4096, | |
177 | "bs": 4096, | |
178 | "size": 16*4096, | |
179 | "io_size": 16*4096, | |
180 | }, | |
181 | { | |
182 | "zonerange": 16*1024*1024, | |
183 | "zonesize": 32*1024*1024, | |
184 | "bs": 4096, | |
185 | "size": 256*1024*1024, | |
186 | "io_size": 256*1024*204, | |
187 | }, | |
188 | { | |
189 | "zonerange": 8192, | |
190 | "zonesize": 4096, | |
191 | "bs": 4096, | |
192 | "size": 16*4096, | |
193 | "io_size": 16*4096, | |
194 | }, | |
195 | { | |
196 | "zonerange": 16*1024*1024, | |
197 | "zonesize": 8*1024*1024, | |
198 | "bs": 4096, | |
199 | "size": 256*1024*1024, | |
200 | "io_size": 256*1024*204, | |
201 | }, | |
202 | # lfsr | |
203 | { | |
204 | "random_generator": "lfsr", | |
022a8946 VF |
205 | "zonerange": 4096*1024, |
206 | "zonesize": 4096*1024, | |
6d5af490 | 207 | "bs": 4096, |
022a8946 VF |
208 | "offset": 8*4096*1024, |
209 | "size": 16*4096*1024, | |
210 | "io_size": 16*4096*1024, | |
6d5af490 VF |
211 | }, |
212 | { | |
213 | "random_generator": "lfsr", | |
022a8946 VF |
214 | "zonerange": 4096*1024, |
215 | "zonesize": 4096*1024, | |
6d5af490 | 216 | "bs": 4096, |
022a8946 VF |
217 | "size": 16*4096*1024, |
218 | "io_size": 16*4096*1024, | |
6d5af490 VF |
219 | }, |
220 | { | |
221 | "random_generator": "lfsr", | |
222 | "zonerange": 16*1024*1024, | |
223 | "zonesize": 16*1024*1024, | |
224 | "bs": 4096, | |
225 | "size": 256*1024*1024, | |
226 | "io_size": 256*1024*204, | |
227 | }, | |
228 | { | |
229 | "random_generator": "lfsr", | |
022a8946 VF |
230 | "zonerange": 4096*1024, |
231 | "zonesize": 4*4096*1024, | |
6d5af490 | 232 | "bs": 4096, |
022a8946 VF |
233 | "size": 16*4096*1024, |
234 | "io_size": 16*4096*1024, | |
6d5af490 VF |
235 | }, |
236 | { | |
237 | "random_generator": "lfsr", | |
238 | "zonerange": 16*1024*1024, | |
239 | "zonesize": 32*1024*1024, | |
240 | "bs": 4096, | |
241 | "size": 256*1024*1024, | |
242 | "io_size": 256*1024*204, | |
243 | }, | |
244 | { | |
245 | "random_generator": "lfsr", | |
022a8946 VF |
246 | "zonerange": 8192*1024, |
247 | "zonesize": 4096*1024, | |
6d5af490 | 248 | "bs": 4096, |
022a8946 VF |
249 | "size": 16*4096*1024, |
250 | "io_size": 16*4096*1024, | |
6d5af490 VF |
251 | }, |
252 | { | |
253 | "random_generator": "lfsr", | |
254 | "zonerange": 16*1024*1024, | |
255 | "zonesize": 8*1024*1024, | |
256 | "bs": 4096, | |
257 | "size": 256*1024*1024, | |
258 | "io_size": 256*1024*204, | |
259 | }, | |
260 | # norandommap | |
261 | { | |
262 | "norandommap": 1, | |
263 | "zonerange": 4096, | |
264 | "zonesize": 4096, | |
265 | "bs": 4096, | |
266 | "offset": 8*4096, | |
267 | "size": 16*4096, | |
268 | "io_size": 16*4096, | |
269 | }, | |
270 | { | |
271 | "norandommap": 1, | |
272 | "zonerange": 4096, | |
273 | "zonesize": 4096, | |
274 | "bs": 4096, | |
275 | "size": 16*4096, | |
276 | "io_size": 16*4096, | |
277 | }, | |
278 | { | |
279 | "norandommap": 1, | |
280 | "zonerange": 16*1024*1024, | |
281 | "zonesize": 16*1024*1024, | |
282 | "bs": 4096, | |
283 | "size": 256*1024*1024, | |
284 | "io_size": 256*1024*204, | |
285 | }, | |
286 | { | |
287 | "norandommap": 1, | |
288 | "zonerange": 4096, | |
289 | "zonesize": 8192, | |
290 | "bs": 4096, | |
291 | "size": 16*4096, | |
292 | "io_size": 16*4096, | |
293 | }, | |
294 | { | |
295 | "norandommap": 1, | |
296 | "zonerange": 16*1024*1024, | |
297 | "zonesize": 32*1024*1024, | |
298 | "bs": 4096, | |
299 | "size": 256*1024*1024, | |
300 | "io_size": 256*1024*204, | |
301 | }, | |
302 | { | |
303 | "norandommap": 1, | |
304 | "zonerange": 8192, | |
305 | "zonesize": 4096, | |
306 | "bs": 4096, | |
307 | "size": 16*4096, | |
308 | "io_size": 16*4096, | |
309 | }, | |
310 | { | |
311 | "norandommap": 1, | |
312 | "zonerange": 16*1024*1024, | |
313 | "zonesize": 8*1024*1024, | |
314 | "bs": 4096, | |
315 | "size": 256*1024*1024, | |
022a8946 | 316 | "io_size": 256*1024*1024, |
6d5af490 VF |
317 | }, |
318 | ||
319 | ] | |
320 | ||
321 | index = 1 | |
322 | passed = 0 | |
323 | failed = 0 | |
324 | ||
325 | if args.filename: | |
326 | statinfo = os.stat(args.filename) | |
327 | filesize = statinfo.st_size | |
328 | if filesize == 0: | |
329 | f = os.open(args.filename, os.O_RDONLY) | |
330 | filesize = os.lseek(f, 0, os.SEEK_END) | |
331 | os.close(f) | |
332 | ||
333 | for test in tests: | |
334 | if args.filename: | |
335 | test['filename'] = args.filename | |
336 | test['filesize'] = filesize | |
337 | else: | |
338 | test['filesize'] = test['size'] | |
339 | iops_log = run_fio(args.fio, test, index) | |
340 | status = check_output(iops_log, test) | |
341 | print("Test {0} {1}".format(index, ("PASSED" if status else "FAILED"))) | |
342 | if status: | |
343 | passed = passed + 1 | |
344 | else: | |
345 | failed = failed + 1 | |
346 | index = index + 1 | |
347 | ||
348 | print("{0} tests passed, {1} failed".format(passed, failed)) | |
349 | ||
350 | sys.exit(failed) |