mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-10-26 10:57:38 +08:00
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: c1005322ff ("dm writecache: handle REQ_FUA")
Assisted-by: Claude:claude-opus-5
Assisted-by: Codex:gpt-6-sol
This commit is contained in:
1 parent
8a0cc5f344
commit
c0df022cb0
1 file changed
+35
-10
+35
-10
@@ -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)) {
|
||||
|
||||
Reference in new issue
Block a user