Commit 4bf398e1 authored by Mathias Nyman's avatar Mathias Nyman Committed by Greg Kroah-Hartman

xhci: split allocate interrupter into separate alloacte and add parts

The current function that both allocates and adds the interrupter isn't
optimal when using several interrupters. The array of interrupters need
to be protected with a lock while adding or removing interrupters.
If memory is allocated under the default xhci spinlock then GFP_KERNEL
can't be used.

There is no need to allocate the interrupter memory under the lock, so
split this code into separate unlocked allocate part, and a lock
protected add part.
Signed-off-by: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
Message-ID: <20230602144009.1225632-6-mathias.nyman@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 0a477620
......@@ -1831,13 +1831,15 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
* low or high 32 bits of ERSTBA immediately causes the controller to
* dereference the partially cleared 64 bit address, causing IOMMU error.
*/
tmp = readl(&ir->ir_set->erst_size);
tmp &= ERST_SIZE_MASK;
writel(tmp, &ir->ir_set->erst_size);
tmp64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
tmp64 &= (u64) ERST_PTR_MASK;
xhci_write_64(xhci, tmp64, &ir->ir_set->erst_dequeue);
if (ir->ir_set) {
tmp = readl(&ir->ir_set->erst_size);
tmp &= ERST_SIZE_MASK;
writel(tmp, &ir->ir_set->erst_size);
tmp64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
tmp64 &= (u64) ERST_PTR_MASK;
xhci_write_64(xhci, tmp64, &ir->ir_set->erst_dequeue);
}
/* free interrrupter event ring */
if (ir->event_ring)
......@@ -2227,43 +2229,50 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
}
static struct xhci_interrupter *
xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int intr_num, gfp_t flags)
xhci_alloc_interrupter(struct xhci_hcd *xhci, gfp_t flags)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
struct xhci_interrupter *ir;
u64 erst_base;
u32 erst_size;
int ret;
if (intr_num > xhci->max_interrupters) {
xhci_warn(xhci, "Can't allocate interrupter %d, max interrupters %d\n",
intr_num, xhci->max_interrupters);
return NULL;
}
if (xhci->interrupter) {
xhci_warn(xhci, "Can't allocate already set up interrupter %d\n", intr_num);
return NULL;
}
ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev));
if (!ir)
return NULL;
ir->ir_set = &xhci->run_regs->ir_set[intr_num];
ir->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT,
0, flags);
if (!ir->event_ring) {
xhci_warn(xhci, "Failed to allocate interrupter %d event ring\n", intr_num);
goto fail_ir;
xhci_warn(xhci, "Failed to allocate interrupter event ring\n");
kfree(ir);
return NULL;
}
ret = xhci_alloc_erst(xhci, ir->event_ring, &ir->erst, flags);
if (ret) {
xhci_warn(xhci, "Failed to allocate interrupter %d erst\n", intr_num);
goto fail_ev;
xhci_warn(xhci, "Failed to allocate interrupter erst\n");
xhci_ring_free(xhci, ir->event_ring);
kfree(ir);
return NULL;
}
return ir;
}
static int
xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
unsigned int intr_num)
{
u64 erst_base;
u32 erst_size;
if (intr_num > xhci->max_interrupters) {
xhci_warn(xhci, "Can't add interrupter %d, max interrupters %d\n",
intr_num, xhci->max_interrupters);
return -EINVAL;
}
ir->ir_set = &xhci->run_regs->ir_set[intr_num];
/* set ERST count with the number of entries in the segment table */
erst_size = readl(&ir->ir_set->erst_size);
erst_size &= ERST_SIZE_MASK;
......@@ -2278,14 +2287,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int intr_num, gfp_t flags
/* Set the event ring dequeue address of this interrupter */
xhci_set_hc_event_deq(xhci, ir);
return ir;
fail_ev:
xhci_ring_free(xhci, ir->event_ring);
fail_ir:
kfree(ir);
return NULL;
return 0;
}
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
......@@ -2407,15 +2409,17 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
"// Doorbell array is located at offset 0x%x from cap regs base addr",
val);
xhci->dba = (void __iomem *) xhci->cap_regs + val;
/* Set ir_set to interrupt register set 0 */
/* allocate and set up primary interrupter with an event ring. */
/* Allocate and set up primary interrupter 0 with an event ring. */
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Allocating primary event ring");
xhci->interrupter = xhci_alloc_interrupter(xhci, 0, flags);
xhci->interrupter = xhci_alloc_interrupter(xhci, flags);
if (!xhci->interrupter)
goto fail;
if (xhci_add_interrupter(xhci, xhci->interrupter, 0))
goto fail;
xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment