On Tue, Aug 18, 2026 at 06:05:47PM +0800, Ming-Hung Tsai wrote:
> The demotion counter is incremented per incoming write bio before the
> invalidation begins, causing the demotion count to exceed the actual
> number of cached blocks when multiple bios target the same cached
> block. Additionally, the counter is incremented unconditionally
> regardless of invalidation failure.
> 
> Reproduce steps:
> 
> 1. Create a cache device consisting of 512 cache entries
> 
> modprobe brd rd_size=262144
> dmsetup create cmeta --table "0 8192 linear /dev/ram0 0"
> dmsetup create cdata --table "0 65536 linear /dev/ram0 8192"
> dmsetup create corig --table "0 65536 linear /dev/ram0 262144"
> dd if=/dev/zero of=/dev/mapper/cmeta bs=4k count=1 oflag=direct
> dmsetup create cache --table "0 65536 cache /dev/mapper/cmeta \
> /dev/mapper/cdata /dev/mapper/corig 128 2 metadata2 writethrough smq 0"
> 
> 2. Populate the cache, and record the number of cached blocks
> 
> fio --name=populate --filename=/dev/mapper/cache --rw=randwrite --bs=4k \
> --direct=1 --ioengine=libaio --iodepth=32 --io_size=2048m
> nr_cached=$(dmsetup status cache | awk '{split($7, a, "/"); print a[1]}')
> 
> 3. Reload the cache into passthrough mode
> 
> dmsetup suspend cache
> dmsetup reload cache --table "0 65536 cache /dev/mapper/cmeta \
> /dev/mapper/cdata /dev/mapper/corig 128 2 metadata2 passthrough smq 0"
> dmsetup resume cache
> 
> 4. Write to the passthrough cache with multiple jobs to trigger
>    multiple bios hitting the same cached block.
> 
> fio --filename=/dev/mapper/cache --name=test --rw=write --bs=4k \
> --direct=1 --ioengine=libaio --iodepth=32 --numjobs=4
> 
> 5. Check if demoted matches cached block count. These numbers should
>    match but may differ due to overcounting per bio.
> 
> nr_demoted=$(dmsetup status cache | awk '{print $12}')
> echo "$nr_cached, $nr_demoted"
> 
> Fix by moving the demotion counter increment into invalidate_complete(),
> gated on the success flag.
> 
> Reported-by: Ben Marzinski <[email protected]>
> Fixes: b29d4986d0da ("dm cache: significant rework to leverage 
> dm-bio-prison-v2")
> Signed-off-by: Ming-Hung Tsai <[email protected]>

Reviewed-by: Benjamin Marzinski <[email protected]>

> ---
>  drivers/md/dm-cache-target.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
> index 097315a9bf0f..b79206816956 100644
> --- a/drivers/md/dm-cache-target.c
> +++ b/drivers/md/dm-cache-target.c
> @@ -1461,6 +1461,9 @@ static void invalidate_complete(struct 
> dm_cache_migration *mg, bool success)
>       struct bio_list bios;
>       struct cache *cache = mg->cache;
>  
> +     if (success)
> +             atomic_inc(&cache->stats.demotion);
> +
>       bio_list_init(&bios);
>       if (mg->cell) {
>               if (dm_cell_unlock_v2(cache->prison, mg->cell, &bios))
> @@ -1732,7 +1735,6 @@ static int map_bio(struct cache *cache, struct bio 
> *bio, dm_oblock_t block,
>               if (passthrough_mode(cache)) {
>                       if (bio_data_dir(bio) == WRITE) {
>                               bio_drop_shared_lock(cache, bio);
> -                             atomic_inc(&cache->stats.demotion);
>                               invalidate_start(cache, cblock, block, bio);
>                               return DM_MAPIO_SUBMITTED;
>                       } else
> -- 
> 2.49.0


Reply via email to