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..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. @@ -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..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. @@ -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 */ 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: