Blob Blame History Raw
From b0b6f77b3c75ca881bf21771f629c0716378ece2 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Mon, 29 Jan 2018 13:56:17 +0100
Subject: [PATCH 1/4] Add the NVDIMM plugin

Add a new plugin for working with the NVDIMM devices. Currently
only namespace management is supported.
---
 Makefile.am                         |   1 +
 configure.ac                        |   6 +
 dist/libblockdev.spec.in            |  48 +++-
 docs/libblockdev-docs.xml.in        |   1 +
 docs/libblockdev-sections.txt       |  22 ++
 src/lib/Makefile.am                 |   3 +-
 src/lib/blockdev.c.in               |  14 +-
 src/lib/plugin_apis/nvdimm.api      | 206 ++++++++++++++
 src/lib/plugins.h                   |   1 +
 src/plugins/Makefile.am             |  16 ++
 src/plugins/nvdimm.c                | 544 ++++++++++++++++++++++++++++++++++++
 src/plugins/nvdimm.h                |  76 +++++
 src/python/gi/overrides/BlockDev.py |  43 +++
 13 files changed, 977 insertions(+), 4 deletions(-)
 create mode 100644 src/lib/plugin_apis/nvdimm.api
 create mode 100644 src/plugins/nvdimm.c
 create mode 100644 src/plugins/nvdimm.h

diff --git a/Makefile.am b/Makefile.am
index ad41c1b..d30acd1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -41,6 +41,7 @@ PLUGINS = btrfs \
 	lvm \
 	mdraid \
 	mpath \
+	nvdimm \
 	part \
 	s390 \
 	swap
diff --git a/configure.ac b/configure.ac
index d41a307..4206afa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -141,6 +141,7 @@ LIBBLOCKDEV_PLUGIN([SWAP], [swap])
 LIBBLOCKDEV_PLUGIN([KBD], [kbd])
 LIBBLOCKDEV_PLUGIN([PART], [part])
 LIBBLOCKDEV_PLUGIN([FS], [fs])
+LIBBLOCKDEV_PLUGIN([NVDIMM], [nvdimm])
 
 AM_CONDITIONAL(WITH_PART_O_WITH_FS, test "x$with_part" != "xno" -o "x$with_fs" != "xno")
 
@@ -194,6 +195,11 @@ AS_IF([test "x$with_btrfs" != "xno" -o "x$with_mdraid" != "xno" -o "x$with_kbd"
       [LIBBLOCKDEV_PKG_CHECK_MODULES([BYTESIZE], [bytesize >= 0.1])],
       [])
 
+AS_IF([test "x$with_nvdimm" != "xno"],
+      [LIBBLOCKDEV_PKG_CHECK_MODULES([UUID], [uuid])
+       LIBBLOCKDEV_PKG_CHECK_MODULES([NDCTL], [libndctl])]
+      [])
+
 AC_SUBST([skip_patterns], [$skip_patterns])
 AC_SUBST([MAJOR_VER], [\"2\"])
 
diff --git a/dist/libblockdev.spec.in b/dist/libblockdev.spec.in
index b23bb5b..5e2f52b 100644
--- a/dist/libblockdev.spec.in
+++ b/dist/libblockdev.spec.in
@@ -13,6 +13,7 @@
 %define with_kbd @WITH_KBD@
 %define with_part @WITH_PART@
 %define with_fs @WITH_FS@
+%define with_nvdimm @WITH_NVDIMM@
 %define with_gi @WITH_GI@
 
 # python3 is not available on older RHEL
@@ -59,11 +60,14 @@
 %if %{with_fs} != 1
 %define fs_copts --without-fs
 %endif
+%if %{with_nvdimm} != 1
+%define nvdimm_copts --without-nvdimm
+%endif
 %if %{with_gi} != 1
 %define gi_copts --disable-introspection
 %endif
 
-%define configure_opts %{?distro_copts} %{?btrfs_copts} %{?crypto_copts} %{?dm_copts} %{?loop_copts} %{?lvm_copts} %{?lvm_dbus_copts} %{?mdraid_copts} %{?mpath_copts} %{?swap_copts} %{?kbd_copts} %{?part_copts} %{?fs_copts} %{?gi_copts}
+%define configure_opts %{?distro_copts} %{?btrfs_copts} %{?crypto_copts} %{?dm_copts} %{?loop_copts} %{?lvm_copts} %{?lvm_dbus_copts} %{?mdraid_copts} %{?mpath_copts} %{?swap_copts} %{?kbd_copts} %{?part_copts} %{?fs_copts} %{?nvdimm_copts} %{?gi_copts}
 
 Name:        libblockdev
 Version:     2.16
@@ -394,6 +398,29 @@ This package contains header files and pkg-config files needed for development
 with the libblockdev-mpath plugin/library.
 %endif
 
+%if %{with_nvdimm}
+%package nvdimm
+BuildRequires: ndctl-devel
+BuildRequires: libuuid-devel
+Summary:     The NVDIMM plugin for the libblockdev library
+Requires: %{name}-utils%{?_isa} >= 0.11
+Requires: ndctl
+
+%description nvdimm
+The libblockdev library plugin (and in the same time a standalone library)
+providing the functionality related to operations with NVDIMM devices.
+
+%package nvdimm-devel
+Summary:     Development files for the libblockdev-nvdimm plugin/library
+Requires: %{name}-nvdimm%{?_isa} = %{version}-%{release}
+Requires: %{name}-utils-devel%{?_isa}
+Requires: glib2-devel
+
+%description nvdimm-devel
+This package contains header files and pkg-config files needed for development
+with the libblockdev-nvdimm plugin/library.
+%endif
+
 
 %if %{with_part}
 %package part
@@ -502,6 +529,10 @@ Requires: %{name}-mdraid%{?_isa} = %{version}-%{release}
 Requires: %{name}-mpath%{?_isa} = %{version}-%{release}
 %endif
 
+%if %{with_nvdimm}
+Requires: %{name}-nvdimm%{?_isa} = %{version}-%{release}
+%endif
+
 %if %{with_part}
 Requires: %{name}-part%{?_isa} = %{version}-%{release}
 %endif
@@ -580,6 +611,10 @@ find %{buildroot} -type f -name "*.la" | xargs %{__rm}
 %postun mpath -p /sbin/ldconfig
 %endif
 
+%if %{with_nvdimm}
+%ldconfig_scriptlets nvdimm
+%endif
+
 %if %{with_part}
 %post part -p /sbin/ldconfig
 %postun part -p /sbin/ldconfig
@@ -765,6 +800,17 @@ find %{buildroot} -type f -name "*.la" | xargs %{__rm}
 %endif
 
 
+%if %{with_nvdimm}
+%files nvdimm
+%{_libdir}/libbd_nvdimm.so.*
+
+%files nvdimm-devel
+%{_libdir}/libbd_nvdimm.so
+%dir %{_includedir}/blockdev
+%{_includedir}/blockdev/nvdimm.h
+%endif
+
+
 %if %{with_part}
 %files part
 %{_libdir}/libbd_part.so.*
diff --git a/docs/libblockdev-docs.xml.in b/docs/libblockdev-docs.xml.in
index a758657..98aa0bf 100644
--- a/docs/libblockdev-docs.xml.in
+++ b/docs/libblockdev-docs.xml.in
@@ -29,6 +29,7 @@
     <xi:include href="xml/lvm.xml"/>
     <xi:include href="xml/mdraid.xml"/>
     <xi:include href="xml/mpath.xml"/>
+    <xi:include href="xml/nvdimm.xml"/>
     <xi:include href="xml/plugins.xml"/>
     <xi:include href="xml/part.xml"/>
     <xi:include href="xml/swap.xml"/>
diff --git a/docs/libblockdev-sections.txt b/docs/libblockdev-sections.txt
index cc0ae7a..756b5b5 100644
--- a/docs/libblockdev-sections.txt
+++ b/docs/libblockdev-sections.txt
@@ -565,3 +565,25 @@ BDS390Tech
 BDS390TechMode
 bd_s390_is_tech_avail
 </SECTION>
+
+<SECTION>
+<FILE>nvdimm</FILE>
+bd_nvdimm_check_deps
+bd_nvdimm_close
+bd_nvdimm_init
+bd_nvdimm_error_quark
+BD_NVDIMM_ERROR
+BDNVDIMMError
+BDNVDIMMNamespaceMode
+BDNVDIMMNamespaceInfo
+bd_nvdimm_namespace_get_mode_from_str
+bd_nvdimm_namespace_get_mode_str
+bd_nvdimm_namespace_enable
+bd_nvdimm_namespace_disable
+bd_nvdimm_namespace_info
+bd_nvdimm_list_namespaces
+bd_nvdimm_namespace_reconfigure
+BDNVDIMMTech
+BDNVDIMMTechMode
+bd_nvdimm_is_tech_avail
+</SECTION>
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index e020703..d9a91ca 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -30,7 +30,8 @@ GIHEADERS = ${builddir}/plugin_apis/kbd.h \
 	${builddir}/plugin_apis/loop.h \
 	${builddir}/plugin_apis/mpath.h \
 	${builddir}/plugin_apis/part.h \
-	${builddir}/plugin_apis/fs.h
+	${builddir}/plugin_apis/fs.h \
+	${builddir}/plugin_apis/nvdimm.h
 
 GIHEADERS += $(wildcard ${srcdir}/../utils/*.[ch])
 GIHEADERS += blockdev.c blockdev.h plugins.c plugins.h
diff --git a/src/lib/blockdev.c.in b/src/lib/blockdev.c.in
index 2aeeac2..fd36fee 100644
--- a/src/lib/blockdev.c.in
+++ b/src/lib/blockdev.c.in
@@ -25,6 +25,8 @@
 #include "plugin_apis/part.c"
 #include "plugin_apis/fs.h"
 #include "plugin_apis/fs.c"
+#include "plugin_apis/nvdimm.h"
+#include "plugin_apis/nvdimm.c"
 
 #if defined(__s390__) || defined(__s390x__)
 #include "plugin_apis/s390.h"
@@ -59,7 +61,8 @@ static gchar * default_plugin_so[BD_PLUGIN_UNDEF] = {
     "libbd_crypto.so."@MAJOR_VER@, "libbd_mpath.so."@MAJOR_VER@,
     "libbd_dm.so."@MAJOR_VER@, "libbd_mdraid.so."@MAJOR_VER@,
     "libbd_kbd.so."@MAJOR_VER@,"libbd_s390.so."@MAJOR_VER@,
-    "libbd_part.so."@MAJOR_VER@, "libbd_fs.so."@MAJOR_VER@
+    "libbd_part.so."@MAJOR_VER@, "libbd_fs.so."@MAJOR_VER@,
+    "libbd_nvdimm.so."@MAJOR_VER@
 };
 static BDPluginStatus plugins[BD_PLUGIN_UNDEF] = {
     {{BD_PLUGIN_LVM, NULL}, NULL},
@@ -74,9 +77,10 @@ static BDPluginStatus plugins[BD_PLUGIN_UNDEF] = {
     {{BD_PLUGIN_S390, NULL}, NULL},
     {{BD_PLUGIN_PART, NULL}, NULL},
     {{BD_PLUGIN_FS, NULL}, NULL},
+    {{BD_PLUGIN_NVDIMM, NULL}, NULL},
 };
 static gchar* plugin_names[BD_PLUGIN_UNDEF] = {
-    "lvm", "btrfs", "swap", "loop", "crypto", "mpath", "dm", "mdraid", "kbd", "s390", "part", "fs"
+    "lvm", "btrfs", "swap", "loop", "crypto", "mpath", "dm", "mdraid", "kbd", "s390", "part", "fs", "nvdimm"
 };
 
 static void set_plugin_so_name (BDPlugin name, const gchar *so_name) {
@@ -226,6 +230,10 @@ static void unload_plugins () {
     if (plugins[BD_PLUGIN_FS].handle && !unload_fs (plugins[BD_PLUGIN_FS].handle))
         g_warning ("Failed to close the fs plugin");
     plugins[BD_PLUGIN_FS].handle = NULL;
+
+    if (plugins[BD_PLUGIN_NVDIMM].handle && !unload_nvdimm (plugins[BD_PLUGIN_NVDIMM].handle))
+        g_warning ("Failed to close the nvdimm plugin");
+    plugins[BD_PLUGIN_NVDIMM].handle = NULL;
 }
 
 static void load_plugin_from_sonames (BDPlugin plugin, LoadFunc load_fn, void **handle, GSList *sonames) {
@@ -264,6 +272,8 @@ static void do_load (GSList **plugins_sonames) {
         load_plugin_from_sonames (BD_PLUGIN_PART, load_part_from_plugin, &(plugins[BD_PLUGIN_PART].handle), plugins_sonames[BD_PLUGIN_PART]);
     if (!plugins[BD_PLUGIN_FS].handle && plugins_sonames[BD_PLUGIN_FS])
         load_plugin_from_sonames (BD_PLUGIN_FS, load_fs_from_plugin, &(plugins[BD_PLUGIN_FS].handle), plugins_sonames[BD_PLUGIN_FS]);
+    if (!plugins[BD_PLUGIN_NVDIMM].handle && plugins_sonames[BD_PLUGIN_NVDIMM])
+        load_plugin_from_sonames (BD_PLUGIN_NVDIMM, load_nvdimm_from_plugin, &(plugins[BD_PLUGIN_NVDIMM].handle), plugins_sonames[BD_PLUGIN_NVDIMM]);
 }
 
 static gboolean load_plugins (BDPluginSpec **require_plugins, gboolean reload, guint64 *num_loaded) {
diff --git a/src/lib/plugin_apis/nvdimm.api b/src/lib/plugin_apis/nvdimm.api
new file mode 100644
index 0000000..3d74f62
--- /dev/null
+++ b/src/lib/plugin_apis/nvdimm.api
@@ -0,0 +1,206 @@
+#include <glib.h>
+#include <glib-object.h>
+#include <blockdev/utils.h>
+
+#ifndef BD_NVDIMM_API
+#define BD_NVDIMM_API
+
+GQuark bd_nvdimm_error_quark (void) {
+    return g_quark_from_static_string ("g-bd-nvdimm-error-quark");
+}
+
+#define BD_NVDIMM_ERROR bd_nvdimm_error_quark ()
+typedef enum {
+    BD_NVDIMM_ERROR_NAMESPACE_PARSE,
+    BD_NVDIMM_ERROR_NAMESPACE_FAIL,
+    BD_NVDIMM_ERROR_NAMESPACE_NOEXIST,
+    BD_NVDIMM_ERROR_NAMESPACE_MODE_INVAL
+} BDNVDIMMError;
+
+typedef enum {
+    BD_NVDIMM_NAMESPACE_MODE_RAW,
+    BD_NVDIMM_NAMESPACE_MODE_SECTOR,
+    BD_NVDIMM_NAMESPACE_MODE_MEMORY,
+    BD_NVDIMM_NAMESPACE_MODE_DAX,
+    BD_NVDIMM_NAMESPACE_MODE_UNKNOWN
+} BDNVDIMMNamespaceMode;
+
+#define BD_NVDIMM_TYPE_NAMESPACE_INFO (bd_nvdimm_namespace_info_get_type ())
+GType bd_nvdimm_namespace_info_get_type();
+
+/**
+ * BDNVDIMMNamespaceInfo:
+ * @dev: namespace device name ("namespaceX.Y")
+ * @mode: mode of the namespace (BDNVDIMMNamespaceMode)
+ * @size: size of the namespace
+ * @uuid: UUID of the namespace
+ * @sector_size: sector size of the namespace (0 for non-sector namespaces)
+ * @blockdev: name of the block device for the namespace
+ * @enabled: whether the namespace is enabled or not
+ */
+typedef struct BDNVDIMMNamespaceInfo {
+    gchar *dev;
+    guint64 mode;
+    guint64 size;
+    gchar *uuid;
+    guint64 sector_size;
+    gchar *blockdev;
+    gboolean enabled;
+} BDNVDIMMNamespaceInfo;
+
+/**
+ * bd_nvdimm_namespace_info_free: (skip)
+ *
+ * Frees @info.
+ */
+void bd_nvdimm_namespace_info_free (BDNVDIMMNamespaceInfo *info) {
+    g_free (info->dev);
+    g_free (info->uuid);
+    g_free (info->blockdev);
+    g_free (info);
+}
+
+/**
+ * bd_nvdimm_namespace_info_copy: (skip)
+ *
+ * Creates a new copy of @info.
+ */
+BDNVDIMMNamespaceInfo* bd_nvdimm_namespace_info_copy (BDNVDIMMNamespaceInfo *info) {
+    BDNVDIMMNamespaceInfo *new_info = g_new0 (BDNVDIMMNamespaceInfo, 1);
+
+    new_info->dev = info->dev;
+    new_info->mode = info->mode;
+    new_info->size = info->size;
+    new_info->uuid = g_strdup (info->uuid);
+    new_info->sector_size = info->sector_size;
+    new_info->blockdev = g_strdup (info->blockdev);
+    new_info->enabled = info->enabled;
+
+    return new_info;
+}
+
+GType bd_nvdimm_namespace_info_get_type () {
+    static GType type = 0;
+
+    if (G_UNLIKELY(type == 0)) {
+        type = g_boxed_type_register_static("BDNVDIMMNamespaceInfo",
+                                            (GBoxedCopyFunc) bd_nvdimm_namespace_info_copy,
+                                            (GBoxedFreeFunc) bd_nvdimm_namespace_info_free);
+    }
+
+    return type;
+}
+
+typedef enum {
+    BD_NVDIMM_TECH_NAMESPACE = 0,
+} BDNVDIMMTech;
+
+typedef enum {
+    BD_NVDIMM_TECH_MODE_CREATE              = 1 << 0,
+    BD_NVDIMM_TECH_MODE_REMOVE              = 1 << 1,
+    BD_NVDIMM_TECH_MODE_ACTIVATE_DEACTIVATE = 1 << 2,
+    BD_NVDIMM_TECH_MODE_QUERY               = 1 << 3,
+    BD_NVDIMM_TECH_MODE_RECONFIGURE         = 1 << 4,
+} BDNVDIMMTechMode;
+
+/**
+ * bd_nvdimm_is_tech_avail:
+ * @tech: the queried tech
+ * @mode: a bit mask of queried modes of operation (#BDNVDIMMTechMode) for @tech
+ * @error: (out): place to store error (details about why the @tech-@mode combination is not available)
+ *
+ * Returns: whether the @tech-@mode combination is available -- supported by the
+ *          plugin implementation and having all the runtime dependencies available
+ */
+gboolean bd_nvdimm_is_tech_avail (BDNVDIMMTech tech, guint64 mode, GError **error);
+
+/**
+ * bd_nvdimm_namespace_get_mode_from_str:
+ * @mode_str: string representation of mode
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: mode matching the @mode_str given or %BD_NVDIMM_NAMESPACE_MODE_UNKNOWN in case of no match
+ *
+ * Tech category: always available
+ */
+BDNVDIMMNamespaceMode bd_nvdimm_namespace_get_mode_from_str (const gchar *mode_str, GError **error);
+
+/**
+ * bd_nvdimm_namespace_get_mode_str:
+ * @mode: mode to get string representation of
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: (transfer none): string representation of @mode or %NULL in case of error
+ *
+ * Tech category: always available
+ */
+const gchar* bd_nvdimm_namespace_get_mode_str (BDNVDIMMNamespaceMode mode, GError **error);
+
+/**
+ * bd_nvdimm_namespace_enable:
+ * @namespace: name of the namespace to enable
+ * @extra: (allow-none) (array zero-terminated=1): extra options (currently unused)
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether the @namespace was successfully enabled or not
+ *
+ * Tech category: %BD_NVDIMM_TECH_NAMESPACE-%BD_NVDIMM_TECH_MODE_ACTIVATE_DEACTIVATE
+ */
+gboolean bd_nvdimm_namespace_enable (const gchar *namespace, const BDExtraArg **extra, GError **error);
+
+/**
+ * bd_nvdimm_namespace_disable:
+ * @namespace: name of the namespace to disable
+ * @extra: (allow-none) (array zero-terminated=1): extra options (currently unused)
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether the @namespace was successfully disabled or not
+ *
+ * Tech category: %BD_NVDIMM_TECH_NAMESPACE-%BD_NVDIMM_TECH_MODE_ACTIVATE_DEACTIVATE
+ */
+gboolean bd_nvdimm_namespace_disable (const gchar *namespace, const BDExtraArg **extra, GError **error);
+
+/**
+ * bd_nvdimm_namespace_info:
+ * @namespace: namespace to get information about
+ * @extra: (allow-none) (array zero-terminated=1): extra options (currently unused)
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: (transfer full): information about given namespace or %NULL if no such
+ *                           namespace was found (@error may be set to indicate error)
+ *
+ * Tech category: %BD_NVDIMM_TECH_NAMESPACE-%BD_NVDIMM_TECH_MODE_QUERY
+ */
+BDNVDIMMNamespaceInfo* bd_nvdimm_namespace_info (const gchar *namespace, const BDExtraArg **extra, GError **error);
+
+/**
+ * bd_nvdimm_list_namespaces:
+ * @bus: (allow-none): return only namespaces on given bus (specified by name),
+ *                     %NULL may be specified to return namespaces from all buses
+ * @region: (allow-none): return only namespaces on given region (specified by regionX name or region id),
+ *                        %NULL may be specified to return namespaces from all regions
+ * @idle: whether to list idle (not enabled) namespaces too
+ * @extra: (allow-none) (array zero-terminated=1): extra options for the creation (right now
+ *                                                 passed to the 'ndctl' utility)
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: (array zero-terminated=1): information about the namespaces on @bus and @region or
+ *                                     %NULL if no namespaces were found (@error may be set to indicate error)
+ *
+ * Tech category: %BD_NVDIMM_TECH_NAMESPACE-%BD_NVDIMM_TECH_MODE_QUERY
+ */
+BDNVDIMMNamespaceInfo** bd_nvdimm_list_namespaces (const gchar *bus, const gchar *region, gboolean idle, const BDExtraArg **extra, GError **error);
+
+/**
+ * bd_nvdimm_namespace_reconfigure:
+ * @namespace: name of the namespace to recofigure
+ * @mode: mode type to set (memory/sector/raw/dax)
+ * @error: (out): place to store error if any
+ * @extra: (allow-none) (array zero-terminated=1): extra options for the creation (right now
+ *                                                 passed to the 'ndctl' utility)
+ *
+ * Returns: whether @namespace was successfully reconfigured or not
+ */
+gboolean bd_nvdimm_namespace_reconfigure (const gchar* namespace, BDNVDIMMNamespaceMode mode, gboolean force, const BDExtraArg **extra, GError** error);
+
+#endif  /* BD_NVDIMM_API */
diff --git a/src/lib/plugins.h b/src/lib/plugins.h
index 48d2217..9bc9799 100644
--- a/src/lib/plugins.h
+++ b/src/lib/plugins.h
@@ -17,6 +17,7 @@ typedef enum {
     BD_PLUGIN_S390,
     BD_PLUGIN_PART,
     BD_PLUGIN_FS,
+    BD_PLUGIN_NVDIMM,
     BD_PLUGIN_UNDEF
 } BDPlugin;
 
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index b69c8f7..d11bdca 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -36,6 +36,10 @@ if WITH_MPATH
 lib_LTLIBRARIES += libbd_mpath.la
 endif
 
+if WITH_NVDIMM
+lib_LTLIBRARIES += libbd_nvdimm.la
+endif
+
 if WITH_SWAP
 lib_LTLIBRARIES += libbd_swap.la
 endif
@@ -122,6 +126,14 @@ libbd_mpath_la_CPPFLAGS = -I${builddir}/../../include/
 libbd_mpath_la_SOURCES = mpath.c mpath.h check_deps.c check_deps.h
 endif
 
+if WITH_NVDIMM
+libbd_nvdimm_la_CFLAGS = $(GLIB_CFLAGS) $(UUID_CFLAGS) $(NDCTL_CFLAGS) -Wall -Wextra -Werror
+libbd_nvdimm_la_LIBADD = $(GLIB_LIBS) $(UUID_LIBS) $(NDCTL_LIBS) ${builddir}/../utils/libbd_utils.la
+libbd_nvdimm_la_LDFLAGS = -L${srcdir}/../utils/ -version-info 2:0:0 -Wl,--no-undefined
+libbd_nvdimm_la_CPPFLAGS = -I${builddir}/../../include/
+libbd_nvdimm_la_SOURCES = nvdimm.c nvdimm.h check_deps.c check_deps.h
+endif
+
 if WITH_SWAP
 libbd_swap_la_CFLAGS = $(GLIB_CFLAGS) -Wall -Wextra -Werror
 libbd_swap_la_LIBADD = $(GLIB_LIBS) ${builddir}/../utils/libbd_utils.la
@@ -194,6 +206,10 @@ if WITH_MPATH
 libinclude_HEADERS += mpath.h
 endif
 
+if WITH_NVDIMM
+libinclude_HEADERS += nvdimm.h
+endif
+
 if WITH_SWAP
 libinclude_HEADERS += swap.h
 endif
diff --git a/src/plugins/nvdimm.c b/src/plugins/nvdimm.c
new file mode 100644
index 0000000..7a6802a
--- /dev/null
+++ b/src/plugins/nvdimm.c
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2018  Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Vojtech Trefny <vtrefny@redhat.com>
+ */
+
+#include <glib.h>
+#include <blockdev/utils.h>
+#include <ndctl/libndctl.h>
+#include <uuid.h>
+#include <string.h>
+
+#include "nvdimm.h"
+#include "check_deps.h"
+
+/**
+ * SECTION: nvdimm
+ * @short_description: plugin for operations with nvdimm space
+ * @title: NVDIMM
+ * @include: nvdimm.h
+ *
+ * A plugin for operations with NVDIMM devices.
+ */
+
+/**
+ * bd_nvdimm_error_quark: (skip)
+ */
+GQuark bd_nvdimm_error_quark (void) {
+    return g_quark_from_static_string ("g-bd-nvdimm-error-quark");
+}
+
+void bd_nvdimm_namespace_info_free (BDNVDIMMNamespaceInfo *info) {
+    g_free (info->dev);
+    g_free (info->uuid);
+    g_free (info->blockdev);
+    g_free (info);
+}
+
+BDNVDIMMNamespaceInfo* bd_nvdimm_namespace_info_copy (BDNVDIMMNamespaceInfo *info) {
+    BDNVDIMMNamespaceInfo *new_info = g_new0 (BDNVDIMMNamespaceInfo, 1);
+
+    new_info->dev = g_strdup (info->dev);
+    new_info->mode = info->mode;
+    new_info->size = info->size;
+    new_info->uuid = g_strdup (info->uuid);
+    new_info->sector_size = info->sector_size;
+    new_info->blockdev = g_strdup (info->blockdev);
+    new_info->enabled = info->enabled;
+
+    return new_info;
+}
+
+
+static const gchar * const mode_str[BD_NVDIMM_NAMESPACE_MODE_UNKNOWN+1] = {"raw", "sector", "memory", "dax", "unknown"};
+
+static volatile guint avail_deps = 0;
+static GMutex deps_check_lock;
+
+#define DEPS_NDCTL 0
+#define DEPS_NDCTL_MASK (1 << DEPS_NDCTL)
+#define DEPS_LAST 1
+
+static UtilDep deps[DEPS_LAST] = {
+    {"ndctl", NULL, NULL, NULL},
+};
+
+/**
+ * bd_nvdimm_check_deps:
+ *
+ * Returns: whether the plugin's runtime dependencies are satisfied or not
+ *
+ * Function checking plugin's runtime dependencies.
+ *
+ */
+gboolean bd_nvdimm_check_deps () {
+    GError *error = NULL;
+    guint i = 0;
+    gboolean status = FALSE;
+    gboolean ret = TRUE;
+
+    for (i=0; i < DEPS_LAST; i++) {
+        status = bd_utils_check_util_version (deps[i].name, deps[i].version,
+                                              deps[i].ver_arg, deps[i].ver_regexp, &error);
+        if (!status)
+            g_warning ("%s", error->message);
+        else
+            g_atomic_int_or (&avail_deps, 1 << i);
+        g_clear_error (&error);
+        ret = ret && status;
+    }
+
+    if (!ret)
+        g_warning("Cannot load the NVDIMM plugin");
+
+    return ret;
+}
+
+/**
+ * bd_nvdimm_init:
+ *
+ * Initializes the plugin. **This function is called automatically by the
+ * library's initialization functions.**
+ *
+ */
+gboolean bd_nvdimm_init () {
+    /* nothing to do here */
+    return TRUE;
+}
+
+/**
+ * bd_nvdimm_close:
+ *
+ * Cleans up after the plugin. **This function is called automatically by the
+ * library's functions that unload it.**
+ *
+ */
+void bd_nvdimm_close () {
+    /* nothing to do here */
+    return;
+}
+
+#define UNUSED __attribute__((unused))
+
+/**
+ * bd_nvdimm_is_tech_avail:
+ * @tech: the queried tech
+ * @mode: a bit mask of queried modes of operation (#BDNVDIMMTechMode) for @tech
+ * @error: (out): place to store error (details about why the @tech-@mode combination is not available)
+ *
+ * Returns: whether the @tech-@mode combination is available -- supported by the
+ *          plugin implementation and having all the runtime dependencies available
+ */
+gboolean bd_nvdimm_is_tech_avail (BDNVDIMMTech tech, guint64 mode, GError **error) {
+  /* all tech-mode combinations are supported by this implementation of the
+     plugin, namespace reconfigure requires the 'ndctl' utility */
+
+    if (tech == BD_NVDIMM_TECH_NAMESPACE) {
+        if (mode & BD_NVDIMM_TECH_MODE_RECONFIGURE)
+            return check_deps (&avail_deps, DEPS_NDCTL_MASK, deps, DEPS_LAST, &deps_check_lock, error);
+        else
+            return TRUE;
+    } else {
+        g_set_error (error, BD_NVDIMM_ERROR, BD_NVDIMM_ERROR_TECH_UNAVAIL, "Unknown technology");
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/**
+ * bd_nvdimm_namespace_get_mode_from_str:
+ * @mode_str: string representation of mode
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: mode matching the @mode_str given or %BD_NVDIMM_NAMESPACE_MODE_UNKNOWN in case of no match
+ *
+ * Tech category: always available
+ */
+BDNVDIMMNamespaceMode bd_nvdimm_namespace_get_mode_from_str (const gchar *mode_str, GError **error) {
+    if (g_strcmp0 (mode_str, "raw") == 0)
+        return BD_NVDIMM_NAMESPACE_MODE_RAW;
+    else if (g_strcmp0 (mode_str, "sector") == 0)
+        return BD_NVDIMM_NAMESPACE_MODE_SECTOR;
+    else if (g_strcmp0 (mode_str, "memory") == 0)
+        return BD_NVDIMM_NAMESPACE_MODE_MEMORY;
+    else if (g_strcmp0 (mode_str, "dax") == 0)
+        return BD_NVDIMM_NAMESPACE_MODE_DAX;
+    else {
+        g_set_error (error, BD_NVDIMM_ERROR, BD_NVDIMM_ERROR_NAMESPACE_MODE_INVAL,
+                     "Invalid mode given: '%s'", mode_str);
+        return BD_NVDIMM_NAMESPACE_MODE_UNKNOWN;
+    }
+}
+
+/**
+ * bd_nvdimm_namespace_get_mode_str:
+ * @mode: mode to get string representation of
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: (transfer none): string representation of @mode or %NULL in case of error
+ *
+ * Tech category: always available
+ */
+const gchar* bd_nvdimm_namespace_get_mode_str (BDNVDIMMNamespaceMode mode, GError **error) {
+    if (mode <= BD_NVDIMM_NAMESPACE_MODE_UNKNOWN)
+        return mode_str[mode];
+    else {
+        g_set_error (error, BD_NVDIMM_ERROR, BD_NVDIMM_ERROR_NAMESPACE_MODE_INVAL,
+                     "Invalid mode given: %d", mode);
+        return NULL;
+    }
+}
+
+static struct ndctl_namespace* get_namespace_by_name (const gchar *namespace, struct ndctl_ctx *ctx) {
+    struct ndctl_namespace *ndns = NULL;
+    struct ndctl_region *region = NULL;
+    struct ndctl_bus *bus = NULL;
+
+    ndctl_bus_foreach (ctx, bus) {
+        ndctl_region_foreach (bus, region) {
+            ndctl_namespace_foreach (region, ndns) {
+                if (g_strcmp0 (namespace, ndctl_namespace_get_devname (ndns)) == 0)
+                    return ndns;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+/**
+ * bd_nvdimm_namespace_enable:
+ * @namespace: name of the namespace to enable
+ * @extra: (allow-none) (array zero-terminated=1): extra options (currently unused)
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether the @namespace was successfully enabled or not
+ *
+ * Tech category: %BD_NVDIMM_TECH_NAMESPACE-%BD_NVDIMM_TECH_MODE_ACTIVATE_DEACTIVATE
+ */
+gboolean bd_nvdimm_namespace_enable (const gchar *namespace, const BDExtraArg **extra UNUSED, GError **error) {
+    struct ndctl_ctx *ctx = NULL;
+    struct ndctl_namespace *ndns = NULL;
+    gint ret = 0;
+
+    ret = ndctl_new (&ctx);
+    if (ret != 0) {
+        g_set_error (error, BD_NVDIMM_ERROR, BD_NVDIMM_ERROR_NAMESPACE_FAIL,
+                     "Failed to create ndctl context");
+        return FALSE;
+    }
+
+    ndns = get_namespace_by_name (namespace, ctx);
+    if (!ndns) {
+        g_set_error (error, BD_NVDIMM_ERROR, BD_NVDIMM_ERROR_NAMESPACE_NOEXIST,
+                     "Failed to enable namespace: namespace '%s' not found.", namespace);
+        return FALSE;
+    }
+
+    ret = ndctl_namespace_enable (ndns);
+    if (ret < 0) {
+        g_set_error (error, BD_NVDIMM_ERROR, BD_NVDIMM_ERROR_NAMESPACE_FAIL,
+                     "Failed to enable namespace: %s", strerror (-ret));
+        ndctl_unref (ctx);
+        return FALSE;
+    }
+
+    ndctl_unref (ctx);
+    return TRUE;
+}
+
+/**
+ * bd_nvdimm_namespace_disable:
+ * @namespace: name of the namespace to disable
+ * @extra: (allow-none) (array zero-terminated=1): extra options (currently unused)
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether the @namespace was successfully disabled or not
+ *
+ * Tech category: %BD_NVDIMM_TECH_NAMESPACE-%BD_NVDIMM_TECH_MODE_ACTIVATE_DEACTIVATE
+ */
+gboolean bd_nvdimm_namespace_disable (const gchar *namespace, const BDExtraArg **extra UNUSED, GError **error) {
+    struct ndctl_ctx *ctx = NULL;
+    struct ndctl_namespace *ndns = NULL;
+    gint ret = 0;
+
+    ret = ndctl_new (&ctx);
+    if (ret != 0) {
+        g_set_error (error, BD_NVDIMM_ERROR, BD_NVDIMM_ERROR_NAMESPACE_FAIL,
+                     "Failed to create ndctl context");
+        return FALSE;
+    }
+
+    ndns = get_namespace_by_name (namespace, ctx);
+    if (!ndns) {
+        g_set_error (error, BD_NVDIMM_ERROR, BD_NVDIMM_ERROR_NAMESPACE_NOEXIST,
+                     "Failed to disable namespace: namespace '%s' not found.", namespace);
+        return FALSE;
+    }
+
+    ret = ndctl_namespace_disable_safe (ndns);
+    if (ret != 0) {
+        g_set_error (error, BD_NVDIMM_ERROR, BD_NVDIMM_ERROR_NAMESPACE_FAIL,
+                     "Failed to disable namespace: %s", strerror (-ret));
+        ndctl_unref (ctx);
+        return FALSE;
+    }
+
+    ndctl_unref (ctx);
+    return TRUE;
+}
+
+static BDNVDIMMNamespaceInfo* get_nvdimm_namespace_info (struct ndctl_namespace *ndns, GError **error) {
+    struct ndctl_btt *btt;
+    struct ndctl_pfn *pfn;
+    struct ndctl_dax *dax;
+    enum ndctl_namespace_mode mode;
+    gchar uuid_buf[37] = {0};
+    uuid_t uuid;
+
+    btt = ndctl_namespace_get_btt (ndns);
+    dax = ndctl_namespace_get_dax (ndns);
+    pfn = ndctl_namespace_get_pfn (ndns);
+    mode = ndctl_namespace_get_mode (ndns);
+
+    BDNVDIMMNamespaceInfo *info = g_new0 (BDNVDIMMNamespaceInfo, 1);
+
+    info->dev = g_strdup (ndctl_namespace_get_devname (ndns));
+
+    switch (mode) {
+        case NDCTL_NS_MODE_MEMORY:
+            if (pfn)
+              info->size = ndctl_pfn_get_size (pfn);
+            else
+              info->size = ndctl_namespace_get_size (ndns);
+          info->mode = BD_NVDIMM_NAMESPACE_MODE_MEMORY;
+          break;
+        case NDCTL_NS_MODE_DAX:
+            if (!dax) {
+                g_set_error (error, BD_NVDIMM_ERROR, BD_NVDIMM_ERROR_NAMESPACE_FAIL,
+                             "Failed to get information about namespaces: DAX mode "
+                             "detected but no DAX device found.");
+                bd_nvdimm_namespace_info_free (info);
+                return NULL;
+            }
+            info->size = ndctl_dax_get_size (dax);
+            info->mode = BD_NVDIMM_NAMESPACE_MODE_DAX;
+            break;
+        case NDCTL_NS_MODE_SAFE:
+            if (!btt) {
+                g_set_error (error, BD_NVDIMM_ERROR, BD_NVDIMM_ERROR_NAMESPACE_FAIL,
+                             "Failed to get information about namespaces: Sector mode "
+                             "detected but no BTT device found.");
+                bd_nvdimm_namespace_info_free (info);
+                return NULL;
+            }
+            info->size = ndctl_btt_get_size (btt);
+            info->mode = BD_NVDIMM_NAMESPACE_MODE_SECTOR;
+            break;
+        case NDCTL_NS_MODE_RAW:
+            info->size = ndctl_namespace_get_size (ndns);
+            info->mode = BD_NVDIMM_NAMESPACE_MODE_RAW;
+            break;
+        default:
+            g_set_error (error, BD_NVDIMM_ERROR, BD_NVDIMM_ERROR_NAMESPACE_FAIL,
+                         "Failed to get information about namespaces: Unknow mode.");
+            bd_nvdimm_namespace_info_free (info);
+            return NULL;
+    }
+
+    if (btt) {
+        ndctl_btt_get_uuid (btt, uuid);
+        uuid_unparse (uuid, uuid_buf);
+        info->uuid = g_strdup (uuid_buf);
+
+        info->sector_size = ndctl_btt_get_sector_size (btt);
+        info->blockdev = g_strdup (ndctl_btt_get_block_device (btt));
+    } else if (pfn) {
+        ndctl_pfn_get_uuid (pfn, uuid);
+        uuid_unparse (uuid, uuid_buf);
+        info->uuid = g_strdup (uuid_buf);
+
+        info->sector_size = 0; // no sector size for memory mode
+        info->blockdev = g_strdup (ndctl_pfn_get_block_device (pfn));
+    } else if (dax) {
+        ndctl_dax_get_uuid (dax, uuid);
+        uuid_unparse (uuid, uuid_buf);
+        info->uuid = g_strdup (uuid_buf);
+
+        /* no sector size or blockdev for dax mode */
+        info->sector_size = 0;
+        info->blockdev = NULL;
+    } else {
+        ndctl_namespace_get_uuid (ndns, uuid);
+
+        if (uuid_is_null (uuid))
+            info->uuid = NULL;
+        else {
+            uuid_unparse (uuid, uuid_buf);
+            info->uuid = g_strdup (uuid_buf);
+        }
+
+        info->sector_size = 0; // no sector size for raw mode
+        info->blockdev = g_strdup (ndctl_namespace_get_block_device (ndns));
+    }
+
+    info->enabled = ndctl_namespace_is_active (ndns);
+
+    return info;
+}
+
+/**
+ * bd_nvdimm_namespace_info:
+ * @namespace: namespace to get information about
+ * @extra: (allow-none) (array zero-terminated=1): extra options (currently unused)
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: (transfer full): information about given namespace or %NULL if no such
+ *                           namespace was found (@error may be set to indicate error)
+ *
+ * Tech category: %BD_NVDIMM_TECH_NAMESPACE-%BD_NVDIMM_TECH_MODE_QUERY
+ */
+BDNVDIMMNamespaceInfo* bd_nvdimm_namespace_info (const gchar *namespace, const BDExtraArg **extra UNUSED, GError **error) {
+    struct ndctl_ctx *ctx = NULL;
+    struct ndctl_namespace *ndns = NULL;
+    BDNVDIMMNamespaceInfo *info = NULL;
+    gint ret = 0;
+
+    ret = ndctl_new (&ctx);
+    if (ret != 0) {
+        g_set_error (error, BD_NVDIMM_ERROR, BD_NVDIMM_ERROR_NAMESPACE_FAIL,
+                     "Failed to create ndctl context");
+        return NULL;
+    }
+
+    ndns = get_namespace_by_name (namespace, ctx);
+    if (ndns) {
+        info = get_nvdimm_namespace_info (ndns, error);
+        ndctl_unref (ctx);
+        return info;
+    }
+
+    ndctl_unref (ctx);
+    return NULL;
+}
+
+/**
+ * bd_nvdimm_list_namespaces:
+ * @bus_name: (allow-none): return only namespaces on given bus (specified by name),
+ *                          %NULL may be specified to return namespaces from all buses
+ * @region_name: (allow-none): return only namespaces on given region (specified by 'regionX' name),
+ *                             %NULL may be specified to return namespaces from all regions
+ * @idle: whether to list idle (not enabled) namespaces too
+ * @extra: (allow-none) (array zero-terminated=1): extra options (currently unused)
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: (array zero-terminated=1): information about the namespaces on @bus and @region or
+ *                                     %NULL if no namespaces were found (@error may be set to indicate error)
+ *
+ * Tech category: %BD_NVDIMM_TECH_NAMESPACE-%BD_NVDIMM_TECH_MODE_QUERY
+ */
+BDNVDIMMNamespaceInfo** bd_nvdimm_list_namespaces (const gchar *bus_name, const gchar *region_name, gboolean idle, const BDExtraArg **extra UNUSED, GError **error) {
+    struct ndctl_ctx *ctx = NULL;
+    struct ndctl_namespace *ndns = NULL;
+    struct ndctl_region *region = NULL;
+    struct ndctl_bus *bus = NULL;
+    gint ret = 0;
+    BDNVDIMMNamespaceInfo **info = NULL;
+
+    GPtrArray *namespaces = g_ptr_array_new ();
+
+    ret = ndctl_new (&ctx);
+    if (ret != 0) {
+        g_set_error (error, BD_NVDIMM_ERROR, BD_NVDIMM_ERROR_NAMESPACE_FAIL,
+                     "Failed to create ndctl context");
+        return NULL;
+    }
+
+    ndctl_bus_foreach (ctx, bus) {
+        if (bus_name && g_strcmp0 (bus_name, ndctl_bus_get_devname (bus)) != 0)
+            continue;
+
+        ndctl_region_foreach (bus, region) {
+            if (region_name && g_strcmp0 (bus_name, ndctl_region_get_devname (region)) != 0)
+                continue;
+
+            ndctl_namespace_foreach (region, ndns) {
+                if (!idle && !ndctl_namespace_is_active (ndns))
+                    continue;
+
+                BDNVDIMMNamespaceInfo *info = get_nvdimm_namespace_info (ndns, error);
+                if (!info) {
+                    g_ptr_array_foreach (namespaces, (GFunc) (void *) bd_nvdimm_namespace_info_free, NULL);
+                    g_ptr_array_free (namespaces, FALSE);
+                    ndctl_unref (ctx);
+                    return NULL;
+                }
+
+                g_ptr_array_add (namespaces, info);
+            }
+        }
+    }
+
+    if (namespaces->len == 0) {
+        ndctl_unref (ctx);
+        return NULL;
+    }
+
+    g_ptr_array_add (namespaces, NULL);
+
+    info = (BDNVDIMMNamespaceInfo **) g_ptr_array_free (namespaces, FALSE);
+    ndctl_unref (ctx);
+
+    return info;
+}
+
+/**
+ * bd_nvdimm_namespace_reconfigure:
+ * @namespace: name of the namespace to recofigure
+ * @mode: mode type to set
+ * @error: (out): place to store error if any
+ * @extra: (allow-none) (array zero-terminated=1): extra options for the creation (right now
+ *                                                 passed to the 'ndctl' utility)
+ *
+ * Returns: whether @namespace was successfully reconfigured or not
+ *
+ * Tech category: %BD_NVDIMM_TECH_NAMESPACE-%BD_NVDIMM_TECH_MODE_RECONFIGURE
+ */
+gboolean bd_nvdimm_namespace_reconfigure (const gchar* namespace, BDNVDIMMNamespaceMode mode, gboolean force, const BDExtraArg **extra, GError** error) {
+    const gchar *args[8] = {"ndctl", "create-namespace", "-e", namespace, "-m", NULL, NULL, NULL};
+    gboolean ret = FALSE;
+    const gchar *mode_str = NULL;
+
+    if (!check_deps (&avail_deps, DEPS_NDCTL_MASK, deps, DEPS_LAST, &deps_check_lock, error))
+      return FALSE;
+
+    mode_str = bd_nvdimm_namespace_get_mode_str (mode, error);
+    if (!mode_str)
+        /* error is already populated */
+        return FALSE;
+
+    args[5] = g_strdup (mode_str);
+
+    if (force)
+      args[6] = "-f";
+
+    ret = bd_utils_exec_and_report_error (args, extra, error);
+
+    g_free ((gchar *) args[5]);
+    return ret;
+}
diff --git a/src/plugins/nvdimm.h b/src/plugins/nvdimm.h
new file mode 100644
index 0000000..5a1642f
--- /dev/null
+++ b/src/plugins/nvdimm.h
@@ -0,0 +1,76 @@
+#include <glib.h>
+#include <blockdev/utils.h>
+
+#ifndef BD_NVDIMM
+#define BD_NVDIMM
+
+GQuark bd_nvdimm_error_quark (void);
+#define BD_NVDIMM_ERROR bd_nvdimm_error_quark ()
+typedef enum {
+    BD_NVDIMM_ERROR_NAMESPACE_PARSE,
+    BD_NVDIMM_ERROR_NAMESPACE_FAIL,
+    BD_NVDIMM_ERROR_NAMESPACE_NOEXIST,
+    BD_NVDIMM_ERROR_NAMESPACE_MODE_INVAL,
+    BD_NVDIMM_ERROR_TECH_UNAVAIL,
+} BDNVDIMMError;
+
+typedef enum {
+    BD_NVDIMM_NAMESPACE_MODE_RAW,
+    BD_NVDIMM_NAMESPACE_MODE_SECTOR,
+    BD_NVDIMM_NAMESPACE_MODE_MEMORY,
+    BD_NVDIMM_NAMESPACE_MODE_DAX,
+    BD_NVDIMM_NAMESPACE_MODE_UNKNOWN
+} BDNVDIMMNamespaceMode;
+
+typedef struct BDNVDIMMNamespaceInfo {
+    gchar *dev;
+    guint64 mode;
+    guint64 size;
+    gchar *uuid;
+    guint64 sector_size;
+    gchar *blockdev;
+    gboolean enabled;
+} BDNVDIMMNamespaceInfo;
+
+void bd_nvdimm_namespace_info_free (BDNVDIMMNamespaceInfo *info);
+BDNVDIMMNamespaceInfo* bd_nvdimm_namespace_info_copy (BDNVDIMMNamespaceInfo *info);
+
+typedef enum {
+    BD_NVDIMM_TECH_NAMESPACE = 0,
+} BDNVDIMMTech;
+
+typedef enum {
+    BD_NVDIMM_TECH_MODE_CREATE              = 1 << 0,
+    BD_NVDIMM_TECH_MODE_REMOVE              = 1 << 1,
+    BD_NVDIMM_TECH_MODE_ACTIVATE_DEACTIVATE = 1 << 2,
+    BD_NVDIMM_TECH_MODE_QUERY               = 1 << 3,
+    BD_NVDIMM_TECH_MODE_RECONFIGURE         = 1 << 4,
+} BDNVDIMMTechMode;
+
+/*
+ * If using the plugin as a standalone library, the following functions should
+ * be called to:
+ *
+ * check_deps() - check plugin's dependencies, returning TRUE if satisfied
+ * init()       - initialize the plugin, returning TRUE on success
+ * close()      - clean after the plugin at the end or if no longer used
+ *
+ */
+gboolean bd_nvdimm_check_deps ();
+gboolean bd_nvdimm_init ();
+void bd_nvdimm_close ();
+
+gboolean bd_nvdimm_is_tech_avail (BDNVDIMMTech tech, guint64 mode, GError **error);
+
+BDNVDIMMNamespaceMode bd_nvdimm_namespace_get_mode_from_str (const gchar *mode_str, GError **error);
+const gchar* bd_nvdimm_namespace_get_mode_str (BDNVDIMMNamespaceMode mode, GError **error);
+
+gboolean bd_nvdimm_namespace_enable (const gchar *namespace, const BDExtraArg **extra, GError **error);
+gboolean bd_nvdimm_namespace_disable (const gchar *namespace, const BDExtraArg **extra, GError **error);
+
+BDNVDIMMNamespaceInfo* bd_nvdimm_namespace_info (const gchar *namespace, const BDExtraArg **extra, GError **error);
+BDNVDIMMNamespaceInfo** bd_nvdimm_list_namespaces (const gchar *bus, const gchar *region, gboolean idle, const BDExtraArg **extra, GError **error);
+
+gboolean bd_nvdimm_namespace_reconfigure (const gchar* namespace, BDNVDIMMNamespaceMode mode, gboolean force, const BDExtraArg **extra, GError** error);
+
+#endif  /* BD_CRYPTO */
diff --git a/src/python/gi/overrides/BlockDev.py b/src/python/gi/overrides/BlockDev.py
index fb3ffb4..dc634ab 100644
--- a/src/python/gi/overrides/BlockDev.py
+++ b/src/python/gi/overrides/BlockDev.py
@@ -50,6 +50,7 @@ bd_plugins = { "lvm": BlockDev.Plugin.LVM,
                "part": BlockDev.Plugin.PART,
                "fs": BlockDev.Plugin.FS,
                "s390": BlockDev.Plugin.S390,
+               "nvdimm": BlockDev.Plugin.NVDIMM,
 }
 
 
@@ -692,6 +693,41 @@ def part_create_table(disk, type, ignore_existing=True):
 __all__.append("part_create_table")
 
 
+_nvdimm_namespace_reconfigure = BlockDev.nvdimm_namespace_reconfigure
+@override(BlockDev.nvdimm_namespace_reconfigure)
+def nvdimm_namespace_reconfigure(namespace, mode, force=False, extra=None, **kwargs):
+    extra = _get_extra(extra, kwargs)
+    return _nvdimm_namespace_reconfigure(namespace, mode, force, extra)
+__all__.append("nvdimm_namespace_reconfigure")
+
+_nvdimm_namespace_info = BlockDev.nvdimm_namespace_info
+@override(BlockDev.nvdimm_namespace_info)
+def nvdimm_namespace_info(namespace, extra=None, **kwargs):
+    extra = _get_extra(extra, kwargs)
+    return _nvdimm_namespace_info(namespace, extra)
+__all__.append("nvdimm_namespace_info")
+
+_nvdimm_list_namespaces = BlockDev.nvdimm_list_namespaces
+@override(BlockDev.nvdimm_list_namespaces)
+def nvdimm_list_namespaces(bus=None, region=None, idle=False, extra=None, **kwargs):
+    extra = _get_extra(extra, kwargs)
+    return _nvdimm_list_namespaces(bus, region, idle, extra)
+__all__.append("nvdimm_list_namespaces")
+
+_nvdimm_namespace_enable = BlockDev.nvdimm_namespace_enable
+@override(BlockDev.nvdimm_namespace_enable)
+def nvdimm_namespace_enable(namespace, extra=None, **kwargs):
+    extra = _get_extra(extra, kwargs)
+    return _nvdimm_namespace_enable(namespace, extra)
+__all__.append("nvdimm_namespace_enable")
+
+_nvdimm_namespace_disable = BlockDev.nvdimm_namespace_disable
+@override(BlockDev.nvdimm_namespace_disable)
+def nvdimm_namespace_disable(namespace, extra=None, **kwargs):
+    extra = _get_extra(extra, kwargs)
+    return _nvdimm_namespace_disable(namespace, extra)
+__all__.append("nvdimm_namespace_disable")
+
 ## defined in this overrides only!
 def plugin_specs_from_names(plugin_names):
     ret = []
@@ -886,6 +922,10 @@ class UtilsError(BlockDevError):
     pass
 __all__.append("UtilsError")
 
+class NVDIMMError(BlockDevError):
+    pass
+__all__.append("NVDIMMError")
+
 class BlockDevNotImplementedError(NotImplementedError, BlockDevError):
     pass
 __all__.append("BlockDevNotImplementedError")
@@ -927,6 +967,9 @@ __all__.append("part")
 fs = ErrorProxy("fs", BlockDev, [(GLib.Error, FSError)], [not_implemented_rule, fs_nofs_rule])
 __all__.append("fs")
 
+nvdimm = ErrorProxy("nvdimm", BlockDev, [(GLib.Error, NVDIMMError)], [not_implemented_rule])
+__all__.append("nvdimm")
+
 s390 = ErrorProxy("s390", BlockDev, [(GLib.Error, S390Error)], [not_implemented_rule])
 __all__.append("s390")
 
-- 
1.8.3.1


From 2fda30f62f962eb9e10894b4d919acd013c9c93b Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Wed, 31 Jan 2018 16:27:59 +0100
Subject: [PATCH 2/4] Add tests for the NVDIMM plugin

---
 tests/nvdimm_test.py | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 178 insertions(+)
 create mode 100644 tests/nvdimm_test.py

diff --git a/tests/nvdimm_test.py b/tests/nvdimm_test.py
new file mode 100644
index 0000000..b4eb43d
--- /dev/null
+++ b/tests/nvdimm_test.py
@@ -0,0 +1,178 @@
+import json
+import os
+import unittest
+import overrides_hack
+
+from utils import run_command, read_file, skip_on, fake_path
+from gi.repository import BlockDev, GLib
+
+
+@skip_on("debian", reason="NVDIMM plugin doesn't work on Debian (missing ndctl)")
+class NVDIMMTestCase(unittest.TestCase):
+
+    requested_plugins = BlockDev.plugin_specs_from_names(("nvdimm",))
+
+    @classmethod
+    def setUpClass(cls):
+        if not BlockDev.is_initialized():
+            BlockDev.init(cls.requested_plugins, None)
+        else:
+            BlockDev.reinit(cls.requested_plugins, True, None)
+
+class NVDIMMNamespaceTestCase(NVDIMMTestCase):
+
+    sys_info = None
+
+    def _get_nvdimm_info(self):
+        ret, out, _err = run_command("ndctl list")
+        if ret != 0 or not out:
+            return None
+
+        decoder = json.JSONDecoder()
+        decoded = decoder.decode(out)
+        return decoded
+
+    def setUp(self):
+        self.sys_info = self._get_nvdimm_info()
+        if not self.sys_info:
+            self.skipTest("No NVDIMM devices available.")
+
+        # skip the test if there is more than one device
+        # these tests change nvdimm mode a we need to be sure that we are really
+        # working with the 'fake' device created by the memmap kernel cmdline option
+        if type(self.sys_info) is not dict:
+            self.skipTest("Multiple NVDIMM devices found.")
+
+        # skip the tests if the nvdimm is a 'fake' one
+        cmdline = read_file("/proc/cmdline")
+        if "memmap=" not in cmdline:
+            self.skipTest("NVDIMM device found, but not created by the 'memmap' kernel command-line option.")
+
+    def _check_namespace_info(self, bd_info):
+        self.assertEqual(bd_info.dev, self.sys_info["dev"])
+        self.assertEqual(bd_info.mode, BlockDev.nvdimm_namespace_get_mode_from_str(self.sys_info["mode"]))
+        self.assertEqual(bd_info.size, self.sys_info["size"])
+
+        if "uuid" in self.sys_info.keys():
+            self.assertEqual(bd_info.uuid, self.sys_info["uuid"])
+        else:
+            self.assertIsNone(bd_info.uuid)
+
+        if "blockdev" in self.sys_info.keys():
+            self.assertEqual(bd_info.blockdev, self.sys_info["blockdev"])
+        else:
+            self.assertIsNone(bd_info.blockdev)
+
+        if "sector_size" in self.sys_info.keys():
+            self.assertEqual(bd_info.sector_size, self.sys_info["sector_size"])
+        else:
+            self.assertEqual(bd_info.sector_size, 0)
+
+    def test_namespace_info(self):
+        # get info about our 'testing' namespace
+        info = BlockDev.nvdimm_namespace_info(self.sys_info["dev"])
+        self._check_namespace_info(info)
+
+        info = BlockDev.nvdimm_namespace_info("definitely-not-a-namespace")
+        self.assertIsNone(info)
+
+    def test_list_namespaces(self):
+        bd_namespaces = BlockDev.nvdimm_list_namespaces()
+        self.assertEqual(len(bd_namespaces), 1)
+
+        self._check_namespace_info(bd_namespaces[0])
+
+    @unittest.skipUnless("JENKINS_HOME" in os.environ, "skipping test that modifies system configuration")
+    def test_enable_disable(self):
+        # non-existing/unknow namespace
+        with self.assertRaises(GLib.GError):
+            BlockDev.nvdimm_namespace_enable("definitely-not-a-namespace")
+
+        # enable the namespace
+        ret = BlockDev.nvdimm_namespace_enable(self.sys_info["dev"])
+        self.assertTrue(ret)
+
+        info = BlockDev.nvdimm_namespace_info(self.sys_info["dev"])
+        self.assertTrue(info.enabled)
+
+        # disable the namespace
+        ret = BlockDev.nvdimm_namespace_disable(self.sys_info["dev"])
+        self.assertTrue(ret)
+
+        info = BlockDev.nvdimm_namespace_info(self.sys_info["dev"])
+        self.assertFalse(info.enabled)
+
+        # and enable it again
+        ret = BlockDev.nvdimm_namespace_enable(self.sys_info["dev"])
+        self.assertTrue(ret)
+
+        info = BlockDev.nvdimm_namespace_info(self.sys_info["dev"])
+        self.assertTrue(info.enabled)
+
+    @unittest.skipUnless("JENKINS_HOME" in os.environ, "skipping test that modifies system configuration")
+    def test_namespace_reconfigure(self):
+        # active namespace -- reconfigure doesn't work without force
+        with self.assertRaises(GLib.GError):
+            BlockDev.nvdimm_namespace_reconfigure(self.sys_info["dev"],
+                                                  BlockDev.NVDIMMNamespaceMode.SECTOR,
+                                                  False)
+
+        # non-existing/unknow mode
+        with self.assertRaises(GLib.GError):
+            BlockDev.nvdimm_namespace_reconfigure(self.sys_info["dev"],
+                                                  BlockDev.NVDIMMNamespaceMode.UNKNOWN,
+                                                  True)
+
+        # non-existing/unknow namespace
+        with self.assertRaises(GLib.GError):
+            BlockDev.nvdimm_namespace_reconfigure("definitely-not-a-namespace",
+                                                  BlockDev.NVDIMMNamespaceMode.SECTOR,
+                                                  True)
+
+        # switch to sector mode
+        ret = BlockDev.nvdimm_namespace_reconfigure(self.sys_info["dev"],
+                                                    BlockDev.NVDIMMNamespaceMode.SECTOR,
+                                                    True, extra={"-l": "512"})
+        self.assertTrue(ret)
+        info = BlockDev.nvdimm_namespace_info(self.sys_info["dev"])
+        self.assertEqual(info.mode, BlockDev.NVDIMMNamespaceMode.SECTOR)
+
+        # and now to memory mode
+        ret = BlockDev.nvdimm_namespace_reconfigure(self.sys_info["dev"],
+                                                    BlockDev.NVDIMMNamespaceMode.MEMORY,
+                                                    True)
+        self.assertTrue(ret)
+        info = BlockDev.nvdimm_namespace_info(self.sys_info["dev"])
+        self.assertEqual(info.mode, BlockDev.NVDIMMNamespaceMode.MEMORY)
+
+        # and back to sector
+        ret = BlockDev.nvdimm_namespace_reconfigure(self.sys_info["dev"],
+                                                    BlockDev.NVDIMMNamespaceMode.SECTOR,
+                                                    True, extra={"-l": "512"})
+        self.assertTrue(ret)
+        info = BlockDev.nvdimm_namespace_info(self.sys_info["dev"])
+        self.assertEqual(info.mode, BlockDev.NVDIMMNamespaceMode.SECTOR)
+
+
+class NVDIMMUnloadTest(NVDIMMTestCase):
+    def setUp(self):
+        # make sure the library is initialized with all plugins loaded for other
+        # tests
+        self.addCleanup(BlockDev.reinit, self.requested_plugins, True, None)
+
+    def test_check_no_ndctl(self):
+        """Verify that checking ndctl tool availability works as expected"""
+
+        # unload all plugins first
+        self.assertTrue(BlockDev.reinit([], True, None))
+
+        with fake_path(all_but="ndctl"):
+            # no ndctl tool available, the NVDIMM plugin should fail to load
+            with self.assertRaises(GLib.GError):
+                BlockDev.reinit(self.requested_plugins, True, None)
+
+            self.assertNotIn("nvdimm", BlockDev.get_available_plugin_names())
+
+        # load the plugins back
+        self.assertTrue(BlockDev.reinit(self.requested_plugins, True, None))
+        self.assertIn("nvdimm", BlockDev.get_available_plugin_names())
-- 
1.8.3.1


From 333bad8ceafc229b9a8c97df23715a02361aee01 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Thu, 1 Feb 2018 14:46:51 +0100
Subject: [PATCH 3/4] Add --without-xyz to DISTCHECK_CONFIGURE_FLAGS for
 disabled plugins

We need to disable these plugins for distcheck because of missing
build dependecies etc.
---
 Makefile.am | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/Makefile.am b/Makefile.am
index d30acd1..54b89e1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,6 +4,59 @@ SHELL = /bin/bash
 ACLOCAL_AMFLAGS = -I m4
 DISTCHECK_CONFIGURE_FLAGS = --enable-introspection
 
+if !WITH_BTRFS
+DISTCHECK_CONFIGURE_FLAGS += --without-btrfs
+endif
+
+if !WITH_CRYPTO
+DISTCHECK_CONFIGURE_FLAGS += --without-crypto
+endif
+
+if !WITH_DM
+DISTCHECK_CONFIGURE_FLAGS += --without-dm
+endif
+
+if !WITH_LOOP
+DISTCHECK_CONFIGURE_FLAGS += --without-loop
+endif
+
+if !WITH_LVM
+DISTCHECK_CONFIGURE_FLAGS += --without-lvm
+endif
+
+if !WITH_LVM_DBUS
+DISTCHECK_CONFIGURE_FLAGS += --without-lvm-dbus
+endif
+
+if !WITH_MDRAID
+DISTCHECK_CONFIGURE_FLAGS += --without-mdraid
+endif
+
+if !WITH_MPATH
+DISTCHECK_CONFIGURE_FLAGS += --without-mpath
+endif
+
+if !WITH_NVDIMM
+DISTCHECK_CONFIGURE_FLAGS += --without-nvdimm
+endif
+
+if !WITH_SWAP
+DISTCHECK_CONFIGURE_FLAGS += --without-swap
+endif
+
+if !WITH_KBD
+DISTCHECK_CONFIGURE_FLAGS += --without-kbd
+endif
+
+if !WITH_PART
+DISTCHECK_CONFIGURE_FLAGS += --without-part
+endif
+
+if !WITH_S390
+DISTCHECK_CONFIGURE_FLAGS += --without-s390
+endif
+
+
 SUBDIRS = include src dist scripts data
 if WITH_GTK_DOC
 SUBDIRS += docs
-- 
1.8.3.1


From 7ebc9d20fa8b97482a109121dd016e01c4d032e4 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Tue, 6 Feb 2018 14:56:10 +0100
Subject: [PATCH 4/4] Add function for getting NVDIMM namespace name from
 devname or path

For example getting "namespace0.0" from "pmem0" or "/dev/pmem0".
---
 docs/libblockdev-sections.txt  |  1 +
 src/lib/plugin_apis/nvdimm.api | 13 +++++++++
 src/plugins/nvdimm.c           | 63 ++++++++++++++++++++++++++++++++++++++++++
 src/plugins/nvdimm.h           |  2 ++
 tests/nvdimm_test.py           |  8 ++++++
 5 files changed, 87 insertions(+)

diff --git a/docs/libblockdev-sections.txt b/docs/libblockdev-sections.txt
index 756b5b5..8aaa290 100644
--- a/docs/libblockdev-sections.txt
+++ b/docs/libblockdev-sections.txt
@@ -578,6 +578,7 @@ BDNVDIMMNamespaceMode
 BDNVDIMMNamespaceInfo
 bd_nvdimm_namespace_get_mode_from_str
 bd_nvdimm_namespace_get_mode_str
+bd_nvdimm_namespace_get_devname
 bd_nvdimm_namespace_enable
 bd_nvdimm_namespace_disable
 bd_nvdimm_namespace_info
diff --git a/src/lib/plugin_apis/nvdimm.api b/src/lib/plugin_apis/nvdimm.api
index 3d74f62..9d5260c 100644
--- a/src/lib/plugin_apis/nvdimm.api
+++ b/src/lib/plugin_apis/nvdimm.api
@@ -137,6 +137,19 @@ BDNVDIMMNamespaceMode bd_nvdimm_namespace_get_mode_from_str (const gchar *mode_s
 const gchar* bd_nvdimm_namespace_get_mode_str (BDNVDIMMNamespaceMode mode, GError **error);
 
 /**
+ * bd_nvdimm_namespace_get_devname:
+ * @device: name or path of a block device (e.g. "/dev/pmem0")
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: (transfer full): namespace device name (e.g. "namespaceX.Y") for @device
+ *                           or %NULL if @device is not a NVDIMM namespace
+ *                           (@error may be set to indicate error)
+ *
+ * Tech category: %BD_NVDIMM_TECH_NAMESPACE-%BD_NVDIMM_TECH_MODE_QUERY
+ */
+gchar* bd_nvdimm_namespace_get_devname (const gchar *device, GError **error);
+
+/**
  * bd_nvdimm_namespace_enable:
  * @namespace: name of the namespace to enable
  * @extra: (allow-none) (array zero-terminated=1): extra options (currently unused)
diff --git a/src/plugins/nvdimm.c b/src/plugins/nvdimm.c
index 7a6802a..40ade05 100644
--- a/src/plugins/nvdimm.c
+++ b/src/plugins/nvdimm.c
@@ -222,6 +222,69 @@ static struct ndctl_namespace* get_namespace_by_name (const gchar *namespace, st
 }
 
 /**
+ * bd_nvdimm_namespace_get_devname:
+ * @device: name or path of a block device (e.g. "/dev/pmem0")
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: (transfer full): namespace device name (e.g. "namespaceX.Y") for @device
+ *                           or %NULL if @device is not a NVDIMM namespace
+ *                           (@error may be set to indicate error)
+ *
+ * Tech category: %BD_NVDIMM_TECH_NAMESPACE-%BD_NVDIMM_TECH_MODE_QUERY
+ */
+gchar* bd_nvdimm_namespace_get_devname (const gchar *device, GError **error) {
+    struct ndctl_ctx *ctx = NULL;
+    struct ndctl_namespace *ndns = NULL;
+    struct ndctl_region *region = NULL;
+    struct ndctl_bus *bus = NULL;
+    gint success = 0;
+    gchar *ret = NULL;
+
+    /* get rid of the "/dev/" prefix (if any) */
+    if (g_str_has_prefix (device, "/dev/"))
+        device = device + 5;
+
+    success = ndctl_new (&ctx);
+    if (success != 0) {
+        g_set_error (error, BD_NVDIMM_ERROR, BD_NVDIMM_ERROR_NAMESPACE_FAIL,
+                     "Failed to create ndctl context");
+        return FALSE;
+    }
+
+    ndctl_bus_foreach (ctx, bus) {
+        ndctl_region_foreach (bus, region) {
+            ndctl_namespace_foreach (region, ndns) {
+                if (!ndctl_namespace_is_active (ndns))
+                    continue;
+
+                struct ndctl_btt *btt = ndctl_namespace_get_btt (ndns);
+                struct ndctl_dax *dax = ndctl_namespace_get_dax (ndns);
+                struct ndctl_pfn *pfn = ndctl_namespace_get_pfn (ndns);
+                const gchar *blockdev = NULL;
+
+                if (dax)
+                    continue;
+                else if (btt)
+                    blockdev = ndctl_btt_get_block_device (btt);
+                else if (pfn)
+                    blockdev = ndctl_pfn_get_block_device (pfn);
+                else
+                    blockdev = ndctl_namespace_get_block_device (ndns);
+
+                if (g_strcmp0 (blockdev, device) == 0) {
+                    ret = g_strdup (ndctl_namespace_get_devname (ndns));
+                    ndctl_unref (ctx);
+                    return ret;
+                }
+            }
+        }
+    }
+
+    ndctl_unref (ctx);
+    return NULL;
+}
+
+/**
  * bd_nvdimm_namespace_enable:
  * @namespace: name of the namespace to enable
  * @extra: (allow-none) (array zero-terminated=1): extra options (currently unused)
diff --git a/src/plugins/nvdimm.h b/src/plugins/nvdimm.h
index 5a1642f..fcc1038 100644
--- a/src/plugins/nvdimm.h
+++ b/src/plugins/nvdimm.h
@@ -65,6 +65,8 @@ gboolean bd_nvdimm_is_tech_avail (BDNVDIMMTech tech, guint64 mode, GError **erro
 BDNVDIMMNamespaceMode bd_nvdimm_namespace_get_mode_from_str (const gchar *mode_str, GError **error);
 const gchar* bd_nvdimm_namespace_get_mode_str (BDNVDIMMNamespaceMode mode, GError **error);
 
+gchar* bd_nvdimm_namespace_get_devname (const gchar *device, GError **error);
+
 gboolean bd_nvdimm_namespace_enable (const gchar *namespace, const BDExtraArg **extra, GError **error);
 gboolean bd_nvdimm_namespace_disable (const gchar *namespace, const BDExtraArg **extra, GError **error);
 
diff --git a/tests/nvdimm_test.py b/tests/nvdimm_test.py
index b4eb43d..92a6653 100644
--- a/tests/nvdimm_test.py
+++ b/tests/nvdimm_test.py
@@ -73,6 +73,14 @@ class NVDIMMNamespaceTestCase(NVDIMMTestCase):
         info = BlockDev.nvdimm_namespace_info(self.sys_info["dev"])
         self._check_namespace_info(info)
 
+        # test also that getting namespace devname from blockdev name works
+        namespace = BlockDev.nvdimm_namespace_get_devname(info.blockdev)
+        self.assertEqual(namespace, self.sys_info["dev"])
+
+        # should work even with path, e.g. /dev/pmem0
+        namespace = BlockDev.nvdimm_namespace_get_devname("/dev/" + info.blockdev)
+        self.assertEqual(namespace, self.sys_info["dev"])
+
         info = BlockDev.nvdimm_namespace_info("definitely-not-a-namespace")
         self.assertIsNone(info)
 
-- 
1.8.3.1