Commit 4ca8b8bd authored by Ming-Hung Tsai's avatar Ming-Hung Tsai Committed by Mikulas Patocka
Browse files

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: b29d4986 ("dm cache: significant rework to leverage dm-bio-prison-v2")
Signed-off-by: default avatarMing-Hung Tsai <mtsai@redhat.com>
Signed-off-by: default avatarMikulas Patocka <mpatocka@redhat.com>
parent 0c5eef0a
Loading
Loading
Loading
Loading
+25 −5
Original line number Diff line number Diff line
@@ -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);
}