mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-29 18:18:54 +00:00
a24224ffc3
This patch takes advantage of block transfer interrupts on Intel XHCI controllers which is used during isochronous transfers. Because of a bug in hardware (see usb_host_isoc_bei.patch header), this feature has been disabled for Intel leading to up to 8000 interrupts/s for isochronous transfer causing severe CPU consumption on Genode. With this commit we lower host driver consumption to normal levels. issue #4149
108 lines
4.6 KiB
Diff
108 lines
4.6 KiB
Diff
For Intel host controllers an interrupt is raised for every isochronous packet,
|
|
because of the "AVOID_BEI" quirk. The reasoning for this is found in the
|
|
following commit:
|
|
|
|
commit 227a4fd801c8a9fa2c4700ab98ec1aec06e3b44d
|
|
Author: Lu Baolu <baolu.lu@linux.intel.com>
|
|
Date: Mon Mar 23 18:27:42 2015 +0200
|
|
|
|
usb: xhci: apply XHCI_AVOID_BEI quirk to all Intel xHCI controllers
|
|
|
|
When a device with an isochronous endpoint is plugged into the Intel
|
|
xHCI host controller, and the driver submits multiple frames per URB,
|
|
the xHCI driver will set the Block Event Interrupt (BEI) flag on all
|
|
but the last TD for the URB. This causes the host controller to place
|
|
an event on the event ring, but not send an interrupt. When the last
|
|
TD for the URB completes, BEI is cleared, and we get an interrupt for
|
|
the whole URB.
|
|
|
|
However, under Intel xHCI host controllers, if the event ring is full
|
|
of events from transfers with BEI set, an "Event Ring is Full" event
|
|
will be posted to the last entry of the event ring, but no interrupt
|
|
is generated. Host will cease all transfer and command executions and
|
|
wait until software completes handling the pending events in the event
|
|
ring. That means xHC stops, but event of "event ring is full" is not
|
|
notified. As the result, the xHC looks like dead to user.
|
|
|
|
This patch is to apply XHCI_AVOID_BEI quirk to Intel xHC devices. And
|
|
it should be backported to kernels as old as 3.0, that contains the
|
|
commit 69e848c2090a ("Intel xhci: Support EHCI/xHCI port switching.").
|
|
|
|
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
|
|
Tested-by: Alistair Grant <akgrant0710@gmail.com>
|
|
Cc: stable@vger.kernel.org
|
|
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
|
|
Because this behavior up to 8000 interrupts/s can be raised by the host
|
|
controller during isochronous transfer. Since 2020 there exists a patch that
|
|
relaxes this burden and which we will apply.
|
|
|
|
Author: Mathias Nyman <mathias.nyman@linux.intel.com>
|
|
Date: Fr Sep 18 2020 13:17:24 2020 +0200
|
|
|
|
xhci: Tune interrupt blocking for isochronous transfers
|
|
|
|
controllers with XHCI_AVOID_BEI quirk cause too frequent interrupts
|
|
and affect power management.
|
|
|
|
To avoid interrupting on every isochronous interval the BEI (Block
|
|
Event Interrupt) flag is set for all except the last Isoch TRB in a URB.
|
|
This lead to event ring filling up in case several isoc URB were
|
|
queued and cancelled rapidly, which some controllers didn't
|
|
handle well, and thus the XHCI_AVOID_BEI quirk was introduced.
|
|
see commit 227a4fd801c8 ("usb: xhci: apply XHCI_AVOID_BEI quirk to all
|
|
Intel xHCI controllers")
|
|
|
|
With the XHCI_AVOID_BEI quirk each Isoch TRB will trigger an interrupt.
|
|
This can cause up to 8000 interrupts per second for isochronous transfers
|
|
with HD USB3 cameras, affecting power saving.
|
|
|
|
The event ring fits 256 events, instead of interrupting on every
|
|
isochronous TRB if XHCI_AVOID_BEI is set we make sure at least every
|
|
8th Isochronous TRB asserts an interrupt, clearing the event ring.
|
|
|
|
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
|
|
|
|
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
|
|
index 028891a..31f6bd3 100644
|
|
--- a/drivers/usb/host/xhci-ring.c
|
|
+++ b/drivers/usb/host/xhci-ring.c
|
|
@@ -3608,6 +3608,24 @@ static int xhci_get_isoc_frame_id(struct xhci_hcd *xhci,
|
|
return start_frame;
|
|
}
|
|
|
|
+/* Check if we should generate event interrupt for a TD in an isoc URB */
|
|
+static bool trb_block_event_intr(struct xhci_hcd *xhci, int num_tds, int i)
|
|
+{
|
|
+ if (xhci->hci_version < 0x100)
|
|
+ return false;
|
|
+ /* always generate an event interrupt for the last TD */
|
|
+ if (i == num_tds - 1)
|
|
+ return false;
|
|
+ /*
|
|
+ * If AVOID_BEI is set the host handles full event rings poorly,
|
|
+ * generate an event at least every 8th TD to clear the event ring
|
|
+ */
|
|
+ if (i && xhci->quirks & XHCI_AVOID_BEI)
|
|
+ return !!(i % 8);
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
/* This is for isoc transfer */
|
|
static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|
struct urb *urb, int slot_id, unsigned int ep_index)
|
|
@@ -3715,10 +3733,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|
more_trbs_coming = false;
|
|
td->last_trb = ep_ring->enqueue;
|
|
field |= TRB_IOC;
|
|
- /* set BEI, except for the last TD */
|
|
- if (xhci->hci_version >= 0x100 &&
|
|
- !(xhci->quirks & XHCI_AVOID_BEI) &&
|
|
- i < num_tds - 1)
|
|
+ if (trb_block_event_intr(xhci, num_tds, i))
|
|
field |= TRB_BEI;
|
|
}
|
|
/* Calculate TRB length */
|