From 63dc2c34a91642d9ae615a9609841fa317a36c92 Mon Sep 17 00:00:00 2001 From: Hou Wenlong Date: Thu, 22 Jan 2026 18:06:14 +0800 Subject: [PATCH 1/6] x86/xen: Build identity mapping page tables dynamically for XENPV After commit 47ffe0578aee ("x86/pvh: Add 64bit relocation page tables"), the PVH entry uses a new set of page tables instead of the preconstructed page tables in head64.S. Since those preconstructed page tables are only used in XENPV now and XENPV does not actually need the preconstructed identity page tables directly, they can be filled in xen_setup_kernel_pagetable(). Therefore, build the identity mapping page table dynamically to remove the preconstructed page tables and make the code cleaner. Signed-off-by: Hou Wenlong Reviewed-by: Juergen Gross Acked-by: "Borislav Petkov (AMD)" Signed-off-by: Juergen Gross Message-ID: <453981eae7e8158307f971d1632d5023adbe03c3.1769074722.git.houwenlong.hwl@antgroup.com> --- arch/x86/include/asm/pgtable_64.h | 2 -- arch/x86/kernel/head_64.S | 28 ---------------------------- arch/x86/xen/mmu_pv.c | 9 +++++++++ 3 files changed, 9 insertions(+), 30 deletions(-) diff --git a/arch/x86/include/asm/pgtable_64.h b/arch/x86/include/asm/pgtable_64.h index f06e5d6a2747..ce45882ccd07 100644 --- a/arch/x86/include/asm/pgtable_64.h +++ b/arch/x86/include/asm/pgtable_64.h @@ -19,10 +19,8 @@ extern p4d_t level4_kernel_pgt[512]; extern p4d_t level4_ident_pgt[512]; extern pud_t level3_kernel_pgt[512]; -extern pud_t level3_ident_pgt[512]; extern pmd_t level2_kernel_pgt[512]; extern pmd_t level2_fixmap_pgt[512]; -extern pmd_t level2_ident_pgt[512]; extern pte_t level1_fixmap_pgt[512 * FIXMAP_PMD_NUM]; extern pgd_t init_top_pgt[]; diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S index 21816b48537c..85d4a5094f6b 100644 --- a/arch/x86/kernel/head_64.S +++ b/arch/x86/kernel/head_64.S @@ -616,38 +616,10 @@ SYM_DATA(early_recursion_flag, .long 0) .data -#if defined(CONFIG_XEN_PV) || defined(CONFIG_PVH) -SYM_DATA_START_PTI_ALIGNED(init_top_pgt) - .quad level3_ident_pgt - __START_KERNEL_map + _KERNPG_TABLE_NOENC - .org init_top_pgt + L4_PAGE_OFFSET*8, 0 - .quad level3_ident_pgt - __START_KERNEL_map + _KERNPG_TABLE_NOENC - .org init_top_pgt + L4_START_KERNEL*8, 0 - /* (2^48-(2*1024*1024*1024))/(2^39) = 511 */ - .quad level3_kernel_pgt - __START_KERNEL_map + _PAGE_TABLE_NOENC - .fill PTI_USER_PGD_FILL,8,0 -SYM_DATA_END(init_top_pgt) - -SYM_DATA_START_PAGE_ALIGNED(level3_ident_pgt) - .quad level2_ident_pgt - __START_KERNEL_map + _KERNPG_TABLE_NOENC - .fill 511, 8, 0 -SYM_DATA_END(level3_ident_pgt) -SYM_DATA_START_PAGE_ALIGNED(level2_ident_pgt) - /* - * Since I easily can, map the first 1G. - * Don't set NX because code runs from these pages. - * - * Note: This sets _PAGE_GLOBAL despite whether - * the CPU supports it or it is enabled. But, - * the CPU should ignore the bit. - */ - PMDS(0, __PAGE_KERNEL_IDENT_LARGE_EXEC, PTRS_PER_PMD) -SYM_DATA_END(level2_ident_pgt) -#else SYM_DATA_START_PTI_ALIGNED(init_top_pgt) .fill 512,8,0 .fill PTI_USER_PGD_FILL,8,0 SYM_DATA_END(init_top_pgt) -#endif SYM_DATA_START_PAGE_ALIGNED(level4_kernel_pgt) .fill 511,8,0 diff --git a/arch/x86/xen/mmu_pv.c b/arch/x86/xen/mmu_pv.c index aa6755385887..50172d64aed6 100644 --- a/arch/x86/xen/mmu_pv.c +++ b/arch/x86/xen/mmu_pv.c @@ -105,6 +105,9 @@ pte_t xen_make_pte_init(pteval_t pte); static pud_t level3_user_vsyscall[PTRS_PER_PUD] __page_aligned_bss; #endif +static pud_t level3_ident_pgt[PTRS_PER_PUD] __page_aligned_bss; +static pmd_t level2_ident_pgt[PTRS_PER_PMD] __page_aligned_bss; + /* * Protects atomic reservation decrease/increase against concurrent increases. * Also protects non-atomic updates of current_pages and balloon lists. @@ -1777,6 +1780,12 @@ void __init xen_setup_kernel_pagetable(pgd_t *pgd, unsigned long max_pfn) /* Zap identity mapping */ init_top_pgt[0] = __pgd(0); + init_top_pgt[pgd_index(__PAGE_OFFSET_BASE_L4)].pgd = + __pa_symbol(level3_ident_pgt) + _KERNPG_TABLE_NOENC; + init_top_pgt[pgd_index(__START_KERNEL_map)].pgd = + __pa_symbol(level3_kernel_pgt) + _PAGE_TABLE_NOENC; + level3_ident_pgt[0].pud = __pa_symbol(level2_ident_pgt) + _KERNPG_TABLE_NOENC; + /* Pre-constructed entries are in pfn, so convert to mfn */ /* L4[273] -> level3_ident_pgt */ /* L4[511] -> level3_kernel_pgt */ From 8b57227d59a86fc06d4f09de08f98133680f2cae Mon Sep 17 00:00:00 2001 From: David Thomson Date: Tue, 24 Feb 2026 09:37:11 +0000 Subject: [PATCH 2/6] xen/acpi-processor: fix _CST detection using undersized evaluation buffer read_acpi_id() attempts to evaluate _CST using a stack buffer of sizeof(union acpi_object) (48 bytes), but _CST returns a nested Package of sub-Packages (one per C-state, each containing a register descriptor, type, latency, and power) requiring hundreds of bytes. The evaluation always fails with AE_BUFFER_OVERFLOW. On modern systems using FFH/MWAIT entry (where pblk is zero), this causes the function to return before setting the acpi_id_cst_present bit. In check_acpi_ids(), flags.power is then zero for all Phase 2 CPUs (physical CPUs beyond dom0's vCPU count), so push_cxx_to_hypervisor() is never called for them. On a system with dom0_max_vcpus=2 and 8 physical CPUs, only PCPUs 0-1 receive C-state data. PCPUs 2-7 are stuck in C0/C1 idle, unable to enter C2/C3. This costs measurable wall power (4W observed on an Intel Core Ultra 7 265K with Xen 4.20). The function never uses the _CST return value -- it only needs to know whether _CST exists. Replace the broken acpi_evaluate_object() call with acpi_has_method(), which correctly detects _CST presence using acpi_get_handle() without any buffer allocation. This brings C-state detection to parity with the P-state path, which already works correctly for Phase 2 CPUs. Fixes: 59a568029181 ("xen/acpi-processor: C and P-state driver that uploads said data to hypervisor.") Signed-off-by: David Thomson Reviewed-by: Jan Beulich Signed-off-by: Juergen Gross Message-ID: <20260224093707.19679-1-dt@linux-mail.net> --- drivers/xen/xen-acpi-processor.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c index f2e8eaf684ba..8d1860bd5d57 100644 --- a/drivers/xen/xen-acpi-processor.c +++ b/drivers/xen/xen-acpi-processor.c @@ -379,11 +379,8 @@ read_acpi_id(acpi_handle handle, u32 lvl, void *context, void **rv) acpi_psd[acpi_id].domain); } - status = acpi_evaluate_object(handle, "_CST", NULL, &buffer); - if (ACPI_FAILURE(status)) { - if (!pblk) - return AE_OK; - } + if (!pblk && !acpi_has_method(handle, "_CST")) + return AE_OK; /* .. and it has a C-state */ __set_bit(acpi_id, acpi_id_cst_present); From b8c460a045985a8cb7a7c34912d2db91afee242f Mon Sep 17 00:00:00 2001 From: kexinsun Date: Tue, 24 Feb 2026 10:24:24 +0800 Subject: [PATCH 3/6] x86/xen: update outdated comment The function xen_flush_tlb_others() was renamed xen_flush_tlb_multi() by commit 4ce94eabac16 ("x86/mm/tlb: Flush remote and local TLBs concurrently"). Update the comment accordingly. Signed-off-by: kexinsun Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross Message-ID: <20260224022424.1718-1-kexinsun@smail.nju.edu.cn> --- arch/x86/xen/enlighten_pv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/xen/enlighten_pv.c b/arch/x86/xen/enlighten_pv.c index b74ff8bc7f2a..fde5f44460ee 100644 --- a/arch/x86/xen/enlighten_pv.c +++ b/arch/x86/xen/enlighten_pv.c @@ -392,7 +392,7 @@ static void __init xen_init_capabilities(void) /* * Xen PV would need some work to support PCID: CR3 handling as well - * as xen_flush_tlb_others() would need updating. + * as xen_flush_tlb_multi() would need updating. */ setup_clear_cpu_cap(X86_FEATURE_PCID); From 7271cb98e4981e32c9b2a5f9eb5ad0cd0cdd5bf3 Mon Sep 17 00:00:00 2001 From: Hou Wenlong Date: Sun, 1 Mar 2026 13:04:52 +0800 Subject: [PATCH 4/6] x86/PVH: Use boot params to pass RSDP address in start_info page After commit e6e094e053af75 ("x86/acpi, x86/boot: Take RSDP address from boot params if available"), the RSDP address can be passed in boot params. Therefore, store the RSDP address in start_info page into boot params in the PVH entry instead of registering a different callback. This removes an absolute reference during the PVH entry and is more standardized. Signed-off-by: Hou Wenlong Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross Message-ID: <76675c4d49d3a8f72252076812ef8f22276230c2.1772282441.git.houwenlong.hwl@antgroup.com> --- arch/x86/platform/pvh/enlighten.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/arch/x86/platform/pvh/enlighten.c b/arch/x86/platform/pvh/enlighten.c index 2263885d16ba..f2053cbe9b0c 100644 --- a/arch/x86/platform/pvh/enlighten.c +++ b/arch/x86/platform/pvh/enlighten.c @@ -25,11 +25,6 @@ struct hvm_start_info __initdata pvh_start_info; const unsigned int __initconst pvh_start_info_sz = sizeof(pvh_start_info); -static u64 __init pvh_get_root_pointer(void) -{ - return pvh_start_info.rsdp_paddr; -} - /* * Xen guests are able to obtain the memory map from the hypervisor via the * HYPERVISOR_memory_op hypercall. @@ -95,7 +90,7 @@ static void __init init_pvh_bootparams(bool xen_guest) pvh_bootparams.hdr.version = (2 << 8) | 12; pvh_bootparams.hdr.type_of_loader = ((xen_guest ? 0x9 : 0xb) << 4) | 0; - x86_init.acpi.get_root_pointer = pvh_get_root_pointer; + pvh_bootparams.acpi_rsdp_addr = pvh_start_info.rsdp_paddr; } /* From 82169dace41cbaa951341b0f80f4570be3b2dec0 Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Wed, 18 Feb 2026 10:52:04 +0100 Subject: [PATCH 5/6] xenbus: add xenbus_device parameter to xenbus_read_driver_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to prepare checking the xenbus device status in xenbus_read_driver_state(), add the pointer to struct xenbus_device as a parameter. Tested-by: Marek Marczykowski-Górecki Signed-off-by: Juergen Gross Acked-by: "Martin K. Petersen" # SCSI Acked-by: Jakub Kicinski Acked-by: Bjorn Helgaas # drivers/pci/xen-pcifront.c Signed-off-by: Juergen Gross Message-ID: <20260218095205.453657-2-jgross@suse.com> --- drivers/net/xen-netfront.c | 34 +++++++++++----------- drivers/pci/xen-pcifront.c | 8 ++--- drivers/scsi/xen-scsifront.c | 2 +- drivers/xen/xen-pciback/xenbus.c | 10 +++---- drivers/xen/xenbus/xenbus_client.c | 4 ++- drivers/xen/xenbus/xenbus_probe.c | 6 ++-- drivers/xen/xenbus/xenbus_probe_frontend.c | 2 +- include/xen/xenbus.h | 3 +- 8 files changed, 36 insertions(+), 33 deletions(-) diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 7c2220366623..e2da977c9c50 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1646,7 +1646,7 @@ static int xennet_xdp_set(struct net_device *dev, struct bpf_prog *prog, /* avoid the race with XDP headroom adjustment */ wait_event(module_wq, - xenbus_read_driver_state(np->xbdev->otherend) == + xenbus_read_driver_state(np->xbdev, np->xbdev->otherend) == XenbusStateReconfigured); np->netfront_xdp_enabled = true; @@ -1764,9 +1764,9 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev) do { xenbus_switch_state(dev, XenbusStateInitialising); err = wait_event_timeout(module_wq, - xenbus_read_driver_state(dev->otherend) != + xenbus_read_driver_state(dev, dev->otherend) != XenbusStateClosed && - xenbus_read_driver_state(dev->otherend) != + xenbus_read_driver_state(dev, dev->otherend) != XenbusStateUnknown, XENNET_TIMEOUT); } while (!err); @@ -2627,31 +2627,31 @@ static void xennet_bus_close(struct xenbus_device *dev) { int ret; - if (xenbus_read_driver_state(dev->otherend) == XenbusStateClosed) + if (xenbus_read_driver_state(dev, dev->otherend) == XenbusStateClosed) return; do { xenbus_switch_state(dev, XenbusStateClosing); ret = wait_event_timeout(module_wq, - xenbus_read_driver_state(dev->otherend) == - XenbusStateClosing || - xenbus_read_driver_state(dev->otherend) == - XenbusStateClosed || - xenbus_read_driver_state(dev->otherend) == - XenbusStateUnknown, - XENNET_TIMEOUT); + xenbus_read_driver_state(dev, dev->otherend) == + XenbusStateClosing || + xenbus_read_driver_state(dev, dev->otherend) == + XenbusStateClosed || + xenbus_read_driver_state(dev, dev->otherend) == + XenbusStateUnknown, + XENNET_TIMEOUT); } while (!ret); - if (xenbus_read_driver_state(dev->otherend) == XenbusStateClosed) + if (xenbus_read_driver_state(dev, dev->otherend) == XenbusStateClosed) return; do { xenbus_switch_state(dev, XenbusStateClosed); ret = wait_event_timeout(module_wq, - xenbus_read_driver_state(dev->otherend) == - XenbusStateClosed || - xenbus_read_driver_state(dev->otherend) == - XenbusStateUnknown, - XENNET_TIMEOUT); + xenbus_read_driver_state(dev, dev->otherend) == + XenbusStateClosed || + xenbus_read_driver_state(dev, dev->otherend) == + XenbusStateUnknown, + XENNET_TIMEOUT); } while (!ret); } diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c index 11636634ae51..cd22bf984024 100644 --- a/drivers/pci/xen-pcifront.c +++ b/drivers/pci/xen-pcifront.c @@ -856,7 +856,7 @@ static void pcifront_try_connect(struct pcifront_device *pdev) int err; /* Only connect once */ - if (xenbus_read_driver_state(pdev->xdev->nodename) != + if (xenbus_read_driver_state(pdev->xdev, pdev->xdev->nodename) != XenbusStateInitialised) return; @@ -876,7 +876,7 @@ static int pcifront_try_disconnect(struct pcifront_device *pdev) enum xenbus_state prev_state; - prev_state = xenbus_read_driver_state(pdev->xdev->nodename); + prev_state = xenbus_read_driver_state(pdev->xdev, pdev->xdev->nodename); if (prev_state >= XenbusStateClosing) goto out; @@ -895,7 +895,7 @@ out: static void pcifront_attach_devices(struct pcifront_device *pdev) { - if (xenbus_read_driver_state(pdev->xdev->nodename) == + if (xenbus_read_driver_state(pdev->xdev, pdev->xdev->nodename) == XenbusStateReconfiguring) pcifront_connect(pdev); } @@ -909,7 +909,7 @@ static int pcifront_detach_devices(struct pcifront_device *pdev) struct pci_dev *pci_dev; char str[64]; - state = xenbus_read_driver_state(pdev->xdev->nodename); + state = xenbus_read_driver_state(pdev->xdev, pdev->xdev->nodename); if (state == XenbusStateInitialised) { dev_dbg(&pdev->xdev->dev, "Handle skipped connect.\n"); /* We missed Connected and need to initialize. */ diff --git a/drivers/scsi/xen-scsifront.c b/drivers/scsi/xen-scsifront.c index 924025305753..ef74d4da5ab0 100644 --- a/drivers/scsi/xen-scsifront.c +++ b/drivers/scsi/xen-scsifront.c @@ -1175,7 +1175,7 @@ static void scsifront_backend_changed(struct xenbus_device *dev, return; } - if (xenbus_read_driver_state(dev->nodename) == + if (xenbus_read_driver_state(dev, dev->nodename) == XenbusStateInitialised) scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_ADD_LUN); diff --git a/drivers/xen/xen-pciback/xenbus.c b/drivers/xen/xen-pciback/xenbus.c index b11e401f1b1e..4bd1c7a8957e 100644 --- a/drivers/xen/xen-pciback/xenbus.c +++ b/drivers/xen/xen-pciback/xenbus.c @@ -149,12 +149,12 @@ static int xen_pcibk_attach(struct xen_pcibk_device *pdev) mutex_lock(&pdev->dev_lock); /* Make sure we only do this setup once */ - if (xenbus_read_driver_state(pdev->xdev->nodename) != + if (xenbus_read_driver_state(pdev->xdev, pdev->xdev->nodename) != XenbusStateInitialised) goto out; /* Wait for frontend to state that it has published the configuration */ - if (xenbus_read_driver_state(pdev->xdev->otherend) != + if (xenbus_read_driver_state(pdev->xdev, pdev->xdev->otherend) != XenbusStateInitialised) goto out; @@ -374,7 +374,7 @@ static int xen_pcibk_reconfigure(struct xen_pcibk_device *pdev, dev_dbg(&pdev->xdev->dev, "Reconfiguring device ...\n"); mutex_lock(&pdev->dev_lock); - if (xenbus_read_driver_state(pdev->xdev->nodename) != state) + if (xenbus_read_driver_state(pdev->xdev, pdev->xdev->nodename) != state) goto out; err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, "num_devs", "%d", @@ -572,7 +572,7 @@ static int xen_pcibk_setup_backend(struct xen_pcibk_device *pdev) /* It's possible we could get the call to setup twice, so make sure * we're not already connected. */ - if (xenbus_read_driver_state(pdev->xdev->nodename) != + if (xenbus_read_driver_state(pdev->xdev, pdev->xdev->nodename) != XenbusStateInitWait) goto out; @@ -662,7 +662,7 @@ static void xen_pcibk_be_watch(struct xenbus_watch *watch, struct xen_pcibk_device *pdev = container_of(watch, struct xen_pcibk_device, be_watch); - switch (xenbus_read_driver_state(pdev->xdev->nodename)) { + switch (xenbus_read_driver_state(pdev->xdev, pdev->xdev->nodename)) { case XenbusStateInitWait: xen_pcibk_setup_backend(pdev); break; diff --git a/drivers/xen/xenbus/xenbus_client.c b/drivers/xen/xenbus/xenbus_client.c index 2dc874fb5506..e502435145ae 100644 --- a/drivers/xen/xenbus/xenbus_client.c +++ b/drivers/xen/xenbus/xenbus_client.c @@ -931,12 +931,14 @@ static int xenbus_unmap_ring_hvm(struct xenbus_device *dev, void *vaddr) /** * xenbus_read_driver_state - read state from a store path + * @dev: xenbus device pointer * @path: path for driver * * Returns: the state of the driver rooted at the given store path, or * XenbusStateUnknown if no state can be read. */ -enum xenbus_state xenbus_read_driver_state(const char *path) +enum xenbus_state xenbus_read_driver_state(const struct xenbus_device *dev, + const char *path) { enum xenbus_state result; int err = xenbus_gather(XBT_NIL, path, "state", "%d", &result, NULL); diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index 9f9011cd7447..2eed06ba5d38 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -191,7 +191,7 @@ void xenbus_otherend_changed(struct xenbus_watch *watch, return; } - state = xenbus_read_driver_state(dev->otherend); + state = xenbus_read_driver_state(dev, dev->otherend); dev_dbg(&dev->dev, "state is %d, (%s), %s, %s\n", state, xenbus_strstate(state), dev->otherend_watch.node, path); @@ -364,7 +364,7 @@ void xenbus_dev_remove(struct device *_dev) * closed. */ if (!drv->allow_rebind || - xenbus_read_driver_state(dev->nodename) == XenbusStateClosing) + xenbus_read_driver_state(dev, dev->nodename) == XenbusStateClosing) xenbus_switch_state(dev, XenbusStateClosed); } EXPORT_SYMBOL_GPL(xenbus_dev_remove); @@ -514,7 +514,7 @@ int xenbus_probe_node(struct xen_bus_type *bus, size_t stringlen; char *tmpstring; - enum xenbus_state state = xenbus_read_driver_state(nodename); + enum xenbus_state state = xenbus_read_driver_state(NULL, nodename); if (state != XenbusStateInitialising) { /* Device is not new, so ignore it. This can happen if a diff --git a/drivers/xen/xenbus/xenbus_probe_frontend.c b/drivers/xen/xenbus/xenbus_probe_frontend.c index f04707d1f667..ca04609730df 100644 --- a/drivers/xen/xenbus/xenbus_probe_frontend.c +++ b/drivers/xen/xenbus/xenbus_probe_frontend.c @@ -253,7 +253,7 @@ static int print_device_status(struct device *dev, void *data) } else if (xendev->state < XenbusStateConnected) { enum xenbus_state rstate = XenbusStateUnknown; if (xendev->otherend) - rstate = xenbus_read_driver_state(xendev->otherend); + rstate = xenbus_read_driver_state(xendev, xendev->otherend); pr_warn("Timeout connecting to device: %s (local state %d, remote state %d)\n", xendev->nodename, xendev->state, rstate); } diff --git a/include/xen/xenbus.h b/include/xen/xenbus.h index c94caf852aea..15319da65b7f 100644 --- a/include/xen/xenbus.h +++ b/include/xen/xenbus.h @@ -228,7 +228,8 @@ int xenbus_unmap_ring_vfree(struct xenbus_device *dev, void *vaddr); int xenbus_alloc_evtchn(struct xenbus_device *dev, evtchn_port_t *port); int xenbus_free_evtchn(struct xenbus_device *dev, evtchn_port_t port); -enum xenbus_state xenbus_read_driver_state(const char *path); +enum xenbus_state xenbus_read_driver_state(const struct xenbus_device *dev, + const char *path); __printf(3, 4) void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt, ...); From e2dcf9065536ab4a1b00828ff0d19f7d282dfecc Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Wed, 18 Feb 2026 10:52:05 +0100 Subject: [PATCH 6/6] xen/xenbus: better handle backend crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the backend domain crashes, coordinated device cleanup is not possible (as it involves waiting for the backend state change). In that case, toolstack forcefully removes frontend xenstore entries. xenbus_dev_changed() handles this case, and triggers device cleanup. It's possible that toolstack manages to connect new device in that place, before xenbus_dev_changed() notices the old one is missing. If that happens, new one won't be probed and will forever remain in XenbusStateInitialising. Fix this by checking the frontend's state in Xenstore. In case it has been reset to XenbusStateInitialising by Xen tools, consider this being the result of an unplug+plug operation. It's important that cleanup on such unplug doesn't modify Xenstore entries (especially the "state" key) as it belong to the new device to be probed - changing it would derail establishing connection to the new backend (most likely, closing the device before it was even connected). Handle this case by setting new xenbus_device->vanished flag to true, and check it before changing state entry. And even if xenbus_dev_changed() correctly detects the device was forcefully removed, the cleanup handling is still racy. Since this whole handling doesn't happened in a single Xenstore transaction, it's possible that toolstack might put a new device there already. Avoid re-creating the state key (which in the case of loosing the race would actually close newly attached device). The problem does not apply to frontend domain crash, as this case involves coordinated cleanup. Problem originally reported at https://lore.kernel.org/xen-devel/aOZvivyZ9YhVWDLN@mail-itl/T/#t, including reproduction steps. Based-on-patch-by: Marek Marczykowski-Górecki Tested-by: Marek Marczykowski-Górecki Signed-off-by: Juergen Gross Message-ID: <20260218095205.453657-3-jgross@suse.com> --- drivers/xen/xenbus/xenbus_client.c | 13 +++++++++-- drivers/xen/xenbus/xenbus_probe.c | 36 ++++++++++++++++++++++++++++++ include/xen/xenbus.h | 1 + 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/drivers/xen/xenbus/xenbus_client.c b/drivers/xen/xenbus/xenbus_client.c index e502435145ae..19672b08a680 100644 --- a/drivers/xen/xenbus/xenbus_client.c +++ b/drivers/xen/xenbus/xenbus_client.c @@ -226,8 +226,9 @@ __xenbus_switch_state(struct xenbus_device *dev, struct xenbus_transaction xbt; int current_state; int err, abort; + bool vanished = false; - if (state == dev->state) + if (state == dev->state || dev->vanished) return 0; again: @@ -242,6 +243,10 @@ again: err = xenbus_scanf(xbt, dev->nodename, "state", "%d", ¤t_state); if (err != 1) goto abort; + if (current_state != dev->state && current_state == XenbusStateInitialising) { + vanished = true; + goto abort; + } err = xenbus_printf(xbt, dev->nodename, "state", "%d", state); if (err) { @@ -256,7 +261,7 @@ abort: if (err == -EAGAIN && !abort) goto again; xenbus_switch_fatal(dev, depth, err, "ending transaction"); - } else + } else if (!vanished) dev->state = state; return 0; @@ -941,6 +946,10 @@ enum xenbus_state xenbus_read_driver_state(const struct xenbus_device *dev, const char *path) { enum xenbus_state result; + + if (dev && dev->vanished) + return XenbusStateUnknown; + int err = xenbus_gather(XBT_NIL, path, "state", "%d", &result, NULL); if (err) result = XenbusStateUnknown; diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index 2eed06ba5d38..eb260eceb4d2 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -444,6 +444,9 @@ static void xenbus_cleanup_devices(const char *path, struct bus_type *bus) info.dev = NULL; bus_for_each_dev(bus, NULL, &info, cleanup_dev); if (info.dev) { + dev_warn(&info.dev->dev, + "device forcefully removed from xenstore\n"); + info.dev->vanished = true; device_unregister(&info.dev->dev); put_device(&info.dev->dev); } @@ -659,6 +662,39 @@ void xenbus_dev_changed(const char *node, struct xen_bus_type *bus) return; dev = xenbus_device_find(root, &bus->bus); + /* + * Backend domain crash results in not coordinated frontend removal, + * without going through XenbusStateClosing. If this is a new instance + * of the same device Xen tools will have reset the state to + * XenbusStateInitializing. + * It might be that the backend crashed early during the init phase of + * device setup, in which case the known state would have been + * XenbusStateInitializing. So test the backend domid to match the + * saved one. In case the new backend happens to have the same domid as + * the old one, we can just carry on, as there is no inconsistency + * resulting in this case. + */ + if (dev && !strcmp(bus->root, "device")) { + enum xenbus_state state = xenbus_read_driver_state(dev, dev->nodename); + unsigned int backend = xenbus_read_unsigned(root, "backend-id", + dev->otherend_id); + + if (state == XenbusStateInitialising && + (state != dev->state || backend != dev->otherend_id)) { + /* + * State has been reset, assume the old one vanished + * and new one needs to be probed. + */ + dev_warn(&dev->dev, + "state reset occurred, reconnecting\n"); + dev->vanished = true; + } + if (dev->vanished) { + device_unregister(&dev->dev); + put_device(&dev->dev); + dev = NULL; + } + } if (!dev) xenbus_probe_node(bus, type, root); else diff --git a/include/xen/xenbus.h b/include/xen/xenbus.h index 15319da65b7f..8ca15743af7f 100644 --- a/include/xen/xenbus.h +++ b/include/xen/xenbus.h @@ -80,6 +80,7 @@ struct xenbus_device { const char *devicetype; const char *nodename; const char *otherend; + bool vanished; int otherend_id; struct xenbus_watch otherend_watch; struct device dev;