xhci: split free interrupter into separate remove and free parts
[ 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>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
3f6e8ad65b
commit
ff0a51339a
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
@@ -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");
|
||||
|
Reference in New Issue
Block a user