From: no-reply@sashiko.virtuozzo.com
To: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>,
Konstantin Khorenko <khorenko@virtuozzo.com>
Cc: devel@openvz.org
Subject: Re: [Devel] [PATCH hci-8.1 v4 3/3] block/blk-cbt: account a write request also when dm starts it
Date: Tue, 27 Oct 2026 11:55:00 +0000 [thread overview]
Message-ID: <010701a17d36b738-9f5c4634-66c4-46f6-a68e-12e5b990b59b-000000@eu-central-1.amazonses.com> (raw)
In-Reply-To: <20261027-ptikhomirov-vstor-149268-cbt-account-at-rq-start-v4-3-b4d92264f6a4@virtuozzo.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] block: cbt_alloc_work_fn() races with unlocked CBT_PAGE_MISSED store in __blk_cbt_set()
--
commit e384a8a44cb8b1957ca1f16104c5b091e997663e
Author: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
block/blk-cbt: account a write request also when dm starts it
This patch accounts a write again when request based dm starts it, by setting bits directly in the already-present bitmap pages from blk_cbt_rq_start(). Allocation of missing bitmap pages is queued to kblockd at submission time through cbt_alloc_work_fn(). If a page is still missing at dm start time, dm_mq_queue_rq() returns BLK_STS_RESOURCE so blk-mq retries the request later.
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
[ ... ]
> +/* Allocate the map pages queued in cbt->missing */
> +static void cbt_alloc_work_fn(struct work_struct *work)
> +{
> + struct cbt_info *cbt = container_of(work, struct cbt_info, alloc_work);
> + unsigned long idx;
> +
> + for_each_set_bit(idx, cbt->missing, NR_PAGES(cbt->block_max)) {
> + struct page *page;
> +
> + clear_bit(idx, cbt->missing);
> + if (CBT_PAGE(cbt, idx))
> + continue;
> +
> + page = alloc_page(GFP_NOIO | __GFP_ZERO);
> + spin_lock_irq(&cbt->lock);
> + if (!CBT_PAGE(cbt, idx) && !test_bit(CBT_DEAD, &cbt->flags)) {
> + if (page) {
> + cbt->map[idx] = page;
> + page = NULL;
[Severity: High]
Can this store race with the CBT_PAGE_MISSED store in __blk_cbt_set()?
cbt_alloc_work_fn() installs the new page under cbt->lock. However, the
sentinel store in __blk_cbt_set() takes no cbt->lock:
block/blk-cbt.c:__blk_cbt_set() {
...
} else if (pages_missed) {
(*pages_missed)++;
if (!*idx_first)
*idx_first = idx;
cbt->map[idx] = CBT_PAGE_MISSED;
...
}
__blk_cbt_set() is reached with a non-NULL pages_missed from the
on_each_cpu() IPI in cbt_flush_cache():
on_each_cpu(__cbt_flush_cpu_cache, &ctx, 1);
A possible interleaving:
CPU B (IPI, __blk_cbt_set)
page = CBT_PAGE(cbt, idx); /* NULL */
CPU A (kblockd, cbt_alloc_work_fn)
spin_lock_irq(&cbt->lock);
cbt->map[idx] = page;
CPU B
cbt->map[idx] = CBT_PAGE_MISSED; /* overwrites the new page */
The new page is then no longer reachable, since CBT_PAGE() treats the slot
as empty and cbt_release_callback() skips it, so it is leaked.
Any dirty bits already in that page are also lost. This includes bits set
by cbt_set_present() from blk_cbt_rq_start(), which is the case this patch
is meant to protect. A later cbt_page_alloc() from cbt_flush_cache() would
install a fresh zeroed page.
Before this patch the window was much narrower. Now cbt_queue_alloc_pages()
queues an asynchronous installer on every write that touches a missing
page, unless CBT_NOCACHE is set. Neither cbt_mutex nor any other exclusion
keeps cbt_alloc_work_fn() out while cbt_flush_cache() runs.
Could the sentinel store be done under cbt->lock, or could the flush path
be serialized against alloc_work (for example with flush_work())?
[ ... ]
> +static bool cbt_set_present(struct cbt_info *cbt, blkcnt_t start, blkcnt_t len)
> +{
[ ... ]
> + struct page *page = CBT_PAGE(cbt, idx);
> +
> + if (page) {
> + local_irq_save(flags);
> + spin_lock_page(page);
> + set_bits(page_address(page), off, n, 1);
[Severity: High]
Related to the comment above: bits set here in cbt_set_present() are lost
if the page is subsequently discarded by the unlocked CBT_PAGE_MISSED store
in __blk_cbt_set(). That would leave an incremental backup missing changed
data for a write that dm has already started.
[ ... ]
--
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261027-ptikhomirov-vstor-149268-cbt-account-at-rq-start-v4-0-b4d92264f6a4@virtuozzo.com?part=3
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
prev parent reply other threads:[~2026-10-27 11:56 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-27 11:47 [Devel] [PATCH hci-8.1 v4 0/3] block/blk-cbt: don't lose writes held across a dm suspend Pavel Tikhomirov
2026-10-27 11:47 ` [Devel] [PATCH hci-8.1 v4 1/3] block/blk-cbt: shift the block size as unsigned Pavel Tikhomirov
2026-10-27 11:50 ` no-reply
2026-10-27 11:47 ` [Devel] [PATCH hci-8.1 v4 2/3] block/blk-throttle: account a throttled write in CBT Pavel Tikhomirov
2026-10-27 11:52 ` no-reply
2026-10-27 11:47 ` [Devel] [PATCH hci-8.1 v4 3/3] block/blk-cbt: account a write request also when dm starts it Pavel Tikhomirov
2026-10-27 11:55 ` no-reply [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=010701a17d36b738-9f5c4634-66c4-46f6-a68e-12e5b990b59b-000000@eu-central-1.amazonses.com \
--to=no-reply@sashiko.virtuozzo.com \
--cc=devel@openvz.org \
--cc=khorenko@virtuozzo.com \
--cc=ptikhomirov@virtuozzo.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.