Skip to content

Commit 2b69899

Browse files
Sarah Sharpgregkh
authored andcommitted
xhci: USB 3.0 BW checking.
The Intel Panther Point xHCI host tracks SuperSpeed endpoints in a different way than USB 2.0/1.1 endpoints. The bandwidth interval tables are not used, and instead the bandwidth is calculated in a very simple way. Bandwidth for SuperSpeed endpoints is tracked individually in each direction, since each direction has the full USB 3.0 bandwidth available. 10% of the bus bandwidth is reserved for non-periodic transfers. This checking would be more complex if we had USB 3.0 LPM enabled, because an additional latency for isochronous ping times need to be taken into account. However, we don't have USB 3.0 LPM support in Linux yet. Signed-off-by: Sarah Sharp <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 170c026 commit 2b69899

File tree

2 files changed

+67
-5
lines changed

2 files changed

+67
-5
lines changed

drivers/usb/host/xhci.c

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1810,6 +1810,22 @@ static int xhci_check_tt_bw_table(struct xhci_hcd *xhci,
18101810
return 0;
18111811
}
18121812

1813+
static int xhci_check_ss_bw(struct xhci_hcd *xhci,
1814+
struct xhci_virt_device *virt_dev)
1815+
{
1816+
unsigned int bw_reserved;
1817+
1818+
bw_reserved = DIV_ROUND_UP(SS_BW_RESERVED*SS_BW_LIMIT_IN, 100);
1819+
if (virt_dev->bw_table->ss_bw_in > (SS_BW_LIMIT_IN - bw_reserved))
1820+
return -ENOMEM;
1821+
1822+
bw_reserved = DIV_ROUND_UP(SS_BW_RESERVED*SS_BW_LIMIT_OUT, 100);
1823+
if (virt_dev->bw_table->ss_bw_out > (SS_BW_LIMIT_OUT - bw_reserved))
1824+
return -ENOMEM;
1825+
1826+
return 0;
1827+
}
1828+
18131829
/*
18141830
* This algorithm is a very conservative estimate of the worst-case scheduling
18151831
* scenario for any one interval. The hardware dynamically schedules the
@@ -1866,6 +1882,9 @@ static int xhci_check_bw_table(struct xhci_hcd *xhci,
18661882
unsigned int packets_remaining = 0;
18671883
unsigned int i;
18681884

1885+
if (virt_dev->udev->speed == USB_SPEED_SUPER)
1886+
return xhci_check_ss_bw(xhci, virt_dev);
1887+
18691888
if (virt_dev->udev->speed == USB_SPEED_HIGH) {
18701889
max_bandwidth = HS_BW_LIMIT;
18711890
/* Convert percent of bus BW reserved to blocks reserved */
@@ -2028,6 +2047,25 @@ static bool xhci_is_async_ep(unsigned int ep_type)
20282047
ep_type != INT_IN_EP);
20292048
}
20302049

2050+
static bool xhci_is_sync_in_ep(unsigned int ep_type)
2051+
{
2052+
return (ep_type == ISOC_IN_EP || ep_type != INT_IN_EP);
2053+
}
2054+
2055+
static unsigned int xhci_get_ss_bw_consumed(struct xhci_bw_info *ep_bw)
2056+
{
2057+
unsigned int mps = DIV_ROUND_UP(ep_bw->max_packet_size, SS_BLOCK);
2058+
2059+
if (ep_bw->ep_interval == 0)
2060+
return SS_OVERHEAD_BURST +
2061+
(ep_bw->mult * ep_bw->num_packets *
2062+
(SS_OVERHEAD + mps));
2063+
return DIV_ROUND_UP(ep_bw->mult * ep_bw->num_packets *
2064+
(SS_OVERHEAD + mps + SS_OVERHEAD_BURST),
2065+
1 << ep_bw->ep_interval);
2066+
2067+
}
2068+
20312069
void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci,
20322070
struct xhci_bw_info *ep_bw,
20332071
struct xhci_interval_bw_table *bw_table,
@@ -2038,10 +2076,24 @@ void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci,
20382076
struct xhci_interval_bw *interval_bw;
20392077
int normalized_interval;
20402078

2041-
if (xhci_is_async_ep(ep_bw->type) ||
2042-
list_empty(&virt_ep->bw_endpoint_list))
2079+
if (xhci_is_async_ep(ep_bw->type))
20432080
return;
20442081

2082+
if (udev->speed == USB_SPEED_SUPER) {
2083+
if (xhci_is_sync_in_ep(ep_bw->type))
2084+
xhci->devs[udev->slot_id]->bw_table->ss_bw_in -=
2085+
xhci_get_ss_bw_consumed(ep_bw);
2086+
else
2087+
xhci->devs[udev->slot_id]->bw_table->ss_bw_out -=
2088+
xhci_get_ss_bw_consumed(ep_bw);
2089+
return;
2090+
}
2091+
2092+
/* SuperSpeed endpoints never get added to intervals in the table, so
2093+
* this check is only valid for HS/FS/LS devices.
2094+
*/
2095+
if (list_empty(&virt_ep->bw_endpoint_list))
2096+
return;
20452097
/* For LS/FS devices, we need to translate the interval expressed in
20462098
* microframes to frames.
20472099
*/
@@ -2091,6 +2143,16 @@ static void xhci_add_ep_to_interval_table(struct xhci_hcd *xhci,
20912143
if (xhci_is_async_ep(ep_bw->type))
20922144
return;
20932145

2146+
if (udev->speed == USB_SPEED_SUPER) {
2147+
if (xhci_is_sync_in_ep(ep_bw->type))
2148+
xhci->devs[udev->slot_id]->bw_table->ss_bw_in +=
2149+
xhci_get_ss_bw_consumed(ep_bw);
2150+
else
2151+
xhci->devs[udev->slot_id]->bw_table->ss_bw_out +=
2152+
xhci_get_ss_bw_consumed(ep_bw);
2153+
return;
2154+
}
2155+
20942156
/* For LS/FS devices, we need to translate the interval expressed in
20952157
* microframes to frames.
20962158
*/
@@ -2169,9 +2231,6 @@ static int xhci_reserve_bandwidth(struct xhci_hcd *xhci,
21692231
struct xhci_input_control_ctx *ctrl_ctx;
21702232
int old_active_eps = 0;
21712233

2172-
if (virt_dev->udev->speed == USB_SPEED_SUPER)
2173-
return 0;
2174-
21752234
if (virt_dev->tt_info)
21762235
old_active_eps = virt_dev->tt_info->active_eps;
21772236

drivers/usb/host/xhci.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,7 @@ struct xhci_bw_info {
799799
/* Percentage of bus bandwidth reserved for non-periodic transfers */
800800
#define FS_BW_RESERVED 10
801801
#define HS_BW_RESERVED 20
802+
#define SS_BW_RESERVED 10
802803

803804
struct xhci_virt_ep {
804805
struct xhci_ring *ring;
@@ -869,6 +870,8 @@ struct xhci_interval_bw_table {
869870
struct xhci_interval_bw interval_bw[XHCI_MAX_INTERVAL];
870871
/* Includes reserved bandwidth for async endpoints */
871872
unsigned int bw_used;
873+
unsigned int ss_bw_in;
874+
unsigned int ss_bw_out;
872875
};
873876

874877

0 commit comments

Comments
 (0)