diff options
author | Kirill Smelkov <kirr@mns.spb.ru> | 2011-07-03 12:36:57 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-07-08 17:51:33 -0400 |
commit | cc62a7eb6396e8be95b9a30053ed09191818b99b (patch) | |
tree | 0f4bbb0d3c9a99402bf19345e4b4ccf7bd94e216 /drivers/usb | |
parent | 4c67045bfc2c14a1d3c6040e80eb4a62946282dd (diff) |
USB: EHCI: Allow users to override 80% max periodic bandwidth
There are cases, when 80% max isochronous bandwidth is too limiting.
For example I have two USB video capture cards which stream uncompressed
video, and to stream full NTSC + PAL videos we'd need
NTSC 640x480 YUV422 @30fps ~17.6 MB/s
PAL 720x576 YUV422 @25fps ~19.7 MB/s
isoc bandwidth.
Now, due to limited alt settings in capture devices NTSC one ends up
streaming with max_pkt_size=2688 and PAL with max_pkt_size=2892, both
with interval=1. In terms of microframe time allocation this gives
NTSC ~53us
PAL ~57us
and together
~110us > 100us == 80% of 125us uframe time.
So those two devices can't work together simultaneously because the'd
over allocate isochronous bandwidth.
80% seemed a bit arbitrary to me, and I've tried to raise it to 90% and
both devices started to work together, so I though sometimes it would be
a good idea for users to override hardcoded default of max 80% isoc
bandwidth.
After all, isn't it a user who should decide how to load the bus? If I
can live with 10% or even 5% bulk bandwidth that should be ok. I'm a USB
newcomer, but that 80% set in stone by USB 2.0 specification seems to be
chosen pretty arbitrary to me, just to serve as a reasonable default.
NOTE 1
~~~~~~
for two streams with max_pkt_size=3072 (worst case) both time
allocation would be 60us+60us=120us which is 96% periodic bandwidth
leaving 4% for bulk and control. Alan Stern suggested that bulk then
would be problematic (less than 300*8 bittimes left per microframe), but
I think that is still enough for control traffic.
NOTE 2
~~~~~~
Sarah Sharp expressed concern that maxing out periodic bandwidth
could lead to vendor-specific hardware bugs on host controllers, because
> It's entirely possible that you'll run into
> vendor-specific bugs if you try to pack the schedule with isochronous
> transfers. I don't think any hardware designer would seriously test or
> validate their hardware with a schedule that is basically a violation of
> the USB bus spec (more than 80% for periodic transfers).
So far I've only tested this patch on my HP Mini 5103 with N10 chipset
kirr@mini:~$ lspci
00:00.0 Host bridge: Intel Corporation N10 Family DMI Bridge
00:02.0 VGA compatible controller: Intel Corporation N10 Family Integrated Graphics Controller
00:02.1 Display controller: Intel Corporation N10 Family Integrated Graphics Controller
00:1b.0 Audio device: Intel Corporation N10/ICH 7 Family High Definition Audio Controller (rev 02)
00:1c.0 PCI bridge: Intel Corporation N10/ICH 7 Family PCI Express Port 1 (rev 02)
00:1c.3 PCI bridge: Intel Corporation N10/ICH 7 Family PCI Express Port 4 (rev 02)
00:1d.0 USB Controller: Intel Corporation N10/ICH 7 Family USB UHCI Controller #1 (rev 02)
00:1d.1 USB Controller: Intel Corporation N10/ICH 7 Family USB UHCI Controller #2 (rev 02)
00:1d.2 USB Controller: Intel Corporation N10/ICH 7 Family USB UHCI Controller #3 (rev 02)
00:1d.3 USB Controller: Intel Corporation N10/ICH 7 Family USB UHCI Controller #4 (rev 02)
00:1d.7 USB Controller: Intel Corporation N10/ICH 7 Family USB2 EHCI Controller (rev 02)
00:1e.0 PCI bridge: Intel Corporation 82801 Mobile PCI Bridge (rev e2)
00:1f.0 ISA bridge: Intel Corporation NM10 Family LPC Controller (rev 02)
00:1f.2 SATA controller: Intel Corporation N10/ICH7 Family SATA AHCI Controller (rev 02)
01:00.0 Network controller: Broadcom Corporation BCM4313 802.11b/g/n Wireless LAN Controller (rev 01)
02:00.0 Ethernet controller: Marvell Technology Group Ltd. 88E8059 PCI-E Gigabit Ethernet Controller (rev 11)
and the system works stable with 110us/uframe (~88%) isoc bandwith allocated for
above-mentioned isochronous transfers.
NOTE 3
~~~~~~
This feature is off by default. I mean max periodic bandwidth is set to
100us/uframe by default exactly as it was before the patch. So only those of us
who need the extreme settings are taking the risk - normal users who do not
alter uframe_periodic_max sysfs attribute should not see any change at all.
NOTE 4
~~~~~~
I've tried to update documentation in Documentation/ABI/ thoroughly, but
only "TBD" was put into Documentation/usb/ehci.txt -- the text there seems
to be outdated and much needing refreshing, before it could be amended.
Cc: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Kirill Smelkov <kirr@mns.spb.ru>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 6 | ||||
-rw-r--r-- | drivers/usb/host/ehci-sched.c | 17 | ||||
-rw-r--r-- | drivers/usb/host/ehci-sysfs.c | 104 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 2 |
4 files changed, 115 insertions, 14 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 8306155de9e5..4ee62bef3723 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -572,6 +572,12 @@ static int ehci_init(struct usb_hcd *hcd) | |||
572 | hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params); | 572 | hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params); |
573 | 573 | ||
574 | /* | 574 | /* |
575 | * by default set standard 80% (== 100 usec/uframe) max periodic | ||
576 | * bandwidth as required by USB 2.0 | ||
577 | */ | ||
578 | ehci->uframe_periodic_max = 100; | ||
579 | |||
580 | /* | ||
575 | * hw default: 1K periodic list heads, one per frame. | 581 | * hw default: 1K periodic list heads, one per frame. |
576 | * periodic_size can shrink by USBCMD update if hcc_params allows. | 582 | * periodic_size can shrink by USBCMD update if hcc_params allows. |
577 | */ | 583 | */ |
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 6c9fbe352f73..2abf8543f083 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c | |||
@@ -172,7 +172,7 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) | |||
172 | } | 172 | } |
173 | } | 173 | } |
174 | #ifdef DEBUG | 174 | #ifdef DEBUG |
175 | if (usecs > 100) | 175 | if (usecs > ehci->uframe_periodic_max) |
176 | ehci_err (ehci, "uframe %d sched overrun: %d usecs\n", | 176 | ehci_err (ehci, "uframe %d sched overrun: %d usecs\n", |
177 | frame * 8 + uframe, usecs); | 177 | frame * 8 + uframe, usecs); |
178 | #endif | 178 | #endif |
@@ -709,11 +709,8 @@ static int check_period ( | |||
709 | if (uframe >= 8) | 709 | if (uframe >= 8) |
710 | return 0; | 710 | return 0; |
711 | 711 | ||
712 | /* | 712 | /* convert "usecs we need" to "max already claimed" */ |
713 | * 80% periodic == 100 usec/uframe available | 713 | usecs = ehci->uframe_periodic_max - usecs; |
714 | * convert "usecs we need" to "max already claimed" | ||
715 | */ | ||
716 | usecs = 100 - usecs; | ||
717 | 714 | ||
718 | /* we "know" 2 and 4 uframe intervals were rejected; so | 715 | /* we "know" 2 and 4 uframe intervals were rejected; so |
719 | * for period 0, check _every_ microframe in the schedule. | 716 | * for period 0, check _every_ microframe in the schedule. |
@@ -1286,9 +1283,9 @@ itd_slot_ok ( | |||
1286 | { | 1283 | { |
1287 | uframe %= period; | 1284 | uframe %= period; |
1288 | do { | 1285 | do { |
1289 | /* can't commit more than 80% periodic == 100 usec */ | 1286 | /* can't commit more than uframe_periodic_max usec */ |
1290 | if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7) | 1287 | if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7) |
1291 | > (100 - usecs)) | 1288 | > (ehci->uframe_periodic_max - usecs)) |
1292 | return 0; | 1289 | return 0; |
1293 | 1290 | ||
1294 | /* we know urb->interval is 2^N uframes */ | 1291 | /* we know urb->interval is 2^N uframes */ |
@@ -1345,7 +1342,7 @@ sitd_slot_ok ( | |||
1345 | #endif | 1342 | #endif |
1346 | 1343 | ||
1347 | /* check starts (OUT uses more than one) */ | 1344 | /* check starts (OUT uses more than one) */ |
1348 | max_used = 100 - stream->usecs; | 1345 | max_used = ehci->uframe_periodic_max - stream->usecs; |
1349 | for (tmp = stream->raw_mask & 0xff; tmp; tmp >>= 1, uf++) { | 1346 | for (tmp = stream->raw_mask & 0xff; tmp; tmp >>= 1, uf++) { |
1350 | if (periodic_usecs (ehci, frame, uf) > max_used) | 1347 | if (periodic_usecs (ehci, frame, uf) > max_used) |
1351 | return 0; | 1348 | return 0; |
@@ -1354,7 +1351,7 @@ sitd_slot_ok ( | |||
1354 | /* for IN, check CSPLIT */ | 1351 | /* for IN, check CSPLIT */ |
1355 | if (stream->c_usecs) { | 1352 | if (stream->c_usecs) { |
1356 | uf = uframe & 7; | 1353 | uf = uframe & 7; |
1357 | max_used = 100 - stream->c_usecs; | 1354 | max_used = ehci->uframe_periodic_max - stream->c_usecs; |
1358 | do { | 1355 | do { |
1359 | tmp = 1 << uf; | 1356 | tmp = 1 << uf; |
1360 | tmp <<= 8; | 1357 | tmp <<= 8; |
diff --git a/drivers/usb/host/ehci-sysfs.c b/drivers/usb/host/ehci-sysfs.c index 29824a98aec9..14ced00ba220 100644 --- a/drivers/usb/host/ehci-sysfs.c +++ b/drivers/usb/host/ehci-sysfs.c | |||
@@ -74,21 +74,117 @@ static ssize_t store_companion(struct device *dev, | |||
74 | } | 74 | } |
75 | static DEVICE_ATTR(companion, 0644, show_companion, store_companion); | 75 | static DEVICE_ATTR(companion, 0644, show_companion, store_companion); |
76 | 76 | ||
77 | |||
78 | /* | ||
79 | * Display / Set uframe_periodic_max | ||
80 | */ | ||
81 | static ssize_t show_uframe_periodic_max(struct device *dev, | ||
82 | struct device_attribute *attr, | ||
83 | char *buf) | ||
84 | { | ||
85 | struct ehci_hcd *ehci; | ||
86 | int n; | ||
87 | |||
88 | ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); | ||
89 | n = scnprintf(buf, PAGE_SIZE, "%d\n", ehci->uframe_periodic_max); | ||
90 | return n; | ||
91 | } | ||
92 | |||
93 | |||
94 | static ssize_t store_uframe_periodic_max(struct device *dev, | ||
95 | struct device_attribute *attr, | ||
96 | const char *buf, size_t count) | ||
97 | { | ||
98 | struct ehci_hcd *ehci; | ||
99 | unsigned uframe_periodic_max; | ||
100 | unsigned frame, uframe; | ||
101 | unsigned short allocated_max; | ||
102 | unsigned long flags; | ||
103 | ssize_t ret; | ||
104 | |||
105 | ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); | ||
106 | if (kstrtouint(buf, 0, &uframe_periodic_max) < 0) | ||
107 | return -EINVAL; | ||
108 | |||
109 | if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) { | ||
110 | ehci_info(ehci, "rejecting invalid request for " | ||
111 | "uframe_periodic_max=%u\n", uframe_periodic_max); | ||
112 | return -EINVAL; | ||
113 | } | ||
114 | |||
115 | ret = -EINVAL; | ||
116 | |||
117 | /* | ||
118 | * lock, so that our checking does not race with possible periodic | ||
119 | * bandwidth allocation through submitting new urbs. | ||
120 | */ | ||
121 | spin_lock_irqsave (&ehci->lock, flags); | ||
122 | |||
123 | /* | ||
124 | * for request to decrease max periodic bandwidth, we have to check | ||
125 | * every microframe in the schedule to see whether the decrease is | ||
126 | * possible. | ||
127 | */ | ||
128 | if (uframe_periodic_max < ehci->uframe_periodic_max) { | ||
129 | allocated_max = 0; | ||
130 | |||
131 | for (frame = 0; frame < ehci->periodic_size; ++frame) | ||
132 | for (uframe = 0; uframe < 7; ++uframe) | ||
133 | allocated_max = max(allocated_max, | ||
134 | periodic_usecs (ehci, frame, uframe)); | ||
135 | |||
136 | if (allocated_max > uframe_periodic_max) { | ||
137 | ehci_info(ehci, | ||
138 | "cannot decrease uframe_periodic_max becase " | ||
139 | "periodic bandwidth is already allocated " | ||
140 | "(%u > %u)\n", | ||
141 | allocated_max, uframe_periodic_max); | ||
142 | goto out_unlock; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | /* increasing is always ok */ | ||
147 | |||
148 | ehci_info(ehci, "setting max periodic bandwidth to %u%% " | ||
149 | "(== %u usec/uframe)\n", | ||
150 | 100*uframe_periodic_max/125, uframe_periodic_max); | ||
151 | |||
152 | if (uframe_periodic_max != 100) | ||
153 | ehci_warn(ehci, "max periodic bandwidth set is non-standard\n"); | ||
154 | |||
155 | ehci->uframe_periodic_max = uframe_periodic_max; | ||
156 | ret = count; | ||
157 | |||
158 | out_unlock: | ||
159 | spin_unlock_irqrestore (&ehci->lock, flags); | ||
160 | return ret; | ||
161 | } | ||
162 | static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max, store_uframe_periodic_max); | ||
163 | |||
164 | |||
77 | static inline int create_sysfs_files(struct ehci_hcd *ehci) | 165 | static inline int create_sysfs_files(struct ehci_hcd *ehci) |
78 | { | 166 | { |
167 | struct device *controller = ehci_to_hcd(ehci)->self.controller; | ||
79 | int i = 0; | 168 | int i = 0; |
80 | 169 | ||
81 | /* with integrated TT there is no companion! */ | 170 | /* with integrated TT there is no companion! */ |
82 | if (!ehci_is_TDI(ehci)) | 171 | if (!ehci_is_TDI(ehci)) |
83 | i = device_create_file(ehci_to_hcd(ehci)->self.controller, | 172 | i = device_create_file(controller, &dev_attr_companion); |
84 | &dev_attr_companion); | 173 | if (i) |
174 | goto out; | ||
175 | |||
176 | i = device_create_file(controller, &dev_attr_uframe_periodic_max); | ||
177 | out: | ||
85 | return i; | 178 | return i; |
86 | } | 179 | } |
87 | 180 | ||
88 | static inline void remove_sysfs_files(struct ehci_hcd *ehci) | 181 | static inline void remove_sysfs_files(struct ehci_hcd *ehci) |
89 | { | 182 | { |
183 | struct device *controller = ehci_to_hcd(ehci)->self.controller; | ||
184 | |||
90 | /* with integrated TT there is no companion! */ | 185 | /* with integrated TT there is no companion! */ |
91 | if (!ehci_is_TDI(ehci)) | 186 | if (!ehci_is_TDI(ehci)) |
92 | device_remove_file(ehci_to_hcd(ehci)->self.controller, | 187 | device_remove_file(controller, &dev_attr_companion); |
93 | &dev_attr_companion); | 188 | |
189 | device_remove_file(controller, &dev_attr_uframe_periodic_max); | ||
94 | } | 190 | } |
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index bd6ff489baf9..fa3129fe1ee0 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h | |||
@@ -87,6 +87,8 @@ struct ehci_hcd { /* one per controller */ | |||
87 | union ehci_shadow *pshadow; /* mirror hw periodic table */ | 87 | union ehci_shadow *pshadow; /* mirror hw periodic table */ |
88 | int next_uframe; /* scan periodic, start here */ | 88 | int next_uframe; /* scan periodic, start here */ |
89 | unsigned periodic_sched; /* periodic activity count */ | 89 | unsigned periodic_sched; /* periodic activity count */ |
90 | unsigned uframe_periodic_max; /* max periodic time per uframe */ | ||
91 | |||
90 | 92 | ||
91 | /* list of itds & sitds completed while clock_frame was still active */ | 93 | /* list of itds & sitds completed while clock_frame was still active */ |
92 | struct list_head cached_itd_list; | 94 | struct list_head cached_itd_list; |