Re: [PATCH 2/2] x86/tdx: Accept hotplugged memory before online

From: Marc-André Lureau

Date: Mon Mar 30 2026 - 08:23:09 EST


Hi

On Fri, Mar 27, 2026 at 1:09 PM Yan Zhao <yan.y.zhao@xxxxxxxxx> wrote:
>
> On Tue, Mar 24, 2026 at 07:21:48PM +0400, Marc-André Lureau wrote:
> > In TDX guests, hotplugged memory (e.g., via virtio-mem) is never
> > accepted before use. The first access triggers a fatal "SEPT entry in
> > PENDING state" EPT violation and KVM terminates the guest.
> >
> > Fix this by registering a MEM_GOING_ONLINE memory hotplug notifier that
> > calls tdx_accept_memory() for the range being onlined.
> >
> > The notifier returns NOTIFY_BAD on acceptance failure, preventing the
> > memory from going online.
> >
> > Assisted-by: Claude:claude-opus-4-6
> > Reported-by: Chenyi Qiang <chenyi.qiang@xxxxxxxxx>
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@xxxxxxxxxx>
> > ---
> > arch/x86/coco/tdx/tdx.c | 38 ++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 38 insertions(+)
> >
> > diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c
> > index 7b2833705d475..89f90bc303258 100644
> > --- a/arch/x86/coco/tdx/tdx.c
> > +++ b/arch/x86/coco/tdx/tdx.c
> > @@ -8,6 +8,7 @@
> > #include <linux/export.h>
> > #include <linux/io.h>
> > #include <linux/kexec.h>
> > +#include <linux/memory.h>
> > #include <asm/coco.h>
> > #include <asm/tdx.h>
> > #include <asm/vmx.h>
> > @@ -1194,3 +1195,40 @@ void __init tdx_early_init(void)
> >
> > tdx_announce();
> > }
> > +
> > +#ifdef CONFIG_MEMORY_HOTPLUG
> > +static int tdx_guest_memory_notifier(struct notifier_block *nb,
> > + unsigned long action, void *v)
> > +{
> > + struct memory_notify *mn = v;
> > + phys_addr_t start, end;
> > +
> > + if (action != MEM_GOING_ONLINE)
> > + return NOTIFY_OK;
> > +
> > + start = PFN_PHYS(mn->start_pfn);
> > + end = start + PFN_PHYS(mn->nr_pages);
> > +
> > + if (!tdx_accept_memory(start, end)) {
> > + pr_err("Failed to accept memory [0x%llx, 0x%llx)\n",
> > + (unsigned long long)start,
> > + (unsigned long long)end);
> > + return NOTIFY_BAD;
> > + }
> > +
> > + return NOTIFY_OK;
> > +}
> > +
> > +static struct notifier_block tdx_guest_memory_nb = {
> > + .notifier_call = tdx_guest_memory_notifier,
> > +};
> > +
> > +static int __init tdx_guest_memory_init(void)
> > +{
> > + if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST))
> > + return 0;
> > +
> > + return register_memory_notifier(&tdx_guest_memory_nb);
> > +}
> If I read the code correctly,
>
> online_pages
> 1. memory_notify(MEM_GOING_ONLINE, &mem_arg);
> 2. online_pages_range(pfn, nr_pages);
> (*online_page_callback)(page, order);
> generic_online_page
> __free_pages_core(page, order, MEMINIT_HOTPLUG);
>
> In __free_pages_core(), there's accept_memory() already:
>
> if (page_contains_unaccepted(page, order)) {
> if (order == MAX_PAGE_ORDER && __free_unaccepted(page))
> return;
>
> accept_memory(page_to_phys(page), PAGE_SIZE << order);
> }
>
> __free_unaccepted() also adds the pages to the unaccepted_pages list, so
> cond_accept_memory() will accept the memory later:
>
> So, is it because the virtio mem sets online_page_callback to
> virtio_mem_online_page_cb, which doesn't invoke __free_pages_core() properly?
>
> Or am I missing something that makes the memory notifier approach necessary?

virtio-mem doesn't modify efi_unaccepted_memory bitmap (populated by
TDVF code when the VM is started)