aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2010-07-14 11:03:53 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2010-08-10 17:35:40 -0400
commit1fb2e0558781b07d2ecaabf94c81c17ac820d8f0 (patch)
treeb06dfa7e482664070e688d1b8b2e4e2113dc9099 /drivers/usb
parentffda080353979273e8aa69fc1e6134f20643ae56 (diff)
USB: EHCI: reorganize isochronous scheduler routine
This patch (as1408) rearranges the scheduling code in ehci-hcd, partly to improve its structure, but mainly to change the way it works. Whether or not a transfer exceeds the hardware schedule length will now be determined by looking at the last frame the transfer would use, instead of the first available frame following the end of the transfer. The benefit of this change is that it allows the driver to accept valid URBs which would otherwise be rejected. For example, suppose the schedule length is 1024 frames, the endpoint period is 256 frames, and a four-packet URB is submitted. The four transfers would occupy slots that are 0, 256, 512, and 768 frames past the current frame (plus an extra slop factor). These don't exceed the 1024-frame limit, so the URB should be accepted. But the current code notices that the next available slot would be 1024 frames (plus slop) in the future, which is beyond the limit, and so the URB is rejected unnecessarily. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> CC: David Brownell <david-b@pacbell.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/ehci-sched.c110
1 files changed, 57 insertions, 53 deletions
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index dd37350170bb..3381319a2b3f 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1413,13 +1413,6 @@ iso_stream_schedule (
1413 goto fail; 1413 goto fail;
1414 } 1414 }
1415 1415
1416 if (stream->depth + span > mod) {
1417 ehci_dbg (ehci, "request %p would overflow (%d+%d>%d)\n",
1418 urb, stream->depth, span, mod);
1419 status = -EFBIG;
1420 goto fail;
1421 }
1422
1423 now = ehci_readl(ehci, &ehci->regs->frame_index) & (mod - 1); 1416 now = ehci_readl(ehci, &ehci->regs->frame_index) & (mod - 1);
1424 1417
1425 /* Typical case: reuse current schedule, stream is still active. 1418 /* Typical case: reuse current schedule, stream is still active.
@@ -1428,7 +1421,7 @@ iso_stream_schedule (
1428 * slot in the schedule, implicitly assuming URB_ISO_ASAP. 1421 * slot in the schedule, implicitly assuming URB_ISO_ASAP.
1429 */ 1422 */
1430 if (likely (!list_empty (&stream->td_list))) { 1423 if (likely (!list_empty (&stream->td_list))) {
1431 start = stream->next_uframe; 1424 u32 excess;
1432 1425
1433 /* For high speed devices, allow scheduling within the 1426 /* For high speed devices, allow scheduling within the
1434 * isochronous scheduling threshold. For full speed devices 1427 * isochronous scheduling threshold. For full speed devices
@@ -1440,21 +1433,23 @@ iso_stream_schedule (
1440 else 1433 else
1441 next = now; 1434 next = now;
1442 1435
1443 /* Fell behind (by up to twice the slop amount)? */ 1436 /* Fell behind (by up to twice the slop amount)?
1444 if (((start - next) & (mod - 1)) >= 1437 * We decide based on the time of the last currently-scheduled
1445 mod - 2 * SCHEDULE_SLOP) 1438 * slot, not the time of the next available slot.
1446 start += period * DIV_ROUND_UP( 1439 */
1447 (next - start) & (mod - 1), 1440 excess = (stream->next_uframe - period - next) & (mod - 1);
1448 period); 1441 if (excess >= mod - 2 * SCHEDULE_SLOP)
1449 1442 start = next + excess - mod + period *
1450 /* Tried to schedule too far into the future? */ 1443 DIV_ROUND_UP(mod - excess, period);
1451 if (unlikely(((start - now) & (mod - 1)) + span 1444 else
1452 >= mod - 2 * SCHEDULE_SLOP)) { 1445 start = next + excess + period;
1446 if (start - now >= mod) {
1447 ehci_dbg(ehci, "request %p would overflow (%d+%d >= %d)\n",
1448 urb, start - now - period, period,
1449 mod);
1453 status = -EFBIG; 1450 status = -EFBIG;
1454 goto fail; 1451 goto fail;
1455 } 1452 }
1456 stream->next_uframe = start;
1457 goto ready;
1458 } 1453 }
1459 1454
1460 /* need to schedule; when's the next (u)frame we could start? 1455 /* need to schedule; when's the next (u)frame we could start?
@@ -1463,51 +1458,60 @@ iso_stream_schedule (
1463 * can also help high bandwidth if the dma and irq loads don't 1458 * can also help high bandwidth if the dma and irq loads don't
1464 * jump until after the queue is primed. 1459 * jump until after the queue is primed.
1465 */ 1460 */
1466 start = SCHEDULE_SLOP + (now & ~0x07); 1461 else {
1467 start &= mod - 1; 1462 start = SCHEDULE_SLOP + (now & ~0x07);
1468 stream->next_uframe = start; 1463
1469 1464 /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */
1470 /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ 1465
1471 1466 /* find a uframe slot with enough bandwidth */
1472 /* find a uframe slot with enough bandwidth */ 1467 next = start + period;
1473 for (; start < (stream->next_uframe + period); start++) { 1468 for (; start < next; start++) {
1474 int enough_space; 1469
1475 1470 /* check schedule: enough space? */
1476 /* check schedule: enough space? */ 1471 if (stream->highspeed) {
1477 if (stream->highspeed) 1472 if (itd_slot_ok(ehci, mod, start,
1478 enough_space = itd_slot_ok (ehci, mod, start, 1473 stream->usecs, period))
1479 stream->usecs, period); 1474 break;
1480 else { 1475 } else {
1481 if ((start % 8) >= 6) 1476 if ((start % 8) >= 6)
1482 continue; 1477 continue;
1483 enough_space = sitd_slot_ok (ehci, mod, stream, 1478 if (sitd_slot_ok(ehci, mod, stream,
1484 start, sched, period); 1479 start, sched, period))
1480 break;
1481 }
1485 } 1482 }
1486 1483
1487 /* schedule it here if there's enough bandwidth */ 1484 /* no room in the schedule */
1488 if (enough_space) { 1485 if (start == next) {
1489 stream->next_uframe = start & (mod - 1); 1486 ehci_dbg(ehci, "iso resched full %p (now %d max %d)\n",
1490 goto ready; 1487 urb, now, now + mod);
1488 status = -ENOSPC;
1489 goto fail;
1491 } 1490 }
1492 } 1491 }
1493 1492
1494 /* no room in the schedule */ 1493 /* Tried to schedule too far into the future? */
1495 ehci_dbg (ehci, "iso %ssched full %p (now %d max %d)\n", 1494 if (unlikely(start - now + span - period
1496 list_empty (&stream->td_list) ? "" : "re", 1495 >= mod - 2 * SCHEDULE_SLOP)) {
1497 urb, now, now + mod); 1496 ehci_dbg(ehci, "request %p would overflow (%d+%d >= %d)\n",
1498 status = -ENOSPC; 1497 urb, start - now, span - period,
1498 mod - 2 * SCHEDULE_SLOP);
1499 status = -EFBIG;
1500 goto fail;
1501 }
1499 1502
1500fail: 1503 stream->next_uframe = start & (mod - 1);
1501 iso_sched_free (stream, sched);
1502 urb->hcpriv = NULL;
1503 return status;
1504 1504
1505ready:
1506 /* report high speed start in uframes; full speed, in frames */ 1505 /* report high speed start in uframes; full speed, in frames */
1507 urb->start_frame = stream->next_uframe; 1506 urb->start_frame = stream->next_uframe;
1508 if (!stream->highspeed) 1507 if (!stream->highspeed)
1509 urb->start_frame >>= 3; 1508 urb->start_frame >>= 3;
1510 return 0; 1509 return 0;
1510
1511 fail:
1512 iso_sched_free(stream, sched);
1513 urb->hcpriv = NULL;
1514 return status;
1511} 1515}
1512 1516
1513/*-------------------------------------------------------------------------*/ 1517/*-------------------------------------------------------------------------*/