commit 6cb3772bdb92399319eb463e658ce62b692c669a
Author: Joe Thornber <ejt@redhat.com>
Date:   Mon Sep 24 14:48:16 2018 +0100

    [bm journal] Journalling version of block manager.
    
    Can be used to confirm we're crash proof with the thin_journal_check tool.

diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c
index 69dddeab124c..f7b11f270846 100644
--- a/drivers/md/dm-cache-metadata.c
+++ b/drivers/md/dm-cache-metadata.c
@@ -106,6 +106,8 @@ struct dm_cache_metadata {
 
 	unsigned version;
 	struct block_device *bdev;
+	struct block_device *journal_dev;
+
 	struct dm_block_manager *bm;
 	struct dm_space_map *metadata_sm;
 	struct dm_transaction_manager *tm;
@@ -281,7 +283,7 @@ static int __superblock_all_zeroes(struct dm_block_manager *bm, bool *result)
 		}
 	}
 
-	dm_bm_unlock(b);
+	dm_bm_unlock(bm, b);
 
 	return 0;
 }
@@ -504,12 +506,12 @@ static int __open_metadata(struct dm_cache_metadata *cmd)
 	dm_disk_bitset_init(cmd->tm, &cmd->discard_info);
 	sb_flags = le32_to_cpu(disk_super->flags);
 	cmd->clean_when_opened = test_bit(CLEAN_SHUTDOWN, &sb_flags);
-	dm_bm_unlock(sblock);
+	dm_bm_unlock(cmd->bm, sblock);
 
 	return 0;
 
 bad:
-	dm_bm_unlock(sblock);
+	dm_bm_unlock(cmd->bm, sblock);
 	return r;
 }
 
@@ -533,8 +535,9 @@ static int __create_persistent_data_objects(struct dm_cache_metadata *cmd,
 					    bool may_format_device)
 {
 	int r;
-	cmd->bm = dm_block_manager_create(cmd->bdev, DM_CACHE_METADATA_BLOCK_SIZE << SECTOR_SHIFT,
-					  CACHE_MAX_CONCURRENT_LOCKS);
+	cmd->bm = dm_block_manager_create_with_journal(cmd->bdev,
+                                          DM_CACHE_METADATA_BLOCK_SIZE << SECTOR_SHIFT,
+					  CACHE_MAX_CONCURRENT_LOCKS, cmd->journal_dev);
 	if (IS_ERR(cmd->bm)) {
 		DMERR("could not create block manager");
 		return PTR_ERR(cmd->bm);
@@ -621,9 +624,8 @@ static int __begin_transaction_flags(struct dm_cache_metadata *cmd,
 	disk_super = dm_block_data(sblock);
 	update_flags(disk_super, mutator);
 	read_superblock_fields(cmd, disk_super);
-	dm_bm_unlock(sblock);
 
-	return dm_bm_flush(cmd->bm);
+	return dm_bm_flush_and_unlock(cmd->bm, sblock);
 }
 
 static int __begin_transaction(struct dm_cache_metadata *cmd)
@@ -642,7 +644,7 @@ static int __begin_transaction(struct dm_cache_metadata *cmd)
 
 	disk_super = dm_block_data(sblock);
 	read_superblock_fields(cmd, disk_super);
-	dm_bm_unlock(sblock);
+	dm_bm_unlock(cmd->bm, sblock);
 
 	return 0;
 }
@@ -1775,7 +1777,7 @@ int dm_cache_metadata_set_needs_check(struct dm_cache_metadata *cmd)
 	disk_super = dm_block_data(sblock);
 	disk_super->flags = cpu_to_le32(cmd->flags);
 
-	dm_bm_unlock(sblock);
+	dm_bm_unlock(cmd->bm, sblock);
 
 out:
 	WRITE_UNLOCK(cmd);
diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c
index 8e48920a3ffa..4aad158a58e8 100644
--- a/drivers/md/dm-era-target.c
+++ b/drivers/md/dm-era-target.c
@@ -342,7 +342,7 @@ static int superblock_all_zeroes(struct dm_block_manager *bm, bool *result)
 		}
 	}
 
-	dm_bm_unlock(b);
+	dm_bm_unlock(bm, b);
 
 	return 0;
 }
@@ -583,12 +583,12 @@ static int open_metadata(struct era_metadata *md)
 	md->metadata_snap = le64_to_cpu(disk->metadata_snap);
 	md->archived_writesets = true;
 
-	dm_bm_unlock(sblock);
+	dm_bm_unlock(md->bm, sblock);
 
 	return 0;
 
 bad:
-	dm_bm_unlock(sblock);
+	dm_bm_unlock(md->bm, sblock);
 	return r;
 }
 
diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c
index 72142021b5c9..8420b67b0e51 100644
--- a/drivers/md/dm-thin-metadata.c
+++ b/drivers/md/dm-thin-metadata.c
@@ -146,6 +146,8 @@ struct dm_pool_metadata {
 	struct hlist_node hash;
 
 	struct block_device *bdev;
+	struct block_device *journal_dev;
+
 	struct dm_block_manager *bm;
 	struct dm_space_map *metadata_sm;
 	struct dm_space_map *data_sm;
@@ -399,7 +401,7 @@ static int __superblock_all_zeroes(struct dm_block_manager *bm, int *result)
 		}
 	}
 
-	dm_bm_unlock(b);
+	dm_bm_unlock(bm, b);
 
 	return 0;
 }
@@ -655,7 +657,7 @@ static int __open_metadata(struct dm_pool_metadata *pmd)
 	}
 
 	__setup_btree_details(pmd);
-	dm_bm_unlock(sblock);
+	dm_bm_unlock(pmd->bm, sblock);
 
 	return 0;
 
@@ -665,7 +667,7 @@ static int __open_metadata(struct dm_pool_metadata *pmd)
 	dm_tm_destroy(pmd->tm);
 	dm_sm_destroy(pmd->metadata_sm);
 bad_unlock_sblock:
-	dm_bm_unlock(sblock);
+	dm_bm_unlock(pmd->bm, sblock);
 
 	return r;
 }
@@ -688,8 +690,18 @@ static int __create_persistent_data_objects(struct dm_pool_metadata *pmd, bool f
 {
 	int r;
 
-	pmd->bm = dm_block_manager_create(pmd->bdev, THIN_METADATA_BLOCK_SIZE << SECTOR_SHIFT,
-					  THIN_MAX_CONCURRENT_LOCKS);
+	pr_alert("pmd->journal_dev = %p\n", pmd->journal_dev);
+	if (pmd->journal_dev)
+		pmd->bm = dm_block_manager_create_with_journal(
+				  pmd->bdev,
+			          THIN_METADATA_BLOCK_SIZE << SECTOR_SHIFT,
+				  THIN_MAX_CONCURRENT_LOCKS,
+				  pmd->journal_dev);
+	else
+		pmd->bm = dm_block_manager_create(pmd->bdev,
+                                                  THIN_METADATA_BLOCK_SIZE << SECTOR_SHIFT,
+                                                  THIN_MAX_CONCURRENT_LOCKS);
+
 	if (IS_ERR(pmd->bm)) {
 		DMERR("could not create block manager");
 		return PTR_ERR(pmd->bm);
@@ -734,7 +746,7 @@ static int __begin_transaction(struct dm_pool_metadata *pmd)
 	pmd->flags = le32_to_cpu(disk_super->flags);
 	pmd->data_block_size = le32_to_cpu(disk_super->data_block_size);
 
-	dm_bm_unlock(sblock);
+	dm_bm_unlock(pmd->bm, sblock);
 	return 0;
 }
 
@@ -818,7 +830,8 @@ static int __commit_transaction(struct dm_pool_metadata *pmd)
 
 struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,
 					       sector_t data_block_size,
-					       bool format_device)
+					       bool format_device,
+                                               struct block_device *journal)
 {
 	int r;
 	struct dm_pool_metadata *pmd;
@@ -834,6 +847,7 @@ struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,
 	INIT_LIST_HEAD(&pmd->thin_devices);
 	pmd->fail_io = false;
 	pmd->bdev = bdev;
+	pmd->journal_dev = journal;
 	pmd->data_block_size = data_block_size;
 
 	r = __create_persistent_data_objects(pmd, format_device);
@@ -1253,7 +1267,7 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd)
 
 	disk_super = dm_block_data(sblock);
 	disk_super->held_root = cpu_to_le64(held_root);
-	dm_bm_unlock(sblock);
+	dm_bm_unlock(pmd->bm, sblock);
 	return 0;
 }
 
@@ -1284,7 +1298,7 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd)
 	held_root = le64_to_cpu(disk_super->held_root);
 	disk_super->held_root = cpu_to_le64(0);
 
-	dm_bm_unlock(sblock);
+	dm_bm_unlock(pmd->bm, sblock);
 
 	if (!held_root) {
 		DMWARN("No pool metadata snapshot found: nothing to release.");
@@ -1332,7 +1346,7 @@ static int __get_metadata_snap(struct dm_pool_metadata *pmd,
 	disk_super = dm_block_data(sblock);
 	*result = le64_to_cpu(disk_super->held_root);
 
-	dm_bm_unlock(sblock);
+	dm_bm_unlock(pmd->bm, sblock);
 
 	return 0;
 }
@@ -1790,6 +1804,10 @@ int dm_pool_abort_metadata(struct dm_pool_metadata *pmd)
 
 	__set_abort_with_changes_flags(pmd);
 	__destroy_persistent_data_objects(pmd);
+
+	// FIXME: hack to avoid writing code for reopening the journal
+	BUG();
+
 	r = __create_persistent_data_objects(pmd, false);
 	if (r)
 		pmd->fail_io = true;
@@ -1985,7 +2003,7 @@ int dm_pool_metadata_set_needs_check(struct dm_pool_metadata *pmd)
 	disk_super = dm_block_data(sblock);
 	disk_super->flags = cpu_to_le32(pmd->flags);
 
-	dm_bm_unlock(sblock);
+	dm_bm_unlock(pmd->bm, sblock);
 out:
 	up_write(&pmd->root_lock);
 	return r;
diff --git a/drivers/md/dm-thin-metadata.h b/drivers/md/dm-thin-metadata.h
index 35e954ea20a9..6bd01c74e925 100644
--- a/drivers/md/dm-thin-metadata.h
+++ b/drivers/md/dm-thin-metadata.h
@@ -43,7 +43,8 @@ typedef uint64_t dm_thin_id;
  */
 struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,
 					       sector_t data_block_size,
-					       bool format_device);
+					       bool format_device,
+                                               struct block_device *journal);
 
 int dm_pool_metadata_close(struct dm_pool_metadata *pmd);
 
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index 7bd60a150f8f..66f03447a05e 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -8,6 +8,7 @@
 #include "dm-bio-prison-v1.h"
 #include "dm.h"
 
+#include <linux/ctype.h>
 #include <linux/device-mapper.h>
 #include <linux/dm-io.h>
 #include <linux/dm-kcopyd.h>
@@ -34,6 +35,10 @@
 
 static unsigned no_space_timeout_secs = NO_SPACE_TIMEOUT_SECS;
 
+static char *journal_name = NULL;
+module_param_named(block_manager_journal, journal_name, charp, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(block_manager_journal, "Device to recieve the block manager journal (used for debugging)");
+
 DECLARE_DM_KCOPYD_THROTTLE_WITH_MODULE_PARM(snapshot_copy_throttle,
 		"A percentage of time allocated for copy on write");
 
@@ -287,6 +292,7 @@ struct pool_c {
 	struct pool *pool;
 	struct dm_dev *data_dev;
 	struct dm_dev *metadata_dev;
+	struct dm_dev *journal_dev;
 	struct dm_target_callbacks callbacks;
 
 	dm_block_t low_water_blocks;
@@ -2839,6 +2845,7 @@ static struct kmem_cache *_new_mapping_cache;
 
 static struct pool *pool_create(struct mapped_device *pool_md,
 				struct block_device *metadata_dev,
+                                struct block_device *journal_dev,
 				unsigned long block_size,
 				int read_only, char **error)
 {
@@ -2848,7 +2855,8 @@ static struct pool *pool_create(struct mapped_device *pool_md,
 	struct dm_pool_metadata *pmd;
 	bool format_device = read_only ? false : true;
 
-	pmd = dm_pool_metadata_open(metadata_dev, block_size, format_device);
+	pr_alert("passing journal_dev = %p\n", journal_dev);
+	pmd = dm_pool_metadata_open(metadata_dev, block_size, format_device, journal_dev);
 	if (IS_ERR(pmd)) {
 		*error = "Error creating metadata object";
 		return (struct pool *)pmd;
@@ -2986,6 +2994,7 @@ static void __pool_dec(struct pool *pool)
 
 static struct pool *__pool_find(struct mapped_device *pool_md,
 				struct block_device *metadata_dev,
+                                struct block_device *journal_dev,
 				unsigned long block_size, int read_only,
 				char **error, int *created)
 {
@@ -3008,7 +3017,7 @@ static struct pool *__pool_find(struct mapped_device *pool_md,
 			__pool_inc(pool);
 
 		} else {
-			pool = pool_create(pool_md, metadata_dev, block_size, read_only, error);
+			pool = pool_create(pool_md, metadata_dev, journal_dev, block_size, read_only, error);
 			*created = 1;
 		}
 	}
@@ -3029,6 +3038,7 @@ static void pool_dtr(struct dm_target *ti)
 	__pool_dec(pt->pool);
 	dm_put_device(ti, pt->metadata_dev);
 	dm_put_device(ti, pt->data_dev);
+	dm_put_device(ti, pt->journal_dev);
 	kfree(pt);
 
 	mutex_unlock(&dm_thin_pool_table.mutex);
@@ -3145,6 +3155,14 @@ static dm_block_t calc_metadata_threshold(struct pool_c *pt)
 	return min((dm_block_t)1024ULL /* 4M */, quarter);
 }
 
+static void normalise_journal_name_(const char *name, char *buffer, size_t len)
+{
+	while (*name && !isspace(*name) && --len)
+		*buffer++ = *name++;
+
+	*buffer = '\0';
+}
+
 /*
  * thin-pool <metadata dev> <data dev>
  *	     <data block size (sectors)>
@@ -3169,6 +3187,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
 	unsigned long block_size;
 	dm_block_t low_water_blocks;
 	struct dm_dev *metadata_dev;
+	struct dm_dev *journal_dev = NULL;
 	fmode_t metadata_mode;
 
 	/*
@@ -3230,7 +3249,21 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
 		goto out;
 	}
 
-	pool = __pool_find(dm_table_get_md(ti->table), metadata_dev->bdev,
+	if (journal_name) {
+		char buffer[64];
+		normalise_journal_name_(journal_name, buffer, sizeof(buffer));
+		if (buffer[0]) {
+			r = dm_get_device(ti, buffer, FMODE_READ | FMODE_WRITE, &journal_dev);
+			if (r) {
+				pr_alert("couldn't open journal device '%s'", buffer);
+				journal_dev = NULL;
+			} else {
+				pr_alert("opened journal device '%s'", buffer);
+			}
+		}
+	}
+
+	pool = __pool_find(dm_table_get_md(ti->table), metadata_dev->bdev, journal_dev ? journal_dev->bdev : NULL,
 			   block_size, pf.mode == PM_READ_ONLY, &ti->error, &pool_created);
 	if (IS_ERR(pool)) {
 		r = PTR_ERR(pool);
@@ -3253,6 +3286,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
 	pt->ti = ti;
 	pt->metadata_dev = metadata_dev;
 	pt->data_dev = data_dev;
+	pt->journal_dev = journal_dev;
 	pt->low_water_blocks = low_water_blocks;
 	pt->adjusted_pf = pt->requested_pf = pf;
 	ti->num_flush_bios = 1;
@@ -4400,6 +4434,7 @@ module_exit(dm_thin_exit);
 module_param_named(no_space_timeout, no_space_timeout_secs, uint, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(no_space_timeout, "Out of data space queue IO timeout in seconds");
 
+
 MODULE_DESCRIPTION(DM_NAME " thin provisioning target");
 MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
 MODULE_LICENSE("GPL");
diff --git a/drivers/md/persistent-data/dm-block-manager.c b/drivers/md/persistent-data/dm-block-manager.c
index 492a3f8ac119..b1f773cb037f 100644
--- a/drivers/md/persistent-data/dm-block-manager.c
+++ b/drivers/md/persistent-data/dm-block-manager.c
@@ -291,6 +291,7 @@ static int bl_down_write(struct block_lock *lock)
 static void bl_up_write(struct block_lock *lock)
 {
 	spin_lock(&lock->lock);
+	BUG_ON(lock->count != -1);
 	__del_holder(lock, current);
 	lock->count = 0;
 	if (!list_empty(&lock->waiters))
@@ -343,13 +344,16 @@ void *dm_block_data(struct dm_block *b)
 }
 EXPORT_SYMBOL_GPL(dm_block_data);
 
+// FIXME: test to see if it's worth reducing this
+#define CHECKSUM_SIZE 32
+#define NR_CHECKSUMS (4096 / CHECKSUM_SIZE)
+
 struct buffer_aux {
 	struct dm_block_validator *validator;
+	struct block_lock lock;
 	int write_locked;
 
-#ifdef CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING
-	struct block_lock lock;
-#endif
+	uint32_t checksums[NR_CHECKSUMS];
 };
 
 static void dm_block_manager_alloc_callback(struct dm_buffer *buf)
@@ -368,69 +372,43 @@ static void dm_block_manager_write_callback(struct dm_buffer *buf)
 	}
 }
 
-/*----------------------------------------------------------------
- * Public interface
- *--------------------------------------------------------------*/
-struct dm_block_manager {
+/*--------------------------------------------------------------*/
+
+struct block_manager {
+	struct dm_block_manager bm;
 	struct dm_bufio_client *bufio;
 	bool read_only:1;
 };
 
-struct dm_block_manager *dm_block_manager_create(struct block_device *bdev,
-						 unsigned block_size,
-						 unsigned max_held_per_thread)
-{
-	int r;
-	struct dm_block_manager *bm;
-
-	bm = kmalloc(sizeof(*bm), GFP_KERNEL);
-	if (!bm) {
-		r = -ENOMEM;
-		goto bad;
-	}
-
-	bm->bufio = dm_bufio_client_create(bdev, block_size, max_held_per_thread,
-					   sizeof(struct buffer_aux),
-					   dm_block_manager_alloc_callback,
-					   dm_block_manager_write_callback);
-	if (IS_ERR(bm->bufio)) {
-		r = PTR_ERR(bm->bufio);
-		kfree(bm);
-		goto bad;
-	}
-
-	bm->read_only = false;
-
-	return bm;
+#define DECLARE_BM struct block_manager *bm = container_of(dbm, struct block_manager, bm)
 
-bad:
-	return ERR_PTR(r);
-}
-EXPORT_SYMBOL_GPL(dm_block_manager_create);
-
-void dm_block_manager_destroy(struct dm_block_manager *bm)
+static void _destroy(struct dm_block_manager *dbm)
 {
+	DECLARE_BM;
+
 	dm_bufio_client_destroy(bm->bufio);
 	kfree(bm);
 }
-EXPORT_SYMBOL_GPL(dm_block_manager_destroy);
 
-unsigned dm_bm_block_size(struct dm_block_manager *bm)
+static unsigned _block_size(struct dm_block_manager *dbm)
 {
+	DECLARE_BM;
 	return dm_bufio_get_block_size(bm->bufio);
 }
-EXPORT_SYMBOL_GPL(dm_bm_block_size);
 
-dm_block_t dm_bm_nr_blocks(struct dm_block_manager *bm)
+static dm_block_t _nr_blocks(struct dm_block_manager *dbm)
 {
+	DECLARE_BM;
 	return dm_bufio_get_device_size(bm->bufio);
 }
 
-static int dm_bm_validate_buffer(struct dm_block_manager *bm,
-				 struct dm_buffer *buf,
-				 struct buffer_aux *aux,
-				 struct dm_block_validator *v)
+static int _validate_buffer(struct dm_block_manager *dbm,
+			    struct dm_buffer *buf,
+			    struct buffer_aux *aux,
+			    struct dm_block_validator *v)
 {
+	DECLARE_BM;
+
 	if (unlikely(!aux->validator)) {
 		int r;
 		if (!v)
@@ -453,10 +431,18 @@ static int dm_bm_validate_buffer(struct dm_block_manager *bm,
 
 	return 0;
 }
-int dm_bm_read_lock(struct dm_block_manager *bm, dm_block_t b,
-		    struct dm_block_validator *v,
-		    struct dm_block **result)
+
+static void _prefetch(struct dm_block_manager *dbm, dm_block_t b)
 {
+	DECLARE_BM;
+	dm_bufio_prefetch(bm->bufio, b, 1);
+}
+
+static int _read_lock(struct dm_block_manager *dbm, dm_block_t b,
+		      struct dm_block_validator *v,
+		      struct dm_block **result)
+{
+	DECLARE_BM;
 	struct buffer_aux *aux;
 	void *p;
 	int r;
@@ -475,7 +461,7 @@ int dm_bm_read_lock(struct dm_block_manager *bm, dm_block_t b,
 
 	aux->write_locked = 0;
 
-	r = dm_bm_validate_buffer(bm, to_buffer(*result), aux, v);
+	r = dm_bm_validate_buffer(dbm, to_buffer(*result), aux, v);
 	if (unlikely(r)) {
 		bl_up_read(&aux->lock);
 		dm_bufio_release(to_buffer(*result));
@@ -484,12 +470,12 @@ int dm_bm_read_lock(struct dm_block_manager *bm, dm_block_t b,
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(dm_bm_read_lock);
 
-int dm_bm_write_lock(struct dm_block_manager *bm,
-		     dm_block_t b, struct dm_block_validator *v,
-		     struct dm_block **result)
+static int _write_lock(struct dm_block_manager *dbm,
+		       dm_block_t b, struct dm_block_validator *v,
+		       struct dm_block **result)
 {
+	DECLARE_BM;
 	struct buffer_aux *aux;
 	void *p;
 	int r;
@@ -511,7 +497,7 @@ int dm_bm_write_lock(struct dm_block_manager *bm,
 
 	aux->write_locked = 1;
 
-	r = dm_bm_validate_buffer(bm, to_buffer(*result), aux, v);
+	r = dm_bm_validate_buffer(dbm, to_buffer(*result), aux, v);
 	if (unlikely(r)) {
 		bl_up_write(&aux->lock);
 		dm_bufio_release(to_buffer(*result));
@@ -520,12 +506,12 @@ int dm_bm_write_lock(struct dm_block_manager *bm,
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(dm_bm_write_lock);
 
-int dm_bm_read_try_lock(struct dm_block_manager *bm,
-			dm_block_t b, struct dm_block_validator *v,
-			struct dm_block **result)
+static int _read_try_lock(struct dm_block_manager *dbm,
+			  dm_block_t b, struct dm_block_validator *v,
+			  struct dm_block **result)
 {
+	DECLARE_BM;
 	struct buffer_aux *aux;
 	void *p;
 	int r;
@@ -545,7 +531,7 @@ int dm_bm_read_try_lock(struct dm_block_manager *bm,
 	}
 	aux->write_locked = 0;
 
-	r = dm_bm_validate_buffer(bm, to_buffer(*result), aux, v);
+	r = dm_bm_validate_buffer(dbm, to_buffer(*result), aux, v);
 	if (unlikely(r)) {
 		bl_up_read(&aux->lock);
 		dm_bufio_release(to_buffer(*result));
@@ -555,10 +541,11 @@ int dm_bm_read_try_lock(struct dm_block_manager *bm,
 	return 0;
 }
 
-int dm_bm_write_lock_zero(struct dm_block_manager *bm,
-			  dm_block_t b, struct dm_block_validator *v,
-			  struct dm_block **result)
+static int _write_lock_zero(struct dm_block_manager *dbm,
+			    dm_block_t b, struct dm_block_validator *v,
+			    struct dm_block **result)
 {
+	DECLARE_BM;
 	int r;
 	struct buffer_aux *aux;
 	void *p;
@@ -570,7 +557,7 @@ int dm_bm_write_lock_zero(struct dm_block_manager *bm,
 	if (unlikely(IS_ERR(p)))
 		return PTR_ERR(p);
 
-	memset(p, 0, dm_bm_block_size(bm));
+	memset(p, 0, dm_bm_block_size(dbm));
 
 	aux = dm_bufio_get_aux_data(to_buffer(*result));
 	r = bl_down_write(&aux->lock);
@@ -584,9 +571,8 @@ int dm_bm_write_lock_zero(struct dm_block_manager *bm,
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(dm_bm_write_lock_zero);
 
-void dm_bm_unlock(struct dm_block *b)
+static void _unlock(struct dm_block_manager *bm, struct dm_block *b)
 {
 	struct buffer_aux *aux;
 	aux = dm_bufio_get_aux_data(to_buffer(b));
@@ -599,39 +585,579 @@ void dm_bm_unlock(struct dm_block *b)
 
 	dm_bufio_release(to_buffer(b));
 }
-EXPORT_SYMBOL_GPL(dm_bm_unlock);
 
-int dm_bm_flush(struct dm_block_manager *bm)
+static int _flush(struct dm_block_manager *dbm)
 {
+	DECLARE_BM;
+
 	if (bm->read_only)
 		return -EPERM;
 
 	return dm_bufio_write_dirty_buffers(bm->bufio);
 }
-EXPORT_SYMBOL_GPL(dm_bm_flush);
 
-void dm_bm_prefetch(struct dm_block_manager *bm, dm_block_t b)
+static int _flush_and_unlock(struct dm_block_manager *dbm,
+			     struct dm_block *superblock)
 {
-	dm_bufio_prefetch(bm->bufio, b, 1);
+	DECLARE_BM;
+	int r;
+
+	if (bm->read_only)
+		return -EPERM;
+
+	r = dm_bufio_write_dirty_buffers(bm->bufio);
+	if (unlikely(r)) {
+		dm_bm_unlock(dbm, superblock);
+		return r;
+	}
+
+	dm_bm_unlock(dbm, superblock);
+
+	return dm_bufio_write_dirty_buffers(bm->bufio);
 }
 
-bool dm_bm_is_read_only(struct dm_block_manager *bm)
+static bool _is_read_only(struct dm_block_manager *dbm)
 {
+	DECLARE_BM;
 	return bm->read_only;
 }
-EXPORT_SYMBOL_GPL(dm_bm_is_read_only);
 
-void dm_bm_set_read_only(struct dm_block_manager *bm)
+static void _set_read_only(struct dm_block_manager *dbm)
 {
+	DECLARE_BM;
 	bm->read_only = true;
 }
-EXPORT_SYMBOL_GPL(dm_bm_set_read_only);
 
-void dm_bm_set_read_write(struct dm_block_manager *bm)
+static void _set_read_write(struct dm_block_manager *dbm)
 {
+	DECLARE_BM;
 	bm->read_only = false;
 }
-EXPORT_SYMBOL_GPL(dm_bm_set_read_write);
+#undef DECLARE_BM
+
+static void _check_bm_filled_out(struct dm_block_manager *dbm)
+{
+	BUG_ON(!dbm->destroy);
+	BUG_ON(!dbm->block_size);
+	BUG_ON(!dbm->nr_blocks);
+	BUG_ON(!dbm->validate_buffer);
+	BUG_ON(!dbm->prefetch);
+	BUG_ON(!dbm->read_lock_);
+	BUG_ON(!dbm->write_lock_);
+	BUG_ON(!dbm->read_try_lock_);
+	BUG_ON(!dbm->write_lock_zero);
+	BUG_ON(!dbm->unlock);
+	BUG_ON(!dbm->flush);
+	BUG_ON(!dbm->flush_and_unlock);
+	BUG_ON(!dbm->is_read_only);
+	BUG_ON(!dbm->set_read_only);
+	BUG_ON(!dbm->set_read_write);
+}
+
+struct dm_block_manager *dm_block_manager_create(struct block_device *bdev,
+						 unsigned block_size,
+						 unsigned max_held_per_thread)
+{
+	int r;
+	struct block_manager *bm;
+
+	bm = kmalloc(sizeof(*bm), GFP_KERNEL);
+	if (!bm) {
+		r = -ENOMEM;
+		goto bad;
+	}
+
+	bm->bm.destroy = _destroy;
+	bm->bm.block_size = _block_size;
+	bm->bm.nr_blocks = _nr_blocks;
+	bm->bm.validate_buffer = _validate_buffer;
+	bm->bm.prefetch = _prefetch;
+	bm->bm.read_lock_ = _read_lock;
+	bm->bm.write_lock_ = _write_lock;
+	bm->bm.read_try_lock_ = _read_try_lock;
+	bm->bm.write_lock_zero = _write_lock_zero;
+	bm->bm.unlock = _unlock;
+	bm->bm.flush = _flush;
+	bm->bm.flush_and_unlock = _flush_and_unlock;
+	bm->bm.is_read_only = _is_read_only;
+	bm->bm.set_read_only = _set_read_only;
+	bm->bm.set_read_write = _set_read_write;
+
+        bm->bufio = dm_bufio_client_create(bdev, block_size, max_held_per_thread,
+                                           sizeof(struct buffer_aux),
+                                           dm_block_manager_alloc_callback,
+                                           dm_block_manager_write_callback);
+
+	if (IS_ERR(bm->bufio)) {
+		r = PTR_ERR(bm->bufio);
+		kfree(bm);
+		goto bad;
+	}
+
+	bm->read_only = false;
+
+	_check_bm_filled_out(&bm->bm);
+
+	pr_alert("created real at %p\n", &bm->bm);
+	return &bm->bm;
+
+bad:
+	return ERR_PTR(r);
+}
+EXPORT_SYMBOL_GPL(dm_block_manager_create);
+
+/*----------------------------------------------------------------*/
+
+enum msg_type {
+	MT_OPEN_JOURNAL,
+	MT_CLOSE_JOURNAL,
+
+	MT_READ_LOCK,
+	MT_WRITE_LOCK,
+	MT_ZERO_LOCK,
+	MT_TRY_READ_LOCK,
+	MT_UNLOCK,
+	MT_VERIFY,
+	MT_PREPARE,
+	MT_FLUSH,
+	MT_FLUSH_AND_UNLOCK,
+	MT_PREFETCH,
+	MT_SET_READ_ONLY,
+	MT_SET_READ_WRITE,
+};
+
+struct byte_stream {
+	spinlock_t lock;
+	struct block_device *dev;
+	struct dm_bufio_client *cache;
+
+	uint64_t block_index;
+	struct dm_buffer *current_buffer;
+	void *current_data;
+	uint8_t *out_begin;
+	uint8_t *out_end;
+};
+
+#define JOURNAL_BLOCK_SIZE (1024 * 1024 * 1024)
+
+// We just BUG if there's an error; this is developement code.
+static void _prep_block(struct byte_stream *bs, uint64_t block)
+{
+	bs->current_data = dm_bufio_new(bs->cache, block, &bs->current_buffer);
+	BUG_ON(!bs->current_data);
+	bs->out_begin = bs->current_data;
+	bs->out_end = bs->current_data + JOURNAL_BLOCK_SIZE;
+}
+
+static void _commit_block(struct byte_stream *bs)
+{
+	dm_bufio_mark_buffer_dirty(bs->current_buffer);
+	dm_bufio_release(bs->current_buffer);
+}
+
+static struct byte_stream *_bs_open(struct block_device *dev)
+{
+	struct byte_stream *bs = kzalloc(sizeof(*bs), GFP_KERNEL);
+
+	if (!bs)
+		return NULL;
+
+	spin_lock_init(&bs->lock);
+	bs->dev = dev;
+	bs->cache = dm_bufio_client_create(dev, JOURNAL_BLOCK_SIZE,
+		       			   2, 0, NULL, NULL);
+	if (!bs->cache) {
+		kfree(bs);
+		return NULL;
+	}
+
+	_prep_block(bs, 0);
+	
+	return bs;
+}
+
+static void _bs_close(struct byte_stream *bs)
+{
+	_commit_block(bs);
+	dm_bufio_client_destroy(bs->cache);
+	kfree(bs);
+}
+
+static size_t _cpy_bytes(struct byte_stream *bs, uint8_t *b, uint8_t *e)
+{
+	size_t len = min(e - b, bs->out_end - bs->out_begin);
+	memcpy(bs->out_begin, b, len);
+	bs->out_begin += len;
+	return len;
+}
+
+static bool _no_space(struct byte_stream *bs)
+{
+	return bs->out_begin == bs->out_end;
+}
+
+static void _push_bytes(struct byte_stream *bs, uint8_t *b, uint8_t *e)
+{
+	while (b != e) {
+		if (_no_space(bs)) {
+			pr_alert("push_bytes: out of space\n");
+			_commit_block(bs);
+			_prep_block(bs, bs->block_index + 1);
+			pr_alert("done");
+		}
+
+		b += _cpy_bytes(bs, b, e);
+	}
+}
+
+static void _push_u8(struct byte_stream *bs, uint8_t v)
+{
+	return _push_bytes(bs, &v, &v + 1);
+}
+
+static void _push_u16(struct byte_stream *bs, uint16_t v)
+{
+	return _push_bytes(bs, (uint8_t *) &v, (uint8_t *) (&v + 1));
+}
+
+static void _push_u64(struct byte_stream *bs, uint64_t v)
+{
+	return _push_bytes(bs, (uint8_t *) &v, (uint8_t *) (&v + 1));
+}
+
+static void _push_msg(struct byte_stream *bs, enum msg_type t, int err)
+{
+	uint8_t b = t << 1;
+	b |= err ? 0 : 1;
+	_push_u8(bs, b);
+}
+
+/*----------------------------------------------------------------*/
+
+static u32 _cs_chunk(const void *data, unsigned chunk)
+{
+	return crc32c(0, data + (chunk * CHECKSUM_SIZE), CHECKSUM_SIZE);
+}
+
+static void _calc_checksums(struct dm_block *b)
+{
+	unsigned i;
+	const void *data = dm_block_data(b);
+	struct buffer_aux *aux = dm_bufio_get_aux_data((struct dm_buffer *) b);
+
+	for (i = 0; i < NR_CHECKSUMS; i++)
+		aux->checksums[i] = _cs_chunk(data, i);
+}
+
+static void _write_delta(struct byte_stream *bs, struct dm_block *b, unsigned chunk)
+{
+	uint8_t *begin = dm_block_data(b) + (chunk * CHECKSUM_SIZE);
+	uint8_t *end = begin + CHECKSUM_SIZE;
+
+	_push_u16(bs, chunk);
+	_push_bytes(bs, begin, end);
+}
+
+static void _terminate_deltas(struct byte_stream *bs)
+{
+	BUG_ON(NR_CHECKSUMS > 0xff);
+	_push_u16(bs, 0xffff);
+}
+
+static void _push_deltas(struct byte_stream *bs, struct dm_block *b)
+{
+	unsigned i;
+	uint32_t sum;
+	const void *data = dm_block_data(b);
+	struct buffer_aux *aux = dm_bufio_get_aux_data((struct dm_buffer *) b);
+
+	if (aux->write_locked)
+		for (i = 0; i < NR_CHECKSUMS; i++) {
+			sum = _cs_chunk(data, i);
+			if (sum != aux->checksums[i])
+				_write_delta(bs, b, i);
+		}
+
+	_terminate_deltas(bs);
+}
+
+/*----------------------------------------------------------------*/
+
+struct journal_bm {
+	struct dm_block_manager bm;
+	struct dm_block_manager *inner;
+	struct byte_stream *out;
+};
+
+#define DECLARE_BM struct journal_bm *bm = container_of(dbm, struct journal_bm, bm)
+
+static void _j_destroy(struct dm_block_manager *dbm)
+{
+	DECLARE_BM;
+	_push_msg(bm->out, MT_CLOSE_JOURNAL, true);
+	_bs_close(bm->out);
+	bm->inner->destroy(bm->inner);
+	kfree(bm);
+}
+
+static unsigned _j_block_size(struct dm_block_manager *dbm)
+{
+	DECLARE_BM;
+	return bm->inner->block_size(bm->inner);
+}
+
+static dm_block_t _j_nr_blocks(struct dm_block_manager *dbm)
+{
+	DECLARE_BM;
+	return bm->inner->nr_blocks(bm->inner);
+}
+
+static int _j_validate_buffer(struct dm_block_manager *dbm,
+			    struct dm_buffer *buf,
+			    struct buffer_aux *aux,
+			    struct dm_block_validator *v)
+{
+	int r; 
+	DECLARE_BM;
+	unsigned long flags;
+
+	r = bm->inner->validate_buffer(bm->inner, buf, aux, v);
+
+	spin_lock_irqsave(&bm->out->lock, flags);
+	_push_msg(bm->out, MT_VERIFY, r);
+	_push_u64(bm->out, dm_bufio_get_block_number(buf));
+	spin_unlock_irqrestore(&bm->out->lock, flags);
+
+	return r;
+}
+
+static void _j_prefetch(struct dm_block_manager *dbm, dm_block_t b)
+{
+	DECLARE_BM;
+	bm->inner->prefetch(bm->inner, b);
+}
+
+static int _j_read_lock(struct dm_block_manager *dbm, dm_block_t b,
+		        struct dm_block_validator *v,
+		        struct dm_block **result)
+{
+	int r;
+	DECLARE_BM;
+	unsigned long flags;
+
+	r = bm->inner->read_lock_(bm->inner, b, v, result);
+
+	// No need to calculate checksums for a read lock
+	spin_lock_irqsave(&bm->out->lock, flags);
+	_push_msg(bm->out, MT_READ_LOCK, r);
+	_push_u64(bm->out, b);
+	spin_unlock_irqrestore(&bm->out->lock, flags);
+
+	return r;
+}
+
+static int _j_write_lock(struct dm_block_manager *dbm,
+		         dm_block_t b, struct dm_block_validator *v,
+		         struct dm_block **result)
+{
+	int r;
+	DECLARE_BM;
+	unsigned long flags;
+
+	r = bm->inner->write_lock_(bm->inner, b, v, result);
+	if (!r)
+		_calc_checksums(*result);
+
+	spin_lock_irqsave(&bm->out->lock, flags);
+	_push_msg(bm->out, MT_WRITE_LOCK, r);
+	_push_u64(bm->out, b);
+	spin_unlock_irqrestore(&bm->out->lock, flags);
+
+	return r;
+}
+
+static int _j_read_try_lock(struct dm_block_manager *dbm,
+			    dm_block_t b, struct dm_block_validator *v,
+			    struct dm_block **result)
+{
+	int r;
+	DECLARE_BM;
+	unsigned long flags;
+
+	r = bm->inner->read_try_lock_(bm->inner, b, v, result);
+
+	// try_read_lock is called from request context, so we mustn't trigger io.
+	// FIXME: work out a way to journal this!
+	spin_lock_irqsave(&bm->out->lock, flags);
+	_push_msg(bm->out, MT_TRY_READ_LOCK, r);
+	_push_u64(bm->out, b);
+	spin_unlock_irqrestore(&bm->out->lock, flags);
+
+	return r;
+}
+
+static int _j_write_lock_zero(struct dm_block_manager *dbm,
+			      dm_block_t b, struct dm_block_validator *v,
+			      struct dm_block **result)
+{
+	int r;
+	DECLARE_BM;
+	unsigned long flags;
+
+	r = bm->inner->write_lock_zero(bm->inner, b, v, result);
+	if (!r)
+		_calc_checksums(*result);
+
+	spin_lock_irqsave(&bm->out->lock, flags);
+	_push_msg(bm->out, MT_ZERO_LOCK, r);
+	_push_u64(bm->out, b);
+	spin_unlock_irqrestore(&bm->out->lock, flags);
+
+	return r;
+}
+
+static void _j_unlock(struct dm_block_manager *dbm, struct dm_block *b)
+{
+	DECLARE_BM;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bm->out->lock, flags);
+	_push_msg(bm->out, MT_UNLOCK, 0);
+	_push_u64(bm->out, dm_block_location(b));
+	_push_deltas(bm->out, b);
+	spin_unlock_irqrestore(&bm->out->lock, flags);
+
+	bm->inner->unlock(bm->inner, b);
+}
+
+static int _j_flush(struct dm_block_manager *dbm)
+{
+	int r;
+	DECLARE_BM;
+	unsigned long flags;
+
+	r = bm->inner->flush(bm->inner);
+	spin_lock_irqsave(&bm->out->lock, flags);
+	_push_msg(bm->out, MT_FLUSH, r);
+	spin_unlock_irqrestore(&bm->out->lock, flags);
+	return r;
+}
+
+static int _j_flush_and_unlock(struct dm_block_manager *dbm,
+			       struct dm_block *superblock)
+{
+	DECLARE_BM;
+	unsigned long flags;
+
+	pr_alert("flush_and_unlock\n");
+	spin_lock_irqsave(&bm->out->lock, flags);
+	_push_msg(bm->out, MT_FLUSH_AND_UNLOCK, 0);
+	_push_u64(bm->out, dm_block_location(superblock));
+	_push_deltas(bm->out, superblock);
+	spin_unlock_irqrestore(&bm->out->lock, flags);
+
+	return bm->inner->flush_and_unlock(bm->inner, superblock);
+}
+
+static bool _j_is_read_only(struct dm_block_manager *dbm)
+{
+	DECLARE_BM;
+
+	return bm->inner->is_read_only(bm->inner);
+}
+
+static void _j_set_read_only(struct dm_block_manager *dbm)
+{
+	DECLARE_BM;
+	unsigned long flags;
+
+	bm->inner->set_read_only(bm->inner);
+
+	spin_lock_irqsave(&bm->out->lock, flags);
+	_push_msg(bm->out, MT_SET_READ_ONLY, true);
+	spin_unlock_irqrestore(&bm->out->lock, flags);
+}
+
+static void _j_set_read_write(struct dm_block_manager *dbm)
+{
+	DECLARE_BM;
+	unsigned long flags;
+
+	bm->inner->set_read_write(bm->inner);
+
+	spin_lock_irqsave(&bm->out->lock, flags);
+	_push_msg(bm->out, MT_SET_READ_WRITE, true);
+	spin_unlock_irqrestore(&bm->out->lock, flags);
+}
+
+#undef DECLARE_BM
+
+static bool _unformatted_journal(struct byte_stream *bs)
+{
+	// The journal is unformatted if the first sector (512 bytes) is zeroed.
+	uint8_t buffer[64];
+
+	for (i = 0; i < 8; i++) {
+		_bs_
+	}
+
+	_bs_rewrind(bs);
+}
+
+struct dm_block_manager *dm_block_manager_create_with_journal(struct block_device *bdev,
+                                                              unsigned block_size,
+							      unsigned max_held_per_thread,
+							      struct block_device *jdev)
+{
+	struct journal_bm *jbm;
+	struct dm_block_manager *inner = dm_block_manager_create(bdev, block_size, max_held_per_thread);
+
+	if (IS_ERR(inner))
+		return inner;
+
+	jbm = kmalloc(sizeof(*jbm), GFP_KERNEL);
+	if (!jbm) {
+		inner->destroy(inner);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	jbm->out = _bs_open(jdev);
+	if (!jbm->out) {
+		inner->destroy(inner);
+		kfree(jbm);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	jbm->bm.destroy = _j_destroy;
+	jbm->bm.block_size = _j_block_size;
+	jbm->bm.nr_blocks = _j_nr_blocks;
+	jbm->bm.validate_buffer = _j_validate_buffer;
+	jbm->bm.prefetch = _j_prefetch;
+	jbm->bm.read_lock_ = _j_read_lock;
+	jbm->bm.write_lock_ = _j_write_lock;
+	jbm->bm.read_try_lock_ = _j_read_try_lock;
+	jbm->bm.write_lock_zero = _j_write_lock_zero;
+	jbm->bm.unlock = _j_unlock;
+	jbm->bm.flush = _j_flush;
+	jbm->bm.flush_and_unlock = _j_flush_and_unlock;
+	jbm->bm.is_read_only = _j_is_read_only;
+	jbm->bm.set_read_only = _j_set_read_only;
+	jbm->bm.set_read_write = _j_set_read_write;
+
+	_check_bm_filled_out(&jbm->bm);
+
+	jbm->inner = inner;
+
+	pr_alert("journalling block manager created\n");
+
+	_push_msg(jbm->out, MT_OPEN_JOURNAL, 0);
+	_push_u64(jbm->out, dm_bm_nr_blocks(inner));
+
+	return &jbm->bm;
+}
+EXPORT_SYMBOL_GPL(dm_block_manager_create_with_journal);
+
+/*----------------------------------------------------------------*/
 
 u32 dm_bm_checksum(const void *data, size_t len, u32 init_xor)
 {
@@ -645,4 +1171,5 @@ MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
 MODULE_DESCRIPTION("Immutable metadata library for dm");
 
+
 /*----------------------------------------------------------------*/
diff --git a/drivers/md/persistent-data/dm-block-manager.h b/drivers/md/persistent-data/dm-block-manager.h
index e728937f376a..adb55f6aceac 100644
--- a/drivers/md/persistent-data/dm-block-manager.h
+++ b/drivers/md/persistent-data/dm-block-manager.h
@@ -23,23 +23,8 @@ void *dm_block_data(struct dm_block *b);
 
 /*----------------------------------------------------------------*/
 
-/*
- * @name should be a unique identifier for the block manager, no longer
- * than 32 chars.
- *
- * @max_held_per_thread should be the maximum number of locks, read or
- * write, that an individual thread holds at any one time.
- */
-struct dm_block_manager;
-struct dm_block_manager *dm_block_manager_create(
-	struct block_device *bdev, unsigned block_size,
-	unsigned max_held_per_thread);
-void dm_block_manager_destroy(struct dm_block_manager *bm);
-
-unsigned dm_bm_block_size(struct dm_block_manager *bm);
-dm_block_t dm_bm_nr_blocks(struct dm_block_manager *bm);
-
-/*----------------------------------------------------------------*/
+struct dm_buffer;
+struct buffer_aux;
 
 /*
  * The validator allows the caller to verify newly-read data and modify
@@ -57,44 +42,141 @@ struct dm_block_validator {
 	int (*check)(struct dm_block_validator *v, struct dm_block *b, size_t block_size);
 };
 
+struct dm_block_manager {
+	void (*destroy)(struct dm_block_manager *bm);
+	unsigned (*block_size)(struct dm_block_manager *bm);
+	dm_block_t (*nr_blocks)(struct dm_block_manager *bm);
+	int (*validate_buffer)(struct dm_block_manager *bm,
+			       struct dm_buffer *buf,
+			       struct buffer_aux *aux,
+			       struct dm_block_validator *v);
+	void (*prefetch)(struct dm_block_manager *bm, dm_block_t b);
+	int (*read_lock_)(struct dm_block_manager *bm, dm_block_t b,
+			 struct dm_block_validator *v,
+			 struct dm_block **result);
+	int (*write_lock_)(struct dm_block_manager *bm,
+			  dm_block_t b, struct dm_block_validator *v,
+			  struct dm_block **result);
+	int (*read_try_lock_)(struct dm_block_manager *bm,
+			     dm_block_t b, struct dm_block_validator *v,
+			     struct dm_block **result);
+	int (*write_lock_zero)(struct dm_block_manager *bm,
+			       dm_block_t b, struct dm_block_validator *v,
+			       struct dm_block **result);
+	void (*unlock)(struct dm_block_manager *bm, struct dm_block *b);
+	int (*flush)(struct dm_block_manager *bm);
+	int (*flush_and_unlock)(struct dm_block_manager *bm,
+				struct dm_block *superblock);
+	bool (*is_read_only)(struct dm_block_manager *bm);
+	void (*set_read_only)(struct dm_block_manager *bm);
+	void (*set_read_write)(struct dm_block_manager *bm);
+};
+
+/*
+ * @name should be a unique identifier for the block manager, no longer
+ * than 32 chars.
+ *
+ * @max_held_per_thread should be the maximum number of locks, read or
+ * write, that an individual thread holds at any one time.
+ */
+
+struct dm_block_manager *dm_block_manager_create(
+	struct block_device *bdev, unsigned block_size,
+	unsigned max_held_per_thread);
+
+struct dm_block_manager *dm_block_manager_create_with_journal(
+	struct block_device *bdev, unsigned block_size,
+	unsigned max_held_per_thread,
+	struct block_device *jdev);
+
 /*----------------------------------------------------------------*/
 
+static inline void dm_block_manager_destroy(struct dm_block_manager *bm)
+{
+	bm->destroy(bm);
+}
+
+static inline unsigned dm_bm_block_size(struct dm_block_manager *bm)
+{
+	return bm->block_size(bm);
+}
+
+static inline dm_block_t dm_bm_nr_blocks(struct dm_block_manager *bm)
+{
+	return bm->nr_blocks(bm);
+}
+
+/*----------------------------------------------------------------*/
+
+static inline int dm_bm_validate_buffer(struct dm_block_manager *bm,
+				        struct dm_buffer *buf,
+				        struct buffer_aux *aux,
+				        struct dm_block_validator *v)
+{
+	return bm->validate_buffer(bm, buf, aux, v);
+}
+
 /*
  * You can have multiple concurrent readers or a single writer holding a
  * block lock.
  */
 
+static inline void dm_bm_prefetch(struct dm_block_manager *bm, dm_block_t b)
+{
+	bm->prefetch(bm, b);
+}
+
 /*
  * dm_bm_lock() locks a block and returns through @result a pointer to
  * memory that holds a copy of that block.  If you have write-locked the
  * block then any changes you make to memory pointed to by @result will be
  * written back to the disk sometime after dm_bm_unlock is called.
  */
-int dm_bm_read_lock(struct dm_block_manager *bm, dm_block_t b,
-		    struct dm_block_validator *v,
-		    struct dm_block **result);
-
-int dm_bm_write_lock(struct dm_block_manager *bm, dm_block_t b,
-		     struct dm_block_validator *v,
-		     struct dm_block **result);
+static inline int dm_bm_read_lock(struct dm_block_manager *bm, dm_block_t b,
+		    		  struct dm_block_validator *v,
+		    		  struct dm_block **result)
+{
+	return bm->read_lock_(bm, b, v, result);
+}
+
+static inline int dm_bm_write_lock(struct dm_block_manager *bm, dm_block_t b,
+		     		   struct dm_block_validator *v,
+		     		   struct dm_block **result)
+{
+	return bm->write_lock_(bm, b, v, result);
+}
 
 /*
  * The *_try_lock variants return -EWOULDBLOCK if the block isn't
  * available immediately.
  */
-int dm_bm_read_try_lock(struct dm_block_manager *bm, dm_block_t b,
-			struct dm_block_validator *v,
-			struct dm_block **result);
+static inline int dm_bm_read_try_lock(struct dm_block_manager *bm, dm_block_t b,
+				      struct dm_block_validator *v,
+				      struct dm_block **result)
+{
+	return bm->read_try_lock_(bm, b, v, result);
+}
 
 /*
  * Use dm_bm_write_lock_zero() when you know you're going to
  * overwrite the block completely.  It saves a disk read.
  */
-int dm_bm_write_lock_zero(struct dm_block_manager *bm, dm_block_t b,
-			  struct dm_block_validator *v,
-			  struct dm_block **result);
-
-void dm_bm_unlock(struct dm_block *b);
+static inline int dm_bm_write_lock_zero(struct dm_block_manager *bm, dm_block_t b,
+			  		struct dm_block_validator *v,
+			  		struct dm_block **result)
+{
+	return bm->write_lock_zero(bm, b, v, result);
+}
+
+static inline void dm_bm_unlock(struct dm_block_manager *bm, struct dm_block *b)
+{
+	bm->unlock(bm, b);
+}
+
+static inline int dm_bm_flush(struct dm_block_manager *bm)
+{
+	return bm->flush(bm);
+}
 
 /*
  * It's a common idiom to have a superblock that should be committed last.
@@ -105,12 +187,11 @@ void dm_bm_unlock(struct dm_block *b);
  *
  * This method always blocks.
  */
-int dm_bm_flush(struct dm_block_manager *bm);
-
-/*
- * Request data is prefetched into the cache.
- */
-void dm_bm_prefetch(struct dm_block_manager *bm, dm_block_t b);
+static inline int dm_bm_flush_and_unlock(struct dm_block_manager *bm,
+			   		 struct dm_block *superblock)
+{
+	return bm->flush_and_unlock(bm, superblock);
+}
 
 /*
  * Switches the bm to a read only mode.  Once read-only mode
@@ -123,9 +204,20 @@ void dm_bm_prefetch(struct dm_block_manager *bm, dm_block_t b);
  * Additionally you should not use dm_bm_unlock_move, however no error will
  * be returned if you do.
  */
-bool dm_bm_is_read_only(struct dm_block_manager *bm);
-void dm_bm_set_read_only(struct dm_block_manager *bm);
-void dm_bm_set_read_write(struct dm_block_manager *bm);
+static inline void dm_bm_set_read_only(struct dm_block_manager *bm)
+{
+	return bm->set_read_only(bm);
+}
+
+static inline bool dm_bm_is_read_only(struct dm_block_manager *bm)
+{
+	return bm->is_read_only(bm);
+}
+
+static inline void dm_bm_set_read_write(struct dm_block_manager *bm)
+{
+	bm->set_read_write(bm);
+}
 
 u32 dm_bm_checksum(const void *data, size_t len, u32 init_xor);
 
diff --git a/drivers/md/persistent-data/dm-transaction-manager.c b/drivers/md/persistent-data/dm-transaction-manager.c
index abe2c5dd0993..5b447efdc2fb 100644
--- a/drivers/md/persistent-data/dm-transaction-manager.c
+++ b/drivers/md/persistent-data/dm-transaction-manager.c
@@ -225,7 +225,7 @@ int dm_tm_commit(struct dm_transaction_manager *tm, struct dm_block *root)
 		return -EWOULDBLOCK;
 
 	wipe_shadow_table(tm);
-	dm_bm_unlock(root);
+	dm_bm_unlock(tm->bm, root);
 
 	return dm_bm_flush(tm->bm);
 }
@@ -289,14 +289,14 @@ static int __shadow_block(struct dm_transaction_manager *tm, dm_block_t orig,
 	 */
 	r = dm_bm_write_lock_zero(tm->bm, new, v, result);
 	if (r) {
-		dm_bm_unlock(orig_block);
+		dm_bm_unlock(tm->bm, orig_block);
 		return r;
 	}
 
 	memcpy(dm_block_data(*result), dm_block_data(orig_block),
 	       dm_bm_block_size(tm->bm));
 
-	dm_bm_unlock(orig_block);
+	dm_bm_unlock(tm->bm, orig_block);
 	return r;
 }
 
@@ -344,7 +344,10 @@ EXPORT_SYMBOL_GPL(dm_tm_read_lock);
 
 void dm_tm_unlock(struct dm_transaction_manager *tm, struct dm_block *b)
 {
-	dm_bm_unlock(b);
+	if (tm->is_clone)
+		tm = tm->real;
+
+	dm_bm_unlock(tm->bm, b);
 }
 EXPORT_SYMBOL_GPL(dm_tm_unlock);
 
