dm cache: fix write hang in passthrough mode

The invalidate_remove() function has incomplete logic for handling write
hit bios after cache invalidation. It sets up the remapping for the
overwrite_bio but then drops it immediately without submission, causing
write operations to hang.

Fix by adding a new invalidate_committed() continuation that submits
the remapped writes to the cache origin after metadata commit completes,
while using the overwrite_endio hook to ensure proper completion
sequencing. This maintains existing coherency. Also improve error
handling in invalidate_complete() to preserve the original error status
instead of using bio_io_error() unconditionally.

Fixes: b29d4986d0 ("dm cache: significant rework to leverage dm-bio-prison-v2")
Signed-off-by: Ming-Hung Tsai <mtsai@redhat.com>
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
This commit is contained in:
Ming-Hung Tsai 2026-02-09 15:54:07 +08:00 committed by Mikulas Patocka
parent 0c5eef0aad
commit 4ca8b8bd95

View File

@ -1467,8 +1467,14 @@ static void invalidate_complete(struct dm_cache_migration *mg, bool success)
free_prison_cell(cache, mg->cell);
}
if (!success && mg->overwrite_bio)
bio_io_error(mg->overwrite_bio);
if (mg->overwrite_bio) {
// Set generic error if the bio hasn't been issued yet,
// e.g., invalidation or metadata commit failed before bio
// submission. Otherwise preserve the bio's own error status.
if (!success && !mg->overwrite_bio->bi_status)
mg->overwrite_bio->bi_status = BLK_STS_IOERR;
bio_endio(mg->overwrite_bio);
}
free_migration(mg);
defer_bios(cache, &bios);
@ -1508,6 +1514,22 @@ static int invalidate_cblock(struct cache *cache, dm_cblock_t cblock)
return r;
}
static void invalidate_committed(struct work_struct *ws)
{
struct dm_cache_migration *mg = ws_to_mg(ws);
struct cache *cache = mg->cache;
struct bio *bio = mg->overwrite_bio;
struct per_bio_data *pb = get_per_bio_data(bio);
if (mg->k.input)
invalidate_complete(mg, false);
init_continuation(&mg->k, invalidate_completed);
remap_to_origin_clear_discard(cache, bio, mg->invalidate_oblock);
dm_hook_bio(&pb->hook_info, bio, overwrite_endio, mg);
dm_submit_bio_remap(bio, NULL);
}
static void invalidate_remove(struct work_struct *ws)
{
int r;
@ -1520,10 +1542,8 @@ static void invalidate_remove(struct work_struct *ws)
return;
}
init_continuation(&mg->k, invalidate_completed);
init_continuation(&mg->k, invalidate_committed);
continue_after_commit(&cache->committer, &mg->k);
remap_to_origin_clear_discard(cache, mg->overwrite_bio, mg->invalidate_oblock);
mg->overwrite_bio = NULL;
schedule_commit(&cache->committer);
}