Skip to content

Commit 901d3a3

Browse files
committed
tests: handle PI and MS settings
Teach the test framework about PI and MS. Signed-off-by: Daniel Wagner <wagi@kernel.org>
1 parent 74df8d2 commit 901d3a3

5 files changed

Lines changed: 165 additions & 2 deletions

File tree

tests/nvme_compare_test.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,19 @@ def setUp(self):
6060
super().setUp()
6161
if not self.compare_cmd_supported():
6262
self.skipTest("because: Optional NVM Command 'Compare' (NVMCMPS) not supported")
63-
self.data_size = 1024
6463
self.start_block = 1023
6564
self.setup_log_dir(self.__class__.__name__)
6665
self.compare_file = self.test_log_dir + "/" + "compare_file.txt"
6766
self.write_file = self.test_log_dir + "/" + self.write_file
6867
self.create_data_file(self.write_file, self.data_size, "15")
6968
self.create_data_file(self.compare_file, self.data_size, "25")
69+
self.compare_meta_file = None
70+
if self.ms > 0 and not self.ns_meta_ext:
71+
self.write_meta_file = self.test_log_dir + "/" + self.write_meta_file
72+
self.compare_meta_file = \
73+
self.test_log_dir + "/" + "compare_meta_file.bin"
74+
self.create_meta_file(self.write_meta_file, self.ms)
75+
self.create_meta_file(self.compare_meta_file, self.ms)
7076

7177
def tearDown(self):
7278
""" Post Section for TestNVMeCompareCmd """
@@ -83,6 +89,11 @@ def nvme_compare(self, cmp_file):
8389
f"--start-block={str(self.start_block)} " + \
8490
f"--block-count={str(self.block_count)} " + \
8591
f"--data-size={str(self.data_size)} --data={cmp_file}"
92+
if self.prinfo:
93+
compare_cmd += f" --prinfo={self.prinfo}"
94+
if self.compare_meta_file:
95+
compare_cmd += \
96+
f" --metadata-size={self.ms} --metadata={self.compare_meta_file}"
8697
return self.exec_cmd(compare_cmd)
8798

8899
def test_nvme_compare(self):

tests/nvme_read_write_test.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ def setUp(self):
5454
self.read_file = self.test_log_dir + "/" + self.read_file
5555
self.create_data_file(self.write_file, self.data_size, "15")
5656
open(self.read_file, 'a').close()
57+
if self.ms > 0 and not self.ns_meta_ext:
58+
self.write_meta_file = self.test_log_dir + "/" + self.write_meta_file
59+
self.read_meta_file = self.test_log_dir + "/" + self.read_meta_file
60+
self.create_meta_file(self.write_meta_file, self.ms)
5761

5862
def tearDown(self):
5963
""" Post Section for TestNVMeReadWriteTest """

tests/nvme_test.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ def setUp(self):
8080
self.do_validate_pci_device = True
8181
self.default_nsid = 0x1
8282
self.flbas = 0
83+
self.ns_dps = 0
84+
self.ns_meta_ext = False
85+
self.pif = 0
8386
self.config_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config.json')
8487

8588
self.load_config()
@@ -88,6 +91,11 @@ def setUp(self):
8891
self.ns_mgmt_supported = self.get_ns_mgmt_support()
8992
if self.ns_mgmt_supported:
9093
self.create_and_attach_default_ns()
94+
else:
95+
self.flbas = self._get_active_lbaf_index()
96+
self.ns_dps = self._get_ns_dps()
97+
self.ns_meta_ext = self._is_metadata_ext()
98+
self.pif = self._get_pif()
9199
logger.debug("setup: ctrl: %s, ns1: %s, default_nsid: %s, flbas: %s",
92100
self.ctrl, self.ns1, self.default_nsid, self.flbas)
93101

@@ -300,6 +308,77 @@ def get_lba_status_supported(self):
300308
"""
301309
return to_decimal(self.get_id_ctrl_field_value("oacs")) & (1 << 9)
302310

311+
def _get_active_lbaf_index(self):
312+
""" Return the index of the currently active LBA format for ns1.
313+
- Args:
314+
- None
315+
- Returns:
316+
- lbaf index (int) of the format whose in_use flag is set,
317+
or 0 if no in_use entry is found.
318+
"""
319+
nvme_id_ns_cmd = f"{self.nvme_bin} id-ns {self.ns1} " + \
320+
"--output-format=json"
321+
result = self.run_cmd(nvme_id_ns_cmd)
322+
self.assertEqual(result.returncode, 0, "ERROR : reading id-ns")
323+
json_output = json.loads(result.stdout)
324+
for lbaf in json_output.get('lbafs', []):
325+
if lbaf.get('in_use') == 1:
326+
return int(lbaf['lbaf'])
327+
return 0
328+
329+
def _get_ns_dps(self):
330+
""" Return the Data Protection Settings (DPS) field for ns1.
331+
- Args:
332+
- None
333+
- Returns:
334+
- dps value (int); bits 2:0 are the PI type (non-zero means
335+
end-to-end PI is enabled), bits 5:3 are the Protection
336+
Information Format (PIF) on NVMe 2.0+ devices.
337+
"""
338+
nvme_id_ns_cmd = f"{self.nvme_bin} id-ns {self.ns1} " + \
339+
"--output-format=json"
340+
result = self.run_cmd(nvme_id_ns_cmd)
341+
self.assertEqual(result.returncode, 0, "ERROR : reading id-ns")
342+
json_output = json.loads(result.stdout)
343+
return int(json_output.get('dps', 0))
344+
345+
def _get_pif(self):
346+
""" Return the Protection Information Format (PIF) for ns1.
347+
348+
The PIF is stored in bits 5:3 of the DPS field (NVMe 2.0+):
349+
PIF 0 - 8-byte PI, 16-bit CRC guard (Type 1/2/3, all NVMe 1.x)
350+
PIF 1 - 16-byte PI, 64-bit CRC guard
351+
PIF 2 - 8-byte PI, 32-bit CRC guard
352+
353+
NVMe 1.x devices always return 0 for these bits.
354+
355+
- Args:
356+
- None
357+
- Returns:
358+
- pif value (int, 0-7).
359+
"""
360+
nvme_id_ns_cmd = f"{self.nvme_bin} id-ns {self.ns1} " + \
361+
"--output-format=json"
362+
result = self.run_cmd(nvme_id_ns_cmd)
363+
self.assertEqual(result.returncode, 0, "ERROR : reading id-ns")
364+
json_output = json.loads(result.stdout)
365+
dps = int(json_output.get('dps', 0))
366+
return (dps >> 3) & 0x7
367+
368+
def _is_metadata_ext(self):
369+
""" Return True if the active LBA format uses extended LBA (bit 4 of
370+
the flbas field is set, meaning metadata is appended at the end of
371+
the data buffer). Return False if bit 4 is clear, meaning metadata
372+
is transferred as a separate, contiguous buffer.
373+
"""
374+
nvme_id_ns_cmd = f"{self.nvme_bin} id-ns {self.ns1} " + \
375+
"--output-format=json"
376+
result = self.run_cmd(nvme_id_ns_cmd)
377+
self.assertEqual(result.returncode, 0, "ERROR : reading id-ns")
378+
json_output = json.loads(result.stdout)
379+
flbas = int(json_output.get('flbas', 0))
380+
return bool(flbas & (1 << 4))
381+
303382
def get_lba_format_size(self):
304383
""" Wrapper for extracting lba format size of the given flbas
305384
- Args:

tests/nvme_test_io.py

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,54 @@ def setUp(self):
4343
""" Pre Section for TestNVMeIO """
4444
super().setUp()
4545
# common code used in various testcases.
46-
(self.data_size, _) = self.get_lba_format_size()
46+
(ds, ms) = self.get_lba_format_size()
47+
self.ms = ms
48+
# PI type occupies bits 2:0 of the DPS field; bits 5:3 are PIF.
49+
pi_type = self.ns_dps & 0x7
50+
if pi_type != 0 and ms != 0 and self.ns_meta_ext:
51+
# PI active + extended LBA (metadata appended to data buffer).
52+
# Use PRACT=1 (--prinfo=8) so the controller inserts and strips PI
53+
# automatically. With PRACT=1 the PI bytes are not transferred
54+
# over the host interface, so data_size equals the logical block
55+
# data size only (ds), not ds+ms. This works for all PI sizes
56+
# (8 bytes for PIF 0/2, 16 bytes for PIF 1) and all guard widths
57+
# (16-bit, 32-bit, 64-bit CRC) because the controller handles
58+
# the PI entirely.
59+
self.prinfo = 8
60+
self.data_size = ds
61+
elif pi_type != 0 and ms != 0 and not self.ns_meta_ext:
62+
# PI active + separate metadata (flbas bit 4 clear). PRACT=1
63+
# (--prinfo=8) is invalid for the Compare command on this format
64+
# (NVMe spec: PRACT=1 for Compare requires PI in the host data
65+
# buffer, which only applies to the extended-LBA layout). Use
66+
# prinfo=0 (PRACT=0, PRCHK=0) for all operations and supply an
67+
# explicit zero-filled metadata buffer of ms bytes so that the
68+
# stored metadata and the compared metadata are both known zeros.
69+
# PRCHK=0 skips PI validation, so the zero PI bytes are accepted
70+
# by the controller on write and matched exactly on compare. This
71+
# is PI-format and guard-width agnostic: the entire ms-byte
72+
# metadata slot (whether holding an 8-byte PI with 16-bit or
73+
# 32-bit guard, or a 16-byte PI with 64-bit guard) is zeroed.
74+
self.prinfo = 0
75+
self.data_size = ds
76+
else:
77+
# No PI. For extended LBA format (metadata appended to the data
78+
# buffer) include the metadata bytes so that the controller sees
79+
# a consistent data+metadata unit. For separate metadata format
80+
# (flbas bit 4 clear) the metadata is transferred via a different
81+
# pointer and must NOT be folded into the data buffer; use ds only
82+
# so that the data transfer length matches exactly one LBA.
83+
self.prinfo = 0
84+
self.data_size = ds + ms if self.ns_meta_ext else ds
4785
self.start_block = 0
4886
self.block_count = 0
4987
self.write_file = "write_file.txt"
5088
self.read_file = "read_file.txt"
89+
# Basename only; subclasses must prepend the test_log_dir path before
90+
# use (same convention as write_file and read_file above).
91+
if self.ms > 0 and not self.ns_meta_ext:
92+
self.write_meta_file = "write_meta_file.bin"
93+
self.read_meta_file = "read_meta_file.bin"
5194

5295
def tearDown(self):
5396
""" Post Section for TestNVMeIO """
@@ -70,6 +113,18 @@ def create_data_file(self, pathname, data_size, pattern):
70113
os.fsync(data_file.fileno())
71114
data_file.close()
72115

116+
def create_meta_file(self, pathname, meta_size):
117+
""" Creates a binary file of meta_size zero bytes for use as a
118+
separate-metadata buffer in nvme write/read/compare commands.
119+
- Args:
120+
- pathname : metadata file path name.
121+
- meta_size : total size of the metadata in bytes.
122+
- Returns:
123+
None
124+
"""
125+
with open(pathname, "wb") as meta_file:
126+
meta_file.write(bytes(meta_size))
127+
73128
def nvme_write(self):
74129
""" Wrapper for nvme write operation
75130
- Args:
@@ -81,6 +136,11 @@ def nvme_write(self):
81136
f"--start-block={str(self.start_block)} " + \
82137
f"--block-count={str(self.block_count)} " + \
83138
f"--data-size={str(self.data_size)} --data={self.write_file}"
139+
if self.prinfo:
140+
write_cmd += f" --prinfo={self.prinfo}"
141+
if self.ms > 0 and not self.ns_meta_ext:
142+
write_cmd += \
143+
f" --metadata-size={self.ms} --metadata={self.write_meta_file}"
84144
return self.exec_cmd(write_cmd)
85145

86146
def nvme_read(self):
@@ -94,4 +154,9 @@ def nvme_read(self):
94154
f"--start-block={str(self.start_block)} " + \
95155
f"--block-count={str(self.block_count)} " + \
96156
f"--data-size={str(self.data_size)} --data={self.read_file}"
157+
if self.prinfo:
158+
read_cmd += f" --prinfo={self.prinfo}"
159+
if self.ms > 0 and not self.ns_meta_ext:
160+
read_cmd += \
161+
f" --metadata-size={self.ms} --metadata={self.read_meta_file}"
97162
return self.exec_cmd(read_cmd)

tests/nvme_writezeros_test.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ def setUp(self):
5959
self.create_data_file(self.write_file, self.data_size, "15")
6060
self.create_data_file(self.zero_file, self.data_size, '\0')
6161
open(self.read_file, 'a').close()
62+
if self.ms > 0 and not self.ns_meta_ext:
63+
self.write_meta_file = self.test_log_dir + "/" + self.write_meta_file
64+
self.read_meta_file = self.test_log_dir + "/" + self.read_meta_file
65+
self.create_meta_file(self.write_meta_file, self.ms)
6266

6367
def tearDown(self):
6468
""" Post Section for TestNVMeWriteZeros """

0 commit comments

Comments
 (0)