]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.9-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 1 Mar 2021 16:03:17 +0000 (17:03 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 1 Mar 2021 16:03:17 +0000 (17:03 +0100)
added patches:
dm-era-update-in-core-bitset-after-committing-the-metadata.patch

queue-4.9/dm-era-update-in-core-bitset-after-committing-the-metadata.patch [new file with mode: 0644]
queue-4.9/series

diff --git a/queue-4.9/dm-era-update-in-core-bitset-after-committing-the-metadata.patch b/queue-4.9/dm-era-update-in-core-bitset-after-committing-the-metadata.patch
new file mode 100644 (file)
index 0000000..dbc6ac2
--- /dev/null
@@ -0,0 +1,118 @@
+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)
index 3723dcbcf21945e8c5e919477f51b0fd05025f12..4cc241d9b2448a7b458b43dd5072235a1f3ff33c 100644 (file)
@@ -131,3 +131,4 @@ sunvnet-use-icmp_ndo_send-helper.patch
 ipv6-icmp6-avoid-indirect-call-for-icmpv6_send.patch
 ipv6-silence-compilation-warning-for-non-ipv6-builds.patch
 net-icmp-pass-zeroed-opts-from-icmp-v6-_ndo_send-before-sending.patch
+dm-era-update-in-core-bitset-after-committing-the-metadata.patch