Recent changes (master)

Jens Axboe <[email protected]> Tue, 2 Jun 2026 06:00:01 -0600
Newsgroups org.kernel.vger.fio
Message-ID <[email protected]>
The following changes since commit 6a66cccb6cb2b321e107b97733faac4db4d95ebc:

  Merge branch 'fix-musl-prctl-conflict' of https://github.com/lucashecht/fio (2026-05-28 10:30:47 -0400)

are available in the Git repository at:

  git://git.kernel.dk/fio.git master

for you to fetch changes up to e682f9b3406e35a28a87f39789e3a0c6138b30b4:

  ci: add nvmept_write_mode.py to test harness (2026-06-01 14:38:22 -0400)

----------------------------------------------------------------
Swarna Prabhu (1):
      engines/nvme: fix addr/data_len for no data nvme commands

Vincent Fu (3):
      Merge branch 'fio_5_27' of https://github.com/swarnagh6/fio
      t/nvmept_write_mode.py: test script for io_uring_cmd write modes
      ci: add nvmept_write_mode.py to test harness

 .github/workflows/qemu.yml |   2 +-
 engines/nvme.c             |  20 +++-
 t/nvmept_write_mode.py     | 282 +++++++++++++++++++++++++++++++++++++++++++++
 t/run-fio-tests.py         |   8 ++
 4 files changed, 306 insertions(+), 6 deletions(-)
 create mode 100755 t/nvmept_write_mode.py

---

Diff of recent changes:

diff --git a/.github/workflows/qemu.yml b/.github/workflows/qemu.yml
index 8160d43e..ba0a00ac 100644
--- a/.github/workflows/qemu.yml
+++ b/.github/workflows/qemu.yml
@@ -18,7 +18,7 @@ jobs:
               -device nvme,id=nvme0,serial=deadbeef
               -drive id=nvm-0,file=nvme0.img,format=raw,if=none,discard=unmap,media=disk
               -device nvme-ns,id=nvm-0,drive=nvm-0,bus=nvme0,nsid=1
-            test_cmd: "python3 t/run-fio-tests.py --nvmecdev /dev/ng0n1 --run-only 1014 1015"
+            test_cmd: "python3 t/run-fio-tests.py --nvmecdev /dev/ng0n1 --run-only 1014 1015 1022 -p '1022:--skip 30 31 32 33 34'"
             extra_pkgs: "nvme-cli"
           - config: 16-bit Guard PI tests (long)
             device: >-
diff --git a/engines/nvme.c b/engines/nvme.c
index 528b2a29..e480fce0 100644
--- a/engines/nvme.c
+++ b/engines/nvme.c
@@ -390,12 +390,22 @@ int fio_nvme_uring_cmd_prep(struct nvme_uring_cmd *cmd, struct io_u *io_u,
 		cmd->addr = (__u64)(uintptr_t)iov;
 		cmd->data_len = 1;
 	} else {
-		/* no buffer for write zeroes */
-		if (cmd->opcode != nvme_cmd_write_zeroes)
+		/* use buffer only for data transfer commands */
+		switch (cmd->opcode) {
+		case nvme_cmd_read:
+		case nvme_cmd_write:
+		case nvme_cmd_compare:
 			cmd->addr = (__u64)(uintptr_t)io_u->xfer_buf;
-		else
-			cmd->addr = (__u64)(uintptr_t)NULL;
-		cmd->data_len = io_u->xfer_buflen;
+			cmd->data_len = io_u->xfer_buflen;
+			break;
+		case nvme_cmd_write_zeroes:
+		case nvme_cmd_write_uncor:
+		case nvme_cmd_verify:
+			/* since cmd->addr and cmd->data_len is set to 0 by memset */
+			break;
+		default:
+			return -ENOTSUP;
+		}
 	}
 	if (data->lba_shift && data->ms) {
 		cmd->metadata = (__u64)(uintptr_t)io_u->mmap_data;
diff --git a/t/nvmept_write_mode.py b/t/nvmept_write_mode.py
new file mode 100755
index 00000000..b52bd8b0
--- /dev/null
+++ b/t/nvmept_write_mode.py
@@ -0,0 +1,282 @@
+#!/usr/bin/env python3
+#
+# Copyright 2026 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# For conditions of distribution and use, see the accompanying COPYING file.
+#
+"""
+# nvmept_write_mode.py
+#
+# Test fio's io_uring_cmd ioengine with NVMe pass-through write modes
+#
+# USAGE
+# see python3 nvmept_write_mode.py --help
+#
+# EXAMPLES
+# python3 t/nvmept_write_mode.py --dut /dev/ng0n1
+# python3 t/nvmept_write_mode.py --dut /dev/ng1n1 -f ./fio
+#
+# REQUIREMENTS
+# Python 3.6
+#
+"""
+import os
+import sys
+import time
+import logging
+import argparse
+from pathlib import Path
+from fiotestlib import FioJobCmdTest, run_fio_tests
+from fiotestcommon import SUCCESS_NONZERO
+
+
+class WriteModeTest(FioJobCmdTest):
+    """
+    NVMe pass-through test class. Check to make sure output for selected data
+    direction(s) is non-zero and that zero data appears for other directions.
+    """
+
+    def setup(self, parameters):
+        """Setup a test."""
+
+        fio_args = [
+            "--name=nvmept-write-mode",
+            "--ioengine=io_uring_cmd",
+            "--cmd_type=nvme",
+            f"--filename={self.fio_opts['filename']}",
+            f"--rw={self.fio_opts['rw']}",
+            f"--output={self.filenames['output']}",
+            f"--output-format={self.fio_opts['output-format']}",
+        ]
+        for opt in ['fixedbufs', 'nonvectored', 'force_async', 'registerfiles',
+                    'sqthread_poll', 'sqthread_poll_cpu', 'hipri', 'nowait',
+                    'time_based', 'runtime', 'verify', 'io_size', 'num_range',
+                    'iodepth', 'iodepth_batch', 'iodepth_batch_complete',
+                    'size', 'rate', 'bs', 'bssplit', 'bsrange', 'randrepeat',
+                    'buffer_pattern', 'verify_pattern', 'verify', 'offset',
+                    'filesize', 'write_mode', ]:
+            if opt in self.fio_opts:
+                option = f"--{opt}={self.fio_opts[opt]}"
+                fio_args.append(option)
+
+        super().setup(fio_args)
+
+
+    def check_result(self):
+
+        super().check_result()
+
+        if 'rw' not in self.fio_opts or \
+                not self.passed or \
+                'json' not in self.fio_opts['output-format']:
+            return
+
+        job = self.json_data['jobs'][0]
+
+        if self.fio_opts['rw'] in ['read', 'randread']:
+            self.passed = self.check_all_ddirs(['read'], job)
+        elif self.fio_opts['rw'] in ['write', 'randwrite']:
+            if 'verify' not in self.fio_opts:
+                self.passed = self.check_all_ddirs(['write'], job)
+            else:
+                self.passed = self.check_all_ddirs(['read', 'write'], job)
+        elif self.fio_opts['rw'] in ['trim', 'randtrim']:
+            self.passed = self.check_all_ddirs(['trim'], job)
+        elif self.fio_opts['rw'] in ['readwrite', 'randrw']:
+            self.passed = self.check_all_ddirs(['read', 'write'], job)
+        elif self.fio_opts['rw'] in ['trimwrite', 'randtrimwrite']:
+            self.passed = self.check_all_ddirs(['trim', 'write'], job)
+        else:
+            logging.error("Unhandled rw value %s", self.fio_opts['rw'])
+            self.passed = False
+
+TEST_SIZE="16M"
+
+TEST_LIST = [
+    {
+        # Use write_mode=write to precondition device for write_mode=verify
+        # which just tells the device to check the integrity of the stored data
+        "test_id": 10,
+        "fio_opts": {
+            "rw": 'randwrite',
+            "filesize": TEST_SIZE,
+            "write_mode": "write",
+            "randrepeat": 0,
+            "output-format": "json",
+            },
+        "test_class": WriteModeTest,
+    },
+    {
+        "test_id": 11,
+        "fio_opts": {
+            "rw": 'write',
+            "filesize": TEST_SIZE,
+            "write_mode": "verify",
+            "output-format": "json",
+            },
+        "test_class": WriteModeTest,
+    },
+    {
+        "test_id": 12,
+        "fio_opts": {
+            "rw": 'randwrite',
+            "filesize": TEST_SIZE,
+            "write_mode": "verify",
+            "randrepeat": 0,
+            "output-format": "json",
+            },
+        "test_class": WriteModeTest,
+    },
+
+    {
+        # Precondition device using write zeroes. Then use pattern verification
+        # to read everything back
+        "test_id": 20,
+        "fio_opts": {
+            "rw": 'randwrite',
+            "filesize": TEST_SIZE,
+            "write_mode": "zeroes",
+            "randrepeat": 0,
+            "output-format": "json",
+            },
+        "test_class": WriteModeTest,
+    },
+    {
+        "test_id": 21,
+        "fio_opts": {
+            "rw": 'read',
+            "filesize": TEST_SIZE,
+            "verify": "pattern",
+            "verify_pattern": 0,
+            "output-format": "json",
+            },
+        "test_class": WriteModeTest,
+    },
+    {
+        "test_id": 22,
+        "fio_opts": {
+            "rw": 'randread',
+            "filesize": TEST_SIZE,
+            "verify": "pattern",
+            "verify_pattern": 0,
+            "randrepeat": 0,
+            "output-format": "json",
+            },
+        "test_class": WriteModeTest,
+    },
+
+    {
+        # Precondition device for write_mode=verify which just tells the device
+        # to check the integrity of the stored data
+        # Issue write uncorrectable commands which instruct the device to
+        # return an uncorrectable error when reading back the data
+        "test_id": 30,
+        "fio_opts": {
+            "rw": 'randwrite',
+            "filesize": TEST_SIZE,
+            "write_mode": "uncor",
+            "output-format": "json",
+            },
+        "test_class": WriteModeTest,
+    },
+    {
+        "test_id": 31,
+        "fio_opts": {
+            "rw": 'write',
+            "filesize": TEST_SIZE,
+            "write_mode": "verify",
+            "output-format": "json",
+            },
+        "test_class": WriteModeTest,
+        "success": SUCCESS_NONZERO,
+    },
+    {
+        "test_id": 32,
+        "fio_opts": {
+            "rw": 'randwrite',
+            "filesize": TEST_SIZE,
+            "write_mode": "verify",
+            "output-format": "json",
+            "randrepeat": 0,
+            },
+        "test_class": WriteModeTest,
+        "success": SUCCESS_NONZERO,
+    },
+    {
+        "test_id": 33,
+        "fio_opts": {
+            "rw": 'read',
+            "filesize": TEST_SIZE,
+            "output-format": "json",
+            },
+        "test_class": WriteModeTest,
+        "success": SUCCESS_NONZERO,
+    },
+    {
+        "test_id": 34,
+        "fio_opts": {
+            "rw": 'randread',
+            "filesize": TEST_SIZE,
+            "output-format": "json",
+            "randrepeat": 0,
+            },
+        "test_class": WriteModeTest,
+        "success": SUCCESS_NONZERO,
+    },
+]
+
+def parse_args():
+    """Parse command-line arguments."""
+
+    parser = argparse.ArgumentParser()
+    parser.add_argument('-d', '--debug', help='Enable debug messages', action='store_true')
+    parser.add_argument('-f', '--fio', help='path to file executable (e.g., ./fio)')
+    parser.add_argument('-a', '--artifact-root', help='artifact root directory')
+    parser.add_argument('-s', '--skip', nargs='+', type=int,
+                        help='list of test(s) to skip')
+    parser.add_argument('-o', '--run-only', nargs='+', type=int,
+                        help='list of test(s) to run, skipping all others')
+    parser.add_argument('--dut', help='target NVMe character device to test '
+                        '(e.g., /dev/ng0n1). WARNING: THIS IS A DESTRUCTIVE TEST', required=True)
+    args = parser.parse_args()
+
+    return args
+
+
+def main():
+    """Run tests using fio's io_uring_cmd ioengine to send NVMe pass through commands."""
+
+    args = parse_args()
+
+    if args.debug:
+        logging.basicConfig(level=logging.DEBUG)
+    else:
+        logging.basicConfig(level=logging.INFO)
+
+    artifact_root = args.artifact_root if args.artifact_root else \
+        f"nvmept-write-mode-test-{time.strftime('%Y%m%d-%H%M%S')}"
+    os.mkdir(artifact_root)
+    print(f"Artifact directory is {artifact_root}")
+
+    if args.fio:
+        fio_path = str(Path(args.fio).absolute())
+    else:
+        fio_root = str(Path(__file__).absolute().parent.parent)
+    print(f"fio path is {fio_path}")
+
+    for test in TEST_LIST:
+        test['fio_opts']['filename'] = args.dut
+
+    test_env = {
+              'fio_path': fio_path,
+              'fio_root': str(Path(__file__).absolute().parent.parent),
+              'artifact_root': artifact_root,
+              'basename': 'nvmept-write-mode',
+              }
+
+    _, failed, _ = run_fio_tests(TEST_LIST, test_env, args)
+    sys.exit(failed)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/t/run-fio-tests.py b/t/run-fio-tests.py
index c3545dd6..afecd67d 100755
--- a/t/run-fio-tests.py
+++ b/t/run-fio-tests.py
@@ -1147,6 +1147,14 @@ TEST_LIST = [
         'success':          SUCCESS_DEFAULT,
         'requirements':     [],
     },
+    {
+        'test_id':          1022,
+        'test_class':       FioExeTest,
+        'exe':              't/nvmept_write_mode.py',
+        'parameters':       ['-f', '{fio_path}', '--dut', '{nvmecdev}'],
+        'success':          SUCCESS_DEFAULT,
+        'requirements':     [Requirements.linux, Requirements.nvmecdev],
+    },
 ]