提交 94ae4976 编写于 作者: A Alan Stern 提交者: Greg Kroah-Hartman

USB: EHCI: unlink unused QHs when the controller is stopped

This patch (as1458) fixes a problem affecting ultra-reliable systems:
When hardware failover of an EHCI controller occurs, the data
structures do not get released correctly.  This is because the routine
responsible for removing unused QHs from the async schedule assumes
the controller is running properly (the frame counter is used in
determining how long the QH has been idle) -- but when a failover
causes the controller to be electronically disconnected from the PCI
bus, obviously it stops running.

The solution is simple: Allow scan_async() to remove a QH from the
async schedule if it has been idle for long enough _or_ if the
controller is stopped.
Signed-off-by: NAlan Stern <stern@rowland.harvard.edu>
Reported-and-Tested-by: NDan Duval <dan.duval@stratus.com>
CC: <stable@kernel.org>
Signed-off-by: NGreg Kroah-Hartman <gregkh@suse.de>
上级 505d1f69
...@@ -1247,24 +1247,27 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -1247,24 +1247,27 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
static void scan_async (struct ehci_hcd *ehci) static void scan_async (struct ehci_hcd *ehci)
{ {
bool stopped;
struct ehci_qh *qh; struct ehci_qh *qh;
enum ehci_timer_action action = TIMER_IO_WATCHDOG; enum ehci_timer_action action = TIMER_IO_WATCHDOG;
ehci->stamp = ehci_readl(ehci, &ehci->regs->frame_index); ehci->stamp = ehci_readl(ehci, &ehci->regs->frame_index);
timer_action_done (ehci, TIMER_ASYNC_SHRINK); timer_action_done (ehci, TIMER_ASYNC_SHRINK);
rescan: rescan:
stopped = !HC_IS_RUNNING(ehci_to_hcd(ehci)->state);
qh = ehci->async->qh_next.qh; qh = ehci->async->qh_next.qh;
if (likely (qh != NULL)) { if (likely (qh != NULL)) {
do { do {
/* clean any finished work for this qh */ /* clean any finished work for this qh */
if (!list_empty (&qh->qtd_list) if (!list_empty(&qh->qtd_list) && (stopped ||
&& qh->stamp != ehci->stamp) { qh->stamp != ehci->stamp)) {
int temp; int temp;
/* unlinks could happen here; completion /* unlinks could happen here; completion
* reporting drops the lock. rescan using * reporting drops the lock. rescan using
* the latest schedule, but don't rescan * the latest schedule, but don't rescan
* qhs we already finished (no looping). * qhs we already finished (no looping)
* unless the controller is stopped.
*/ */
qh = qh_get (qh); qh = qh_get (qh);
qh->stamp = ehci->stamp; qh->stamp = ehci->stamp;
...@@ -1285,9 +1288,9 @@ static void scan_async (struct ehci_hcd *ehci) ...@@ -1285,9 +1288,9 @@ static void scan_async (struct ehci_hcd *ehci)
*/ */
if (list_empty(&qh->qtd_list) if (list_empty(&qh->qtd_list)
&& qh->qh_state == QH_STATE_LINKED) { && qh->qh_state == QH_STATE_LINKED) {
if (!ehci->reclaim if (!ehci->reclaim && (stopped ||
&& ((ehci->stamp - qh->stamp) & 0x1fff) ((ehci->stamp - qh->stamp) & 0x1fff)
>= (EHCI_SHRINK_FRAMES * 8)) >= EHCI_SHRINK_FRAMES * 8))
start_unlink_async(ehci, qh); start_unlink_async(ehci, qh);
else else
action = TIMER_ASYNC_SHRINK; action = TIMER_ASYNC_SHRINK;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册