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_ */
