libnvme/tests/ioctl: run the mock ioctl tests on Windows - #3815
Open
bbusacker wants to merge 6 commits into
Open
libnvme/tests/ioctl: run the mock ioctl tests on Windows#3815bbusacker wants to merge 6 commits into
bbusacker wants to merge 6 commits into
Conversation
The Linux passthru ioctls always write the CQE result back, so callers can read cmd->result after the call regardless of what it held on entry. The Windows implementation only assigns cmd->result on the paths that manage to retrieve completion data, which leaves whatever the caller happened to have in the field for every command that fails early -- an unsupported opcode, a rejected Command Set Identifier, or a failed IOCTL. Zero it in both entry points so the two platforms agree: after a failed command, cmd->result reads back as 0 rather than as stale caller data. Signed-off-by: Brandon Busacker <bbusacker@micron.com>
sndk_do_sn861_drive_resize() copies admin_cmd.result out unconditionally, including on the error paths where no completion data was retrieved, and its caller declares the receiving variable uninitialised. Copy the result only when the command succeeded, and initialise the caller's variable, so a failed resize cannot be reported with a fabricated result. Signed-off-by: Brandon Busacker <bbusacker@micron.com>
The %m conversion is a glibc printf extension. It is not in C, and mingw's printf does not implement it, so these diagnostics would emit a literal "%m" once these tests are built for Windows. Call strerror(errno) explicitly and include <string.h> (and <errno.h> in logs.c) for it. Signed-off-by: Brandon Busacker <bbusacker@micron.com>
Several of these tests assert an exact error or result value that a non-Linux implementation cannot reproduce. Windows has no passthru ioctl that carries a Command Set Identifier, so libnvme rejects those commands before issuing any IOCTL; the Windows IOCTLs report a failed command as EIO rather than the NVMe status code; get-features sets DNR where Linux does not; and no completion data is available for a failed command, so the result reads back as 0. Rather than #ifdef the assertions, carry the difference in the mock: win_err overrides the expected error on Windows, win_no_ioctl says the command never reaches the mock layer at all, and mock_err(), mock_ok() and mock_result() read the expectation for the platform under test. WIN_CSI_UNSUPPORTED names the common case of a rejected Command Set. On Linux every one of these resolves to the value the test asserted before, so behaviour there is unchanged. The one new test, set_error_clears_stale_result, checks that a failed command does not leave a caller-supplied result in place; it seeds cmd->result after the init helper precisely so it is testing the passthru entry point and not nvme_init_set_features_async_event(). Signed-off-by: Brandon Busacker <bbusacker@micron.com>
The ioctl mock is injected with LD_PRELOAD on Linux, which Windows has no equivalent of. ld --wrap does not substitute for it either: libnvme is built as a DLL, so its call to DeviceIoControl is bound to its own import thunk at load time and never passes through a symbol the test executable could wrap. Overwriting that thunk does work. Every PE image reaches an imported function through a slot in its Import Address Table, so rewriting libnvme's IAT slot redirects the call without modifying any code, and the patch is confined to the one module -- which is what a test wants. iat_patch() finds the slot for a named import and swaps in a replacement, optionally handing back the original so it can be called through or restored. Nothing builds this yet; the mock that uses it follows. Signed-off-by: Brandon Busacker <bbusacker@micron.com>
…dows The ioctl tests have been Linux-only because both halves of the harness are Linux-specific: the mock is injected with LD_PRELOAD and it intercepts the NVMe passthru ioctl. Windows offers neither. What it does offer is a small, fixed set of NVMe-specific IOCTLs that libnvme drives through DeviceIoControl, which is interceptable by patching libnvme's import thunk. mock-win.c is that interception. It patches DeviceIoControl in the libnvme module, decodes the Windows IOCTL back into the NVMe command it represents, matches it against the next expected mock_cmd exactly as mock.c does, and synthesises the reply the driver would have returned. The decoders cover the three command families libnvme implements on Windows today: identify, get/set features, and get log page. The remaining test programs stay off Windows. They exercise families that reach the driver by other routes -- SCSI CDBs for read, write and flush, dedicated firmware, format and sanitize IOCTLs -- or that libnvme does not implement on Windows at all. Each needs its own decoder, so enabling them is future work rather than a line in this file, and they are left disabled instead of reporting a pass they did not earn. With the mock chosen inside ioctl/meson.build the host gate in the parent meson.build has nothing left to decide, so it goes away. README.md records how the two mocks differ and what a new decoder has to do. Signed-off-by: Brandon Busacker <bbusacker@micron.com>
igaw
reviewed
Aug 12, 2026
| .cdw11 = (TEST_FIDX << 0) | (TEST_CSI << 24), | ||
| .cdw14 = TEST_UUID, | ||
| .out_data = &expected_id, | ||
| WIN_CSI_UNSUPPORTED, |
Collaborator
There was a problem hiding this comment.
.err = WIN_CSI_UNSUPPORTED ?
igaw
reviewed
Aug 12, 2026
| (((sizeof(expected_log) >> 2) - 1) << 16), | ||
| .cdw14 = (TEST_CSI << 24), | ||
| .out_data = &expected_log, | ||
| WIN_CSI_UNSUPPORTED, |
Collaborator
There was a problem hiding this comment.
same here is it .err or .win_err?
igaw
reviewed
Aug 12, 2026
| @@ -0,0 +1,83 @@ | |||
| # ioctl mock tests | |||
Collaborator
There was a problem hiding this comment.
There is libnvme/design folder. I suggest to add a sub folder tests and move this document there, with a different name obviously.
Collaborator
|
Thank for the effort. IMO the ioctl tests are not really bringing a lot to the table, they are mostly exercising the serializing/deserializing path which is platform neutral. So I am wondering if a Windows port is worth having. Don't get me wrong, I don't mind it as long it doesn't add additional maintenance burden. WDYT? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The
libnvme/tests/ioctlsuite is the only place command-building is checkedwithout a device, and it has been Linux-only because both halves of the harness
are: the mock is injected with
LD_PRELOAD, and it intercepts the NVMe passthruioctl. Windows has neither.
This series adds a Windows interception layer so the existing
identify,featuresandlogstests run there against the same expectations, and fixesthe two result-handling bugs the new coverage exposed.
How it works
libnvme reaches the driver through
DeviceIoControl.--wrapcannot interceptthat -- libnvme is a DLL, so the call is bound through its own import thunk at
load time and never passes through a symbol the test executable links. What does
work is rewriting the thunk:
iat-hook.cpatches libnvme's Import Address Tableslot, which redirects the call without touching any code and leaves other modules
(including libnvme's own device enumeration) alone.
mock-win.cthen decodes the Windows IOCTL back into the NVMe command itrepresents and matches it against the next expected
struct mock_cmd, exactly asmock.cdoes. That forward translation inioctl-win.cis lossy, so each decoderrecords which fields it actually recovered -- and a bit mask where a field
survives only partly, e.g. Windows' 4-bit
LogSpecificFieldagainst NVMe's 7.Only recovered fields are compared; a discarded field is skipped rather than
compared against zero, which would turn a real mismatch into a false pass.
Where Windows genuinely cannot behave identically, the test says so instead of
being skipped:
win_errandwin_no_ioctlonstruct mock_cmd, read throughmock_err()/mock_ok()/mock_result(). On Linux every one of those resolvesto the value the test asserted before. The mock rejects an expectation libnvme
could not have produced, so an undeclared divergence fails loudly.
libnvme/tests/ioctl/README.mddocuments all of this, including what a newdecoder has to do.
Two fixes
cmd->resulton paths thatretrieved completion data, leaving stale caller data in the field for any
command that failed early. Linux always writes it back. Now both do.
sndk_do_sn861_drive_resize()copiedadmin_cmd.resultout unconditionally,including on error paths, and its caller left the receiving variable
uninitialised.
Scope
The other ioctl tests stay off Windows. They exercise families that reach the
driver by other routes -- SCSI CDBs for read, write and flush, dedicated
firmware, format and sanitize IOCTLs -- or that libnvme does not implement on
Windows at all. Each needs its own decoder, so they are left disabled rather than
reporting a pass they didn't earn.
Verification
identical to
upstream/master-- the only difference in the list isregistration order, since
subdir('ioctl')moved ahead ofsubdir('sysfs').warnings, with
libnvme - identify,libnvme - featuresandlibnvme - logspassing.
last are all in
mock-win.cand deliberate -- a Windows API function-pointertypedef (explained in a comment at the declaration), long
fail()messagestrings, the
-ENOSYS->ERROR_CALL_NOT_IMPLEMENTEDerrno mapping, and a"falls through" phrase in prose that isn't a switch fallthrough.