]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
xhci: split free interrupter into separate remove and free parts
authorMathias Nyman <mathias.nyman@linux.intel.com>
Thu, 19 Oct 2023 10:29:21 +0000 (13:29 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 9 May 2025 07:44:06 +0000 (09:44 +0200)
[ Upstream commit 47f503cf5f799ec02e5f4b7c3b9afe145eca2aef ]

The current function that both removes and frees an interrupter isn't
optimal when using several interrupters. The array of interrupters need
to be protected with a lock while removing interrupters, but the default
xhci spin lock can't be used while freeing the interrupters event ring
segment table as dma_free_coherent() should be called with IRQs enabled.

There is no need to free the interrupter under the lock, so split this
code into separate unlocked free part, and a lock protected remove part.

Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Link: https://lore.kernel.org/r/20231019102924.2797346-17-mathias.nyman@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Stable-dep-of: bea5892d0ed2 ("xhci: Limit time spent with xHC interrupts disabled during bus resume")
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/usb/host/xhci-mem.c

index 45240299fa171260cea4b09d91293d93a8ee642b..f00e96c9ca57a24ef935005a592da134265f3755 100644 (file)
@@ -1803,22 +1803,13 @@ int xhci_alloc_erst(struct xhci_hcd *xhci,
 }
 
 static void
-xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
+xhci_remove_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
 {
-       struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
-       size_t erst_size;
        u32 tmp;
 
        if (!ir)
                return;
 
-       erst_size = sizeof(struct xhci_erst_entry) * ir->erst.num_entries;
-       if (ir->erst.entries)
-               dma_free_coherent(dev, erst_size,
-                                 ir->erst.entries,
-                                 ir->erst.erst_dma_addr);
-       ir->erst.entries = NULL;
-
        /*
         * Clean out interrupter registers except ERSTBA. Clearing either the
         * low or high 32 bits of ERSTBA immediately causes the controller to
@@ -1831,10 +1822,28 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
 
                xhci_write_64(xhci, ERST_EHB, &ir->ir_set->erst_dequeue);
        }
+}
+
+static void
+xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
+{
+       struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
+       size_t erst_size;
+
+       if (!ir)
+               return;
+
+       erst_size = sizeof(struct xhci_erst_entry) * ir->erst.num_entries;
+       if (ir->erst.entries)
+               dma_free_coherent(dev, erst_size,
+                                 ir->erst.entries,
+                                 ir->erst.erst_dma_addr);
+       ir->erst.entries = NULL;
 
-       /* free interrrupter event ring */
+       /* free interrupter event ring */
        if (ir->event_ring)
                xhci_ring_free(xhci, ir->event_ring);
+
        ir->event_ring = NULL;
 
        kfree(ir);
@@ -1847,6 +1856,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
 
        cancel_delayed_work_sync(&xhci->cmd_timer);
 
+       xhci_remove_interrupter(xhci, xhci->interrupter);
        xhci_free_interrupter(xhci, xhci->interrupter);
        xhci->interrupter = NULL;
        xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary event ring");