* [PATCH hci-8.0/hci-7.4 1/1] hostmem: fix live update crash of VMs with bound guest memory #VSTOR-147016
@ 2026-09-23 16:20 Denis V. Lunev
2026-09-23 16:42 ` Andrey Drobyshev
0 siblings, 1 reply; 2+ messages in thread
From: Denis V. Lunev @ 2026-09-23 16:20 UTC (permalink / raw)
To: svt-core; +Cc: andrey.drobyshev, Denis V. Lunev
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;
+ }
#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 "
--
2.53.0
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH hci-8.0/hci-7.4 1/1] hostmem: fix live update crash of VMs with bound guest memory #VSTOR-147016
2026-09-23 16:20 [PATCH hci-8.0/hci-7.4 1/1] hostmem: fix live update crash of VMs with bound guest memory #VSTOR-147016 Denis V. Lunev
@ 2026-09-23 16:42 ` Andrey Drobyshev
0 siblings, 0 replies; 2+ messages in thread
From: Andrey Drobyshev @ 2026-09-23 16:42 UTC (permalink / raw)
To: Denis V. Lunev, svt-core
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 "
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-09-23 16:42 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-23 16:20 [PATCH hci-8.0/hci-7.4 1/1] hostmem: fix live update crash of VMs with bound guest memory #VSTOR-147016 Denis V. Lunev
2026-09-23 16:42 ` Andrey Drobyshev
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox