Blob Blame History Raw
/*
 * Copyright (C) 2018  Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; 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) {
    if (info == NULL)
        return;

    g_free (info->dev);
    g_free (info->uuid);
    g_free (info->blockdev);
    g_free (info);
}

BDNVDIMMNamespaceInfo* bd_nvdimm_namespace_info_copy (BDNVDIMMNamespaceInfo *info) {
    if (info == NULL)
        return NULL;

    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", "fsdax", "devdax", "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 const 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 (void) {
    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 (void) {
    /* 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 (void) {
    /* 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 if (g_strcmp0 (mode_str, "fsdax") == 0)
        return BD_NVDIMM_NAMESPACE_MODE_FSDAX;
    else if (g_strcmp0 (mode_str, "devdax") == 0)
        return BD_NVDIMM_NAMESPACE_MODE_DEVDAX;
    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_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)
 * @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);
#ifndef LIBNDCTL_NEW_MODES
          info->mode = BD_NVDIMM_NAMESPACE_MODE_MEMORY;
#else
          info->mode = BD_NVDIMM_NAMESPACE_MODE_FSDAX;
#endif
          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);
#ifndef LIBNDCTL_NEW_MODES
            info->mode = BD_NVDIMM_NAMESPACE_MODE_DAX;
#else
            info->mode = BD_NVDIMM_NAMESPACE_MODE_DEVDAX;
#endif
            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->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->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 blockdev for dax mode */
        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->blockdev = g_strdup (ndctl_namespace_get_block_device (ndns));
    }

    if (btt)
        info->sector_size = ndctl_btt_get_sector_size (btt);
    else if (dax)
        /* no sector size for dax mode */
        info->sector_size = 0;
    else {
        info->sector_size = ndctl_namespace_get_sector_size (ndns);

        /* apparently the default value for sector size is 512
           on non DAX namespaces even if libndctl says it's 0
           https://github.com/pmem/ndctl/commit/a7320456f1bca5edf15352ce977e757fdf78ed58
         */

        if (info->sector_size == 0)
            info->sector_size = 512;
    }

    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;
}


static guint64 blk_sector_sizes[] = { 512, 520, 528, 4096, 4104, 4160, 4224, 0 };
static guint64 pmem_sector_sizes[] = { 512, 4096, 0 };
static guint64 io_sector_sizes[] = { 0 };

/**
 * bd_nvdimm_namepace_get_supported_sector_sizes:
 * @mode: namespace mode
 * @error: (out): place to store error if any
 *
 * Returns: (transfer none) (array zero-terminated=1): list of supported sector sizes for @mode
 *
 * Tech category: %BD_NVDIMM_TECH_NAMESPACE-%BD_NVDIMM_TECH_MODE_QUERY
 */
const guint64 *bd_nvdimm_namepace_get_supported_sector_sizes (BDNVDIMMNamespaceMode mode, GError **error) {
    switch (mode) {
        case BD_NVDIMM_NAMESPACE_MODE_RAW:
        case BD_NVDIMM_NAMESPACE_MODE_MEMORY:
        case BD_NVDIMM_NAMESPACE_MODE_FSDAX:
            return pmem_sector_sizes;

        case BD_NVDIMM_NAMESPACE_MODE_DAX:
        case BD_NVDIMM_NAMESPACE_MODE_DEVDAX:
            return io_sector_sizes;

        case BD_NVDIMM_NAMESPACE_MODE_SECTOR:
            return blk_sector_sizes;

        default:
            g_set_error (error, BD_NVDIMM_ERROR, BD_NVDIMM_ERROR_NAMESPACE_MODE_INVAL,
                         "Invalid/unknown mode specified.");
            return NULL;
    }
}