From b0b6f77b3c75ca881bf21771f629c0716378ece2 Mon Sep 17 00:00:00 2001 From: Vojtech Trefny 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 @@ + 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 + +
+nvdimm +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 +
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 +#include +#include + +#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 . + * + * Author: Vojtech Trefny + */ + +#include +#include +#include +#include +#include + +#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 +#include + +#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 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 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 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