Commit 47f503cf authored by Mathias Nyman's avatar Mathias Nyman Committed by Greg Kroah-Hartman
Browse files

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: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
Link: https://lore.kernel.org/r/20231019102924.2797346-17-mathias.nyman@linux.intel.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a5d6264b
Loading
Loading
Loading
Loading
+21 −11
Original line number Diff line number Diff line
@@ -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);
	}
}

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);
@@ -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");