RE: usbcore and root-hub suspend/wakeup races

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 23 Mar 2012, Chen Peter-B29397 wrote:

> *************************************************************************************************************
> Index: usb-3.3/drivers/usb/core/hcd.c
> ===================================================================
> --- usb-3.3.orig/drivers/usb/core/hcd.c
> +++ usb-3.3/drivers/usb/core/hcd.c
> @@ -1978,6 +1978,13 @@ int hcd_bus_suspend(struct usb_device *r
>        if (status == 0) {
>                usb_set_device_state(rhdev, USB_STATE_SUSPENDED);
>                hcd->state = HC_STATE_SUSPENDED;
> +
> +               /* Did we race with a root-hub wakeup event? */
> +               if (unlikely(HCD_POLL_PENDING(hcd) &&
> +                               rhdev->do_remote_wakeup)) {
> +                       hcd_bus_resume(rhdev, PMSG_AUTO_RESUME);
> +                       status = -EBUSY;
> +               }
> 
> This patch may not useful, as if the remote wakeup occurs before the bus(roothub) suspend
> (taking ehic_bus_suspend as an example), the bus suspend will quit due to there is a resume
> signal at one port. If the remote wakeup occurs after bus suspend, but before your patch,
> the HCD_FLAG_POLL_PENDING will not be set due to hcd->driver->hub_status_data returns 0 (taking
> ehci as an example), as this flag is set after 25ms later after remote wakeup occurs.

I promised an update for this patch, and here it is.  The new version
requires changes to the HCD drivers.  Basically, the HCD is now
required to return a non-zero value from the hub_status_data routine if
a resume is currently in progress on any port.  The changes for
ehci-hcd are included as a separate patch below.

Alan Stern



Index: usb-3.3/drivers/usb/core/hcd.c
===================================================================
--- usb-3.3.orig/drivers/usb/core/hcd.c
+++ usb-3.3/drivers/usb/core/hcd.c
@@ -1978,6 +1978,18 @@ int hcd_bus_suspend(struct usb_device *r
 	if (status == 0) {
 		usb_set_device_state(rhdev, USB_STATE_SUSPENDED);
 		hcd->state = HC_STATE_SUSPENDED;
+
+		/* Did we race with a root-hub wakeup event? */
+		if (rhdev->do_remote_wakeup) {
+			char	buffer[6];
+
+			status = hcd->driver->hub_status_data(hcd, buffer);
+			if (status != 0) {
+				dev_dbg(&rhdev->dev, "suspend raced with wakeup event\n");
+				hcd_bus_resume(rhdev, PMSG_AUTO_RESUME);
+				status = -EBUSY;
+			}
+		}
 	} else {
 		spin_lock_irq(&hcd_root_hub_lock);
 		if (!HCD_DEAD(hcd)) {




Index: usb-3.3/drivers/usb/host/ehci-hub.c
===================================================================
--- usb-3.3.orig/drivers/usb/host/ehci-hub.c
+++ usb-3.3/drivers/usb/host/ehci-hub.c
@@ -223,15 +223,10 @@ static int ehci_bus_suspend (struct usb_
 	 * remote wakeup, we must fail the suspend.
 	 */
 	if (hcd->self.root_hub->do_remote_wakeup) {
-		port = HCS_N_PORTS(ehci->hcs_params);
-		while (port--) {
-			if (ehci->reset_done[port] != 0) {
-				spin_unlock_irq(&ehci->lock);
-				ehci_dbg(ehci, "suspend failed because "
-						"port %d is resuming\n",
-						port + 1);
-				return -EBUSY;
-			}
+		if (ehci->resuming_ports) {
+			spin_unlock_irq(&ehci->lock);
+			ehci_dbg(ehci, "suspend failed because a port is resuming\n");
+			return -EBUSY;
 		}
 	}
 
@@ -554,16 +549,12 @@ static int
 ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
 {
 	struct ehci_hcd	*ehci = hcd_to_ehci (hcd);
-	u32		temp, status = 0;
+	u32		temp, status;
 	u32		mask;
 	int		ports, i, retval = 1;
 	unsigned long	flags;
 	u32		ppcd = 0;
 
-	/* if !USB_SUSPEND, root hub timers won't get shut down ... */
-	if (ehci->rh_state != EHCI_RH_RUNNING)
-		return 0;
-
 	/* init status to no-changes */
 	buf [0] = 0;
 	ports = HCS_N_PORTS (ehci->hcs_params);
@@ -572,6 +563,11 @@ ehci_hub_status_data (struct usb_hcd *hc
 		retval++;
 	}
 
+	/* Inform the core about resumes-in-progress by returning
+	 * a non-zero value even if there are no status changes.
+	 */
+	status = ehci->resuming_ports;
+
 	/* Some boards (mostly VIA?) report bogus overcurrent indications,
 	 * causing massive log spam unless we completely ignore them.  It
 	 * may be relevant that VIA VT8235 controllers, where PORT_POWER is
@@ -846,6 +842,7 @@ static int ehci_hub_control (
 				ehci_writel(ehci,
 					temp & ~(PORT_RWC_BITS | PORT_RESUME),
 					status_reg);
+				clear_bit(wIndex, &ehci->resuming_ports);
 				retval = handshake(ehci, status_reg,
 					   PORT_RESUME, 0, 2000 /* 2msec */);
 				if (retval != 0) {
@@ -864,6 +861,7 @@ static int ehci_hub_control (
 					ehci->reset_done[wIndex])) {
 			status |= USB_PORT_STAT_C_RESET << 16;
 			ehci->reset_done [wIndex] = 0;
+			clear_bit(wIndex, &ehci->resuming_ports);
 
 			/* force reset to complete */
 			ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET),
@@ -884,8 +882,10 @@ static int ehci_hub_control (
 					ehci_readl(ehci, status_reg));
 		}
 
-		if (!(temp & (PORT_RESUME|PORT_RESET)))
+		if (!(temp & (PORT_RESUME|PORT_RESET))) {
 			ehci->reset_done[wIndex] = 0;
+			clear_bit(wIndex, &ehci->resuming_ports);
+		}
 
 		/* transfer dedicated ports to the companion hc */
 		if ((temp & PORT_CONNECT) &&
@@ -920,6 +920,7 @@ static int ehci_hub_control (
 			status |= USB_PORT_STAT_SUSPEND;
 		} else if (test_bit(wIndex, &ehci->suspended_ports)) {
 			clear_bit(wIndex, &ehci->suspended_ports);
+			clear_bit(wIndex, &ehci->resuming_ports);
 			ehci->reset_done[wIndex] = 0;
 			if (temp & PORT_PE)
 				set_bit(wIndex, &ehci->port_c_suspend);
Index: usb-3.3/drivers/usb/host/ehci.h
===================================================================
--- usb-3.3.orig/drivers/usb/host/ehci.h
+++ usb-3.3/drivers/usb/host/ehci.h
@@ -117,6 +117,8 @@ struct ehci_hcd {			/* one per controlle
 			the change-suspend feature turned on */
 	unsigned long		suspended_ports;	/* which ports are
 			suspended */
+	unsigned long		resuming_ports;		/* which ports have
+			start to resume */
 
 	/* per-HC memory pools (could be per-bus, but ...) */
 	struct dma_pool		*qh_pool;	/* qh per active urb */

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux