From ec540b77c53e57478d9470726811b11f21960ed6 Mon Sep 17 00:00:00 2001 From: Tomas Bzatek Date: Sun, 21 Dec 2025 14:52:50 +0100 Subject: [PATCH 1/3] nvme: Add bd_nvme_find_namespaces_for_ctrl() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Utility function to find all namespaces associated with a given controller. This mirrors bd_nvme_find_ctrls_for_ns() but performs the reverse operation - looking up namespaces for a controller. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- docs/libblockdev-sections.txt | 1 + src/lib/plugin_apis/nvme.api | 18 ++++++++ src/plugins/nvme/nvme-fabrics.c | 74 +++++++++++++++++++++++++++++++++ src/plugins/nvme/nvme.h | 6 +++ 4 files changed, 99 insertions(+) diff --git a/docs/libblockdev-sections.txt b/docs/libblockdev-sections.txt index 48f52edf..4a6996fa 100644 --- a/docs/libblockdev-sections.txt +++ b/docs/libblockdev-sections.txt @@ -834,6 +834,7 @@ bd_nvme_connect bd_nvme_disconnect bd_nvme_disconnect_by_path bd_nvme_find_ctrls_for_ns +bd_nvme_find_namespaces_for_ctrl
diff --git a/src/lib/plugin_apis/nvme.api b/src/lib/plugin_apis/nvme.api index b37b54e9..9be1278d 100644 --- a/src/lib/plugin_apis/nvme.api +++ b/src/lib/plugin_apis/nvme.api @@ -1382,4 +1382,22 @@ gboolean bd_nvme_disconnect_by_path (const gchar *path, GError **error); */ gchar ** bd_nvme_find_ctrls_for_ns (const gchar *ns_sysfs_path, const gchar *subsysnqn, const gchar *host_nqn, const gchar *host_id, GError **error); +/** + * bd_nvme_find_namespaces_for_ctrl: + * @ctrl_sysfs_path: NVMe controller sysfs path. + * @subsysnqn: (nullable): Limit matching to the specified subsystem NQN. + * @host_nqn: (nullable): Limit matching to the specified host NQN. + * @host_id: (nullable): Limit matching to the specified host ID. + * @error: (out) (nullable): Place to store error (if any). + * + * A convenient utility function to look up all namespaces associated + * with the specified NVMe controller. + * + * Returns: (transfer full) (array zero-terminated=1): list of namespace sysfs paths + * or %NULL in case of an error (with @error set). + * + * Tech category: %BD_NVME_TECH_FABRICS-%BD_NVME_TECH_MODE_INITIATOR + */ +gchar ** bd_nvme_find_namespaces_for_ctrl (const gchar *ctrl_sysfs_path, const gchar *subsysnqn, const gchar *host_nqn, const gchar *host_id, GError **error); + #endif /* BD_NVME_API */ diff --git a/src/plugins/nvme/nvme-fabrics.c b/src/plugins/nvme/nvme-fabrics.c index 1fb6d29f..a7457783 100644 --- a/src/plugins/nvme/nvme-fabrics.c +++ b/src/plugins/nvme/nvme-fabrics.c @@ -521,6 +521,80 @@ gchar ** bd_nvme_find_ctrls_for_ns (const gchar *ns_sysfs_path, const gchar *sub } +/** + * bd_nvme_find_namespaces_for_ctrl: + * @ctrl_sysfs_path: NVMe controller sysfs path. + * @subsysnqn: (nullable): Limit matching to the specified subsystem NQN. + * @host_nqn: (nullable): Limit matching to the specified host NQN. + * @host_id: (nullable): Limit matching to the specified host ID. + * @error: (out) (nullable): Place to store error (if any). + * + * A convenient utility function to look up all namespaces associated + * with the specified NVMe controller. + * + * Returns: (transfer full) (array zero-terminated=1): list of namespace sysfs paths + * or %NULL in case of an error (with @error set). + * + * Tech category: %BD_NVME_TECH_FABRICS-%BD_NVME_TECH_MODE_INITIATOR + */ +gchar ** bd_nvme_find_namespaces_for_ctrl (const gchar *ctrl_sysfs_path, const gchar *subsysnqn, const gchar *host_nqn, const gchar *host_id, GError **error G_GNUC_UNUSED) { + GPtrArray *ptr_array; + nvme_root_t root; + nvme_host_t h; + nvme_subsystem_t s; + nvme_ctrl_t c; + nvme_ns_t n; + char realp[PATH_MAX]; + gchar *subsysnqn_p; + + /* libnvme strips trailing spaces and newlines when reading values from sysfs */ + subsysnqn_p = g_strdup (subsysnqn); + if (subsysnqn_p) + g_strchomp (subsysnqn_p); + + ptr_array = g_ptr_array_new (); + + root = nvme_scan (NULL); + g_warn_if_fail (root != NULL); + + nvme_for_each_host (root, h) { + if (host_nqn && g_strcmp0 (nvme_host_get_hostnqn (h), host_nqn) != 0) + continue; + if (host_id && g_strcmp0 (nvme_host_get_hostid (h), host_id) != 0) + continue; + + nvme_for_each_subsystem (h, s) { + if (subsysnqn && g_strcmp0 (nvme_subsystem_get_nqn (s), subsysnqn_p) != 0) + continue; + + nvme_subsystem_for_each_ctrl (s, c) { + if (realpath (nvme_ctrl_get_sysfs_dir (c), realp) && + g_strcmp0 (realp, ctrl_sysfs_path) == 0) { + /* Found matching controller, add all its namespaces */ + nvme_ctrl_for_each_ns (c, n) { + if (realpath (nvme_ns_get_sysfs_dir (n), realp)) { + g_ptr_array_add (ptr_array, g_strdup (realp)); + } + } + /* Also check subsystem-level namespaces */ + nvme_subsystem_for_each_ns (s, n) { + if (realpath (nvme_ns_get_sysfs_dir (n), realp)) { + g_ptr_array_add (ptr_array, g_strdup (realp)); + } + } + break; + } + } + } + } + nvme_free_tree (root); + g_free (subsysnqn_p); + + g_ptr_array_add (ptr_array, NULL); /* trailing NULL element */ + return (gchar **) g_ptr_array_free (ptr_array, FALSE); +} + + /** * bd_nvme_get_host_nqn: * @error: (out) (nullable): Place to store error (if any). diff --git a/src/plugins/nvme/nvme.h b/src/plugins/nvme/nvme.h index 1281ec48..ae3e4f8f 100644 --- a/src/plugins/nvme/nvme.h +++ b/src/plugins/nvme/nvme.h @@ -629,5 +629,11 @@ gchar ** bd_nvme_find_ctrls_for_ns (const gchar *ns_sysf const gchar *host_id, GError **error); +gchar ** bd_nvme_find_namespaces_for_ctrl (const gchar *ctrl_sysfs_path, + const gchar *subsysnqn, + const gchar *host_nqn, + const gchar *host_id, + GError **error); + #endif /* BD_NVME */ From fbc950af99988f797af2399665a07f30c962111f Mon Sep 17 00:00:00 2001 From: Tomas Bzatek Date: Sun, 21 Dec 2025 14:54:22 +0100 Subject: [PATCH 2/3] nvme: Clarify argument doc strings for bd_nvme_find_ctrls_for_ns() --- src/lib/plugin_apis/nvme.api | 2 +- src/plugins/nvme/nvme-fabrics.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/plugin_apis/nvme.api b/src/lib/plugin_apis/nvme.api index 9be1278d..08cd2f99 100644 --- a/src/lib/plugin_apis/nvme.api +++ b/src/lib/plugin_apis/nvme.api @@ -1366,7 +1366,7 @@ gboolean bd_nvme_disconnect_by_path (const gchar *path, GError **error); /** * bd_nvme_find_ctrls_for_ns: - * @ns_sysfs_path: NVMe namespace device file. + * @ns_sysfs_path: NVMe namespace sysfs path. * @subsysnqn: (nullable): Limit matching to the specified subsystem NQN. * @host_nqn: (nullable): Limit matching to the specified host NQN. * @host_id: (nullable): Limit matching to the specified host ID. diff --git a/src/plugins/nvme/nvme-fabrics.c b/src/plugins/nvme/nvme-fabrics.c index a7457783..4445141e 100644 --- a/src/plugins/nvme/nvme-fabrics.c +++ b/src/plugins/nvme/nvme-fabrics.c @@ -440,7 +440,7 @@ gboolean bd_nvme_disconnect_by_path (const gchar *path, GError **error) { /** * bd_nvme_find_ctrls_for_ns: - * @ns_sysfs_path: NVMe namespace device file. + * @ns_sysfs_path: NVMe namespace sysfs path. * @subsysnqn: (nullable): Limit matching to the specified subsystem NQN. * @host_nqn: (nullable): Limit matching to the specified host NQN. * @host_id: (nullable): Limit matching to the specified host ID. From e708e495552e3b687f2b1ff584e2cbe94745a22a Mon Sep 17 00:00:00 2001 From: Tomas Bzatek Date: Sun, 21 Dec 2025 15:06:29 +0100 Subject: [PATCH 3/3] tests: Add tests for bd_nvme_find_namespaces_for_ctrl() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- tests/nvme_test.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/nvme_test.py b/tests/nvme_test.py index 296664bb..a785ec82 100644 --- a/tests/nvme_test.py +++ b/tests/nvme_test.py @@ -476,6 +476,32 @@ def test_connect_multiple_ns(self): ctrl_sysfs_paths = BlockDev.nvme_find_ctrls_for_ns(ns_sysfs_path, self.SUBNQN, self.hostnqn, "unknownhostid") self.assertEqual(len(ctrl_sysfs_paths), 0) + # test bd_nvme_find_namespaces_for_ctrl() + ns_sysfs_paths = BlockDev.nvme_find_namespaces_for_ctrl(ctrl_sysfs_path, None, None, None) + self.assertIsNotNone(ns_sysfs_paths) + self.assertEqual(len(ns_sysfs_paths), NUM_NS) + self.assertIn(ns_sysfs_path, ns_sysfs_paths) + + ns_sysfs_paths = BlockDev.nvme_find_namespaces_for_ctrl(ctrl_sysfs_path + "xxx", None, None, None) + self.assertEqual(len(ns_sysfs_paths), 0) + ns_sysfs_paths = BlockDev.nvme_find_namespaces_for_ctrl(ctrl_sysfs_path, self.SUBNQN, None, None) + self.assertEqual(len(ns_sysfs_paths), NUM_NS) + self.assertIn(ns_sysfs_path, ns_sysfs_paths) + ns_sysfs_paths = BlockDev.nvme_find_namespaces_for_ctrl(ctrl_sysfs_path, self.SUBNQN, self.hostnqn, None) + self.assertEqual(len(ns_sysfs_paths), NUM_NS) + self.assertIn(ns_sysfs_path, ns_sysfs_paths) + + ns_sysfs_paths = BlockDev.nvme_find_namespaces_for_ctrl(ctrl_sysfs_path, "unknownsubsysnqn", None, None) + self.assertEqual(len(ns_sysfs_paths), 0) + ns_sysfs_paths = BlockDev.nvme_find_namespaces_for_ctrl(ctrl_sysfs_path, None, "unknownhostnqn", None) + self.assertEqual(len(ns_sysfs_paths), 0) + ns_sysfs_paths = BlockDev.nvme_find_namespaces_for_ctrl(ctrl_sysfs_path, self.SUBNQN, "unknownhostnqn", None) + self.assertEqual(len(ns_sysfs_paths), 0) + ns_sysfs_paths = BlockDev.nvme_find_namespaces_for_ctrl(ctrl_sysfs_path, None, None, "unknownhostid") + self.assertEqual(len(ns_sysfs_paths), 0) + ns_sysfs_paths = BlockDev.nvme_find_namespaces_for_ctrl(ctrl_sysfs_path, self.SUBNQN, self.hostnqn, "unknownhostid") + self.assertEqual(len(ns_sysfs_paths), 0) + # disconnect BlockDev.nvme_disconnect_by_path(ctrls[0]) for c in ctrls: