xhci: Add adaptive interrupt rate for isoch TRBs with XHCI_AVOID_BEI quirk

Save a bit of power by not interrupting so often by default if
XHCI_AVOID_BEI quirk is set.

In normal cases the xhci driver will only generate an interrupt on the last
isochronous TRB of an URB. In a common UVC webcam usecase there are 32 TRBs
per URB.

if AVOID_BEI flag is set then xhci driver will force an interrupt every 8th
isoc TRB to make sure the event ring doesn't get too full.

This is however way too frequent in common single webcam use cases, causing
1000 interrupts/sec and thus poor powermanagement performance.

Instead start with interrupting every 32 isoc TRB, and halve it in case
event ring becomes half-full. Stop halving when reaching a rate of every
8th trb.

This is a one way solution. If interrupt rate is increased it will stay
high until driver is reloaded. The highest rate is the same as the old
default rate.

Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Link: https://lore.kernel.org/r/20210617150354.1512157-3-mathias.nyman@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Mathias Nyman 2021-06-17 18:03:52 +03:00 committed by Greg Kroah-Hartman
parent ebd88cf507
commit 90d551a5bc
3 changed files with 15 additions and 1 deletions

View file

@ -2547,6 +2547,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Wrote ERST address to ir_set 0.");
xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;
/*
* XXX: Might need to set the Interrupter Moderation Register to
* something other than the default (~1ms minimum between interrupts).

View file

@ -3076,6 +3076,11 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
if (event_loop++ < TRBS_PER_SEGMENT / 2)
continue;
xhci_update_erst_dequeue(xhci, event_ring_deq);
/* ring is half-full, force isoc trbs to interrupt more often */
if (xhci->isoc_bei_interval > AVOID_BEI_INTERVAL_MIN)
xhci->isoc_bei_interval = xhci->isoc_bei_interval / 2;
event_loop = 0;
}
@ -3956,7 +3961,7 @@ static bool trb_block_event_intr(struct xhci_hcd *xhci, int num_tds, int i)
* generate an event at least every 8th TD to clear the event ring
*/
if (i && xhci->quirks & XHCI_AVOID_BEI)
return !!(i % 8);
return !!(i % xhci->isoc_bei_interval);
return true;
}

View file

@ -1526,6 +1526,12 @@ static inline const char *xhci_trb_type_string(u8 type)
#define TRB_BUFF_LEN_UP_TO_BOUNDARY(addr) (TRB_MAX_BUFF_SIZE - \
(addr & (TRB_MAX_BUFF_SIZE - 1)))
#define MAX_SOFT_RETRY 3
/*
* Limits of consecutive isoc trbs that can Block Event Interrupt (BEI) if
* XHCI_AVOID_BEI quirk is in use.
*/
#define AVOID_BEI_INTERVAL_MIN 8
#define AVOID_BEI_INTERVAL_MAX 32
struct xhci_segment {
union xhci_trb *trbs;
@ -1768,6 +1774,7 @@ struct xhci_hcd {
u8 isoc_threshold;
/* imod_interval in ns (I * 250ns) */
u32 imod_interval;
u32 isoc_bei_interval;
int event_ring_max;
/* 4KB min, 128MB max */
int page_size;