Commit | Line | Data |
---|---|---|
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 | """ | |
27 | import os | |
28 | import sys | |
29 | import json | |
30 | import time | |
31 | import locale | |
32 | import logging | |
33 | import argparse | |
34 | import subprocess | |
35 | from pathlib import Path | |
36 | from fiotestlib import FioJobCmdTest, run_fio_tests | |
37 | from fiotestcommon import SUCCESS_NONZERO | |
38 | ||
39 | ||
40 | class 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 | ||
124 | class 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 | |
302 | class 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 | ||
329 | class 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 | ||
345 | def 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 | ||
363 | def 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 | ||
382 | def 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 | ||
395 | def 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 | ||
413 | TEST_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 | ||
1008 | def 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 | ||
1026 | FIO_FDP_MAX_RUAMW = 0 | |
1027 | FIO_FDP_NUMBER_PLIDS = 0 | |
1028 | ||
1029 | def 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 | ||
1073 | if __name__ == '__main__': | |
1074 | main() |