xhci: split free interrupter into separate remove and free parts

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>
This commit is contained in:
Mathias Nyman
2023-10-19 13:29:21 +03:00
committed by Greg Kroah-Hartman
parent a5d6264b63
commit 47f503cf5f

View File

@@ -1807,22 +1807,13 @@ static 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
@@ -1835,10 +1826,28 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
xhci_write_64(xhci, ERST_EHB, &ir->ir_set->erst_dequeue);
}
}
/* free interrrupter event ring */
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 interrupter event ring */
if (ir->event_ring)
xhci_ring_free(xhci, ir->event_ring);
ir->event_ring = NULL;
kfree(ir);
@@ -1851,6 +1860,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");