1506 lines
42 KiB
Diff
1506 lines
42 KiB
Diff
|
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);
|
||
|
|