Merge branch 'atomic-writes'
[fio.git] / t / nvmept.py
CommitLineData
b03ed937
VF
1#!/usr/bin/env python3
2"""
3# nvmept.py
4#
5# Test fio's io_uring_cmd ioengine with NVMe pass-through commands.
6#
7# USAGE
8# see python3 nvmept.py --help
9#
10# EXAMPLES
11# python3 t/nvmept.py --dut /dev/ng0n1
12# python3 t/nvmept.py --dut /dev/ng1n1 -f ./fio
13#
14# REQUIREMENTS
15# Python 3.6
16#
17"""
18import os
19import sys
b03ed937 20import time
b03ed937 21import argparse
b03ed937 22from pathlib import Path
cebaf30e 23from fiotestlib import FioJobCmdTest, run_fio_tests
b03ed937 24
b03ed937 25
cebaf30e
VF
26class PassThruTest(FioJobCmdTest):
27 """
28 NVMe pass-through test class. Check to make sure output for selected data
29 direction(s) is non-zero and that zero data appears for other directions.
30 """
b03ed937 31
cebaf30e
VF
32 def setup(self, parameters):
33 """Setup a test."""
b03ed937
VF
34
35 fio_args = [
36 "--name=nvmept",
37 "--ioengine=io_uring_cmd",
38 "--cmd_type=nvme",
39 "--iodepth=8",
40 "--iodepth_batch=4",
41 "--iodepth_batch_complete=4",
cebaf30e
VF
42 f"--filename={self.fio_opts['filename']}",
43 f"--rw={self.fio_opts['rw']}",
b03ed937 44 f"--output={self.filenames['output']}",
cebaf30e 45 f"--output-format={self.fio_opts['output-format']}",
b03ed937
VF
46 ]
47 for opt in ['fixedbufs', 'nonvectored', 'force_async', 'registerfiles',
48 'sqthread_poll', 'sqthread_poll_cpu', 'hipri', 'nowait',
49 'time_based', 'runtime', 'verify', 'io_size']:
cebaf30e
VF
50 if opt in self.fio_opts:
51 option = f"--{opt}={self.fio_opts[opt]}"
b03ed937
VF
52 fio_args.append(option)
53
cebaf30e 54 super().setup(fio_args)
b03ed937
VF
55
56
cebaf30e 57 def check_result(self):
4883f8f6
VF
58 super().check_result()
59
cebaf30e
VF
60 if 'rw' not in self.fio_opts:
61 return
b03ed937 62
cebaf30e
VF
63 if not self.passed:
64 return
b03ed937
VF
65
66 job = self.json_data['jobs'][0]
b03ed937 67
cebaf30e
VF
68 if self.fio_opts['rw'] in ['read', 'randread']:
69 self.passed = self.check_all_ddirs(['read'], job)
70 elif self.fio_opts['rw'] in ['write', 'randwrite']:
71 if 'verify' not in self.fio_opts:
72 self.passed = self.check_all_ddirs(['write'], job)
b03ed937 73 else:
cebaf30e
VF
74 self.passed = self.check_all_ddirs(['read', 'write'], job)
75 elif self.fio_opts['rw'] in ['trim', 'randtrim']:
76 self.passed = self.check_all_ddirs(['trim'], job)
77 elif self.fio_opts['rw'] in ['readwrite', 'randrw']:
78 self.passed = self.check_all_ddirs(['read', 'write'], job)
79 elif self.fio_opts['rw'] in ['trimwrite', 'randtrimwrite']:
80 self.passed = self.check_all_ddirs(['trim', 'write'], job)
b03ed937 81 else:
cebaf30e
VF
82 print(f"Unhandled rw value {self.fio_opts['rw']}")
83 self.passed = False
b03ed937 84
8604be05
VF
85 if job['iodepth_level']['8'] < 95:
86 print("Did not achieve requested iodepth")
87 self.passed = False
88
b03ed937 89
e84adcf5
MI
90class FlushTest(FioJobCmdTest):
91 def setup(self, parameters):
92 fio_args = [
93 "--name=nvmept-flush",
94 "--ioengine=io_uring_cmd",
95 "--cmd_type=nvme",
96 "--randrepeat=0",
97 f"--filename={self.fio_opts['filename']}",
98 f"--rw={self.fio_opts['rw']}",
99 f"--output={self.filenames['output']}",
100 f"--output-format={self.fio_opts['output-format']}",
101 ]
102
103 for opt in ['fixedbufs', 'nonvectored', 'force_async', 'registerfiles',
104 'sqthread_poll', 'sqthread_poll_cpu', 'hipri', 'nowait',
105 'time_based', 'runtime', 'verify', 'io_size', 'num_range',
106 'iodepth', 'iodepth_batch', 'iodepth_batch_complete',
107 'size', 'rate', 'bs', 'bssplit', 'bsrange', 'randrepeat',
108 'buffer_pattern', 'verify_pattern', 'offset', 'fdp',
109 'fdp_pli', 'fdp_pli_select', 'dataplacement', 'plid_select',
110 'plids', 'dp_scheme', 'number_ios', 'read_iolog', 'fsync']:
111 if opt in self.fio_opts:
112 option = f"--{opt}={self.fio_opts[opt]}"
113 fio_args.append(option)
114
115 super().setup(fio_args)
116
117 def check_result(self):
118 super().check_result()
119
120 job = self.json_data['jobs'][0]
121
122 rw = self.fio_opts['rw']
123 fsync = self.fio_opts['fsync']
124
125 nr_write = job['write']['total_ios']
126 nr_sync = job['sync']['total_ios']
127
128 nr_sync_exp = nr_write // fsync
129
130 # The actual number of DDIR_SYNC issued might miss one DDIR_SYNC command
131 # when the last command issued was DDIR_WRITE command.
132 if not ((nr_sync == nr_sync_exp) or (nr_sync + 1 == nr_sync_exp)):
133 logging.error(f"nr_write={nr_write}, nr_sync={nr_sync}, fsync={fsync}")
134 self.passed = False
135
136
cebaf30e
VF
137TEST_LIST = [
138 {
139 "test_id": 1,
140 "fio_opts": {
b03ed937
VF
141 "rw": 'read',
142 "timebased": 1,
143 "runtime": 3,
144 "output-format": "json",
cebaf30e
VF
145 },
146 "test_class": PassThruTest,
147 },
148 {
149 "test_id": 2,
150 "fio_opts": {
b03ed937
VF
151 "rw": 'randread',
152 "timebased": 1,
153 "runtime": 3,
154 "output-format": "json",
cebaf30e
VF
155 },
156 "test_class": PassThruTest,
157 },
158 {
159 "test_id": 3,
160 "fio_opts": {
b03ed937
VF
161 "rw": 'write',
162 "timebased": 1,
163 "runtime": 3,
164 "output-format": "json",
cebaf30e
VF
165 },
166 "test_class": PassThruTest,
167 },
168 {
169 "test_id": 4,
170 "fio_opts": {
b03ed937
VF
171 "rw": 'randwrite',
172 "timebased": 1,
173 "runtime": 3,
174 "output-format": "json",
cebaf30e
VF
175 },
176 "test_class": PassThruTest,
177 },
178 {
179 "test_id": 5,
180 "fio_opts": {
b03ed937
VF
181 "rw": 'trim',
182 "timebased": 1,
183 "runtime": 3,
184 "output-format": "json",
cebaf30e
VF
185 },
186 "test_class": PassThruTest,
187 },
188 {
189 "test_id": 6,
190 "fio_opts": {
b03ed937
VF
191 "rw": 'randtrim',
192 "timebased": 1,
193 "runtime": 3,
194 "output-format": "json",
cebaf30e
VF
195 },
196 "test_class": PassThruTest,
197 },
198 {
199 "test_id": 7,
200 "fio_opts": {
b03ed937
VF
201 "rw": 'write',
202 "io_size": 1024*1024,
203 "verify": "crc32c",
204 "output-format": "json",
cebaf30e
VF
205 },
206 "test_class": PassThruTest,
207 },
208 {
209 "test_id": 8,
210 "fio_opts": {
b03ed937
VF
211 "rw": 'randwrite',
212 "io_size": 1024*1024,
213 "verify": "crc32c",
214 "output-format": "json",
cebaf30e
VF
215 },
216 "test_class": PassThruTest,
217 },
218 {
219 "test_id": 9,
220 "fio_opts": {
b03ed937
VF
221 "rw": 'readwrite',
222 "timebased": 1,
223 "runtime": 3,
224 "output-format": "json",
cebaf30e
VF
225 },
226 "test_class": PassThruTest,
227 },
228 {
229 "test_id": 10,
230 "fio_opts": {
b03ed937
VF
231 "rw": 'randrw',
232 "timebased": 1,
233 "runtime": 3,
234 "output-format": "json",
cebaf30e
VF
235 },
236 "test_class": PassThruTest,
237 },
238 {
239 "test_id": 11,
240 "fio_opts": {
b03ed937
VF
241 "rw": 'trimwrite',
242 "timebased": 1,
243 "runtime": 3,
244 "output-format": "json",
cebaf30e
VF
245 },
246 "test_class": PassThruTest,
247 },
248 {
249 "test_id": 12,
250 "fio_opts": {
b03ed937
VF
251 "rw": 'randtrimwrite',
252 "timebased": 1,
253 "runtime": 3,
254 "output-format": "json",
cebaf30e
VF
255 },
256 "test_class": PassThruTest,
257 },
258 {
259 "test_id": 13,
260 "fio_opts": {
b03ed937
VF
261 "rw": 'randread',
262 "timebased": 1,
263 "runtime": 3,
264 "fixedbufs": 1,
265 "nonvectored": 1,
266 "force_async": 1,
267 "registerfiles": 1,
268 "sqthread_poll": 1,
269 "output-format": "json",
cebaf30e
VF
270 },
271 "test_class": PassThruTest,
272 },
273 {
274 "test_id": 14,
275 "fio_opts": {
b03ed937
VF
276 "rw": 'randwrite',
277 "timebased": 1,
278 "runtime": 3,
279 "fixedbufs": 1,
280 "nonvectored": 1,
281 "force_async": 1,
282 "registerfiles": 1,
283 "sqthread_poll": 1,
284 "output-format": "json",
cebaf30e
VF
285 },
286 "test_class": PassThruTest,
287 },
d47132c6
VF
288 {
289 # We can't enable fixedbufs because for trim-only
290 # workloads fio actually does not allocate any buffers
291 "test_id": 15,
292 "fio_opts": {
293 "rw": 'randtrim',
294 "timebased": 1,
295 "runtime": 3,
296 "fixedbufs": 0,
297 "nonvectored": 1,
298 "force_async": 1,
299 "registerfiles": 1,
300 "sqthread_poll": 1,
301 "output-format": "json",
302 },
303 "test_class": PassThruTest,
304 },
e84adcf5
MI
305 {
306 "test_id": 16,
307 "fio_opts": {
308 "rw": 'read',
309 "bs": 4096,
310 "number_ios": 10,
311 "fsync": 1,
312 "output-format": "json",
313 },
314 "test_class": FlushTest,
315 },
316 {
317 "test_id": 17,
318 "fio_opts": {
319 "rw": 'write',
320 "bs": 4096,
321 "number_ios": 10,
322 "fsync": 1,
323 "output-format": "json",
324 },
325 "test_class": FlushTest,
326 },
327 {
328 "test_id": 18,
329 "fio_opts": {
330 "rw": 'readwrite',
331 "bs": 4096,
332 "number_ios": 10,
333 "fsync": 1,
334 "output-format": "json",
335 },
336 "test_class": FlushTest,
337 },
338 {
339 "test_id": 19,
340 "fio_opts": {
341 "rw": 'trimwrite',
342 "bs": 4096,
343 "number_ios": 10,
344 "fsync": 1,
345 "output-format": "json",
346 },
347 "test_class": FlushTest,
348 },
cebaf30e 349]
b03ed937 350
cebaf30e
VF
351def parse_args():
352 """Parse command-line arguments."""
b03ed937 353
cebaf30e
VF
354 parser = argparse.ArgumentParser()
355 parser.add_argument('-f', '--fio', help='path to file executable (e.g., ./fio)')
356 parser.add_argument('-a', '--artifact-root', help='artifact root directory')
357 parser.add_argument('-s', '--skip', nargs='+', type=int,
358 help='list of test(s) to skip')
359 parser.add_argument('-o', '--run-only', nargs='+', type=int,
360 help='list of test(s) to run, skipping all others')
361 parser.add_argument('--dut', help='target NVMe character device to test '
362 '(e.g., /dev/ng0n1). WARNING: THIS IS A DESTRUCTIVE TEST', required=True)
363 args = parser.parse_args()
364
365 return args
366
367
368def main():
369 """Run tests using fio's io_uring_cmd ioengine to send NVMe pass through commands."""
370
371 args = parse_args()
372
373 artifact_root = args.artifact_root if args.artifact_root else \
374 f"nvmept-test-{time.strftime('%Y%m%d-%H%M%S')}"
375 os.mkdir(artifact_root)
376 print(f"Artifact directory is {artifact_root}")
377
378 if args.fio:
379 fio_path = str(Path(args.fio).absolute())
380 else:
381 fio_path = 'fio'
382 print(f"fio path is {fio_path}")
b03ed937 383
cebaf30e
VF
384 for test in TEST_LIST:
385 test['fio_opts']['filename'] = args.dut
b03ed937 386
cebaf30e
VF
387 test_env = {
388 'fio_path': fio_path,
389 'fio_root': str(Path(__file__).absolute().parent.parent),
390 'artifact_root': artifact_root,
df50839f 391 'basename': 'nvmept',
cebaf30e 392 }
b03ed937 393
cebaf30e 394 _, failed, _ = run_fio_tests(TEST_LIST, test_env, args)
b03ed937
VF
395 sys.exit(failed)
396
397
398if __name__ == '__main__':
399 main()