On Fri, 9 May 2014, Dan Williams wrote:

> Testing looks good.  In my objection to this approach I neglected to
> consider that the port_status_lock now protects us from port_event()
> usb_port_{suspend_resume} collisions.  I have updated the changelog
> accordingly.
> 
> 8<-----------
> Subject: usb: resume (wakeup) child device when port is powered on
> 
> From: Dan Williams <dan.j.willi...@intel.com>
> 
> Unconditionally wake up the child device when the power session is
> recovered.
> 
> This addresses the following scenarios:
> 
> 1/ The device may need a reset on power-session loss, without this
>    change port power-on recovery exposes khubd to scenarios that
>    usb_port_resume() is set to handle.  Prior to port power control the
>    only time a power session would be lost is during dpm_suspend of the
>    hub.  In that scenario usb_port_resume() is guaranteed to be called
>    prior to khubd running for that port.  With this change we wakeup the
>    child device as soon as possible (prior to khubd running again for this
>    port).
> 
>    Although khubd has facilities to wake a child device it will only do
>    so if the portstatus / portchange indicates a suspend state.  In the
>    case of port power control we are not coming from a hub-port-suspend
>    state.  This implementation extends usb_wake_notification() for the
>    port rpm_resume case.

Actually it doesn't any more (extend usb_wake_notification(), that is).

> @@ -2057,9 +2057,11 @@ static void hub_free_dev(struct usb_device *udev)
>   */
>  void usb_disconnect(struct usb_device **pdev)
>  {
> -     struct usb_device       *udev = *pdev;
> -     struct usb_hub          *hub = usb_hub_to_struct_hub(udev);
> -     int                     i;
> +     int i;
> +     struct usb_device *udev = *pdev;
> +     int port1 = udev->portnum;
> +     struct usb_port *port_dev = NULL;
> +     struct usb_hub *hub = usb_hub_to_struct_hub(udev);
>  
>       /* mark the device as inactive, so any further urb submissions for
>        * this device (and any of its children) will fail immediately.
> @@ -2086,15 +2088,18 @@ void usb_disconnect(struct usb_device **pdev)
>       usb_hcd_synchronize_unlinks(udev);
>  
>       if (udev->parent) {
> -             int port1 = udev->portnum;
>               struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
> -             struct usb_port *port_dev = hub->ports[port1 - 1];
>  
> +             port_dev = hub->ports[port1 - 1];
>               sysfs_remove_link(&udev->dev.kobj, "port");
>               sysfs_remove_link(&port_dev->dev.kobj, "device");
>  
> -             if (test_and_clear_bit(port1, hub->child_usage_bits))
> -                     pm_runtime_put(&port_dev->dev);
> +             /*
> +              * As usb_port_runtime_resume() de-references udev, make
> +              * sure no resumes occur during removal
> +              */
> +             if (!test_and_set_bit(port1, hub->child_usage_bits))
> +                     pm_runtime_get_sync(&port_dev->dev);
>       }
>  
>       usb_remove_ep_devs(&udev->ep0);
> @@ -2116,6 +2121,9 @@ void usb_disconnect(struct usb_device **pdev)
>       *pdev = NULL;
>       spin_unlock_irq(&device_state_lock);
>  
> +     if (port_dev && test_and_clear_bit(port1, hub->child_usage_bits))
> +             pm_runtime_put(&port_dev->dev);
> +

There's a nasty bug here.  I'll let you figure it out for yourself.  :-)
Hint: Hiding a variable by declaring another local variable with the 
same name in an inner block often leads to trouble.

Alan Stern

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

Reply via email to