[LTP] [PATCH v3] testcases/kernel/device-drivers/nvme: Add NVMe device discovery test
Sebastian Chlad
sebastianchlad@gmail.com
Tue May 5 05:46:00 CEST 2026
Hi Priyama,
Sorry for top-posting, but I have a broader question here.
First of all, thanks for working on this and for looking into NVMe coverage.
Before going further, could you please explain the intended LTP-specific
value of
this test? blktests already has a dedicated NVMe test group with much
broader
coverage, including controller/namespace discovery, NVMe commands, fabrics,
etc.:
https://github.com/linux-blktests/blktests/tree/master/tests/nvme
So I think we should be careful not to duplicate coverage unless there is a
clear
reason for having a small LTP-level smoke test as well. Maybe your
intention is
different? Could you please clarify?
Cheers,
Sebastian
On Thu, 30 Apr 2026 at 07:10, <priyama2@linux.ibm.com> wrote:
> From: priyama2 <priyama2@linux.ibm.com>
>
> This test fills a gap in NVMe test coverage by validating basic device
> enumeration and driver binding, which are fundamental prerequisites for
> any NVMe functionality testing.
>
> The test verifies:
> - Detection of NVMe controllers in /dev
> - Enumeration of namespaces for detected controllers
> - PCI enumeration (class code 0x010802)
> - NVMe driver binding
>
> Changes in v3:
> - Fix subject prefix to match directory path
> - Update copyright year to 2026
> - Add nvme to SUBDIRS in testcases/kernel/device-drivers/Makefile
> - Reset static variables at start of run() for -i support
> - Remove .needs_root (all operations are world-readable)
> - Use SAFE_FOPEN/SAFE_FCLOSE for PCI class file access
> - Use SAFE_READLINK for driver symlink reading
> - Add .gitignore entry for nvme01 binary
> - Add runtest/device-drivers entry for nvme01
> - Fix author field to use priyama2
>
> Changes in v2:
> - Use LTP buildsystem instead of standalone Makefile
> - Remove README file (use doc comments instead)
> - Add proper LTP documentation comment format
> - Use SAFE_OPENDIR() and SAFE_CLOSEDIR() macros
> - Remove obvious and redundant comments
> - Code cleanup and style improvements
>
> Signed-off-by: priyama2 <priyama2@linux.ibm.com>
> ---
> runtest/device-drivers | 1 +
> testcases/kernel/device-drivers/Makefile | 1 +
> .../kernel/device-drivers/nvme/.gitignore | 1 +
> testcases/kernel/device-drivers/nvme/Makefile | 7 +
> testcases/kernel/device-drivers/nvme/nvme01.c | 205 ++++++++++++++++++
> 5 files changed, 215 insertions(+)
> create mode 100644 runtest/device-drivers
> create mode 100644 testcases/kernel/device-drivers/nvme/.gitignore
> create mode 100644 testcases/kernel/device-drivers/nvme/Makefile
> create mode 100644 testcases/kernel/device-drivers/nvme/nvme01.c
>
> diff --git a/runtest/device-drivers b/runtest/device-drivers
> new file mode 100644
> index 000000000..a7295584e
> --- /dev/null
> +++ b/runtest/device-drivers
> @@ -0,0 +1 @@
> +nvme01 nvme01
> diff --git a/testcases/kernel/device-drivers/Makefile
> b/testcases/kernel/device-drivers/Makefile
> index 229a50683..664f4cd18 100644
> --- a/testcases/kernel/device-drivers/Makefile
> +++ b/testcases/kernel/device-drivers/Makefile
> @@ -9,6 +9,7 @@ SUBDIRS := acpi \
> block \
> cpufreq \
> locking \
> + nvme \
> pci \
> rcu \
> rtc \
> diff --git a/testcases/kernel/device-drivers/nvme/.gitignore
> b/testcases/kernel/device-drivers/nvme/.gitignore
> new file mode 100644
> index 000000000..c60bc210c
> --- /dev/null
> +++ b/testcases/kernel/device-drivers/nvme/.gitignore
> @@ -0,0 +1 @@
> +nvme01
> diff --git a/testcases/kernel/device-drivers/nvme/Makefile
> b/testcases/kernel/device-drivers/nvme/Makefile
> new file mode 100644
> index 000000000..d47e99e6a
> --- /dev/null
> +++ b/testcases/kernel/device-drivers/nvme/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +# Copyright (c) 2024 IBM Corporation
> +
> +top_srcdir ?= ../../../..
> +
> +include $(top_srcdir)/include/mk/env_pre.mk
> +include $(top_srcdir)/include/mk/generic_trunk_target.mk
> diff --git a/testcases/kernel/device-drivers/nvme/nvme01.c
> b/testcases/kernel/device-drivers/nvme/nvme01.c
> new file mode 100644
> index 000000000..3c3cb92ce
> --- /dev/null
> +++ b/testcases/kernel/device-drivers/nvme/nvme01.c
> @@ -0,0 +1,205 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (c) 2026 IBM Corporation
> + * Author: priyama2 <priyama2@linux.ibm.com>
> + */
> +
> +/* * [Description]
> + *
> + * Verify NVMe device discovery and identification.
> + *
> + * This test fills a gap in NVMe test coverage by validating basic device
> + * enumeration and driver binding, which are fundamental prerequisites for
> + * any NVMe functionality testing.
> + *
> + * - Detect NVMe controllers in /dev
> + * - Enumerate namespaces for detected controllers
> + * - Verify PCI enumeration (class code 0x010802)
> + * - Check NVMe driver binding
> + */
> +
> +#define _GNU_SOURCE
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <dirent.h>
> +#include <errno.h>
> +
> +#include "tst_test.h"
> +#include "tst_safe_stdio.h"
> +
> +#define NVME_DEV_PATH "/dev"
> +#define NVME_SYS_PATH "/sys/class/nvme"
> +#define PCI_DEVICES_PATH "/sys/bus/pci/devices"
> +
> +static int nvme_device_found;
> +static char nvme_dev_name[256];
> +
> +static void test_detect_nvme_controllers(void)
> +{
> + DIR *dir;
> + struct dirent *entry;
> + int found = 0;
> +
> + dir = SAFE_OPENDIR(NVME_DEV_PATH);
> +
> + tst_res(TINFO, "Scanning for NVMe devices in %s", NVME_DEV_PATH);
> +
> + while ((entry = readdir(dir)) != NULL) {
> + if (strncmp(entry->d_name, "nvme", 4) == 0 &&
> + strlen(entry->d_name) == 5) {
> + tst_res(TINFO, "Found NVMe controller: %s",
> entry->d_name);
> + strncpy(nvme_dev_name, entry->d_name,
> sizeof(nvme_dev_name) - 1);
> + found = 1;
> + nvme_device_found = 1;
> + }
> + }
> +
> + SAFE_CLOSEDIR(dir);
> +
> + if (found)
> + tst_res(TPASS, "NVMe controller(s) detected successfully");
> + else
> + tst_res(TCONF, "No NVMe controllers found in system");
> +}
> +
> +static void test_enumerate_namespaces(void)
> +{
> + DIR *dir;
> + struct dirent *entry;
> + int ns_count = 0;
> + char search_pattern[32];
> +
> + if (!nvme_device_found) {
> + tst_res(TCONF, "No NVMe device found, skipping namespace
> enumeration");
> + return;
> + }
> +
> + snprintf(search_pattern, sizeof(search_pattern), "%sn",
> nvme_dev_name);
> +
> + dir = SAFE_OPENDIR(NVME_DEV_PATH);
> +
> + tst_res(TINFO, "Enumerating namespaces for %s", nvme_dev_name);
> +
> + while ((entry = readdir(dir)) != NULL) {
> + if (strncmp(entry->d_name, search_pattern,
> strlen(search_pattern)) == 0) {
> + tst_res(TINFO, "Found namespace: %s",
> entry->d_name);
> + ns_count++;
> + }
> + }
> +
> + SAFE_CLOSEDIR(dir);
> +
> + if (ns_count > 0)
> + tst_res(TPASS, "Enumerated %d namespace(s) successfully",
> ns_count);
> + else
> + tst_res(TPASS, "No namespaces found for %s (device may not
> have namespaces configured)", nvme_dev_name);
> +}
> +
> +static void test_verify_pci_enumeration(void)
> +{
> + DIR *dir;
> + struct dirent *entry;
> + int found = 0;
> + char class_path[512];
> + FILE *fp;
> + char class_id[16];
> +
> + if (!nvme_device_found) {
> + tst_res(TCONF, "No NVMe device found, skipping PCI
> enumeration check");
> + return;
> + }
> +
> + dir = SAFE_OPENDIR(PCI_DEVICES_PATH);
> +
> + tst_res(TINFO, "Checking PCI enumeration for NVMe devices");
> +
> + while ((entry = readdir(dir)) != NULL) {
> + if (entry->d_name[0] == '.')
> + continue;
> +
> + snprintf(class_path, sizeof(class_path), "%s/%s/class",
> + PCI_DEVICES_PATH, entry->d_name);
> +
> + fp = SAFE_FOPEN(class_path, "r");
> +
> + if (fgets(class_id, sizeof(class_id), fp)) {
> + if (strncmp(class_id, "0x010802", 8) == 0) {
> + tst_res(TINFO, "Found NVMe PCI device: %s
> (class: %s)",
> + entry->d_name, class_id);
> + found = 1;
> + }
> + }
> + SAFE_FCLOSE(fp);
> + }
> +
> + SAFE_CLOSEDIR(dir);
> +
> + if (found)
> + tst_res(TPASS, "NVMe device properly enumerated on PCI
> bus");
> + else
> + tst_res(TFAIL, "NVMe device not found on PCI bus");
> +}
> +
> +static void test_check_driver_binding(void)
> +{
> + char driver_path[512];
> + char driver_link[512];
> +
> + if (!nvme_device_found) {
> + tst_res(TCONF, "No NVMe device found, skipping driver
> binding check");
> + return;
> + }
> +
> + snprintf(driver_path, sizeof(driver_path), "%s/%s/device/driver",
> + NVME_SYS_PATH, nvme_dev_name);
> +
> + SAFE_READLINK(driver_path, driver_link, sizeof(driver_link));
> +
> + tst_res(TINFO, "Driver binding: %s", driver_link);
> +
> + if (strstr(driver_link, "nvme") != NULL)
> + tst_res(TPASS, "NVMe driver properly bound to device");
> + else
> + tst_res(TFAIL, "Unexpected driver bound to NVMe device:
> %s", driver_link);
> +}
> +
> +static void setup(void)
> +{
> + if (access("/sys/module/nvme", F_OK) != 0)
> + tst_brk(TCONF, "NVMe kernel module not loaded");
> +
> + tst_res(TINFO, "NVMe Device Discovery & Identification Test");
> +}
> +
> +static void run(unsigned int n)
> +{
> + /* Reset state for each iteration */
> + nvme_device_found = 0;
> + memset(nvme_dev_name, 0, sizeof(nvme_dev_name));
> +
> + switch (n) {
> + case 0:
> + test_detect_nvme_controllers();
> + break;
> + case 1:
> + test_enumerate_namespaces();
> + break;
> + case 2:
> + test_verify_pci_enumeration();
> + break;
> + case 3:
> + test_check_driver_binding();
> + break;
> + }
> +}
> +
> +static struct tst_test test = {
> + .test = run,
> + .tcnt = 4,
> + .setup = setup,
> +};
> --
> 2.52.0
>
>
> --
> Mailing list info: https://lists.linux.it/listinfo/ltp
>
More information about the ltp
mailing list