diff mbox

[ndctl,3/4] ndctl: rework btt instances to consume namespaces instead of block_devices

Message ID 20150630003144.7510.85616.stgit@dwillia2-desk3.jf.intel.com (mailing list archive)
State Accepted
Commit 509873093da1
Headers show

Commit Message

Dan Williams June 30, 2015, 12:31 a.m. UTC
The kernel has made the btt device a child of a region, a peer of the
namespace devices in the region.  It has also arranged for btt devices
to be driven by the pmem driver or the nd_blk driver depending on which
namespace type is hosting the btt.  This has several implications:

1/ replace ndctl_btt_{get|set}_backing_dev() with
   ndctl_btt_{get|set}_namespace()

2/ add ndctl_namespace_get_btt() since there will now always be a 1:1
   relationship between a btt and a namespace

3/ read-only-policy management is now centrally managed via a region's
   "read_only" attribute.  Add ndctl_{get|set}_ro() apis.  Update the
   unit test accordingly.

4/ The kernel has removed the 'delete' property in favor of
   auto-removing / resetting a btt instance when its associated
   namespace is removed.  Previously a btt would be removed when its
   backing block device was removed.

5/ Enabling a namespace may instead trigger a btt device to get created
   and enabled (bound to the namespace driver).  The return value from
   ndctl_namespace_enable() now encodes this possibility with a positive
   return code indicating that the namespace is not enabled, but a
   hosted btt instance in that namespace is enabled.

6/ The behavior of probing for a btt instance on a namespace can be
   suppressed by setting the 'raw_mode' flag for the namespace.  The
   ndctl_namespace_{set,get}_raw_mode() apis are added for this purpose.

7/ BTT instances are now invalidated on region disable, so we need to
   track their 'generation' same as namesapces.

Acked-by: Vishal Verma <vishal.l.verma@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 lib/libndctl.c       |  377 ++++++++++++++++++++++++++++++++++----------------
 lib/libndctl.sym     |   13 +-
 lib/ndctl/libndctl.h |   23 ++-
 lib/test-libndctl.c  |  256 +++++++++++++++++++---------------
 ndctl.h              |    2 
 5 files changed, 425 insertions(+), 246 deletions(-)
diff mbox

Patch

diff --git a/lib/libndctl.c b/lib/libndctl.c
index 366ea4a013a3..86a3a365f1d4 100644
--- a/lib/libndctl.c
+++ b/lib/libndctl.c
@@ -91,12 +91,10 @@  struct ndctl_bus {
 	struct ndctl_ctx *ctx;
 	unsigned int id, major, minor, revision;
 	char *provider;
-	struct list_head btts;
 	struct list_head dimms;
 	struct list_head regions;
 	struct list_node list;
 	int dimms_init;
-	int btts_init;
 	int regions_init;
 	int has_nfit;
 	char *bus_path;
@@ -181,24 +179,27 @@  struct ndctl_mapping {
  * coordinating configuration with peer regions.
  *
  * When a region is disabled a client may have pending references to
- * namespaces.  After a disable event the client can
+ * namespaces and btts.  After a disable event the client can
  * ndctl_region_cleanup() to clean up invalid objects, or it can
  * specify the cleanup flag to ndctl_region_disable().
  */
 struct ndctl_region {
 	struct kmod_module *module;
 	struct ndctl_bus *bus;
-	int id, num_mappings, nstype, range_index;
+	int id, num_mappings, nstype, range_index, ro;
 	int mappings_init;
 	int namespaces_init;
+	int btts_init;
 	unsigned long long size;
 	char *region_path;
 	char *region_buf;
 	int buf_len;
 	int generation;
+	struct list_head btts;
 	struct list_head mappings;
 	struct list_head namespaces;
 	struct list_head stale_namespaces;
+	struct list_head stale_btts;
 	struct list_node list;
 	/**
 	 * struct ndctl_interleave_set - extra info for interleave sets
@@ -242,7 +243,7 @@  struct ndctl_namespace {
 	char *ndns_path;
 	char *ndns_buf;
 	char *bdev;
-	int type, id, buf_len;
+	int type, id, buf_len, raw_mode;
 	int generation;
 	unsigned long long size;
 	char *alt_name;
@@ -254,8 +255,8 @@  struct ndctl_namespace {
  * struct ndctl_btt - stacked block device provided sector atomicity
  * @module: kernel module (nd_btt)
  * @lbasize: sector size info
- * @bus: parent bus
- * @backing_dev: backing block device name
+ * @ndns: host namespace for the btt instance
+ * @region: parent region
  * @btt_path: btt devpath
  * @uuid: unique identifier for a btt instance
  * @btt_buf: space to print paths for bind/unbind operations
@@ -263,16 +264,16 @@  struct ndctl_namespace {
  */
 struct ndctl_btt {
 	struct kmod_module *module;
-	struct ndctl_bus *bus;
+	struct ndctl_region *region;
+	struct ndctl_namespace *ndns;
 	struct list_node list;
 	struct ndctl_lbasize lbasize;
-	char *backing_dev;
 	char *btt_path;
 	char *btt_buf;
 	char *bdev;
 	int buf_len;
 	uuid_t uuid;
-	int id;
+	int id, generation;
 };
 
 /**
@@ -505,12 +506,47 @@  static void free_namespace(struct ndctl_namespace *ndns, struct list_head *head)
 	free(ndns);
 }
 
-static void free_namespaces(struct ndctl_region *region, struct list_head *head)
+static void free_namespaces(struct ndctl_region *region)
+{
+	struct ndctl_namespace *ndns, *_n;
+
+	list_for_each_safe(&region->namespaces, ndns, _n, list)
+		free_namespace(ndns, &region->namespaces);
+}
+
+static void free_stale_namespaces(struct ndctl_region *region)
 {
 	struct ndctl_namespace *ndns, *_n;
 
-	list_for_each_safe(head, ndns, _n, list)
-		free_namespace(ndns, head);
+	list_for_each_safe(&region->stale_namespaces, ndns, _n, list)
+		free_namespace(ndns, &region->stale_namespaces);
+}
+
+static void free_btt(struct ndctl_btt *btt, struct list_head *head)
+{
+	if (head)
+		list_del_from(head, &btt->list);
+	kmod_module_unref(btt->module);
+	free(btt->lbasize.supported);
+	free(btt->btt_path);
+	free(btt->btt_buf);
+	free(btt);
+}
+
+static void free_btts(struct ndctl_region *region)
+{
+	struct ndctl_btt *btt, *_b;
+
+	list_for_each_safe(&region->btts, btt, _b, list)
+		free_btt(btt, &region->btts);
+}
+
+static void free_stale_btts(struct ndctl_region *region)
+{
+	struct ndctl_btt *btt, *_b;
+
+	list_for_each_safe(&region->stale_btts, btt, _b, list)
+		free_btt(btt, &region->stale_btts);
 }
 
 static void free_region(struct ndctl_region *region)
@@ -522,8 +558,10 @@  static void free_region(struct ndctl_region *region)
 		list_del_from(&region->mappings, &mapping->list);
 		free(mapping);
 	}
-	free_namespaces(region, &region->namespaces);
-	free_namespaces(region, &region->stale_namespaces);
+	free_btts(region);
+	free_stale_btts(region);
+	free_namespaces(region);
+	free_stale_namespaces(region);
 	list_del_from(&bus->regions, &region->list);
 	kmod_module_unref(region->module);
 	free(region->region_buf);
@@ -531,25 +569,8 @@  static void free_region(struct ndctl_region *region)
 	free(region);
 }
 
-#define BTT_DELETE 1
-#define BTT_SKIP_DELETE 0
-static void free_btt(struct ndctl_btt *btt, int delete)
-{
-	struct ndctl_bus *bus = btt->bus;
-
-	if (delete)
-		list_del_from(&bus->btts, &btt->list);
-	kmod_module_unref(btt->module);
-	free(btt->lbasize.supported);
-	free(btt->backing_dev);
-	free(btt->btt_path);
-	free(btt->btt_buf);
-	free(btt);
-}
-
 static void free_bus(struct ndctl_bus *bus)
 {
-	struct ndctl_btt *btt, *_b;
 	struct ndctl_dimm *dimm, *_d;
 	struct ndctl_region *region, *_r;
 
@@ -557,8 +578,6 @@  static void free_bus(struct ndctl_bus *bus)
 		list_del_from(&bus->dimms, &dimm->list);
 		free(dimm);
 	}
-	list_for_each_safe(&bus->btts, btt, _b, list)
-		free_btt(btt, BTT_DELETE);
 	list_for_each_safe(&bus->regions, region, _r, list)
 		free_region(region);
 	list_del_from(&bus->ctx->busses, &bus->list);
@@ -841,7 +860,6 @@  static int add_bus(void *parent, int id, const char *ctl_base)
 	bus = calloc(1, sizeof(*bus));
 	if (!bus)
 		goto err_bus;
-	list_head_init(&bus->btts);
 	list_head_init(&bus->dimms);
 	list_head_init(&bus->regions);
 	bus->ctx = ctx;
@@ -983,29 +1001,54 @@  NDCTL_EXPORT struct ndctl_bus *ndctl_bus_get_by_provider(struct ndctl_ctx *ctx,
 	return NULL;
 }
 
-NDCTL_EXPORT struct ndctl_btt *ndctl_bus_get_btt_seed(struct ndctl_bus *bus)
+NDCTL_EXPORT struct ndctl_btt *ndctl_region_get_btt_seed(struct ndctl_region *region)
 {
-	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
-	char *path = bus->bus_buf;
-	int len = bus->buf_len;
+	struct ndctl_ctx *ctx = ndctl_region_get_ctx(region);
+	char *path = region->region_buf;
+	int len = region->buf_len;
 	struct ndctl_btt *btt;
 	char buf[50];
 
-	if (snprintf(path, len, "%s/btt_seed", bus->bus_path) >= len) {
+	if (snprintf(path, len, "%s/btt_seed", region->region_path) >= len) {
 		err(ctx, "%s: buffer too small!\n",
-				ndctl_bus_get_devname(bus));
+				ndctl_region_get_devname(region));
 		return NULL;
 	}
 
 	if (sysfs_read_attr(ctx, path, buf) < 0)
 		return NULL;
 
-	ndctl_btt_foreach(bus, btt)
+	ndctl_btt_foreach(region, btt)
 		if (strcmp(buf, ndctl_btt_get_devname(btt)) == 0)
 			return btt;
 	return NULL;
 }
 
+NDCTL_EXPORT int ndctl_region_get_ro(struct ndctl_region *region)
+{
+	return region->ro;
+}
+
+NDCTL_EXPORT int ndctl_region_set_ro(struct ndctl_region *region, int ro)
+{
+	struct ndctl_ctx *ctx = ndctl_region_get_ctx(region);
+	char *path = region->region_buf;
+	int len = region->buf_len;
+
+	if (snprintf(path, len, "%s/read_only", region->region_path) >= len) {
+		err(ctx, "%s: buffer too small!\n",
+				ndctl_region_get_devname(region));
+		return -ENXIO;
+	}
+
+	ro = !!ro;
+	if (sysfs_write_attr(ctx, path, ro ? "1\n" : "0\n") < 0)
+		return -ENXIO;
+
+	region->ro = ro;
+	return ro;
+}
+
 NDCTL_EXPORT const char *ndctl_bus_get_cmd_name(struct ndctl_bus *bus, int cmd)
 {
 	return nvdimm_bus_cmd_name(cmd);
@@ -1404,6 +1447,8 @@  static int add_region(void *parent, int id, const char *region_base)
 	region = calloc(1, sizeof(*region));
 	if (!region)
 		goto err_region;
+	list_head_init(&region->btts);
+	list_head_init(&region->stale_btts);
 	list_head_init(&region->mappings);
 	list_head_init(&region->namespaces);
 	list_head_init(&region->stale_namespaces);
@@ -1443,6 +1488,11 @@  static int add_region(void *parent, int id, const char *region_base)
 				region->iset.cookie);
 	}
 
+	sprintf(path, "%s/read_only", region_base);
+	if (sysfs_read_attr(ctx, path, buf) < 0)
+		goto err_read;
+	region->ro = strtoul(buf, NULL, 0);
+
 	sprintf(path, "%s/modalias", region_base);
 	if (sysfs_read_attr(ctx, path, buf) < 0)
 		goto err_read;
@@ -2189,7 +2239,8 @@  NDCTL_EXPORT int ndctl_region_enable(struct ndctl_region *region)
 
 NDCTL_EXPORT void ndctl_region_cleanup(struct ndctl_region *region)
 {
-	free_namespaces(region, &region->stale_namespaces);
+	free_stale_namespaces(region);
+	free_stale_btts(region);
 }
 
 static int ndctl_region_disable(struct ndctl_region *region, int cleanup)
@@ -2207,7 +2258,9 @@  static int ndctl_region_disable(struct ndctl_region *region, int cleanup)
 		return -EBUSY;
 	}
 	region->namespaces_init = 0;
+	region->btts_init = 0;
 	list_append_list(&region->stale_namespaces, &region->namespaces);
+	list_append_list(&region->stale_btts, &region->btts);
 	region->generation++;
 	if (cleanup)
 		ndctl_region_cleanup(region);
@@ -2545,6 +2598,11 @@  static int add_namespace(void *parent, int id, const char *ndns_base)
 		goto err_read;
 	ndns->size = strtoull(buf, NULL, 0);
 
+	sprintf(path, "%s/force_raw", ndns_base);
+	if (sysfs_read_attr(ctx, path, buf) < 0)
+		goto err_read;
+	ndns->raw_mode = strtoul(buf, NULL, 0);
+
 	switch (ndns->type) {
 	case ND_DEVICE_NAMESPACE_BLK:
 		sprintf(path, "%s/sector_size", ndns_base);
@@ -2670,6 +2728,30 @@  NDCTL_EXPORT const char *ndctl_namespace_get_devname(struct ndctl_namespace *ndn
 	return devpath_to_devname(ndns->ndns_path);
 }
 
+NDCTL_EXPORT struct ndctl_btt *ndctl_namespace_get_btt(struct ndctl_namespace *ndns)
+{
+	struct ndctl_region *region = ndctl_namespace_get_region(ndns);
+	struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns);
+	char *path = ndns->ndns_buf;
+	int len = ndns->buf_len;
+	struct ndctl_btt *btt;
+	char buf[50];
+
+	if (snprintf(path, len, "%s/holder", ndns->ndns_path) >= len) {
+		err(ctx, "%s: buffer too small!\n",
+				ndctl_namespace_get_devname(ndns));
+		return NULL;
+	}
+
+	if (sysfs_read_attr(ctx, path, buf) < 0)
+		return NULL;
+
+	ndctl_btt_foreach(region, btt)
+		if (strcmp(buf, ndctl_btt_get_devname(btt)) == 0)
+			return btt;
+	return NULL;
+}
+
 NDCTL_EXPORT const char *ndctl_namespace_get_block_device(struct ndctl_namespace *ndns)
 {
 	struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns);
@@ -2698,6 +2780,32 @@  NDCTL_EXPORT int ndctl_namespace_is_valid(struct ndctl_namespace *ndns)
 	return ndns->generation == region->generation;
 }
 
+NDCTL_EXPORT int ndctl_namespace_get_raw_mode(struct ndctl_namespace *ndns)
+{
+	return ndns->raw_mode;
+}
+
+NDCTL_EXPORT int ndctl_namespace_set_raw_mode(struct ndctl_namespace *ndns,
+		int raw_mode)
+{
+	struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns);
+	char *path = ndns->ndns_buf;
+	int len = ndns->buf_len;
+
+	if (snprintf(path, len, "%s/force_raw", ndns->ndns_path) >= len) {
+		err(ctx, "%s: buffer too small!\n",
+				ndctl_namespace_get_devname(ndns));
+		return -ENXIO;
+	}
+
+	raw_mode = !!raw_mode;
+	if (sysfs_write_attr(ctx, path, raw_mode ? "1\n" : "0\n") < 0)
+		return -ENXIO;
+
+	ndns->raw_mode = raw_mode;
+	return raw_mode;
+}
+
 NDCTL_EXPORT int ndctl_namespace_is_enabled(struct ndctl_namespace *ndns)
 {
 	struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns);
@@ -2788,13 +2896,30 @@  static int ndctl_unbind(struct ndctl_ctx *ctx, const char *devpath)
 	return sysfs_write_attr(ctx, path, devname);
 }
 
-static void btts_init(struct ndctl_bus *bus);
+static int add_btt(void *parent, int id, const char *btt_base);
+
+static void btts_init(struct ndctl_region *region)
+{
+	struct ndctl_bus *bus = ndctl_region_get_bus(region);
+	char btt_fmt[20];
+
+	if (region->btts_init)
+		return;
+	region->btts_init = 1;
+
+	sprintf(btt_fmt, "btt%d.", region->id);
+	device_parse(bus->ctx, bus, region->region_path, btt_fmt, region, add_btt);
+}
 
+/*
+ * Return 0 if enabled, < 0 if failed to enable, and > 0 if claimed by
+ * another device and that device is enabled.  In the > 0 case a
+ * subsequent call to ndctl_namespace_is_enabled() will return 'false'.
+ */
 NDCTL_EXPORT int ndctl_namespace_enable(struct ndctl_namespace *ndns)
 {
 	const char *devname = ndctl_namespace_get_devname(ndns);
 	struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns);
-	struct ndctl_bus *bus = ndctl_namespace_get_bus(ndns);
 	struct ndctl_region *region = ndns->region;
 	int rc;
 
@@ -2804,22 +2929,31 @@  NDCTL_EXPORT int ndctl_namespace_enable(struct ndctl_namespace *ndns)
 	rc = ndctl_bind(ctx, ndns->module, devname);
 
 	if (!ndctl_namespace_is_enabled(ndns)) {
+		struct ndctl_btt *btt = ndctl_namespace_get_btt(ndns);
+
+		if (btt && ndctl_btt_is_enabled(btt)) {
+			dbg(ctx, "%s: enabled via %s\n", devname,
+					ndctl_btt_get_devname(btt));
+			rc = 1;
+			goto out;
+		}
 		err(ctx, "%s: failed to enable\n", devname);
 		return rc ? rc : -ENXIO;
 	}
+	rc = 0;
+	dbg(ctx, "%s: enabled\n", devname);
 
 	/*
 	 * Rescan now as successfully enabling a namespace device leads
 	 * to a new one being created, and potentially btts being attached
 	 */
+ out:
 	region->namespaces_init = 0;
+	region->btts_init = 0;
 	namespaces_init(region);
-	bus->btts_init = 0;
-	btts_init(bus);
+	btts_init(region);
 
-	dbg(ctx, "%s: enabled\n", devname);
-
-	return 0;
+	return rc;
 }
 
 NDCTL_EXPORT int ndctl_namespace_disable(struct ndctl_namespace *ndns)
@@ -3133,10 +3267,10 @@  static int parse_lbasize_supported(struct ndctl_ctx *ctx, const char *devname,
 
 static int add_btt(void *parent, int id, const char *btt_base)
 {
+	struct ndctl_ctx *ctx = ndctl_region_get_ctx(parent);
 	const char *devname = devpath_to_devname(btt_base);
 	char *path = calloc(1, strlen(btt_base) + 100);
-	struct ndctl_bus *bus = parent;
-	struct ndctl_ctx *ctx = bus->ctx;
+	struct ndctl_region *region = parent;
 	struct ndctl_btt *btt, *btt_dup;
 	char buf[SYSFS_ATTR_SIZE];
 	int rc = -ENOMEM;
@@ -3148,7 +3282,8 @@  static int add_btt(void *parent, int id, const char *btt_base)
 	if (!btt)
 		goto err_btt;
 	btt->id = id;
-	btt->bus = bus;
+	btt->region = region;
+	btt->generation = region->generation;
 
 	btt->btt_path = strdup(btt_base);
 	if (!btt->btt_path)
@@ -3178,21 +3313,14 @@  static int add_btt(void *parent, int id, const char *btt_base)
 	if (parse_lbasize_supported(ctx, devname, buf, &btt->lbasize) < 0)
 		goto err_read;
 
-	sprintf(path, "%s/backing_dev", btt_base);
-	if (sysfs_read_attr(ctx, path, buf) < 0)
-		goto err_read;
-	btt->backing_dev = strdup(buf);
-	if (!btt->backing_dev)
-		goto err_read;
-
 	free(path);
-	ndctl_btt_foreach(bus, btt_dup)
+	ndctl_btt_foreach(region, btt_dup)
 		if (btt->id == btt_dup->id) {
-			free_btt(btt, BTT_SKIP_DELETE);
+			free_btt(btt, NULL);
 			return 1;
 		}
 
-	list_add(&bus->btts, &btt->list);
+	list_add(&region->btts, &btt->list);
 	return 0;
 
  err_read:
@@ -3205,27 +3333,18 @@  static int add_btt(void *parent, int id, const char *btt_base)
 	return rc;
 }
 
-static void btts_init(struct ndctl_bus *bus)
+NDCTL_EXPORT struct ndctl_btt *ndctl_btt_get_first(struct ndctl_region *region)
 {
-	if (bus->btts_init)
-		return;
-	bus->btts_init = 1;
+	btts_init(region);
 
-	device_parse(bus->ctx, bus, bus->bus_path, "btt", bus, add_btt);
-}
-
-NDCTL_EXPORT struct ndctl_btt *ndctl_btt_get_first(struct ndctl_bus *bus)
-{
-	btts_init(bus);
-
-	return list_top(&bus->btts, struct ndctl_btt, list);
+	return list_top(&region->btts, struct ndctl_btt, list);
 }
 
 NDCTL_EXPORT struct ndctl_btt *ndctl_btt_get_next(struct ndctl_btt *btt)
 {
-	struct ndctl_bus *bus = btt->bus;
+	struct ndctl_region *region = btt->region;
 
-	return list_next(&bus->btts, btt, list);
+	return list_next(&region->btts, btt, list);
 }
 
 NDCTL_EXPORT unsigned int ndctl_btt_get_id(struct ndctl_btt *btt)
@@ -3252,9 +3371,32 @@  NDCTL_EXPORT int ndctl_btt_get_num_sector_sizes(struct ndctl_btt *btt)
 	return btt->lbasize.num;
 }
 
-NDCTL_EXPORT const char *ndctl_btt_get_backing_dev(struct ndctl_btt *btt)
+NDCTL_EXPORT struct ndctl_namespace *ndctl_btt_get_namespace(struct ndctl_btt *btt)
 {
-	return btt->backing_dev;
+	struct ndctl_ctx *ctx = ndctl_btt_get_ctx(btt);
+	struct ndctl_namespace *ndns, *found = NULL;
+	struct ndctl_region *region = btt->region;
+	char *path = region->region_buf;
+	int len = region->buf_len;
+	char buf[50];
+
+	if (btt->ndns)
+		return btt->ndns;
+
+	if (snprintf(path, len, "%s/namespace", btt->btt_path) >= len) {
+		err(ctx, "%s: buffer too small!\n",
+				ndctl_btt_get_devname(btt));
+		return NULL;
+	}
+
+	if (sysfs_read_attr(ctx, path, buf) < 0)
+		return NULL;
+
+	ndctl_namespace_foreach(region, ndns)
+		if (strcmp(buf, ndctl_namespace_get_devname(ndns)) == 0)
+			found = ndns;
+	btt->ndns = found;
+	return found;
 }
 
 NDCTL_EXPORT void ndctl_btt_get_uuid(struct ndctl_btt *btt, uuid_t uu)
@@ -3307,31 +3449,30 @@  NDCTL_EXPORT int ndctl_btt_set_sector_size(struct ndctl_btt *btt,
 	return 0;
 }
 
-NDCTL_EXPORT int ndctl_btt_set_backing_dev(struct ndctl_btt *btt,
-		const char *backing_dev)
+NDCTL_EXPORT int ndctl_btt_set_namespace(struct ndctl_btt *btt,
+		struct ndctl_namespace *ndns)
 {
 	struct ndctl_ctx *ctx = ndctl_btt_get_ctx(btt);
 	char *path = btt->btt_buf;
 	int len = btt->buf_len;
 
-	if (snprintf(path, len, "%s/backing_dev", btt->btt_path) >= len) {
+	if (snprintf(path, len, "%s/namespace", btt->btt_path) >= len) {
 		err(ctx, "%s: buffer too small!\n",
 				ndctl_btt_get_devname(btt));
 		return -ENXIO;
 	}
 
-	if (sysfs_write_attr(ctx, path, backing_dev ? : "\n") != 0)
+	if (sysfs_write_attr(ctx, path, ndns
+				? ndctl_namespace_get_devname(ndns) : "\n"))
 		return -ENXIO;
 
-	free(btt->backing_dev);
-	btt->backing_dev = backing_dev ? strdup(backing_dev) : NULL;
-
+	btt->ndns = ndns;
 	return 0;
 }
 
 NDCTL_EXPORT struct ndctl_bus *ndctl_btt_get_bus(struct ndctl_btt *btt)
 {
-	return btt->bus;
+	return btt->region->bus;
 }
 
 NDCTL_EXPORT struct ndctl_ctx *ndctl_btt_get_ctx(struct ndctl_btt *btt)
@@ -3365,6 +3506,13 @@  NDCTL_EXPORT const char *ndctl_btt_get_block_device(struct ndctl_btt *btt)
 	return btt->bdev ? btt->bdev : "";
 }
 
+NDCTL_EXPORT int ndctl_btt_is_valid(struct ndctl_btt *btt)
+{
+	struct ndctl_region *region = ndctl_btt_get_region(btt);
+
+	return btt->generation == region->generation;
+}
+
 NDCTL_EXPORT int ndctl_btt_is_enabled(struct ndctl_btt *btt)
 {
 	struct ndctl_ctx *ctx = ndctl_btt_get_ctx(btt);
@@ -3380,11 +3528,16 @@  NDCTL_EXPORT int ndctl_btt_is_enabled(struct ndctl_btt *btt)
 	return is_enabled(ndctl_btt_get_bus(btt), path);
 }
 
+NDCTL_EXPORT struct ndctl_region *ndctl_btt_get_region(struct ndctl_btt *btt)
+{
+	return btt->region;
+}
+
 NDCTL_EXPORT int ndctl_btt_enable(struct ndctl_btt *btt)
 {
+	struct ndctl_region *region = ndctl_btt_get_region(btt);
 	const char *devname = ndctl_btt_get_devname(btt);
 	struct ndctl_ctx *ctx = ndctl_btt_get_ctx(btt);
-	struct ndctl_bus *bus = ndctl_btt_get_bus(btt);
 	char *path = btt->btt_buf;
 	int len = btt->buf_len;
 
@@ -3411,58 +3564,48 @@  NDCTL_EXPORT int ndctl_btt_enable(struct ndctl_btt *btt)
 	 * Rescan now as successfully enabling a btt device leads to a
 	 * new one being created
 	 */
-	bus->btts_init = 0;
-	btts_init(bus);
+	region->btts_init = 0;
+	btts_init(region);
 
 	return 0;
 }
 
 NDCTL_EXPORT int ndctl_btt_delete(struct ndctl_btt *btt)
 {
+	struct ndctl_region *region = ndctl_btt_get_region(btt);
 	struct ndctl_ctx *ctx = ndctl_btt_get_ctx(btt);
-	int len = btt->buf_len, rc;
-	char *path = btt->btt_buf;
+	int rc;
 
-	/*
-	 * Only permit deletion when we know a replacement seed device
-	 * is present.
-	 */
-	if (!ndctl_btt_is_enabled(btt))
-		return -EINVAL;
+	if (!ndctl_btt_is_valid(btt)) {
+		free_btt(btt, &region->stale_btts);
+		return 0;
+	}
 
 	ndctl_unbind(ctx, btt->btt_path);
 
-	rc = ndctl_btt_set_backing_dev(btt, NULL);
+	rc = ndctl_btt_set_namespace(btt, NULL);
 	if (rc) {
-		dbg(ctx, "%s: failed to clear backing dev: %d\n",
+		dbg(ctx, "%s: failed to clear namespace: %d\n",
 			ndctl_btt_get_devname(btt), rc);
 		return rc;
 	}
 
-	if (snprintf(path, len, "%s/delete", btt->btt_path) >= len) {
-		err(ctx, "%s: buffer too small!\n",
-				ndctl_btt_get_devname(btt));
-		return -EINVAL;
-	}
-
-	rc = sysfs_write_attr(ctx, path, "1\n");
-	if (rc < 0)
-		return -ENXIO;
-	free_btt(btt, BTT_DELETE);
+	free_btt(btt, &region->btts);
+	region->btts_init = 0;
 
 	return 0;
 }
 
 NDCTL_EXPORT int ndctl_btt_is_configured(struct ndctl_btt *btt)
 {
-	if (ndctl_btt_get_sector_size(btt) == UINT_MAX)
-		return 0;
+	if (ndctl_btt_get_namespace(btt))
+		return 1;
 
-	if (strcmp(ndctl_btt_get_backing_dev(btt), "") == 0)
-		return 0;
+	if (ndctl_btt_get_sector_size(btt) != UINT_MAX)
+		return 1;
 
-	if (memcmp(&btt->uuid, null_uuid, sizeof(null_uuid)) == 0)
-		return 0;
+	if (memcmp(&btt->uuid, null_uuid, sizeof(null_uuid)) != 0)
+		return 1;
 
-	return 1;
+	return 0;
 }
diff --git a/lib/libndctl.sym b/lib/libndctl.sym
index e21ffdd3a0a4..3391a095490c 100644
--- a/lib/libndctl.sym
+++ b/lib/libndctl.sym
@@ -24,7 +24,6 @@  global:
 	ndctl_bus_get_minor;
 	ndctl_bus_get_devname;
 	ndctl_bus_get_by_provider;
-	ndctl_bus_get_btt_seed;
 	ndctl_bus_get_cmd_name;
 	ndctl_bus_is_cmd_supported;
 	ndctl_bus_has_nfit;
@@ -85,6 +84,7 @@  global:
 	ndctl_region_get_available_size;
 	ndctl_region_get_type;
 	ndctl_region_get_namespace_seed;
+	ndctl_region_get_btt_seed;
 	ndctl_region_get_type_name;
 	ndctl_region_get_bus;
 	ndctl_region_get_ctx;
@@ -96,6 +96,8 @@  global:
 	ndctl_region_disable_preserve;
 	ndctl_region_cleanup;
 	ndctl_region_get_interleave_set;
+	ndctl_region_get_ro;
+	ndctl_region_set_ro;
 	ndctl_interleave_set_get_first;
 	ndctl_interleave_set_get_next;
 	ndctl_interleave_set_is_active;
@@ -115,6 +117,7 @@  global:
 	ndctl_namespace_get_next;
 	ndctl_namespace_get_ctx;
 	ndctl_namespace_get_bus;
+	ndctl_namespace_get_btt;
 	ndctl_namespace_get_region;
 	ndctl_namespace_get_id;
 	ndctl_namespace_get_devname;
@@ -138,22 +141,26 @@  global:
 	ndctl_namespace_get_sector_size;
 	ndctl_namespace_get_num_sector_sizes;
 	ndctl_namespace_set_sector_size;
+	ndctl_namespace_get_raw_mode;
+	ndctl_namespace_set_raw_mode;
 	ndctl_btt_get_first;
 	ndctl_btt_get_next;
 	ndctl_btt_get_ctx;
 	ndctl_btt_get_bus;
+	ndctl_btt_get_region;
 	ndctl_btt_get_id;
 	ndctl_btt_get_supported_sector_size;
 	ndctl_btt_get_sector_size;
 	ndctl_btt_get_num_sector_sizes;
-	ndctl_btt_get_backing_dev;
+	ndctl_btt_get_namespace;
 	ndctl_btt_get_uuid;
 	ndctl_btt_is_enabled;
+	ndctl_btt_is_valid;
 	ndctl_btt_get_devname;
 	ndctl_btt_get_block_device;
 	ndctl_btt_set_uuid;
 	ndctl_btt_set_sector_size;
-	ndctl_btt_set_backing_dev;
+	ndctl_btt_set_namespace;
 	ndctl_btt_enable;
 	ndctl_btt_delete;
 	ndctl_btt_is_configured;
diff --git a/lib/ndctl/libndctl.h b/lib/ndctl/libndctl.h
index 18c3cf303692..c92910db2015 100644
--- a/lib/ndctl/libndctl.h
+++ b/lib/ndctl/libndctl.h
@@ -103,7 +103,6 @@  unsigned int ndctl_bus_get_minor(struct ndctl_bus *bus);
 const char *ndctl_bus_get_devname(struct ndctl_bus *bus);
 struct ndctl_bus *ndctl_bus_get_by_provider(struct ndctl_ctx *ctx,
 		const char *provider);
-struct ndctl_btt *ndctl_bus_get_btt_seed(struct ndctl_bus *bus);
 const char *ndctl_bus_get_cmd_name(struct ndctl_bus *bus, int cmd);
 int ndctl_bus_is_cmd_supported(struct ndctl_bus *bus, int cmd);
 unsigned int ndctl_bus_get_revision(struct ndctl_bus *bus);
@@ -194,6 +193,9 @@  unsigned int ndctl_region_get_range_index(struct ndctl_region *region);
 unsigned int ndctl_region_get_type(struct ndctl_region *region);
 struct ndctl_namespace *ndctl_region_get_namespace_seed(
 		struct ndctl_region *region);
+int ndctl_region_get_ro(struct ndctl_region *region);
+int ndctl_region_set_ro(struct ndctl_region *region, int ro);
+struct ndctl_btt *ndctl_region_get_btt_seed(struct ndctl_region *region);
 unsigned int ndctl_region_get_nstype(struct ndctl_region *region);
 const char *ndctl_region_get_type_name(struct ndctl_region *region);
 struct ndctl_bus *ndctl_region_get_bus(struct ndctl_region *region);
@@ -266,6 +268,7 @@  struct ndctl_namespace *ndctl_namespace_get_next(struct ndctl_namespace *ndns);
 struct ndctl_ctx *ndctl_namespace_get_ctx(struct ndctl_namespace *ndns);
 struct ndctl_bus *ndctl_namespace_get_bus(struct ndctl_namespace *ndns);
 struct ndctl_region *ndctl_namespace_get_region(struct ndctl_namespace *ndns);
+struct ndctl_btt *ndctl_namespace_get_btt(struct ndctl_namespace *ndns);
 unsigned int ndctl_namespace_get_id(struct ndctl_namespace *ndns);
 const char *ndctl_namespace_get_devname(struct ndctl_namespace *ndns);
 unsigned int ndctl_namespace_get_type(struct ndctl_namespace *ndns);
@@ -291,34 +294,38 @@  unsigned int ndctl_namespace_get_sector_size(struct ndctl_namespace *ndns);
 int ndctl_namespace_get_num_sector_sizes(struct ndctl_namespace *ndns);
 int ndctl_namespace_set_sector_size(struct ndctl_namespace *ndns,
 		unsigned int sector_size);
+int ndctl_namespace_get_raw_mode(struct ndctl_namespace *ndns);
+int ndctl_namespace_set_raw_mode(struct ndctl_namespace *ndns, int raw_mode);
 
 struct ndctl_btt;
-struct ndctl_btt *ndctl_btt_get_first(struct ndctl_bus *bus);
+struct ndctl_btt *ndctl_btt_get_first(struct ndctl_region *region);
 struct ndctl_btt *ndctl_btt_get_next(struct ndctl_btt *btt);
-#define ndctl_btt_foreach(bus, btt) \
-        for (btt = ndctl_btt_get_first(bus); \
+#define ndctl_btt_foreach(region, btt) \
+        for (btt = ndctl_btt_get_first(region); \
              btt != NULL; \
              btt = ndctl_btt_get_next(btt))
-#define ndctl_btt_foreach_safe(bus, btt, _btt) \
-	for (btt = ndctl_btt_get_first(bus), \
+#define ndctl_btt_foreach_safe(region, btt, _btt) \
+	for (btt = ndctl_btt_get_first(region), \
 	     _btt = ndctl_btt_get_next(btt); \
 	     btt != NULL; \
 	     btt = _btt, \
 	     _btt = _btt ? ndctl_btt_get_next(_btt) : NULL)
 struct ndctl_ctx *ndctl_btt_get_ctx(struct ndctl_btt *btt);
 struct ndctl_bus *ndctl_btt_get_bus(struct ndctl_btt *btt);
+struct ndctl_region *ndctl_btt_get_region(struct ndctl_btt *btt);
 unsigned int ndctl_btt_get_id(struct ndctl_btt *btt);
 unsigned int ndctl_btt_get_supported_sector_size(struct ndctl_btt *btt, int i);
 unsigned int ndctl_btt_get_sector_size(struct ndctl_btt *btt);
 int ndctl_btt_get_num_sector_sizes(struct ndctl_btt *btt);
-const char *ndctl_btt_get_backing_dev(struct ndctl_btt *btt);
+struct ndctl_namespace *ndctl_btt_get_namespace(struct ndctl_btt *btt);
 void ndctl_btt_get_uuid(struct ndctl_btt *btt, uuid_t uu);
 int ndctl_btt_is_enabled(struct ndctl_btt *btt);
+int ndctl_btt_is_valid(struct ndctl_btt *btt);
 const char *ndctl_btt_get_devname(struct ndctl_btt *btt);
 const char *ndctl_btt_get_block_device(struct ndctl_btt *btt);
 int ndctl_btt_set_uuid(struct ndctl_btt *btt, uuid_t uu);
 int ndctl_btt_set_sector_size(struct ndctl_btt *btt, unsigned int sector_size);
-int ndctl_btt_set_backing_dev(struct ndctl_btt *btt, const char *backing_dev);
+int ndctl_btt_set_namespace(struct ndctl_btt *btt, struct ndctl_namespace *ndns);
 int ndctl_btt_enable(struct ndctl_btt *btt);
 int ndctl_btt_delete(struct ndctl_btt *btt);
 int ndctl_btt_is_configured(struct ndctl_btt *btt);
diff --git a/lib/test-libndctl.c b/lib/test-libndctl.c
index f8e9797d4483..8c1d6c992db6 100644
--- a/lib/test-libndctl.c
+++ b/lib/test-libndctl.c
@@ -150,6 +150,13 @@  static struct dimm dimms1[] = {
 	}
 };
 
+struct btt {
+	int enabled;
+	uuid_t uuid;
+	int num_sector_sizes;
+	unsigned int sector_sizes[7];
+};
+
 struct region {
 	union {
 		unsigned int range_index;
@@ -163,16 +170,10 @@  struct region {
 	struct set {
 		int active;
 	} iset;
+	struct btt *btts[2];
 	struct namespace *namespaces[4];
 };
 
-struct btt {
-	int enabled;
-	uuid_t uuid;
-	int num_sector_sizes;
-	unsigned int sector_sizes[7];
-};
-
 static struct btt btt_settings = {
 	.enabled = 1,
 	.uuid = {  0,  1,  2,  3,  4,  5,  6,  7,
@@ -263,19 +264,61 @@  static struct namespace namespace5_blk0 = {
 	  8, 8, 8, 8, }, 1, 1, 7, 0, blk_sector_sizes,
 };
 
+static struct btt default_btt = {
+	0, { 0, }, 7, { 512, 520, 528, 4096, 4104, 4160, 4224, },
+};
+
 static struct region regions0[] = {
 	{ { 1 }, 2, 1, "pmem", SZ_32M, SZ_32M, { 1 },
-		{ &namespace0_pmem0, NULL, }, },
+		.namespaces = {
+			[0] = &namespace0_pmem0,
+		},
+		.btts = {
+			[0] = &default_btt,
+		},
+	},
 	{ { 2 }, 4, 1, "pmem", SZ_64M, SZ_64M, { 1 },
-		{ &namespace1_pmem0, NULL, }, },
-	{ { DIMM_HANDLE(0, 0, 0, 0, 0) }, 1, 1, "blk", SZ_18M, SZ_32M, { },
-		{ &namespace2_blk0, &namespace2_blk1, NULL, }, },
-	{ { DIMM_HANDLE(0, 0, 0, 0, 1) }, 1, 1, "blk", SZ_18M, SZ_32M, { },
-		{ &namespace3_blk0, &namespace3_blk1, NULL, }, },
-	{ { DIMM_HANDLE(0, 0, 1, 0, 0) }, 1, 1, "blk", SZ_27M, SZ_32M, { },
-		{ &namespace4_blk0, NULL, }, },
-	{ { DIMM_HANDLE(0, 0, 1, 0, 1) }, 1, 1, "blk", SZ_27M, SZ_32M, { },
-		{ &namespace5_blk0, NULL, }, },
+		.namespaces = {
+			[0] = &namespace1_pmem0,
+		},
+		.btts = {
+			[0] = &default_btt,
+		},
+	},
+	{ { DIMM_HANDLE(0, 0, 0, 0, 0) }, 1, 1, "blk", SZ_18M, SZ_32M,
+		.namespaces = {
+			[0] = &namespace2_blk0,
+			[1] = &namespace2_blk1,
+		},
+		.btts = {
+			[0] = &default_btt,
+		},
+	},
+	{ { DIMM_HANDLE(0, 0, 0, 0, 1) }, 1, 1, "blk", SZ_18M, SZ_32M,
+		.namespaces = {
+			[0] = &namespace3_blk0,
+			[1] = &namespace3_blk1,
+		},
+		.btts = {
+			[0] = &default_btt,
+		},
+	},
+	{ { DIMM_HANDLE(0, 0, 1, 0, 0) }, 1, 1, "blk", SZ_27M, SZ_32M,
+		.namespaces = {
+			[0] = &namespace4_blk0,
+		},
+		.btts = {
+			[0] = &default_btt,
+		},
+	},
+	{ { DIMM_HANDLE(0, 0, 1, 0, 1) }, 1, 1, "blk", SZ_27M, SZ_32M,
+		.namespaces = {
+			[0] = &namespace5_blk0,
+		},
+		.btts = {
+			[0] = &default_btt,
+		},
+	},
 };
 
 static struct namespace namespace1 = {
@@ -294,14 +337,6 @@  static struct region regions1[] = {
 	},
 };
 
-static struct btt btts0[] = {
-	{ 0, { 0, }, 7, { 512, 520, 528, 4096, 4104, 4160, 4224, }, },
-};
-
-static struct btt btts1[] = {
-	{ 0, { 0, }, 7, { 512, 520, 528, 4096, 4104, 4160, 4224, }, },
-};
-
 static unsigned long commands0 = 1UL << ND_CMD_GET_CONFIG_SIZE
 		| 1UL << ND_CMD_GET_CONFIG_DATA
 		| 1UL << ND_CMD_SET_CONFIG_DATA;
@@ -317,11 +352,11 @@  static struct ndctl_dimm *get_dimm_by_handle(struct ndctl_bus *bus, unsigned int
 	return NULL;
 }
 
-static struct ndctl_btt *get_idle_btt(struct ndctl_bus *bus)
+static struct ndctl_btt *get_idle_btt(struct ndctl_region *region)
 {
 	struct ndctl_btt *btt;
 
-	ndctl_btt_foreach(bus, btt)
+	ndctl_btt_foreach(region, btt)
 		if (!ndctl_btt_is_enabled(btt) && !ndctl_btt_is_configured(btt))
 			return btt;
 
@@ -388,6 +423,7 @@  static struct ndctl_region *get_blk_region_by_dimm_handle(struct ndctl_bus *bus,
 
 static int check_namespaces(struct ndctl_region *region,
 		struct namespace **namespaces);
+static int check_btts(struct ndctl_region *region, struct btt **btts);
 
 static int check_regions(struct ndctl_bus *bus, struct region *regions, int n)
 {
@@ -464,6 +500,10 @@  static int check_regions(struct ndctl_bus *bus, struct region *regions, int n)
 			return -ENXIO;
 		}
 
+		rc = check_btts(region, regions[i].btts);
+		if (rc)
+			return rc;
+
 		if (regions[i].namespaces)
 			rc = check_namespaces(region, regions[i].namespaces);
 		if (rc)
@@ -473,9 +513,10 @@  static int check_regions(struct ndctl_bus *bus, struct region *regions, int n)
 	return rc;
 }
 
-static int check_btt_create(struct ndctl_bus *bus, struct ndctl_namespace *ndns,
-		struct btt *create_btt)
+static int check_btt_create(struct ndctl_region *region, struct ndctl_namespace *ndns,
+		struct namespace *namespace)
 {
+	struct btt *btt_s = namespace->btt_settings;
 	int i, fd, retry = 10;
 	struct ndctl_btt *btt;
 	const char *devname;
@@ -483,25 +524,41 @@  static int check_btt_create(struct ndctl_bus *bus, struct ndctl_namespace *ndns,
 	void *buf = NULL;
 	ssize_t rc;
 
-	if (!create_btt)
+	if (!namespace->btt_settings)
 		return 0;
 
 	if (posix_memalign(&buf, 4096, 4096) != 0)
 		return -ENXIO;
 
-	for (i = 0; i < create_btt->num_sector_sizes; i++) {
-		btt = get_idle_btt(bus);
+	for (i = 0; i < btt_s->num_sector_sizes; i++) {
+		btt = get_idle_btt(region);
 		if (!btt)
 			return -ENXIO;
 
-		sprintf(bdevpath, "/dev/%s", ndctl_namespace_get_block_device(ndns));
-		ndctl_btt_set_uuid(btt, create_btt->uuid);
-		ndctl_btt_set_sector_size(btt, create_btt->sector_sizes[i]);
-		ndctl_btt_set_backing_dev(btt, bdevpath);
-		ndctl_btt_enable(btt);
+		devname = ndctl_btt_get_devname(btt);
+		ndctl_btt_set_uuid(btt, btt_s->uuid);
+		ndctl_btt_set_sector_size(btt, btt_s->sector_sizes[i]);
+		ndctl_btt_set_namespace(btt, ndns);
+		ndctl_namespace_disable(ndns);
+		rc = ndctl_btt_enable(btt);
+		if (namespace->ro == (rc == 0)) {
+			fprintf(stderr, "%s: expected btt enable %s, %s read-%s\n",
+					devname,
+					namespace->ro ? "failure" : "success",
+					ndctl_region_get_devname(region),
+					namespace->ro ? "only" : "write");
+			return -ENXIO;
+		}
+		if (namespace->ro) {
+			ndctl_region_set_ro(region, 0);
+			rc = ndctl_btt_enable(btt);
+			fprintf(stderr, "%s: failed to enable after setting rw\n",
+					devname);
+			ndctl_region_set_ro(region, 1);
+			return -ENXIO;
+		}
 
 		sprintf(bdevpath, "/dev/%s", ndctl_btt_get_block_device(btt));
-		devname = ndctl_btt_get_devname(btt);
 		rc = -ENXIO;
 		fd = open(bdevpath, O_RDWR|O_DIRECT);
 		if (fd < 0)
@@ -531,6 +588,8 @@  static int check_btt_create(struct ndctl_bus *bus, struct ndctl_namespace *ndns,
 			rc = 0;
 			break;
 		}
+		if (namespace->ro)
+			ndctl_region_set_ro(region, 1);
 		if (fd >= 0)
 			close(fd);
 
@@ -591,16 +650,16 @@  static int check_btt_autodetect(struct ndctl_bus *bus,
 		struct ndctl_namespace *ndns, void *buf,
 		struct namespace *namespace)
 {
-	const char *ndns_bdev = ndctl_namespace_get_block_device(ndns);
+	struct ndctl_region *region = ndctl_namespace_get_region(ndns);
 	const char *devname = ndctl_namespace_get_devname(ndns);
 	struct btt *auto_btt = namespace->btt_settings;
 	struct ndctl_btt *btt, *found = NULL;
-	int btt_fd = -1, backing_fd = -1, ro;
-	const char *backing_bdev;
 	ssize_t rc = -ENXIO;
-	char btt_bdev[50];
+	char bdev[50];
+	int fd, ro;
 
-	ndctl_btt_foreach(bus, btt) {
+	ndctl_btt_foreach(region, btt) {
+		struct ndctl_namespace *btt_ndns;
 		uuid_t uu;
 
 		ndctl_btt_get_uuid(btt, uu);
@@ -608,9 +667,11 @@  static int check_btt_autodetect(struct ndctl_bus *bus,
 			continue;
 		if (!ndctl_btt_is_enabled(btt))
 			continue;
-		backing_bdev = ndctl_btt_get_backing_dev(btt);
-		if (strcmp(backing_bdev+5, ndns_bdev) != 0)
+		btt_ndns = ndctl_btt_get_namespace(btt);
+		if (strcmp(ndctl_namespace_get_devname(btt_ndns), devname) != 0)
 			continue;
+		fprintf(stderr, "%s: btt_ndns: %p ndns: %p\n", __func__,
+				btt_ndns, ndns);
 		found = btt;
 		break;
 	}
@@ -618,13 +679,13 @@  static int check_btt_autodetect(struct ndctl_bus *bus,
 	if (!found)
 		return -ENXIO;
 
-	sprintf(btt_bdev, "/dev/%s", ndctl_btt_get_block_device(btt));
-	btt_fd = open(btt_bdev, O_RDONLY);
-	if (btt_fd < 0)
+	sprintf(bdev, "/dev/%s", ndctl_btt_get_block_device(btt));
+	fd = open(bdev, O_RDONLY);
+	if (fd < 0)
 		return -ENXIO;
-	rc = ioctl(btt_fd, BLKROGET, &ro);
+	rc = ioctl(fd, BLKROGET, &ro);
 	if (rc < 0) {
-		fprintf(stderr, "%s: failed to open %s\n", __func__, btt_bdev);
+		fprintf(stderr, "%s: failed to open %s\n", __func__, bdev);
 		rc = -ENXIO;
 		goto out;
 	}
@@ -632,70 +693,40 @@  static int check_btt_autodetect(struct ndctl_bus *bus,
 	rc = -ENXIO;
 	if (ro != namespace->ro) {
 		fprintf(stderr, "%s: read-%s expected read-%s by default\n",
-				btt_bdev, ro ? "only" : "write",
+				bdev, ro ? "only" : "write",
 				namespace->ro ? "only" : "write");
 		goto out;
 	}
 
-	backing_fd = open(backing_bdev, O_RDONLY);
-	if (backing_fd < 0) {
-		fprintf(stderr, "%s: failed to open %s to set rw\n",
-				__func__, backing_bdev);
-		goto out;
-	}
-
-	ro = 0;
-	rc = ioctl(backing_fd, BLKROSET, &ro);
-	if (rc < 0) {
-		fprintf(stderr, "failed to set %s rw\n", backing_bdev);
-		rc = -ENXIO;
-		goto out;
-	}
-
-	rc = ioctl(btt_fd, BLKROGET, &ro);
-	if (rc < 0) {
-		fprintf(stderr, "failed to verify that %s is now rw\n",
-			btt_bdev);
-		rc = -ENXIO;
-		goto out;
-	}
-
-	if (ro) {
-		fprintf(stderr, "setting %s rw did not set %s rw\n",
-				backing_bdev, btt_bdev);
-		rc = -ENXIO;
-		goto out;
-	}
-
-	backing_bdev = strdup(backing_bdev);
-	if (!backing_bdev) {
-		fprintf(stderr, "%s: failed to dup backing_bdev\n", devname);
-		goto out;
-	}
-
+	/* destroy btt device */
 	ndctl_btt_delete(found);
 
-	/* destroy btt */
-	backing_fd = open(backing_bdev, O_RDWR|O_DIRECT|O_EXCL);
-	if (backing_fd < 0) {
+	/* clear read-write, and enable raw mode */
+	ndctl_region_set_ro(region, 0);
+	ndctl_namespace_set_raw_mode(ndns, 1);
+	ndctl_namespace_enable(ndns);
+
+	/* destroy btt metadata */
+	sprintf(bdev, "/dev/%s", ndctl_namespace_get_block_device(ndns));
+	fd = open(bdev, O_RDWR|O_DIRECT|O_EXCL);
+	if (fd < 0) {
 		fprintf(stderr, "%s: failed to open %s to destroy btt\n",
-				devname, backing_bdev);
+				devname, bdev);
 		goto out;
 	}
 
 	memset(buf, 0, 4096);
-	rc = pwrite(backing_fd, buf, 4096, 4096);
+	rc = pwrite(fd, buf, 4096, 4096);
 	if (rc < 4096) {
 		rc = -ENXIO;
 		fprintf(stderr, "%s: failed to overwrite btt on %s\n",
-				devname, backing_bdev);
+				devname, bdev);
 	}
  out:
-	free((char *) backing_bdev);
-	if (backing_fd >= 0)
-		close(backing_fd);
-	if (btt_fd >= 0)
-		close(btt_fd);
+	ndctl_region_set_ro(region, namespace->ro);
+	ndctl_namespace_set_raw_mode(ndns, 0);
+	if (fd >= 0)
+		close(fd);
 
 	return rc;
 }
@@ -846,7 +877,7 @@  static int check_namespaces(struct ndctl_region *region,
 			close(fd);
 			fd = -1;
 
-			if (check_btt_create(bus, ndns, namespace->btt_settings) < 0) {
+			if (check_btt_create(region, ndns, namespace) < 0) {
 				fprintf(stderr, "%s: failed to create btt\n", devname);
 				rc = -ENXIO;
 				break;
@@ -976,17 +1007,18 @@  static int check_btt_supported_sectors(struct ndctl_btt *btt, struct btt *expect
 	return 0;
 }
 
-static int check_btts(struct ndctl_bus *bus, struct btt *btts, int n)
+static int check_btts(struct ndctl_region *region, struct btt **btts)
 {
+	struct btt *btt_s;
 	int i;
 
-	for (i = 0; i < n; i++) {
+	for (i = 0; (btt_s = btts[i]); i++) {
 		struct ndctl_btt *btt;
 		char devname[50];
 		uuid_t btt_uuid;
 		int rc;
 
-		btt = get_idle_btt(bus);
+		btt = get_idle_btt(region);
 		if (!btt) {
 			fprintf(stderr, "failed to find idle btt\n");
 			return -ENXIO;
@@ -994,24 +1026,24 @@  static int check_btts(struct ndctl_bus *bus, struct btt *btts, int n)
 		snprintf(devname, sizeof(devname), "btt%d",
 				ndctl_btt_get_id(btt));
 		ndctl_btt_get_uuid(btt, btt_uuid);
-		if (uuid_compare(btt_uuid, btts[i].uuid) != 0) {
+		if (uuid_compare(btt_uuid, btt_s->uuid) != 0) {
 			char expect[40], actual[40];
 
 			uuid_unparse(btt_uuid, actual);
-			uuid_unparse(btts[i].uuid, expect);
+			uuid_unparse(btt_s->uuid, expect);
 			fprintf(stderr, "%s: expected uuid: %s got: %s\n",
 					devname, expect, actual);
 			return -ENXIO;
 		}
-		if (ndctl_btt_get_num_sector_sizes(btt) != btts[i].num_sector_sizes) {
+		if (ndctl_btt_get_num_sector_sizes(btt) != btt_s->num_sector_sizes) {
 			fprintf(stderr, "%s: expected num_sector_sizes: %d got: %d\n",
-					devname, btts[i].num_sector_sizes,
+					devname, btt_s->num_sector_sizes,
 					ndctl_btt_get_num_sector_sizes(btt));
 		}
-		rc = check_btt_supported_sectors(btt, &btts[i]);
+		rc = check_btt_supported_sectors(btt, btt_s);
 		if (rc)
 			return rc;
-		if (btts[i].enabled && ndctl_btt_is_enabled(btt)) {
+		if (btt_s->enabled && ndctl_btt_is_enabled(btt)) {
 			fprintf(stderr, "%s: expected disabled by default\n",
 					devname);
 			return -ENXIO;
@@ -1289,10 +1321,6 @@  static int do_test0(struct ndctl_ctx *ctx)
 	if (rc)
 		return rc;
 
-	rc = check_btts(bus, btts0, ARRAY_SIZE(btts0));
-	if (rc)
-		return rc;
-
 	/* set regions back to their default state */
 	ndctl_region_foreach(bus, region)
 		ndctl_region_enable(region);
@@ -1312,10 +1340,6 @@  static int do_test1(struct ndctl_ctx *ctx)
 	if (rc)
 		return rc;
 
-	rc = check_btts(bus, btts1, ARRAY_SIZE(btts1));
-	if (rc)
-		return rc;
-
 	return check_regions(bus, regions1, ARRAY_SIZE(regions1));
 }
 
diff --git a/ndctl.h b/ndctl.h
index 2a04206e3bbe..e678a48cd976 100644
--- a/ndctl.h
+++ b/ndctl.h
@@ -181,7 +181,6 @@  static __inline__ const char *nvdimm_cmd_name(unsigned cmd)
 #define ND_DEVICE_NAMESPACE_IO 4    /* legacy persistent memory */
 #define ND_DEVICE_NAMESPACE_PMEM 5  /* PMEM namespace (may alias with BLK) */
 #define ND_DEVICE_NAMESPACE_BLK 6   /* BLK namespace (may alias with PMEM) */
-#define ND_DEVICE_BTT 7		    /* block-translation table device */
 
 enum nd_driver_flags {
 	ND_DRIVER_DIMM            = 1 << ND_DEVICE_DIMM,
@@ -190,7 +189,6 @@  enum nd_driver_flags {
 	ND_DRIVER_NAMESPACE_IO    = 1 << ND_DEVICE_NAMESPACE_IO,
 	ND_DRIVER_NAMESPACE_PMEM  = 1 << ND_DEVICE_NAMESPACE_PMEM,
 	ND_DRIVER_NAMESPACE_BLK   = 1 << ND_DEVICE_NAMESPACE_BLK,
-	ND_DRIVER_BTT		  = 1 << ND_DEVICE_BTT,
 };
 
 enum {