Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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: b29d4986d0da ("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>

authored by

Ming-Hung Tsai and committed by
Mikulas Patocka
4ca8b8bd 0c5eef0a

+25 -5
+25 -5
drivers/md/dm-cache-target.c
··· 1467 1467 free_prison_cell(cache, mg->cell); 1468 1468 } 1469 1469 1470 - if (!success && mg->overwrite_bio) 1471 - bio_io_error(mg->overwrite_bio); 1470 + if (mg->overwrite_bio) { 1471 + // Set generic error if the bio hasn't been issued yet, 1472 + // e.g., invalidation or metadata commit failed before bio 1473 + // submission. Otherwise preserve the bio's own error status. 1474 + if (!success && !mg->overwrite_bio->bi_status) 1475 + mg->overwrite_bio->bi_status = BLK_STS_IOERR; 1476 + bio_endio(mg->overwrite_bio); 1477 + } 1472 1478 1473 1479 free_migration(mg); 1474 1480 defer_bios(cache, &bios); ··· 1514 1508 return r; 1515 1509 } 1516 1510 1511 + static void invalidate_committed(struct work_struct *ws) 1512 + { 1513 + struct dm_cache_migration *mg = ws_to_mg(ws); 1514 + struct cache *cache = mg->cache; 1515 + struct bio *bio = mg->overwrite_bio; 1516 + struct per_bio_data *pb = get_per_bio_data(bio); 1517 + 1518 + if (mg->k.input) 1519 + invalidate_complete(mg, false); 1520 + 1521 + init_continuation(&mg->k, invalidate_completed); 1522 + remap_to_origin_clear_discard(cache, bio, mg->invalidate_oblock); 1523 + dm_hook_bio(&pb->hook_info, bio, overwrite_endio, mg); 1524 + dm_submit_bio_remap(bio, NULL); 1525 + } 1526 + 1517 1527 static void invalidate_remove(struct work_struct *ws) 1518 1528 { 1519 1529 int r; ··· 1542 1520 return; 1543 1521 } 1544 1522 1545 - init_continuation(&mg->k, invalidate_completed); 1523 + init_continuation(&mg->k, invalidate_committed); 1546 1524 continue_after_commit(&cache->committer, &mg->k); 1547 - remap_to_origin_clear_discard(cache, mg->overwrite_bio, mg->invalidate_oblock); 1548 - mg->overwrite_bio = NULL; 1549 1525 schedule_commit(&cache->committer); 1550 1526 } 1551 1527