Skip to content

Commit 788303d

Browse files
committed
tests: check and format namespace for copy test
Copy Descriptor Formats 1h and 3h require the source namespace to be formatted with 64-bit guard protection information (pif=2 in the extended LBA format entry per nvm-id-ns). When the namespace uses a standard 16-bit guard LBA format the controller returns "Invalid Format" (0x410a). Signed-off-by: Daniel Wagner <wagi@kernel.org>
1 parent 9a05332 commit 788303d

1 file changed

Lines changed: 160 additions & 25 deletions

File tree

tests/nvme_copy_test.py

Lines changed: 160 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,15 @@
1010
"""
1111
NVMe Copy Testcase:-
1212
13-
1. Issue copy command on set of block; shall pass.
14-
2. If cross-namespace copy formats are supported, enable and test
15-
cross-namespace copy formats.
13+
Test classes are split by descriptor format group:
14+
15+
TestNVMeCopyFormat0 - Descriptor Format 0 (16-bit guard, in-namespace copy).
16+
TestNVMeCopyFormat1 - Descriptor Format 1 (64-bit guard, in-namespace copy).
17+
The namespace is reformatted to a 64-bit guard LBA
18+
format before the test runs.
19+
TestNVMeCopyFormat23 - Descriptor Formats 2 and 3 (cross-namespace copy).
20+
Format 3 additionally requires a 64-bit guard namespace
21+
and reformats before running.
1622
1723
"""
1824

@@ -24,27 +30,29 @@
2430
class TestNVMeCopy(TestNVMe):
2531

2632
"""
27-
Represents NVMe Copy testcase.
33+
Base class for NVMe Copy tests.
34+
35+
Provides shared setUp/tearDown and helper methods used by all copy test
36+
subclasses.
2837
- Attributes:
29-
- ocfs : optional copy formats supported
30-
- original_cdfe : saved cdfe value to restore during teardown, or None
31-
- test_log_dir : directory for logs, temp files.
38+
- ocfs : optional copy formats supported (from id-ctrl)
39+
- original_cdfe : saved cdfe value restored in tearDown, or None
40+
- mcl : Maximum Copy Length (blocks)
41+
- mssrl : Maximum Single Source Range Length (blocks)
42+
- msrc : Maximum Source Range Count
43+
- ns1_nsid : numeric namespace ID of self.ns1
3244
"""
3345

3446
def setUp(self):
3547
""" Pre Section for TestNVMeCopy """
3648
super().setUp()
3749
self.ocfs = self.get_ocfs()
3850
self.original_cdfe = None
39-
self.mcl = to_decimal(self.get_id_ns_field_value("mcl"))
40-
self.mssrl = to_decimal(self.get_id_ns_field_value("mssrl"))
41-
self.msrc = to_decimal(self.get_id_ns_field_value("msrc"))
51+
self._refresh_ns_copy_limits()
4252
get_ns_id_cmd = f"{self.nvme_bin} get-ns-id {self.ns1}"
4353
result = self.run_cmd(get_ns_id_cmd)
44-
err = result.returncode
45-
self.assertEqual(err, 0, "ERROR : nvme get-ns-id failed")
46-
output = result.stdout
47-
self.ns1_nsid = int(output.strip().split(':')[-1])
54+
self.assertEqual(result.returncode, 0, "ERROR : nvme get-ns-id failed")
55+
self.ns1_nsid = int(result.stdout.strip().split(':')[-1])
4856
self.setup_log_dir(self.__class__.__name__)
4957

5058
def tearDown(self):
@@ -56,6 +64,16 @@ def tearDown(self):
5664
self.run_cmd(set_features_cmd)
5765
super().tearDown()
5866

67+
# ------------------------------------------------------------------
68+
# Internal helpers
69+
# ------------------------------------------------------------------
70+
71+
def _refresh_ns_copy_limits(self):
72+
""" Read MCL, MSSRL, MSRC from the current namespace into instance attrs """
73+
self.mcl = to_decimal(self.get_id_ns_field_value("mcl"))
74+
self.mssrl = to_decimal(self.get_id_ns_field_value("mssrl"))
75+
self.msrc = to_decimal(self.get_id_ns_field_value("msrc"))
76+
5977
def _check_format_supported(self, desc_format):
6078
""" Skip test if the given copy descriptor format is not supported """
6179
if not self.ocfs & (1 << desc_format):
@@ -69,10 +87,81 @@ def _check_ns_copy_limits(self):
6987
if missing:
7088
self.skipTest(f"{', '.join(missing)} are 0, copy not supported on this namespace")
7189

90+
def _find_64b_guard_lbaf_index(self):
91+
"""
92+
Search the nvm-id-ns elbafs for a format with 64-bit guard PI (pif == 2).
93+
94+
Returns the lbaf index (0-based position in the lbafs[] array), or None
95+
if no such format exists or the nvm-id-ns command is not supported.
96+
"""
97+
nvm_id_ns_cmd = f"{self.nvme_bin} nvm-id-ns {self.ns1} --output-format=json"
98+
result = self.run_cmd(nvm_id_ns_cmd)
99+
if result.returncode != 0:
100+
return None
101+
elbafs = json.loads(result.stdout).get("elbafs", [])
102+
for i, elbaf in enumerate(elbafs):
103+
if elbaf.get("pif", 0) == 2: # NVME_NVM_PIF_64B_GUARD = 2
104+
return i
105+
return None
106+
107+
def _create_ns_with_lbaf(self, lbaf_index):
108+
"""
109+
Delete and recreate the default namespace using the given lbaf_index.
110+
111+
The lbaf_index is encoded into the flbas byte per NVMe spec:
112+
flbas[3:0] = lbaf_index[3:0], flbas[6:5] = lbaf_index[5:4]
113+
114+
After recreating, self.mcl/mssrl/msrc are refreshed from the new
115+
namespace. Calls skipTest if namespace management is not supported
116+
or if the create/attach step fails.
117+
"""
118+
if not self.ns_mgmt_supported:
119+
self.skipTest("namespace management not supported; cannot reformat namespace")
120+
121+
# encode lbaf_index into the 8-bit flbas field
122+
flbas = (lbaf_index & 0xF) | (((lbaf_index >> 4) & 0x3) << 5)
123+
124+
# get_lba_format_size() in the parent class indexes the id-ns lbafs[] array
125+
# using self.flbas, so set it to lbaf_index here for the size look-up.
126+
# This is intentional: lbaf_index is the direct array position, while flbas
127+
# (computed above) is the encoded byte passed to create-ns --flbas.
128+
self.flbas = lbaf_index
129+
(ds, ms) = self.get_lba_format_size()
130+
if ds == 0:
131+
self.skipTest(f"lbaf {lbaf_index} reports zero data size; cannot create namespace")
132+
ncap = int(self.get_ncap() / (ds + ms))
133+
134+
ctrl_id = self.get_ctrl_id()
135+
self.delete_all_ns()
136+
err = self.create_and_validate_ns(self.default_nsid, ncap, ncap, flbas, 0)
137+
self.assertEqual(err, 0,
138+
f"ERROR: failed to create namespace with lbaf {lbaf_index} (flbas={flbas:#x})")
139+
self.assertEqual(self.attach_ns(ctrl_id, self.default_nsid), 0,
140+
"ERROR: failed to attach reformatted namespace")
141+
142+
# refresh copy limits for the new namespace
143+
self._refresh_ns_copy_limits()
144+
145+
def _setup_64b_guard_ns(self):
146+
"""
147+
Reformat the default namespace to a 64-bit guard PI LBA format (pif == 2).
148+
149+
Skips the test if:
150+
- namespace management is not supported, or
151+
- no LBA format with 64-bit guard PI exists on this controller.
152+
"""
153+
lbaf_index = self._find_64b_guard_lbaf_index()
154+
if lbaf_index is None:
155+
self.skipTest("no LBA format with 64-bit guard PI (pif=2) found; "
156+
"cannot run copy descriptor format 1/3 test")
157+
self._create_ns_with_lbaf(lbaf_index)
158+
72159
def _enable_cdfe_for_format(self, desc_format):
73-
""" Enable the host-behavior-support cdfe bit for the given cross-namespace format.
74-
Only the bit corresponding to desc_format is enabled; other bits are left unchanged.
75-
The original value is saved in self.original_cdfe for tearDown to restore.
160+
"""
161+
Enable the host-behavior-support cdfe bit for the given cross-namespace
162+
copy descriptor format. Only the single required bit is enabled; other
163+
bits are left unchanged. The original value is saved in self.original_cdfe
164+
for tearDown to restore.
76165
"""
77166
cdfe_bit = 1 << desc_format
78167
get_features_cmd = f"{self.nvme_bin} feat host-behavior-support " + \
@@ -114,29 +203,79 @@ def copy(self, sdlba, blocks, slbs, **kwargs):
114203
- None
115204
"""
116205
desc_format = kwargs.get("descriptor_format", 0)
117-
# build copy command
118206
copy_cmd = f"{self.nvme_bin} copy {self.ns1} " + \
119207
f"--format={desc_format} --sdlba={sdlba} --blocks={blocks} " + \
120208
f"--slbs={slbs}"
121209
if "snsids" in kwargs:
122210
copy_cmd += f" --snsids={kwargs['snsids']}"
123211
if "sopts" in kwargs:
124212
copy_cmd += f" --sopts={kwargs['sopts']}"
125-
# run and assert success
126213
self.assertEqual(self.exec_cmd(copy_cmd), 0)
127214

215+
216+
class TestNVMeCopyFormat0(TestNVMeCopy):
217+
218+
"""
219+
NVMe Copy tests using Descriptor Format 0.
220+
221+
Format 0 uses 16-bit guard PI and copies within a single namespace.
222+
No special namespace formatting is required.
223+
"""
224+
128225
def test_copy_format_0(self):
129226
""" Test copy with descriptor format 0 """
130227
self._check_format_supported(0)
131228
self._check_ns_copy_limits()
132229
self.copy(0, 1, 2, descriptor_format=0)
133230

231+
232+
class TestNVMeCopyFormat1(TestNVMeCopy):
233+
234+
"""
235+
NVMe Copy tests using Descriptor Format 1.
236+
237+
Format 1 uses 64-bit guard PI and copies within a single namespace.
238+
setUp reformats the namespace to a 64-bit guard LBA format; the test is
239+
skipped if no such format is available or namespace management is not
240+
supported.
241+
"""
242+
243+
def setUp(self):
244+
""" Pre Section for TestNVMeCopyFormat1 """
245+
super().setUp()
246+
self._setup_64b_guard_ns()
247+
134248
def test_copy_format_1(self):
135249
""" Test copy with descriptor format 1 """
136250
self._check_format_supported(1)
137251
self._check_ns_copy_limits()
138252
self.copy(0, 1, 2, descriptor_format=1)
139253

254+
255+
class TestNVMeCopyFormat23(TestNVMeCopy):
256+
257+
"""
258+
NVMe Copy tests using Descriptor Formats 2 and 3.
259+
260+
Formats 2 and 3 perform cross-namespace copy operations.
261+
Format 2 uses 16-bit guard PI and works with the default namespace.
262+
Format 3 uses 64-bit guard PI; those tests reformat the namespace inline
263+
(rather than in setUp) so that format 2 tests can still use the standard
264+
namespace.
265+
"""
266+
267+
def _run_format_3_copy(self, **kwargs):
268+
"""
269+
Reformat the namespace to 64-bit guard PI, check copy limits, enable
270+
cdfe for format 3, then execute the copy command.
271+
272+
Additional keyword arguments are forwarded to self.copy() (e.g. sopts).
273+
"""
274+
self._setup_64b_guard_ns()
275+
self._check_ns_copy_limits()
276+
self._enable_cdfe_for_format(3)
277+
self.copy(0, 1, 2, descriptor_format=3, snsids=self.ns1_nsid, **kwargs)
278+
140279
def test_copy_format_2(self):
141280
""" Test copy with descriptor format 2 """
142281
self._check_format_supported(2)
@@ -154,13 +293,9 @@ def test_copy_format_2_sopts(self):
154293
def test_copy_format_3(self):
155294
""" Test copy with descriptor format 3 """
156295
self._check_format_supported(3)
157-
self._check_ns_copy_limits()
158-
self._enable_cdfe_for_format(3)
159-
self.copy(0, 1, 2, descriptor_format=3, snsids=self.ns1_nsid)
296+
self._run_format_3_copy()
160297

161298
def test_copy_format_3_sopts(self):
162299
""" Test copy with descriptor format 3 and source options """
163300
self._check_format_supported(3)
164-
self._check_ns_copy_limits()
165-
self._enable_cdfe_for_format(3)
166-
self.copy(0, 1, 2, descriptor_format=3, snsids=self.ns1_nsid, sopts=0)
301+
self._run_format_3_copy(sopts=0)

0 commit comments

Comments
 (0)