From 2c0f228b99071a4dab9c8de99a52e604bba770fe Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Mar 2021 17:03:17 +0100 Subject: [PATCH] 4.9-stable patches added patches: dm-era-update-in-core-bitset-after-committing-the-metadata.patch --- ...bitset-after-committing-the-metadata.patch | 118 ++++++++++++++++++ queue-4.9/series | 1 + 2 files changed, 119 insertions(+) create mode 100644 queue-4.9/dm-era-update-in-core-bitset-after-committing-the-metadata.patch 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 index 00000000000..dbc6ac248eb --- /dev/null +++ b/queue-4.9/dm-era-update-in-core-bitset-after-committing-the-metadata.patch @@ -0,0 +1,118 @@ +From 2099b145d77c1d53f5711f029c37cc537897cee6 Mon Sep 17 00:00:00 2001 +From: Nikos Tsironis +Date: Fri, 22 Jan 2021 17:19:31 +0200 +Subject: dm era: Update in-core bitset after committing the metadata + +From: Nikos Tsironis + +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 +Signed-off-by: Mike Snitzer +Signed-off-by: Greg Kroah-Hartman +--- + 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) diff --git a/queue-4.9/series b/queue-4.9/series index 3723dcbcf21..4cc241d9b24 100644 --- a/queue-4.9/series +++ b/queue-4.9/series @@ -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 -- 2.47.3