diff options
| author | Mikulas Patocka <mpatocka@redhat.com> | 2026-09-30 21:43:26 +0200 |
|---|---|---|
| committer | Mikulas Patocka <mpatocka@redhat.com> | 2026-10-01 13:09:48 +0200 |
| commit | c0df022cb0fa2bbee74bd9b90c66790bcd4e3050 (patch) | |
| tree | 0159744a2ad550fdadd3f90207a07a98cf5c2ae3 /drivers/md | |
| parent | 8a0cc5f3446a324b13f62020eeddb32d6ba1c2db (diff) | |
dm-writecache: fix REQ_FUA processing
REQ_FUA did not flush metadata in SSD mode, this commit fixes it.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Cc: stable@vger.kernel.org
Fixes: c1005322ff02 ("dm writecache: handle REQ_FUA")
Assisted-by: Claude:claude-opus-5
Assisted-by: Codex:gpt-6-sol
Diffstat (limited to 'drivers/md')
| -rw-r--r-- | drivers/md/dm-writecache.c | 45 |
1 files changed, 35 insertions, 10 deletions
diff --git a/drivers/md/dm-writecache.c b/drivers/md/dm-writecache.c index a38780a7b1be..6f977fb8c939 100644 --- a/drivers/md/dm-writecache.c +++ b/drivers/md/dm-writecache.c @@ -188,6 +188,7 @@ struct dm_writecache { struct task_struct *endio_thread; struct task_struct *flush_thread; + spinlock_t flush_list_lock; struct bio_list flush_list; struct dm_kcopyd_client *dm_kcopyd; @@ -1289,9 +1290,11 @@ static int writecache_flush_thread(void *data) struct bio *bio; wc_lock(wc); + spin_lock_irq(&wc->flush_list_lock); bio = bio_list_pop(&wc->flush_list); if (!bio) { set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irq(&wc->flush_list_lock); wc_unlock(wc); if (unlikely(kthread_should_stop())) { @@ -1302,6 +1305,7 @@ static int writecache_flush_thread(void *data) schedule(); continue; } + spin_unlock_irq(&wc->flush_list_lock); if (bio_op(bio) == REQ_OP_DISCARD) { writecache_discard(wc, bio->bi_iter.bi_sector, @@ -1323,9 +1327,12 @@ static int writecache_flush_thread(void *data) static void writecache_offload_bio(struct dm_writecache *wc, struct bio *bio) { + unsigned long flags; + spin_lock_irqsave(&wc->flush_list_lock, flags); if (bio_list_empty(&wc->flush_list)) wake_up_process(wc->flush_thread); bio_list_add(&wc->flush_list, bio); + spin_unlock_irqrestore(&wc->flush_list_lock, flags); } enum wc_map_op { @@ -1437,6 +1444,8 @@ static void writecache_bio_copy_ssd(struct dm_writecache *wc, struct bio *bio, static enum wc_map_op writecache_map_write(struct dm_writecache *wc, struct bio *bio) { struct wc_entry *e; + enum wc_map_op ret = WC_MAP_SUBMIT; + bool need_flush = false; do { bool found_entry = false; @@ -1473,7 +1482,8 @@ direct_write: writecache_map_remap_origin(wc, bio, e); wc->stats.writes_around += bio->bi_iter.bi_size >> wc->block_size_bits; wc->stats.writes += bio->bi_iter.bi_size >> wc->block_size_bits; - return WC_MAP_REMAP_ORIGIN; + ret = WC_MAP_REMAP_ORIGIN; + goto flush_pmem_and_ret; } wc->stats.writes_blocked_on_freelist++; writecache_wait_on_freelist(wc); @@ -1487,21 +1497,25 @@ bio_copy: if (WC_MODE_PMEM(wc)) { bio_copy_block(wc, bio, memory_data(wc, e)); wc->stats.writes++; + need_flush = true; } else { writecache_bio_copy_ssd(wc, bio, e, search_used); return WC_MAP_REMAP; } } while (bio->bi_iter.bi_size); - if (unlikely(bio->bi_opf & REQ_FUA || wc->uncommitted_blocks >= wc->autocommit_blocks)) { - writecache_flush(wc); - if (writecache_has_error(wc)) - bio->bi_status = BLK_STS_IOERR; - } else { - writecache_schedule_autocommit(wc); +flush_pmem_and_ret: + if (need_flush) { + if (unlikely(bio->bi_opf & REQ_FUA || wc->uncommitted_blocks >= wc->autocommit_blocks)) { + writecache_flush(wc); + if (writecache_has_error(wc)) + ret = WC_MAP_ERROR; + } else { + writecache_schedule_autocommit(wc); + } } - return WC_MAP_SUBMIT; + return ret; } static enum wc_map_op writecache_map_flush(struct dm_writecache *wc, struct bio *bio) @@ -1591,7 +1605,10 @@ done: case WC_MAP_REMAP: /* make sure that writecache_end_io decrements bio_in_progress: */ - bio->bi_private = (void *)1; + if (unlikely(bio->bi_opf & REQ_FUA) && bio_op(bio) == REQ_OP_WRITE) + bio->bi_private = (void *)3; + else + bio->bi_private = (void *)1; atomic_inc(&wc->bio_in_progress[bio_data_dir(bio)]); wc_unlock(wc); return DM_MAPIO_REMAPPED; @@ -1621,12 +1638,19 @@ static int writecache_end_io(struct dm_target *ti, struct bio *bio, blk_status_t { struct dm_writecache *wc = ti->private; - if (bio->bi_private == (void *)1) { + if (bio->bi_private == (void *)1 || bio->bi_private == (void *)3) { int dir = bio_data_dir(bio); + bool fua = bio->bi_private == (void *)3; if (atomic_dec_and_test(&wc->bio_in_progress[dir])) if (unlikely(waitqueue_active(&wc->bio_in_progress_wait[dir]))) wake_up(&wc->bio_in_progress_wait[dir]); + + if (fua && likely(*status == BLK_STS_OK)) { + bio->bi_private = NULL; + writecache_offload_bio(wc, bio); + return DM_ENDIO_INCOMPLETE; + } } else if (bio->bi_private == (void *)2) { dm_iot_io_end(&wc->iot, 1); } @@ -2519,6 +2543,7 @@ invalid_optional: wc->memory_map_size -= (uint64_t)wc->start_sector << SECTOR_SHIFT; + spin_lock_init(&wc->flush_list_lock); bio_list_init(&wc->flush_list); wc->flush_thread = kthread_run(writecache_flush_thread, wc, "dm_writecache_flush"); if (IS_ERR(wc->flush_thread)) { |
