Virtuozzo QEMU development (svt-core@virtuozzo.com)
 help / color / mirror / Atom feed
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 "


      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