From c1085136a0d87045dcb1395cc3195fd74b0b9cb8 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Fri, 13 Nov 2026 14:35:37 -0800 Subject: [PATCH] xen/xenbus: report a ring's host node only when Xen resolved it xenbus_ring_host_node() answered page_to_nid() of the ring's placeholder page for every HVM mapping. That is the host node of the foreign frame only when xen_mfn_to_node() resolved it at map time and the placeholders were relocated to match; nothing recorded whether it had, so the documented NUMA_NO_NODE fallback never happened. Wherever the lookup fails, in a driver domain, on a hypervisor without XENMEM_get_mfn_pxms, under an XSM policy that refuses it, or on a kernel without CONFIG_XEN_BACKEND_NUMA_AFFINITY, the placeholders come from the pool of whichever CPU xenwatch ran on, so every ring reported that one node, and netback and blkback hard-pinned every guest's queue kthreads and event-channel IRQs onto that node's CPUs. Record the resolved node on the mapping, NUMA_NO_NODE when Xen did not say, and have xenbus_ring_host_node() look it up there, as its header comment already promised. Signed-off-by: Ariadne Conill --- drivers/xen/xenbus/xenbus_client.c | 33 +++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/drivers/xen/xenbus/xenbus_client.c b/drivers/xen/xenbus/xenbus_client.c index 382912ed53ddea..38eae5927f833b 100644 --- a/drivers/xen/xenbus/xenbus_client.c +++ b/drivers/xen/xenbus/xenbus_client.c @@ -67,6 +67,10 @@ struct xenbus_map_node { struct page *pages[XENBUS_MAX_RING_PAGES]; unsigned long addrs[XENBUS_MAX_RING_GRANTS]; void *addr; + /* Host node of the foreign frames; NUMA_NO_NODE if Xen + * did not say. + */ + int host_node; } hvm; }; grant_handle_t handles[XENBUS_MAX_RING_GRANTS]; @@ -868,6 +872,7 @@ static int xenbus_map_ring_hvm(struct xenbus_device *dev, } node->hvm.addr = addr; + node->hvm.host_node = host_node; spin_lock(&xenbus_valloc_lock); list_add(&node->next, &xenbus_valloc_pages); @@ -912,24 +917,32 @@ EXPORT_SYMBOL_GPL(xenbus_unmap_ring_vfree); int xenbus_ring_host_node(struct xenbus_device *dev, void *vaddr) { - struct page *page; + struct xenbus_map_node *node; + int host_node = NUMA_NO_NODE; /* * PV mappings install foreign MFNs directly in the PTEs and have - * no struct page in dom0's mem_map for the foreign frame. PVH - * dom0 keeps a placeholder struct page (allocated from the - * matching per-node pool of xen_alloc_unpopulated_pages_node) - * whose page_to_nid() reports the host node of the foreign frame - * by construction. + * no struct page in dom0's mem_map for the foreign frame. An HVM + * mapping records the node Xen reported for the frame when it was + * mapped. Its placeholder pages' own node is no substitute: when + * Xen did not say (a driver domain, a hypervisor without the query, + * a kernel without CONFIG_XEN_BACKEND_NUMA_AFFINITY), they come from + * the pool of whichever CPU xenwatch happened to run on, and every + * ring would report that one node. */ if (xen_pv_domain()) return NUMA_NO_NODE; - page = vmalloc_to_page(vaddr); - if (!page) - return NUMA_NO_NODE; + spin_lock(&xenbus_valloc_lock); + list_for_each_entry(node, &xenbus_valloc_pages, next) { + if (node->hvm.addr == vaddr) { + host_node = node->hvm.host_node; + break; + } + } + spin_unlock(&xenbus_valloc_lock); - return page_to_nid(page); + return host_node; } EXPORT_SYMBOL_GPL(xenbus_ring_host_node);