From: Peter Xu <peterx@redhat.com>
To: hongmianquan <hongmianquan@bytedance.com>
Cc: qemu-devel@nongnu.org, farosas@suse.de,
caixiangfeng@bytedance.com, yanfei.xu@bytedance.com
Subject: Re: [RFC v1] migration/postcopy: fix page_requested leak for vhost-user shared pages
Date: Mon, 24 Aug 2026 15:16:05 -0400 [thread overview]
Message-ID: <aoyYdZj2kEStUTOO@x1.local> (raw)
In-Reply-To: <20260824122655.63646-2-hongmianquan@bytedance.com>
On Mon, Aug 24, 2026 at 08:26:56PM +0800, hongmianquan wrote:
> With postcopy-preempt enabled, a postcopy migration of a guest with a
> vhost-user device can hang at the very end on the destination, after all
> pages are transferred (query-migrate: status=postcopy-active, remaining=0).
>
> Root cause is an add/del key mismatch on mis->page_requested:
>
> - add: a backend fault goes through postcopy_request_shared_page() ->
> postcopy_request_page() -> migrate_send_rp_req_pages(), which inserts
> the request and bumps page_requested_count keyed by client_addr.
> - del: qemu_ufd_copy_ioctl() removes the entry and drops the counter
> keyed by this QEMU process's host address for the page.
>
> client_addr is a VA in the external vhost-user backend's address space and
> never equals QEMU's host address, so the removal misses and the counter
> leaks. postcopy_ram_incoming_cleanup() then waits for it to reach zero
> forever, which also stalls the source (it waits for the return path).
>
> Racing threads:
> dst: postcopy_ram_listen_thread -> postcopy_ram_incoming_cleanup ->
> qemu_cond_wait_impl (waits for page_requested_count==0)
> (postcopy_preempt_thread already placed/woke the pages)
> src: migration_thread -> migration_completion ->
> await_return_path_close_on_source -> qemu_thread_join
> (source_return_path_thread blocked in recvmsg)
>
> The leak is only triggered when the vhost-user backend faults on a page
> that has not been received yet: only then does the request take the
> shared-fault slow path and register an entry in page_requested. If that
> page is subsequently delivered while the request is still outstanding,
> its entry is never removed. (Pages already present when the backend
> faults just take the wake path and never register.) So a run may leak
> only a few entries (9 of ~244k requests in our repro) yet still hang.
>
> Fix: key the request with the same host address the removal uses
> (rb->host + aligned_rbo) instead of client_addr. The backend wake still
> happens at placement time via postcopy_wake_shared().
>
> Signed-off-by: hongmianquan <hongmianquan@bytedance.com>
> ---
> migration/postcopy-ram.c | 15 ++++++++++++++-
> 1 file changed, 14 insertions(+), 1 deletion(-)
>
> diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
> index a3314d3180..98268b3c55 100644
> --- a/migration/postcopy-ram.c
> +++ b/migration/postcopy-ram.c
> @@ -992,7 +992,20 @@ int postcopy_request_shared_page(struct PostCopyFD *pcfd, RAMBlock *rb,
> return postcopy_wake_shared(pcfd, client_addr, rb);
> }
> /* TODO: support blocktime tracking */
> - postcopy_request_page(mis, rb, aligned_rbo, client_addr, 0);
> +
> + /*
> + * The page will be placed by qemu_ufd_copy_ioctl(), which removes the
> + * matching entry from mis->page_requested (and drops
> + * page_requested_count) using this QEMU process's host address for the
> + * page. Register the request with the same key, rb->host + aligned_rbo,
> + * not client_addr: client_addr is a VA in the external vhost-user
> + * backend's address space and can never equal that host address, so the
> + * removal would miss forever, leaking page_requested_count and hanging
> + * postcopy teardown.
> + */
> + postcopy_request_page(mis, rb, aligned_rbo,
> + (uint64_t)(uintptr_t)qemu_ram_get_host_addr(rb) +
> + aligned_rbo, 0);
> return 0;
> }
True.. this should have been overlooked, thanks for fixing it.
Reviewed-by: Peter Xu <peterx@redhat.com>
I haven't been looking at vhost-user path for years, now looking at it
again, I do have this feeling that this ->handler() API is a bit awkward
and too heavy: what we need is a translation of an HVA -> (rb, rb_offset),
then processing it the same as when the fault was generated from QEMU
process. The vhost-user / client code might be more straightforward with a
->translate_hva().
I'm not sure if you agree with that idea, or would like to work on such a
cleanup, but if you're a serious vhost-user postcopy user (we don't have a
lot on the list..), feel free to take this if this sounds beneficial.
Thanks,
--
Peter Xu
next prev parent reply other threads:[~2026-08-24 19:16 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-24 12:26 [RFC v1] migration/postcopy: fix page_requested leak for vhost-user shared pages hongmianquan
2026-08-24 19:16 ` Peter Xu [this message]
2026-08-26 15:18 ` hongmianquan
2026-08-26 15:36 ` Peter Xu
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=aoyYdZj2kEStUTOO@x1.local \
--to=peterx@redhat.com \
--cc=caixiangfeng@bytedance.com \
--cc=farosas@suse.de \
--cc=hongmianquan@bytedance.com \
--cc=qemu-devel@nongnu.org \
--cc=yanfei.xu@bytedance.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.