Skip to content

Commit 3c82378

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 c2706b6 commit 3c82378

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):
@@ -55,6 +63,16 @@ def tearDown(self):
5563
self.run_cmd(set_features_cmd)
5664
super().tearDown()
5765

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

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

211+
212+
class TestNVMeCopyFormat0(TestNVMeCopy):
213+
214+
"""
215+
NVMe Copy tests using Descriptor Format 0.
216+
217+
Format 0 uses 16-bit guard PI and copies within a single namespace.
218+
No special namespace formatting is required.
219+
"""
220+
124221
def test_copy_format_0(self):
125222
""" Test copy with descriptor format 0 """
126223
self._check_format_supported(0)
127224
self._check_ns_copy_limits()
128225
self.copy(0, 1, 2, descriptor_format=0)
129226

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

250+
251+
class TestNVMeCopyFormat23(TestNVMeCopy):
252+
253+
"""
254+
NVMe Copy tests using Descriptor Formats 2 and 3.
255+
256+
Formats 2 and 3 perform cross-namespace copy operations.
257+
Format 2 uses 16-bit guard PI and works with the default namespace.
258+
Format 3 uses 64-bit guard PI; those tests reformat the namespace inline
259+
(rather than in setUp) so that format 2 tests can still use the standard
260+
namespace.
261+
"""
262+
263+
def _run_format_3_copy(self, **kwargs):
264+
"""
265+
Reformat the namespace to 64-bit guard PI, check copy limits, enable
266+
cdfe for format 3, then execute the copy command.
267+
268+
Additional keyword arguments are forwarded to self.copy() (e.g. sopts).
269+
"""
270+
self._setup_64b_guard_ns()
271+
self._check_ns_copy_limits()
272+
self._enable_cdfe_for_format(3)
273+
self.copy(0, 1, 2, descriptor_format=3, snsids=self.ns1_nsid, **kwargs)
274+
136275
def test_copy_format_2(self):
137276
""" Test copy with descriptor format 2 """
138277
self._check_format_supported(2)
@@ -150,13 +289,9 @@ def test_copy_format_2_sopts(self):
150289
def test_copy_format_3(self):
151290
""" Test copy with descriptor format 3 """
152291
self._check_format_supported(3)
153-
self._check_ns_copy_limits()
154-
self._enable_cdfe_for_format(3)
155-
self.copy(0, 1, 2, descriptor_format=3, snsids=self.ns1_nsid)
292+
self._run_format_3_copy()
156293

157294
def test_copy_format_3_sopts(self):
158295
""" Test copy with descriptor format 3 and source options """
159296
self._check_format_supported(3)
160-
self._check_ns_copy_limits()
161-
self._enable_cdfe_for_format(3)
162-
self.copy(0, 1, 2, descriptor_format=3, snsids=self.ns1_nsid, sopts=0)
297+
self._run_format_3_copy(sopts=0)

0 commit comments

Comments
 (0)