git: 561571d48067 - main - nhi: Fix a race with interrupt teardown during detach

From: John Baldwin <jhb_at_FreeBSD.org>
Date: Wed, 29 Oct 2025 18:56:42 UTC
The branch main has been updated by jhb:

URL: https://cgit.FreeBSD.org/src/commit/?id=561571d480679933287a6d21c6929c39a7963857

commit 561571d480679933287a6d21c6929c39a7963857
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2025-10-29 18:56:17 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2025-10-29 18:56:17 +0000

    nhi: Fix a race with interrupt teardown during detach
    
    When nhi fails to attach on one of my systems (X1 Carbon 6th gen), an
    interrupt races with the teardown code such that the rings have been
    freed by nhi_detach() before an interrupt triggers.  At the time of
    the crash, the thread invoking nhi_attach() is blocked in
    bus_teardown_intr() from nhi_pci_free() waiting for the interrupt
    thread to finish executing the handler.
    
    To fix, don't just disable the interrupts in nhi_detach(), but
    actually tear the handlers down and disable MSI-X before freeing the
    rings.
    
    Reviewed by:    obiwac
    Differential Revision:  https://reviews.freebsd.org/D53201
---
 sys/dev/thunderbolt/nhi.c     |  1 +
 sys/dev/thunderbolt/nhi_pci.c | 10 +++++++---
 sys/dev/thunderbolt/nhi_var.h |  1 +
 3 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/sys/dev/thunderbolt/nhi.c b/sys/dev/thunderbolt/nhi.c
index 205e69c16253..30a72652535a 100644
--- a/sys/dev/thunderbolt/nhi.c
+++ b/sys/dev/thunderbolt/nhi.c
@@ -322,6 +322,7 @@ nhi_detach(struct nhi_softc *sc)
 	tbdev_remove_interface(sc);
 
 	nhi_pci_disable_interrupts(sc);
+	nhi_pci_free_interrupts(sc);
 
 	nhi_free_ring0(sc);
 
diff --git a/sys/dev/thunderbolt/nhi_pci.c b/sys/dev/thunderbolt/nhi_pci.c
index 7dacff523cef..865963e275ec 100644
--- a/sys/dev/thunderbolt/nhi_pci.c
+++ b/sys/dev/thunderbolt/nhi_pci.c
@@ -67,7 +67,7 @@ static int	nhi_pci_suspend(device_t);
 static int	nhi_pci_resume(device_t);
 static void	nhi_pci_free(struct nhi_softc *);
 static int	nhi_pci_allocate_interrupts(struct nhi_softc *);
-static void	nhi_pci_free_interrupts(struct nhi_softc *);
+static void	nhi_pci_free_resources(struct nhi_softc *);
 static int	nhi_pci_icl_poweron(struct nhi_softc *);
 
 static device_method_t nhi_methods[] = {
@@ -253,7 +253,7 @@ static void
 nhi_pci_free(struct nhi_softc *sc)
 {
 
-	nhi_pci_free_interrupts(sc);
+	nhi_pci_free_resources(sc);
 
 	if (sc->parent_dmat != NULL) {
 		bus_dma_tag_destroy(sc->parent_dmat);
@@ -307,7 +307,7 @@ nhi_pci_allocate_interrupts(struct nhi_softc *sc)
 	return (error);
 }
 
-static void
+void
 nhi_pci_free_interrupts(struct nhi_softc *sc)
 {
 	int i;
@@ -319,7 +319,11 @@ nhi_pci_free_interrupts(struct nhi_softc *sc)
 	}
 
 	pci_release_msi(sc->dev);
+}
 
+static void
+nhi_pci_free_resources(struct nhi_softc *sc)
+{
 	if (sc->irq_table != NULL) {
 		bus_release_resource(sc->dev, SYS_RES_MEMORY,
 		    sc->irq_table_rid, sc->irq_table);
diff --git a/sys/dev/thunderbolt/nhi_var.h b/sys/dev/thunderbolt/nhi_var.h
index 2b9e878af47d..e79ecc954c1f 100644
--- a/sys/dev/thunderbolt/nhi_var.h
+++ b/sys/dev/thunderbolt/nhi_var.h
@@ -217,6 +217,7 @@ struct nhi_dispatch {
 int nhi_pci_configure_interrupts(struct nhi_softc *sc);
 void nhi_pci_enable_interrupt(struct nhi_ring_pair *r);
 void nhi_pci_disable_interrupts(struct nhi_softc *sc);
+void nhi_pci_free_interrupts(struct nhi_softc *sc);
 int nhi_pci_get_uuid(struct nhi_softc *sc);
 int nhi_read_lc_mailbox(struct nhi_softc *, u_int reg, uint32_t *val);
 int nhi_write_lc_mailbox(struct nhi_softc *, u_int reg, uint32_t val);