From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
To: "Denis V. Lunev" <den@openvz.org>, svt-core@virtuozzo.com
Subject: Re: [PATCH hci-8.0/hci-7.4 1/1] hostmem: fix live update crash of VMs with bound guest memory #VSTOR-147016
Date: Wed, 23 Sep 2026 19:42:28 +0300 [thread overview]
Message-ID: <167c7d00-dbcb-4c3c-9468-918f77f296db@virtuozzo.com> (raw)
In-Reply-To: <20260923162056.3488196-1-den@openvz.org>
FYI: this doesn't apply to hci-7.4. Also see below.
On 9/23/26 7:20 PM, Denis V. Lunev wrote:
> From: Denis V. Lunev <den@openvz.org>
>
> A live QEMU update kills a VM whose memory backend has host-nodes when
> the emulator cgroup no longer allows those nodes. The old QEMU saves its
> state, the new one exits during startup with
>
> qemu-kvm: cannot bind memory to host NUMA nodes: Invalid argument
>
> and the guest is lost.
>
> Guest RAM survives cpr-exec as the same anonymous mapping, carrying its
> pages and its memory policy. host_memory_backend_memory_complete() does
> not know that and treats it as fresh memory: it calls mbind() with
> MPOL_MF_STRICT | MPOL_MF_MOVE and preallocates again. The exec keeps the
> PID and the cgroup, so if cpuset.mems has been changed to nodes outside
> host-nodes since the VM started, mbind() returns EINVAL and QEMU exits.
> Where it succeeds, it only walks or migrates pages that are already in
> place, while the guest is stopped.
>
> Record in ram_block_add() that a block was reused on cpr-exec and skip
> the NUMA binding and preallocation for such blocks. This is a property
> of the block rather than of the incoming mode: file backends get a new
> mapping after the exec and still need mbind(), and memory added later
> keeps the full path.
>
> Signed-off-by: Denis V. Lunev <den@openvz.org>
> ---
> backends/hostmem.c | 4 ++++
> include/exec/cpu-common.h | 1 +
> include/system/ramblock.h | 2 ++
> system/physmem.c | 6 ++++++
> 4 files changed, 13 insertions(+)
>
> diff --git a/backends/hostmem.c b/backends/hostmem.c
> index 35734d6f4d1..acb1bc8dfcb 100644
> --- a/backends/hostmem.c
> +++ b/backends/hostmem.c
> @@ -363,6 +363,10 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
> if (!backend->dump) {
> qemu_madvise(ptr, sz, QEMU_MADV_DONTDUMP);
> }
> + /* Inherited memory keeps the policy and the pages it had before exec */
> + if (qemu_ram_is_cpr_reused(backend->mr.ram_block)) {
> + return;
> + }
Instead of having to keep the state in a 'bool' flag for each and every
block, won't it be enough to just do:
bool reused = cpr_exec_is_incoming() &&
memory_region_get_fd(&backend->mr) < 0;
Andrey
> #ifdef CONFIG_NUMA
> unsigned long lastbit = find_last_bit(backend->host_nodes, MAX_NODES);
> /* lastbit == MAX_NODES means maxnode = 0 */
> diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h
> index 9b658a3f48f..155270b96a7 100644
> --- a/include/exec/cpu-common.h
> +++ b/include/exec/cpu-common.h
> @@ -96,6 +96,7 @@ bool qemu_ram_is_migratable(RAMBlock *rb);
> void qemu_ram_set_migratable(RAMBlock *rb);
> void qemu_ram_unset_migratable(RAMBlock *rb);
> bool qemu_ram_is_named_file(RAMBlock *rb);
> +bool qemu_ram_is_cpr_reused(RAMBlock *rb);
> int qemu_ram_get_fd(RAMBlock *rb);
>
> size_t qemu_ram_pagesize(RAMBlock *block);
> diff --git a/include/system/ramblock.h b/include/system/ramblock.h
> index 87e847e184a..beaa6b23dcb 100644
> --- a/include/system/ramblock.h
> +++ b/include/system/ramblock.h
> @@ -43,6 +43,8 @@ struct RAMBlock {
> QLIST_ENTRY(RAMBlock) next;
> QLIST_HEAD(, RAMBlockNotifier) ramblock_notifiers;
> Error *cpr_blocker;
> + /* Memory was inherited over cpr-exec rather than allocated */
> + bool cpr_reused;
> int fd;
> uint64_t fd_offset;
> int guest_memfd;
> diff --git a/system/physmem.c b/system/physmem.c
> index 6f89232d257..4d649bc57e2 100644
> --- a/system/physmem.c
> +++ b/system/physmem.c
> @@ -1668,6 +1668,11 @@ bool qemu_ram_is_named_file(RAMBlock *rb)
> return rb->flags & RAM_NAMED_FILE;
> }
>
> +bool qemu_ram_is_cpr_reused(RAMBlock *rb)
> +{
> + return rb->cpr_reused;
> +}
> +
> int qemu_ram_get_fd(RAMBlock *rb)
> {
> return rb->fd;
> @@ -1942,6 +1947,7 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
> /* RAM block is being reused on CPR exec */
> assert(ram.length == new_block->max_length);
> new_block->mr->align = QEMU_VMALLOC_ALIGN;
> + new_block->cpr_reused = true;
> } else {
> if (ram.addr || ram.length) {
> error_report("CPR mode=exec wasn't requested, but ram "
prev parent reply other threads:[~2026-09-23 16:42 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-23 16:20 Denis V. Lunev
2026-09-23 16:42 ` Andrey Drobyshev [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=167c7d00-dbcb-4c3c-9468-918f77f296db@virtuozzo.com \
--to=andrey.drobyshev@virtuozzo.com \
--cc=den@openvz.org \
--cc=svt-core@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox