diff options
author | zhangyi (F) <yi.zhang@huawei.com> | 2019-06-05 09:27:08 -0400 |
---|---|---|
committer | Mike Snitzer <snitzer@redhat.com> | 2019-06-25 14:09:13 -0400 |
commit | 211ad4b733037f66f9be0a79eade3da7ab11cbb8 (patch) | |
tree | 885221a90ef43a449871fbd4b886dbfa154660f9 | |
parent | 10c9c8e7c09b4d32b31df1bd14673bd6dbfc50be (diff) |
dm log writes: make sure super sector log updates are written in order
Currently, although we submit super bios in order (and super.nr_entries
is incremented by each logged entry), submit_bio() is async so each
super sector may not be written to log device in order and then the
final nr_entries may be smaller than it should be.
This problem can be reproduced by the xfstests generic/455 with ext4:
QA output created by 455
-Silence is golden
+mark 'end' does not exist
Fix this by serializing submission of super sectors to make sure each
is written to the log disk in order.
Fixes: 0e9cebe724597 ("dm: add log writes target")
Cc: stable@vger.kernel.org
Signed-off-by: zhangyi (F) <yi.zhang@huawei.com>
Suggested-by: Josef Bacik <josef@toxicpanda.com>
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
-rw-r--r-- | drivers/md/dm-log-writes.c | 23 |
1 files changed, 21 insertions, 2 deletions
diff --git a/drivers/md/dm-log-writes.c b/drivers/md/dm-log-writes.c index 9ea2b0291f20..e549392e0ea5 100644 --- a/drivers/md/dm-log-writes.c +++ b/drivers/md/dm-log-writes.c | |||
@@ -60,6 +60,7 @@ | |||
60 | 60 | ||
61 | #define WRITE_LOG_VERSION 1ULL | 61 | #define WRITE_LOG_VERSION 1ULL |
62 | #define WRITE_LOG_MAGIC 0x6a736677736872ULL | 62 | #define WRITE_LOG_MAGIC 0x6a736677736872ULL |
63 | #define WRITE_LOG_SUPER_SECTOR 0 | ||
63 | 64 | ||
64 | /* | 65 | /* |
65 | * The disk format for this is braindead simple. | 66 | * The disk format for this is braindead simple. |
@@ -115,6 +116,7 @@ struct log_writes_c { | |||
115 | struct list_head logging_blocks; | 116 | struct list_head logging_blocks; |
116 | wait_queue_head_t wait; | 117 | wait_queue_head_t wait; |
117 | struct task_struct *log_kthread; | 118 | struct task_struct *log_kthread; |
119 | struct completion super_done; | ||
118 | }; | 120 | }; |
119 | 121 | ||
120 | struct pending_block { | 122 | struct pending_block { |
@@ -180,6 +182,14 @@ static void log_end_io(struct bio *bio) | |||
180 | bio_put(bio); | 182 | bio_put(bio); |
181 | } | 183 | } |
182 | 184 | ||
185 | static void log_end_super(struct bio *bio) | ||
186 | { | ||
187 | struct log_writes_c *lc = bio->bi_private; | ||
188 | |||
189 | complete(&lc->super_done); | ||
190 | log_end_io(bio); | ||
191 | } | ||
192 | |||
183 | /* | 193 | /* |
184 | * Meant to be called if there is an error, it will free all the pages | 194 | * Meant to be called if there is an error, it will free all the pages |
185 | * associated with the block. | 195 | * associated with the block. |
@@ -215,7 +225,8 @@ static int write_metadata(struct log_writes_c *lc, void *entry, | |||
215 | bio->bi_iter.bi_size = 0; | 225 | bio->bi_iter.bi_size = 0; |
216 | bio->bi_iter.bi_sector = sector; | 226 | bio->bi_iter.bi_sector = sector; |
217 | bio_set_dev(bio, lc->logdev->bdev); | 227 | bio_set_dev(bio, lc->logdev->bdev); |
218 | bio->bi_end_io = log_end_io; | 228 | bio->bi_end_io = (sector == WRITE_LOG_SUPER_SECTOR) ? |
229 | log_end_super : log_end_io; | ||
219 | bio->bi_private = lc; | 230 | bio->bi_private = lc; |
220 | bio_set_op_attrs(bio, REQ_OP_WRITE, 0); | 231 | bio_set_op_attrs(bio, REQ_OP_WRITE, 0); |
221 | 232 | ||
@@ -418,11 +429,18 @@ static int log_super(struct log_writes_c *lc) | |||
418 | super.nr_entries = cpu_to_le64(lc->logged_entries); | 429 | super.nr_entries = cpu_to_le64(lc->logged_entries); |
419 | super.sectorsize = cpu_to_le32(lc->sectorsize); | 430 | super.sectorsize = cpu_to_le32(lc->sectorsize); |
420 | 431 | ||
421 | if (write_metadata(lc, &super, sizeof(super), NULL, 0, 0)) { | 432 | if (write_metadata(lc, &super, sizeof(super), NULL, 0, |
433 | WRITE_LOG_SUPER_SECTOR)) { | ||
422 | DMERR("Couldn't write super"); | 434 | DMERR("Couldn't write super"); |
423 | return -1; | 435 | return -1; |
424 | } | 436 | } |
425 | 437 | ||
438 | /* | ||
439 | * Super sector should be writen in-order, otherwise the | ||
440 | * nr_entries could be rewritten incorrectly by an old bio. | ||
441 | */ | ||
442 | wait_for_completion_io(&lc->super_done); | ||
443 | |||
426 | return 0; | 444 | return 0; |
427 | } | 445 | } |
428 | 446 | ||
@@ -531,6 +549,7 @@ static int log_writes_ctr(struct dm_target *ti, unsigned int argc, char **argv) | |||
531 | INIT_LIST_HEAD(&lc->unflushed_blocks); | 549 | INIT_LIST_HEAD(&lc->unflushed_blocks); |
532 | INIT_LIST_HEAD(&lc->logging_blocks); | 550 | INIT_LIST_HEAD(&lc->logging_blocks); |
533 | init_waitqueue_head(&lc->wait); | 551 | init_waitqueue_head(&lc->wait); |
552 | init_completion(&lc->super_done); | ||
534 | atomic_set(&lc->io_blocks, 0); | 553 | atomic_set(&lc->io_blocks, 0); |
535 | atomic_set(&lc->pending_blocks, 0); | 554 | atomic_set(&lc->pending_blocks, 0); |
536 | 555 | ||