RE: usbcore and root-hub suspend/wakeup races

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

 



 
> 
> >
> *************************************************************************
> ************************************
> > 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
> 
You may forget somewhere to set resuming_ports, a patch like below may be needed.

diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index a007a9f..3f8a67f 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -941,6 +941,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
                         */
                        ehci->reset_done[i] = jiffies + msecs_to_jiffies(25);
                        ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
+                       set_bit(i, &ehci->resuming_ports);
                        mod_timer(&hcd->rh_timer, ehci->reset_done[i]);
                }
        }

> 
> 
> 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