[dm-devel] [patch 3/3] dm-writecache: rework writecache_flush_thread
Mikulas Patocka
mpatocka at redhat.com
Wed Jun 6 15:31:45 UTC 2018
In order to avoid deadlocks due to bio queuing, discard and flush bios
must be offloaded to a different thread.
writecache_flush_thread was not resistant to spurious wake-up, when it was
woken up, it was assumed that wc->flush_bio is set.
This patch reworks writecache_flush_thread so that it uses a list of bios
and thus it is resistant to spurious wake-up.
Signed-off-by: Mikulas Patocka <mpatocka at redhat.com>
---
drivers/md/dm-writecache.c | 78 ++++++++++++++++++++++++++++-----------------
1 file changed, 50 insertions(+), 28 deletions(-)
Index: linux-2.6/drivers/md/dm-writecache.c
===================================================================
--- linux-2.6.orig/drivers/md/dm-writecache.c 2018-06-06 00:32:05.000000000 +0200
+++ linux-2.6/drivers/md/dm-writecache.c 2018-06-06 01:16:15.000000000 +0200
@@ -172,8 +172,7 @@ struct dm_writecache {
struct task_struct *endio_thread;
struct task_struct *flush_thread;
- struct completion flush_completion;
- struct bio *flush_bio;
+ struct bio_list flush_list;
struct dm_kcopyd_client *dm_kcopyd;
unsigned long *dirty_bitmap;
@@ -1065,35 +1064,48 @@ static int writecache_flush_thread(void
{
struct dm_writecache *wc = data;
- while (!kthread_should_stop()) {
- struct bio *bio = wc->flush_bio;
-
- if (likely(bio)) {
- if (bio_op(bio) == REQ_OP_DISCARD)
- writecache_discard(wc, bio->bi_iter.bi_sector, bio_end_sector(bio));
- else
- writecache_flush(wc);
- }
+ while (1) {
+ struct bio *bio;
+ wc_lock(wc);
+ bio = bio_list_pop(&wc->flush_list);
+ if (bio)
+ goto process_bio;
set_current_state(TASK_INTERRUPTIBLE);
- /* for debugging - catch uninitialized use */
- wc->flush_bio = (void *)0x600 + POISON_POINTER_DELTA;
- complete(&wc->flush_completion);
+ wc_unlock(wc);
+
+ if (unlikely(kthread_should_stop())) {
+ set_current_state(TASK_RUNNING);
+ break;
+ }
schedule();
- }
- set_current_state(TASK_RUNNING);
+ continue;
+
+process_bio:
+ if (bio_op(bio) == REQ_OP_DISCARD) {
+ writecache_discard(wc, bio->bi_iter.bi_sector, bio_end_sector(bio));
+ wc_unlock(wc);
+ bio_set_dev(bio, wc->dev->bdev);
+ generic_make_request(bio);
+ } else {
+ writecache_flush(wc);
+ wc_unlock(wc);
+ if (writecache_has_error(wc))
+ bio->bi_status = BLK_STS_IOERR;
+ bio_endio(bio);
+ }
+ }
return 0;
}
static void writecache_offload_bio(struct dm_writecache *wc, struct bio *bio)
{
- wc->flush_bio = bio;
- reinit_completion(&wc->flush_completion);
- wake_up_process(wc->flush_thread);
- wait_for_completion_io(&wc->flush_completion);
+ if (bio_list_empty(&wc->flush_list))
+ wake_up_process(wc->flush_thread);
+ bio_list_add(&wc->flush_list, bio);
}
static int writecache_map(struct dm_target *ti, struct bio *bio)
@@ -1108,11 +1120,15 @@ static int writecache_map(struct dm_targ
if (unlikely(bio->bi_opf & REQ_PREFLUSH)) {
if (writecache_has_error(wc))
goto unlock_error;
- if (WC_MODE_PMEM(wc))
+ if (WC_MODE_PMEM(wc)) {
writecache_flush(wc);
- else
+ if (writecache_has_error(wc))
+ goto unlock_error;
+ goto unlock_ok;
+ } else {
writecache_offload_bio(wc, bio);
- goto unlock_ok;
+ goto unlock_return;
+ }
}
bio->bi_iter.bi_sector = dm_target_offset(ti, bio->bi_iter.bi_sector);
@@ -1128,11 +1144,13 @@ static int writecache_map(struct dm_targ
if (unlikely(bio_op(bio) == REQ_OP_DISCARD)) {
if (writecache_has_error(wc))
goto unlock_error;
- if (WC_MODE_PMEM(wc))
+ if (WC_MODE_PMEM(wc)) {
writecache_discard(wc, bio->bi_iter.bi_sector, bio_end_sector(bio));
- else
+ goto unlock_remap_origin;
+ } else {
writecache_offload_bio(wc, bio);
- goto unlock_remap_origin;
+ goto unlock_return;
+ }
}
if (bio_data_dir(bio) == READ) {
@@ -1224,6 +1242,10 @@ unlock_ok:
bio_endio(bio);
return DM_MAPIO_SUBMITTED;
+unlock_return:
+ wc_unlock(wc);
+ return DM_MAPIO_SUBMITTED;
+
unlock_error:
wc_unlock(wc);
bio_io_error(bio);
@@ -2017,7 +2039,7 @@ invalid_optional:
size_t n_blocks, n_metadata_blocks;
uint64_t n_bitmap_bits;
- init_completion(&wc->flush_completion);
+ bio_list_init(&wc->flush_list);
wc->flush_thread = kthread_create(writecache_flush_thread, wc, "dm_writecache_flush");
if (IS_ERR(wc->flush_thread)) {
r = PTR_ERR(wc->flush_thread);
@@ -2025,7 +2047,7 @@ invalid_optional:
ti->error = "Couldn't spawn endio thread";
goto bad;
}
- writecache_offload_bio(wc, NULL);
+ wake_up_process(wc->flush_thread);
r = calculate_memory_size(wc->memory_map_size, wc->block_size,
&n_blocks, &n_metadata_blocks);
More information about the dm-devel
mailing list