Commit 8fdbfe8d authored by Nikos Tsironis's avatar Nikos Tsironis Committed by Mike Snitzer

dm clone metadata: Use a two phase commit

Split the metadata commit in two parts:

1. dm_clone_metadata_pre_commit(): Prepare the current transaction for
   committing. After this is called, all subsequent metadata updates,
   done through either dm_clone_set_region_hydrated() or
   dm_clone_cond_set_range(), will be part of the next transaction.

2. dm_clone_metadata_commit(): Actually commit the current transaction
   to disk and start a new transaction.

This is required by the following commit. It allows dm-clone to flush
the destination device after step (1) to ensure that all freshly
hydrated regions, for which we are updating the metadata, are properly
written to non-volatile storage and won't be lost in case of a crash.

Fixes: 7431b783 ("dm: add clone target")
Cc: stable@vger.kernel.org # v5.4+
Signed-off-by: default avatarNikos Tsironis <ntsironis@arrikto.com>
Signed-off-by: default avatarMike Snitzer <snitzer@redhat.com>
parent e6a505f3
...@@ -127,6 +127,9 @@ struct dm_clone_metadata { ...@@ -127,6 +127,9 @@ struct dm_clone_metadata {
struct dirty_map dmap[2]; struct dirty_map dmap[2];
struct dirty_map *current_dmap; struct dirty_map *current_dmap;
/* Protected by lock */
struct dirty_map *committing_dmap;
/* /*
* In core copy of the on-disk bitmap to save constantly doing look ups * In core copy of the on-disk bitmap to save constantly doing look ups
* on disk. * on disk.
...@@ -511,6 +514,7 @@ static int dirty_map_init(struct dm_clone_metadata *cmd) ...@@ -511,6 +514,7 @@ static int dirty_map_init(struct dm_clone_metadata *cmd)
} }
cmd->current_dmap = &cmd->dmap[0]; cmd->current_dmap = &cmd->dmap[0];
cmd->committing_dmap = NULL;
return 0; return 0;
} }
...@@ -775,15 +779,17 @@ static int __flush_dmap(struct dm_clone_metadata *cmd, struct dirty_map *dmap) ...@@ -775,15 +779,17 @@ static int __flush_dmap(struct dm_clone_metadata *cmd, struct dirty_map *dmap)
return 0; return 0;
} }
int dm_clone_metadata_commit(struct dm_clone_metadata *cmd) int dm_clone_metadata_pre_commit(struct dm_clone_metadata *cmd)
{ {
int r = -EPERM; int r = 0;
struct dirty_map *dmap, *next_dmap; struct dirty_map *dmap, *next_dmap;
down_write(&cmd->lock); down_write(&cmd->lock);
if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) {
r = -EPERM;
goto out; goto out;
}
/* Get current dirty bitmap */ /* Get current dirty bitmap */
dmap = cmd->current_dmap; dmap = cmd->current_dmap;
...@@ -795,7 +801,7 @@ int dm_clone_metadata_commit(struct dm_clone_metadata *cmd) ...@@ -795,7 +801,7 @@ int dm_clone_metadata_commit(struct dm_clone_metadata *cmd)
* The last commit failed, so we don't have a clean dirty-bitmap to * The last commit failed, so we don't have a clean dirty-bitmap to
* use. * use.
*/ */
if (WARN_ON(next_dmap->changed)) { if (WARN_ON(next_dmap->changed || cmd->committing_dmap)) {
r = -EINVAL; r = -EINVAL;
goto out; goto out;
} }
...@@ -805,11 +811,33 @@ int dm_clone_metadata_commit(struct dm_clone_metadata *cmd) ...@@ -805,11 +811,33 @@ int dm_clone_metadata_commit(struct dm_clone_metadata *cmd)
cmd->current_dmap = next_dmap; cmd->current_dmap = next_dmap;
spin_unlock_irq(&cmd->bitmap_lock); spin_unlock_irq(&cmd->bitmap_lock);
/* /* Set old dirty bitmap as currently committing */
* No one is accessing the old dirty bitmap anymore, so we can flush cmd->committing_dmap = dmap;
* it. out:
*/ up_write(&cmd->lock);
r = __flush_dmap(cmd, dmap);
return r;
}
int dm_clone_metadata_commit(struct dm_clone_metadata *cmd)
{
int r = -EPERM;
down_write(&cmd->lock);
if (cmd->fail_io || dm_bm_is_read_only(cmd->bm))
goto out;
if (WARN_ON(!cmd->committing_dmap)) {
r = -EINVAL;
goto out;
}
r = __flush_dmap(cmd, cmd->committing_dmap);
if (!r) {
/* Clear committing dmap */
cmd->committing_dmap = NULL;
}
out: out:
up_write(&cmd->lock); up_write(&cmd->lock);
......
...@@ -75,7 +75,23 @@ void dm_clone_metadata_close(struct dm_clone_metadata *cmd); ...@@ -75,7 +75,23 @@ void dm_clone_metadata_close(struct dm_clone_metadata *cmd);
/* /*
* Commit dm-clone metadata to disk. * Commit dm-clone metadata to disk.
*
* We use a two phase commit:
*
* 1. dm_clone_metadata_pre_commit(): Prepare the current transaction for
* committing. After this is called, all subsequent metadata updates, done
* through either dm_clone_set_region_hydrated() or
* dm_clone_cond_set_range(), will be part of the **next** transaction.
*
* 2. dm_clone_metadata_commit(): Actually commit the current transaction to
* disk and start a new transaction.
*
* This allows dm-clone to flush the destination device after step (1) to
* ensure that all freshly hydrated regions, for which we are updating the
* metadata, are properly written to non-volatile storage and won't be lost in
* case of a crash.
*/ */
int dm_clone_metadata_pre_commit(struct dm_clone_metadata *cmd);
int dm_clone_metadata_commit(struct dm_clone_metadata *cmd); int dm_clone_metadata_commit(struct dm_clone_metadata *cmd);
/* /*
...@@ -112,6 +128,7 @@ int dm_clone_metadata_abort(struct dm_clone_metadata *cmd); ...@@ -112,6 +128,7 @@ int dm_clone_metadata_abort(struct dm_clone_metadata *cmd);
* Switches metadata to a read only mode. Once read-only mode has been entered * Switches metadata to a read only mode. Once read-only mode has been entered
* the following functions will return -EPERM: * the following functions will return -EPERM:
* *
* dm_clone_metadata_pre_commit()
* dm_clone_metadata_commit() * dm_clone_metadata_commit()
* dm_clone_set_region_hydrated() * dm_clone_set_region_hydrated()
* dm_clone_cond_set_range() * dm_clone_cond_set_range()
......
...@@ -1122,8 +1122,13 @@ static int commit_metadata(struct clone *clone) ...@@ -1122,8 +1122,13 @@ static int commit_metadata(struct clone *clone)
goto out; goto out;
} }
r = dm_clone_metadata_commit(clone->cmd); r = dm_clone_metadata_pre_commit(clone->cmd);
if (unlikely(r)) {
__metadata_operation_failed(clone, "dm_clone_metadata_pre_commit", r);
goto out;
}
r = dm_clone_metadata_commit(clone->cmd);
if (unlikely(r)) { if (unlikely(r)) {
__metadata_operation_failed(clone, "dm_clone_metadata_commit", r); __metadata_operation_failed(clone, "dm_clone_metadata_commit", r);
goto out; goto out;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment