--- /dev/null
+From 2099b145d77c1d53f5711f029c37cc537897cee6 Mon Sep 17 00:00:00 2001
+From: Nikos Tsironis <ntsironis@arrikto.com>
+Date: Fri, 22 Jan 2021 17:19:31 +0200
+Subject: dm era: Update in-core bitset after committing the metadata
+
+From: Nikos Tsironis <ntsironis@arrikto.com>
+
+commit 2099b145d77c1d53f5711f029c37cc537897cee6 upstream.
+
+In case of a system crash, dm-era might fail to mark blocks as written
+in its metadata, although the corresponding writes to these blocks were
+passed down to the origin device and completed successfully.
+
+Consider the following sequence of events:
+
+1. We write to a block that has not been yet written in the current era
+2. era_map() checks the in-core bitmap for the current era and sees
+ that the block is not marked as written.
+3. The write is deferred for submission after the metadata have been
+ updated and committed.
+4. The worker thread processes the deferred write
+ (process_deferred_bios()) and marks the block as written in the
+ in-core bitmap, **before** committing the metadata.
+5. The worker thread starts committing the metadata.
+6. We do more writes that map to the same block as the write of step (1)
+7. era_map() checks the in-core bitmap and sees that the block is marked
+ as written, **although the metadata have not been committed yet**.
+8. These writes are passed down to the origin device immediately and the
+ device reports them as completed.
+9. The system crashes, e.g., power failure, before the commit from step
+ (5) finishes.
+
+When the system recovers and we query the dm-era target for the list of
+written blocks it doesn't report the aforementioned block as written,
+although the writes of step (6) completed successfully.
+
+The issue is that era_map() decides whether to defer or not a write
+based on non committed information. The root cause of the bug is that we
+update the in-core bitmap, **before** committing the metadata.
+
+Fix this by updating the in-core bitmap **after** successfully
+committing the metadata.
+
+Fixes: eec40579d84873 ("dm: add era target")
+Cc: stable@vger.kernel.org # v3.15+
+Signed-off-by: Nikos Tsironis <ntsironis@arrikto.com>
+Signed-off-by: Mike Snitzer <snitzer@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/md/dm-era-target.c | 25 +++++++++++++++++++------
+ 1 file changed, 19 insertions(+), 6 deletions(-)
+
+--- a/drivers/md/dm-era-target.c
++++ b/drivers/md/dm-era-target.c
+@@ -134,7 +134,7 @@ static int writeset_test_and_set(struct
+ {
+ int r;
+
+- if (!test_and_set_bit(block, ws->bits)) {
++ if (!test_bit(block, ws->bits)) {
+ r = dm_bitset_set_bit(info, ws->md.root, block, &ws->md.root);
+ if (r) {
+ /* FIXME: fail mode */
+@@ -1242,8 +1242,10 @@ static void process_deferred_bios(struct
+ int r;
+ struct bio_list deferred_bios, marked_bios;
+ struct bio *bio;
++ struct blk_plug plug;
+ bool commit_needed = false;
+ bool failed = false;
++ struct writeset *ws = era->md->current_writeset;
+
+ bio_list_init(&deferred_bios);
+ bio_list_init(&marked_bios);
+@@ -1253,9 +1255,11 @@ static void process_deferred_bios(struct
+ bio_list_init(&era->deferred_bios);
+ spin_unlock(&era->deferred_lock);
+
++ if (bio_list_empty(&deferred_bios))
++ return;
++
+ while ((bio = bio_list_pop(&deferred_bios))) {
+- r = writeset_test_and_set(&era->md->bitset_info,
+- era->md->current_writeset,
++ r = writeset_test_and_set(&era->md->bitset_info, ws,
+ get_block(era, bio));
+ if (r < 0) {
+ /*
+@@ -1263,7 +1267,6 @@ static void process_deferred_bios(struct
+ * FIXME: finish.
+ */
+ failed = true;
+-
+ } else if (r == 0)
+ commit_needed = true;
+
+@@ -1279,9 +1282,19 @@ static void process_deferred_bios(struct
+ if (failed)
+ while ((bio = bio_list_pop(&marked_bios)))
+ bio_io_error(bio);
+- else
+- while ((bio = bio_list_pop(&marked_bios)))
++ else {
++ blk_start_plug(&plug);
++ while ((bio = bio_list_pop(&marked_bios))) {
++ /*
++ * Only update the in-core writeset if the on-disk one
++ * was updated too.
++ */
++ if (commit_needed)
++ set_bit(get_block(era, bio), ws->bits);
+ generic_make_request(bio);
++ }
++ blk_finish_plug(&plug);
++ }
+ }
+
+ static void process_rpc_calls(struct era *era)