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 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.