diff mbox

[ndctl,06/13] ndctl: create namespace

Message ID 20160128225229.17855.88143.stgit@dwillia2-desk3.amr.corp.intel.com (mailing list archive)
State Accepted
Commit 3139dfda1158
Headers show

Commit Message

Dan Williams Jan. 28, 2016, 10:52 p.m. UTC
A helper utility for instantiating a namespace.  With no arguments it
defaults to a maximally sized pmem namespace in memory mode.  The search
for available capacity can be constrained by bus, region, and/or
namespace type using the same mechanism to filter namespaces as 'ndctl
enable-namespace' and 'ndctl disable-namespace'.

See the include man page for more details.

Cc: Jeff Moyer <jmoyer@redhat.com>
[jmoyer: null dereference fix]
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 Documentation/Makefile.am                |    3 
 Documentation/ndctl-create-namespace.txt |  127 +++++
 Documentation/ndctl.txt                  |    2 
 Makefile.am                              |    4 
 builtin-create-nfit.c                    |   45 --
 builtin-xable-namespace.c                |  153 -------
 builtin-xaction-namespace.c              |  693 ++++++++++++++++++++++++++++++
 builtin.h                                |    3 
 ndctl.c                                  |    1 
 util/size.c                              |   42 ++
 util/size.h                              |   12 +
 11 files changed, 882 insertions(+), 203 deletions(-)
 create mode 100644 Documentation/ndctl-create-namespace.txt
 delete mode 100644 builtin-xable-namespace.c
 create mode 100644 builtin-xaction-namespace.c
 create mode 100644 util/size.c
 create mode 100644 util/size.h

Comments

Jeff Moyer Jan. 28, 2016, 11:39 p.m. UTC | #1
Dan Williams <dan.j.williams@intel.com> writes:

> A helper utility for instantiating a namespace.  With no arguments it
> defaults to a maximally sized pmem namespace in memory mode.  The search
> for available capacity can be constrained by bus, region, and/or
> namespace type using the same mechanism to filter namespaces as 'ndctl
> enable-namespace' and 'ndctl disable-namespace'.
>
> See the include man page for more details.
>
> Cc: Jeff Moyer <jmoyer@redhat.com>
> [jmoyer: null dereference fix]

Hi, Dan,

If that's the only fix since last I saw this patch, then it's still got
bugs.  ;-)  Sorry I can't test right this second, but I'll dedicate
some time to it tomorrow.  I'm pretty sure I hit another null pointer
dereference.  Did you end up writing unit tests for this?

Cheers,
Jeff

> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> ---
>  Documentation/Makefile.am                |    3 
>  Documentation/ndctl-create-namespace.txt |  127 +++++
>  Documentation/ndctl.txt                  |    2 
>  Makefile.am                              |    4 
>  builtin-create-nfit.c                    |   45 --
>  builtin-xable-namespace.c                |  153 -------
>  builtin-xaction-namespace.c              |  693 ++++++++++++++++++++++++++++++
>  builtin.h                                |    3 
>  ndctl.c                                  |    1 
>  util/size.c                              |   42 ++
>  util/size.h                              |   12 +
>  11 files changed, 882 insertions(+), 203 deletions(-)
>  create mode 100644 Documentation/ndctl-create-namespace.txt
>  delete mode 100644 builtin-xable-namespace.c
>  create mode 100644 builtin-xaction-namespace.c
>  create mode 100644 util/size.c
>  create mode 100644 util/size.h
>
> diff --git a/Documentation/Makefile.am b/Documentation/Makefile.am
> index bd43714d5215..51d186505b44 100644
> --- a/Documentation/Makefile.am
> +++ b/Documentation/Makefile.am
> @@ -4,7 +4,8 @@ man1_MANS = \
>  	ndctl-enable-region.1 \
>  	ndctl-disable-region.1 \
>  	ndctl-enable-namespace.1 \
> -	ndctl-disable-namespace.1
> +	ndctl-disable-namespace.1 \
> +	ndctl-create-namespace.1
>  
>  XML_DEPS = \
>  	$(top_srcdir)/version.m4 \
> diff --git a/Documentation/ndctl-create-namespace.txt b/Documentation/ndctl-create-namespace.txt
> new file mode 100644
> index 000000000000..3a9f911c231c
> --- /dev/null
> +++ b/Documentation/ndctl-create-namespace.txt
> @@ -0,0 +1,127 @@
> +ndctl-create-namespace(1)
> +=========================
> +
> +NAME
> +----
> +ndctl-create-namespace - provision or reconfigure a namespace
> +
> +SYNOPSIS
> +--------
> +[verse]
> +'ndctl create-namespace' [<options>]
> +
> +include::namespace-description.txt[]
> +
> +EXAMPLES
> +--------
> +
> +Create a maximally sized pmem namespace in 'memory' mode
> +[verse]
> +ndctl create-namespace
> +
> +Convert namespace0.0 to 'safe' mode
> +[verse]
> +ndctl create-namespace -f -e namespace0.0 --mode=safe
> +
> +OPTIONS
> +-------
> +-t::
> +--type=::
> +	Create a 'pmem' or 'blk' namespace (subject to available
> +	capacity).  A pmem namespace supports the DAX (direct access)
> +	capability to linkndctl:mmap[2] persistent memory directly into
> +	a process address space.  A blk namespace access persistent
> +	memory through a block-window-aperture.  Compared to pmem it
> +	supports a traditional storage error model (EIO on error rather
> +	than a cpu exception on a bad memory access), but it does not
> +	support DAX.
> +
> +-m::
> +--mode=::
> +	- "safe": persistent memory, given that it is byte addressable,
> +	  does not support sector atomicity.  The problematic aspect of
> +	  sector tearing is that most applications do not know they have
> +	  a atomic sector update dependency.  At least a disk rarely
> +	  ever tears sectors and if it does it almost certainly returns
> +	  a checksum error on access.  Persistent memory devices will
> +	  always tear and always silently.  Until an application is
> +	  audited to be robust in the presence of sector-tearing "safe"
> +	  mode is recommended.  This imposes some performance overhead
> +	  and disables the DAX capability.
> +
> +	- "memory": A pmem namespace in this mode supports direct I/O
> +	  to/from DAX mappings.  Depending on the kernel this mode may
> +	  come at the cost of allocating per-pmem-page metadata.  If that
> +	  allocation is required the capacity can be allocated from
> +	  "System RAM" or from a reserved portion of pmem (see the --map=
> +	  option).
> +
> +	- "raw": expose the namespace capacity directly with some
> +	  limitations.  Neither a raw pmem namepace nor raw blk namespace
> +	  support sector atomicity by default (see "safe" mode below).  A
> +	  raw pmem namespace may have limited support for passing a DAX
> +	  mapping to other syscalls.  I.e. direct-I/O to/from a DAX buffer
> +	  may fail for a pmem namespace in raw mode.
> +
> +-s::
> +--size=::
> +	For NVDIMM devices that support namespace labels, set the
> +	namespace size.  Otherwise it defaults to the maximum size
> +	specified by platform firmware.
> +
> +-e::
> +--reconfig=::
> +	Reconfigure an existing namespace (change the mode, sector size,
> +	etc...).  All namespace parameters, save uuid, default to the
> +	current attributes of the specified namespace.  The namespace is
> +	then re-created with the specified modifications.  The uuid is
> +	refreshed to a new value by default whenever the data layout of
> +	a namespace is changed, see --uuid= to set a specific uuid.
> +
> +-u::
> +--uuid=::
> +	This option is not recommended as a new uuid should be generated
> +	every time a namespace is (re-)created.  For recovery scenarios
> +	however the uuid may be specified.
> +
> +-n::
> +--name=::
> +	For NVDIMM devices that support namespace labels,
> +	specify a human friendly name for a namespace.  This name is
> +	available as device attribute for use in udev rules or
> +	elsewhere.
> +
> +-l::
> +--sector-size::
> +	Specify the logical sector size (LBA size) of the block storage
> +	device associated with a namespace.
> +
> +-M::
> +--map=::
> +	A pmem namespace in "memory" mode may require allocation
> +	of per-page metadata.  The allocation can be drawn from either:
> +	- "mem": typical system memory
> +	- "dev": persistent memory reserved from the namespace
> +
> +-f::
> +--force::
> +	Unless this option is specified a 'reconfigure
> +	namespace' operation will fail if the namespace is presently
> +	active.  Specifying --force causes the namespace to be disabled
> +	before reconfiguring.
> +
> +-v::
> +--verbose::
> +	Emit debug messages for the namespace creation process
> +
> +-r::
> +--region=::
> +include::xable-region-options.txt[]
> +
> +SEE ALSO
> +--------
> +linkndctl:ndctl-zero-labels[1],
> +linkndctl:ndctl-disable-namespace[1],
> +linkndctl:ndctl-enable-namespace[1],
> +http://pmem.io/documents/NVDIMM_Namespace_Spec.pdf[NVDIMM Namespace
> +Specification]
> diff --git a/Documentation/ndctl.txt b/Documentation/ndctl.txt
> index 87d2fbfcbcf9..13a0e36627f3 100644
> --- a/Documentation/ndctl.txt
> +++ b/Documentation/ndctl.txt
> @@ -32,7 +32,7 @@ namspaces) associated with an NVDIMM bus.
>  
>  SEE ALSO
>  --------
> -linkndctl:ndctl-zero-labels[1],
> +linkndctl:ndctl-create-namespace[1],
>  linkndctl:ndctl-enable-region[1],
>  linkndctl:ndctl-disable-region[1],
>  linkndctl:ndctl-enable-namespace[1],
> diff --git a/Makefile.am b/Makefile.am
> index ffbbd044050f..baae4d0c1ee4 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -64,7 +64,7 @@ bin_PROGRAMS = ndctl
>  
>  ndctl_SOURCES = ndctl.c \
>  		builtin-create-nfit.c \
> -		builtin-xable-namespace.c \
> +		builtin-xaction-namespace.c \
>  		builtin-xable-region.c \
>  		builtin-test.c \
>  		builtin-help.c \
> @@ -72,8 +72,8 @@ ndctl_SOURCES = ndctl.c \
>  		util/parse-options.c \
>  		util/parse-options.h \
>  		util/usage.c \
> +		util/size.c \
>  		util/strbuf.c \
> -		util/strbuf.h \
>  		util/wrapper.c
>  
>  if ENABLE_TEST
> diff --git a/builtin-create-nfit.c b/builtin-create-nfit.c
> index 6884b28db340..fad6b9a4b65d 100644
> --- a/builtin-create-nfit.c
> +++ b/builtin-create-nfit.c
> @@ -8,6 +8,7 @@
>  #include <sys/stat.h>
>  #include <ccan/list/list.h>
>  #include <util/parse-options.h>
> +#include <util/size.h>
>  
>  #include <nfit.h>
>  
> @@ -20,50 +21,6 @@ struct spa {
>  	unsigned long long size, offset;
>  };
>  
> -#define SZ_1K     0x00000400
> -#define SZ_1M     0x00100000
> -#define SZ_1G     0x40000000
> -#define SZ_1T 0x10000000000ULL
> -
> -static unsigned long long parse_size64(const char *str)
> -{
> -	unsigned long long val, check;
> -	char *end;
> -
> -	val = strtoull(str, &end, 0);
> -	if (val == ULLONG_MAX)
> -		return val;
> -	check = val;
> -	switch (*end) {
> -	case 'k':
> -	case 'K':
> -		val *= SZ_1K;
> -		end++;
> -		break;
> -	case 'm':
> -	case 'M':
> -		val *= SZ_1M;
> -		end++;
> -		break;
> -	case 'g':
> -	case 'G':
> -		val *= SZ_1G;
> -		end++;
> -		break;
> -	case 't':
> -	case 'T':
> -		val *= SZ_1T;
> -		end++;
> -		break;
> -	default:
> -		break;
> -	}
> -
> -	if (val < check || *end != '\0')
> -		val = ULLONG_MAX;
> -	return val;
> -}
> -
>  static int parse_add_spa(const struct option *option, const char *__arg, int unset)
>  {
>  	struct spa *s = calloc(1, sizeof(struct spa));
> diff --git a/builtin-xable-namespace.c b/builtin-xable-namespace.c
> deleted file mode 100644
> index 13a08633487a..000000000000
> --- a/builtin-xable-namespace.c
> +++ /dev/null
> @@ -1,153 +0,0 @@
> -#include <stdio.h>
> -#include <errno.h>
> -#include <stdlib.h>
> -#include <unistd.h>
> -#include <limits.h>
> -#include <util/parse-options.h>
> -#include <ndctl/libndctl.h>
> -
> -static const char *namespace_bus;
> -static const char *namespace_region;
> -
> -static const struct option namespace_options[] = {
> -	OPT_STRING('b', "bus", &namespace_bus, "bus-id",
> -			"<namespace> must be on a bus with an id/provider of <bus-id>"),
> -	OPT_STRING('r', "region", &namespace_region, "region-id",
> -			"<namespace> must be a child of a region with an id/name of <region-id>"),
> -	OPT_END(),
> -};
> -
> -static const char *parse_namespace_options(int argc, const char **argv,
> -		char *xable_usage)
> -{
> -	const char * const u[] = {
> -		xable_usage,
> -		NULL
> -	};
> -	int i;
> -
> -        argc = parse_options(argc, argv, namespace_options, u, 0);
> -
> -	if (argc == 0)
> -		error("specify a namespace to delete, or \"all\"\n");
> -	for (i = 1; i < argc; i++)
> -		error("unknown extra parameter \"%s\"\n", argv[i]);
> -	if (argc == 0 || argc > 1) {
> -		usage_with_options(u, namespace_options);
> -		return NULL; /* we won't return from usage_with_options() */
> -	}
> -	return argv[0];
> -}
> -
> -static int do_xable_namespace(const char *namespace,
> -		int (*xable_fn)(struct ndctl_namespace *))
> -{
> -	unsigned long bus_id = ULONG_MAX, region_id = ULONG_MAX, id;
> -	const char *provider, *region_name, *ndns_name;
> -	int rc = -ENXIO, success = 0;
> -	struct ndctl_namespace *ndns;
> -	struct ndctl_region *region;
> -	struct ndctl_ctx *ctx;
> -	struct ndctl_bus *bus;
> -
> -	if (!namespace)
> -		goto out;
> -
> -	rc = ndctl_new(&ctx);
> -	if (rc < 0)
> -		goto out;
> -
> -	if (namespace_bus) {
> -		char *end = NULL;
> -
> -		bus_id = strtoul(namespace_bus, &end, 0);
> -		if (end)
> -			bus_id = ULONG_MAX;
> -	}
> -
> -	if (namespace_region) {
> -		char *end = NULL;
> -
> -		region_id = strtoul(namespace_region, &end, 0);
> -		if (end)
> -			region_id = ULONG_MAX;
> -	}
> -
> -        ndctl_bus_foreach(ctx, bus) {
> -		provider = ndctl_bus_get_provider(bus);
> -		id = ndctl_bus_get_id(bus);
> -
> -		if (bus_id < ULONG_MAX && bus_id != id)
> -			continue;
> -		else if (bus_id == ULONG_MAX && namespace_bus
> -				&& strcmp(namespace_bus, provider) != 0)
> -			continue;
> -
> -		ndctl_region_foreach(bus, region) {
> -			region_name = ndctl_region_get_devname(region);
> -			id = ndctl_region_get_id(region);
> -
> -			if (region_id < ULONG_MAX && region_id != id)
> -				continue;
> -			else if (region_id == ULONG_MAX && namespace_region
> -					&& strcmp(namespace_region, region_name) != 0)
> -				continue;
> -			ndctl_namespace_foreach(region, ndns) {
> -				ndns_name = ndctl_namespace_get_devname(ndns);
> -
> -				if (strcmp(namespace, "all") != 0
> -						&& strcmp(namespace, ndns_name) != 0)
> -					continue;
> -				if (xable_fn(ndns) == 0)
> -					success++;
> -			}
> -		}
> -	}
> -
> -	rc = success;
> -	ndctl_unref(ctx);
> - out:
> -	namespace_bus = NULL;
> -	namespace_region = NULL;
> -	return rc;
> -}
> -
> -int cmd_disable_namespace(int argc, const char **argv)
> -{
> -	char *xable_usage = "ndctl disable-namespace <namespace> [<options>]";
> -	const char *namespace = parse_namespace_options(argc, argv, xable_usage);
> -	int disabled = do_xable_namespace(namespace, ndctl_namespace_disable_invalidate);
> -
> -	if (disabled < 0) {
> -		fprintf(stderr, "error disabling namespaces: %s\n",
> -				strerror(-disabled));
> -		return disabled;
> -	} else if (disabled == 0) {
> -		fprintf(stderr, "disabled 0 namespaces\n");
> -		return -ENXIO;
> -	} else {
> -		fprintf(stderr, "disabled %d namespace%s\n", disabled,
> -				disabled > 1 ? "s" : "");
> -		return disabled;
> -	}
> -}
> -
> -int cmd_enable_namespace(int argc, const char **argv)
> -{
> -	char *xable_usage = "ndctl enable-namespace <namespace> [<options>]";
> -	const char *namespace = parse_namespace_options(argc, argv, xable_usage);
> -	int enabled = do_xable_namespace(namespace, ndctl_namespace_enable);
> -
> -	if (enabled < 0) {
> -		fprintf(stderr, "error enabling namespaces: %s\n",
> -				strerror(-enabled));
> -		return enabled;
> -	} else if (enabled == 0) {
> -		fprintf(stderr, "enabled 0 namespaces\n");
> -		return -ENXIO;
> -	} else {
> -		fprintf(stderr, "enabled %d namespace%s\n", enabled,
> -				enabled > 1 ? "s" : "");
> -		return enabled;
> -	}
> -}
> diff --git a/builtin-xaction-namespace.c b/builtin-xaction-namespace.c
> new file mode 100644
> index 000000000000..fdf20dfc01af
> --- /dev/null
> +++ b/builtin-xaction-namespace.c
> @@ -0,0 +1,693 @@
> +/*
> + * Copyright(c) 2015 Intel Corporation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of version 2 of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * 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.
> + */
> +#include <stdio.h>
> +#include <fcntl.h>
> +#include <errno.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <limits.h>
> +#include <syslog.h>
> +#include <sys/stat.h>
> +#include <uuid/uuid.h>
> +#include <sys/types.h>
> +#include <util/size.h>
> +#include <ndctl/libndctl.h>
> +#include <util/parse-options.h>
> +#include <ccan/array_size/array_size.h>
> +
> +#ifdef HAVE_NDCTL_H
> +#include <linux/ndctl.h>
> +#else
> +#include <ndctl.h>
> +#endif
> +
> +static bool verbose;
> +static bool force;
> +static struct parameters {
> +	const char *bus;
> +	const char *map;
> +	const char *type;
> +	const char *uuid;
> +	const char *name;
> +	const char *size;
> +	const char *mode;
> +	const char *region;
> +	const char *reconfig;
> +	const char *sector_size;
> +} param;
> +
> +#define NSLABEL_NAME_LEN 64
> +struct parsed_parameters {
> +	enum ndctl_pfn_loc loc;
> +	uuid_t uuid;
> +	char name[NSLABEL_NAME_LEN];
> +	enum ndctl_namespace_mode mode;
> +	unsigned long long size;
> +	unsigned long sector_size;
> +};
> +
> +#define debug(fmt, ...) \
> +	if (verbose) { \
> +		fprintf(stderr, "%s:%d: " fmt, __func__, __LINE__, ##__VA_ARGS__); \
> +	} else { \
> +		do { } while (0); \
> +	}
> +
> +#define BASE_OPTIONS() \
> +OPT_STRING('b', "bus", &param.bus, "bus-id", \
> +	"limit namespace to a bus with an id or provider of <bus-id>"), \
> +OPT_STRING('r', "region", &param.region, "region-id", \
> +	"limit namespace to a region with an id or name of <region-id>")
> +
> +#define CREATE_OPTIONS() \
> +OPT_STRING('e', "reconfig", &param.reconfig, "reconfig namespace", \
> +	"reconfigure existing namespace"), \
> +OPT_STRING('u', "uuid", &param.uuid, "uuid", \
> +	"specify the uuid for the namespace (default: autogenerate)"), \
> +OPT_STRING('n', "name", &param.name, "name", \
> +	"specify an optional free form name for the namespace"), \
> +OPT_STRING('s', "size", &param.size, "size", \
> +	"specify the namespace size in bytes (default: available capacity)"), \
> +OPT_STRING('m', "mode", &param.mode, "operation-mode", \
> +	"specify a mode for the namespace, 'safe', 'memory', or 'raw'"), \
> +OPT_STRING('M', "map", &param.map, "memmap-location", \
> +	"specify 'mem' or 'dev' for the location of the memmap"), \
> +OPT_STRING('l', "sector-size", &param.sector_size, "lba-size", \
> +	"specify the logical sector size in bytes"), \
> +OPT_STRING('t', "type", &param.type, "type", \
> +	"specify the type of namespace to create 'pmem' or 'blk'"), \
> +OPT_BOOLEAN('f', "force", &force, "reconfigure namespace even if currently active"), \
> +OPT_BOOLEAN('v', "verbose", &verbose, "emit extra debug messages to stderr")
> +
> +static const struct option base_options[] = {
> +	BASE_OPTIONS(),
> +	OPT_END(),
> +};
> +
> +static const struct option create_options[] = {
> +	BASE_OPTIONS(),
> +	CREATE_OPTIONS(),
> +	OPT_END(),
> +};
> +
> +enum namespace_action {
> +	ACTION_ENABLE,
> +	ACTION_DISABLE,
> +	ACTION_CREATE,
> +};
> +
> +/*
> + * parse_namespace_options - basic parsing sanity checks before we start
> + * looking at actual namespace devices and available resources.
> + */
> +static const char *parse_namespace_options(int argc, const char **argv,
> +		enum namespace_action mode, const struct option *options,
> +		char *xable_usage)
> +{
> +	const char * const u[] = {
> +		xable_usage,
> +		NULL
> +	};
> +	int i, rc = 0;
> +
> +        argc = parse_options(argc, argv, options, u, 0);
> +
> +	if (param.type) {
> +		if (strcmp(param.type, "pmem") == 0)
> +			/* pass */;
> +		else if (strcmp(param.type, "blk") == 0)
> +			/* pass */;
> +		else {
> +			error("invalid type '%s', must be either 'pmem' or 'blk'\n",
> +				param.type);
> +			rc = -EINVAL;
> +		}
> +	} else if (!param.reconfig)
> +		param.type = "pmem";
> +
> +	if (param.mode) {
> +		if (strcmp(param.mode, "safe") == 0)
> +			/* pass */;
> +		else if (strcmp(param.mode, "sector") == 0)
> +		      param.mode = "safe"; /* pass */
> +		else if (strcmp(param.mode, "memory") == 0)
> +		      /* pass */;
> +		else if (strcmp(param.mode, "raw") == 0)
> +		      /* pass */;
> +		else {
> +			error("invalid mode '%s'\n", param.mode);
> +			rc = -EINVAL;
> +		}
> +	} else if (!param.reconfig) {
> +		if (strcmp(param.type, "pmem") == 0)
> +			param.mode = "memory";
> +		else
> +			param.mode = "safe";
> +	}
> +
> +	if (param.map) {
> +		if (strcmp(param.map, "mem") == 0)
> +			/* pass */;
> +		else if (strcmp(param.map, "dev") == 0)
> +			/* pass */;
> +		else {
> +			error("invalid map location '%s'\n", param.map);
> +			rc = -EINVAL;
> +		}
> +
> +		if (!param.reconfig && strcmp(param.mode, "memory") != 0) {
> +			error("--map only valid for a memory mode pmem namespace\n");
> +			rc = -EINVAL;
> +		}
> +	} else if (!param.reconfig)
> +		param.map = "dev";
> +
> +	/* check for incompatible mode and type combinations */
> +	if (param.type && param.mode && strcmp(param.type, "blk") == 0
> +			&& strcmp(param.mode, "memory") == 0) {
> +		error("only 'pmem' namespaces can be placed into 'memory' mode\n");
> +		rc = -ENXIO;
> +	}
> +
> +	if (param.size && parse_size64(param.size) == ULLONG_MAX) {
> +		error("failed to parse namespace size '%s'\n",
> +				param.size);
> +		rc = -EINVAL;
> +	}
> +
> +	if (param.uuid) {
> +		uuid_t uuid;
> +
> +		if (uuid_parse(param.uuid, uuid)) {
> +			error("failed to parse uuid: '%s'\n", param.uuid);
> +			rc = -EINVAL;
> +		}
> +	}
> +
> +	if (param.sector_size) {
> +		if (parse_size64(param.sector_size) == ULLONG_MAX) {
> +			error("invalid sector size: %s\n", param.sector_size);
> +			rc = -EINVAL;
> +		}
> +
> +		if (param.type && param.mode && strcmp(param.type, "pmem") == 0
> +				&& strcmp(param.mode, "safe") != 0) {
> +			error("'pmem' namespaces do not support setting 'sector size'\n");
> +			rc = -EINVAL;
> +		}
> +	} else if (!param.reconfig)
> +		param.sector_size = "4096";
> +
> +	if (argc == 0 && mode != ACTION_CREATE) {
> +		error("specify a namespace to %s, or \"all\"\n",
> +				mode == ACTION_ENABLE ? "enable" : "disable");
> +		rc = -EINVAL;
> +	}
> +	for (i = mode == ACTION_CREATE ? 0 : 1; i < argc; i++) {
> +		error("unknown extra parameter \"%s\"\n", argv[i]);
> +		rc = -EINVAL;
> +	}
> +
> +	if (rc) {
> +		usage_with_options(u, options);
> +		return NULL; /* we won't return from usage_with_options() */
> +	}
> +
> +	return mode == ACTION_CREATE ? param.reconfig : argv[0];
> +}
> +
> +#define try(prefix, op, dev, p) \
> +do { \
> +	int __rc = prefix##_##op(dev, p); \
> +	if (__rc) { \
> +		debug("%s: " #op " failed: %d\n", \
> +				prefix##_get_devname(dev), __rc); \
> +		return __rc; \
> +	} \
> +} while (0)
> +
> +static int setup_namespace(struct ndctl_region *region,
> +		struct ndctl_namespace *ndns, struct parsed_parameters *p)
> +{
> +	uuid_t uuid;
> +	int rc;
> +
> +	if (ndctl_namespace_get_type(ndns) != ND_DEVICE_NAMESPACE_IO) {
> +		try(ndctl_namespace, set_uuid, ndns, p->uuid);
> +		try(ndctl_namespace, set_alt_name, ndns, p->name);
> +		try(ndctl_namespace, set_size, ndns, p->size);
> +	}
> +
> +	if (ndctl_namespace_get_type(ndns) == ND_DEVICE_NAMESPACE_BLK)
> +		try(ndctl_namespace, set_sector_size, ndns, p->sector_size);
> +
> +	uuid_generate(uuid);
> +	if (ndctl_namespace_get_mode(ndns) != NDCTL_NS_MODE_MEMORY
> +			&& p->mode == NDCTL_NS_MODE_MEMORY) {
> +		struct ndctl_pfn *pfn = ndctl_region_get_pfn_seed(region);
> +
> +		try(ndctl_pfn, set_uuid, pfn, uuid);
> +		try(ndctl_pfn, set_location, pfn, p->loc);
> +		try(ndctl_pfn, set_align, pfn, SZ_2M);
> +		try(ndctl_pfn, set_namespace, pfn, ndns);
> +		rc = ndctl_pfn_enable(pfn);
> +	} else if (p->mode == NDCTL_NS_MODE_SAFE) {
> +		struct ndctl_btt *btt = ndctl_region_get_btt_seed(region);
> +
> +		try(ndctl_btt, set_uuid, btt, uuid);
> +		try(ndctl_btt, set_sector_size, btt, p->sector_size);
> +		try(ndctl_btt, set_namespace, btt, ndns);
> +		rc = ndctl_btt_enable(btt);
> +	} else
> +		rc = ndctl_namespace_enable(ndns);
> +
> +	if (rc) {
> +		error("%s: failed to enable\n",
> +				ndctl_namespace_get_devname(ndns));
> +	}
> +	return rc;
> +}
> +
> +static int is_namespace_active(struct ndctl_namespace *ndns)
> +{
> +	return ndns && (ndctl_namespace_is_enabled(ndns)
> +		|| ndctl_namespace_get_pfn(ndns)
> +		|| ndctl_namespace_get_btt(ndns));
> +}
> +
> +/*
> + * validate_namespace_options - init parameters for setup_namespace
> + * @ndns: specified when we are reconfiguring, NULL otherwise
> + * @p: parameters to fill
> + *
> + * parse_namespace_options() will have already done basic verification
> + * of the parameters and set defaults in the !reconfigure case.  When
> + * reconfiguring fill in any unset options with defaults from the
> + * namespace itself.
> + *
> + * Given that parse_namespace_options() runs before we have identified
> + * the target namespace we need to do basic sanity checks here for
> + * pmem-only attributes specified for blk namespace and vice versa.
> + */
> +static int validate_namespace_options(struct ndctl_namespace *ndns,
> +		struct parsed_parameters *p)
> +{
> +	int rc = 0;
> +
> +	memset(p, 0, sizeof(*p));
> +
> +	if (param.size)
> +		p->size = parse_size64(param.size);
> +	else if (ndns)
> +		p->size = ndctl_namespace_get_size(ndns);
> +
> +	if (param.uuid) {
> +		if (uuid_parse(param.uuid, p->uuid) != 0) {
> +			debug("%s: invalid uuid\n", __func__);
> +			return -EINVAL;
> +		}
> +	} else
> +		uuid_generate(p->uuid);
> +
> +	if (param.name)
> +		rc = snprintf(p->name, sizeof(p->name), "%s", param.name);
> +	else if (ndns)
> +		rc = snprintf(p->name, sizeof(p->name), "%s",
> +				ndctl_namespace_get_alt_name(ndns));
> +	if (rc >= (int) sizeof(p->name)) {
> +		debug("%s: alt name overflow\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (param.mode) {
> +		if (strcmp(param.mode, "memory") == 0)
> +			p->mode = NDCTL_NS_MODE_MEMORY;
> +		else if (strcmp(param.mode, "safe") == 0)
> +			p->mode = NDCTL_NS_MODE_SAFE;
> +		else
> +			p->mode = NDCTL_NS_MODE_RAW;
> +
> +		if (ndns && ndctl_namespace_get_type(ndns)
> +				== ND_DEVICE_NAMESPACE_BLK
> +				&& p->mode == NDCTL_NS_MODE_MEMORY) {
> +			debug("%s: blk namespace do not support memory mode\n",
> +					ndctl_namespace_get_devname(ndns));
> +				return -EINVAL;
> +		}
> +	} else if (ndns)
> +		p->mode = ndctl_namespace_get_mode(ndns);
> +
> +	if (param.sector_size) {
> +		p->sector_size = parse_size64(param.sector_size);
> +
> +		if (ndns && p->mode != NDCTL_NS_MODE_SAFE
> +				&& ndctl_namespace_get_type(ndns)
> +				== ND_DEVICE_NAMESPACE_PMEM) {
> +			debug("%s: does not support sector_size modification\n",
> +					ndctl_namespace_get_devname(ndns));
> +				return -EINVAL;
> +		}
> +	} else if (ndns) {
> +		struct ndctl_btt *btt = ndctl_namespace_get_btt(ndns);
> +
> +		if (btt)
> +			p->sector_size = ndctl_btt_get_sector_size(btt);
> +		else if (ndctl_namespace_get_type(ndns)
> +				== ND_DEVICE_NAMESPACE_BLK)
> +			p->sector_size = ndctl_namespace_get_sector_size(ndns);
> +	}
> +
> +	if (param.map) {
> +		if (strcmp(param.map, "mem"))
> +			p->loc = NDCTL_PFN_LOC_RAM;
> +		else
> +			p->loc = NDCTL_PFN_LOC_PMEM;
> +
> +		if (ndns && p->mode != NDCTL_NS_MODE_MEMORY) {
> +			debug("%s: --map= only valid for memory mode namespace\n",
> +				ndctl_namespace_get_devname(ndns));
> +			return -EINVAL;
> +		}
> +	} else if (p->mode == NDCTL_NS_MODE_MEMORY)
> +		p->loc = NDCTL_PFN_LOC_PMEM;
> +
> +	return 0;
> +}
> +
> +static int namespace_create(struct ndctl_region *region)
> +{
> +	const char *devname = ndctl_region_get_devname(region);
> +	unsigned long long available;
> +	struct ndctl_namespace *ndns;
> +	struct parsed_parameters p;
> +
> +	if (validate_namespace_options(NULL, &p))
> +		return -EINVAL;
> +
> +	if (ndctl_region_get_ro(region)) {
> +		debug("%s: read-only, inelligible for namespace creation\n",
> +			devname);
> +		return -EAGAIN;
> +	}
> +
> +	available = ndctl_region_get_available_size(region);
> +	if (!available || p.size > available) {
> +		debug("%s: insufficient capacity size: %llx avail: %llx\n",
> +			devname, p.size, available);
> +		return -EAGAIN;
> +	}
> +
> +	if (p.size == 0)
> +		p.size = available;
> +
> +	ndns = ndctl_region_get_namespace_seed(region);
> +	if (is_namespace_active(ndns)) {
> +		debug("%s: no %s namespace seed\n", devname,
> +				ndns ? "idle" : "available");
> +		return -ENODEV;
> +	}
> +
> +	return setup_namespace(region, ndns, &p);
> +}
> +
> +static int zero_info_block(struct ndctl_namespace *ndns)
> +{
> +	const char *devname = ndctl_namespace_get_devname(ndns);
> +	int fd, rc = -ENXIO;
> +	char path[50];
> +	void *buf;
> +
> +	if (posix_memalign(&buf, 4096, 4096) != 0)
> +		return -ENXIO;
> +
> +	ndctl_namespace_set_raw_mode(ndns, 1);
> +	ndctl_namespace_enable(ndns);
> +
> +	sprintf(path, "/dev/%s", ndctl_namespace_get_block_device(ndns));
> +	fd = open(path, O_RDWR|O_DIRECT|O_EXCL);
> +	if (fd < 0) {
> +		debug("%s: failed to open %s to zero info block\n",
> +				devname, path);
> +		goto out;
> +	}
> +
> +	memset(buf, 0, 4096);
> +	rc = pwrite(fd, buf, 4096, 4096);
> +	if (rc < 4096) {
> +		debug("%s: failed to zero info block %s\n",
> +				devname, path);
> +		rc = -ENXIO;
> +	} else
> +		rc = 0;
> +	close(fd);
> + out:
> +	ndctl_namespace_set_raw_mode(ndns, 0);
> +	ndctl_namespace_disable_invalidate(ndns);
> +	free(buf);
> +	return rc;
> +}
> +
> +static int namespace_reconfig(struct ndctl_region *region,
> +		struct ndctl_namespace *ndns)
> +{
> +	const char *devname = ndctl_namespace_get_devname(ndns);
> +	struct ndctl_pfn *pfn = ndctl_namespace_get_pfn(ndns);
> +	struct ndctl_btt *btt = ndctl_namespace_get_btt(ndns);
> +	struct parsed_parameters p;
> +	const char *bdev = NULL;
> +	char path[50];
> +	int fd, rc;
> +
> +	if (validate_namespace_options(ndns, &p))
> +		return -EINVAL;
> +
> +	if (ndctl_region_get_ro(region)) {
> +		error("%s: read-only, re-configuration disabled\n",
> +				devname);
> +		return -ENXIO;
> +	}
> +
> +	if (pfn && ndctl_pfn_is_enabled(pfn))
> +		bdev = ndctl_pfn_get_block_device(pfn);
> +	else if (btt && ndctl_btt_is_enabled(btt))
> +		bdev = ndctl_btt_get_block_device(btt);
> +	else if (ndctl_namespace_is_enabled(ndns))
> +		bdev = ndctl_namespace_get_block_device(ndns);
> +
> +	if (bdev && !force) {
> +		error("%s is active, specify --force for re-configuration\n",
> +				devname);
> +		return -EBUSY;
> +	} else if (bdev) {
> +		sprintf(path, "/dev/%s", bdev);
> +		fd = open(path, O_RDWR|O_EXCL);
> +		if (fd >= 0) {
> +			/*
> +			 * Got it, now block new mounts while we have it
> +			 * pinned.
> +			 */
> +			ndctl_namespace_disable_invalidate(ndns);
> +			close(fd);
> +		} else {
> +			/*
> +			 * Yes, TOCTOU hole, but if you're racing namespace
> +			 * creation you have other problems, and there's nothing
> +			 * stopping the !bdev case from racing to mount an fs or
> +			 * re-enabling the namepace.
> +			 */
> +			return -EBUSY;
> +		}
> +	}
> +
> +	if (pfn || btt) {
> +		rc = zero_info_block(ndns);
> +		if (rc)
> +			return rc;
> +	}
> +
> +	rc = ndctl_namespace_delete(ndns);
> +	if (rc) {
> +		error("%s: failed to reclaim\n", devname);
> +		return rc;
> +	}
> +
> +	ndns = ndctl_region_get_namespace_seed(region);
> +	if (is_namespace_active(ndns)) {
> +		debug("%s: no %s namespace seed\n", devname,
> +				ndns ? "idle" : "available");
> +		return -ENODEV;
> +	}
> +
> +	return setup_namespace(region, ndns, &p);
> +}
> +
> +static int do_xaction_namespace(const char *namespace,
> +		enum namespace_action action)
> +{
> +	unsigned long bus_id = ULONG_MAX, region_id = ULONG_MAX, id;
> +	const char *provider, *region_name, *ndns_name;
> +	int rc = -ENXIO, success = 0;
> +	struct ndctl_namespace *ndns;
> +	struct ndctl_region *region;
> +	struct ndctl_ctx *ctx;
> +	struct ndctl_bus *bus;
> +
> +	if (!namespace && action != ACTION_CREATE)
> +		return rc;
> +
> +	rc = ndctl_new(&ctx);
> +	if (rc < 0)
> +		return rc;
> +
> +	if (verbose)
> +		ndctl_set_log_priority(ctx, LOG_DEBUG);
> +
> +	if (param.bus) {
> +		char *end = NULL;
> +
> +		bus_id = strtoul(param.bus, &end, 0);
> +		if (end)
> +			bus_id = ULONG_MAX;
> +	}
> +
> +	if (param.region) {
> +		char *end = NULL;
> +
> +		region_id = strtoul(param.region, &end, 0);
> +		if (end)
> +			region_id = ULONG_MAX;
> +	}
> +
> +        ndctl_bus_foreach(ctx, bus) {
> +		provider = ndctl_bus_get_provider(bus);
> +		id = ndctl_bus_get_id(bus);
> +
> +		if (bus_id < ULONG_MAX && bus_id != id)
> +			continue;
> +		else if (bus_id == ULONG_MAX && param.bus
> +				&& strcmp(param.bus, provider) != 0)
> +			continue;
> +
> +		ndctl_region_foreach(bus, region) {
> +			region_name = ndctl_region_get_devname(region);
> +			id = ndctl_region_get_id(region);
> +
> +			if (region_id < ULONG_MAX && region_id != id)
> +				continue;
> +			else if (region_id == ULONG_MAX && param.region
> +					&& strcmp(param.region, region_name) != 0)
> +				continue;
> +
> +			if (param.type) {
> +				if (strcmp(param.type, "pmem") == 0
> +						&& ndctl_region_get_type(region)
> +						== ND_DEVICE_REGION_PMEM)
> +					/* pass */;
> +				else if (strcmp(param.type, "blk") == 0
> +						&& ndctl_region_get_type(region)
> +						== ND_DEVICE_REGION_BLK)
> +					/* pass */;
> +				else
> +					continue;
> +			}
> +
> +			if (action == ACTION_CREATE && !namespace) {
> +				rc = namespace_create(region);
> +				if (rc == -EAGAIN) {
> +					rc = 0;
> +					continue;
> +				}
> +				goto done;
> +			}
> +			ndctl_namespace_foreach(region, ndns) {
> +				ndns_name = ndctl_namespace_get_devname(ndns);
> +
> +				if (strcmp(namespace, "all") != 0
> +						&& strcmp(namespace, ndns_name) != 0)
> +					continue;
> +				switch (action) {
> +				case ACTION_DISABLE:
> +					rc = ndctl_namespace_disable_invalidate(ndns);
> +					break;
> +				case ACTION_ENABLE:
> +					rc = ndctl_namespace_enable(ndns);
> +					break;
> +				case ACTION_CREATE:
> +					return namespace_reconfig(region, ndns);
> +				}
> +				if (rc == 0)
> +					success++;
> +			}
> +		}
> +	}
> +
> +	rc = success;
> + done:
> +	ndctl_unref(ctx);
> +	return rc;
> +}
> +
> +int cmd_disable_namespace(int argc, const char **argv)
> +{
> +	char *xable_usage = "ndctl disable-namespace <namespace> [<options>]";
> +	const char *namespace = parse_namespace_options(argc, argv,
> +			ACTION_DISABLE, base_options, xable_usage);
> +	int disabled = do_xaction_namespace(namespace, ACTION_DISABLE);
> +
> +	if (disabled < 0) {
> +		fprintf(stderr, "error disabling namespaces: %s\n",
> +				strerror(-disabled));
> +		return disabled;
> +	} else if (disabled == 0) {
> +		fprintf(stderr, "disabled 0 namespaces\n");
> +		return -ENXIO;
> +	} else {
> +		fprintf(stderr, "disabled %d namespace%s\n", disabled,
> +				disabled > 1 ? "s" : "");
> +		return disabled;
> +	}
> +}
> +
> +int cmd_enable_namespace(int argc, const char **argv)
> +{
> +	char *xable_usage = "ndctl enable-namespace <namespace> [<options>]";
> +	const char *namespace = parse_namespace_options(argc, argv,
> +			ACTION_ENABLE, base_options, xable_usage);
> +	int enabled = do_xaction_namespace(namespace, ACTION_ENABLE);
> +
> +	if (enabled < 0) {
> +		fprintf(stderr, "error enabling namespaces: %s\n",
> +				strerror(-enabled));
> +		return enabled;
> +	} else if (enabled == 0) {
> +		fprintf(stderr, "enabled 0 namespaces\n");
> +		return -ENXIO;
> +	} else {
> +		fprintf(stderr, "enabled %d namespace%s\n", enabled,
> +				enabled > 1 ? "s" : "");
> +		return enabled;
> +	}
> +}
> +
> +int cmd_create_namespace(int argc, const char **argv)
> +{
> +	char *xable_usage = "ndctl create-namespace [<options>]";
> +	const char *namespace = parse_namespace_options(argc, argv,
> +			ACTION_CREATE, create_options, xable_usage);
> +	int created = do_xaction_namespace(namespace, ACTION_CREATE);
> +
> +	if (created < 0)
> +		fprintf(stderr, "failed to %s namespace\n", namespace
> +				? "reconfigure" : "create");
> +	return created;
> +}
> diff --git a/builtin.h b/builtin.h
> index ed0b7e497a43..27ff248a6d84 100644
> --- a/builtin.h
> +++ b/builtin.h
> @@ -10,6 +10,7 @@ struct cmd_struct {
>  
>  int cmd_create_nfit(int argc, const char **argv);
>  int cmd_enable_namespace(int argc, const char **argv);
> +int cmd_create_namespace(int argc, const char **argv);
>  int cmd_disable_namespace(int argc, const char **argv);
>  int cmd_enable_region(int argc, const char **argv);
>  int cmd_disable_region(int argc, const char **argv);
> @@ -21,6 +22,4 @@ int cmd_test(int argc, const char **argv);
>  #ifdef ENABLE_DESTRUCTIVE
>  int cmd_bat(int argc, const char **argv);
>  #endif
> -
>  #endif /* _NDCTL_BUILTIN_H_ */
> -
> diff --git a/ndctl.c b/ndctl.c
> index f4e2746f85e3..0c117d7fbd2f 100644
> --- a/ndctl.c
> +++ b/ndctl.c
> @@ -25,6 +25,7 @@ static struct cmd_struct commands[] = {
>  	{ "version", cmd_version },
>  	{ "create-nfit", cmd_create_nfit },
>  	{ "enable-namespace", cmd_enable_namespace },
> +	{ "create-namespace", cmd_create_namespace },
>  	{ "disable-namespace", cmd_disable_namespace },
>  	{ "enable-region", cmd_enable_region },
>  	{ "disable-region", cmd_disable_region },
> diff --git a/util/size.c b/util/size.c
> new file mode 100644
> index 000000000000..1ca2dcff5e28
> --- /dev/null
> +++ b/util/size.c
> @@ -0,0 +1,42 @@
> +#include <stdlib.h>
> +#include <limits.h>
> +#include <util/size.h>
> +
> +unsigned long long parse_size64(const char *str)
> +{
> +	unsigned long long val, check;
> +	char *end;
> +
> +	val = strtoull(str, &end, 0);
> +	if (val == ULLONG_MAX)
> +		return val;
> +	check = val;
> +	switch (*end) {
> +		case 'k':
> +		case 'K':
> +			val *= SZ_1K;
> +			end++;
> +			break;
> +		case 'm':
> +		case 'M':
> +			val *= SZ_1M;
> +			end++;
> +			break;
> +		case 'g':
> +		case 'G':
> +			val *= SZ_1G;
> +			end++;
> +			break;
> +		case 't':
> +		case 'T':
> +			val *= SZ_1T;
> +			end++;
> +			break;
> +		default:
> +			break;
> +	}
> +
> +	if (val < check || *end != '\0')
> +		val = ULLONG_MAX;
> +	return val;
> +}
> diff --git a/util/size.h b/util/size.h
> new file mode 100644
> index 000000000000..64c146480ef0
> --- /dev/null
> +++ b/util/size.h
> @@ -0,0 +1,12 @@
> +#ifndef _NDCTL_SIZE_H_
> +#define _NDCTL_SIZE_H_
> +
> +#define SZ_1K     0x00000400
> +#define SZ_1M     0x00100000
> +#define SZ_2M     0x00200000
> +#define SZ_1G     0x40000000
> +#define SZ_1T 0x10000000000ULL
> +
> +unsigned long long parse_size64(const char *str);
> +
> +#endif /* _NDCTL_SIZE_H_ */
Dan Williams Jan. 29, 2016, 12:38 a.m. UTC | #2
On Thu, Jan 28, 2016 at 3:39 PM, Jeff Moyer <jmoyer@redhat.com> wrote:
> Dan Williams <dan.j.williams@intel.com> writes:
>
>> A helper utility for instantiating a namespace.  With no arguments it
>> defaults to a maximally sized pmem namespace in memory mode.  The search
>> for available capacity can be constrained by bus, region, and/or
>> namespace type using the same mechanism to filter namespaces as 'ndctl
>> enable-namespace' and 'ndctl disable-namespace'.
>>
>> See the include man page for more details.
>>
>> Cc: Jeff Moyer <jmoyer@redhat.com>
>> [jmoyer: null dereference fix]
>
> Hi, Dan,
>
> If that's the only fix since last I saw this patch, then it's still got
> bugs.  ;-)

Oh yes, there were indeed a pile more.  I neglected to comment on them
in this update.

> Sorry I can't test right this second, but I'll dedicate
> some time to it tomorrow.  I'm pretty sure I hit another null pointer
> dereference.  Did you end up writing unit tests for this?

Yes, see patch 13:

https://lists.01.org/pipermail/linux-nvdimm/2016-January/004215.html
diff mbox

Patch

diff --git a/Documentation/Makefile.am b/Documentation/Makefile.am
index bd43714d5215..51d186505b44 100644
--- a/Documentation/Makefile.am
+++ b/Documentation/Makefile.am
@@ -4,7 +4,8 @@  man1_MANS = \
 	ndctl-enable-region.1 \
 	ndctl-disable-region.1 \
 	ndctl-enable-namespace.1 \
-	ndctl-disable-namespace.1
+	ndctl-disable-namespace.1 \
+	ndctl-create-namespace.1
 
 XML_DEPS = \
 	$(top_srcdir)/version.m4 \
diff --git a/Documentation/ndctl-create-namespace.txt b/Documentation/ndctl-create-namespace.txt
new file mode 100644
index 000000000000..3a9f911c231c
--- /dev/null
+++ b/Documentation/ndctl-create-namespace.txt
@@ -0,0 +1,127 @@ 
+ndctl-create-namespace(1)
+=========================
+
+NAME
+----
+ndctl-create-namespace - provision or reconfigure a namespace
+
+SYNOPSIS
+--------
+[verse]
+'ndctl create-namespace' [<options>]
+
+include::namespace-description.txt[]
+
+EXAMPLES
+--------
+
+Create a maximally sized pmem namespace in 'memory' mode
+[verse]
+ndctl create-namespace
+
+Convert namespace0.0 to 'safe' mode
+[verse]
+ndctl create-namespace -f -e namespace0.0 --mode=safe
+
+OPTIONS
+-------
+-t::
+--type=::
+	Create a 'pmem' or 'blk' namespace (subject to available
+	capacity).  A pmem namespace supports the DAX (direct access)
+	capability to linkndctl:mmap[2] persistent memory directly into
+	a process address space.  A blk namespace access persistent
+	memory through a block-window-aperture.  Compared to pmem it
+	supports a traditional storage error model (EIO on error rather
+	than a cpu exception on a bad memory access), but it does not
+	support DAX.
+
+-m::
+--mode=::
+	- "safe": persistent memory, given that it is byte addressable,
+	  does not support sector atomicity.  The problematic aspect of
+	  sector tearing is that most applications do not know they have
+	  a atomic sector update dependency.  At least a disk rarely
+	  ever tears sectors and if it does it almost certainly returns
+	  a checksum error on access.  Persistent memory devices will
+	  always tear and always silently.  Until an application is
+	  audited to be robust in the presence of sector-tearing "safe"
+	  mode is recommended.  This imposes some performance overhead
+	  and disables the DAX capability.
+
+	- "memory": A pmem namespace in this mode supports direct I/O
+	  to/from DAX mappings.  Depending on the kernel this mode may
+	  come at the cost of allocating per-pmem-page metadata.  If that
+	  allocation is required the capacity can be allocated from
+	  "System RAM" or from a reserved portion of pmem (see the --map=
+	  option).
+
+	- "raw": expose the namespace capacity directly with some
+	  limitations.  Neither a raw pmem namepace nor raw blk namespace
+	  support sector atomicity by default (see "safe" mode below).  A
+	  raw pmem namespace may have limited support for passing a DAX
+	  mapping to other syscalls.  I.e. direct-I/O to/from a DAX buffer
+	  may fail for a pmem namespace in raw mode.
+
+-s::
+--size=::
+	For NVDIMM devices that support namespace labels, set the
+	namespace size.  Otherwise it defaults to the maximum size
+	specified by platform firmware.
+
+-e::
+--reconfig=::
+	Reconfigure an existing namespace (change the mode, sector size,
+	etc...).  All namespace parameters, save uuid, default to the
+	current attributes of the specified namespace.  The namespace is
+	then re-created with the specified modifications.  The uuid is
+	refreshed to a new value by default whenever the data layout of
+	a namespace is changed, see --uuid= to set a specific uuid.
+
+-u::
+--uuid=::
+	This option is not recommended as a new uuid should be generated
+	every time a namespace is (re-)created.  For recovery scenarios
+	however the uuid may be specified.
+
+-n::
+--name=::
+	For NVDIMM devices that support namespace labels,
+	specify a human friendly name for a namespace.  This name is
+	available as device attribute for use in udev rules or
+	elsewhere.
+
+-l::
+--sector-size::
+	Specify the logical sector size (LBA size) of the block storage
+	device associated with a namespace.
+
+-M::
+--map=::
+	A pmem namespace in "memory" mode may require allocation
+	of per-page metadata.  The allocation can be drawn from either:
+	- "mem": typical system memory
+	- "dev": persistent memory reserved from the namespace
+
+-f::
+--force::
+	Unless this option is specified a 'reconfigure
+	namespace' operation will fail if the namespace is presently
+	active.  Specifying --force causes the namespace to be disabled
+	before reconfiguring.
+
+-v::
+--verbose::
+	Emit debug messages for the namespace creation process
+
+-r::
+--region=::
+include::xable-region-options.txt[]
+
+SEE ALSO
+--------
+linkndctl:ndctl-zero-labels[1],
+linkndctl:ndctl-disable-namespace[1],
+linkndctl:ndctl-enable-namespace[1],
+http://pmem.io/documents/NVDIMM_Namespace_Spec.pdf[NVDIMM Namespace
+Specification]
diff --git a/Documentation/ndctl.txt b/Documentation/ndctl.txt
index 87d2fbfcbcf9..13a0e36627f3 100644
--- a/Documentation/ndctl.txt
+++ b/Documentation/ndctl.txt
@@ -32,7 +32,7 @@  namspaces) associated with an NVDIMM bus.
 
 SEE ALSO
 --------
-linkndctl:ndctl-zero-labels[1],
+linkndctl:ndctl-create-namespace[1],
 linkndctl:ndctl-enable-region[1],
 linkndctl:ndctl-disable-region[1],
 linkndctl:ndctl-enable-namespace[1],
diff --git a/Makefile.am b/Makefile.am
index ffbbd044050f..baae4d0c1ee4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -64,7 +64,7 @@  bin_PROGRAMS = ndctl
 
 ndctl_SOURCES = ndctl.c \
 		builtin-create-nfit.c \
-		builtin-xable-namespace.c \
+		builtin-xaction-namespace.c \
 		builtin-xable-region.c \
 		builtin-test.c \
 		builtin-help.c \
@@ -72,8 +72,8 @@  ndctl_SOURCES = ndctl.c \
 		util/parse-options.c \
 		util/parse-options.h \
 		util/usage.c \
+		util/size.c \
 		util/strbuf.c \
-		util/strbuf.h \
 		util/wrapper.c
 
 if ENABLE_TEST
diff --git a/builtin-create-nfit.c b/builtin-create-nfit.c
index 6884b28db340..fad6b9a4b65d 100644
--- a/builtin-create-nfit.c
+++ b/builtin-create-nfit.c
@@ -8,6 +8,7 @@ 
 #include <sys/stat.h>
 #include <ccan/list/list.h>
 #include <util/parse-options.h>
+#include <util/size.h>
 
 #include <nfit.h>
 
@@ -20,50 +21,6 @@  struct spa {
 	unsigned long long size, offset;
 };
 
-#define SZ_1K     0x00000400
-#define SZ_1M     0x00100000
-#define SZ_1G     0x40000000
-#define SZ_1T 0x10000000000ULL
-
-static unsigned long long parse_size64(const char *str)
-{
-	unsigned long long val, check;
-	char *end;
-
-	val = strtoull(str, &end, 0);
-	if (val == ULLONG_MAX)
-		return val;
-	check = val;
-	switch (*end) {
-	case 'k':
-	case 'K':
-		val *= SZ_1K;
-		end++;
-		break;
-	case 'm':
-	case 'M':
-		val *= SZ_1M;
-		end++;
-		break;
-	case 'g':
-	case 'G':
-		val *= SZ_1G;
-		end++;
-		break;
-	case 't':
-	case 'T':
-		val *= SZ_1T;
-		end++;
-		break;
-	default:
-		break;
-	}
-
-	if (val < check || *end != '\0')
-		val = ULLONG_MAX;
-	return val;
-}
-
 static int parse_add_spa(const struct option *option, const char *__arg, int unset)
 {
 	struct spa *s = calloc(1, sizeof(struct spa));
diff --git a/builtin-xable-namespace.c b/builtin-xable-namespace.c
deleted file mode 100644
index 13a08633487a..000000000000
--- a/builtin-xable-namespace.c
+++ /dev/null
@@ -1,153 +0,0 @@ 
-#include <stdio.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <limits.h>
-#include <util/parse-options.h>
-#include <ndctl/libndctl.h>
-
-static const char *namespace_bus;
-static const char *namespace_region;
-
-static const struct option namespace_options[] = {
-	OPT_STRING('b', "bus", &namespace_bus, "bus-id",
-			"<namespace> must be on a bus with an id/provider of <bus-id>"),
-	OPT_STRING('r', "region", &namespace_region, "region-id",
-			"<namespace> must be a child of a region with an id/name of <region-id>"),
-	OPT_END(),
-};
-
-static const char *parse_namespace_options(int argc, const char **argv,
-		char *xable_usage)
-{
-	const char * const u[] = {
-		xable_usage,
-		NULL
-	};
-	int i;
-
-        argc = parse_options(argc, argv, namespace_options, u, 0);
-
-	if (argc == 0)
-		error("specify a namespace to delete, or \"all\"\n");
-	for (i = 1; i < argc; i++)
-		error("unknown extra parameter \"%s\"\n", argv[i]);
-	if (argc == 0 || argc > 1) {
-		usage_with_options(u, namespace_options);
-		return NULL; /* we won't return from usage_with_options() */
-	}
-	return argv[0];
-}
-
-static int do_xable_namespace(const char *namespace,
-		int (*xable_fn)(struct ndctl_namespace *))
-{
-	unsigned long bus_id = ULONG_MAX, region_id = ULONG_MAX, id;
-	const char *provider, *region_name, *ndns_name;
-	int rc = -ENXIO, success = 0;
-	struct ndctl_namespace *ndns;
-	struct ndctl_region *region;
-	struct ndctl_ctx *ctx;
-	struct ndctl_bus *bus;
-
-	if (!namespace)
-		goto out;
-
-	rc = ndctl_new(&ctx);
-	if (rc < 0)
-		goto out;
-
-	if (namespace_bus) {
-		char *end = NULL;
-
-		bus_id = strtoul(namespace_bus, &end, 0);
-		if (end)
-			bus_id = ULONG_MAX;
-	}
-
-	if (namespace_region) {
-		char *end = NULL;
-
-		region_id = strtoul(namespace_region, &end, 0);
-		if (end)
-			region_id = ULONG_MAX;
-	}
-
-        ndctl_bus_foreach(ctx, bus) {
-		provider = ndctl_bus_get_provider(bus);
-		id = ndctl_bus_get_id(bus);
-
-		if (bus_id < ULONG_MAX && bus_id != id)
-			continue;
-		else if (bus_id == ULONG_MAX && namespace_bus
-				&& strcmp(namespace_bus, provider) != 0)
-			continue;
-
-		ndctl_region_foreach(bus, region) {
-			region_name = ndctl_region_get_devname(region);
-			id = ndctl_region_get_id(region);
-
-			if (region_id < ULONG_MAX && region_id != id)
-				continue;
-			else if (region_id == ULONG_MAX && namespace_region
-					&& strcmp(namespace_region, region_name) != 0)
-				continue;
-			ndctl_namespace_foreach(region, ndns) {
-				ndns_name = ndctl_namespace_get_devname(ndns);
-
-				if (strcmp(namespace, "all") != 0
-						&& strcmp(namespace, ndns_name) != 0)
-					continue;
-				if (xable_fn(ndns) == 0)
-					success++;
-			}
-		}
-	}
-
-	rc = success;
-	ndctl_unref(ctx);
- out:
-	namespace_bus = NULL;
-	namespace_region = NULL;
-	return rc;
-}
-
-int cmd_disable_namespace(int argc, const char **argv)
-{
-	char *xable_usage = "ndctl disable-namespace <namespace> [<options>]";
-	const char *namespace = parse_namespace_options(argc, argv, xable_usage);
-	int disabled = do_xable_namespace(namespace, ndctl_namespace_disable_invalidate);
-
-	if (disabled < 0) {
-		fprintf(stderr, "error disabling namespaces: %s\n",
-				strerror(-disabled));
-		return disabled;
-	} else if (disabled == 0) {
-		fprintf(stderr, "disabled 0 namespaces\n");
-		return -ENXIO;
-	} else {
-		fprintf(stderr, "disabled %d namespace%s\n", disabled,
-				disabled > 1 ? "s" : "");
-		return disabled;
-	}
-}
-
-int cmd_enable_namespace(int argc, const char **argv)
-{
-	char *xable_usage = "ndctl enable-namespace <namespace> [<options>]";
-	const char *namespace = parse_namespace_options(argc, argv, xable_usage);
-	int enabled = do_xable_namespace(namespace, ndctl_namespace_enable);
-
-	if (enabled < 0) {
-		fprintf(stderr, "error enabling namespaces: %s\n",
-				strerror(-enabled));
-		return enabled;
-	} else if (enabled == 0) {
-		fprintf(stderr, "enabled 0 namespaces\n");
-		return -ENXIO;
-	} else {
-		fprintf(stderr, "enabled %d namespace%s\n", enabled,
-				enabled > 1 ? "s" : "");
-		return enabled;
-	}
-}
diff --git a/builtin-xaction-namespace.c b/builtin-xaction-namespace.c
new file mode 100644
index 000000000000..fdf20dfc01af
--- /dev/null
+++ b/builtin-xaction-namespace.c
@@ -0,0 +1,693 @@ 
+/*
+ * Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <uuid/uuid.h>
+#include <sys/types.h>
+#include <util/size.h>
+#include <ndctl/libndctl.h>
+#include <util/parse-options.h>
+#include <ccan/array_size/array_size.h>
+
+#ifdef HAVE_NDCTL_H
+#include <linux/ndctl.h>
+#else
+#include <ndctl.h>
+#endif
+
+static bool verbose;
+static bool force;
+static struct parameters {
+	const char *bus;
+	const char *map;
+	const char *type;
+	const char *uuid;
+	const char *name;
+	const char *size;
+	const char *mode;
+	const char *region;
+	const char *reconfig;
+	const char *sector_size;
+} param;
+
+#define NSLABEL_NAME_LEN 64
+struct parsed_parameters {
+	enum ndctl_pfn_loc loc;
+	uuid_t uuid;
+	char name[NSLABEL_NAME_LEN];
+	enum ndctl_namespace_mode mode;
+	unsigned long long size;
+	unsigned long sector_size;
+};
+
+#define debug(fmt, ...) \
+	if (verbose) { \
+		fprintf(stderr, "%s:%d: " fmt, __func__, __LINE__, ##__VA_ARGS__); \
+	} else { \
+		do { } while (0); \
+	}
+
+#define BASE_OPTIONS() \
+OPT_STRING('b', "bus", &param.bus, "bus-id", \
+	"limit namespace to a bus with an id or provider of <bus-id>"), \
+OPT_STRING('r', "region", &param.region, "region-id", \
+	"limit namespace to a region with an id or name of <region-id>")
+
+#define CREATE_OPTIONS() \
+OPT_STRING('e', "reconfig", &param.reconfig, "reconfig namespace", \
+	"reconfigure existing namespace"), \
+OPT_STRING('u', "uuid", &param.uuid, "uuid", \
+	"specify the uuid for the namespace (default: autogenerate)"), \
+OPT_STRING('n', "name", &param.name, "name", \
+	"specify an optional free form name for the namespace"), \
+OPT_STRING('s', "size", &param.size, "size", \
+	"specify the namespace size in bytes (default: available capacity)"), \
+OPT_STRING('m', "mode", &param.mode, "operation-mode", \
+	"specify a mode for the namespace, 'safe', 'memory', or 'raw'"), \
+OPT_STRING('M', "map", &param.map, "memmap-location", \
+	"specify 'mem' or 'dev' for the location of the memmap"), \
+OPT_STRING('l', "sector-size", &param.sector_size, "lba-size", \
+	"specify the logical sector size in bytes"), \
+OPT_STRING('t', "type", &param.type, "type", \
+	"specify the type of namespace to create 'pmem' or 'blk'"), \
+OPT_BOOLEAN('f', "force", &force, "reconfigure namespace even if currently active"), \
+OPT_BOOLEAN('v', "verbose", &verbose, "emit extra debug messages to stderr")
+
+static const struct option base_options[] = {
+	BASE_OPTIONS(),
+	OPT_END(),
+};
+
+static const struct option create_options[] = {
+	BASE_OPTIONS(),
+	CREATE_OPTIONS(),
+	OPT_END(),
+};
+
+enum namespace_action {
+	ACTION_ENABLE,
+	ACTION_DISABLE,
+	ACTION_CREATE,
+};
+
+/*
+ * parse_namespace_options - basic parsing sanity checks before we start
+ * looking at actual namespace devices and available resources.
+ */
+static const char *parse_namespace_options(int argc, const char **argv,
+		enum namespace_action mode, const struct option *options,
+		char *xable_usage)
+{
+	const char * const u[] = {
+		xable_usage,
+		NULL
+	};
+	int i, rc = 0;
+
+        argc = parse_options(argc, argv, options, u, 0);
+
+	if (param.type) {
+		if (strcmp(param.type, "pmem") == 0)
+			/* pass */;
+		else if (strcmp(param.type, "blk") == 0)
+			/* pass */;
+		else {
+			error("invalid type '%s', must be either 'pmem' or 'blk'\n",
+				param.type);
+			rc = -EINVAL;
+		}
+	} else if (!param.reconfig)
+		param.type = "pmem";
+
+	if (param.mode) {
+		if (strcmp(param.mode, "safe") == 0)
+			/* pass */;
+		else if (strcmp(param.mode, "sector") == 0)
+		      param.mode = "safe"; /* pass */
+		else if (strcmp(param.mode, "memory") == 0)
+		      /* pass */;
+		else if (strcmp(param.mode, "raw") == 0)
+		      /* pass */;
+		else {
+			error("invalid mode '%s'\n", param.mode);
+			rc = -EINVAL;
+		}
+	} else if (!param.reconfig) {
+		if (strcmp(param.type, "pmem") == 0)
+			param.mode = "memory";
+		else
+			param.mode = "safe";
+	}
+
+	if (param.map) {
+		if (strcmp(param.map, "mem") == 0)
+			/* pass */;
+		else if (strcmp(param.map, "dev") == 0)
+			/* pass */;
+		else {
+			error("invalid map location '%s'\n", param.map);
+			rc = -EINVAL;
+		}
+
+		if (!param.reconfig && strcmp(param.mode, "memory") != 0) {
+			error("--map only valid for a memory mode pmem namespace\n");
+			rc = -EINVAL;
+		}
+	} else if (!param.reconfig)
+		param.map = "dev";
+
+	/* check for incompatible mode and type combinations */
+	if (param.type && param.mode && strcmp(param.type, "blk") == 0
+			&& strcmp(param.mode, "memory") == 0) {
+		error("only 'pmem' namespaces can be placed into 'memory' mode\n");
+		rc = -ENXIO;
+	}
+
+	if (param.size && parse_size64(param.size) == ULLONG_MAX) {
+		error("failed to parse namespace size '%s'\n",
+				param.size);
+		rc = -EINVAL;
+	}
+
+	if (param.uuid) {
+		uuid_t uuid;
+
+		if (uuid_parse(param.uuid, uuid)) {
+			error("failed to parse uuid: '%s'\n", param.uuid);
+			rc = -EINVAL;
+		}
+	}
+
+	if (param.sector_size) {
+		if (parse_size64(param.sector_size) == ULLONG_MAX) {
+			error("invalid sector size: %s\n", param.sector_size);
+			rc = -EINVAL;
+		}
+
+		if (param.type && param.mode && strcmp(param.type, "pmem") == 0
+				&& strcmp(param.mode, "safe") != 0) {
+			error("'pmem' namespaces do not support setting 'sector size'\n");
+			rc = -EINVAL;
+		}
+	} else if (!param.reconfig)
+		param.sector_size = "4096";
+
+	if (argc == 0 && mode != ACTION_CREATE) {
+		error("specify a namespace to %s, or \"all\"\n",
+				mode == ACTION_ENABLE ? "enable" : "disable");
+		rc = -EINVAL;
+	}
+	for (i = mode == ACTION_CREATE ? 0 : 1; i < argc; i++) {
+		error("unknown extra parameter \"%s\"\n", argv[i]);
+		rc = -EINVAL;
+	}
+
+	if (rc) {
+		usage_with_options(u, options);
+		return NULL; /* we won't return from usage_with_options() */
+	}
+
+	return mode == ACTION_CREATE ? param.reconfig : argv[0];
+}
+
+#define try(prefix, op, dev, p) \
+do { \
+	int __rc = prefix##_##op(dev, p); \
+	if (__rc) { \
+		debug("%s: " #op " failed: %d\n", \
+				prefix##_get_devname(dev), __rc); \
+		return __rc; \
+	} \
+} while (0)
+
+static int setup_namespace(struct ndctl_region *region,
+		struct ndctl_namespace *ndns, struct parsed_parameters *p)
+{
+	uuid_t uuid;
+	int rc;
+
+	if (ndctl_namespace_get_type(ndns) != ND_DEVICE_NAMESPACE_IO) {
+		try(ndctl_namespace, set_uuid, ndns, p->uuid);
+		try(ndctl_namespace, set_alt_name, ndns, p->name);
+		try(ndctl_namespace, set_size, ndns, p->size);
+	}
+
+	if (ndctl_namespace_get_type(ndns) == ND_DEVICE_NAMESPACE_BLK)
+		try(ndctl_namespace, set_sector_size, ndns, p->sector_size);
+
+	uuid_generate(uuid);
+	if (ndctl_namespace_get_mode(ndns) != NDCTL_NS_MODE_MEMORY
+			&& p->mode == NDCTL_NS_MODE_MEMORY) {
+		struct ndctl_pfn *pfn = ndctl_region_get_pfn_seed(region);
+
+		try(ndctl_pfn, set_uuid, pfn, uuid);
+		try(ndctl_pfn, set_location, pfn, p->loc);
+		try(ndctl_pfn, set_align, pfn, SZ_2M);
+		try(ndctl_pfn, set_namespace, pfn, ndns);
+		rc = ndctl_pfn_enable(pfn);
+	} else if (p->mode == NDCTL_NS_MODE_SAFE) {
+		struct ndctl_btt *btt = ndctl_region_get_btt_seed(region);
+
+		try(ndctl_btt, set_uuid, btt, uuid);
+		try(ndctl_btt, set_sector_size, btt, p->sector_size);
+		try(ndctl_btt, set_namespace, btt, ndns);
+		rc = ndctl_btt_enable(btt);
+	} else
+		rc = ndctl_namespace_enable(ndns);
+
+	if (rc) {
+		error("%s: failed to enable\n",
+				ndctl_namespace_get_devname(ndns));
+	}
+	return rc;
+}
+
+static int is_namespace_active(struct ndctl_namespace *ndns)
+{
+	return ndns && (ndctl_namespace_is_enabled(ndns)
+		|| ndctl_namespace_get_pfn(ndns)
+		|| ndctl_namespace_get_btt(ndns));
+}
+
+/*
+ * validate_namespace_options - init parameters for setup_namespace
+ * @ndns: specified when we are reconfiguring, NULL otherwise
+ * @p: parameters to fill
+ *
+ * parse_namespace_options() will have already done basic verification
+ * of the parameters and set defaults in the !reconfigure case.  When
+ * reconfiguring fill in any unset options with defaults from the
+ * namespace itself.
+ *
+ * Given that parse_namespace_options() runs before we have identified
+ * the target namespace we need to do basic sanity checks here for
+ * pmem-only attributes specified for blk namespace and vice versa.
+ */
+static int validate_namespace_options(struct ndctl_namespace *ndns,
+		struct parsed_parameters *p)
+{
+	int rc = 0;
+
+	memset(p, 0, sizeof(*p));
+
+	if (param.size)
+		p->size = parse_size64(param.size);
+	else if (ndns)
+		p->size = ndctl_namespace_get_size(ndns);
+
+	if (param.uuid) {
+		if (uuid_parse(param.uuid, p->uuid) != 0) {
+			debug("%s: invalid uuid\n", __func__);
+			return -EINVAL;
+		}
+	} else
+		uuid_generate(p->uuid);
+
+	if (param.name)
+		rc = snprintf(p->name, sizeof(p->name), "%s", param.name);
+	else if (ndns)
+		rc = snprintf(p->name, sizeof(p->name), "%s",
+				ndctl_namespace_get_alt_name(ndns));
+	if (rc >= (int) sizeof(p->name)) {
+		debug("%s: alt name overflow\n", __func__);
+		return -EINVAL;
+	}
+
+	if (param.mode) {
+		if (strcmp(param.mode, "memory") == 0)
+			p->mode = NDCTL_NS_MODE_MEMORY;
+		else if (strcmp(param.mode, "safe") == 0)
+			p->mode = NDCTL_NS_MODE_SAFE;
+		else
+			p->mode = NDCTL_NS_MODE_RAW;
+
+		if (ndns && ndctl_namespace_get_type(ndns)
+				== ND_DEVICE_NAMESPACE_BLK
+				&& p->mode == NDCTL_NS_MODE_MEMORY) {
+			debug("%s: blk namespace do not support memory mode\n",
+					ndctl_namespace_get_devname(ndns));
+				return -EINVAL;
+		}
+	} else if (ndns)
+		p->mode = ndctl_namespace_get_mode(ndns);
+
+	if (param.sector_size) {
+		p->sector_size = parse_size64(param.sector_size);
+
+		if (ndns && p->mode != NDCTL_NS_MODE_SAFE
+				&& ndctl_namespace_get_type(ndns)
+				== ND_DEVICE_NAMESPACE_PMEM) {
+			debug("%s: does not support sector_size modification\n",
+					ndctl_namespace_get_devname(ndns));
+				return -EINVAL;
+		}
+	} else if (ndns) {
+		struct ndctl_btt *btt = ndctl_namespace_get_btt(ndns);
+
+		if (btt)
+			p->sector_size = ndctl_btt_get_sector_size(btt);
+		else if (ndctl_namespace_get_type(ndns)
+				== ND_DEVICE_NAMESPACE_BLK)
+			p->sector_size = ndctl_namespace_get_sector_size(ndns);
+	}
+
+	if (param.map) {
+		if (strcmp(param.map, "mem"))
+			p->loc = NDCTL_PFN_LOC_RAM;
+		else
+			p->loc = NDCTL_PFN_LOC_PMEM;
+
+		if (ndns && p->mode != NDCTL_NS_MODE_MEMORY) {
+			debug("%s: --map= only valid for memory mode namespace\n",
+				ndctl_namespace_get_devname(ndns));
+			return -EINVAL;
+		}
+	} else if (p->mode == NDCTL_NS_MODE_MEMORY)
+		p->loc = NDCTL_PFN_LOC_PMEM;
+
+	return 0;
+}
+
+static int namespace_create(struct ndctl_region *region)
+{
+	const char *devname = ndctl_region_get_devname(region);
+	unsigned long long available;
+	struct ndctl_namespace *ndns;
+	struct parsed_parameters p;
+
+	if (validate_namespace_options(NULL, &p))
+		return -EINVAL;
+
+	if (ndctl_region_get_ro(region)) {
+		debug("%s: read-only, inelligible for namespace creation\n",
+			devname);
+		return -EAGAIN;
+	}
+
+	available = ndctl_region_get_available_size(region);
+	if (!available || p.size > available) {
+		debug("%s: insufficient capacity size: %llx avail: %llx\n",
+			devname, p.size, available);
+		return -EAGAIN;
+	}
+
+	if (p.size == 0)
+		p.size = available;
+
+	ndns = ndctl_region_get_namespace_seed(region);
+	if (is_namespace_active(ndns)) {
+		debug("%s: no %s namespace seed\n", devname,
+				ndns ? "idle" : "available");
+		return -ENODEV;
+	}
+
+	return setup_namespace(region, ndns, &p);
+}
+
+static int zero_info_block(struct ndctl_namespace *ndns)
+{
+	const char *devname = ndctl_namespace_get_devname(ndns);
+	int fd, rc = -ENXIO;
+	char path[50];
+	void *buf;
+
+	if (posix_memalign(&buf, 4096, 4096) != 0)
+		return -ENXIO;
+
+	ndctl_namespace_set_raw_mode(ndns, 1);
+	ndctl_namespace_enable(ndns);
+
+	sprintf(path, "/dev/%s", ndctl_namespace_get_block_device(ndns));
+	fd = open(path, O_RDWR|O_DIRECT|O_EXCL);
+	if (fd < 0) {
+		debug("%s: failed to open %s to zero info block\n",
+				devname, path);
+		goto out;
+	}
+
+	memset(buf, 0, 4096);
+	rc = pwrite(fd, buf, 4096, 4096);
+	if (rc < 4096) {
+		debug("%s: failed to zero info block %s\n",
+				devname, path);
+		rc = -ENXIO;
+	} else
+		rc = 0;
+	close(fd);
+ out:
+	ndctl_namespace_set_raw_mode(ndns, 0);
+	ndctl_namespace_disable_invalidate(ndns);
+	free(buf);
+	return rc;
+}
+
+static int namespace_reconfig(struct ndctl_region *region,
+		struct ndctl_namespace *ndns)
+{
+	const char *devname = ndctl_namespace_get_devname(ndns);
+	struct ndctl_pfn *pfn = ndctl_namespace_get_pfn(ndns);
+	struct ndctl_btt *btt = ndctl_namespace_get_btt(ndns);
+	struct parsed_parameters p;
+	const char *bdev = NULL;
+	char path[50];
+	int fd, rc;
+
+	if (validate_namespace_options(ndns, &p))
+		return -EINVAL;
+
+	if (ndctl_region_get_ro(region)) {
+		error("%s: read-only, re-configuration disabled\n",
+				devname);
+		return -ENXIO;
+	}
+
+	if (pfn && ndctl_pfn_is_enabled(pfn))
+		bdev = ndctl_pfn_get_block_device(pfn);
+	else if (btt && ndctl_btt_is_enabled(btt))
+		bdev = ndctl_btt_get_block_device(btt);
+	else if (ndctl_namespace_is_enabled(ndns))
+		bdev = ndctl_namespace_get_block_device(ndns);
+
+	if (bdev && !force) {
+		error("%s is active, specify --force for re-configuration\n",
+				devname);
+		return -EBUSY;
+	} else if (bdev) {
+		sprintf(path, "/dev/%s", bdev);
+		fd = open(path, O_RDWR|O_EXCL);
+		if (fd >= 0) {
+			/*
+			 * Got it, now block new mounts while we have it
+			 * pinned.
+			 */
+			ndctl_namespace_disable_invalidate(ndns);
+			close(fd);
+		} else {
+			/*
+			 * Yes, TOCTOU hole, but if you're racing namespace
+			 * creation you have other problems, and there's nothing
+			 * stopping the !bdev case from racing to mount an fs or
+			 * re-enabling the namepace.
+			 */
+			return -EBUSY;
+		}
+	}
+
+	if (pfn || btt) {
+		rc = zero_info_block(ndns);
+		if (rc)
+			return rc;
+	}
+
+	rc = ndctl_namespace_delete(ndns);
+	if (rc) {
+		error("%s: failed to reclaim\n", devname);
+		return rc;
+	}
+
+	ndns = ndctl_region_get_namespace_seed(region);
+	if (is_namespace_active(ndns)) {
+		debug("%s: no %s namespace seed\n", devname,
+				ndns ? "idle" : "available");
+		return -ENODEV;
+	}
+
+	return setup_namespace(region, ndns, &p);
+}
+
+static int do_xaction_namespace(const char *namespace,
+		enum namespace_action action)
+{
+	unsigned long bus_id = ULONG_MAX, region_id = ULONG_MAX, id;
+	const char *provider, *region_name, *ndns_name;
+	int rc = -ENXIO, success = 0;
+	struct ndctl_namespace *ndns;
+	struct ndctl_region *region;
+	struct ndctl_ctx *ctx;
+	struct ndctl_bus *bus;
+
+	if (!namespace && action != ACTION_CREATE)
+		return rc;
+
+	rc = ndctl_new(&ctx);
+	if (rc < 0)
+		return rc;
+
+	if (verbose)
+		ndctl_set_log_priority(ctx, LOG_DEBUG);
+
+	if (param.bus) {
+		char *end = NULL;
+
+		bus_id = strtoul(param.bus, &end, 0);
+		if (end)
+			bus_id = ULONG_MAX;
+	}
+
+	if (param.region) {
+		char *end = NULL;
+
+		region_id = strtoul(param.region, &end, 0);
+		if (end)
+			region_id = ULONG_MAX;
+	}
+
+        ndctl_bus_foreach(ctx, bus) {
+		provider = ndctl_bus_get_provider(bus);
+		id = ndctl_bus_get_id(bus);
+
+		if (bus_id < ULONG_MAX && bus_id != id)
+			continue;
+		else if (bus_id == ULONG_MAX && param.bus
+				&& strcmp(param.bus, provider) != 0)
+			continue;
+
+		ndctl_region_foreach(bus, region) {
+			region_name = ndctl_region_get_devname(region);
+			id = ndctl_region_get_id(region);
+
+			if (region_id < ULONG_MAX && region_id != id)
+				continue;
+			else if (region_id == ULONG_MAX && param.region
+					&& strcmp(param.region, region_name) != 0)
+				continue;
+
+			if (param.type) {
+				if (strcmp(param.type, "pmem") == 0
+						&& ndctl_region_get_type(region)
+						== ND_DEVICE_REGION_PMEM)
+					/* pass */;
+				else if (strcmp(param.type, "blk") == 0
+						&& ndctl_region_get_type(region)
+						== ND_DEVICE_REGION_BLK)
+					/* pass */;
+				else
+					continue;
+			}
+
+			if (action == ACTION_CREATE && !namespace) {
+				rc = namespace_create(region);
+				if (rc == -EAGAIN) {
+					rc = 0;
+					continue;
+				}
+				goto done;
+			}
+			ndctl_namespace_foreach(region, ndns) {
+				ndns_name = ndctl_namespace_get_devname(ndns);
+
+				if (strcmp(namespace, "all") != 0
+						&& strcmp(namespace, ndns_name) != 0)
+					continue;
+				switch (action) {
+				case ACTION_DISABLE:
+					rc = ndctl_namespace_disable_invalidate(ndns);
+					break;
+				case ACTION_ENABLE:
+					rc = ndctl_namespace_enable(ndns);
+					break;
+				case ACTION_CREATE:
+					return namespace_reconfig(region, ndns);
+				}
+				if (rc == 0)
+					success++;
+			}
+		}
+	}
+
+	rc = success;
+ done:
+	ndctl_unref(ctx);
+	return rc;
+}
+
+int cmd_disable_namespace(int argc, const char **argv)
+{
+	char *xable_usage = "ndctl disable-namespace <namespace> [<options>]";
+	const char *namespace = parse_namespace_options(argc, argv,
+			ACTION_DISABLE, base_options, xable_usage);
+	int disabled = do_xaction_namespace(namespace, ACTION_DISABLE);
+
+	if (disabled < 0) {
+		fprintf(stderr, "error disabling namespaces: %s\n",
+				strerror(-disabled));
+		return disabled;
+	} else if (disabled == 0) {
+		fprintf(stderr, "disabled 0 namespaces\n");
+		return -ENXIO;
+	} else {
+		fprintf(stderr, "disabled %d namespace%s\n", disabled,
+				disabled > 1 ? "s" : "");
+		return disabled;
+	}
+}
+
+int cmd_enable_namespace(int argc, const char **argv)
+{
+	char *xable_usage = "ndctl enable-namespace <namespace> [<options>]";
+	const char *namespace = parse_namespace_options(argc, argv,
+			ACTION_ENABLE, base_options, xable_usage);
+	int enabled = do_xaction_namespace(namespace, ACTION_ENABLE);
+
+	if (enabled < 0) {
+		fprintf(stderr, "error enabling namespaces: %s\n",
+				strerror(-enabled));
+		return enabled;
+	} else if (enabled == 0) {
+		fprintf(stderr, "enabled 0 namespaces\n");
+		return -ENXIO;
+	} else {
+		fprintf(stderr, "enabled %d namespace%s\n", enabled,
+				enabled > 1 ? "s" : "");
+		return enabled;
+	}
+}
+
+int cmd_create_namespace(int argc, const char **argv)
+{
+	char *xable_usage = "ndctl create-namespace [<options>]";
+	const char *namespace = parse_namespace_options(argc, argv,
+			ACTION_CREATE, create_options, xable_usage);
+	int created = do_xaction_namespace(namespace, ACTION_CREATE);
+
+	if (created < 0)
+		fprintf(stderr, "failed to %s namespace\n", namespace
+				? "reconfigure" : "create");
+	return created;
+}
diff --git a/builtin.h b/builtin.h
index ed0b7e497a43..27ff248a6d84 100644
--- a/builtin.h
+++ b/builtin.h
@@ -10,6 +10,7 @@  struct cmd_struct {
 
 int cmd_create_nfit(int argc, const char **argv);
 int cmd_enable_namespace(int argc, const char **argv);
+int cmd_create_namespace(int argc, const char **argv);
 int cmd_disable_namespace(int argc, const char **argv);
 int cmd_enable_region(int argc, const char **argv);
 int cmd_disable_region(int argc, const char **argv);
@@ -21,6 +22,4 @@  int cmd_test(int argc, const char **argv);
 #ifdef ENABLE_DESTRUCTIVE
 int cmd_bat(int argc, const char **argv);
 #endif
-
 #endif /* _NDCTL_BUILTIN_H_ */
-
diff --git a/ndctl.c b/ndctl.c
index f4e2746f85e3..0c117d7fbd2f 100644
--- a/ndctl.c
+++ b/ndctl.c
@@ -25,6 +25,7 @@  static struct cmd_struct commands[] = {
 	{ "version", cmd_version },
 	{ "create-nfit", cmd_create_nfit },
 	{ "enable-namespace", cmd_enable_namespace },
+	{ "create-namespace", cmd_create_namespace },
 	{ "disable-namespace", cmd_disable_namespace },
 	{ "enable-region", cmd_enable_region },
 	{ "disable-region", cmd_disable_region },
diff --git a/util/size.c b/util/size.c
new file mode 100644
index 000000000000..1ca2dcff5e28
--- /dev/null
+++ b/util/size.c
@@ -0,0 +1,42 @@ 
+#include <stdlib.h>
+#include <limits.h>
+#include <util/size.h>
+
+unsigned long long parse_size64(const char *str)
+{
+	unsigned long long val, check;
+	char *end;
+
+	val = strtoull(str, &end, 0);
+	if (val == ULLONG_MAX)
+		return val;
+	check = val;
+	switch (*end) {
+		case 'k':
+		case 'K':
+			val *= SZ_1K;
+			end++;
+			break;
+		case 'm':
+		case 'M':
+			val *= SZ_1M;
+			end++;
+			break;
+		case 'g':
+		case 'G':
+			val *= SZ_1G;
+			end++;
+			break;
+		case 't':
+		case 'T':
+			val *= SZ_1T;
+			end++;
+			break;
+		default:
+			break;
+	}
+
+	if (val < check || *end != '\0')
+		val = ULLONG_MAX;
+	return val;
+}
diff --git a/util/size.h b/util/size.h
new file mode 100644
index 000000000000..64c146480ef0
--- /dev/null
+++ b/util/size.h
@@ -0,0 +1,12 @@ 
+#ifndef _NDCTL_SIZE_H_
+#define _NDCTL_SIZE_H_
+
+#define SZ_1K     0x00000400
+#define SZ_1M     0x00100000
+#define SZ_2M     0x00200000
+#define SZ_1G     0x40000000
+#define SZ_1T 0x10000000000ULL
+
+unsigned long long parse_size64(const char *str);
+
+#endif /* _NDCTL_SIZE_H_ */