Commit c06d68b814d556cff5a4dc589215f5ed9f0b7fd5
Committed by
Greg Kroah-Hartman
1 parent
d6d98a4d8d
Exists in
master
and in
7 other branches
USB: xhci: Minimize HW event ring dequeue pointer writes.
The xHCI specification suggests that writing the hardware event ring dequeue pointer register too often can be an expensive operation for the xHCI hardware to manage. It suggests minimizing the number of writes to that register. Originally, the driver wrote the event ring dequeue pointer after each event was processed. Depending on how the event ring moderation register is set up and how fast the transfers are completing, there may be several events processed for each interrupt. This patch makes the hardware event ring dequeue pointer be written only once per interrupt. Make the transfer event handler and port status event handler only write the software event ring dequeue pointer. Move the updating of the hardware event ring dequeue pointer into the interrupt function. Move the contents of xhci_set_hc_event_deq() into the interrupt handler. The interrupt handler must clear the event handler busy flag, so it might as well also write the dequeue pointer to the same register. This eliminates two 32-bit PCI reads and two 32-bit PCI writes. Reported-by: Andiry Xu <andiry.xu@amd.com> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Showing 1 changed file with 37 additions and 13 deletions Side-by-side Diff
drivers/usb/host/xhci-ring.c
... | ... | @@ -1184,7 +1184,6 @@ |
1184 | 1184 | |
1185 | 1185 | /* Update event ring dequeue pointer before dropping the lock */ |
1186 | 1186 | inc_deq(xhci, xhci->event_ring, true); |
1187 | - xhci_set_hc_event_deq(xhci); | |
1188 | 1187 | |
1189 | 1188 | spin_unlock(&xhci->lock); |
1190 | 1189 | /* Pass this up to the core */ |
... | ... | @@ -1924,7 +1923,6 @@ |
1924 | 1923 | */ |
1925 | 1924 | if (trb_comp_code == COMP_MISSED_INT || !ep->skip) { |
1926 | 1925 | inc_deq(xhci, xhci->event_ring, true); |
1927 | - xhci_set_hc_event_deq(xhci); | |
1928 | 1926 | } |
1929 | 1927 | |
1930 | 1928 | if (ret) { |
1931 | 1929 | |
... | ... | @@ -2022,11 +2020,10 @@ |
2022 | 2020 | return; |
2023 | 2021 | } |
2024 | 2022 | |
2025 | - if (update_ptrs) { | |
2026 | - /* Update SW and HC event ring dequeue pointer */ | |
2023 | + if (update_ptrs) | |
2024 | + /* Update SW event ring dequeue pointer */ | |
2027 | 2025 | inc_deq(xhci, xhci->event_ring, true); |
2028 | - xhci_set_hc_event_deq(xhci); | |
2029 | - } | |
2026 | + | |
2030 | 2027 | /* Are there more items on the event ring? */ |
2031 | 2028 | xhci_handle_event(xhci); |
2032 | 2029 | } |
... | ... | @@ -2042,6 +2039,8 @@ |
2042 | 2039 | u32 status, irq_pending; |
2043 | 2040 | union xhci_trb *trb; |
2044 | 2041 | u64 temp_64; |
2042 | + union xhci_trb *event_ring_deq; | |
2043 | + dma_addr_t deq; | |
2045 | 2044 | |
2046 | 2045 | spin_lock(&xhci->lock); |
2047 | 2046 | trb = xhci->event_ring->dequeue; |
2048 | 2047 | |
2049 | 2048 | |
2050 | 2049 | |
2051 | 2050 | |
... | ... | @@ -2090,18 +2089,43 @@ |
2090 | 2089 | irq_pending |= 0x3; |
2091 | 2090 | xhci_writel(xhci, irq_pending, &xhci->ir_set->irq_pending); |
2092 | 2091 | |
2093 | - if (xhci->xhc_state & XHCI_STATE_DYING) | |
2092 | + if (xhci->xhc_state & XHCI_STATE_DYING) { | |
2094 | 2093 | xhci_dbg(xhci, "xHCI dying, ignoring interrupt. " |
2095 | 2094 | "Shouldn't IRQs be disabled?\n"); |
2096 | - else | |
2097 | - /* FIXME this should be a delayed service routine | |
2098 | - * that clears the EHB. | |
2095 | + /* Clear the event handler busy flag (RW1C); | |
2096 | + * the event ring should be empty. | |
2099 | 2097 | */ |
2100 | - xhci_handle_event(xhci); | |
2098 | + temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); | |
2099 | + xhci_write_64(xhci, temp_64 | ERST_EHB, | |
2100 | + &xhci->ir_set->erst_dequeue); | |
2101 | + spin_unlock(&xhci->lock); | |
2101 | 2102 | |
2102 | - /* Clear the event handler busy flag (RW1C); event ring is empty. */ | |
2103 | + return IRQ_HANDLED; | |
2104 | + } | |
2105 | + | |
2106 | + event_ring_deq = xhci->event_ring->dequeue; | |
2107 | + /* FIXME this should be a delayed service routine | |
2108 | + * that clears the EHB. | |
2109 | + */ | |
2110 | + xhci_handle_event(xhci); | |
2111 | + | |
2103 | 2112 | temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); |
2104 | - xhci_write_64(xhci, temp_64 | ERST_EHB, &xhci->ir_set->erst_dequeue); | |
2113 | + /* If necessary, update the HW's version of the event ring deq ptr. */ | |
2114 | + if (event_ring_deq != xhci->event_ring->dequeue) { | |
2115 | + deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, | |
2116 | + xhci->event_ring->dequeue); | |
2117 | + if (deq == 0) | |
2118 | + xhci_warn(xhci, "WARN something wrong with SW event " | |
2119 | + "ring dequeue ptr.\n"); | |
2120 | + /* Update HC event ring dequeue pointer */ | |
2121 | + temp_64 &= ERST_PTR_MASK; | |
2122 | + temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK); | |
2123 | + } | |
2124 | + | |
2125 | + /* Clear the event handler busy flag (RW1C); event ring is empty. */ | |
2126 | + temp_64 |= ERST_EHB; | |
2127 | + xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue); | |
2128 | + | |
2105 | 2129 | spin_unlock(&xhci->lock); |
2106 | 2130 | |
2107 | 2131 | return IRQ_HANDLED; |