Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753003AbYJRTHG (ORCPT ); Sat, 18 Oct 2008 15:07:06 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1751719AbYJRTDV (ORCPT ); Sat, 18 Oct 2008 15:03:21 -0400 Received: from mx2.suse.de ([195.135.220.15]:47310 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751903AbYJRTDT (ORCPT ); Sat, 18 Oct 2008 15:03:19 -0400 Date: Sat, 18 Oct 2008 11:34:19 -0700 From: Greg KH To: linux-kernel@vger.kernel.org, stable@kernel.org, jejb@kernel.org Cc: Justin Forbes , Zwane Mwaikambo , "Theodore Ts'o" , Randy Dunlap , Dave Jones , Chuck Wolber , Chris Wedgwood , Michael Krufky , Chuck Ebbert , Domenico Andreoli , Willy Tarreau , Rodrigo Rubira Branco , Jake Edge , Eugene Teo , torvalds@linux-foundation.org, akpm@linux-foundation.org, alan@lxorguk.ukuu.org.uk, Alan Stern Subject: [patch 10/17] USB: OHCI: fix endless polling behavior Message-ID: <20081018183418.GK14035@suse.de> References: <20081018182721.521723254@mini.kroah.org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline; filename="usb-ohci-fix-endless-polling-behavior.patch" In-Reply-To: <20081018183334.GA14035@suse.de> User-Agent: Mutt/1.5.16 (2007-06-09) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 5662 Lines: 167 2.6.27-stable review patch. If anyone has any objections, please let us know. ------------------ From: Alan Stern commit 71b7497c078a97e2afb774ad7c1f8ff5bdda8a60 upstream This patch (as1149) fixes an obscure problem in OHCI polling. In the current code, if the RHSC interrupt status flag turns on at a time when RHSC interrupts are disabled, it will remain on forever: The interrupt handler is the only place where RHSC status gets turned back off; The interrupt handler won't turn RHSC status off because it doesn't turn off status flags if the corresponding interrupt isn't enabled; RHSC interrupts will never get enabled because ohci_root_hub_state_changes() doesn't reenable RHSC if RHSC status is on! As a result we will continue polling indefinitely instead of reverting to interrupt-driven operation, and the root hub will not autosuspend. This particular sequence of events is not at all unusual; in fact plugging a USB device into an OHCI controller will usually cause it to occur. Of course, this is a bug. The proper thing to do is to turn off RHSC status just before reading the actual port status values. That way either a port status change will be detected (if it occurs before the status read) or it will turn RHSC back on. Possibly both, but that won't hurt anything. We can still check for systems in which RHSC is totally broken, by re-reading RHSC after clearing it and before reading the port statuses. (This re-read has to be done anyway, to post the earlier write.) If RHSC is on but no port-change statuses are set, then we know that RHSC is broken and we can avoid re-enabling it. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-hub.c | 51 +++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 19 deletions(-) --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -359,17 +359,15 @@ static void ohci_finish_controller_resum /* Carry out polling-, autostop-, and autoresume-related state changes */ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, - int any_connected) + int any_connected, int rhsc_status) { int poll_rh = 1; - int rhsc_status, rhsc_enable; + int rhsc_enable; /* Some broken controllers never turn off RHCS in the interrupt * status register. For their sake we won't re-enable RHSC * interrupts if the interrupt bit is already active. */ - rhsc_status = ohci_readl(ohci, &ohci->regs->intrstatus) & - OHCI_INTR_RHSC; rhsc_enable = ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC; @@ -421,14 +419,23 @@ static int ohci_root_hub_state_changes(s ohci_rh_resume(ohci); else usb_hcd_resume_root_hub(ohci_to_hcd(ohci)); + + /* If remote wakeup is disabled, stop polling */ + } else if (!ohci->autostop && + !ohci_to_hcd(ohci)->self.root_hub-> + do_remote_wakeup) { + poll_rh = 0; + } else { - if (!rhsc_enable && !rhsc_status && (ohci->autostop || - ohci_to_hcd(ohci)->self.root_hub-> - do_remote_wakeup)) { + /* If no status changes are pending, + * enable RHSC interrupts + */ + if (!rhsc_enable && !rhsc_status) { rhsc_enable = OHCI_INTR_RHSC; ohci_writel(ohci, rhsc_enable, &ohci->regs->intrenable); } + /* Keep polling until RHSC is enabled */ if (rhsc_enable) poll_rh = 0; } @@ -448,22 +455,22 @@ static inline int ohci_rh_resume(struct * autostop isn't used when CONFIG_PM is turned off. */ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, - int any_connected) + int any_connected, int rhsc_status) { - int rhsc_status; - /* If RHSC is enabled, don't poll */ if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC) return 0; - /* If no status changes are pending, enable RHSC interrupts */ - rhsc_status = ohci_readl(ohci, &ohci->regs->intrstatus) & - OHCI_INTR_RHSC; - if (!changed && !rhsc_status) { - ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); - return 0; - } - return 1; + /* If status changes are pending, continue polling. + * Conversely, if no status changes are pending but the RHSC + * status bit was set, then RHSC may be broken so continue polling. + */ + if (changed || rhsc_status) + return 1; + + /* It's safe to re-enable RHSC interrupts */ + ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); + return 0; } #endif /* CONFIG_PM */ @@ -478,6 +485,7 @@ ohci_hub_status_data (struct usb_hcd *hc struct ohci_hcd *ohci = hcd_to_ohci (hcd); int i, changed = 0, length = 1; int any_connected = 0; + int rhsc_status; unsigned long flags; spin_lock_irqsave (&ohci->lock, flags); @@ -503,6 +511,11 @@ ohci_hub_status_data (struct usb_hcd *hc length++; } + /* Clear the RHSC status flag before reading the port statuses */ + ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrstatus); + rhsc_status = ohci_readl(ohci, &ohci->regs->intrstatus) & + OHCI_INTR_RHSC; + /* look at each port */ for (i = 0; i < ohci->num_ports; i++) { u32 status = roothub_portstatus (ohci, i); @@ -521,7 +534,7 @@ ohci_hub_status_data (struct usb_hcd *hc } hcd->poll_rh = ohci_root_hub_state_changes(ohci, changed, - any_connected); + any_connected, rhsc_status); done: spin_unlock_irqrestore (&ohci->lock, flags); -- -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/