[Devel] [PATCH hci-8.1] block/blk-cbt: account a write request also when dm starts it

no-reply at sashiko.virtuozzo.com no-reply at sashiko.virtuozzo.com
Fri Oct 16 17:41:42 MSK 2026


Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] block: cbt_page_alloc() GFP_ATOMIC failure sets CBT_ERROR on dm dispatch path
--

commit 07126a82d2afc4a47285774d1cb334f3398a6e89
Author: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>

block/blk-cbt: account a write request also when dm starts it

This patch adds blk_cbt_rq_start(), called from dm_start_request(), so a
write is accounted in CBT a second time when request based dm starts it.
A missing bitmap page is allocated with GFP_ATOMIC in this path, and
CBT_ERROR is set if that allocation fails.

Link: https://virtuozzo.atlassian.net/browse/VSTOR-149268

> diff --git a/block/blk-cbt.c b/block/blk-cbt.c
> --- a/block/blk-cbt.c
> +++ b/block/blk-cbt.c

[ ... ]

> @@ -129,11 +137,15 @@ static int cbt_page_alloc(struct cbt_info  **cbt_pp, unsigned long idx,
>  		}
>  	}
>  	spin_unlock_irq(&cbt->lock);
> -	if (in_rcu)
> -		rcu_read_unlock();
> -	page = alloc_page(GFP_NOIO|__GFP_ZERO);
> -	if (in_rcu)
> -		rcu_read_lock();
> +	if (alloc == CBT_ALLOC_ATOMIC) {
> +		page = alloc_page(GFP_ATOMIC | __GFP_ZERO | __GFP_NOWARN);

[Severity: Medium]
Can this GFP_ATOMIC allocation fail often enough to matter?

Under memory pressure alloc_page(GFP_ATOMIC) has no direct reclaim and
can fail.  When it does, a few lines further down in cbt_page_alloc():

	if (unlikely(!page)) {
		set_bit(CBT_ERROR, &cbt->flags);
		spin_unlock_irq(&cbt->lock);
		return -ENOMEM;
	}

CBT_ERROR is a sticky flag, and __GFP_NOWARN makes the failure silent.
The call chain is:

dm_mq_queue_rq()
  dm_start_request()
    blk_cbt_rq_start()
      blk_cbt_add(..., CBT_ALLOC_ATOMIC)
        __blk_cbt_set()
          cbt_page_alloc()

Neither blk_cbt_rq_start() nor blk_cbt_add() checks the return value.

After the failure, blk_cbt_add() returns early for this CBT on every
later write:

	if (unlikely(test_bit(CBT_ERROR, &cbt->flags)))
		return;

cbt_ioc_get() and cbt_ioc_set() then return -EIO.

So a transient atomic allocation failure permanently invalidates the CBT,
and with it the incremental backup chain, until the CBT is re-created.
The old submission time path used GFP_NOIO with the RCU lock dropped,
which does not fail in practice.

Is there a reason not to retry from a sleepable context, or to avoid
setting CBT_ERROR in the atomic case?

[ ... ]

> @@ -248,7 +261,7 @@ static void blk_cbt_add(struct cbt_info *cbt, blkcnt_t start, blkcnt_t len,
>  	put_cpu_ptr(cbt->cache);
>  
>  	if (likely(old.len))
> -		__blk_cbt_set(cbt, old.start, old.len, 1, 1, NULL, NULL);
> +		__blk_cbt_set(cbt, old.start, old.len, alloc, 1, NULL, NULL);

[Severity: Medium]
Is the commit message claim correct that a page can be missing here only
for requests submitted before BLKCBTSTART?

The commit message says:

  "This only happens for requests submitted before BLKCBTSTART: any later
  write has its page allocated already by its submission time accounting."

On the normal (cached) path, blk_cbt_add() only records the extent in the
per-CPU cache.  It calls __blk_cbt_set(), and so allocates pages, only
for the evicted old extent.

That means a write submitted after BLKCBTSTART can still have no bitmap
page when dm_start_request() runs.

In blk_cbt_rq_start(), re-adding a range that is not contiguous with the
cached extent evicts the previous cached extent, which is often an
unrelated earlier write.  That extent is flushed with CBT_ALLOC_ATOMIC.

So GFP_ATOMIC page allocation looks possible on ordinary write-heavy
workloads, not only in the narrow pre-BLKCBTSTART window.

Also, re-adding the same range from blk_cbt_rq_start() never coalesces,
since ex->start + ex->len != start for it.  Each started request
therefore causes an extra __blk_cbt_set() call and bitmap page lock.

Could the commit message and the allocation strategy be revisited in
light of this?

[ ... ]

> +void blk_cbt_rq_start(struct request *rq)
> +{

[ ... ]

> +			blk_cbt_add(cbt, bio->bi_iter.bi_sector << 9,
> +				    bio->bi_iter.bi_size, CBT_ALLOC_ATOMIC);

[Severity: Medium]
This is the same issue as above, seen from the caller.  Errors from
blk_cbt_add() are dropped here, so a CBT_ERROR set by a failed
GFP_ATOMIC allocation in blk_cbt_rq_start() is not reported to the dm
dispatch path.  Is that intended?

[ ... ]

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261016-ptikhomirov-vstor-149268-cbt-account-at-rq-start-v1-1-1f20b151431d@virtuozzo.com?part=1



More information about the Devel mailing list