On Wed, 30 Apr 2014, Dan Williams wrote: > > I'm kind of doubtful about this. Remember, if the hub is being reset > > then it probably isn't working correctly. There's a good chance that > > asking it to change a port's power level will fail. > > > > It would be better if the port suspend and resume routines knew when a > > hub reset was in progress. If it was, they could set the power_is_on > > flag appropriately and immediately return 0. When the reset finished, > > hub_activate() would then automatically apply power to the right ports. > > Great point. Continuing the line of thinking I think we should also > clear any previous pm runtime errors in the ports when the hub is reset. > > Hmm, I'm struggling to make the update of the "hub->in_reset" state > atomic with respect to in-flight runtime pm transitions. How about this > incremental addition to the previous suggestion where we: > > 1/ Still try to runtime resume all the ports > > 2/ Force any ports that didn't make the transition active > > 3/ Clear any runtime errors as a result of that forcing, and mark the > ports "on" for the purpose of hub_power_on(). > > diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c > index 02b0833b0a67..a662c7172f5b 100644 > --- a/drivers/usb/core/hub.c > +++ b/drivers/usb/core/hub.c > @@ -5183,31 +5183,36 @@ static int descriptors_changed(struct usb_device *udev, > return changed; > } > > -static void usb_hub_ports_pm_get_sync(struct usb_device *hdev) > +static void usb_hub_pm_prep_reset(struct usb_device *hdev) > { > struct usb_hub *hub = usb_hub_to_struct_hub(hdev); > + struct usb_port *port_dev; > int port1; > > + if (!hub) > + return; > for (port1 = 1; port1 <= hdev->maxchild; port1++) { > - struct usb_port *port_dev = hub->ports[port1 - 1]; > - > + port_dev = hub->ports[port1 - 1]; > pm_runtime_get_sync(&port_dev->dev); > + pm_runtime_set_active(&port_dev->dev); > + set_bit(port1, hub->power_bits); > } > } > > -static void usb_hub_ports_pm_put(struct usb_device *hdev) > +static void usb_hub_pm_finish_reset(struct usb_device *hdev) > { > struct usb_hub *hub = usb_hub_to_struct_hub(hdev); > + struct usb_port *port_dev; > int port1; > > + if (!hub) > + return; > for (port1 = 1; port1 <= hdev->maxchild; port1++) { > - struct usb_port *port_dev = hub->ports[port1 - 1]; > - > + port_dev = hub->ports[port1 - 1]; > pm_runtime_put(&port_dev->dev); > } > } > > - > /** > * usb_reset_and_verify_device - perform a USB port reset to reinitialize a device > * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) > @@ -5265,7 +5270,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) > parent_hub = usb_hub_to_struct_hub(parent_hdev); > > /* Disable usb_port power management */ > - usb_hub_ports_pm_get_sync(udev); > + usb_hub_pm_prep_reset(udev); > > /* Disable USB2 hardware LPM. > * It will be re-enabled by the enumeration process. > @@ -5386,11 +5391,11 @@ done: > usb_enable_ltm(udev); > usb_release_bos_descriptor(udev); > udev->bos = bos; > - usb_hub_ports_pm_put(udev); > + usb_hub_pm_finish_reset(udev); > return 0; > > re_enumerate: > - usb_hub_ports_pm_put(udev); > + usb_hub_pm_finish_reset(udev); > /* LPM state doesn't matter when we're about to destroy the device. */ > hub_port_logical_disconnect(parent_hub, port1); > usb_release_bos_descriptor(udev); I don't like the way you changed usb_reset_and_verify_device(). It doesn't seem necessary when all we're worried about is hubs. And I still think that PM operations on the ports should be avoided, if possible, when a hub is being reset. Here's my own effort (not tested). It doesn't clear any runtime PM errors, but you can add that in if you want. Index: usb-3.15/drivers/usb/core/hub.h =================================================================== --- usb-3.15.orig/drivers/usb/core/hub.h +++ usb-3.15/drivers/usb/core/hub.h @@ -66,6 +66,7 @@ struct usb_hub { unsigned limited_power:1; unsigned quiescing:1; unsigned disconnected:1; + unsigned in_reset:1; unsigned quirk_check_port_auto_suspend:1; Index: usb-3.15/drivers/usb/core/hub.c =================================================================== --- usb-3.15.orig/drivers/usb/core/hub.c +++ usb-3.15/drivers/usb/core/hub.c @@ -1276,12 +1276,22 @@ static void hub_quiesce(struct usb_hub * flush_work(&hub->tt.clear_work); } +static void hub_pm_barrier_for_all_ports(struct usb_hub *hub) +{ + int i; + + for (i = 0; i < hub->hdev->maxchild; ++i) + pm_runtime_barrier(&hub->ports[i]->dev); +} + /* caller has locked the hub device */ static int hub_pre_reset(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata(intf); hub_quiesce(hub, HUB_PRE_RESET); + hub->in_reset = 1; + hub_pm_barrier_for_all_ports(hub); return 0; } @@ -1290,6 +1300,8 @@ static int hub_post_reset(struct usb_int { struct usb_hub *hub = usb_get_intfdata(intf); + hub->in_reset = 0; + hub_pm_barrier_for_all_ports(hub); hub_activate(hub, HUB_POST_RESET); return 0; } Index: usb-3.15/drivers/usb/core/port.c =================================================================== --- usb-3.15.orig/drivers/usb/core/port.c +++ usb-3.15/drivers/usb/core/port.c @@ -82,6 +82,11 @@ static int usb_port_runtime_resume(struc if (!hub) return -EINVAL; + if (hub->in_reset) { + port_dev->power_is_on = 1; + return 0; + } + usb_autopm_get_interface(intf); set_bit(port1, hub->busy_bits); @@ -122,6 +127,11 @@ static int usb_port_runtime_suspend(stru == PM_QOS_FLAGS_ALL) return -EAGAIN; + if (hub->in_reset) { + port_dev->power_is_on = 0; + return 0; + } + usb_autopm_get_interface(intf); set_bit(port1, hub->busy_bits); retval = usb_hub_set_port_power(hdev, hub, port1, false); > > PS: Regarding those atomic flags... Maybe the best way to save space > > is to move the existing atomic flags in usb_hub down into the usb_port > > structures. Along with the two flags that are already there, we'd end > > up with one long per port rather than 6 longs per hub. What do you > > think? > > > > I did the conversion of putting them in struct usb_hub and aesthetically > I like the look of: > > _bit(port1, hub->power_bits) > > ...better than: > > _bit(USB_PORTDEV_POWER, &port_dev->flags) > > So I'm inclined to keep them there regardless of the space argument. > Thoughts? I don't care much where the flags go, so long as they all go in the same place. :-) Alan Stern -- 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