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