Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 317031
b: refs/heads/master
c: 32830f2
h: refs/heads/master
i:
  317029: f5e7969
  317027: f08d84a
  317023: f38674a
v: v3
  • Loading branch information
Alan Stern authored and Greg Kroah-Hartman committed Jul 16, 2012
1 parent afc91c6 commit 5c1e8dc
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 43 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 3c273a056bf46167f0a1309c2ba72282a17d2541
refs/heads/master: 32830f207691176234b4c4dd17f0d7ab6d87d94b
16 changes: 1 addition & 15 deletions trunk/drivers/usb/host/ehci-hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,6 @@ static const char hcd_name [] = "ehci_hcd";
#define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */

#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
#define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1)
/* 5-ms async qh unlink delay */

/* Initial IRQ latency: faster than hw default */
static int log2_irq_thresh = 0; // 0 to 6
Expand Down Expand Up @@ -130,15 +128,6 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us");
static void
timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action)
{
/* Don't override timeouts which shrink or (later) disable
* the async ring; just the I/O watchdog. Note that if a
* SHRINK were pending, OFF would never be requested.
*/
if (timer_pending(&ehci->watchdog)
&& (BIT(TIMER_ASYNC_SHRINK)
& ehci->actions))
return;

if (!test_and_set_bit(action, &ehci->actions)) {
unsigned long t;

Expand All @@ -148,10 +137,6 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action)
return;
t = EHCI_IO_JIFFIES;
break;
/* case TIMER_ASYNC_SHRINK: */
default:
t = EHCI_SHRINK_JIFFIES;
break;
}
mod_timer(&ehci->watchdog, t + jiffies);
}
Expand Down Expand Up @@ -307,6 +292,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
/*-------------------------------------------------------------------------*/

static void end_unlink_async(struct ehci_hcd *ehci);
static void unlink_empty_async(struct ehci_hcd *ehci);
static void ehci_work(struct ehci_hcd *ehci);
static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
Expand Down
1 change: 1 addition & 0 deletions trunk/drivers/usb/host/ehci-hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
ehci->rh_state = EHCI_RH_SUSPENDED;

end_unlink_async(ehci);
unlink_empty_async(ehci);
ehci_handle_intr_unlinks(ehci);
end_free_itds(ehci);

Expand Down
78 changes: 53 additions & 25 deletions trunk/drivers/usb/host/ehci-q.c
Original file line number Diff line number Diff line change
Expand Up @@ -1205,7 +1205,7 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested)
end_unlink_async(ehci);

/* Otherwise start a new IAA cycle */
} else {
} else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) {
/* Make sure the unlinks are all visible to the hardware */
wmb();

Expand Down Expand Up @@ -1253,6 +1253,39 @@ static void end_unlink_async(struct ehci_hcd *ehci)
}
}

static void unlink_empty_async(struct ehci_hcd *ehci)
{
struct ehci_qh *qh, *next;
bool stopped = (ehci->rh_state < EHCI_RH_RUNNING);
bool check_unlinks_later = false;

/* Unlink all the async QHs that have been empty for a timer cycle */
next = ehci->async->qh_next.qh;
while (next) {
qh = next;
next = qh->qh_next.qh;

if (list_empty(&qh->qtd_list) &&
qh->qh_state == QH_STATE_LINKED) {
if (!stopped && qh->unlink_cycle ==
ehci->async_unlink_cycle)
check_unlinks_later = true;
else
single_unlink_async(ehci, qh);
}
}

/* Start a new IAA cycle if any QHs are waiting for it */
if (ehci->async_unlink)
start_iaa_cycle(ehci, false);

/* QHs that haven't been empty for long enough will be handled later */
if (check_unlinks_later) {
ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true);
++ehci->async_unlink_cycle;
}
}

/* makes sure the async qh will become idle */
/* caller must own ehci->lock */

Expand All @@ -1277,12 +1310,8 @@ static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)

static void scan_async (struct ehci_hcd *ehci)
{
bool stopped;
struct ehci_qh *qh;
enum ehci_timer_action action = TIMER_IO_WATCHDOG;

timer_action_done (ehci, TIMER_ASYNC_SHRINK);
stopped = (ehci->rh_state < EHCI_RH_RUNNING);
bool check_unlinks_later = false;

ehci->qh_scan_next = ehci->async->qh_next.qh;
while (ehci->qh_scan_next) {
Expand All @@ -1301,28 +1330,27 @@ static void scan_async (struct ehci_hcd *ehci)
* in single_unlink_async().
*/
temp = qh_completions(ehci, qh);
if (qh->needs_rescan)
if (qh->needs_rescan) {
start_unlink_async(ehci, qh);
qh->unlink_time = jiffies + EHCI_SHRINK_JIFFIES;
if (temp != 0)
} else if (list_empty(&qh->qtd_list)
&& qh->qh_state == QH_STATE_LINKED) {
qh->unlink_cycle = ehci->async_unlink_cycle;
check_unlinks_later = true;
} else if (temp != 0)
goto rescan;
}
}

/* unlink idle entries, reducing DMA usage as well
* as HCD schedule-scanning costs. delay for any qh
* we just scanned, there's a not-unusual case that it
* doesn't stay idle for long.
* (plus, avoids some kind of re-activation race.)
*/
if (list_empty(&qh->qtd_list)
&& qh->qh_state == QH_STATE_LINKED) {
if (!ehci->async_unlink && (stopped ||
time_after_eq(jiffies, qh->unlink_time)))
start_unlink_async(ehci, qh);
else
action = TIMER_ASYNC_SHRINK;
}
/*
* Unlink empty entries, reducing DMA usage as well
* as HCD schedule-scanning costs. Delay for any qh
* we just scanned, there's a not-unusual case that it
* doesn't stay idle for long.
*/
if (check_unlinks_later && ehci->rh_state == EHCI_RH_RUNNING &&
!(ehci->enabled_hrtimer_events &
BIT(EHCI_HRTIMER_ASYNC_UNLINKS))) {
ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true);
++ehci->async_unlink_cycle;
}
if (action == TIMER_ASYNC_SHRINK)
timer_action (ehci, TIMER_ASYNC_SHRINK);
}
2 changes: 2 additions & 0 deletions trunk/drivers/usb/host/ehci-timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ static unsigned event_delays_ns[] = {
1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */
1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */
2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */
6 * NSEC_PER_MSEC, /* EHCI_HRTIMER_ASYNC_UNLINKS */
10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */
10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */
15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */
Expand Down Expand Up @@ -347,6 +348,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = {
ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */
ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */
end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */
unlink_empty_async, /* EHCI_HRTIMER_ASYNC_UNLINKS */
ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */
ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */
ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */
Expand Down
4 changes: 2 additions & 2 deletions trunk/drivers/usb/host/ehci.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ enum ehci_hrtimer_event {
EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */
EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */
EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */
EHCI_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */
EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */
EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */
EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */
Expand Down Expand Up @@ -123,6 +124,7 @@ struct ehci_hcd { /* one per controller */
struct ehci_qh *async_unlink_last;
struct ehci_qh *async_iaa;
struct ehci_qh *qh_scan_next;
unsigned async_unlink_cycle;
unsigned async_count; /* async activity count */

/* periodic schedule support */
Expand Down Expand Up @@ -232,7 +234,6 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)

enum ehci_timer_action {
TIMER_IO_WATCHDOG,
TIMER_ASYNC_SHRINK,
};

static inline void
Expand Down Expand Up @@ -382,7 +383,6 @@ struct ehci_qh {
struct ehci_qtd *dummy;
struct ehci_qh *unlink_next; /* next on unlink list */

unsigned long unlink_time;
unsigned unlink_cycle;
unsigned stamp;

Expand Down

0 comments on commit 5c1e8dc

Please sign in to comment.