smalloc: add a comment explaining why scalloc does not zero memory
[fio.git] / t / nvmept_fdp.py
CommitLineData
ece3a998
VF
1#!/usr/bin/env python3
2#
3# Copyright 2024 Samsung Electronics Co., Ltd All Rights Reserved
4#
5# For conditions of distribution and use, see the accompanying COPYING file.
6#
7"""
8# nvmept_fdp.py
9#
10# Test fio's io_uring_cmd ioengine with NVMe pass-through FDP write commands.
11#
12# USAGE
13# see python3 nvmept_fdp.py --help
14#
15# EXAMPLES
16# python3 t/nvmept_fdp.py --dut /dev/ng0n1
17# python3 t/nvmept_fdp.py --dut /dev/ng1n1 -f ./fio
18#
19# REQUIREMENTS
20# Python 3.6
21# Device formatted with LBA data size 4096 bytes
22# Device with at least five placement IDs
23#
24# WARNING
25# This is a destructive test
26"""
27import os
28import sys
29import json
30import time
31import locale
32import logging
33import argparse
34import subprocess
35from pathlib import Path
36from fiotestlib import FioJobCmdTest, run_fio_tests
37from fiotestcommon import SUCCESS_NONZERO
38
39
40class FDPTest(FioJobCmdTest):
41 """
42 NVMe pass-through test class. Check to make sure output for selected data
43 direction(s) is non-zero and that zero data appears for other directions.
44 """
45
46 def setup(self, parameters):
47 """Setup a test."""
48
49 fio_args = [
50 "--name=nvmept-fdp",
51 "--ioengine=io_uring_cmd",
52 "--cmd_type=nvme",
53 "--randrepeat=0",
54 f"--filename={self.fio_opts['filename']}",
55 f"--rw={self.fio_opts['rw']}",
56 f"--output={self.filenames['output']}",
57 f"--output-format={self.fio_opts['output-format']}",
58 ]
96566b09 59
ece3a998
VF
60 for opt in ['fixedbufs', 'nonvectored', 'force_async', 'registerfiles',
61 'sqthread_poll', 'sqthread_poll_cpu', 'hipri', 'nowait',
62 'time_based', 'runtime', 'verify', 'io_size', 'num_range',
63 'iodepth', 'iodepth_batch', 'iodepth_batch_complete',
64 'size', 'rate', 'bs', 'bssplit', 'bsrange', 'randrepeat',
65 'buffer_pattern', 'verify_pattern', 'offset', 'fdp',
66 'fdp_pli', 'fdp_pli_select', 'dataplacement', 'plid_select',
e6a96fa8 67 'plids', 'dp_scheme', 'number_ios', 'read_iolog']:
ece3a998
VF
68 if opt in self.fio_opts:
69 option = f"--{opt}={self.fio_opts[opt]}"
70 fio_args.append(option)
71
72 super().setup(fio_args)
73
74
75 def check_result(self):
76 try:
77 self._check_result()
78 finally:
79 if not update_all_ruhs(self.fio_opts['filename']):
80 logging.error("Could not reset device")
81 if not check_all_ruhs(self.fio_opts['filename']):
82 logging.error("Reclaim units have inconsistent RUAMW values")
83
84
85 def _check_result(self):
86
87 super().check_result()
88
89 if 'rw' not in self.fio_opts or \
90 not self.passed or \
91 'json' not in self.fio_opts['output-format']:
92 return
93
94 job = self.json_data['jobs'][0]
96566b09 95 rw_fio_opts = self.fio_opts['rw'].split(':')[0]
ece3a998 96
96566b09 97 if rw_fio_opts in ['read', 'randread']:
ece3a998 98 self.passed = self.check_all_ddirs(['read'], job)
96566b09 99 elif rw_fio_opts in ['write', 'randwrite']:
ece3a998
VF
100 if 'verify' not in self.fio_opts:
101 self.passed = self.check_all_ddirs(['write'], job)
102 else:
103 self.passed = self.check_all_ddirs(['read', 'write'], job)
96566b09 104 elif rw_fio_opts in ['trim', 'randtrim']:
ece3a998 105 self.passed = self.check_all_ddirs(['trim'], job)
96566b09 106 elif rw_fio_opts in ['readwrite', 'randrw']:
ece3a998 107 self.passed = self.check_all_ddirs(['read', 'write'], job)
96566b09 108 elif rw_fio_opts in ['trimwrite', 'randtrimwrite']:
ece3a998
VF
109 self.passed = self.check_all_ddirs(['trim', 'write'], job)
110 else:
111 logging.error("Unhandled rw value %s", self.fio_opts['rw'])
112 self.passed = False
113
114 if 'iodepth' in self.fio_opts:
115 # We will need to figure something out if any test uses an iodepth
116 # different from 8
117 if job['iodepth_level']['8'] < 95:
118 logging.error("Did not achieve requested iodepth")
119 self.passed = False
120 else:
121 logging.debug("iodepth 8 target met %s", job['iodepth_level']['8'])
122
123
124class FDPMultiplePLIDTest(FDPTest):
125 """
126 Write to multiple placement IDs.
127 """
128
129 def setup(self, parameters):
130 mapping = {
131 'nruhsd': FIO_FDP_NUMBER_PLIDS,
132 'max_ruamw': FIO_FDP_MAX_RUAMW,
95db41f1 133 'maxplid': FIO_FDP_NUMBER_PLIDS-1,
96566b09
HP
134 # parameters for 400, 401 tests
135 'hole_size': 64*1024,
136 'nios_for_scheme': FIO_FDP_NUMBER_PLIDS//2,
ece3a998
VF
137 }
138 if 'number_ios' in self.fio_opts and isinstance(self.fio_opts['number_ios'], str):
139 self.fio_opts['number_ios'] = eval(self.fio_opts['number_ios'].format(**mapping))
96566b09
HP
140 if 'bs' in self.fio_opts and isinstance(self.fio_opts['bs'], str):
141 self.fio_opts['bs'] = eval(self.fio_opts['bs'].format(**mapping))
142 if 'rw' in self.fio_opts and isinstance(self.fio_opts['rw'], str):
143 self.fio_opts['rw'] = self.fio_opts['rw'].format(**mapping)
95db41f1
VF
144 if 'plids' in self.fio_opts and isinstance(self.fio_opts['plids'], str):
145 self.fio_opts['plids'] = self.fio_opts['plids'].format(**mapping)
146 if 'fdp_pli' in self.fio_opts and isinstance(self.fio_opts['fdp_pli'], str):
147 self.fio_opts['fdp_pli'] = self.fio_opts['fdp_pli'].format(**mapping)
ece3a998
VF
148
149 super().setup(parameters)
96566b09
HP
150
151 if 'dp_scheme' in self.fio_opts:
152 scheme_path = os.path.join(self.paths['test_dir'], self.fio_opts['dp_scheme'])
153 with open(scheme_path, mode='w') as f:
154 for i in range(mapping['nios_for_scheme']):
155 f.write(f'{mapping["hole_size"] * 2 * i}, {mapping["hole_size"] * 2 * (i+1)}, {i}\n')
e6a96fa8
HP
156
157 if 'read_iolog' in self.fio_opts:
158 read_iolog_path = os.path.join(self.paths['test_dir'], self.fio_opts['read_iolog'])
159 with open(read_iolog_path, mode='w') as f:
160 f.write('fio version 2 iolog\n')
161 f.write(f'{self.fio_opts["filename"]} add\n')
162 f.write(f'{self.fio_opts["filename"]} open\n')
163
164 for i in range(mapping['nios_for_scheme']):
165 f.write(f'{self.fio_opts["filename"]} write {mapping["hole_size"] * 2 * i} {mapping["hole_size"]}\n')
166
167 f.write(f'{self.fio_opts["filename"]} close')
96566b09 168
ece3a998
VF
169 def _check_result(self):
170 if 'fdp_pli' in self.fio_opts:
171 plid_list = self.fio_opts['fdp_pli'].split(',')
172 elif 'plids' in self.fio_opts:
173 plid_list = self.fio_opts['plids'].split(',')
174 else:
95db41f1 175 plid_list = [str(i) for i in range(FIO_FDP_NUMBER_PLIDS)]
ece3a998 176
95db41f1
VF
177 range_ids = []
178 for plid in plid_list:
179 if '-' in plid:
180 [start, end] = plid.split('-')
181 range_ids.extend(list(range(int(start), int(end)+1)))
182 else:
183 range_ids.append(int(plid))
184
185 plid_list = sorted(range_ids)
ece3a998
VF
186 logging.debug("plid_list: %s", str(plid_list))
187
188 fdp_status = get_fdp_status(self.fio_opts['filename'])
189
190 select = "roundrobin"
191 if 'fdp_pli_select' in self.fio_opts:
192 select = self.fio_opts['fdp_pli_select']
193 elif 'plid_select' in self.fio_opts:
194 select = self.fio_opts['plid_select']
195
196 if select == "roundrobin":
197 self._check_robin(plid_list, fdp_status)
198 elif select == "random":
199 self._check_random(plid_list, fdp_status)
96566b09
HP
200 elif select == "scheme":
201 self._check_scheme(plid_list, fdp_status)
ece3a998
VF
202 else:
203 logging.error("Unknown plid selection strategy %s", select)
204 self.passed = False
96566b09 205
ece3a998
VF
206 super()._check_result()
207
208 def _check_robin(self, plid_list, fdp_status):
209 """
210 With round robin we can know exactly how many writes each PLID will
211 receive.
212 """
213 ruamw = [FIO_FDP_MAX_RUAMW] * FIO_FDP_NUMBER_PLIDS
214
215 remainder = int(self.fio_opts['number_ios'] % len(plid_list))
216 whole = int((self.fio_opts['number_ios'] - remainder) / len(plid_list))
217 logging.debug("PLIDs in the list should receive %d writes; %d PLIDs will receive one extra",
218 whole, remainder)
219
220 for plid in plid_list:
221 ruamw[plid] -= whole
222 if remainder:
223 ruamw[plid] -= 1
224 remainder -= 1
225 logging.debug("Expected ruamw values: %s", str(ruamw))
226
227 for idx, ruhs in enumerate(fdp_status['ruhss']):
228 if ruhs['ruamw'] != ruamw[idx]:
229 logging.error("RUAMW mismatch with idx %d, pid %d, expected %d, observed %d", idx,
230 ruhs['pid'], ruamw[idx], ruhs['ruamw'])
231 self.passed = False
232 break
233
234 logging.debug("RUAMW match with idx %d, pid %d: ruamw=%d", idx, ruhs['pid'], ruamw[idx])
235
236 def _check_random(self, plid_list, fdp_status):
237 """
238 With random selection, a set of PLIDs will receive all the write
239 operations and the remainder will be untouched.
240 """
241
242 total_ruamw = 0
243 for plid in plid_list:
244 total_ruamw += fdp_status['ruhss'][plid]['ruamw']
245
246 expected = len(plid_list) * FIO_FDP_MAX_RUAMW - self.fio_opts['number_ios']
247 if total_ruamw != expected:
248 logging.error("Expected total ruamw %d for plids %s, observed %d", expected,
249 str(plid_list), total_ruamw)
250 self.passed = False
251 else:
252 logging.debug("Observed expected total ruamw %d for plids %s", expected, str(plid_list))
253
254 for idx, ruhs in enumerate(fdp_status['ruhss']):
255 if idx in plid_list:
256 continue
257 if ruhs['ruamw'] != FIO_FDP_MAX_RUAMW:
258 logging.error("Unexpected ruamw %d for idx %d, pid %d, expected %d", ruhs['ruamw'],
259 idx, ruhs['pid'], FIO_FDP_MAX_RUAMW)
260 self.passed = False
261 else:
262 logging.debug("Observed expected ruamw %d for idx %d, pid %d", ruhs['ruamw'], idx,
263 ruhs['pid'])
264
96566b09
HP
265 def _check_scheme(self, plid_list, fdp_status):
266 """
267 With scheme selection, a set of PLIDs touched by the scheme
268 """
269
270 PLID_IDX_POS = 2
271 plid_list_from_scheme = set()
272
273 scheme_path = os.path.join(self.paths['test_dir'], self.fio_opts['dp_scheme'])
274
275 with open(scheme_path) as f:
276 lines = f.readlines()
277 for line in lines:
278 line_elem = line.strip().replace(' ', '').split(',')
279 plid_list_from_scheme.add(int(line_elem[PLID_IDX_POS]))
280
281 logging.debug(f'plid_list_from_scheme: {plid_list_from_scheme}')
282
283 for idx, ruhs in enumerate(fdp_status['ruhss']):
284 if ruhs['pid'] in plid_list_from_scheme:
285 if ruhs['ruamw'] == FIO_FDP_MAX_RUAMW:
286 logging.error("pid %d should be touched by the scheme. But ruamw of it(%d) equals to %d",
287 ruhs['pid'], ruhs['ruamw'], FIO_FDP_MAX_RUAMW)
288 self.passed = False
289 else:
290 logging.debug("pid %d should be touched by the scheme. ruamw of it(%d) is under %d",
291 ruhs['pid'], ruhs['ruamw'], FIO_FDP_MAX_RUAMW)
292 else:
293 if ruhs['ruamw'] == FIO_FDP_MAX_RUAMW:
294 logging.debug("pid %d should not be touched by the scheme. ruamw of it(%d) equals to %d",
295 ruhs['pid'], ruhs['ruamw'], FIO_FDP_MAX_RUAMW)
296 else:
297 logging.error("pid %d should not be touched by the scheme. But ruamw of it(%d) is under %d",
298 ruhs['pid'], ruhs['ruamw'], FIO_FDP_MAX_RUAMW)
299 self.passed = False
300
ece3a998
VF
301
302class FDPSinglePLIDTest(FDPTest):
303 """
304 Write to a single placement ID only.
305 """
306
307 def _check_result(self):
308 if 'plids' in self.fio_opts:
309 plid = self.fio_opts['plids']
310 elif 'fdp_pli' in self.fio_opts:
311 plid = self.fio_opts['fdp_pli']
312 else:
313 plid = 0
314
315 fdp_status = get_fdp_status(self.fio_opts['filename'])
316 ruamw = fdp_status['ruhss'][plid]['ruamw']
317 lba_count = self.fio_opts['number_ios']
318
319 if FIO_FDP_MAX_RUAMW - lba_count != ruamw:
320 logging.error("FDP accounting mismatch for plid %d; expected ruamw %d, observed %d",
321 plid, FIO_FDP_MAX_RUAMW - lba_count, ruamw)
322 self.passed = False
323 else:
324 logging.debug("FDP accounting as expected for plid %d; ruamw = %d", plid, ruamw)
325
326 super()._check_result()
327
328
329class FDPReadTest(FDPTest):
330 """
331 Read workload test.
332 """
333
334 def _check_result(self):
335 ruamw = check_all_ruhs(self.fio_opts['filename'])
336
337 if ruamw != FIO_FDP_MAX_RUAMW:
338 logging.error("Read workload affected FDP ruamw")
339 self.passed = False
340 else:
341 logging.debug("Read workload did not disturb FDP ruamw")
342 super()._check_result()
343
344
345def get_fdp_status(dut):
346 """
347 Run the nvme-cli command to obtain FDP status and return result as a JSON
348 object.
349 """
350
351 cmd = f"sudo nvme fdp status --output-format=json {dut}"
352 cmd = cmd.split(' ')
353 cmd_result = subprocess.run(cmd, capture_output=True, check=False,
354 encoding=locale.getpreferredencoding())
355
356 if cmd_result.returncode != 0:
357 logging.error("Error obtaining device %s FDP status: %s", dut, cmd_result.stderr)
358 return False
359
360 return json.loads(cmd_result.stdout)
361
362
363def update_ruh(dut, plid):
364 """
365 Update reclaim unit handles with specified ID(s). This tells the device to
366 point the RUH to a new (empty) reclaim unit.
367 """
368
369 ids = ','.join(plid) if isinstance(plid, list) else plid
370 cmd = f"nvme fdp update --pids={ids} {dut}"
371 cmd = cmd.split(' ')
372 cmd_result = subprocess.run(cmd, capture_output=True, check=False,
373 encoding=locale.getpreferredencoding())
374
375 if cmd_result.returncode != 0:
376 logging.error("Error updating RUH %s ID(s) %s", dut, ids)
377 return False
378
379 return True
380
381
382def update_all_ruhs(dut):
383 """
384 Update all reclaim unit handles on the device.
385 """
386
387 fdp_status = get_fdp_status(dut)
388 for ruhs in fdp_status['ruhss']:
389 if not update_ruh(dut, ruhs['pid']):
390 return False
391
392 return True
393
394
395def check_all_ruhs(dut):
396 """
397 Check that all RUHs have the same value for reclaim unit available media
398 writes (RUAMW). Return the RUAMW value.
399 """
400
401 fdp_status = get_fdp_status(dut)
402 ruh_status = fdp_status['ruhss']
403
404 ruamw = ruh_status[0]['ruamw']
405 for ruhs in ruh_status:
406 if ruhs['ruamw'] != ruamw:
407 logging.error("RUAMW mismatch: found %d, expected %d", ruhs['ruamw'], ruamw)
408 return False
409
410 return ruamw
411
412
413TEST_LIST = [
414 # Write one LBA to one PLID using both the old and new sets of options
415 ## omit fdp_pli_select/plid_select
416 {
417 "test_id": 1,
418 "fio_opts": {
419 "rw": 'write',
420 "bs": 4096,
421 "number_ios": 1,
422 "verify": "crc32c",
423 "fdp": 1,
424 "fdp_pli": 3,
425 "output-format": "json",
426 },
427 "test_class": FDPSinglePLIDTest,
428 },
429 {
430 "test_id": 2,
431 "fio_opts": {
432 "rw": 'randwrite',
433 "bs": 4096,
434 "number_ios": 1,
435 "verify": "crc32c",
436 "dataplacement": "fdp",
437 "plids": 3,
438 "output-format": "json",
439 },
440 "test_class": FDPSinglePLIDTest,
441 },
442 ## fdp_pli_select/plid_select=roundrobin
443 {
444 "test_id": 3,
445 "fio_opts": {
446 "rw": 'write',
447 "bs": 4096,
448 "number_ios": 1,
449 "verify": "crc32c",
450 "fdp": 1,
451 "fdp_pli": 3,
452 "fdp_pli_select": "roundrobin",
453 "output-format": "json",
454 },
455 "test_class": FDPSinglePLIDTest,
456 },
457 {
458 "test_id": 4,
459 "fio_opts": {
460 "rw": 'randwrite',
461 "bs": 4096,
462 "number_ios": 1,
463 "verify": "crc32c",
464 "dataplacement": "fdp",
465 "plids": 3,
466 "plid_select": "roundrobin",
467 "output-format": "json",
468 },
469 "test_class": FDPSinglePLIDTest,
470 },
471 ## fdp_pli_select/plid_select=random
472 {
473 "test_id": 5,
474 "fio_opts": {
475 "rw": 'write',
476 "bs": 4096,
477 "number_ios": 1,
478 "verify": "crc32c",
479 "fdp": 1,
480 "fdp_pli": 3,
481 "fdp_pli_select": "random",
482 "output-format": "json",
483 },
484 "test_class": FDPSinglePLIDTest,
485 },
486 {
487 "test_id": 6,
488 "fio_opts": {
489 "rw": 'randwrite',
490 "bs": 4096,
491 "number_ios": 1,
492 "verify": "crc32c",
493 "dataplacement": "fdp",
494 "plids": 3,
495 "plid_select": "random",
496 "output-format": "json",
497 },
498 "test_class": FDPSinglePLIDTest,
499 },
500 # Write four LBAs to one PLID using both the old and new sets of options
501 ## omit fdp_pli_select/plid_select
502 {
503 "test_id": 7,
504 "fio_opts": {
505 "rw": 'write',
506 "bs": 4096,
507 "number_ios": 4,
508 "verify": "crc32c",
509 "fdp": 1,
510 "fdp_pli": 1,
511 "output-format": "json",
512 },
513 "test_class": FDPSinglePLIDTest,
514 },
515 {
516 "test_id": 8,
517 "fio_opts": {
518 "rw": 'randwrite',
519 "bs": 4096,
520 "number_ios": 4,
521 "verify": "crc32c",
522 "dataplacement": "fdp",
523 "plids": 1,
524 "output-format": "json",
525 },
526 "test_class": FDPSinglePLIDTest,
527 },
528 ## fdp_pli_select/plid_select=roundrobin
529 {
530 "test_id": 9,
531 "fio_opts": {
532 "rw": 'write',
533 "bs": 4096,
534 "number_ios": 4,
535 "verify": "crc32c",
536 "fdp": 1,
537 "fdp_pli": 1,
538 "fdp_pli_select": "roundrobin",
539 "output-format": "json",
540 },
541 "test_class": FDPSinglePLIDTest,
542 },
543 {
544 "test_id": 10,
545 "fio_opts": {
546 "rw": 'randwrite',
547 "bs": 4096,
548 "number_ios": 4,
549 "verify": "crc32c",
550 "dataplacement": "fdp",
551 "plids": 1,
552 "plid_select": "roundrobin",
553 "output-format": "json",
554 },
555 "test_class": FDPSinglePLIDTest,
556 },
557 ## fdp_pli_select/plid_select=random
558 {
559 "test_id": 11,
560 "fio_opts": {
561 "rw": 'write',
562 "bs": 4096,
563 "number_ios": 4,
564 "verify": "crc32c",
565 "fdp": 1,
566 "fdp_pli": 1,
567 "fdp_pli_select": "random",
568 "output-format": "json",
569 },
570 "test_class": FDPSinglePLIDTest,
571 },
572 {
573 "test_id": 12,
574 "fio_opts": {
575 "rw": 'randwrite',
576 "bs": 4096,
577 "number_ios": 4,
578 "verify": "crc32c",
579 "dataplacement": "fdp",
580 "plids": 1,
581 "plid_select": "random",
582 "output-format": "json",
583 },
584 "test_class": FDPSinglePLIDTest,
585 },
586 # Just a regular write without FDP directive--should land on plid 0
587 {
588 "test_id": 13,
589 "fio_opts": {
590 "rw": 'randwrite',
591 "bs": 4096,
592 "number_ios": 19,
593 "verify": "crc32c",
594 "output-format": "json",
595 },
596 "test_class": FDPSinglePLIDTest,
597 },
598 # Read workload
599 {
600 "test_id": 14,
601 "fio_opts": {
602 "rw": 'randread',
603 "bs": 4096,
604 "number_ios": 19,
605 "output-format": "json",
606 },
607 "test_class": FDPReadTest,
608 },
609 # write to multiple PLIDs using round robin to select PLIDs
610 ## write to all PLIDs using old and new sets of options
611 {
612 "test_id": 100,
613 "fio_opts": {
614 "rw": 'randwrite',
615 "bs": 4096,
616 "number_ios": "2*{nruhsd}+3",
617 "verify": "crc32c",
618 "fdp": 1,
619 "fdp_pli_select": "roundrobin",
620 "output-format": "json",
621 },
622 "test_class": FDPMultiplePLIDTest,
623 },
624 {
625 "test_id": 101,
626 "fio_opts": {
627 "rw": 'randwrite',
628 "bs": 4096,
629 "number_ios": "2*{nruhsd}+3",
630 "verify": "crc32c",
631 "dataplacement": "fdp",
632 "plid_select": "roundrobin",
633 "output-format": "json",
634 },
635 "test_class": FDPMultiplePLIDTest,
636 },
637 ## write to a subset of PLIDs using old and new sets of options
638 {
639 "test_id": 102,
640 "fio_opts": {
641 "rw": 'randwrite',
642 "bs": 4096,
643 "number_ios": "{nruhsd}+1",
644 "verify": "crc32c",
645 "fdp": 1,
646 "fdp_pli": "1,3",
647 "fdp_pli_select": "roundrobin",
648 "output-format": "json",
649 },
650 "test_class": FDPMultiplePLIDTest,
651 },
652 {
653 "test_id": 103,
654 "fio_opts": {
655 "rw": 'randwrite',
656 "bs": 4096,
657 "number_ios": "{nruhsd}+1",
658 "verify": "crc32c",
659 "dataplacement": "fdp",
660 "plids": "1,3",
661 "plid_select": "roundrobin",
662 "output-format": "json",
663 },
664 "test_class": FDPMultiplePLIDTest,
665 },
666 # write to multiple PLIDs using random selection of PLIDs
667 ## write to all PLIDs using old and new sets of options
668 {
669 "test_id": 200,
670 "fio_opts": {
671 "rw": 'randwrite',
672 "bs": 4096,
673 "number_ios": "{max_ruamw}-1",
674 "verify": "crc32c",
675 "fdp": 1,
676 "fdp_pli_select": "random",
677 "output-format": "json",
678 },
679 "test_class": FDPMultiplePLIDTest,
680 },
681 {
682 "test_id": 201,
683 "fio_opts": {
684 "rw": 'randwrite',
685 "bs": 4096,
686 "number_ios": "{max_ruamw}-1",
687 "verify": "crc32c",
688 "dataplacement": "fdp",
689 "plid_select": "random",
690 "output-format": "json",
691 },
692 "test_class": FDPMultiplePLIDTest,
693 },
694 ## write to a subset of PLIDs using old and new sets of options
695 {
696 "test_id": 202,
697 "fio_opts": {
698 "rw": 'randwrite',
699 "bs": 4096,
700 "number_ios": "{max_ruamw}-1",
701 "verify": "crc32c",
702 "fdp": 1,
703 "fdp_pli": "1,3,4",
704 "fdp_pli_select": "random",
705 "output-format": "json",
706 },
707 "test_class": FDPMultiplePLIDTest,
708 },
709 {
710 "test_id": 203,
711 "fio_opts": {
712 "rw": 'randwrite',
713 "bs": 4096,
714 "number_ios": "{max_ruamw}-1",
715 "verify": "crc32c",
716 "dataplacement": "fdp",
717 "plids": "1,3,4",
718 "plid_select": "random",
719 "output-format": "json",
720 },
721 "test_class": FDPMultiplePLIDTest,
722 },
95db41f1
VF
723 ### use 3-4 to specify plids
724 {
725 "test_id": 204,
726 "fio_opts": {
727 "rw": 'randwrite',
728 "bs": 4096,
729 "number_ios": "{max_ruamw}-1",
730 "verify": "crc32c",
731 "fdp": 1,
732 "fdp_pli": "1,3-4",
733 "fdp_pli_select": "random",
734 "output-format": "json",
735 },
736 "test_class": FDPMultiplePLIDTest,
737 },
738 {
739 "test_id": 205,
740 "fio_opts": {
741 "rw": 'randwrite',
742 "bs": 4096,
743 "number_ios": "{max_ruamw}-1",
744 "verify": "crc32c",
745 "dataplacement": "fdp",
746 "plids": "1,3-4",
747 "plid_select": "random",
748 "output-format": "json",
749 },
750 "test_class": FDPMultiplePLIDTest,
751 },
752 ### use 1-3 to specify plids
753 {
754 "test_id": 206,
755 "fio_opts": {
756 "rw": 'randwrite',
757 "bs": 4096,
758 "number_ios": "{max_ruamw}-1",
759 "verify": "crc32c",
760 "fdp": 1,
761 "fdp_pli": "1-3",
762 "fdp_pli_select": "random",
763 "output-format": "json",
764 },
765 "test_class": FDPMultiplePLIDTest,
766 },
767 {
768 "test_id": 207,
769 "fio_opts": {
770 "rw": 'randwrite',
771 "bs": 4096,
772 "number_ios": "{max_ruamw}-1",
773 "verify": "crc32c",
774 "dataplacement": "fdp",
775 "plids": "1-3",
776 "plid_select": "random",
777 "output-format": "json",
778 },
779 "test_class": FDPMultiplePLIDTest,
780 },
781 ### use multiple ranges to specify plids
782 {
783 "test_id": 208,
784 "fio_opts": {
785 "rw": 'randwrite',
786 "bs": 4096,
787 "number_ios": "{max_ruamw}-1",
788 "verify": "crc32c",
789 "fdp": 1,
790 "fdp_pli": "1-2,3-3",
791 "fdp_pli_select": "random",
792 "output-format": "json",
793 },
794 "test_class": FDPMultiplePLIDTest,
795 },
796 {
797 "test_id": 209,
798 "fio_opts": {
799 "rw": 'randwrite',
800 "bs": 4096,
801 "number_ios": "{max_ruamw}-1",
802 "verify": "crc32c",
803 "dataplacement": "fdp",
804 "plids": "1-2,3-3",
805 "plid_select": "random",
806 "output-format": "json",
807 },
808 "test_class": FDPMultiplePLIDTest,
809 },
810 {
811 "test_id": 210,
812 "fio_opts": {
813 "rw": 'randwrite',
814 "bs": 4096,
815 "number_ios": "{max_ruamw}-1",
816 "verify": "crc32c",
817 "fdp": 1,
818 "fdp_pli": "0-{maxplid}",
819 "fdp_pli_select": "random",
820 "output-format": "json",
821 },
822 "test_class": FDPMultiplePLIDTest,
823 },
824 {
825 "test_id": 211,
826 "fio_opts": {
827 "rw": 'randwrite',
828 "bs": 4096,
829 "number_ios": "{max_ruamw}-1",
830 "verify": "crc32c",
831 "dataplacement": "fdp",
832 "fdp_pli": "0-{maxplid}",
833 "plid_select": "random",
834 "output-format": "json",
835 },
836 "test_class": FDPMultiplePLIDTest,
837 },
ece3a998
VF
838 # Specify invalid options fdp=1 and dataplacement=none
839 {
840 "test_id": 300,
841 "fio_opts": {
842 "rw": 'write',
843 "bs": 4096,
844 "io_size": 4096,
845 "verify": "crc32c",
846 "fdp": 1,
847 "fdp_pli": 3,
848 "output-format": "normal",
849 "dataplacement": "none",
850 },
851 "test_class": FDPTest,
852 "success": SUCCESS_NONZERO,
853 },
854 # Specify invalid options fdp=1 and dataplacement=streams
855 {
856 "test_id": 301,
857 "fio_opts": {
858 "rw": 'write',
859 "bs": 4096,
860 "io_size": 4096,
861 "verify": "crc32c",
862 "fdp": 1,
863 "fdp_pli": 3,
864 "output-format": "normal",
865 "dataplacement": "streams",
866 },
867 "test_class": FDPTest,
868 "success": SUCCESS_NONZERO,
869 },
96566b09
HP
870 # Specify invalid options related to dataplacement scheme
871 ## using old and new sets of options
872 {
873 "test_id": 302,
874 "fio_opts": {
875 "rw": 'write',
876 "bs": 4096,
877 "io_size": 4096,
878 "verify": "crc32c",
879 "fdp": 1,
880 "fdp_pli": 3,
881 "fdp_pli_select": "scheme",
882 "output-format": "normal",
883 },
884 "test_class": FDPTest,
885 "success": SUCCESS_NONZERO,
886 },
887 {
888 "test_id": 303,
889 "fio_opts": {
890 "rw": 'write',
891 "bs": 4096,
892 "io_size": 4096,
893 "verify": "crc32c",
894 "dataplacement": "fdp",
895 "plids": 3,
896 "plid_select": "scheme",
897 "output-format": "normal",
898 },
899 "test_class": FDPTest,
900 "success": SUCCESS_NONZERO,
901 },
95db41f1
VF
902 ## Specify invalid ranges with start > end
903 {
904 "test_id": 304,
905 "fio_opts": {
906 "rw": 'write',
907 "bs": 4096,
908 "io_size": 4096,
909 "verify": "crc32c",
910 "fdp": 1,
911 "plids": "3-1",
912 "output-format": "normal",
913 },
914 "test_class": FDPTest,
915 "success": SUCCESS_NONZERO,
916 },
917 {
918 "test_id": 305,
919 "fio_opts": {
920 "rw": 'write',
921 "bs": 4096,
922 "io_size": 4096,
923 "verify": "crc32c",
924 "fdp": 1,
925 "fdp_pli": "3-1",
926 "output-format": "normal",
927 },
928 "test_class": FDPTest,
929 "success": SUCCESS_NONZERO,
930 },
931 ## Specify too many plids
932 {
933 "test_id": 306,
934 "fio_opts": {
935 "rw": 'write',
936 "bs": 4096,
937 "io_size": 4096,
938 "verify": "crc32c",
939 "fdp": 1,
940 "plids": "0-65535",
941 "output-format": "normal",
942 },
943 "test_class": FDPTest,
944 "success": SUCCESS_NONZERO,
945 },
946 {
947 "test_id": 307,
948 "fio_opts": {
949 "rw": 'write',
950 "bs": 4096,
951 "io_size": 4096,
952 "verify": "crc32c",
953 "fdp": 1,
954 "fdp_pli": "0-65535",
955 "output-format": "normal",
956 },
957 "test_class": FDPTest,
958 "success": SUCCESS_NONZERO,
959 },
96566b09
HP
960 # write to multiple PLIDs using scheme selection of PLIDs
961 ## using old and new sets of options
962 {
963 "test_id": 400,
964 "fio_opts": {
965 "rw": "write:{hole_size}",
966 "bs": "{hole_size}",
967 "number_ios": "{nios_for_scheme}",
968 "verify": "crc32c",
969 "fdp": 1,
970 "fdp_pli_select": "scheme",
971 "dp_scheme": "lba.scheme",
972 "output-format": "json",
973 },
974 "test_class": FDPMultiplePLIDTest,
975 },
976 {
977 "test_id": 401,
978 "fio_opts": {
979 "rw": "write:{hole_size}",
980 "bs": "{hole_size}",
981 "number_ios": "{nios_for_scheme}",
982 "verify": "crc32c",
983 "dataplacement": "fdp",
984 "plid_select": "scheme",
985 "dp_scheme": "lba.scheme",
986 "output-format": "json",
987 },
988 "test_class": FDPMultiplePLIDTest,
989 },
e6a96fa8
HP
990 # check whether dataplacement works while replaying iologs
991 {
992 "test_id": 402,
993 "fio_opts": {
994 "rw": "write:{hole_size}",
995 "bs": "{hole_size}",
996 "number_ios": "{nios_for_scheme}",
997 "verify": "crc32c",
998 "read_iolog": "iolog",
999 "dataplacement": "fdp",
1000 "plid_select": "scheme",
1001 "dp_scheme": "lba.scheme",
1002 "output-format": "json",
1003 },
1004 "test_class": FDPMultiplePLIDTest,
1005 },
ece3a998
VF
1006]
1007
1008def parse_args():
1009 """Parse command-line arguments."""
1010
1011 parser = argparse.ArgumentParser()
1012 parser.add_argument('-d', '--debug', help='Enable debug messages', action='store_true')
1013 parser.add_argument('-f', '--fio', help='path to file executable (e.g., ./fio)')
1014 parser.add_argument('-a', '--artifact-root', help='artifact root directory')
1015 parser.add_argument('-s', '--skip', nargs='+', type=int,
1016 help='list of test(s) to skip')
1017 parser.add_argument('-o', '--run-only', nargs='+', type=int,
1018 help='list of test(s) to run, skipping all others')
1019 parser.add_argument('--dut', help='target NVMe character device to test '
1020 '(e.g., /dev/ng0n1). WARNING: THIS IS A DESTRUCTIVE TEST', required=True)
1021 args = parser.parse_args()
1022
1023 return args
1024
1025
1026FIO_FDP_MAX_RUAMW = 0
1027FIO_FDP_NUMBER_PLIDS = 0
1028
1029def main():
1030 """Run tests using fio's io_uring_cmd ioengine to send NVMe pass through commands."""
1031 global FIO_FDP_MAX_RUAMW
1032 global FIO_FDP_NUMBER_PLIDS
1033
1034 args = parse_args()
1035
1036 if args.debug:
1037 logging.basicConfig(level=logging.DEBUG)
1038 else:
1039 logging.basicConfig(level=logging.INFO)
1040
1041 artifact_root = args.artifact_root if args.artifact_root else \
1042 f"nvmept-fdp-test-{time.strftime('%Y%m%d-%H%M%S')}"
1043 os.mkdir(artifact_root)
1044 print(f"Artifact directory is {artifact_root}")
1045
1046 if args.fio:
1047 fio_path = str(Path(args.fio).absolute())
1048 else:
1049 fio_path = 'fio'
1050 print(f"fio path is {fio_path}")
1051
1052 for test in TEST_LIST:
1053 test['fio_opts']['filename'] = args.dut
1054
1055 fdp_status = get_fdp_status(args.dut)
1056 FIO_FDP_NUMBER_PLIDS = fdp_status['nruhsd']
1057 update_all_ruhs(args.dut)
1058 FIO_FDP_MAX_RUAMW = check_all_ruhs(args.dut)
1059 if not FIO_FDP_MAX_RUAMW:
1060 sys.exit(-1)
1061
1062 test_env = {
1063 'fio_path': fio_path,
1064 'fio_root': str(Path(__file__).absolute().parent.parent),
1065 'artifact_root': artifact_root,
1066 'basename': 'nvmept-fdp',
1067 }
1068
1069 _, failed, _ = run_fio_tests(TEST_LIST, test_env, args)
1070 sys.exit(failed)
1071
1072
1073if __name__ == '__main__':
1074 main()